/** * @file BobinkClient.h * @brief QML singleton managing the OPC UA connection lifecycle. * * Wraps QOpcUaClient into a declarative interface: LDS discovery, * endpoint selection, PKI, and certificate trust flow. * Single connection at a time (app-wide singleton). */ #ifndef BOBINKCLIENT_H #define BOBINKCLIENT_H #include #include #include #include #include #include #include #include #include class BobinkAuth; class BobinkClient : public QObject { Q_OBJECT QML_SINGLETON QML_NAMED_ELEMENT (Bobink) /* -- Connection -- */ Q_PROPERTY (bool connected READ connected NOTIFY connectedChanged) Q_PROPERTY (QString serverUrl READ serverUrl WRITE setServerUrl NOTIFY serverUrlChanged) Q_PROPERTY (BobinkAuth *auth READ auth WRITE setAuth NOTIFY authChanged) /* -- Discovery -- */ Q_PROPERTY (QString discoveryUrl READ discoveryUrl WRITE setDiscoveryUrl NOTIFY discoveryUrlChanged) Q_PROPERTY (bool discovering READ discovering NOTIFY discoveringChanged) Q_PROPERTY (QVariantList servers READ servers NOTIFY serversChanged) /* -- PKI -- */ Q_PROPERTY (QString pkiDir READ pkiDir WRITE setPkiDir NOTIFY pkiDirChanged) Q_PROPERTY ( QString certFile READ certFile WRITE setCertFile NOTIFY certFileChanged) Q_PROPERTY ( QString keyFile READ keyFile WRITE setKeyFile NOTIFY keyFileChanged) public: /// OPC UA message security mode (values match MessageSecurityMode). enum SecurityMode { SignAndEncrypt = 3, Sign = 2, None = 1, }; Q_ENUM (SecurityMode) /// OPC UA security policy for encryption algorithms. enum SecurityPolicy { Basic256Sha256, Aes128_Sha256_RsaOaep, Aes256_Sha256_RsaPss, }; Q_ENUM (SecurityPolicy) BobinkClient (QObject *parent = nullptr); /* -- Connection -- */ bool connected () const; QString serverUrl () const; void setServerUrl (const QString &url); BobinkAuth *auth () const; void setAuth (BobinkAuth *auth); /** @brief Discover endpoints, pick the most secure, connect. */ Q_INVOKABLE void connectToServer (); /** @brief Connect directly without endpoint discovery. */ Q_INVOKABLE void connectDirect (SecurityPolicy policy, SecurityMode mode); Q_INVOKABLE void disconnectFromServer (); /** @brief Accept the pending server certificate. */ Q_INVOKABLE void acceptCertificate (); /** @brief Reject the pending server certificate. */ Q_INVOKABLE void rejectCertificate (); /* -- Discovery -- */ QString discoveryUrl () const; void setDiscoveryUrl (const QString &url); bool discovering () const; const QList &discoveredServers () const; QVariantList servers () const; Q_INVOKABLE void startDiscovery (); Q_INVOKABLE void stopDiscovery (); /* -- PKI -- */ QString pkiDir () const; void setPkiDir (const QString &path); QString certFile () const; void setCertFile (const QString &path); QString keyFile () const; void setKeyFile (const QString &path); /** @brief Auto-detect cert/key from the PKI directory and apply. */ Q_INVOKABLE void autoDetectPki (); /** @brief Apply PKI dirs and cert/key. Call before connecting. */ Q_INVOKABLE void applyPki (); /* -- C++ only -- */ static BobinkClient *instance (); QOpcUaClient *opcuaClient () const; signals: /* -- Connection -- */ void connectedChanged (); void serverUrlChanged (); void authChanged (); /** * @brief Emitted when the server presents an untrusted cert. * * The connection blocks until acceptCertificate() or * rejectCertificate() is called (30 s timeout, auto-rejects). */ void certificateTrustRequested (const QString &certInfo); void connectionError (const QString &message); void statusMessage (const QString &message); /* -- Discovery -- */ void discoveryUrlChanged (); void discoveringChanged (); void serversChanged (); /* -- PKI -- */ void pkiDirChanged (); void certFileChanged (); void keyFileChanged (); private slots: /* -- Connection -- */ void handleStateChanged (QOpcUaClient::ClientState state); void handleEndpointsReceived (const QList &endpoints, QOpcUa::UaStatusCode statusCode, const QUrl &requestUrl); void handleConnectError (QOpcUaErrorState *errorState); /* -- Discovery -- */ void handleFindServersFinished ( const QList &servers, QOpcUa::UaStatusCode statusCode, const QUrl &requestUrl); void doDiscovery (); private: void setupClient (); /* -- Connection -- */ QOpcUaProvider *m_provider = nullptr; QOpcUaClient *m_client = nullptr; BobinkAuth *m_auth = nullptr; QString m_serverUrl; bool m_connected = false; QEventLoop *m_certLoop = nullptr; bool m_certAccepted = false; /* -- Discovery -- */ QString m_discoveryUrl; QTimer m_discoveryTimer; bool m_discovering = false; QList m_discoveredServers; QVariantList m_serversCache; /* -- PKI -- */ QString m_pkiDir; QString m_certFile; QString m_keyFile; static inline BobinkClient *s_instance = nullptr; }; #endif // BOBINKCLIENT_H