Opcua 客户端和服务端

一、环境依赖

1、openssl:主要用于实现SSL和TLS协议,提供通讯中的加密。

2、Open62541:一个开源的C语言库,用来实现OPC UA客户端和服务器,也就是我们本文的核心。

3、WS2_32.lib:Windows Sockets应用程序的接口, 用于和网络应用程序。

文章中提供了依赖文件。

二、服务端源程序

#include <open62541.h>
#include <iostream>
#include <atomic>
#include <thread>
#include <string>

static UA_Boolean running(true);

char mulDn[2][40] = { "SharedDataNode1", "SharedDataNode2"};  //节点名称
char l[] = "en"; //语言
static double sharedData[2] = { 1.1, 2.2}; //节点初始数据

UA_Server *server = nullptr;

static std::atomic <bool> starting = true;


void addMultipleNodes()
{
	if (!server)
	{
		std::cerr << "opcua server dose not open....." << std::endl;
		return;
	}
	// 变量节点的属性设置
	UA_VariableAttributes attr = UA_VariableAttributes_default;
	attr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
	attr.valueRank = UA_VALUERANK_SCALAR;
	attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

	for (int i = 0; i < 2; ++i) {
		// 设置每个节点的属性
		attr.displayName = UA_LOCALIZEDTEXT(l, mulDn[i]);
		UA_Variant_setScalar(&attr.value, &sharedData[i], &UA_TYPES[UA_TYPES_DOUBLE]);
		// 请求的新节点 ID
		UA_NodeId requestedNewNodeId = UA_NODEID_STRING(1, mulDn[i]);

		// 父节点 ID (可以设置为 NULL 作为根节点)
		UA_NodeId parentNodeId = UA_NODEID_NULL;//UA_NODEID_NUMERIC(1, UA_NS0ID_OBJECTSFOLDER);

		// 引用类型 ID (可以设置为 NULL)
		UA_NodeId referenceTypeId = UA_NODEID_NULL;

		// 浏览名称
		UA_QualifiedName browseName = UA_QUALIFIEDNAME(1, mulDn[i]);

		// 类型定义 ID (可以设置为 NULL)
		UA_NodeId typeDefinition = UA_NODEID_NULL;

		// 存储输出的新节点 ID
		UA_NodeId outNewNodeId;

		// 添加变量节点到服务器
		UA_StatusCode retval = UA_Server_addVariableNode(server, requestedNewNodeId,
			parentNodeId, referenceTypeId,
			browseName, typeDefinition,
			attr, NULL, &outNewNodeId);
		if (retval != UA_STATUSCODE_GOOD) {
			std::cerr << "Failed to add variable node: " << mulDn[i] << " code " << retval << std::endl;
		}
		else {
			std::cout << "Variable node " << mulDn[i] << " added successfully with NodeId: "
				<< outNewNodeId.identifier.string.data << std::endl;
		}
	}
}

// 启动 OPC UA 服务端
void startServer() {
	starting.store(true);
	// 创建服务器
	server = UA_Server_new();
	UA_ServerConfig_setDefault(UA_Server_getConfig(server)); // 使用默认配置

	addMultipleNodes();

	UA_ValueCallback callback;
	callback.onWrite = myWriteCallback;
	// 为该节点设置写入回调函数
	//UA_Server_setVariableNode_valueCallback(server, newNodeId, callback);

	// 运行服务器
	std::cout << "Starting OPC UA Server..." << std::endl;
	UA_StatusCode retval = UA_Server_run(server, &running);
	if (retval != UA_STATUSCODE_GOOD) {
		std::cerr << "Failed to start the server!" << std::endl;
	}

	// 释放服务器资源
	UA_Server_delete(server);
}

void startOpcua()
{
	std::cout << "Starting OPC UA Server..." << std::endl;

	// 启动服务端线程
	std::thread serverThread(startServer);
	serverThread.detach();
	while(starting.load())
	{
		Sleep(20);
	}
}

void stopOpcua()
{
	starting.store(false);
	running = false;
}

int main() {
	startOpcua();
	return 0;
}

运行后,可以用UA Expert软件查看opcua服务端是否正常,以及查看节点是否正常加载。如果一切正常可以,则可以操作看到下面的结果。不会用UA Expert的小伙伴,也可以继续往下进行,实现客户端,直接读取节点数据。

四、客户端

#include <open62541.h>
#include <stdio.h>

int main() {

	// 创建一个 OPC UA 客户端
	UA_Client *client = UA_Client_new();
	UA_ClientConfig_setDefault(UA_Client_getConfig(client));
	// 连接到 OPC UA 服务器
	UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");  // 使用你的服务器地址
	if (retval != UA_STATUSCODE_GOOD) {
		printf("连接 OPC UA 服务器失败\n");
		UA_Client_delete(client);
		return -1;
	}

	char dn[] = "SharedDataNode1"; //服务端的节点名
	// 定义节点 ID
	UA_NodeId nodeId = UA_NODEID_STRING(1, dn);  // 你要读取的节点ID

	// 读取数据
	UA_Variant value;
	UA_Variant_init(&value);
	retval = UA_Client_readValueAttribute(client, nodeId, &value);

	if (retval == UA_STATUSCODE_GOOD) {
		// 如果读取成功,输出数据
		printf("读取的值: ");
		if (UA_Variant_isScalar(&value)) {
			printf("%f\n", *(UA_Double *)value.data);
		}
	}
	else {
		// 如果读取失败
		printf("读取节点失败\n");
	}

	// 清理
	UA_Variant_clear(&value);
	UA_Client_disconnect(client);
	UA_Client_delete(client);

	return 0;
}

本代码实现的是opcua客户端代码,功能是读取服务端存在的一个节点的数据。程序运行前,需要先运行我们的服务端程序,否则无法连接。服务端正常启动后,再运行我们的客户端,终端上就可以看到读取的数据,下面截图框出来的就是。


除了从服务端读取数据外,还可以往服务器中写数据,修改对应节点的值。

#include <open62541.h>
#include <stdio.h>
#include "OpcuaWrite.h"

int writeToOpc(char *dataNode, double v)
{
	// 创建一个 OPC UA 客户端
	UA_Client *client = UA_Client_new();
	UA_ClientConfig_setDefault(UA_Client_getConfig(client));
	// 连接到 OPC UA 服务器
	UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840"); // 使用你的服务器地址
	if (retval != UA_STATUSCODE_GOOD) {
		printf("连接 OPC UA 服务器失败\n");
		UA_Client_delete(client);
		return -1;
	}

	//char dn[] = "SharedDataNode";
	// 定义目标节点 ID
	UA_NodeId nodeId = UA_NODEID_STRING(1, dataNode); // 目标节点的 ID

	// 创建要写入的值
	UA_Double valueToWrite = v; 

	// 创建一个变体并将数据写入
	UA_Variant value;
	UA_Variant_init(&value);
	value.type = &UA_TYPES[UA_TYPES_DOUBLE]; // 数据类型
	value.data = &valueToWrite;  // 数据值

	// 写入数据到目标节点
	retval = UA_Client_writeValueAttribute(client, nodeId, &value);

	if (retval == UA_STATUSCODE_GOOD) {
		// 如果写入成功,打印成功消息
		printf("成功写入值: %f\n", valueToWrite);
	}
	else {
		// 如果写入失败,打印错误消息
		printf("写入数据失败\n");
	}

	// 清理
	//UA_Variant_clear(&value);
	UA_Client_disconnect(client);
	UA_Client_delete(client);

	return 0;
}

int main() 
{
	char dn[] = "SharedDataNode1";
	return writeToOpc(dn, 25.5);;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值