Запуск OPC UA сервера с веб-интерфейсом

  • Михаил
  • 8 мин. на прочтение
  • 17
  • 29 Apr 2026
  • 29 Apr 2026
⚠️ Важное предупреждение перед началом: Образ hilschernetpi/netpi-opcua-server поддерживает только архитектуру ARM (Raspberry Pi 3B, netPI). Если ваш сервер работает на x86_64 (обычный ПК/сервер), см. раздел «Альтернативы для x86_64» ниже.

Структура решения

 

📁 /mnt/iscsi_disk/opcuaserver/
├── 📁 certs/              # Сертификаты и ключи (автогенерация)
├── 📁 config/             # Конфигурационные файлы
│   └── 📄 mySensors.xml   # Ваша модель данных (эквивалент sensors.lua)
└── 📄 docker-compose.yml  # Файл развёртывания (опционально)

Шаг 1: Создайте директорию и структуру

 

# Создайте основную директорию

sudo mkdir -p /mnt/iscsi_disk/opcuaserver/{certs,config}
sudo chown -R $USER:$USER /mnt/iscsi_disk/opcuaserver

Шаг 2: Создайте XML Nodeset (эквивалент вашего sensors.lua)

Создайте файл /mnt/iscsi_disk/opcuaserver/config/mySensors.xml:

<?xml version="1.0" encoding="utf-8"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd"
          xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd"
          xmlns:s1="http://mycompany.org/MySensors/"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   
   <!-- Объявление пространства имён -->
   <NamespaceUris>
       <Uri>http://mycompany.org/MySensors/</Uri>
   </NamespaceUris>
   
   <!-- Алиасы для типов данных -->
   <Aliases>
       <Alias Alias="Double">i=11</Alias>
       <Alias Alias="HasComponent">i=47</Alias>
       <Alias Alias="HasTypeDefinition">i=40</Alias>
       <Alias Alias="Organizes">i=35</Alias>
   </Aliases>
   
   <!-- Папка-контейнер "Sensors" в Objects -->
   <UAObject NodeId="ns=1;s=SensorsFolder" BrowseName="1:Sensors">
       <DisplayName>Sensors</DisplayName>
       <References>
           <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
           <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
       </References>
   </UAObject>
   
   <!-- Переменная: Temperature (Double, начальное значение 22.5, READ|WRITE) -->
   <UAVariable NodeId="ns=1;s=Temperature" 
               BrowseName="1:Temperature" 
               DataType="Double" 
               ValueRank="-1" 
               AccessLevel="3" 
               UserAccessLevel="3">
       <DisplayName>Temperature</DisplayName>
       <References>
           <Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SensorsFolder</Reference>
           <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
       </References>
       <Value>
           <uax:Double>22.5</uax:Double>
       </Value>
   </UAVariable>
   
   <!-- Переменная: Pressure (Double, начальное значение 1013.25, READ|WRITE) -->
   <UAVariable NodeId="ns=1;s=Pressure" 
               BrowseName="1:Pressure" 
               DataType="Double" 
               ValueRank="-1" 
               AccessLevel="3" 
               UserAccessLevel="3">
       <DisplayName>Pressure</DisplayName>
       <References>
           <Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SensorsFolder</Reference>
           <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
       </References>
       <Value>
           <uax:Double>1013.25</uax:Double>
       </Value>
   </UAVariable>
   
   <!-- Переменная: Humidity (Double, начальное значение 45.0, READ|WRITE) -->
   <UAVariable NodeId="ns=1;s=Humidity" 
               BrowseName="1:Humidity" 
               DataType="Double" 
               ValueRank="-1" 
               AccessLevel="3" 
               UserAccessLevel="3">
       <DisplayName>Humidity</DisplayName>
       <References>
           <Reference ReferenceType="HasComponent" IsForward="false">ns=1;s=SensorsFolder</Reference>
           <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
       </References>
       <Value>
           <uax:Double>45.0</uax:Double>
       </Value>
   </UAVariable>
   
</UANodeSet>

 

Ключевые параметры:

 

Параметр

Значение

Пояснение

AccessLevel="3" / UserAccessLevel="3"

3 = READ | WRITE

Разрешает чтение и запись через клиента

DataType="Double"

i=11

Тип данных (аналог DataType.DOUBLE в Lua)

NodeId="ns=1;s=..."

String ID

Удобный идентификатор для клиентов

BrowseName="1:..."

Имя для навигации

Отображается в клиенте (например, в UA Expert)


Шаг 3: Запуск контейнера

Вариант А: Docker CLI (быстрый старт)

 

# 1. Создаём volume для сертификатов (если ещё нет)

docker volume create opcuaserver_certs

# 2. Запускаем контейнер с вашими параметрами:

docker run -d \
  --name opc-ua-server \
  --restart=always \
  -p 8073:8080/tcp \        # Веб-интерфейс: хост 8073 → контейнер 8080
  -p 4840:4840/tcp \        # OPC UA: хост 4840 → контейнер 4840
  -v opcuaserver_certs:/certs \
  -v /mnt/iscsi_disk/opcuaserver/config:/config:ro \
  hilschernetpi/netpi-opcua-server:1.2.1

Вариант Б: Docker Compose (рекомендуется для продакшена)

Создайте файл /mnt/iscsi_disk/opcuaserver/docker-compose.yml:

 

version: "2"

services:
  opcuaserver:
    image: hilschernetpi/netpi-opcua-server:1.2.1
    container_name: opc-ua-server
    restart: always
    ports:
      - "8073:8080/tcp"   # Веб-интерфейс
      - "4840:4840/tcp"   # OPC UA endpoint
    volumes:
      - opcuaserver_certs:/certs
      - ./config:/config:ro  # Только чтение для конфигурации
    # Для ARM-хостов (Raspberry Pi) не требуется дополнительных настроек
    # Для x86_64 см. раздел "Альтернативы" ниже

volumes:
  opcuaserver_certs:
    external: false

Запуск:

cd /mnt/iscsi_disk/opcuaserver
docker compose up -d

Шаг 4: Доступ к веб-интерфейсу и настройка

4.1 Откройте веб-интерфейс

http://:8073

4.2 Загрузка и компиляция модели

  1. Загрузите XML: Нажмите Choose File → выберите mySensors.xmlCompile...
  2. Выберите режим компиляции:
    • Compile unsecure — для тестов (без шифрования)
    • Compile secure — для продакшена (требует сертификаты)
  3. Запустите сервер: Нажмите Run...
📝 При первом запуске контейнер автоматически сгенерирует самоподписанный сертификат в /certs.

4.3 Подключение клиента (например, UA Expert)

Endpoint URL: opc.tcp://:4840
Security Policy: None (для unsecure) или Basic256Sha256 (для secure)

Ваши переменные будут доступны по пути:

Root → Objects → Sensors → [Temperature, Pressure, Humidity]

Управление сертификатами (для secure-режима)

 

# Просмотр сгенерированных сертификатов

docker exec -it opc-ua-server ls -la /certs/

 

# Копирование сертификата на хост для доверия в клиенте

docker cp opc-ua-server:/certs/server_certificate.der ./myserver_cert.der

 

# Замена на свой доверенный сертификат:

# 1. Подготовьте файлы: server_certificate.der + server_private_key.pem

# 2. Скопируйте в volume:

docker cp server_certificate.der opc-ua-server:/certs/
docker cp server_private_key.pem opc-ua-server:/certs/

# 3. Перезапустите компиляцию в веб-интерфейсе с опцией "secure"


Обновление конфигурации (без пересоздания контейнера)

  1. Отредактируйте /mnt/iscsi_disk/opcuaserver/config/mySensors.xml
  2. В веб-интерфейсе: загрузите обновлённый файл → Compile...Run...
  3. Старый сервер автоматически остановится, новый запустится с обновлённой моделью
💡 Значения переменных сбрасываются при перезапуске сервера (in-memory хранение). Для персистентности значений требуется доработка сервера или использование внешней БД.

Если ваш хост — x86_64 (не ARM)

Образ hilschernetpi/netpi-opcua-server не запустится на обычном сервере. Выберите один из вариантов:

Вариант 1: Официальный open62541 (минималистичный, без веб-интерфейса)

docker run -d \
  --name opc-ua-server \
  --restart=always \
  -p 4840:4840 \
  -v /mnt/iscsi_disk/opcuaserver:/data \
  open62541/open62541:latest
⚠️ Нет веб-интерфейса. Конфигурация — через C-код или предкомпилированный Nodeset.

Вариант 2: Сборка собственного образа с веб-интерфейсом

Используйте репозиторий HilscherAutomation/netPI-opcua-server как основу, но:

  1. Замените базовый образ на debian:bullseye (x86_64)
  2. Пересоберите open62541 под вашу архитектуру
  3. Добавьте Node.js веб-интерфейс вручную

Вариант 3: Альтернативные решения с веб-интерфейсом

 

Решение

Веб-интерфейс

Архитектуры

Лицензия

node-opcua + свой UI

✅ (свой)

x86_64, ARM

MIT

Prosys OPC UA Simulation Server

x86_64, ARM

Бесплатно для тестов

Unified Automation C++ SDK

x86_64, ARM

Коммерческая


Тестирование работы сервера

Проверка через командную строку (opcua-client):

# Установка клиента (если нет)

pip install opcua-client

# Подключение и чтение переменной

opcua-client --url opc.tcp://localhost:4840 \
  --node "ns=1;s=Temperature" \
  --read

Проверка через Python:

 

from opcua import Client

client = Client("opc.tcp://localhost:4840")
client.connect()

# Чтение
temp = client.get_node("ns=1;s=Temperature")
print(f"Temperature: {temp.get_value()}")

# Запись
temp.set_value(25.0)
print("✓ Значение обновлено")

client.disconnect()

Сводная таблица портов и путей

 

Компонент

Порт контейнера

Порт хоста

Назначение

Веб-интерфейс

8080

8073

Управление сервером через браузер

OPC UA endpoint

4840

4840

Подключение клиентов (UA Expert, SCADA)

Сертификаты

/certs

opcuaserver_certs (volume)

Хранение ключей и сертификатов

Конфигурация

/config

/mnt/iscsi_disk/opcuaserver/config

XML Nodeset файлы


Частые вопросы

❓ Почему значения сбрасываются после перезапуска?
→ Сервер open62541 хранит данные в памяти. Для персистентности нужно:

  • Либо доработать сервер (добавить callback на запись в файл/БД)
  • Либо использовать внешний источник данных (например, Redis)

❓ Как добавить динамику (как в Lua Update())?
→ В open62541 это делается через DataSource callbacks в C-коде. Веб-интерфейс Hilscher не поддерживает динамическую логику — только статическую модель. Для сложной логики рассмотрите:

  • Самостоятельную сборку open62541 с кастомным кодом
  • Использование node-opcua с JavaScript-логикой

❓ Как включить шифрование?

  1. В веб-интерфейсе загрузите свой сертификат + ключ (или используйте сгенерированные)
  2. Выберите Compile secure...
  3. В клиенте укажите политику безопасности: Basic256Sha256 + режим Sign & Encrypt

💡 Рекомендация: Если вам критически важна динамическая логика и веб-интерфейс, рассмотрите создание собственного образа на базе node-opcua + Express.js. Это даст гибкость скриптов и современный веб-интерфейс, но потребует больше начальных усилий.