import { Box, Button, Card, Divider, List, ListItem, Typography } from "@mui/material"; import Grid from '@mui/material/Grid2'; import { Component, Device, Interaction, User } from "models"; import { getContextKeys, getContextTypes } from "pbHelpers/Context"; import { useDeviceAPI, useGlobalState, useSnackbar } from "providers"; import { useEffect, useState } from "react"; import { useLocation, useParams } from "react-router-dom"; import PageContainer from "./PageContainer"; import { useMobile } from "hooks"; import LoadingScreen from "app/LoadingScreen"; import SmartBreadcrumb from "common/SmartBreadcrumb"; import DeviceActions from "device/DeviceActions"; import { pond, quack } from "protobuf-ts/pond"; import { cloneDeep } from "lodash"; import DeviceOverview from "device/DeviceOverview"; import { DeviceAvailabilityMap, FindAvailablePositions, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability"; import ComponentCard from "component/ComponentCard"; import { sameComponentID, sortComponents } from "pbHelpers/Component"; import { isController } from "pbHelpers/ComponentType"; import { or } from "utils"; import ComponentDiagnostics from "component/ComponentDiagnostics"; import DeviceWizard from "device/DeviceWizard"; import DeviceScannedComponents from "device/autoDetect/DeviceScannedComponents"; export interface DevicePageData { device: Device; components: Component[]; interactions: Interaction[]; permissions: pond.Permission[]; preferences: pond.DevicePreferences; // componentPreferences: Map } export default function DevicePage() { const deviceAPI = useDeviceAPI() const snackbar = useSnackbar() const isMobile = useMobile() const deviceID = useParams<{ deviceID: string }>()?.deviceID ?? ""; const groupID = useParams<{ groupID: string }>()?.groupID ?? ""; const { state } = useLocation(); const [{ as, team, user }] = useGlobalState() // console.log(state) const [device, setDevice] = useState(state?.device ? Device.create(state.device) : Device.create()) const [loading, setLoading] = useState(false) const [permissions, setPermissions] = useState([]) const [preferences, setPreferences] = useState(pond.DevicePreferences.create()) // const [tags, setTags] = useState([]); const [interactions, setInteractions] = useState([]); const [prefsMap, setPrefsMap] = useState>(new Map()); const [cellularUsage, _setCellularUsage] = useState(0); const [cellularStatus, _setCellularStatus] = useState(""); const [availablePositions, setAvailablePositions] = useState(new Map()); const [availableOffsets, setAvailableOffsets] = useState(new Map()); const [displayOrder, setDisplayOrder] = useState([]); const [components, setComponents] = useState>(new Map()); const [diagnosticComponents, setDiagnosticComponents] = useState([]); const [scannedAddresses, setScannedAddresses] = useState(undefined) const [scanInProgress, setScanInProgress] = useState(false) // const [components, setComponents] = useState([]); const [addComponentManualDialogOpen, setAddComponentManualDialogOpen] = useState(false) // const [devicePageData, setDevicePageData] = useState(pond.GetDevicePageDataResponse.create()) const loadDevice = () => { if (loading) return if (state?.devicePageData) { setDevice(Device.create(state.devicePageData.device)) setComponents(state.devicePageData.components.map((comp: pond.Component) => Component.create(comp))) setInteractions(state.devicePageData.interactions.map((inter: pond.Interaction) => Interaction.create(inter))) setPreferences(state.devicePageData.preferences) setPermissions(state.devicePageData.permissions) return } setLoading(true) deviceAPI.getPageData(deviceID, getContextKeys(), getContextTypes(), as).then(resp => { // setDevicePageData(resp.data) let device = Device.any(resp.data.device) // console.log(resp.data) setScanInProgress(resp.data.pendingRequests) setDevice(device) let newPermissions: pond.Permission[] = [] resp.data.permissions.forEach(perm => { if (typeof(perm) === "string" ) { const permNumber = pond.Permission[perm as keyof typeof pond.Permission] newPermissions.push(permNumber) } else { newPermissions.push(perm) } }) setPermissions(newPermissions) let u = User.any(resp.data.user); setPreferences(u.preferences) // setTags(resp.data.tags) resp.data.device?.status?.tagNames let newComps: Component[] = [] resp.data.components.forEach(comp => { newComps.push(Component.create(comp)) }) let available = FindAvailablePositions( newComps, device.settings.product ); setAvailablePositions(available.GetAvailability()); let rawComponents: Array = or(resp.data.components, []); let rComponents: Map = new Map(); let diagComponents: Component[] = []; rawComponents.forEach((rawComponent: any) => { let component = Component.any(rawComponent); rComponents.set(component.key(), component); //check if is a diagnostic component switch (component.type()) { case quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE: if (component.subType() === quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG) { diagComponents.push(component); } } }); setDiagnosticComponents(diagComponents) setComponents(rComponents) let interactions: Interaction[] = []; resp.data.interactions.forEach((interaction: pond.Interaction) => { if ( interaction.settings && interaction.settings.nodeOne > interaction.settings.nodeTwo && interaction.settings.nodeTwo !== 0 ) { //flip operator and send negative comparitor to save interaction.settings.conditions.forEach(condition => { //coming back from the backend as a string for some reason if (condition.comparison.toString() === "RELATIONAL_OPERATOR_GREATER_THAN") { condition.comparison = quack.RelationalOperator.RELATIONAL_OPERATOR_LESS_THAN; } else { condition.comparison = quack.RelationalOperator.RELATIONAL_OPERATOR_GREATER_THAN; } condition.value = condition.value * -1; }); } interactions.push(Interaction.any(interaction)); }); setInteractions(interactions); }).catch(err => { setDevice(Device.create()); // setComponents(new Map()); setInteractions([]); setAvailablePositions(new Map()); setPermissions([]); setPreferences(pond.UserPreferences.create()); // setInvalidDevice(true); // error(err); if (err?.response?.data?.error.includes("not found")) { let name = as === team.key() ? team.name() : user.name(); let warningString = name + " not permitted to view Device " + deviceID; let length = getContextTypes().length; if (length > 0) { warningString = warningString + " through " + getContextTypes()[length - 1]; } snackbar.warning(warningString); } else { snackbar.error(err); } }) .finally(() => setLoading(false)); } const loadPortScan = () => { deviceAPI.listFoundComponents(parseInt(deviceID)) .then(resp => { console.log(resp.data) if (resp.data.foundComponents?.i2c || resp.data.foundComponents?.oneWire){ setScannedAddresses(pond.ListFoundComponentsResponse.fromObject(resp.data).foundComponents ?? undefined) } }) .catch(err => { console.log(err) }) } useEffect(() => { loadDevice() loadPortScan() }, [deviceID, as]) const toggleNotificationPreference = () => { let updatedPreferences = cloneDeep(preferences); updatedPreferences.notify = !preferences.notify; deviceAPI .updatePreferences(deviceID, updatedPreferences, getContextKeys(), getContextTypes(), as) .then(() => setPreferences(updatedPreferences)) .catch(() => { snackbar.error( "Error occured while " + (preferences.notify ? "enabling" : "disabling") + " notifications" ); }); }; if (loading) return ( ) const getUsage = () => { let usage = undefined; if (cellularStatus && cellularStatus !== "") { usage = { status: cellularStatus, bytes: cellularUsage }; } return usage; }; const displayedComponentsBlacklist = [ quack.ComponentType.COMPONENT_TYPE_POWER, quack.ComponentType.COMPONENT_TYPE_MODEM ]; const getOrderedComponents = () => { let compsToOrder: Component[] = []; Array.from(components.values()).forEach(comp => { if (!displayedComponentsBlacklist.includes(comp.settings.type)) { compsToOrder.push(comp); } }); if (displayOrder.length === compsToOrder.length) { return compsToOrder.sort((a, b: Component) => sortComponents(a, b, displayOrder)); } else { return compsToOrder; } }; const handleComponentChanged = (component: Component) => { let updatedComponents = cloneDeep(components); let updatedComponent = cloneDeep(component); if (updatedComponents.has(updatedComponent.key())) { updatedComponents.set(updatedComponent.key(), updatedComponent); setComponents(updatedComponents); } }; const componentCards = () => { let orderedComponents = getOrderedComponents(); let sensorComponents: JSX.Element[] = []; let controllerComponents: JSX.Element[] = []; //let componentCards = []; //let hasGPS = false; for (let i = 0; i < orderedComponents.length; i++) { let c = orderedComponents[i]; if (displayedComponentsBlacklist.includes(c.settings.type)) { continue; } let id: quack.IComponentID = quack.ComponentID.fromObject({ type: c.settings.type, addressType: c.settings.addressType, address: c.settings.address, expansionLine: c.settings.expansionLine, muxLine: c.settings.muxLine }); let filteredInteractions = interactions.filter(interaction => { let isSource = false; let isSink = false; if (interaction.settings) { isSource = sameComponentID(interaction.settings.source, id); isSink = sameComponentID(interaction.settings.sink, id); } return isSource || isSink; }); // if (id.type === quack.ComponentType.COMPONENT_TYPE_GPS) { // hasGPS = true; // } if (isController(c.settings.type)) { controllerComponents.push( updatedComponent ? handleComponentChanged(updatedComponent) : loadDevice() } /> ); } else { sensorComponents.push( updatedComponent ? handleComponentChanged(updatedComponent) : loadDevice() } /> ); } } return (
  • Sensors {/* { //setShowErrors(!showErrors); dispatch({ key: "showErrors", value: !showErrors }); }} /> } /> */}
  • {sensorComponents.map((card, index) => ( {card} ))}
  • Controls
  • {controllerComponents.map((card, index) => ( {card} ))} {device.settings.mutations.length > 0 && ( <>
  • Calculated Measurements
  • {/* {device.settings.mutations.map((mut, i) => ( ))} */} )} {device.settings.product !== pond.DeviceProduct.DEVICE_PRODUCT_NONE && permissions.includes(pond.Permission.PERMISSION_WRITE) && ( <>
  • Setup
  • {isMobile ? ( <> {/* add grid card here for I2C detected components */} {(scannedAddresses?.i2c || scannedAddresses?.oneWire) && } {diagnosticComponents.map(comp => ( ))} ) : ( <> {(scannedAddresses?.i2c || scannedAddresses?.oneWire) && } {diagnosticComponents.length > 0 && {diagnosticComponents.map(comp => ( ))} } )} )}
    ); } return ( {componentCards()} ); }