diff --git a/package-lock.json b/package-lock.json index a65ad1c..8184bae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10911,7 +10911,7 @@ }, "node_modules/protobuf-ts": { "version": "1.0.0", - "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#dc29e71ec9598556c1b7cdff4dc02f74ecbac20c", + "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#1b8dd6d6cdcef9c1cf5184db97ef9e06b3df0bed", "dependencies": { "protobufjs": "^6.8.8" } diff --git a/src/bin/BinCardV2.tsx b/src/bin/BinCardV2.tsx index 8f106ee..35fcd12 100644 --- a/src/bin/BinCardV2.tsx +++ b/src/bin/BinCardV2.tsx @@ -87,54 +87,48 @@ export default function BinCard(props: Props) { //loop through the cables in the bin status to prep for display bin.status.grainCables.forEach(cable => { let c: GrainCable = new GrainCable(); - //flip the cables so that for display node 1 is at the bottom + //flip the cables so that for display node 1 is at the bottom, let cableTemps = cloneDeep(cable.celcius); let cableHums = cloneDeep(cable.relativeHumidity); let cableEMC = cloneDeep(cable.moisture); + c.temperatures = cableTemps.reverse(); c.humidities = cableHums.reverse(); c.grainMoistures = cableEMC.reverse(); - c.topNode = cable.topNode; + c.excludedNodes = cable.excludedNodes; newGrainCables.push(c); - //determine the averages for temp/RH/EMC for the bin using all of the cables - let temps = cloneDeep(cable.celcius); - let hums = cloneDeep(cable.relativeHumidity); - let moistures = cloneDeep(cable.moisture); - - const spliceEnd = cable.topNode > 0 ? cable.topNode : temps.length; - let grainTemps = temps.splice(0, spliceEnd); - let grainHums = hums.splice(0, spliceEnd); - let grainEMCs = moistures.splice(0, spliceEnd); - allTemps.push(...grainTemps); - allHums.push(...grainHums); - allMoistures.push(...grainEMCs); + //filter out the excluded nodes and any nodes above the top node + let filteredNodes = c.filteredNodes(true) + + allTemps.push(...filteredNodes.temps); + allHums.push(...filteredNodes.humids); + allMoistures.push(...filteredNodes.moistures); //set the hottest and coldest nodes - if (coldestNode === undefined || Math.min(...grainTemps) < coldestNode.temp) { + if (coldestNode === undefined || Math.min(...filteredNodes.temps) < coldestNode.temp) { let lowTempIndex = - grainTemps.indexOf(Math.min(...grainTemps)) === -1 + filteredNodes.temps.indexOf(Math.min(...filteredNodes.temps)) === -1 ? 0 - : grainTemps.indexOf(Math.min(...grainTemps)); - + : filteredNodes.temps.indexOf(Math.min(...filteredNodes.temps)); coldestNode = { - temp: grainTemps[lowTempIndex], - humid: grainHums[lowTempIndex], - emc: grainEMCs[lowTempIndex] + temp: filteredNodes.temps[lowTempIndex], + humid: filteredNodes.humids[lowTempIndex], + emc: filteredNodes.moistures[lowTempIndex] }; } - if (hottestNode === undefined || Math.max(...grainTemps) > hottestNode.temp) { + if (hottestNode === undefined || Math.max(...filteredNodes.temps) > hottestNode.temp) { let highTempIndex = - grainTemps.indexOf(Math.max(...grainTemps)) === -1 + filteredNodes.temps.indexOf(Math.max(...filteredNodes.temps)) === -1 ? 0 - : grainTemps.indexOf(Math.max(...grainTemps)); + : filteredNodes.temps.indexOf(Math.max(...filteredNodes.temps)); hottestNode = { - temp: grainTemps[highTempIndex], - humid: grainHums[highTempIndex], - emc: grainEMCs[highTempIndex] + temp: filteredNodes.temps[highTempIndex], + humid: filteredNodes.humids[highTempIndex], + emc: filteredNodes.moistures[highTempIndex] }; } diff --git a/src/bin/BinSVGV2.tsx b/src/bin/BinSVGV2.tsx index bfd18db..c183d24 100644 --- a/src/bin/BinSVGV2.tsx +++ b/src/bin/BinSVGV2.tsx @@ -86,6 +86,9 @@ const useStyles = makeStyles((theme: Theme) => { cableNode: { fill: "#fff" }, + excludedNode: { + fill: "grey" + }, hotNode: { fill: "#fff", animation: "$hotNode 1.0s infinite" @@ -168,7 +171,8 @@ interface NodeData { //this could be either a box or a circle depending height: string | number borderRad: string | number nodeNumber: number - nodeTemp: number + nodeTemp: number, + excluded: boolean, } interface CircleData { @@ -427,6 +431,8 @@ export default function BinSVGV2(props: Props) { ); }; + //console.log(cables) + const cableGrainEstimate = ( points: GrainNodePoint[], cableSpacing: number, @@ -609,9 +615,31 @@ export default function BinSVGV2(props: Props) { //determine how high to draw the node on the cable const nodeY = nodeSpacingY * (index + 1) + minNodeY; - //if we are using the auto top nodes only show the heatmap for nodes at or below the top node - if (cable.topNode > 0) { - if (nodeNumber <= cable.topNode) { + //if we are using the auto top nodes only show the heatmap for nodes at or below the top node and it is not an ecluded node + if (!cable.excludedNodes.includes(nodeNumber - 1)){ + if (cable.topNode > 0) { + if (nodeNumber <= cable.topNode) { + // nodeHeatMap.push( + // + // ); + nodeHeatMapData.push({ + key: index, + xCenter: cablePos, + yCenter: nodeY, + xRadius: "120", + yRadius: "60", + fillColour: getGrainGradient(temp) + }) + } + } else if (nodeY > filledToY) { + //otherwise use the fill level of the bin // nodeHeatMap.push( // filledToY) { - //otherwise use the fill level of the bin - // nodeHeatMap.push( - // - // ); - nodeHeatMapData.push({ - key: index, - xCenter: cablePos, - yCenter: nodeY, - xRadius: "120", - yRadius: "60", - fillColour: getGrainGradient(temp) - }) } const displayWidth = 90; const displayHeight = 45; @@ -682,20 +690,9 @@ export default function BinSVGV2(props: Props) { width: displayWidth, borderRad: showTempHum ? cornerRad : "16", nodeNumber: nodeNumber, - nodeTemp: temp + nodeTemp: temp, + excluded: cable.excludedNodes.includes(nodeNumber - 1) }) - - // temps.push( - // - // {displayTemp.toFixed(1)}° - // - // ); tempData.push({ key: "node" + index + "temp", x: cablePos, @@ -704,16 +701,6 @@ export default function BinSVGV2(props: Props) { value: displayTemp.toFixed(1) +"°", filter: "drop-shadow( 4px 4px 10px rgba(0, 0, 0, .7))" }) - // hums.push( - // - // {ExtractMoisture(props.grainType, temp, cable.humidities[index] ?? 0).toFixed(1)}% - // - // ); humData.push({ key: "node" + index + "hum", x: cablePos, @@ -722,20 +709,6 @@ export default function BinSVGV2(props: Props) { value: ExtractMoisture(props.grainType, temp, cable.humidities[index] ?? 0).toFixed(1)+"%", }) !showTempHum && - // nodeClickers.push( - // { - // if (cable.key() !== "" && cableNodeClicked) { - // cableNodeClicked(cable, nodeNumber); - // } - // }} - // cx={cablePos} - // cy={nodeY} - // className={classes.clickableArea} - // r="50" - // /> - // ); nodeClickData.push({ key: "node"+index, centerX: cablePos, @@ -803,13 +776,13 @@ export default function BinSVGV2(props: Props) { width={e.width} height={e.height} rx={e.borderRad} - className={classes.cableNode}/> + className={e.excluded ? classes.excludedNode : classes.cableNode}/> ) } else { return ( - {e.nodeNumber === topNode && topNodeHat(cablePos, e.y)} - + {e.nodeNumber === topNode && !e.excluded && topNodeHat(cablePos, e.y)} + ) } diff --git a/src/bin/BinVisualizerV2.tsx b/src/bin/BinVisualizerV2.tsx index b18a7cd..0db4690 100644 --- a/src/bin/BinVisualizerV2.tsx +++ b/src/bin/BinVisualizerV2.tsx @@ -382,20 +382,25 @@ export default function BinVisualizer(props: Props) { //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(); + let filteredNodes = cable.filteredNodes(true) + temps.push(...filteredNodes.temps) + humids.push(...filteredNodes.humids) + emcs.push(...filteredNodes.moistures) + // 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; - } + // 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); @@ -432,6 +437,7 @@ export default function BinVisualizer(props: Props) { }; } }); + //set the average conditions avgConditions = { tempC: average(temps), diff --git a/src/component/ComponentChart.tsx b/src/component/ComponentChart.tsx index 5cfe487..db010a2 100644 --- a/src/component/ComponentChart.tsx +++ b/src/component/ComponentChart.tsx @@ -127,6 +127,7 @@ export default function ComponentChart(props: Props) { const [expandData, setExpandData] = useState(false); const [showOriginal, setShowOriginal] = useState(true); const [showAveraged, setShowAveraged] = useState(false); + const [showAllNodes, setShowAllNodes] = useState(false); // useEffect(() => { // if (prevComponent !== props.component) { diff --git a/src/component/ComponentDiagnostics.tsx b/src/component/ComponentDiagnostics.tsx index 1fa0d05..38f0cbe 100644 --- a/src/component/ComponentDiagnostics.tsx +++ b/src/component/ComponentDiagnostics.tsx @@ -97,6 +97,7 @@ export default function ComponentDiagnostics(props: Props) { undefined, undefined, undefined, + undefined, as ) .then(resp => { diff --git a/src/component/ExportDataSettings.tsx b/src/component/ExportDataSettings.tsx index c50e375..0a310ba 100644 --- a/src/component/ExportDataSettings.tsx +++ b/src/component/ExportDataSettings.tsx @@ -109,7 +109,8 @@ class ExportDataSettings extends React.Component { undefined, [device.id().toString()], ["device"], - undefined, + undefined, + undefined, as ) .then(resp => { diff --git a/src/models/GrainCable.ts b/src/models/GrainCable.ts index 445cebf..52d6b6b 100644 --- a/src/models/GrainCable.ts +++ b/src/models/GrainCable.ts @@ -10,10 +10,12 @@ export class GrainCable { public settings: pond.ComponentSettings = pond.ComponentSettings.create(); public status: pond.ComponentStatus = pond.ComponentStatus.create(); public lastReading: string = ""; + //bear in mind these three arrays are reversed due to the bin svg needing the first node on the bottom of the image and the svg being drawn from the top public temperatures: number[] = []; public humidities: number[] = []; public grainMoistures: number[] = []; public topNode: number = 0; + public excludedNodes: number[] = []; public static create(comp: Component): GrainCable { let my = new GrainCable(); @@ -58,6 +60,7 @@ export class GrainCable { my.grainMoistures = emc; my.lastReading = lastReading; my.topNode = comp.settings.grainFilledTo; + my.excludedNodes = comp.settings.excludedNodes; return my; } @@ -239,4 +242,41 @@ export class GrainCable { component.lastMeasurement = lastMeasurements; return component; } + + /** + * Filters out the excluded nodes and anything above the top node of the cable, note that the nodes are reversed during creation if a grain cable ie: node 1 is the end of the array + * + * @param unReversed will tell the function to reverse the array back to its original state + * @returns array of active node values + */ + public filteredNodes(unReversed?: boolean): {temps: number[], humids: number[], moistures: number[]} { + //make clones of the node values and reverse them back to their original state for index purposes + let temps: number [] = cloneDeep(this.temperatures).reverse() + let humids: number [] = cloneDeep(this.humidities).reverse() + let moistures: number[] = cloneDeep(this.grainMoistures).reverse() + + //filter out the excluded nodes + temps = this.filter(temps) + humids = this.filter(humids) + moistures = this.filter(moistures) + + if(!unReversed){ + temps.reverse() + humids.reverse() + moistures.reverse() + } + + return {temps, humids, moistures} + } + + private filter(values: number[]): number[] { + let filtered: number[] = [] + values.forEach((val, index) => { + //if the node is not excluded AND is either under the top node OR top node is not set + if(!this.excludedNodes.includes(index) && (index <= this.topNode || this.topNode === 0)){ + filtered.push(val) + } + }) + return filtered + } } diff --git a/src/pages/DeviceComponent.tsx b/src/pages/DeviceComponent.tsx index b7bd09a..ad92c3a 100644 --- a/src/pages/DeviceComponent.tsx +++ b/src/pages/DeviceComponent.tsx @@ -168,7 +168,8 @@ export default function DeviceComponent() { getContextKeys(), getContextTypes(), showErrors, - as + showErrors, + as, ) .then(resp => { if (resp.data.measurements) { @@ -431,6 +432,7 @@ export default function DeviceComponent() { getContextKeys(), getContextTypes(), showErrors, + showErrors, as ) .then(response => { @@ -627,6 +629,7 @@ export default function DeviceComponent() { getContextKeys(), getContextTypes(), showErrors, + showErrors, as) .then(resp => { let newRows = UnitMeasurement.convertMeasurements( diff --git a/src/providers/pond/componentAPI.tsx b/src/providers/pond/componentAPI.tsx index 0717c74..d073b83 100644 --- a/src/providers/pond/componentAPI.tsx +++ b/src/providers/pond/componentAPI.tsx @@ -98,6 +98,7 @@ export interface IComponentAPIContext { keys?: string[], types?: string[], showErrors?: boolean, + allNodes?: boolean, otherTeam?: string ) => Promise>; // possibly a deprecated function as it is not used anywhere @@ -120,6 +121,7 @@ export interface IComponentAPIContext { keys?: string[], types?: string[], showErrors?: boolean, + allNodes?: boolean, otherTeam?: string ) => Promise>; } @@ -480,6 +482,7 @@ export default function ComponentProvider(props: PropsWithChildren) { keys?: string[], types?: string[], showErrors: boolean = true, + allNodes: boolean = false, otherTeam?: string ) => { const view = otherTeam ? otherTeam : as @@ -495,7 +498,8 @@ export default function ComponentProvider(props: PropsWithChildren) { (view ? "&as=" + view : "") + (keys ? "&keys=" + keys : "&keys=" + [device]) + (types ? "&types=" + types : "&types=" + ["device"]) + - (showErrors ? "&showErrors=true" : "&showErrors=false"), + (showErrors ? "&showErrors=true" : "&showErrors=false")+ + (allNodes ? "&allNodes=true" : "&allNodes=false"), demo ); return new Promise>((resolve, reject) => { @@ -545,6 +549,7 @@ export default function ComponentProvider(props: PropsWithChildren) { keys?: string[], types?: string[], showErrors?: boolean, + allNodes: boolean = false, otherTeam?: string ) => { const view = otherTeam ? otherTeam : as @@ -565,7 +570,8 @@ export default function ComponentProvider(props: PropsWithChildren) { (view ? "&as=" + view : "") + (keys ? "&keys=" + keys : "&keys=" + [device]) + (types ? "&types=" + types : "&types=" + ["device"]) + - (showErrors ? "&showErrors=true" : "&showErrors=false") + (showErrors ? "&showErrors=true" : "&showErrors=false") + + (allNodes ? "&allNodes=true" : "&allNodes=false") ); return new Promise>((resolve,reject) => { get(url).then(resp => {