diff --git a/src/bin/BinCardV2.tsx b/src/bin/BinCardV2.tsx index 33b42e4..58bff74 100644 --- a/src/bin/BinCardV2.tsx +++ b/src/bin/BinCardV2.tsx @@ -419,16 +419,12 @@ export default function BinCard(props: Props) { Math.round(current * 35.239).toLocaleString() + "/" + Math.round(capacity * 35.239).toLocaleString() + - (lidarBushels ? "(" + Math.round(lidarBushels * 35.239).toLocaleString() + " est)" : "") + " L" ); } if (getGrainUnit() === pond.GrainUnit.GRAIN_UNIT_WEIGHT && bin.bushelsPerTonne() > 1) { return ( bin.grainTonnes().toLocaleString() + - (lidarBushels - ? "(" + Math.round(lidarBushels / bin.bushelsPerTonne()).toLocaleString() + " est)" - : "") + " mT " + bin.fillPercent() + "%" @@ -438,7 +434,6 @@ export default function BinCard(props: Props) { current.toLocaleString() + "/" + capacity.toLocaleString() + - (lidarBushels ? "(" + lidarBushels.toLocaleString() + " est)" : "") + " bu" ); }; @@ -469,10 +464,10 @@ export default function BinCard(props: Props) { const info = () => { const inv = bin.settings.inventory; - let bushelAmount = inv?.grainBushels; + let bushelAmount = bin.bushels(); let bushelCapacity = bin.settings.specs?.bushelCapacity; const empty = - !inv || inv.empty || !bushelCapacity || bushelCapacity <= 0 || inv.grainBushels <= 0; + !inv || inv.empty || !bushelCapacity || bushelCapacity <= 0 || bushelAmount <= 0; return ( diff --git a/src/bin/BinVisualizerV2.tsx b/src/bin/BinVisualizerV2.tsx index d380c63..8ab96b6 100644 --- a/src/bin/BinVisualizerV2.tsx +++ b/src/bin/BinVisualizerV2.tsx @@ -362,95 +362,95 @@ export default function BinVisualizer(props: Props) { let trendCables = cables; if (bin.key() !== "" && !loadingTrend) { setLoadingTrend(true); - // binAPI - // .getBinsTrendData([bin.key()], 7) - // .then(resp => { - // let binTrend = resp.data.trendData[bin.key()]; - // //the variables to use to build the average conditions - // let temps: number[] = []; - // let humids: number[] = []; - // let emcs: number[] = []; - // let tempTrends: number[] = []; - // let humidTrends: number[] = []; - // let emcTrends: number[] = []; + binAPI + .getBinsTrendData([bin.key()], 7) + .then(resp => { + let binTrend = resp.data.trendData[bin.key()]; + //the variables to use to build the average conditions + let temps: number[] = []; + let humids: number[] = []; + let emcs: number[] = []; + let tempTrends: number[] = []; + let humidTrends: number[] = []; + let emcTrends: number[] = []; - // let lowNodeConditions: GrainConditions | undefined = undefined; - // let highNodeConditions: GrainConditions | undefined = undefined; - // let avgConditions: GrainConditions | undefined = undefined; - // trendCables.forEach(cable => { - // //only use the values below the top node - // //because splice mutates the original array make a clone in the cables so the svg still has all of them for display - // //also reverse it so that the node 1 (end of the cable) is in the first position - // let tempClone = cloneDeep(cable.temperatures).reverse(); - // let humClone = cloneDeep(cable.humidities).reverse(); - // let emcClone = cloneDeep(cable.grainMoistures).reverse(); - // //add the cable data to the proper arrays - // if (cable.topNode > 0) { - // temps.push(...tempClone.splice(0, cable.topNode)); - // humids.push(...humClone.splice(0, cable.topNode)); - // emcs.push(...emcClone.splice(0, cable.topNode)); - // } else { - // //if the cable has no fill set (top node) then use all of the nodes - // temps = tempClone; - // humids = humClone; - // emcs = emcClone; - // } - // //add the trend data to the proper arrays if the top node is set - // let cableTrendData = binTrend.cableTrend[cable.key()]; - // tempTrends.push(cableTrendData.grainCelciusTrend); - // humidTrends.push(cableTrendData.grainHumidityTrend); - // emcTrends.push(cableTrendData.grainEmcTrend); + let lowNodeConditions: GrainConditions | undefined = undefined; + let highNodeConditions: GrainConditions | undefined = undefined; + let avgConditions: GrainConditions | undefined = undefined; + trendCables.forEach(cable => { + //only use the values below the top node + //because splice mutates the original array make a clone in the cables so the svg still has all of them for display + //also reverse it so that the node 1 (end of the cable) is in the first position + let tempClone = cloneDeep(cable.temperatures).reverse(); + let humClone = cloneDeep(cable.humidities).reverse(); + let emcClone = cloneDeep(cable.grainMoistures).reverse(); + //add the cable data to the proper arrays + if (cable.topNode > 0) { + temps.push(...tempClone.splice(0, cable.topNode)); + humids.push(...humClone.splice(0, cable.topNode)); + emcs.push(...emcClone.splice(0, cable.topNode)); + } else { + //if the cable has no fill set (top node) then use all of the nodes + temps = tempClone; + humids = humClone; + emcs = emcClone; + } + //add the trend data to the proper arrays if the top node is set + let cableTrendData = binTrend.cableTrend[cable.key()]; + tempTrends.push(cableTrendData.grainCelciusTrend); + humidTrends.push(cableTrendData.grainHumidityTrend); + emcTrends.push(cableTrendData.grainEmcTrend); - // //if the lowest temp for this cable is less than the temp in the low node conditions or the low node conditions are not set - // //use this cables lowest node and trend data in the condition - // if (!lowNodeConditions || Math.min(...temps) < lowNodeConditions.tempC) { - // //determine which node is the coldest so that the data displayed is for the same node - // let lowTempIndex = - // temps.indexOf(Math.min(...temps)) === -1 ? 0 : temps.indexOf(Math.min(...temps)); - // lowNodeConditions = { - // tempC: temps[lowTempIndex], - // humidity: humids[lowTempIndex], - // emc: emcs[lowTempIndex], - // tempCTrend: cableTrendData.coldNodeTrend?.tempTrend ?? 0, - // humidityTrend: cableTrendData.coldNodeTrend?.humidityTrend ?? 0, - // emcTrend: cableTrendData.coldNodeTrend?.emcTrend ?? 0 - // }; - // } + //if the lowest temp for this cable is less than the temp in the low node conditions or the low node conditions are not set + //use this cables lowest node and trend data in the condition + if (!lowNodeConditions || Math.min(...temps) < lowNodeConditions.tempC) { + //determine which node is the coldest so that the data displayed is for the same node + let lowTempIndex = + temps.indexOf(Math.min(...temps)) === -1 ? 0 : temps.indexOf(Math.min(...temps)); + lowNodeConditions = { + tempC: temps[lowTempIndex], + humidity: humids[lowTempIndex], + emc: emcs[lowTempIndex], + tempCTrend: cableTrendData.coldNodeTrend?.tempTrend ?? 0, + humidityTrend: cableTrendData.coldNodeTrend?.humidityTrend ?? 0, + emcTrend: cableTrendData.coldNodeTrend?.emcTrend ?? 0 + }; + } - // //do the same for the high node - // if (!highNodeConditions || cable.maxTemp() > highNodeConditions.tempC) { - // let highTempIndex = - // temps.indexOf(Math.max(...temps)) === -1 ? 0 : temps.indexOf(Math.max(...temps)); - // highNodeConditions = { - // tempC: temps[highTempIndex], - // humidity: humids[highTempIndex], - // emc: emcs[highTempIndex], - // tempCTrend: cableTrendData.hotNodeTrend?.tempTrend ?? 0, - // humidityTrend: cableTrendData.hotNodeTrend?.humidityTrend ?? 0, - // emcTrend: cableTrendData.hotNodeTrend?.emcTrend ?? 0 - // }; - // } - // }); - // //set the average conditions - // avgConditions = { - // tempC: average(temps), - // humidity: average(humids), - // emc: average(emcs), - // tempCTrend: average(tempTrends), - // humidityTrend: average(humidTrends), - // emcTrend: average(emcTrends) - // }; - // setAverageConditions(avgConditions); - // setLowNodeConditions(lowNodeConditions); - // setHighNodeConditions(highNodeConditions); - // }) - // .catch(err => { - // console.log(err); - // openSnack("Failed to retrieve trend data"); - // }) - // .finally(() => { - // setLoadingTrend(false); - // }); + //do the same for the high node + if (!highNodeConditions || cable.maxTemp() > highNodeConditions.tempC) { + let highTempIndex = + temps.indexOf(Math.max(...temps)) === -1 ? 0 : temps.indexOf(Math.max(...temps)); + highNodeConditions = { + tempC: temps[highTempIndex], + humidity: humids[highTempIndex], + emc: emcs[highTempIndex], + tempCTrend: cableTrendData.hotNodeTrend?.tempTrend ?? 0, + humidityTrend: cableTrendData.hotNodeTrend?.humidityTrend ?? 0, + emcTrend: cableTrendData.hotNodeTrend?.emcTrend ?? 0 + }; + } + }); + //set the average conditions + avgConditions = { + tempC: average(temps), + humidity: average(humids), + emc: average(emcs), + tempCTrend: average(tempTrends), + humidityTrend: average(humidTrends), + emcTrend: average(emcTrends) + }; + setAverageConditions(avgConditions); + setLowNodeConditions(lowNodeConditions); + setHighNodeConditions(highNodeConditions); + }) + .catch(err => { + console.log(err); + openSnack("Failed to retrieve trend data"); + }) + .finally(() => { + setLoadingTrend(false); + }); } } }, [binAPI, bin, cables, openSnack]); //eslint-disable-line react-hooks/exhaustive-deps @@ -485,7 +485,7 @@ export default function BinVisualizer(props: Props) { if (bin.settings.inventory && bin.settings.specs) { const isEmpty = bin.settings.inventory?.empty === true; const capacity = bin.settings.specs.bushelCapacity; - const grainBushels = bin.settings.inventory.grainBushels; + const grainBushels = bin.bushels(); setSliderCoulour("gold"); if (!capacity) { setFillPercentage(null); @@ -535,7 +535,7 @@ export default function BinVisualizer(props: Props) { const inventoryOverview = () => { const capacity = bin.settings.specs?.bushelCapacity ?? 0; - const grainBushels = bin.settings.inventory?.grainBushels ?? 0; + const grainBushels = bin.bushels(); const isEmpty = bin.settings.inventory?.empty === true || !grainBushels || grainBushels <= 0; const grainType = bin.settings.inventory?.grainType; const grainTypeName = isEmpty || !grainType ? "" : GrainDescriber(grainType).name; @@ -1481,9 +1481,7 @@ export default function BinVisualizer(props: Props) { onChange={(_, value) => { setFillPercentage(value as number); const capacity = bin.settings.specs ? bin.settings.specs.bushelCapacity : 0; - const current = bin.settings.inventory - ? bin.settings.inventory.grainBushels - : 0; + const current = bin.bushels() let grainAmount = ((value as number) / 100) * capacity; if (grainAmount < current) { diff --git a/src/group/GroupSettings.tsx b/src/group/GroupSettings.tsx index 2f15ef5..7c34568 100644 --- a/src/group/GroupSettings.tsx +++ b/src/group/GroupSettings.tsx @@ -7,6 +7,7 @@ import { DialogTitle, Divider, Grid2, + IconButton, lighten, List, ListItem, @@ -36,12 +37,23 @@ import { useGlobalState } from "providers"; import React, { useCallback, useEffect, useState } from "react"; import RemoveGroup from "./RemoveGroup"; import { pond } from "protobuf-ts/pond"; +import { Add, Remove } from "@mui/icons-material"; const useStyles = makeStyles((theme: Theme) => { return ({ deviceListContainer: { marginBottom: theme.spacing(1) }, + listTitle: { + fontSize: 20, + fontWeight: 650 + }, + listButton: { + cursor: "pointer", + "&:hover": { + backgroundColor: lighten(theme.palette.background.default, 0.25) + } + }, devicesList: { overflow: "auto", minHeight: "25vh", @@ -87,6 +99,7 @@ export default function GroupSettings(props: Props) { const deviceAPI = useDeviceAPI(); const snackbar = useSnackbar() const [devices, setDevices] = useState([]); + const [nonGroupDevices, setNonGroupDevices] = useState([]); const [group, setGroup] = useState(initialGroup ? Group.clone(initialGroup) : new Group()); const [isRemoveGroupOpen, setIsRemoveGroupOpen] = useState( mode === "remove" ? true : false @@ -109,13 +122,14 @@ export default function GroupSettings(props: Props) { const loadDevices = useCallback(() => { setLoadingDevices(true); + //this lists the devices for the user/team viewing deviceAPI .list(1000000, 0, "asc") .then((response: any) => { let rDevices: Device[] = response.data.devices ? response.data.devices.map((device: any) => Device.any(device)) : []; - + //remove duplicates from the options setDevices(rDevices); }) .catch((_error: any) => { @@ -124,6 +138,16 @@ export default function GroupSettings(props: Props) { .finally(() => setLoadingDevices(false)); }, [deviceAPI, as]); + useEffect(()=>{ + let ungrouped: Device[] = [] + devices.forEach(device => { + if(!groupDeviceNumbers.includes(device.id())){ + ungrouped.push(device) + } + }) + setNonGroupDevices(ungrouped) + },[groupDeviceNumbers, devices]) + useEffect(() => { if (prevInitialGroup !== initialGroup) { setGroup(initialGroup ? Group.clone(initialGroup) : new Group()); @@ -317,27 +341,58 @@ export default function GroupSettings(props: Props) { ) : ( - - {filterDevices(deviceSearch, devices, []) - .sort((a, b: Device) => a.name().localeCompare(b.name())) - .map(device => { - const label = `checkbox-list-secondary-label-${device.id()}`; - return ( - changeDevices(device.id())}> - - - changeDevices(device.id())} - checked={group.settings.devices.includes(device.id())} - inputProps={{ "aria-labelledby": label }} - disabled={!canEdit} - /> - - - ); - })} - + + {groupDevices && + + {filterDevices(deviceSearch, groupDevices, []) + .sort((a, b: Device) => a.name().localeCompare(b.name())) + .map(device => { + const label = `checkbox-list-secondary-label-${device.id()}`; + return ( + { + removeDevice(device.id()) + }}> + + + {/* changeDevices(device.id())} + checked={group.settings.devices.includes(device.id())} + inputProps={{ "aria-labelledby": label }} + disabled={!canEdit} + /> */} + - + + + ); + })} + + } + + {filterDevices(deviceSearch, nonGroupDevices, []) + .sort((a, b: Device) => a.name().localeCompare(b.name())) + .map(device => { + const label = `checkbox-list-secondary-label-${device.id()}`; + return ( + { + addDevice(device.id()) + }}> + + + {/* changeDevices(device.id())} + checked={group.settings.devices.includes(device.id())} + inputProps={{ "aria-labelledby": label }} + disabled={!canEdit} + /> */} + + + + + ); + })} + + )} ); @@ -383,16 +438,49 @@ export default function GroupSettings(props: Props) { ) : ( + + {groupDevices && + + Grouped Devices + + {filterDevices(deviceSearch, groupDevices, []) + .sort((a, b: Device) => a.name().localeCompare(b.name())) + .map(device => { + const label = `checkbox-list-secondary-label-${device.id()}`; + return ( + { + removeDevice(device.id()) + }}> + + + {/* changeDevices(device.id())} + checked={group.settings.devices.includes(device.id())} + inputProps={{ "aria-labelledby": label }} + disabled={!canEdit} + /> */} + + + + ); + })} + + + } + Devices You Can Add - {filterDevices(deviceSearch, devices, []) + {filterDevices(deviceSearch, nonGroupDevices, []) .sort((a, b: Device) => a.name().localeCompare(b.name())) .map(device => { const label = `checkbox-list-secondary-label-${device.id()}`; return ( - changeDevices(device.id())}> + { + addDevice(device.id()) + }}> - { if (checked) addDevice(device.id()); @@ -402,12 +490,14 @@ export default function GroupSettings(props: Props) { checked={groupDeviceNumbers.includes(device.id())} inputProps={{ "aria-labelledby": label }} disabled={!canEdit} - /> + /> */} + ); })} + )} ); diff --git a/src/maps/mapObjectControllers/AgMapController.tsx b/src/maps/mapObjectControllers/AgMapController.tsx index a810e21..9f23bf0 100644 --- a/src/maps/mapObjectControllers/AgMapController.tsx +++ b/src/maps/mapObjectControllers/AgMapController.tsx @@ -185,6 +185,8 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; const [measurement, setMeasurement] = useState(false); const [measurementData, setMeasurementData] = useState>(new Map()); const measurementRef = useRef(measurementData); + const zoomRef = useRef(15) + const fieldMarkerZoom = 15 //watches for changes to the viewing as and sets the load boolean to trigger a load useEffect(() => { @@ -591,12 +593,13 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; } let newFM = fieldMarkers.get(newKey); if (newFM) { - setCurrentView({ + let view = { latitude: newFM.lat(), longitude: newFM.long(), - zoom: 12, + zoom: currentView.zoom, transitionDuration: 1000 - }); + } + setCurrentView(view); } setObjectKey(newKey); setFMIndexKey(newID); @@ -621,12 +624,13 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; } let newFM = fieldMarkers.get(newKey); if (newFM) { - setCurrentView({ + let view = { latitude: newFM.lat(), longitude: newFM.long(), - zoom: 12, + zoom: currentView.zoom, transitionDuration: 1000 - }); + } + setCurrentView(view); } setObjectKey(newKey); setFMIndexKey(newID); @@ -728,13 +732,14 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; subtype: fm.type(), mini: true, clickFunc: (event, index) => { + console.log(zoomRef) clickDelay(); closeDrawers(); setIgnoreFeatures(true); setCurrentView({ latitude: fm.lat(), longitude: fm.long(), - zoom: 12, + zoom: zoomRef.current, transitionDuration: 0 //transitionInterpolator: new FlyToInterpolator() }); @@ -1115,14 +1120,16 @@ import { Result } from "@mapbox/mapbox-gl-geocoder"; let xOff = !mobileOffset ? -250 : undefined; let yOff = mobileOffset ? -150 : undefined; - setCurrentView({ + let view = { latitude: lat, longitude: long, zoom: zoom, transitionDuration: transDuration, xOffset: center ? 0 : xOff, yOffset: center ? 0 : yOff - }); + } + setCurrentView(view); + zoomRef.current = zoom }; const markerDialog = () => { diff --git a/src/models/Bin.ts b/src/models/Bin.ts index 6ad0b03..1157837 100644 --- a/src/models/Bin.ts +++ b/src/models/Bin.ts @@ -125,11 +125,23 @@ export class Bin { return colour; } + public bushels(): number { + let bushels = this.settings.inventory?.grainBushels || 0 + if (this.settings.inventory?.inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR){ + bushels = this.status.grainBushels + } + return bushels + } + public fillPercent(): number { let fill = 0; if (this.settings.inventory && this.settings.specs) { + let bushels = this.settings.inventory.grainBushels + if (this.settings.inventory.inventoryControl === pond.BinInventoryControl.BIN_INVENTORY_CONTROL_AUTOMATIC_LIDAR){ + bushels = this.status.grainBushels + } fill = Math.round( - (this.settings.inventory.grainBushels / this.settings.specs.bushelCapacity) * 100 + (bushels / this.settings.specs.bushelCapacity) * 100 ); } return fill; diff --git a/src/pages/Device.tsx b/src/pages/Device.tsx index dd634f5..702285e 100644 --- a/src/pages/Device.tsx +++ b/src/pages/Device.tsx @@ -96,6 +96,7 @@ export default function DevicePage() { } } }); + setDiagnosticComponents(diagComponents) setComponents(rComponents) let interactions: Interaction[] = []; resp.data.interactions.forEach((interaction: pond.Interaction) => { diff --git a/src/pbHelpers/ComponentType.tsx b/src/pbHelpers/ComponentType.tsx index 04e501c..39b0773 100644 --- a/src/pbHelpers/ComponentType.tsx +++ b/src/pbHelpers/ComponentType.tsx @@ -1,7 +1,7 @@ import { - GraphComponent, - GraphOrientation, - GraphPoint, + // GraphComponent, + // GraphOrientation, + // GraphPoint, GraphType, InteractionLine } from "common/Graph"; @@ -41,15 +41,15 @@ import { Weight, CapacitorCable } from "pbHelpers/ComponentTypes"; -import { multilineGrainCableData } from "pbHelpers/ComponentTypes/GrainCable"; -import { multilineCapCableData } from "./ComponentTypes/CapacitorCable"; +//import { multilineGrainCableData } from "pbHelpers/ComponentTypes/GrainCable"; +//import { multilineCapCableData } from "./ComponentTypes/CapacitorCable"; import { findInteractionsAsSource } from "pbHelpers/Interaction"; import { pond } from "protobuf-ts/pond"; import { quack } from "protobuf-ts/quack"; import { notNull, or } from "utils/types"; -import { emptyComponentId } from "./Component"; -import { multilinePressureCableData } from "./ComponentTypes/PressureCable"; +//import { emptyComponentId } from "./Component"; +//import { multilinePressureCableData } from "./ComponentTypes/PressureCable"; import { describeMeasurement, MeasurementDescriber } from "./MeasurementDescriber"; import { convertedUnitMeasurement, MeasurementsFor, UnitMeasurement } from "models/UnitMeasurement"; import { Sen5x } from "./ComponentTypes/Sen5x"; @@ -622,194 +622,194 @@ export function GetComponentIcon( } //TODO: deprecated function, new graphs are handled in measurementChart (using reCharts instead of victory) -export function getGraphProps( - componentType: quack.ComponentType, - subtype: number, - measurements: pond.Measurement[], - interactions: Interaction[], - overlays: pond.ComponentOverlays[], - orientation: GraphOrientation, - filters?: GraphFilters -): any { - const componentID: quack.IComponentID = quack.ComponentID.fromObject( - or(measurements[0].measurement, { - id: emptyComponentId() - }).id as any - ); - let graphProps = { - orientation: orientation - } as any; - let ext = extension(componentType, subtype); - ext.measurements.forEach((componentMeasurement: ComponentMeasurement, index: number) => { - let describer = describeMeasurement( - componentMeasurement.measurementType, - componentType, - subtype - ); - let range = - filters && filters.ranges && filters.ranges[index] ? filters.ranges[index] : undefined; - let selectedInteractions = - filters && filters.selectedInteractions ? filters.selectedInteractions : []; - let interactionLines = getInteractionLines( - componentType, - subtype, - componentMeasurement.measurementType, - componentID, - interactions, - selectedInteractions - ); - let selectedOverlays = filters?.selectedOverlays ?? []; - let overlayAreas = getOverlayAreas( - componentMeasurement.measurementType, - overlays, - selectedOverlays - ); - graphProps["graphComponent" + (index + 1).toString()] = { - label: componentMeasurement.label, - data: [], - tick: describer.unit(), - colour: componentMeasurement.colour, - type: componentMeasurement.graphType, - range: range, - isBoolean: - componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_BOOLEAN, - isPercent: - componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT || - (componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_ANALOG && - subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL), - interactionLines: interactionLines, - states: ext.states, - decimals: describer.decimals(), - overlays: overlayAreas - } as GraphComponent; - }); +// export function getGraphProps( +// componentType: quack.ComponentType, +// subtype: number, +// measurements: pond.Measurement[], +// interactions: Interaction[], +// overlays: pond.ComponentOverlays[], +// orientation: GraphOrientation, +// filters?: GraphFilters +// ): any { +// const componentID: quack.IComponentID = quack.ComponentID.fromObject( +// or(measurements[0].measurement, { +// id: emptyComponentId() +// }).id as any +// ); +// let graphProps = { +// orientation: orientation +// } as any; +// let ext = extension(componentType, subtype); +// ext.measurements.forEach((componentMeasurement: ComponentMeasurement, index: number) => { +// let describer = describeMeasurement( +// componentMeasurement.measurementType, +// componentType, +// subtype +// ); +// let range = +// filters && filters.ranges && filters.ranges[index] ? filters.ranges[index] : undefined; +// let selectedInteractions = +// filters && filters.selectedInteractions ? filters.selectedInteractions : []; +// let interactionLines = getInteractionLines( +// componentType, +// subtype, +// componentMeasurement.measurementType, +// componentID, +// interactions, +// selectedInteractions +// ); +// let selectedOverlays = filters?.selectedOverlays ?? []; +// let overlayAreas = getOverlayAreas( +// componentMeasurement.measurementType, +// overlays, +// selectedOverlays +// ); +// graphProps["graphComponent" + (index + 1).toString()] = { +// label: componentMeasurement.label, +// data: [], +// tick: describer.unit(), +// colour: componentMeasurement.colour, +// type: componentMeasurement.graphType, +// range: range, +// isBoolean: +// componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_BOOLEAN, +// isPercent: +// componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_PERCENT || +// (componentMeasurement.measurementType === quack.MeasurementType.MEASUREMENT_TYPE_ANALOG && +// subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL), +// interactionLines: interactionLines, +// states: ext.states, +// decimals: describer.decimals(), +// overlays: overlayAreas +// } as GraphComponent; +// }); - //for new cables add here to get the data for multiline - if (showMultilineCable(componentType, filters)) { - let cableData: any = {}; - if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) { - cableData = multilineGrainCableData(measurements, filters); - } else if (componentType === quack.ComponentType.COMPONENT_TYPE_PRESSURE_CABLE) { - cableData = multilinePressureCableData(measurements, filters); - } else if (componentType === quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE) { - cableData = multilineCapCableData(measurements, filters); - } - //will loop through the keys in cableData - //IMPORTANT keys must be data1, data2, etc in the multiline function in the component - for (let i = 1; i <= Object.keys(cableData).length; i++) { - let gc = "graphComponent" + i; - if (graphProps[gc]) { - graphProps[gc].data = cableData["data" + i]; - graphProps[gc].type = GraphType.MULTILINE; - } - } - } else { - measurements.forEach((reading: pond.Measurement, readIndex) => { - if (notNull(reading) && notNull(reading.timestamp) && notNull(reading.measurement)) { - let date = new Date(Date.parse(reading.timestamp)); - for (let index = 0; index < ext.measurements.length; index++) { - let componentMeasurement: ComponentMeasurement = ext.measurements[index]; - if (componentMeasurement.isErrorMeasurement(reading.measurement)) { - break; - } - let dataPoint = null; - switch (componentMeasurement.graphType) { - case GraphType.AREA: - let avg = componentMeasurement.extract( - or(reading.measurement, {} as quack.Measurement), - filters - ); - if (avg && avg.low !== null && avg.high !== null) { - dataPoint = { - x: date, - y: avg.low, - y0: avg.high - } as GraphPoint; - } - break; - case GraphType.SCATTER: - let scatter = componentMeasurement.extract( - or(reading.measurement, {} as quack.Measurement), - filters - ); - if (scatter && scatter.value !== undefined && scatter.value !== null) { - dataPoint = { - x: date, - y: Number(scatter.value), - bubble: or(scatter.bubble, 0) - } as GraphPoint; - } - break; - case GraphType.RADAR: - if (subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_WIND_DIRECTION) { - let value = componentMeasurement.extract( - or(reading.measurement, {} as quack.Measurement), - filters - ); +// //for new cables add here to get the data for multiline +// if (showMultilineCable(componentType, filters)) { +// let cableData: any = {}; +// if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) { +// cableData = multilineGrainCableData(measurements, filters); +// } else if (componentType === quack.ComponentType.COMPONENT_TYPE_PRESSURE_CABLE) { +// cableData = multilinePressureCableData(measurements, filters); +// } else if (componentType === quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE) { +// cableData = multilineCapCableData(measurements, filters); +// } +// //will loop through the keys in cableData +// //IMPORTANT keys must be data1, data2, etc in the multiline function in the component +// for (let i = 1; i <= Object.keys(cableData).length; i++) { +// let gc = "graphComponent" + i; +// if (graphProps[gc]) { +// graphProps[gc].data = cableData["data" + i]; +// graphProps[gc].type = GraphType.MULTILINE; +// } +// } +// } else { +// measurements.forEach((reading: pond.Measurement, readIndex) => { +// if (notNull(reading) && notNull(reading.timestamp) && notNull(reading.measurement)) { +// let date = new Date(Date.parse(reading.timestamp)); +// for (let index = 0; index < ext.measurements.length; index++) { +// let componentMeasurement: ComponentMeasurement = ext.measurements[index]; +// if (componentMeasurement.isErrorMeasurement(reading.measurement)) { +// break; +// } +// let dataPoint = null; +// switch (componentMeasurement.graphType) { +// case GraphType.AREA: +// let avg = componentMeasurement.extract( +// or(reading.measurement, {} as quack.Measurement), +// filters +// ); +// if (avg && avg.low !== null && avg.high !== null) { +// dataPoint = { +// x: date, +// y: avg.low, +// y0: avg.high +// } as GraphPoint; +// } +// break; +// case GraphType.SCATTER: +// let scatter = componentMeasurement.extract( +// or(reading.measurement, {} as quack.Measurement), +// filters +// ); +// if (scatter && scatter.value !== undefined && scatter.value !== null) { +// dataPoint = { +// x: date, +// y: Number(scatter.value), +// bubble: or(scatter.bubble, 0) +// } as GraphPoint; +// } +// break; +// case GraphType.RADAR: +// if (subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_WIND_DIRECTION) { +// let value = componentMeasurement.extract( +// or(reading.measurement, {} as quack.Measurement), +// filters +// ); - dataPoint = { - x: getWindDirection(value).dString, - y: getWindDirection(value).direction - }; - } - break; - default: - //linear and bar - let value = componentMeasurement.extract( - or(reading.measurement, {} as quack.Measurement), - filters - ); +// dataPoint = { +// x: getWindDirection(value).dString, +// y: getWindDirection(value).direction +// }; +// } +// break; +// default: +// //linear and bar +// let value = componentMeasurement.extract( +// or(reading.measurement, {} as quack.Measurement), +// filters +// ); - if (value !== undefined && value !== null) { - if (componentType === quack.ComponentType.COMPONENT_TYPE_ANALOG_INPUT) { - if (subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL) { - if (value > 100) { - value = 0; - } - } - } - if (componentType === quack.ComponentType.COMPONENT_TYPE_EDGE_TRIGGERED) { - if (subtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_WIND_SPEED) { - if (readIndex < measurements.length - 1) { - //do math - //get the time of both the current reading and the reading before it - let currentTime = new Date( - Date.parse(measurements[readIndex].timestamp) - ).valueOf(); - let prevTime = new Date( - Date.parse(measurements[readIndex + 1].timestamp) - ).valueOf(); +// if (value !== undefined && value !== null) { +// if (componentType === quack.ComponentType.COMPONENT_TYPE_ANALOG_INPUT) { +// if (subtype === quack.AnalogInputSubtype.ANALOG_INPUT_SUBTYPE_FUEL) { +// if (value > 100) { +// value = 0; +// } +// } +// } +// if (componentType === quack.ComponentType.COMPONENT_TYPE_EDGE_TRIGGERED) { +// if (subtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_WIND_SPEED) { +// if (readIndex < measurements.length - 1) { +// //do math +// //get the time of both the current reading and the reading before it +// let currentTime = new Date( +// Date.parse(measurements[readIndex].timestamp) +// ).valueOf(); +// let prevTime = new Date( +// Date.parse(measurements[readIndex + 1].timestamp) +// ).valueOf(); - //get the difference in seconds between the two - let deltaTime = (currentTime - prevTime) / 1000; +// //get the difference in seconds between the two +// let deltaTime = (currentTime - prevTime) / 1000; - //divide the ticks by the time between intervals for the average ticks per second and multiply - //by 2.4 to convert to km/h - value = (Number(value) / deltaTime) * 2.4; - } else { - value = 0; - } - } - if (subtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_RAIN) { - value = Math.round(value * 0.2794); - } - } - dataPoint = { - x: date, - y: Number(value) - } as GraphPoint; - } - break; - } - if (dataPoint !== null) { - graphProps["graphComponent" + (index + 1).toString()].data.push(dataPoint); - } - } - } - }); - } - return graphProps; -} +// //divide the ticks by the time between intervals for the average ticks per second and multiply +// //by 2.4 to convert to km/h +// value = (Number(value) / deltaTime) * 2.4; +// } else { +// value = 0; +// } +// } +// if (subtype === quack.EdgeTriggeredSubtype.EDGE_TRIGGERED_SUBTYPE_RAIN) { +// value = Math.round(value * 0.2794); +// } +// } +// dataPoint = { +// x: date, +// y: Number(value) +// } as GraphPoint; +// } +// break; +// } +// if (dataPoint !== null) { +// graphProps["graphComponent" + (index + 1).toString()].data.push(dataPoint); +// } +// } +// } +// }); +// } +// return graphProps; +// } function getWindDirection(value: number) { //determine the general direction based on the value diff --git a/src/pbHelpers/ComponentTypes/GrainCable.ts b/src/pbHelpers/ComponentTypes/GrainCable.ts index 286b2d9..04d8738 100644 --- a/src/pbHelpers/ComponentTypes/GrainCable.ts +++ b/src/pbHelpers/ComponentTypes/GrainCable.ts @@ -4,8 +4,8 @@ import GrainCableLightIcon from "assets/components/grainCableLight.png"; import TemperatureHumidityDarkIcon from "assets/components/temperatureHumidityDark.png"; import TemperatureHumidityLightIcon from "assets/components/temperatureHumidityLight.png"; import { ExtractMoisture, grainName } from "grain"; -import { GraphPoint } from "common/Graph"; -import moment from "moment"; +//import { GraphPoint } from "common/Graph"; +//import moment from "moment"; import { AreaChartData, ComponentMeasurement, @@ -418,38 +418,38 @@ export function binSplitAt(filters?: GraphFilters): number { } //TODO: deprecated function with new measurements -export function multilineGrainCableData( - measurements: Array, - filters?: GraphFilters -) { - let temperature: Array> = []; - let humidity: Array> = []; //humidity is not being used by the graphs - let moisture: Array> = []; - measurements.forEach((measurement: pond.Measurement, i) => { - let nodes = extractNodes(measurement.measurement); - let selectedNodes = filters ? or(filters.selectedNodes, []) : []; - for (let j = 0; j < selectedNodes.length; j++) { - if (i === 0) { - temperature[j] = []; - humidity[j] = []; - moisture[j] = []; - } - let nodeIndex = selectedNodes[j]; - if (nodeIndex < nodes.length) { - let node = nodes[nodeIndex]; - let ts = moment(measurement.timestamp); - temperature[j].push({ x: ts, y: node.temperature }); - humidity[j].push({ x: ts, y: node.humidity }); - let grain = - isBinSplit(filters) && nodeIndex >= binSplitAt(filters) - ? pond.Grain.GRAIN_NONE - : or(filters, { grainType: pond.Grain.GRAIN_NONE }).grainType; - moisture[j].push({ - x: ts, - y: ExtractMoisture(grain, node.temperature, node.humidity) - }); - } - } - }); - return { data1: temperature, data2: moisture, data3: humidity }; -} +// export function multilineGrainCableData( +// measurements: Array, +// filters?: GraphFilters +// ) { +// let temperature: Array> = []; +// let humidity: Array> = []; //humidity is not being used by the graphs +// let moisture: Array> = []; +// measurements.forEach((measurement: pond.Measurement, i) => { +// let nodes = extractNodes(measurement.measurement); +// let selectedNodes = filters ? or(filters.selectedNodes, []) : []; +// for (let j = 0; j < selectedNodes.length; j++) { +// if (i === 0) { +// temperature[j] = []; +// humidity[j] = []; +// moisture[j] = []; +// } +// let nodeIndex = selectedNodes[j]; +// if (nodeIndex < nodes.length) { +// let node = nodes[nodeIndex]; +// let ts = moment(measurement.timestamp); +// temperature[j].push({ x: ts, y: node.temperature }); +// humidity[j].push({ x: ts, y: node.humidity }); +// let grain = +// isBinSplit(filters) && nodeIndex >= binSplitAt(filters) +// ? pond.Grain.GRAIN_NONE +// : or(filters, { grainType: pond.Grain.GRAIN_NONE }).grainType; +// moisture[j].push({ +// x: ts, +// y: ExtractMoisture(grain, node.temperature, node.humidity) +// }); +// } +// } +// }); +// return { data1: temperature, data2: moisture, data3: humidity }; +// } diff --git a/src/pbHelpers/MeasurementDescriber.ts b/src/pbHelpers/MeasurementDescriber.ts index fd4716b..ee45c8f 100644 --- a/src/pbHelpers/MeasurementDescriber.ts +++ b/src/pbHelpers/MeasurementDescriber.ts @@ -110,6 +110,10 @@ export class MeasurementDescriber { } if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) { this.details.graph = GraphType.AREA; + if(componentSubtype === quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG){ + this.details.label = "Cable ID" + this.details.unit = "" + } } if (componentType === quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT) { this.details.path = "vpd.celciusTimes10"; @@ -194,6 +198,10 @@ export class MeasurementDescriber { if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) { this.details.label = "Humidity"; this.details.graph = GraphType.AREA; + if(componentSubtype === quack.GrainCableSubtype.GRAIN_CABLE_SUBTYPE_OPI_DIAG){ + this.details.label = "Cable Type" + this.details.unit = "" + } // can be used for testing node splitting on a cable if you dont have a component that uses it // this.details.nodeDetails = { // colours: ["white", "black"], diff --git a/src/providers/pond/binAPI.tsx b/src/providers/pond/binAPI.tsx index ea626c1..5e9f51a 100644 --- a/src/providers/pond/binAPI.tsx +++ b/src/providers/pond/binAPI.tsx @@ -126,6 +126,10 @@ export interface IBinAPIContext { showErrors?: boolean, otherTeam?: string ) => Promise>; + getBinsTrendData: ( + bins: string[], + days: number + ) => Promise>; } export const BinAPIContext = createContext({} as IBinAPIContext); @@ -629,6 +633,12 @@ export default function BinProvider(props: PropsWithChildren) { }) }; + const getBinsTrendData = (bins: string[], days: number) => { + return get( + pondURL("/binTrends?bins=" + bins.toString() + "&days=" + days + (as ? "&as=" + as : "")) + ); + }; + return ( ) { listHistoryBetween, listBinPrefs, updateTopNodes, - listBinMeasurements + listBinMeasurements, + getBinsTrendData }}> {children} diff --git a/src/providers/pond/groupAPI.tsx b/src/providers/pond/groupAPI.tsx index ef0ba34..45d140c 100644 --- a/src/providers/pond/groupAPI.tsx +++ b/src/providers/pond/groupAPI.tsx @@ -88,7 +88,7 @@ export default function GroupProvider(props: PropsWithChildren) { const addDevice = (group: number, device: number) => { let url = "/groups/" + group + "/devices/" + device + "/add"; - if (as) url = pondURL(url + as) + if (as) url = url + "?as=" + as return new Promise((resolve, reject) => { post(pondURL(url), group).then(resp => { return resolve(resp) @@ -100,7 +100,7 @@ export default function GroupProvider(props: PropsWithChildren) { const removeDevice = (group: number, device: number) => { let url = "/groups/" + group + "/devices/" + device + "/remove"; - if (as) url = pondURL(url + as) + if (as) url = url + "?as=" + as return new Promise((resolve, reject) => { post(pondURL(url), group).then(resp => { return resolve(resp)