# 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`. ## Configuration de Qt Creator **Autocomplétion et diagnostics QML** — activez le serveur de langage QML : Édition > Préférences > Client de langage > QML Language Server. **Formatage QML** — un fichier `.qmlformat.ini` est inclus à la racine du projet. Depuis le terminal, `qmlformat` le détecte automatiquement : `qmlformat -i fichier.qml`. Qt Creator ne respecte pas ce fichier ni ses propres réglages de style lors du formatage ([QTCREATORBUG-29668](https://bugreports.qt.io/browse/QTCREATORBUG-29668)) — utilisez la ligne de commande à la place (désolé les gars).