SIENTIAPDE-1646
SIENTIAPDE-1646 Update dependencies and refactor OPC integration - Replaced `opcua` with `asyncua` in `requirements-local.txt` and `requirements.txt` to utilize the async capabilities. - Updated `values.yaml` to change the GitHub branch from `feature/SIENTIAPDE-1646` to `release/SIENTIAPDE-1646`. - Refactored `opc.py` and `opc_repository.py` to accommodate the new `asyncua` library, ensuring compatibility with the synchronous API. - Adjusted tests in `test_opc_repository.py` to reflect changes in the client implementation and maintain functionality.
This commit is contained in:
@@ -143,7 +143,7 @@ class OPC(SientiaMonitoring):
|
||||
level=NotificationLevel.ERROR,
|
||||
attachment_content=trace,
|
||||
)
|
||||
raise e
|
||||
raise
|
||||
|
||||
def validate_server(self, server_id: str, metadata: dict[str, Any]) -> bool:
|
||||
"""
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"""
|
||||
Synchronous OPC UA client repository using python-opcua (opcua package).
|
||||
Synchronous OPC UA client repository using asyncua (opcua-asyncio) ``sync`` API.
|
||||
|
||||
Connects to OPC UA servers, optionally configures Basic256 security, validates sessions,
|
||||
and writes node values with typed variants and Prometheus-compatible metrics.
|
||||
``asyncua.sync.Client`` runs the asyncio client on a background thread so callers
|
||||
stay synchronous. Connect/disconnect, optional Basic256 security, session checks,
|
||||
and typed writes mirror the previous python-opcua integration.
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -12,8 +13,9 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from opcua import Client, ua
|
||||
from opcua.crypto import security_policies
|
||||
from asyncua import ua
|
||||
from asyncua.crypto import security_policies
|
||||
from asyncua.sync import Client
|
||||
from sientia_do.notifications.handlers import CoreNotificationHandler as NotificationHandler
|
||||
from sientia_do.notifications.models import NotificationLevel
|
||||
from sientia_do.observability.logger import Logger
|
||||
@@ -54,7 +56,7 @@ class OpcRepository(SientiaMonitoring):
|
||||
url: OPC UA endpoint URL.
|
||||
id: Server identifier used in metrics and notifications.
|
||||
server_name: Human-readable server name for labels.
|
||||
client: Active opcua.Client instance while connected.
|
||||
client: Active ``asyncua.sync.Client`` while connected.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -115,16 +117,17 @@ class OpcRepository(SientiaMonitoring):
|
||||
if self.client is None:
|
||||
raise ValueError('Client must be initialized before setting security')
|
||||
|
||||
self.client.application_uri = self.server_uri
|
||||
self.client.application_uri = self.server_uri or self.client.application_uri
|
||||
self.logger.custom_info('Setting security...', self.metadata)
|
||||
self.client.set_security(
|
||||
security_policies.SecurityPolicyBasic256,
|
||||
str(cert),
|
||||
str(private_key),
|
||||
None,
|
||||
str(server_cert) if server_cert else None,
|
||||
)
|
||||
self.client.secure_channel_timeout = 10000000
|
||||
self.client.session_timeout = 10000000
|
||||
self.client.aio_obj.secure_channel_timeout = 10000000
|
||||
self.client.aio_obj.session_timeout = 10000000
|
||||
|
||||
def connect(self) -> tuple[bool, dict[str, Any]]:
|
||||
"""
|
||||
@@ -136,11 +139,11 @@ class OpcRepository(SientiaMonitoring):
|
||||
|
||||
self.client = Client(self.url, timeout=10)
|
||||
|
||||
self.client.name = self.pod_id
|
||||
self.client.application_name = self.pod_id
|
||||
self.client.aio_obj.name = self.pod_id
|
||||
self.client.aio_obj.description = self.pod_id
|
||||
pod_uri = self.pod_id.replace('-', ':')
|
||||
self.client.application_uri = pod_uri
|
||||
self.client.product_uri = pod_uri
|
||||
self.client.aio_obj.product_uri = pod_uri
|
||||
|
||||
if self.cert_path:
|
||||
self.set_security()
|
||||
@@ -336,7 +339,7 @@ class OpcRepository(SientiaMonitoring):
|
||||
Write a typed value to an OPC UA node after validating connectivity.
|
||||
|
||||
Args:
|
||||
node: Node id string accepted by opcua Client.get_node.
|
||||
node: Node id string accepted by ``Client.get_node``.
|
||||
value: Scalar value to encode.
|
||||
data_type: Key into ``data_type_map`` (e.g. float, str).
|
||||
logger: Caller logger for per-write traces.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
temporalio
|
||||
psycopg2-binary
|
||||
sqlalchemy
|
||||
opcua
|
||||
asyncua==1.0.6
|
||||
redis
|
||||
#git+ssh://git@github.com/Aignosi/sientia-dataops-library.git@1.12.0
|
||||
/home/grezewave/Documents/projects/sientia/sientia-dataops-library
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
temporalio
|
||||
psycopg2-binary
|
||||
sqlalchemy
|
||||
opcua
|
||||
asyncua==1.0.6
|
||||
redis
|
||||
sientia_do==1.12.0
|
||||
sientia_model==0.8.2
|
||||
|
||||
@@ -3,7 +3,7 @@ from datetime import datetime
|
||||
from unittest.mock import ANY, MagicMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
from opcua.crypto import security_policies
|
||||
from asyncua.crypto import security_policies
|
||||
from sientia_do.notifications.models import NotificationLevel
|
||||
|
||||
from laborious.utils.repository.opc_repository import OpcRepository
|
||||
@@ -39,6 +39,7 @@ def opc_repository(mock_logger):
|
||||
def mock_client():
|
||||
with patch('laborious.utils.repository.opc_repository.Client') as mock:
|
||||
client_instance = MagicMock()
|
||||
client_instance.aio_obj = MagicMock()
|
||||
mock.return_value = client_instance
|
||||
yield client_instance
|
||||
|
||||
@@ -75,10 +76,11 @@ def test_set_security(opc_repository, mock_client):
|
||||
security_policies.SecurityPolicyBasic256,
|
||||
'/path/to/cert.pem',
|
||||
'/path/to/key.pem',
|
||||
None,
|
||||
'/path/to/server_cert.pem',
|
||||
)
|
||||
assert mock_client.secure_channel_timeout == 10000000
|
||||
assert mock_client.session_timeout == 10000000
|
||||
assert mock_client.aio_obj.secure_channel_timeout == 10000000
|
||||
assert mock_client.aio_obj.session_timeout == 10000000
|
||||
|
||||
|
||||
def test_set_security_missing_certificates(opc_repository):
|
||||
|
||||
@@ -66,7 +66,7 @@ global:
|
||||
- name: GITHUB_REPO_URL
|
||||
value: "git@github.com:Aignosi/sientia-dataops-laborious_temporal.git"
|
||||
- name: GITHUB_BRANCH
|
||||
value: "feature/SIENTIAPDE-1646"
|
||||
value: "release/SIENTIAPDE-1646"
|
||||
- name: PYTHON_APP
|
||||
value: "laborious.worker.worker"
|
||||
- name: PYPI_SERVER
|
||||
@@ -231,7 +231,7 @@ global:
|
||||
# - The runtime name is used by the worker bootstrap to resolve plugins and task queues.
|
||||
# - Keep runtime names in sync with the plugin-store runtime names.
|
||||
runtimes:
|
||||
- name: "single"
|
||||
- name: "basic"
|
||||
replicas: 1
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user