/** * @file BobinkNode.h * @brief QML component representing a single OPC UA node. * * Inherits QQuickItem so that monitoring is automatically tied to * visibility: when the item (or any ancestor) becomes invisible * (e.g. StackView navigates away, Loader unloads), the monitored * item is removed from the subscription. When visible again, * monitoring resumes. */ #ifndef BOBINKNODE_H #define BOBINKNODE_H #include #include #include #include class BobinkNode : public QQuickItem { Q_OBJECT QML_ELEMENT Q_PROPERTY (QString nodeId READ nodeId WRITE setNodeId NOTIFY nodeIdChanged) Q_PROPERTY (QVariant value READ value WRITE setValue NOTIFY valueChanged) Q_PROPERTY (NodeStatus status READ status NOTIFY statusChanged) Q_PROPERTY (QDateTime sourceTimestamp READ sourceTimestamp NOTIFY sourceTimestampChanged) Q_PROPERTY (QDateTime serverTimestamp READ serverTimestamp NOTIFY serverTimestampChanged) public: explicit BobinkNode (QQuickItem *parent = nullptr); ~BobinkNode () override; /// Simplified OPC UA status severity. enum NodeStatus { Good, Uncertain, Bad }; Q_ENUM (NodeStatus) QString nodeId () const; void setNodeId (const QString &id); QVariant value () const; /** Setting value writes to the server; the property updates when * the server confirms via the monitored data change. */ void setValue (const QVariant &value); NodeStatus status () const; QDateTime sourceTimestamp () const; QDateTime serverTimestamp () const; /** Read an attribute on demand (DisplayName, Description, DataType, …). * Result arrives asynchronously via attributeRead(). */ Q_INVOKABLE void readAttribute (const QString &attributeName); signals: void nodeIdChanged (); void valueChanged (); void statusChanged (); void sourceTimestampChanged (); void serverTimestampChanged (); /** Emitted when a readAttribute() call completes. */ void attributeRead (const QString &attributeName, const QVariant &value); /** Emitted when a write to the server fails. */ void writeError (const QString &message); protected: void componentComplete () override; void itemChange (ItemChange change, const ItemChangeData &data) override; private: void startMonitoring (); void stopMonitoring (); void handleDataChange (QOpcUa::NodeAttribute attr, const QVariant &val); void handleAttributeUpdated (QOpcUa::NodeAttribute attr, const QVariant &val); void handleAttributeWritten (QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode); void handleClientConnectedChanged (); static NodeStatus statusFromCode (QOpcUa::UaStatusCode code); static QOpcUa::NodeAttribute attributeFromName (const QString &name); QString m_nodeId; QVariant m_value; NodeStatus m_status = Bad; QDateTime m_sourceTimestamp; QDateTime m_serverTimestamp; QOpcUaNode *m_opcuaNode = nullptr; bool m_componentComplete = false; /** Tracks in-flight readAttribute() requests (enum → original name). */ QHash m_pendingReads; }; #endif // BOBINKNODE_H