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.
This commit is contained in:
Joseph Doherty
2026-03-22 07:31:18 -04:00
parent ac44122bf7
commit efed8352c3
4 changed files with 49 additions and 10 deletions

View File

@@ -1,12 +1,13 @@
# Test Infrastructure # 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 ## Services
| Service | Image | Port(s) | Config | Compose File | | 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 | `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/` | | 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/` | | 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/` | | SMTP (Mailpit) | `axllent/mailpit:latest` | 1025 (SMTP), 8025 (web) | Environment vars | `infra/` |
@@ -66,6 +67,9 @@ For use in `appsettings.Development.json`:
"OpcUa": { "OpcUa": {
"EndpointUrl": "opc.tcp://localhost:50000" "EndpointUrl": "opc.tcp://localhost:50000"
}, },
"OpcUa2": {
"EndpointUrl": "opc.tcp://localhost:50010"
},
"Smtp": { "Smtp": {
"Server": "localhost", "Server": "localhost",
"Port": 1025, "Port": 1025,
@@ -88,7 +92,7 @@ For use in `appsettings.Development.json`:
```bash ```bash
cd infra cd infra
docker compose down # stop containers, preserve SQL data volume 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): **Full teardown** (removes volumes, optionally images and venv):

View File

@@ -6,9 +6,14 @@ The test OPC UA server uses [Azure IoT OPC PLC](https://github.com/Azure-Samples
## Image & Ports ## 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` - **Image**: `mcr.microsoft.com/iotedge/opc-plc:latest`
- **OPC UA endpoint**: `opc.tcp://localhost:50000`
- **Web/config UI**: `http://localhost:8080`
## Startup Flags ## Startup Flags
@@ -43,20 +48,21 @@ The browse path from the Objects root is: `OpcPlc > ScadaLink > Motor|Pump|Tank|
## Verification ## Verification
1. Check the container is running: 1. Check both containers are running:
```bash ```bash
docker ps --filter name=scadalink-opcua 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 ```bash
# Using opcua-commander (npm install -g opcua-commander) # Using opcua-commander (npm install -g opcua-commander)
opcua-commander -e opc.tcp://localhost:50000 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 ## 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 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 ## Relevance to ScadaLink Components

View File

@@ -8,11 +8,12 @@ Local Docker-based test services for ScadaLink development.
docker compose up -d docker compose up -d
``` ```
This starts five services: This starts the following services:
| Service | Port | Purpose | | Service | Port | Purpose |
|---------|------|---------| |---------|------|---------|
| OPC UA (Azure IoT OPC PLC) | 50000 (OPC UA), 8080 (web) | Simulated OPC UA server with ScadaLink-style tags | | 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 | | LDAP (GLAuth) | 3893 | Lightweight LDAP with test users/groups matching ScadaLink roles |
| MS SQL 2022 | 1433 | Configuration and machine data databases | | MS SQL 2022 | 1433 | Configuration and machine data databases |
| SMTP (Mailpit) | 1025 (SMTP), 8025 (web) | Email capture for notification testing | | 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): **Stop a single service** (leave the others running):
```bash ```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 docker compose start opcua # bring it back without recreating
``` ```

View File

@@ -20,6 +20,27 @@ services:
- scadalink-net - scadalink-net
restart: unless-stopped 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: ldap:
image: glauth/glauth:latest image: glauth/glauth:latest
container_name: scadalink-ldap container_name: scadalink-ldap