Aller au contenu

Module opcua

Le module opcua permet de communiquer avec des serveurs OPC UA (Unified Architecture) pour lire et écrire des données industrielles.

Import

Pour importer le module en Lua :

local opcua = require("opcua1")

Configuration YAML

Le module opcua nécessite une configuration dans le fichier YAML de l'agent :

iot:
  modules:
    opcua:
      - name: opcua1                                # nom unique de l'instance
        endpoint: "opc.tcp://localhost:4840"        # URL du serveur OPC UA
        cert_file: "/etc/alemca/opcua/client.pem"   # certificat client (optionnel)
        key_file: "/etc/alemca/opcua/client.key"    # clé privée RSA (optionnel)
        trusted_dir: "/etc/alemca/opcua/trusted"    # dossier certs serveur fiables (optionnel)
        username: ""                                # nom d'utilisateur (optionnel)
        password: ""                                # mot de passe (optionnel)
        timeout: 30                                 # timeout en secondes (optionnel, défaut: 30)
        persistent: true                            # garde la session ouverte (recommandé en production)
        # idle_timeout: 60                          # agent >= 3.3.0, sans effet si persistent: true
        # namespace_uri: "urn:vendor:model"         # namespace du SERVEUR, voir plus bas

Détails de la configuration

  • name : Nom unique de l'instance du module. Utilisé pour l'import en Lua.
  • endpoint : URL complète du serveur OPC UA (format opc.tcp://host:port). L'agent utilise toujours cette URL comme cible de connexion, même si le serveur annonce une URL différente pendant la découverte — utile derrière un NAT ou dans des déploiements conteneurisés.
  • cert_file : Chemin vers le certificat client au format PEM. Quand fourni (avec key_file), active le SecureChannel chiffré et signé. La policy et le mode sont auto-négociés avec le serveur (typiquement Basic256Sha256 + SignAndEncrypt). Le certificat doit contenir un ApplicationURI dans les Subject Alternative Names (exigence OPC UA).
  • key_file : Chemin vers la clé privée RSA au format PEM (PKCS#1 ou PKCS#8). Doit correspondre à cert_file. Permissions recommandées : 0600 (un warning est loggé sinon).
  • trusted_dir : Dossier optionnel contenant les certificats serveur de confiance (*.pem, *.crt). Si renseigné, seuls les serveurs dont le certificat vérifie contre ce pool sont acceptés ; sinon, tout certificat serveur est accepté.
  • username / password : Authentification utilisateur optionnelle.
  • timeout : Délai d'attente pour les opérations, en secondes (défaut: 30).
  • persistent : Garde une seule session ouverte en permanence, quels que soient les appels à connect() et disconnect(). Recommandé en production, surtout avec un certificat client : sans cette clé, chaque nouvelle session rejoue une poignée de main de sécurité complète (calcul RSA), coûteuse sur une passerelle ARM.
  • idle_timeout (agent 3.3.0 et suivants) : En mode non persistant, délai en secondes après lequel une session inutilisée est fermée (défaut : 60). Sans effet avec persistent: true.
  • namespace_uri : URI d'un namespace du serveur OPC UA, utilisée pour résoudre les browse paths et les tags symboliques qui ne précisent pas d'index ns=. Inutile quand on ne manipule que des NodeId complets (ns=2;s=...). Un avertissement configured namespace_uri ... not found on server est journalisé à la connexion si le serveur ne la connaît pas.

Ne pas confondre avec l'URI du certificat client

L'ApplicationURI du certificat client (urn:alemca:agent dans l'exemple ci-dessous) est l'identité de l'agent auprès du serveur. Ce n'est pas un namespace du serveur : la reporter dans namespace_uri, ici ou dans une table d'échange input_opcua du bridge, rend tous les points concernés introuvables, sans erreur visible au niveau info. Les namespaces réels du serveur se lisent avec la commande de découverte opcua_discover du bridge, ou dans un client OPC UA (nœud Server > NamespaceArray).

Génération d'un certificat client

Le serveur OPC UA exige qu'un ApplicationURI soit présent dans le SAN du certificat client. Exemple avec OpenSSL :

openssl req -x509 -newkey rsa:2048 \
  -keyout client.key -out client.pem \
  -days 3650 -nodes \
  -subj "/CN=alemca-agent" \
  -addext "subjectAltName=URI:urn:alemca:agent"
chmod 600 client.key

Déposez ensuite client.pem dans la trust list du serveur OPC UA (la procédure dépend du serveur : souvent un dossier pki/trusted/ avec redémarrage du service).

L'URI urn:alemca:agent saisie ici identifie le client uniquement. Elle apparaît dans le journal de l'agent (loaded client cert CN="alemca-agent" URI="urn:alemca:agent") mais ne doit jamais servir de namespace_uri.

Migration depuis les anciennes versions

Les champs security_policy et security_mode ne sont plus reconnus. Ils sont silencieusement ignorés dans les anciennes configurations. Pour activer la sécurité, fournissez désormais cert_file et key_file : la policy et le mode sont auto-négociés à partir des endpoints que le serveur annonce.

API Lua

Liste des fonctions

Fonction Signature Lua Rôle Retour (succès) Retour (échec)
connect bool, err = opcua.connect() Connecte au serveur OPC UA. true, nil nil, "msg"
disconnect bool, err = opcua.disconnect() Ferme la connexion. true, nil nil, "msg"
read value, err = opcua.read(nodeId) Lit la valeur d'un noeud. string, nil nil, "msg"
write bool, err = opcua.write(nodeId, value) Écrit une valeur dans un noeud. true, nil nil, "msg"
Champs opcua.name, opcua.type Métadonnées du module. – –

opcua.connect()

Établit une connexion avec le serveur OPC UA.

  • Paramètres : Aucun
  • Retour :
  • true si la connexion est réussie.
  • nil et un message d'erreur en cas d'échec.
local opcua = require("opcua1")

local ok, err = opcua.connect()
if not ok then
    print("Erreur de connexion : " .. err)
    return
end

print("Connecté au serveur OPC UA")

opcua.disconnect()

Libère la connexion avec le serveur OPC UA.

Session partagée entre scripts

Une instance opcua est partagée par tous les scripts Lua qui la requièrent (télémétrie EdgeControl, régulation, input_opcua du bridge...).

  • Agent 3.3.0 et suivants : disconnect() ne ferme plus la session. Elle est fermée par un chien de garde après idle_timeout secondes sans utilisation, jamais en mode persistent.
  • Agent antérieur à 3.3.0 : disconnect() ferme réellement la session, y compris pendant qu'un autre script l'utilise. Un script périodique ne doit donc pas l'appeler à chaque exécution : préférez persistent: true sur l'instance et n'appelez pas disconnect().
  • Paramètres : Aucun
  • Retour :
  • true si la déconnexion est réussie.
  • nil et un message d'erreur en cas d'échec.
local opcua = require("opcua1")

local ok, err = opcua.disconnect()
if not ok then
    print("Erreur de déconnexion : " .. err)
end

opcua.read(nodeId)

Lit la valeur d'un noeud OPC UA.

  • Paramètres :
  • nodeId (string) : Identifiant du noeud à lire (ex. ns=2;s=Temperature).

  • Retour :

  • La valeur du noeud (convertie en string).
  • nil et un message d'erreur en cas d'échec.
local opcua = require("opcua1")

opcua.connect()

-- Lecture d'un noeud par son NodeId
local value, err = opcua.read("ns=2;s=Temperature")
if not value then
    print("Erreur de lecture : " .. err)
else
    print("Température : " .. value)
end

opcua.disconnect()

opcua.write(nodeId, value)

Écrit une valeur dans un noeud OPC UA.

  • Paramètres :
  • nodeId (string) : Identifiant du noeud à écrire.
  • value (string) : Valeur à écrire.

  • Retour :

  • true si l'écriture est réussie.
  • nil et un message d'erreur en cas d'échec.
local opcua = require("opcua1")

opcua.connect()

-- Écriture d'une valeur
local ok, err = opcua.write("ns=2;s=Setpoint", "25.5")
if not ok then
    print("Erreur d'écriture : " .. err)
else
    print("Valeur écrite avec succès")
end

opcua.disconnect()

Exemple complet

Script périodique (déclenché par trigger) sur une instance déclarée avec persistent: true. Il ne ferme pas la session, que d'autres scripts peuvent utiliser en même temps (voir opcua.disconnect()).

Télémétrie EdgeControl et bridge

Pour remonter des mesures OPC UA vers la plateforme ou EdgeControl, inutile d'écrire un script : utilisez une table d'échange input_opcua du bridge. Ce script sert à tester une connexion ou à un besoin spécifique.

local opcua = require("opcua1")
local json = require("json")
local alemca = require("alemca")

-- Connexion au serveur
local ok, err = opcua.connect()
if not ok then
    printError("Connexion OPC UA échouée : " .. err)
    return
end

-- Lecture de plusieurs noeuds
local nodes = {
    "ns=2;s=Temperature",
    "ns=2;s=Humidity",
    "ns=2;s=Pressure"
}

local data = {}
for _, nodeId in ipairs(nodes) do
    local value, err = opcua.read(nodeId)
    if value then
        data[nodeId] = tonumber(value) or value
    else
        printError("Erreur lecture " .. nodeId .. " : " .. err)
    end
end

-- Publication des données
alemca.publish(json.encode(data), "opcua_data")

-- Pas de disconnect() : la session est partagée avec les autres scripts

Formats de NodeId

Les NodeId OPC UA peuvent être spécifiés dans plusieurs formats :

Format Exemple Description
Numérique ns=0;i=2258 Identifiant numérique
String ns=2;s=Temperature Identifiant texte
GUID ns=1;g=09087e75-8e5e-499b-954f-f2a9603db28a Identifiant GUID
Opaque ns=1;b=M/RbKBsRVkePCePcx24oRA== Identifiant opaque (base64)

Champs Lua associés

  • opcua.name : Retourne le nom de l'instance du module.
  • opcua.type : Retourne le type du module ("opcua").