学习OPC UA,连接OPC UA服务器
流程图


服务端
import {OPCUAServer,DataType,
} from "node-opcua";const SERVER_PORT = 4840;
const RESOURCE_PATH = "";
async function createTestServer() {try {const server = new OPCUAServer({port: SERVER_PORT,resourcePath: RESOURCE_PATH,buildInfo: {productName: "PLC Simulator",buildNumber: "1.0.0",buildDate: new Date(),manufacturerName: "Test Environment"}});await server.initialize();console.log("✓ 服务器初始化完成");// ============================================================// 第一步:获取地址空间(理解为打开一个文件系统)// ============================================================const addressSpace = server.engine.addressSpace;if (!addressSpace) {console.error("❌ 错误:无法获取地址空间");throw new Error("无法获取地址空间");}console.log("✓ 地址空间获取成功");// 获取命名空间(你的专属区域,不会和其他厂商冲突)const namespace = addressSpace.getOwnNamespace();const namespaceIndex = namespace.index;console.log(`✓ 命名空间获取成功 (ns=${namespaceIndex})`);// ============================================================// 第二步:创建"对象"(相当于创建一个文件夹)// ============================================================// 创建一个名为 "Machine" 的对象(文件夹)// 这个对象本身不存储数据,只是用来组织下面的变量const machineDevice = namespace.addObject({organizedBy: addressSpace.rootFolder.objects, // 放在根目录的 Objects下browseName: "Machine", // 显示名称:MachinenodeId: "s=Machine" // 相对 nodeId(自动使用当前命名空间)});console.log("✓ Machine 对象创建成功");// ============================================================// 第四步:在 Machine 对象里添加变量(相当于在文件夹里创建文件)// =============================================================namespace.addVariable({componentOf: machineDevice, // 放在 Machine 对象(文件夹)里browseName: "TEST", // 显示名称:TESTnodeId: "s=Machine.TEST", // 相对 nodeIddataType: "Double", // 数据类型:双精度浮点数value: {dataType: DataType.Double, value: 6.01}});console.log(`✓ TEST 变量创建成功`);// ============================================================// 最终结构:// Root/// └── Objects/// └── Machine/ (对象 - 文件夹)// ├── TEST = 6.01 (变量 - 文件,存储周向电流)// ============================================================// 启动服务器await server.start();const endpointUrl = server.endpoints[0].endpointDescriptions()[0].endpointUrl;console.log(`\n📡 服务器端点: ${endpointUrl}`);console.log(`🔌 可用节点:`);console.log(` • ns=${namespaceIndex};s=Machine.TEST`);process.on("SIGINT", async () => {console.log("\n\n正在关闭服务器...");await server.shutdown();console.log("✓ 服务器已安全关闭\n");process.exit(0);});} catch (err) {console.error("\n❌ 服务器启动失败:", err);process.exit(1);}
}
// 启动服务器
createTestServer();
客户端
import {OPCUAClient,MessageSecurityMode,SecurityPolicy,AttributeIds,StatusCodes,ClientSession,DataValue
} from 'node-opcua';// 固定的 OPC UA 配置
const OPCUA_ENDPOINT_URL = "opc.tcp://127.0.0.1:4840";
const OPCUA_TEST_NODE_ID = "ns=1;s=Machine.TEST";class OpcUaClient {private client: any = null;private session: ClientSession | null = null;/*** 初始化 OPC UA 客户端,连接服务器,创建会话*/public async initialize() {try {// 创建客户端实例this.client = OPCUAClient.create({applicationName: 'PLCClient',securityMode: MessageSecurityMode.None,securityPolicy: SecurityPolicy.None,endpoint_must_exist: false,connectionStrategy: {initialDelay: 1000,maxRetry: 3}});// 连接服务器await this.client.connect(OPCUA_ENDPOINT_URL);// 创建会话this.session = await this.client.createSession();} catch (err) {console.error('[OpcUaClient] Initialize failed:', err);await this.cleanup();}}/*** 读取 TEST 数据*/public async readData() {if (!this.session) {try {await this.initialize();} catch (_e) {console.error('[OpcUaClient] Failed to initialize:', _e);}if (!this.session) return;}try {// 直接读取TEST节点const read ={nodeId: OPCUA_TEST_NODE_ID,attributeId: AttributeIds.Value}// 批量读取const dataValue: DataValue = await this.session.read(read);let value = 0;// 处理 TEST 值if (dataValue.statusCode === StatusCodes.Good) {value = dataValue.value?.value ?? 0;} else {console.warn(`[OpcUaClient] Failed to read REST:`, dataValue.statusCode.toString());}return value;} catch (err) {console.error('[OpcUaClient] Read data error:', err);try {await this.cleanup();await this.initialize();} catch (_e) {console.error('[OpcUaClient] Failed to initialize:', _e);}return;}}/*** 清理资源*/private async cleanup() {// 关闭会话if (this.session) {try {await this.session.close();} catch (err) {console.error('[OpcUaClient] Failed to close session:', err);}this.session = null;}// 断开连接if (this.client) {try {await this.client.disconnect();} catch (err) {console.error('[OpcUaClient] Failed to disconnect:', err);}this.client = null;}}
}
