Skip to content

Commit

Permalink
Add Global.solarDevices
Browse files Browse the repository at this point in the history
Global.solarDevices.model contains all solarcharger devices, as well
as any multi and inverter devices with /NrOfTrackers > 0.

Add a mock inverter that has a tracker.

This prepares for the removal of Global.solarChargers.

Part of #1574
  • Loading branch information
blammit committed Jan 22, 2025
1 parent 4c6f84a commit bf94162
Show file tree
Hide file tree
Showing 7 changed files with 225 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ set (VENUS_QML_MODULE_SOURCES
components/QuantityTableSummary.qml
components/QuantityTable.qml
components/RsSystemAcIODisplay.qml
components/SolarDeviceModel.qml
components/SegmentedButtonRow.qml
components/SeparatorBar.qml
components/ServiceDeviceModel.qml
Expand Down Expand Up @@ -394,6 +395,7 @@ set (VENUS_QML_MODULE_SOURCES
data/Notifications.qml
data/PvInverters.qml
data/SolarChargers.qml
data/SolarDevices.qml
data/StartPageConfiguration.qml
data/System.qml
data/SystemLoad.qml
Expand Down Expand Up @@ -421,6 +423,7 @@ set (VENUS_QML_MODULE_SOURCES
data/common/PvMonitor.qml
data/common/SolarCharger.qml
data/common/SolarDailyHistory.qml
data/common/SolarDevice.qml
data/common/SolarHistory.qml
data/common/SolarHistoryErrorModel.qml
data/common/SolarTrackerDailyHistory.qml
Expand Down
2 changes: 2 additions & 0 deletions Global.qml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ QtObject {
property var notifications
property var pvInverters
property var solarChargers
property var solarDevices
property var system
property var systemSettings
property var tanks
Expand Down Expand Up @@ -107,6 +108,7 @@ QtObject {
notifications = null
pvInverters = null
solarChargers = null
solarDevices = null
system = null
systemSettings = null
tanks = null
Expand Down
50 changes: 50 additions & 0 deletions components/SolarDeviceModel.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
** Copyright (C) 2025 Victron Energy B.V.
** See LICENSE.txt for license information.
*/

import QtQuick
import Victron.VenusOS

DeviceModel {
id: root

modelId: "solarDevices"

readonly property Instantiator _serviceObjects: Instantiator {
model: ["solarcharger", "multi", "inverter"]
delegate: Instantiator {
id: serviceInstantiator

required property string modelData
readonly property ServiceModelLoader modelLoader: ServiceModelLoader {
serviceType: modelData
}

model: modelLoader.item
delegate: SolarDevice {
id: solarDevice

// solarcharger devices are always included in the model.
// For multi and inverter devices, only include them if /NrOfTrackers > 0.
readonly property bool hasSolarData: valid
&& (serviceInstantiator.modelData === "solarcharger"
|| (_nrOfTrackers.isValid && _nrOfTrackers.value > 0))

serviceUid: model.uid

onHasSolarDataChanged: {
if (hasSolarData) {
root.addDevice(solarDevice)
} else {
root.removeDevice(solarDevice.serviceUid)
}
}

readonly property VeQuickItem _nrOfTrackers: VeQuickItem {
uid: solarDevice.serviceUid + "/NrOfTrackers"
}
}
}
}
}
2 changes: 2 additions & 0 deletions data/DataManager.qml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Item {
&& !!Global.notifications
&& !!Global.pvInverters
&& !!Global.solarChargers
&& !!Global.solarDevices
&& !!Global.system
&& !!Global.systemSettings
&& !!Global.tanks
Expand Down Expand Up @@ -100,6 +101,7 @@ Item {
Notifications {}
PvInverters {}
SolarChargers {}
SolarDevices {}
System {}
SystemSettings {}
Tanks {}
Expand Down
33 changes: 33 additions & 0 deletions data/SolarDevices.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
** Copyright (C) 2025 Victron Energy B.V.
** See LICENSE.txt for license information.
*/

import QtQuick
import Victron.VenusOS

QtObject {
id: root

property SolarDeviceModel model: SolarDeviceModel { }

function formatTrackerName(trackerName, trackerIndex, totalTrackerCount, deviceName, format) {
if (format === VenusOS.TrackerName_WithDevicePrefix) {
if (trackerName.length > 0) {
return "%1-%2".arg(deviceName).arg(trackerName)
} else if (totalTrackerCount > 1) {
return "%1-#%2".arg(deviceName).arg(trackerIndex + 1)
} else {
return deviceName
}
} else { // format === VenusOS.TrackerName_NoDevicePrefix
if (trackerName.length > 0) {
return trackerName
} else {
return "#%1".arg(trackerIndex + 1)
}
}
}

Component.onCompleted: Global.solarDevices = root
}
110 changes: 110 additions & 0 deletions data/common/SolarDevice.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
** Copyright (C) 2025 Victron Energy B.V.
** See LICENSE.txt for license information.
*/

import QtQuick
import Victron.VenusOS

// SolarDevice is a base type for devices with PV trackers and history data.
// This includes the devices provided by solarcharger services, as well as multi and inverter
// services where /NrOfTrackers > 0.

Device {
id: root

readonly property ListModel trackers: ListModel {}
readonly property real power: _totalPower.isValid ? _totalPower.value : NaN
readonly property alias history: _history

// This is the overall error history.
// For the per-day error history, use dailyHistory(day).errorModel
readonly property alias errorModel: _history.errorModel

signal yieldUpdatedForDay(day: int, yieldKwh: real)

function dailyHistory(day) {
return _history.dailyHistory(day)
}

function dailyTrackerHistory(day, trackerIndex) {
return _history.dailyTrackerHistory(day, trackerIndex)
}

function trackerName(trackerIndex, format) {
const tracker = _trackerObjects.objectAt(trackerIndex)
const trackerName = tracker ? tracker.name || "" : ""
return Global.solarChargers.formatTrackerName(trackerName, trackerIndex, trackers.count, root.name, format)
}

//--- internal members below ---

readonly property VeQuickItem _totalPower: VeQuickItem {
uid: root.serviceUid + "/Yield/Power"
}

//--- history ---

readonly property SolarHistory _history: SolarHistory {
id: _history
bindPrefix: root.serviceUid
deviceName: root.name
trackerCount: root.trackers.count
}

//--- Solar trackers ---

readonly property VeQuickItem _trackerCount: VeQuickItem {
uid: root.serviceUid + "/NrOfTrackers"
}

readonly property Instantiator _trackerObjects: Instantiator {
model: _trackerCount.value || 1 // there is always at least one tracker, even if NrOfTrackers is not set
delegate: QtObject {
id: tracker

readonly property int modelIndex: model.index
readonly property real power: root.trackers.count <= 1 ? root.power : _power.value || 0
readonly property real voltage: _voltage.value || 0
readonly property real current: isNaN(power) || isNaN(voltage) || voltage === 0 ? NaN : power / voltage
readonly property string name: _name.value || ""

readonly property VeQuickItem _voltage: VeQuickItem {
uid: root.trackers.count <= 1
? root.serviceUid + "/Pv/V"
: root.serviceUid + "/Pv/" + model.index + "/V"
}

readonly property VeQuickItem _power: VeQuickItem {
uid: root.trackers.count === 1
? "" // only 1 tracker, use root.power instead (i.e. same as /Yield/Power)
: root.serviceUid + "/Pv/" + model.index + "/P"
}

readonly property VeQuickItem _name: VeQuickItem {
uid: root.serviceUid + "/Pv/" + model.index + "/Name"
}
}

onObjectAdded: function(index, object) {
let insertionIndex = root.trackers.count
for (let i = 0; i < root.trackers.count; ++i) {
const sortIndex = root.trackers.get(i).solarTracker.modelIndex
if (index < sortIndex) {
insertionIndex = i
break
}
}
root.trackers.insert(insertionIndex, {"solarTracker": object})
}

onObjectRemoved: function(index, object) {
for (let i = 0; i < root.trackers.count; ++i) {
if (root.trackers.get(i).solarTracker.serviceUid === object.serviceUid) {
root.trackers.remove(i)
break
}
}
}
}
}
26 changes: 25 additions & 1 deletion data/mock/InverterChargersImpl.qml
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,14 @@ QtObject {
Inverter {
id: inverter

function setMockValue(path, value) {
Global.mockDataSimulator.setMockValue(serviceUid + path, value)
}

function mockValue(path) {
return Global.mockDataSimulator.mockValue(serviceUid + path)
}

property Timer _trackerUpdates: Timer {
running: Global.mockDataSimulator.timersActive
repeat: true
Expand All @@ -410,6 +418,13 @@ QtObject {
acOutL1._reportedPower.setValue(Math.random() * 100)
dcVoltage.setValue(Math.random() * 10)
dcCurrent.setValue(Math.random())

if (nrOfTrackers.isValid) {
setMockValue("/Pv/V", Math.random() * 0.5)
setMockValue("/Yield/Power", 100 + (Math.random() * 100))
setMockValue(mockValue("/Yield/System") + (Math.random() * 100))
setMockValue(mockValue("/Yield/User") + (Math.random() * 100))
}
}
}

Expand All @@ -429,11 +444,15 @@ QtObject {
uid: inverter.serviceUid + "/IsInverterCharger"
}

property VeQuickItem nrOfTrackers: VeQuickItem {
uid: inverter.serviceUid + "/NrOfTrackers"
}

Component.onCompleted: {
_deviceInstance.setValue(deviceInstance)
_productName.setValue("Phoenix Inverter 12V 250VA 230V")
_customName.setValue("My Inverter " + deviceInstance)
_state.setValue(Math.floor(Math.random() * VenusOS.System_State_FaultCondition))
_state.setValue(Math.floor(Math.random() * VenusOS.System_State_PassThrough))
mode.setValue(VenusOS.Inverter_Mode_Off)
}
}
Expand All @@ -450,6 +469,11 @@ QtObject {
if (i == 0) {
inverterObj.isInverterCharger.setValue(1)
inverterObj._customName.setValue("Inverter with IsInverterCharger=1")
} else if (i == 1) {
inverterObj.nrOfTrackers.setValue(1)
inverterObj._customName.setValue("Inverter with solar")
inverterObj.setMockValue("/History/Overall/DaysAvailable", 1)
inverterObj.setMockValue("/History/Daily/0/Yield", Math.random())
}
Global.inverterChargers.inverterDevices.addDevice(inverterObj)
}
Expand Down

0 comments on commit bf94162

Please sign in to comment.