diff --git a/components/DataPoint.qml b/components/DataPoint.qml new file mode 100644 index 000000000..a9d140372 --- /dev/null +++ b/components/DataPoint.qml @@ -0,0 +1,111 @@ +/* +** Copyright (C) 2022 Victron Energy B.V. +*/ + +import QtQuick +import Victron.VenusOS +import Victron.Veutil + +QtObject { + id: root + + // Valid 'source' values are: + // - dbus paths, e.g. 'com.victronenergy.blah/path/to/value' + // - as above, but with as full dbus uid, e.g. 'dbus/com.victronenergy.blah/path/to/value' + // - full mqtt uid, e.g. 'mqtt/blah/device-id/path/to/value' + // Note that dbus and mqtt uids should only be used when the application is using their + // respective backend connection types. If a mqtt uid is used on a dbus connection, for + // example, the DataPoint would not produce a valid value. + property string source + property var sourceObject + + readonly property var value: sourceObject ? sourceObject.value : undefined + readonly property bool valid: value !== undefined + + property bool hasMin + property bool hasMax + readonly property var min: hasMin && sourceObject ? sourceObject.min : undefined + readonly property var max: hasMax && sourceObject ? sourceObject.max : undefined + property bool invalidate: true + + property Component _dbusComponent : Component { + VeQuickItem { + uid: root.source.startsWith("dbus/") ? root.source : "dbus/" + root.source + invalidate: root.invalidate + + onInvalidateChanged: root.invalidate = invalidate + } + } + + property Component _mqttComponent : Component { + VeQuickItem { + readonly property string mqttUid: root.source.startsWith("mqtt/") ? root.source : _uidConverter.mqttUid + + readonly property SingleUidHelper _uidConverter: SingleUidHelper { + dbusUid: root.source.length === 0 || root.source.startsWith("mqtt/") + ? "" + : (root.source.startsWith("dbus/") ? root.source : "dbus/" + root.source) + } + + uid: mqttUid + invalidate: root.invalidate + + onInvalidateChanged: root.invalidate = invalidate + } + } + + property Component _mockComponent : Component { + QtObject { + id: root + + property string source: root.source + property var value: Global.mockDataSimulator ? Global.mockDataSimulator.mockDataValues[source] : undefined + property real min: 0 + property real max: 100 + property bool invalidate: root.invalidate + + function setValue(v) { + value = v + } + + onInvalidateChanged: root.invalidate = invalidate + } + } + + function setValue(v) { + if (sourceObject) { + sourceObject.setValue(v) + } else { + console.warn("Set value() failed, no sourceObject for source", source) + } + } + + function _reset() { + if (source.length === 0) { + return + } + if (sourceObject) { + sourceObject.destroy() + sourceObject = null + } + // TODO: maybe use incubateObject() in future if synchronous construction of sourceObject + // takes too long. + switch (BackendConnection.type) { + case BackendConnection.DBusSource: + sourceObject = _dbusComponent.createObject(root) + break + case BackendConnection.MqttSource: + sourceObject = _mqttComponent.createObject(root) + break + case BackendConnection.MockSource: + sourceObject = _mockComponent.createObject(root) + break + default: + console.warn("Unknown DataPoint source type:", BackendConnection.type) + break + } + } + + onSourceChanged: _reset() + Component.onCompleted: _reset() +} diff --git a/components/datapoints/DataPoint.qml b/components/datapoints/DataPoint.qml deleted file mode 100644 index 926a372f7..000000000 --- a/components/datapoints/DataPoint.qml +++ /dev/null @@ -1,182 +0,0 @@ -/* -** Copyright (C) 2022 Victron Energy B.V. -*/ - -import QtQuick -import Victron.VenusOS - -QtObject { - id: root - - // Valid 'source' values are: - // - dbus paths, e.g. 'com.victronenergy.blah/path/to/value' - // - as above, but with as full dbus uid, e.g. 'dbus/com.victronenergy.blah/path/to/value' - // - full mqtt uid, e.g. 'mqtt/blah/device-id/path/to/value' - // Note that dbus and mqtt uids should only be used when the application is using their - // respective backend connection types. If a mqtt uid is used on a dbus connection, for - // example, the DataPoint would not produce a valid value. - property string source - - property var sourceObject - readonly property int sourceType: BackendConnection.type - - readonly property var value: sourceObject ? sourceObject.value : undefined - readonly property bool valid: value !== undefined - - property bool hasMin - property bool hasMax - readonly property var min: hasMin && sourceObject ? sourceObject.min : undefined - readonly property var max: hasMax && sourceObject ? sourceObject.max : undefined - property bool invalidate: true - - property var _dbusImpl - property var _mqttImpl - property var _mqttUidHelper - property Component _mqttUidHelperComponent: Component { - QtObject { - readonly property string mqttUid: root.source.startsWith("mqtt/") ? root.source : _uidConverter.mqttUid - - readonly property SingleUidHelper _uidConverter: SingleUidHelper { - dbusUid: root.source.length === 0 || root.source.startsWith("mqtt/") - ? "" - : (root.source.startsWith("dbus/") ? root.source : "dbus/" + root.source) - } - } - } - - function setValue(v) { - if (sourceObject) { - sourceObject.setValue(v) - } else { - console.warn("Set value() failed, no sourceObject for source", source) - } - } - - function _dbusImplStatusChanged() { - if (!_dbusImpl) { - return - } - if (_dbusImpl.status === Component.Error) { - console.warn("Unable to load DataPointDBusImpl.qml", _dbusImpl.errorString()) - } else if (_dbusImpl.status === Component.Ready) { - _dbusImpl.statusChanged.disconnect(_dbusImplStatusChanged) - _createDBusImpl() - } - } - - function _mqttImplStatusChanged() { - if (!_mqttImpl) { - return - } - if (_mqttImpl.status === Component.Error) { - console.warn("Unable to load DataPointMqttImpl.qml", _mqttImpl.errorString()) - } else if (_mqttImpl.status === Component.Ready) { - _mqttImpl.statusChanged.disconnect(_mqttImplStatusChanged) - _createMqttImpl() - } - } - - function _createDBusImpl() { - if (!_dbusImpl || _dbusImpl.status !== Component.Ready) { - console.warn("Cannot create object from component", _dbusImpl ? _dbusImpl.url : "") - return - } - if (sourceObject) { - sourceObject.destroy() - sourceObject = null - } - sourceObject = _dbusImpl.createObject(root, - { - uid: Qt.binding(function() { return root.source.startsWith("dbus/") ? root.source : "dbus/" + root.source }), - invalidate: Qt.binding(function() { return root.invalidate }) - } - ) - if (!sourceObject) { - console.warn("Failed to create object from DataPointDBusImpl.qml", _dbusImpl.errorString()) - return - } - sourceObject.onInvalidateChanged.connect(function() { root.invalidate = sourceObject.invalidate }) - } - - function _createMqttImpl() { - if (!_mqttImpl || _mqttImpl.status !== Component.Ready) { - console.warn("Cannot create object from component", _mqttImpl ? _mqttImpl.url : "") - return - } - if (sourceObject) { - sourceObject.destroy() - sourceObject = null - } - sourceObject = _mqttImpl.createObject(root, - { - uid: Qt.binding(function() { return _mqttUidHelper.mqttUid }), - invalidate: Qt.binding(function() { return root.invalidate }) - } - ) - if (!sourceObject) { - console.warn("Failed to create object from DataPointMqttImpl.qml", _mqttImpl.errorString()) - return - } - sourceObject.onInvalidateChanged.connect(function() { root.invalidate = sourceObject.invalidate }) - } - - function _reset() { - if (source.length === 0) { - return - } - switch (sourceType) { - case BackendConnection.DBusSource: - if (!_dbusImpl) { - _dbusImpl = Qt.createComponent(Qt.resolvedUrl("DataPointDBusImpl.qml"), - Component.Asynchronous) - } - if (_dbusImpl.status === Component.Loading) { - _dbusImpl.statusChanged.connect(_dbusImplStatusChanged) - } else if (_dbusImpl.status === Component.Ready) { - _createDBusImpl() - } - break - case BackendConnection.MqttSource: - if (!_mqttImpl) { - _mqttUidHelper = _mqttUidHelperComponent.createObject(root) - _mqttImpl = Qt.createComponent(Qt.resolvedUrl("DataPointMqttImpl.qml"), - Component.Asynchronous) - } - if (_mqttImpl.status === Component.Loading) { - _mqttImpl.statusChanged.connect(_mqttImplStatusChanged) - } else if (_mqttImpl.status === Component.Ready) { - _createMqttImpl() - } - break - case BackendConnection.MockSource: - const comp = Qt.createComponent(Qt.resolvedUrl("DataPointMockImpl.qml")) - sourceObject = comp.createObject(root, - { - "source": root.source, - "invalidate": Qt.binding(function() { return root.invalidate }) - } - ) - sourceObject.onInvalidateChanged.connect(function() { root.invalidate = sourceObject.invalidate }) - break - default: - console.warn("Unknown DataPoint source type:", sourceType) - break - } - } - - onSourceChanged: _reset() - onSourceTypeChanged: _reset() - Component.onCompleted: _reset() - Component.onDestruction: { - // As a precaution, if asynchronous component creation finishes after object destruction, - // ensure the statusChanged() handlers are not called. - if (_dbusImpl) { - _dbusImpl.statusChanged.disconnect(_dbusImplStatusChanged) - _dbusImpl = null - } - if (_mqttImpl) { - _mqttImpl.statusChanged.disconnect(_mqttImplStatusChanged) - _mqttImpl = null - } - } -} diff --git a/components/datapoints/DataPointDBusImpl.qml b/components/datapoints/DataPointDBusImpl.qml deleted file mode 100644 index e93361726..000000000 --- a/components/datapoints/DataPointDBusImpl.qml +++ /dev/null @@ -1,11 +0,0 @@ -/* -** Copyright (C) 2022 Victron Energy B.V. -*/ - -import QtQuick -import Victron.VenusOS -import Victron.Veutil - -VeQuickItem { - id: root -} diff --git a/components/datapoints/DataPointMockImpl.qml b/components/datapoints/DataPointMockImpl.qml deleted file mode 100644 index 254ef84dd..000000000 --- a/components/datapoints/DataPointMockImpl.qml +++ /dev/null @@ -1,20 +0,0 @@ -/* -** Copyright (C) 2022 Victron Energy B.V. -*/ - -import QtQml -import Victron.VenusOS - -QtObject { - id: root - - property string source - property var value: Global.mockDataSimulator ? Global.mockDataSimulator.mockDataValues[source] : undefined - property real min: 0 - property real max: 100 - property bool invalidate: true - - function setValue(v) { - value = v - } -} diff --git a/components/datapoints/DataPointMqttImpl.qml b/components/datapoints/DataPointMqttImpl.qml deleted file mode 100644 index e93361726..000000000 --- a/components/datapoints/DataPointMqttImpl.qml +++ /dev/null @@ -1,11 +0,0 @@ -/* -** Copyright (C) 2022 Victron Energy B.V. -*/ - -import QtQuick -import Victron.VenusOS -import Victron.Veutil - -VeQuickItem { - id: root -} diff --git a/qml.qrc b/qml.qrc index b0553c661..912f0c102 100644 --- a/qml.qrc +++ b/qml.qrc @@ -14,6 +14,7 @@ components/CommonWords.qml components/ControlCard.qml components/ControlValue.qml + components/DataPoint.qml components/Device.qml components/ElectricalQuantityLabel.qml components/EnvironmentGauge.qml @@ -83,10 +84,6 @@ components/controls/SpinBox.qml components/controls/Switch.qml components/controls/TextField.qml - components/datapoints/DataPoint.qml - components/datapoints/DataPointDBusImpl.qml - components/datapoints/DataPointMockImpl.qml - components/datapoints/DataPointMqttImpl.qml components/dialogs/DateSelectorDialog.qml components/dialogs/DialogShadow.qml components/dialogs/VrmInstanceSwapDialog.qml diff --git a/src/main.cpp b/src/main.cpp index 7f445f31b..bccef6d03 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -343,6 +343,8 @@ void registerQmlTypes() "Victron.VenusOS", 2, 0, "QuantityTableSummary"); qmlRegisterType(QUrl(QStringLiteral("qrc:/components/QuantityTable.qml")), "Victron.VenusOS", 2, 0, "QuantityTable"); + qmlRegisterType(QUrl(QStringLiteral("qrc:/components/DataPoint.qml")), + "Victron.VenusOS", 2, 0, "DataPoint"); qmlRegisterType(QUrl(QStringLiteral("qrc:/components/ElectricalQuantityLabel.qml")), "Victron.VenusOS", 2, 0, "ElectricalQuantityLabel"); qmlRegisterType(QUrl(QStringLiteral("qrc:/components/FixedWidthLabel.qml")), @@ -402,10 +404,6 @@ void registerQmlTypes() qmlRegisterType(QUrl(QStringLiteral("qrc:/components/WeatherDetails.qml")), "Victron.VenusOS", 2, 0, "WeatherDetails"); - /* data points */ - qmlRegisterType(QUrl(QStringLiteral("qrc:/components/datapoints/DataPoint.qml")), - "Victron.VenusOS", 2, 0, "DataPoint"); - /* list items */ qmlRegisterType(QUrl(QStringLiteral("qrc:/components/listitems/ListLabel.qml")), "Victron.VenusOS", 2, 0, "ListLabel");