From 9f60dd3c9901005517d647d1d833e1b47ce5b544 Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Mon, 15 Jul 2024 17:11:34 +1000 Subject: [PATCH] Reduce the effect of noise on power flow direction visualisation We visualise the direction of power flow with an icon and a colour e.g. feed-in icon with green colour gauge, versus grid-draw icon with blue colour gauge. This commit ensures that we don't flicker between feed-in and grid-draw visualisation if the value is actually zero and the readings are just noise, by requiring that the power value exceeds a certain magnitude before we recognise it. Contributes to #1328 --- components/SideMultiGauge.qml | 10 ++++++---- components/ThreePhaseBarGauge.qml | 19 ++++++++++++------- components/ThreePhaseDisplay.qml | 7 +++++-- data/common/AcInputPhaseModel.qml | 2 +- data/common/ActiveAcInput.qml | 7 +++---- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/components/SideMultiGauge.qml b/components/SideMultiGauge.qml index c07a266e8..946d458d2 100644 --- a/components/SideMultiGauge.qml +++ b/components/SideMultiGauge.qml @@ -47,12 +47,14 @@ Item { delegate: Item { id: gaugeDelegate - readonly property bool feedingToGrid: root.inputMode - && (model.power || 0) < 0 - width: Theme.geometry_briefPage_edgeGauge_width height: root.height + // ignore noise values (close to zero) + readonly property real modelValue: Math.floor(Math.abs(model[root.phaseModelProperty] || 0)) < 1.0 ? 0.0 + : model[root.phaseModelProperty] + readonly property bool feedingToGrid: root.inputMode && modelValue < 0.0 + SideGauge { id: gauge animationEnabled: root.animationEnabled @@ -93,7 +95,7 @@ Item { // reverses the gauge direction so that negative and positive values have the same // value on the gauge, though negative values will be drawn in green. value: root.visible - ? (gaugeDelegate.feedingToGrid ? Math.abs(model[root.phaseModelProperty]) : model[root.phaseModelProperty]) + ? (gaugeDelegate.feedingToGrid ? Math.abs(gaugeDelegate.modelValue) : gaugeDelegate.modelValue) : root.minimumValue minimumValue: 0 maximumValue: Math.max(Math.abs(root.minimumValue), Math.abs(root.maximumValue)) diff --git a/components/ThreePhaseBarGauge.qml b/components/ThreePhaseBarGauge.qml index 0ad7f309d..15d30ccff 100644 --- a/components/ThreePhaseBarGauge.qml +++ b/components/ThreePhaseBarGauge.qml @@ -32,9 +32,16 @@ Flow { id: phaseRepeater delegate: Item { + id: phaseDelegate + width: root.orientation === Qt.Vertical ? root.width : root._delegateLength height: root.orientation === Qt.Vertical ? root._delegateLength : root.height + // ignore noise values (close to zero) + readonly property real modelValue: Math.floor(Math.abs(model[root.phaseModelProperty] || 0)) < 1.0 ? 0.0 + : model[root.phaseModelProperty] + readonly property bool feedingToGrid: root.inputMode && modelValue < 0.0 + Label { id: phaseLabel @@ -53,7 +60,7 @@ Flow { // reverses the gauge direction so that negative and positive values have the same // value on the gauge, though negative values will be drawn in green. value: root.visible - ? gaugeLoader.feedingToGrid ? Math.abs(model[root.phaseModelProperty]) : model[root.phaseModelProperty] + ? phaseDelegate.feedingToGrid ? Math.abs(phaseDelegate.modelValue) : phaseDelegate.modelValue : root.minimumValue minimumValue: 0 maximumValue: Math.max(Math.abs(root.minimumValue), Math.abs(root.maximumValue)) @@ -65,15 +72,13 @@ Flow { width: parent.width - (phaseLabel.visible ? phaseLabel.width : 0) height: parent.height sourceComponent: Global.isGxDevice ? cheapGauge : prettyGauge - readonly property bool feedingToGrid: root.inputMode - && (model.power || 0) < 0 } Component { id: cheapGauge CheapBarGauge { - foregroundColor: Theme.color_darkOk,gaugeLoader.feedingToGrid ? Theme.color_green : Theme.statusColorValue(valueStatus) - backgroundColor: Theme.color_darkOk,gaugeLoader.feedingToGrid ? Theme.color_darkGreen + foregroundColor: Theme.color_darkOk,phaseDelegate.feedingToGrid ? Theme.color_green : Theme.statusColorValue(valueStatus) + backgroundColor: Theme.color_darkOk,phaseDelegate.feedingToGrid ? Theme.color_darkGreen : root.inOverviewWidget && valueStatus === Theme.Ok ? Theme.color_darkishBlue : Theme.statusColorValue(valueStatus, true) valueType: root.valueType @@ -86,8 +91,8 @@ Flow { Component { id: prettyGauge BarGauge { - foregroundColor: Theme.color_darkOk,gaugeLoader.feedingToGrid ? Theme.color_green : Theme.statusColorValue(valueStatus) - backgroundColor: Theme.color_darkOk,gaugeLoader.feedingToGrid ? Theme.color_darkGreen + foregroundColor: Theme.color_darkOk,phaseDelegate.feedingToGrid ? Theme.color_green : Theme.statusColorValue(valueStatus) + backgroundColor: Theme.color_darkOk,phaseDelegate.feedingToGrid ? Theme.color_darkGreen : root.inOverviewWidget && valueStatus === Theme.Ok ? Theme.color_darkishBlue : Theme.statusColorValue(valueStatus, true) valueType: root.valueType diff --git a/components/ThreePhaseDisplay.qml b/components/ThreePhaseDisplay.qml index c95cdb35c..484c852aa 100644 --- a/components/ThreePhaseDisplay.qml +++ b/components/ThreePhaseDisplay.qml @@ -34,7 +34,10 @@ Flow { delegate: Item { id: phaseDelegate - readonly property bool feedingToGrid: root.inputMode && (model.power || 0) < 0 + // ignore noise values (close to zero) + readonly property real modelValue: Math.floor(Math.abs(model[root.phaseModelProperty] || 0)) < 1.0 ? 0.0 + : model[root.phaseModelProperty] + readonly property bool feedingToGrid: root.inputMode && modelValue < 0.0 readonly property int valueStatus: feedingToGrid ? Theme.Ok : root.phaseModelProperty ? Theme.getValueStatus(valueRange.valueAsRatio * 100, root.valueType) : Theme.Ok @@ -82,7 +85,7 @@ Flow { ValueRange { id: valueRange - value: root.phaseModelProperty ? model[root.phaseModelProperty] : 0 + value: phaseDelegate.modelValue minimumValue: root.minimumValue maximumValue: root.maximumValue } diff --git a/data/common/AcInputPhaseModel.qml b/data/common/AcInputPhaseModel.qml index 04f4f8cd1..f6599fc77 100644 --- a/data/common/AcInputPhaseModel.qml +++ b/data/common/AcInputPhaseModel.qml @@ -9,7 +9,7 @@ import Victron.VenusOS ListModel { id: root - property int totalPower + property real totalPower property real firstPhaseCurrent: count === 1 ? get(0).current : NaN readonly property Timer _timer: Timer { // timer needed so the display doesn't update too frequently diff --git a/data/common/ActiveAcInput.qml b/data/common/ActiveAcInput.qml index 7aab5563e..861f5c5a6 100644 --- a/data/common/ActiveAcInput.qml +++ b/data/common/ActiveAcInput.qml @@ -18,15 +18,14 @@ Device { readonly property int source: !!inputInfo ? inputInfo.source : VenusOS.AcInputs_InputSource_NotAvailable readonly property alias gensetStatusCode: _acInputService.gensetStatusCode - readonly property real power: _phases.totalPower + // clamp to zero any values with magnitude < 1 (assume it's noise) to avoid UI flicker. + readonly property real power: (Math.floor(Math.abs(_phases.totalPower)) < 1.0) ? 0.0 : _phases.totalPower readonly property real current: phases.count === 1 ? _phases.firstPhaseCurrent : NaN // multi-phase systems don't have a total current readonly property alias currentLimit: _acInputService.currentLimit readonly property alias phases: _phases // Phase measurements from com.victronenergy.system/Ac/ActiveIn/L<1|2|3> - readonly property AcInputPhaseModel _phases: AcInputPhaseModel { - id: _phases - } + readonly property AcInputPhaseModel _phases: AcInputPhaseModel { id: _phases } // Data from the input-specific service, e.g. com.victronenergy.vebus for a VE.Bus input, // or com.victronenergy.grid for grid parallel systems.