From efed8352c3258360a854934c536e60de7ba7ecfa Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 22 Mar 2026 07:31:18 -0400 Subject: [PATCH] feat(infra): add second OPC UA server instance (opcua2) on port 50010 Enables multi-server testing with independent state. Both instances share the same nodes.json tag config. Updated all infra documentation. --- docs/test_infra/test_infra.md | 8 ++++++-- docs/test_infra/test_infra_opcua.md | 25 +++++++++++++++++++------ infra/README.md | 5 +++-- infra/docker-compose.yml | 21 +++++++++++++++++++++ 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/docs/test_infra/test_infra.md b/docs/test_infra/test_infra.md index babb402..f657888 100644 --- a/docs/test_infra/test_infra.md +++ b/docs/test_infra/test_infra.md @@ -1,12 +1,13 @@ # Test Infrastructure -This document describes the local Docker-based test infrastructure for ScadaLink development. Seven services provide the external dependencies needed to run and test the system locally. The first seven run in `infra/docker-compose.yml`; Traefik runs alongside the cluster nodes in `docker/docker-compose.yml`. +This document describes the local Docker-based test infrastructure for ScadaLink development. Eight services provide the external dependencies needed to run and test the system locally. The first eight run in `infra/docker-compose.yml`; Traefik runs alongside the cluster nodes in `docker/docker-compose.yml`. ## Services | Service | Image | Port(s) | Config | Compose File | |---------|-------|---------|--------|-------------| | OPC UA Server | `mcr.microsoft.com/iotedge/opc-plc:latest` | 50000 (OPC UA), 8080 (web) | `infra/opcua/nodes.json` | `infra/` | +| OPC UA Server 2 | `mcr.microsoft.com/iotedge/opc-plc:latest` | 50010 (OPC UA), 8081 (web) | `infra/opcua/nodes.json` | `infra/` | | LDAP Server | `glauth/glauth:latest` | 3893 | `infra/glauth/config.toml` | `infra/` | | MS SQL 2022 | `mcr.microsoft.com/mssql/server:2022-latest` | 1433 | `infra/mssql/setup.sql` | `infra/` | | SMTP (Mailpit) | `axllent/mailpit:latest` | 1025 (SMTP), 8025 (web) | Environment vars | `infra/` | @@ -66,6 +67,9 @@ For use in `appsettings.Development.json`: "OpcUa": { "EndpointUrl": "opc.tcp://localhost:50000" }, + "OpcUa2": { + "EndpointUrl": "opc.tcp://localhost:50010" + }, "Smtp": { "Server": "localhost", "Port": 1025, @@ -88,7 +92,7 @@ For use in `appsettings.Development.json`: ```bash cd infra docker compose down # stop containers, preserve SQL data volume -docker compose stop opcua # stop a single service (also: ldap, mssql, smtp, restapi) +docker compose stop opcua # stop a single service (also: opcua2, ldap, mssql, smtp, restapi) ``` **Full teardown** (removes volumes, optionally images and venv): diff --git a/docs/test_infra/test_infra_opcua.md b/docs/test_infra/test_infra_opcua.md index 44fd677..1435fb4 100644 --- a/docs/test_infra/test_infra_opcua.md +++ b/docs/test_infra/test_infra_opcua.md @@ -6,9 +6,14 @@ The test OPC UA server uses [Azure IoT OPC PLC](https://github.com/Azure-Samples ## Image & Ports +Two identical OPC UA server instances run with the same tag configuration, on different ports: + +| Instance | OPC UA Endpoint | Web UI | Container | +|----------|----------------|--------|-----------| +| opcua | `opc.tcp://localhost:50000` | `http://localhost:8080` | scadalink-opcua | +| opcua2 | `opc.tcp://localhost:50010` | `http://localhost:8081` | scadalink-opcua2 | + - **Image**: `mcr.microsoft.com/iotedge/opc-plc:latest` -- **OPC UA endpoint**: `opc.tcp://localhost:50000` -- **Web/config UI**: `http://localhost:8080` ## Startup Flags @@ -43,20 +48,21 @@ The browse path from the Objects root is: `OpcPlc > ScadaLink > Motor|Pump|Tank| ## Verification -1. Check the container is running: +1. Check both containers are running: ```bash docker ps --filter name=scadalink-opcua ``` -2. Verify the OPC UA endpoint using any OPC UA client (e.g., UaExpert, opcua-commander): +2. Verify both OPC UA endpoints using any OPC UA client (e.g., UaExpert, opcua-commander): ```bash # Using opcua-commander (npm install -g opcua-commander) opcua-commander -e opc.tcp://localhost:50000 +opcua-commander -e opc.tcp://localhost:50010 ``` -3. Check the web UI at `http://localhost:8080` for server status and node listing. +3. Check the web UIs at `http://localhost:8080` (opcua) and `http://localhost:8081` (opcua2) for server status and node listing. ## CLI Tool @@ -89,7 +95,14 @@ python infra/tools/opcua_tool.py write --node "ns=3;s=Motor.Running" --value tru python infra/tools/opcua_tool.py monitor --nodes "ns=3;s=Motor.Speed,ns=3;s=Pump.FlowRate" --duration 15 ``` -Use `--endpoint` to override the default endpoint (`opc.tcp://localhost:50000`). Run with `--help` for full usage. +Use `--endpoint` to override the default endpoint (`opc.tcp://localhost:50000`). For the second instance: + +```bash +python infra/tools/opcua_tool.py --endpoint opc.tcp://localhost:50010 check +python infra/tools/opcua_tool.py --endpoint opc.tcp://localhost:50010 browse --path "3:OpcPlc.3:ScadaLink.3:Motor" +``` + +Run with `--help` for full usage. ## Relevance to ScadaLink Components diff --git a/infra/README.md b/infra/README.md index af26e22..8918827 100644 --- a/infra/README.md +++ b/infra/README.md @@ -8,11 +8,12 @@ Local Docker-based test services for ScadaLink development. docker compose up -d ``` -This starts five services: +This starts the following services: | Service | Port | Purpose | |---------|------|---------| | OPC UA (Azure IoT OPC PLC) | 50000 (OPC UA), 8080 (web) | Simulated OPC UA server with ScadaLink-style tags | +| OPC UA 2 (Azure IoT OPC PLC) | 50010 (OPC UA), 8081 (web) | Second OPC UA server instance (same tags, independent state) | | LDAP (GLAuth) | 3893 | Lightweight LDAP with test users/groups matching ScadaLink roles | | MS SQL 2022 | 1433 | Configuration and machine data databases | | SMTP (Mailpit) | 1025 (SMTP), 8025 (web) | Email capture for notification testing | @@ -46,7 +47,7 @@ docker compose down **Stop a single service** (leave the others running): ```bash -docker compose stop opcua # or: ldap, mssql, smtp, restapi +docker compose stop opcua # or: opcua2, ldap, mssql, smtp, restapi docker compose start opcua # bring it back without recreating ``` diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index c295532..efe8da0 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -20,6 +20,27 @@ services: - scadalink-net restart: unless-stopped + opcua2: + image: mcr.microsoft.com/iotedge/opc-plc:latest + container_name: scadalink-opcua2 + ports: + - "50010:50010" + - "8081:8080" + volumes: + - ./opcua/nodes.json:/app/config/nodes.json:ro + command: > + --autoaccept + --unsecuretransport + --sph + --sn=5 --sr=10 --st=uint + --fn=5 --fr=1 --ft=uint + --gn=5 + --nf=/app/config/nodes.json + --pn=50010 + networks: + - scadalink-net + restart: unless-stopped + ldap: image: glauth/glauth:latest container_name: scadalink-ldap