aboutsummaryrefslogtreecommitdiffstats
path: root/docs/guide-qml.md
blob: 1bbe0f5be795001a10b875c5a9952286733d57b6 (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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# Guide QML Bobink

Ce guide explique comment utiliser les composants QML fournis par la bibliothèque Bobink pour se connecter à un serveur OPC UA, lire des valeurs en temps réel et écrire des commandes -- le tout directement depuis vos fichiers QML, sans toucher au C++.

Une application de démonstration complète est disponible dans le dossier `demo/` du projet. Les fichiers `Main.qml` et `NodePage.qml` illustrent toutes les fonctionnalités décrites ici. N'hésitez pas à vous en servir comme point de départ.

## Importer le module

En haut de chaque fichier QML où vous utilisez Bobink :

```qml
import Bobink
```

Cet import rend disponibles trois éléments :

| Nom | Rôle |
|-----|------|
| `Bobink` | Singleton qui gère la connexion au serveur |
| `OpcUaAuth` | Configuration de l'authentification |
| `OpcUaMonitoredNode` | Surveillance et écriture d'un noeud OPC UA |

## Connexion au serveur

### 1. Configurer l'adresse

```qml
Bobink.serverUrl = "opc.tcp://192.168.1.10:4840"
```

### 2. Choisir l'authentification

Créez un objet `OpcUaAuth` et assignez-le à `Bobink.auth` avant de vous connecter.

**Connexion anonyme** (aucun identifiant requis) :

```qml
OpcUaAuth {
    id: auth
    mode: OpcUaAuth.Anonymous
}
```

**Connexion avec identifiants** :

```qml
OpcUaAuth {
    id: auth
    mode: OpcUaAuth.UserPass
    username: "operateur"
    password: "motdepasse"
}
```

**Connexion par certificat** :

```qml
OpcUaAuth {
    id: auth
    mode: OpcUaAuth.Certificate
    certPath: "/chemin/vers/certificat.der"
    keyPath: "/chemin/vers/cle.pem"
}
```

Ensuite, assignez et connectez :

```qml
Bobink.auth = auth
Bobink.connectToServer()
```

### 3. Réagir aux événements de connexion

Utilisez un bloc `Connections` pour suivre l'état de la connexion :

```qml
Connections {
    target: Bobink

    function onConnectedChanged() {
        if (Bobink.connected)
            console.log("Connecté au serveur")
        else
            console.log("Déconnecté")
    }

    function onConnectionError(message) {
        console.log("Erreur :", message)
    }
}
```

### 4. Certificat du serveur inconnu

Si le serveur présente un certificat que le client ne connaît pas, le signal `certificateTrustRequested` est émis. Vous pouvez afficher une fenêtre de confirmation :

```qml
Connections {
    target: Bobink
    function onCertificateTrustRequested(certInfo) {
        // Afficher certInfo à l'utilisateur, puis :
        Bobink.acceptCertificate()   // pour accepter
        // ou
        Bobink.rejectCertificate()   // pour refuser
    }
}
```

### 5. Déconnexion

```qml
Bobink.disconnectFromServer()
```

## Surveiller un noeud (lecture en temps réel)

`OpcUaMonitoredNode` est un composant non visuel (comme `Timer` ou `Connections`). Placez-le à l'intérieur de n'importe quel élément QML. Il crée un abonnement OPC UA sur le noeud et reçoit les nouvelles valeurs automatiquement.

```qml
OpcUaMonitoredNode {
    id: temperature
    nodeId: "ns=2;s=Temperature"
    monitored: true
}

Text {
    text: "Température : " + temperature.value
}
```

### Propriétés principales

| Propriété | Type | Description |
|-----------|------|-------------|
| `nodeId` | string | Identifiant du noeud OPC UA (ex. `"ns=2;s=Temperature"`) |
| `monitored` | bool | Active ou désactive la surveillance. Par défaut : `true` |
| `publishingInterval` | double | Intervalle de mise à jour en millisecondes. Par défaut : 250 |
| `value` | variant | Valeur actuelle du noeud (lecture seule, mise à jour automatique) |
| `writable` | bool | `true` si le noeud autorise l'écriture (lecture seule) |
| `info` | objet | Métadonnées du noeud (voir plus bas) |

### Activer/désactiver la surveillance

Il est conseillé de désactiver la surveillance quand le composant n'est pas visible, pour économiser les ressources réseau. Par exemple, avec un `StackView` :

```qml
OpcUaMonitoredNode {
    nodeId: "ns=2;s=Temperature"
    monitored: maPage.StackView.status === StackView.Active
}
```

Ainsi le noeud n'est surveillé que lorsque la page est affichée.

### Changer l'intervalle de mise à jour

```qml
OpcUaMonitoredNode {
    nodeId: "ns=2;s=Temperature"
    publishingInterval: 1000  // une fois par seconde
}
```

**Regroupement des abonnements :** côté serveur, les noeuds qui partagent le même `publishingInterval` sont regroupés dans un même abonnement (subscription) OPC UA. Moins il y a d'abonnements distincts, moins le serveur et le réseau sont sollicités. Essayez donc de donner le même intervalle à autant de noeuds que possible. Par exemple, si la plupart de vos noeuds n'ont pas besoin d'une mise à jour plus rapide que 500 ms, laissez-les tous à 500 ms plutôt que de varier les intervalles sans raison.

Pour appliquer un changement d'intervalle en cours d'exécution, désactivez puis réactivez la surveillance :

```qml
temperature.monitored = false
temperature.publishingInterval = 2000
temperature.monitored = true
```

## Écrire une valeur

### Écriture simple

```qml
OpcUaMonitoredNode {
    id: consigne
    nodeId: "ns=2;s=Setpoint"
}

TextField {
    id: champValeur
    placeholderText: "Nouvelle valeur..."
}

Button {
    text: "Écrire"
    enabled: consigne.writable
    onClicked: consigne.writeValue(champValeur.text)
}
```

La conversion de type est automatique : la valeur QML (texte, nombre, booléen) est convertie vers le type OPC UA du noeud.

Pour écrire un tableau, séparez les valeurs par des virgules :

```qml
consigne.writeValue("10, 20, 30")
```

### Écriture partielle dans un tableau (index range)

Pour modifier uniquement certains éléments d'un tableau sans réécrire le tout :

```qml
// Modifier le premier élément
consigne.writeValueAtRange("42", "0")

// Modifier les éléments 0 à 2
consigne.writeValueAtRange("10, 20, 30", "0:2")
```

### Résultat de l'écriture

Le signal `writeCompleted` indique si l'écriture a réussi :

```qml
OpcUaMonitoredNode {
    id: consigne
    nodeId: "ns=2;s=Setpoint"
    onWriteCompleted: (success, message) => {
        if (success)
            console.log("Écriture réussie")
        else
            console.log("Échec :", message)
    }
}
```

## Métadonnées d'un noeud

La propriété `info` donne accès aux métadonnées, lues une seule fois à la connexion :

```qml
OpcUaMonitoredNode {
    id: noeud
    nodeId: "ns=2;s=Temperature"
}

Column {
    Text { text: "Nom : " + noeud.info.displayName }
    Text { text: "Description : " + noeud.info.description }
    Text { text: "Type : " + noeud.info.dataType }
}
```

| Champ | Description |
|-------|-------------|
| `displayName` | Nom lisible du noeud |
| `description` | Description textuelle |
| `nodeClass` | Classe du noeud (Variable, Object, etc.) |
| `dataType` | Type de donnée (Int32, Float, String, etc.) |
| `valueRank` | Rang (-1 = scalaire, 1 = tableau 1D, etc.) |
| `arrayDimensions` | Dimensions du tableau |
| `accessLevel` | Niveau d'accès (lecture, écriture) |
| `status` | Code de statut OPC UA |
| `sourceTimestamp` | Horodatage côté source |
| `serverTimestamp` | Horodatage côté serveur |

## Découverte de serveurs

Bobink peut interroger un serveur de découverte local (LDS) pour lister les serveurs OPC UA disponibles sur le réseau :

```qml
Component.onCompleted: {
    Bobink.discoveryUrl = "opc.tcp://localhost:4840"
    Bobink.startDiscovery()
}

ListView {
    model: Bobink.servers
    delegate: ItemDelegate {
        required property var modelData
        text: modelData.serverName
        onClicked: Bobink.serverUrl = modelData.discoveryUrls[0]
    }
}
```

Chaque élément de `Bobink.servers` contient :

- `serverName` -- nom du serveur
- `applicationUri` -- identifiant unique de l'application
- `discoveryUrls` -- liste des URL de connexion

Appelez `Bobink.stopDiscovery()` pour arrêter la recherche (par exemple une fois connecté).

## Certificats client (PKI)

Au démarrage, Bobink cherche automatiquement un certificat client dans le dossier PKI par défaut. Si vous devez configurer manuellement :

```qml
Bobink.pkiDir = "/chemin/vers/pki"
Bobink.certFile = "/chemin/vers/certificat.der"
Bobink.keyFile = "/chemin/vers/cle.pem"
Bobink.applyPki()
```

Les propriétés `Bobink.pkiDir`, `Bobink.certFile` et `Bobink.keyFile` sont aussi consultables pour afficher les chemins actuels dans votre interface.

## Récapitulatif

| Ce que vous voulez faire | Code |
|--------------------------|------|
| Se connecter | `Bobink.connectToServer()` |
| Se déconnecter | `Bobink.disconnectFromServer()` |
| Lire une valeur en temps réel | `OpcUaMonitoredNode { nodeId: "..." }` puis `monNoeud.value` |
| Écrire une valeur | `monNoeud.writeValue(valeur)` |
| Écrire dans un tableau partiellement | `monNoeud.writeValueAtRange(valeur, "0:2")` |
| Vérifier si un noeud est inscriptible | `monNoeud.writable` |
| Afficher le nom d'un noeud | `monNoeud.info.displayName` |
| Lister les serveurs | `Bobink.startDiscovery()` puis `Bobink.servers` |

Pour un exemple concret et complet, consultez les fichiers `demo/Main.qml` et `demo/NodePage.qml`.