Как создать собственный OPC UA сервер с использованием Node.js
В данной статье мы рассмотрим процесс создания простого OPC UA сервера с использованием популярной библиотеки node-opcua.
Что такое OPC UA?
OPC UA (Unified Architecture) — это стандарт промышленной автоматизации, который обеспечивает обмен данными между оборудованием, системами управления и приложениями. Он активно используется в системах IoT и промышленности 4.0.
Зачем использовать Node.js?
Node.js — это платформа, основанная на JavaScript, которая идеально подходит для разработки серверных приложений благодаря своей асинхронной модели ввода-вывода. С помощью библиотеки node-opcua мы можем быстро разрабатывать надежные OPC UA серверы.
Подготовка к работе
Перед началом убедитесь, что Node.js установлен на вашем компьютере. Установить Node.js можно, следуя инструкциям на официальном сайте.
Создание проекта
Создайте новую директорию для проекта:
mkdir myserver cd myserver
Инициализируйте проект Node.js:
npm init -y
Установите библиотеку node-opcua:
npm install node-opcua --save
Теперь проект готов к разработке.
Написание скрипта сервера
Создайте файл server.js
и начните с подключения необходимых модулей:
const { OPCUAServer, Variant, DataType, StatusCodes } = require("node-opcua");
const os = require("os");
Шаг 1: Создание экземпляра сервера
Инициализируйте сервер:
const server = new OPCUAServer({
port: 4334,
resourcePath: "/UA/MyLittleServer",
buildInfo: {
productName: "MySampleServer",
buildNumber: "1",
buildDate: new Date()
}
});
Шаг 2: Инициализация сервера
Инициализация сервера выполняется асинхронно:
(async () => {
await server.initialize();
console.log("Сервер инициализирован.");
})();
Шаг 3: Добавление объектов и переменных
Мы создадим объект MyDevice
и добавим в него переменные.
Добавление объекта
const addressSpace = server.engine.addressSpace;
const namespace = addressSpace.getOwnNamespace();
const device = namespace.addObject({
organizedBy: addressSpace.rootFolder.objects,
browseName: "MyDevice"
});
Добавление переменной для чтения
let variable1 = 1;
setInterval(() => {
variable1 += 1;
}, 500);
namespace.addVariable({
componentOf: device,
browseName: "MyVariable1",
dataType: "Double",
value: {
get: () => new Variant({ dataType: DataType.Double, value: variable1 })
}
});
Добавление переменной для чтения и записи
let variable2 = 10.0;
namespace.addVariable({
componentOf: device,
browseName: "MyVariable2",
nodeId: "ns=1;s=MyVariable2",
dataType: "Double",
value: {
get: () => new Variant(
{ dataType: DataType.Double, value: variable2 }),
set: (variant) => {
variable2 = parseFloat(variant.value);
return StatusCodes.Good;
}
}
});
Добавление переменной для отображения свободной памяти
namespace.addVariable({
componentOf: device,
browseName: "FreeMemory",
nodeId: "s=free_memory",
dataType: "Double",
value: {
get: () => new Variant({
dataType: DataType.Double,
value: os.freemem() / os.totalmem() * 100.0
})
}
});
Шаг 4: Запуск сервера
После настройки всех переменных запустите сервер:
(async () => {
await server.start();
console.log("Сервер запущен.");
console.log("URL:",
server.endpoints[0].endpointDescriptions()[0].endpointUrl);
})();
Тестирование сервера
Сохраните файл server.js
и выполните его:
node server.js
После запуска сервер будет доступен по адресу, указанному в консоли. Теперь его можно подключить к OPC UA клиенту для тестирования.
Полный код доступен здесь:
Скрытый текст
const { OPCUAServer, Variant, DataType, DataValue, StatusCodes } = require("node-opcua");
(async () => {
// Let's create an instance of OPCUAServer
const server = new OPCUAServer({
port: 4334, // the port of the listening socket of the server
resourcePath: "/UA/MyLittleServer", // this path will be added to the endpoint resource name
buildInfo: {
productName: "MySampleServer1",
buildNumber: "7658",
buildDate: new Date(2014, 5, 2)
}
});
await server.initialize();
console.log("initialized");
const addressSpace = server.engine.addressSpace;
const namespace = addressSpace.getOwnNamespace();
// declare a new object
const device = namespace.addObject({
organizedBy: addressSpace.rootFolder.objects,
browseName: "MyDevice"
});
// add some variables
// add a variable named MyVariable1 to the newly created folder "MyDevice"
// emulate variable1 changing every 500 ms
var uaVariable1 = namespace.addVariable({
componentOf: device,
browseName: "MyVariable1",
dataType: "Double"
});
let variable1 = 1;
const timerId = setInterval(() => {
variable1 += 1;
uaVariable1.setValueFromSource(new Variant({ dataType: DataType.Double, value: variable1 }));
}, 500);
addressSpace.registerShutdownTask(() => { clearInterval(timerId); });
const uaVariable2 = namespace.addVariable({
componentOf: device,
nodeId: "ns=1;b=1020FFAA", // some opaque NodeId in namespace 4
browseName: "MyVariable2",
dataType: "Double",
accessLevel: "CurrentRead | CurrentWrite",
});
const os = require("os");
/**
* returns the percentage of free memory on the running machine
* @return {double}
*/
function available_memory() {
// var value = process.memoryUsage().heapUsed / 1000000;
const percentageMemUsed = (os.freemem() / os.totalmem()) * 100.0;
return percentageMemUsed;
}
namespace.addVariable({
componentOf: device,
nodeId: "s=free_memory", // a string nodeID
browseName: "FreeMemory",
dataType: "Double",
minimumSamplingInterval: 1000, // we need to specify a minimumSamplingInterval when using a getter
value: {
get: () => new Variant({ dataType: DataType.Double, value: available_memory() })
}
});
const uaVariable3 = namespace.addVariable({
componentOf: device,
nodeId: "s=process_name", // a string nodeID
browseName: "ProcessName",
dataType: "Float",
minimumSamplingInterval: 100,
value: {
timestamped_get: () =>
new DataValue({
statusCode: StatusCodes.Good,
sourceTimestamp: new Date(),
serverTimestamp: new Date(),
value: new Variant({ dataType: DataType.Float, value: Math.random() })
})
}
});
server.start(function() {
console.log("Server is now listening ... ( press CTRL+C to stop)");
console.log("port ", server.endpoints[0].port);
const endpointUrl = server.endpoints[0].endpointDescriptions()[0].endpointUrl;
console.log(" the primary server endpoint url is ", endpointUrl);
});
})();
Заключение
Мы создали базовый OPC UA сервер с помощью Node.js и библиотеки node-opcua. Вы можете расширить этот сервер, добавляя собственные объекты и переменные, чтобы удовлетворить ваши требования.