summaryrefslogtreecommitdiffstats
path: root/src/BobinkClient.h
blob: 7eb6c3c4dab21dbfc88c22f2d171d244266a17fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
 * @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 <QEventLoop>
#include <QObject>
#include <QOpcUaApplicationDescription>
#include <QOpcUaClient>
#include <QOpcUaEndpointDescription>
#include <QOpcUaErrorState>
#include <QOpcUaProvider>
#include <QQmlEngine>
#include <QTimer>

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<QOpcUaApplicationDescription> &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<QOpcUaEndpointDescription> &endpoints,
                           QOpcUa::UaStatusCode statusCode,
                           const QUrl &requestUrl);
  void handleConnectError (QOpcUaErrorState *errorState);
  void handleClientError (QOpcUaClient::ClientError error);

  /* -- Discovery -- */
  void handleFindServersFinished (
      const QList<QOpcUaApplicationDescription> &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<QOpcUaApplicationDescription> m_discoveredServers;
  QVariantList m_serversCache;

  /* -- PKI -- */
  QString m_pkiDir;
  QString m_certFile;
  QString m_keyFile;

  static inline BobinkClient *s_instance = nullptr;
};

#endif // BOBINKCLIENT_H