diff --git a/DMS.Core/Enums/CSharpDataType.cs b/DMS.Core/Enums/CSharpDataType.cs
new file mode 100644
index 0000000..5d41c54
--- /dev/null
+++ b/DMS.Core/Enums/CSharpDataType.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace DMS.Core.Enums
+{
+ ///
+ /// 定义了C#中常用的数据类型。
+ ///
+ public enum CSharpDataType
+ {
+ // 基本数值类型
+ Bool,
+ Byte,
+ SByte,
+ Short,
+ UShort,
+ Int,
+ UInt,
+ Long,
+ ULong,
+ Float,
+ Double,
+ Decimal,
+
+ // 字符和字符串类型
+ Char,
+ String,
+
+ // 时间相关类型
+ DateTime,
+ TimeSpan,
+ DateTimeOffset,
+
+ // 其他常用类型
+ Guid,
+ Object,
+ ByteArray,
+
+ // 可空类型标识
+ Nullable,
+
+ // 未知类型
+ Unknown
+ }
+}
diff --git a/DMS.Core/Enums/SignalType.cs b/DMS.Core/Enums/SignalType.cs
index f4c98c7..fca76de 100644
--- a/DMS.Core/Enums/SignalType.cs
+++ b/DMS.Core/Enums/SignalType.cs
@@ -16,24 +16,3 @@ public enum SignalType
[Description("其他信号")] OtherASignal
}
-///
-/// 定义了C#中常用的数据类型。
-///
-public enum CSharpDataType
-{
- [Description("布尔型")] Bool,
- [Description("字节型")] Byte,
- [Description("短整型")] Short,
- [Description("无符号短整型")] UShort,
- [Description("整型")] Int,
- [Description("无符号整型")] UInt,
- [Description("长整型")] Long,
- [Description("无符号长整型")] ULong,
- [Description("浮点型")] Float,
- [Description("双精度浮点型")] Double,
- [Description("字符串型")] String,
- [Description("日期时间型")] DateTime,
- [Description("时间跨度型")] TimeSpan,
- [Description("对象型")] Object,
- [Description("未知类型")] Unknown
-}
\ No newline at end of file
diff --git a/DMS.Infrastructure.UnitTests/Services/OpcUaServiceTest.cs b/DMS.Infrastructure.UnitTests/Services/OpcUaServiceTest.cs
index c6ab823..603b779 100644
--- a/DMS.Infrastructure.UnitTests/Services/OpcUaServiceTest.cs
+++ b/DMS.Infrastructure.UnitTests/Services/OpcUaServiceTest.cs
@@ -82,7 +82,7 @@ namespace DMS.Infrastructure.UnitTests.Services
// Act & Assert
await Assert.ThrowsAsync(async () =>
{
- await service.ConnectAsync();
+ await service.ConnectAsync("opc.tcp://localhost:4840");
});
}
diff --git a/DMS.Infrastructure/Interfaces/Services/IOpcUaService.cs b/DMS.Infrastructure/Interfaces/Services/IOpcUaService.cs
index 05e9f6e..09c7948 100644
--- a/DMS.Infrastructure/Interfaces/Services/IOpcUaService.cs
+++ b/DMS.Infrastructure/Interfaces/Services/IOpcUaService.cs
@@ -5,20 +5,98 @@ using System.Threading.Tasks;
namespace DMS.Infrastructure.Interfaces.Services
{
+ ///
+ /// OPC UA服务接口,定义了与OPC UA服务器进行通信所需的方法
+ ///
public interface IOpcUaService
{
+ ///
+ /// 获取当前连接状态
+ ///
bool IsConnected { get; }
- Task ConnectAsync(string serviceURL);
+
+ ///
+ /// 异步连接到OPC UA服务器
+ ///
+ /// 表示异步操作的任务
+ Task ConnectAsync(string serverUrl);
+
+ ///
+ /// 异步断开与OPC UA服务器的连接
+ ///
+ /// 表示异步操作的任务
Task DisconnectAsync();
+
+ ///
+ /// 浏览指定节点的子节点
+ ///
+ /// 要浏览的节点,如果为null则浏览根节点
+ /// 表示异步操作的任务,包含子节点列表
Task> BrowseNode(OpcUaNode? nodeToBrowse);
+
+ ///
+ /// 订阅单个节点的数据变化
+ ///
+ /// 要订阅的节点
+ /// 数据变化时的回调方法
+ /// 发布间隔(毫秒)
+ /// 采样间隔(毫秒)
void SubscribeToNode(OpcUaNode node, Action onDataChange, int publishingInterval = 1000, int samplingInterval = 500);
+
+ ///
+ /// 订阅多个节点的数据变化
+ ///
+ /// 要订阅的节点列表
+ /// 数据变化时的回调方法
+ /// 发布间隔(毫秒)
+ /// 采样间隔(毫秒)
void SubscribeToNode(List nodes, Action onDataChange, int publishingInterval = 1000, int samplingInterval = 500);
+
+ ///
+ /// 取消订阅单个节点
+ ///
+ /// 要取消订阅的节点
void UnsubscribeFromNode(OpcUaNode node);
+
+ ///
+ /// 取消订阅多个节点
+ ///
+ /// 要取消订阅的节点列表
void UnsubscribeFromNode(List nodes);
+
+ ///
+ /// 获取当前已订阅的所有节点
+ ///
+ /// 已订阅节点的列表
List GetSubscribedNodes();
+
+ ///
+ /// 异步读取单个节点的值
+ ///
+ /// 要读取的节点
+ /// 表示异步操作的任务
Task ReadNodeValueAsync(OpcUaNode node);
+
+ ///
+ /// 异步读取多个节点的值
+ ///
+ /// 要读取的节点列表
+ /// 表示异步操作的任务
Task ReadNodeValuesAsync(List nodes);
+
+ ///
+ /// 异步写入单个节点的值
+ ///
+ /// 要写入的节点
+ /// 要写入的值
+ /// 表示异步操作的任务,如果写入成功返回true,否则返回false
Task WriteNodeValueAsync(OpcUaNode node, object value);
+
+ ///
+ /// 异步写入多个节点的值
+ ///
+ /// 要写入的节点及其对应值的字典
+ /// 表示异步操作的任务,如果所有写入都成功返回true,否则返回false
Task WriteNodeValuesAsync(Dictionary nodesToWrite);
}
}
diff --git a/DMS.Infrastructure/Models/OpcUaNode.cs b/DMS.Infrastructure/Models/OpcUaNode.cs
index c79079c..8210b78 100644
--- a/DMS.Infrastructure/Models/OpcUaNode.cs
+++ b/DMS.Infrastructure/Models/OpcUaNode.cs
@@ -1,3 +1,4 @@
+using DMS.Core.Enums;
using Opc.Ua;
namespace DMS.Infrastructure.Models
@@ -36,6 +37,10 @@ namespace DMS.Infrastructure.Models
/// 子节点列表
///
public List Children { get; set; } = new List();
+ ///
+ /// 数据类型
+ ///
+ public CSharpDataType DataType { get; set; }
///
/// 返回节点的字符串表示形式。
diff --git a/DMS.Infrastructure/Services/OpcUaService.cs b/DMS.Infrastructure/Services/OpcUaService.cs
index d3f193b..c96333c 100644
--- a/DMS.Infrastructure/Services/OpcUaService.cs
+++ b/DMS.Infrastructure/Services/OpcUaService.cs
@@ -1,6 +1,6 @@
+using DMS.Core.Enums;
using DMS.Infrastructure.Interfaces.Services;
using DMS.Infrastructure.Models;
-using Microsoft.IdentityModel.Tokens;
using Opc.Ua;
using Opc.Ua.Client;
using Opc.Ua.Configuration;
@@ -14,16 +14,15 @@ namespace DMS.Infrastructure.Services
public class OpcUaService : IOpcUaService
{
private readonly ApplicationConfiguration _config;
- private string _serverUrl;
- private Session _session;
- private Subscription _subscription;
- private readonly Dictionary _subscribedNodes = new Dictionary();
+ private string? _serverUrl;
+ private Session? _session;
+ private Subscription? _subscription;
+ private readonly Dictionary _subscribedNodes = new();
public bool IsConnected => _session != null && _session.Connected;
public OpcUaService()
{
-
_config = CreateApplicationConfiguration();
}
@@ -31,11 +30,9 @@ namespace DMS.Infrastructure.Services
{
try
{
- _serverUrl = serverUrl.ToUpper();
- if (_serverUrl.IsNullOrEmpty() || !(_serverUrl.StartsWith("OPC.TCP://")))
- {
- throw new Exception($"serverUrl服务器地址无效,serverUrl:{_serverUrl}");
- }
+ // 保存服务器URL
+ _serverUrl = serverUrl;
+
// 验证客户端应用程序配置的有效性。
await _config.Validate(ApplicationType.Client);
@@ -43,7 +40,10 @@ namespace DMS.Infrastructure.Services
// 这在开发和测试中很方便,但在生产环境中应谨慎使用。
if (_config.SecurityConfiguration.AutoAcceptUntrustedCertificates)
{
- _config.CertificateValidator.CertificateValidation += (s, e) => { e.Accept = e.Error.StatusCode == StatusCodes.BadCertificateUntrusted; };
+ _config.CertificateValidator.CertificateValidation += (s, e) =>
+ {
+ e.Accept = e.Error.StatusCode == StatusCodes.BadCertificateUntrusted;
+ };
}
// 创建一个应用程序实例,它代表了客户端应用程序。
@@ -86,12 +86,19 @@ namespace DMS.Infrastructure.Services
}
}
- public async Task> BrowseNode(OpcUaNode nodeToBrowse)
+ public async Task> BrowseNode(OpcUaNode? nodeToBrowse)
{
if (!IsConnected)
{
throw new InvalidOperationException("会话未连接。请在浏览节点前调用ConnectAsync方法。");
}
+
+ // 检查节点是否为null
+ if (nodeToBrowse == null)
+ {
+ throw new ArgumentNullException(nameof(nodeToBrowse), "要浏览的节点不能为null。");
+ }
+
var nodes = new List();
try
{
@@ -113,20 +120,8 @@ namespace DMS.Infrastructure.Services
out continuationPoint, // continuationPoint: 输出参数,用于处理分页
out references); // references: 输出参数,浏览到的节点引用集合
- if (references != null)
- {
- // 遍历第一次返回的结果
- foreach (var rd in references)
- {
- nodes.Add(new OpcUaNode
- {
- ParentNode = nodeToBrowse,
- NodeId = (NodeId)rd.NodeId,
- DisplayName = rd.DisplayName.Text,
- NodeClass = rd.NodeClass
- });
- }
- }
+ // 处理浏览结果
+ await ProcessBrowseResults(references, nodeToBrowse, nodes);
// 如果continuationPoint不为null,说明服务器还有数据未返回,需要循环调用BrowseNext获取
while (continuationPoint != null)
@@ -134,21 +129,10 @@ namespace DMS.Infrastructure.Services
// 调用BrowseNext获取下一批数据
_session.BrowseNext(null, false, continuationPoint, out continuationPoint, out references);
- if (references != null)
- {
- // 遍历后续批次返回的结果
- foreach (var rd in references)
- {
- nodes.Add(new OpcUaNode
- {
- ParentNode = nodeToBrowse,
- NodeId = (NodeId)rd.NodeId,
- DisplayName = rd.DisplayName.Text,
- NodeClass = rd.NodeClass
- });
- }
- }
+ // 处理后续批次的浏览结果
+ await ProcessBrowseResults(references, nodeToBrowse, nodes);
}
+
// 将找到的子节点列表关联到父节点
nodeToBrowse.Children = nodes;
}
@@ -159,6 +143,54 @@ namespace DMS.Infrastructure.Services
return nodes;
}
+ ///
+ /// 处理浏览结果
+ ///
+ /// 浏览到的节点引用集合
+ /// 父节点
+ /// 节点列表
+ private async Task ProcessBrowseResults(ReferenceDescriptionCollection references, OpcUaNode parentNode, List nodes)
+ {
+ if (references == null)
+ return;
+
+ // 收集所有变量节点,用于批量读取数据类型
+ var variableNodes = new List();
+
+ // 遍历返回的结果
+ foreach (var rd in references)
+ {
+ var node = new OpcUaNode
+ {
+ ParentNode = parentNode,
+ NodeId = (NodeId)rd.NodeId,
+ DisplayName = rd.DisplayName.Text,
+ NodeClass = rd.NodeClass
+ };
+
+ // 如果是变量节点,添加到列表中稍后批量处理
+ if (rd.NodeClass == NodeClass.Variable)
+ {
+ variableNodes.Add(node);
+ }
+
+ nodes.Add(node);
+ }
+
+ // 批量读取变量节点的数据类型
+ if (variableNodes.Any())
+ {
+ try
+ {
+ await ReadNodeDataTypesAsync(variableNodes);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"批量读取节点数据类型时发生错误: {ex.Message}");
+ }
+ }
+ }
+
public void SubscribeToNode(OpcUaNode node, Action onDataChange, int publishingInterval = 1000, int samplingInterval = 500)
{
SubscribeToNode(new List { node }, onDataChange, publishingInterval, samplingInterval);
@@ -171,11 +203,53 @@ namespace DMS.Infrastructure.Services
{
throw new InvalidOperationException("会话未连接。请在订阅节点前调用ConnectAsync方法。");
}
+
// 检查节点列表是否有效
if (nodes == null || !nodes.Any())
{
return;
}
+
+ // 确保订阅对象存在
+ EnsureSubscriptionExists(publishingInterval);
+
+ // 创建一个用于存放待添加监视项的列表
+ var itemsToAdd = new List();
+
+ // 遍历所有请求订阅的节点
+ foreach (var node in nodes)
+ {
+ // 如果节点已经存在于我们的跟踪列表中,则跳过,避免重复订阅
+ if (_subscribedNodes.ContainsKey(node.NodeId))
+ {
+ continue;
+ }
+
+ // 为每个节点创建一个监视项
+ var monitoredItem = CreateMonitoredItem(node, onDataChange, samplingInterval);
+
+ // 将创建的监视项添加到待添加列表
+ itemsToAdd.Add(monitoredItem);
+ // 将节点添加到我们的跟踪字典中
+ _subscribedNodes[node.NodeId] = node;
+ }
+
+ // 如果有新的监视项要添加
+ if (itemsToAdd.Any())
+ {
+ // 将所有新的监视项批量添加到订阅中
+ _subscription.AddItems(itemsToAdd);
+ // 将所有挂起的更改(包括订阅属性修改和添加新项)应用到服务器
+ _subscription.ApplyChanges();
+ }
+ }
+
+ ///
+ /// 确保订阅对象存在
+ ///
+ /// 发布间隔
+ private void EnsureSubscriptionExists(int publishingInterval)
+ {
// 如果还没有订阅对象,则基于会话的默认设置创建一个新的订阅
if (_subscription == null)
{
@@ -194,56 +268,43 @@ namespace DMS.Infrastructure.Services
{
_subscription.PublishingInterval = publishingInterval;
}
+ }
- // 创建一个用于存放待添加监视项的列表
- var itemsToAdd = new List();
- // 遍历所有请求订阅的节点
- foreach (var node in nodes)
+ ///
+ /// 创建监视项
+ ///
+ /// OPC UA节点
+ /// 数据变化回调
+ /// 采样间隔
+ /// 监视项
+ private MonitoredItem CreateMonitoredItem(OpcUaNode node, Action onDataChange, int samplingInterval)
+ {
+ var monitoredItem = new MonitoredItem(_subscription.DefaultItem)
{
- // 如果节点已经存在于我们的跟踪列表中,则跳过,避免重复订阅
- if (_subscribedNodes.ContainsKey(node.NodeId))
+ DisplayName = node.DisplayName,
+ StartNodeId = node.NodeId,
+ AttributeId = Attributes.Value, // 我们关心的是节点的值属性
+ SamplingInterval = samplingInterval // 服务器采样节点值的速率(毫秒)
+ };
+
+ // 设置数据变化通知的回调函数
+ monitoredItem.Notification += (item, e) =>
+ {
+ // 将通知事件参数转换为MonitoredItemNotification
+ if (e.NotificationValue is MonitoredItemNotification notification)
{
- continue;
- }
- // 为每个节点创建一个监视项
- var monitoredItem = new MonitoredItem(_subscription.DefaultItem)
- {
- DisplayName = node.DisplayName,
- StartNodeId = node.NodeId,
- AttributeId = Attributes.Value, // 我们关心的是节点的值属性
- SamplingInterval = samplingInterval // 服务器采样节点值的速率(毫秒)
- };
- // 设置数据变化通知的回调函数
- monitoredItem.Notification += (item, e) =>
- {
- // 将通知事件参数转换为MonitoredItemNotification
- var notification = e.NotificationValue as MonitoredItemNotification;
- if (notification != null)
+ // 通过StartNodeId从我们的跟踪字典中找到对应的OpcUaNode对象
+ if (_subscribedNodes.TryGetValue(item.StartNodeId, out var changedNode))
{
- // 通过StartNodeId从我们的跟踪字典中找到对应的OpcUaNode对象
- if (_subscribedNodes.TryGetValue(item.StartNodeId, out var changedNode))
- {
- // 更新节点对象的值
- changedNode.Value = notification.Value.Value;
- // 调用用户提供的回调函数,并传入更新后的节点
- onDataChange?.Invoke(changedNode);
- }
+ // 更新节点对象的值
+ changedNode.Value = notification.Value.Value;
+ // 调用用户提供的回调函数,并传入更新后的节点
+ onDataChange?.Invoke(changedNode);
}
- };
- // 将创建的监视项添加到待添加列表
- itemsToAdd.Add(monitoredItem);
- // 将节点添加到我们的跟踪字典中
- _subscribedNodes[node.NodeId] = node;
- }
-
- // 如果有新的监视项要添加
- if (itemsToAdd.Any())
- {
- // 将所有新的监视项批量添加到订阅中
- _subscription.AddItems(itemsToAdd);
- // 将所有挂起的更改(包括订阅属性修改和添加新项)应用到服务器
- _subscription.ApplyChanges();
- }
+ }
+ };
+
+ return monitoredItem;
}
public void UnsubscribeFromNode(OpcUaNode node)
@@ -301,15 +362,24 @@ namespace DMS.Infrastructure.Services
return;
}
- // 创建一个用于存放读取请求的集合
- var nodesToRead = new ReadValueIdCollection();
- // 创建一个列表,用于在收到响应后按顺序查找对应的OpcUaNode
- var nodeListForLookup = new List();
- // 遍历所有请求读取的节点
- foreach (var node in nodes)
+ // 筛选出变量类型的节点,因为只有变量才有值
+ var variableNodes = nodes.Where(n => n.NodeClass == NodeClass.Variable).ToList();
+
+ // 如果没有需要读取的变量节点,则直接返回
+ if (!variableNodes.Any())
{
- // 只处理变量类型的节点,因为只有变量才有值
- if (node.NodeClass == NodeClass.Variable)
+ return;
+ }
+
+ try
+ {
+ // 创建一个用于存放读取请求的集合
+ var nodesToRead = new ReadValueIdCollection();
+ // 创建一个列表,用于在收到响应后按顺序查找对应的OpcUaNode
+ var nodeListForLookup = new List();
+
+ // 为每个变量节点创建读取请求
+ foreach (var node in variableNodes)
{
// 创建一个ReadValueId,指定要读取的节点ID和属性(值)
nodesToRead.Add(new ReadValueId
@@ -320,16 +390,7 @@ namespace DMS.Infrastructure.Services
// 将节点添加到查找列表中
nodeListForLookup.Add(node);
}
- }
- // 如果没有需要读取的变量节点,则直接返回
- if (nodesToRead.Count == 0)
- {
- return;
- }
-
- try
- {
// 异步调用Read方法,批量读取所有节点的值
var response = await _session.ReadAsync(
null, // RequestHeader, 使用默认值
@@ -385,24 +446,38 @@ namespace DMS.Infrastructure.Services
return false;
}
+ // 筛选出变量类型的节点,因为只能向变量类型的节点写入值
+ var variableNodesToWrite = nodesToWrite
+ .Where(entry => entry.Key.NodeClass == NodeClass.Variable)
+ .ToList();
+
+ // 如果没有有效的写入请求,则直接返回
+ if (!variableNodesToWrite.Any())
+ {
+ // 输出非变量节点的警告信息
+ var nonVariableNodes = nodesToWrite
+ .Where(entry => entry.Key.NodeClass != NodeClass.Variable)
+ .Select(entry => entry.Key);
+
+ foreach (var node in nonVariableNodes)
+ {
+ Console.WriteLine($"节点 '{node.DisplayName}' 不是变量类型,无法写入。");
+ }
+
+ return false;
+ }
+
// 创建一个用于存放写入请求的集合
var writeValues = new WriteValueCollection();
// 创建一个列表,用于在收到响应后按顺序查找对应的OpcUaNode,以进行错误报告
var nodeListForLookup = new List();
- // 遍历所有请求写入的节点和值
- foreach (var entry in nodesToWrite)
+ // 为每个变量节点创建写入请求
+ foreach (var entry in variableNodesToWrite)
{
var node = entry.Key;
var value = entry.Value;
- // 只能向变量类型的节点写入值
- if (node.NodeClass != NodeClass.Variable)
- {
- Console.WriteLine($"节点 '{node.DisplayName}' 不是变量类型,无法写入。");
- continue; // 跳过非变量节点
- }
-
try
{
// 创建一个WriteValue对象,它封装了写入操作的所有信息
@@ -471,6 +546,125 @@ namespace DMS.Infrastructure.Services
}
}
+ ///
+ /// 批量读取节点的数据类型名称
+ ///
+ /// 节点列表
+ private async Task ReadNodeDataTypesAsync(List nodes)
+ {
+ try
+ {
+ // 创建一个用于存放读取请求的集合
+ var nodesToRead = new ReadValueIdCollection();
+ // 创建一个列表,用于在收到响应后按顺序查找对应的OpcUaNode
+ var nodeListForLookup = new List();
+
+ // 为每个节点创建读取数据类型的请求
+ foreach (var node in nodes)
+ {
+ if (node.NodeId != null)
+ {
+ // 创建一个ReadValueId,指定要读取的节点ID和属性(数据类型)
+ nodesToRead.Add(new ReadValueId
+ {
+ NodeId = node.NodeId,
+ AttributeId = Attributes.DataType
+ });
+ // 将节点添加到查找列表中
+ nodeListForLookup.Add(node);
+ }
+ }
+
+ // 如果没有需要读取的节点,则直接返回
+ if (nodesToRead.Count == 0)
+ {
+ return;
+ }
+
+ // 调用Read方法,批量读取节点的数据类型
+ var response = await _session.ReadAsync(
+ null, // RequestHeader, 使用默认值
+ 0, // maxAge, 0表示直接从设备读取最新值,而不是从缓存读取
+ TimestampsToReturn.Neither, // TimestampsToReturn, 表示我们不关心值的时间戳
+ nodesToRead, // ReadValueIdCollection, 要读取的节点和属性的集合
+ default // CancellationToken
+ );
+
+ // 获取响应中的结果
+ var results = response.Results;
+
+ // 验证响应,确保请求成功
+ ClientBase.ValidateResponse(results, nodesToRead);
+
+ // 遍历返回的结果
+ for (int i = 0; i < results.Count; i++)
+ {
+ // 根据索引找到对应的OpcUaNode
+ var node = nodeListForLookup[i];
+
+ // 检查状态码,确保读取成功
+ if (StatusCode.IsGood(results[i].StatusCode))
+ {
+ // 获取数据类型NodeId
+ if (results[i].Value is NodeId dataTypeId)
+ {
+ // 尝试获取数据类型的友好名称
+ node.DataType = GetDataTypeName(dataTypeId);
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"批量读取节点数据类型时发生错误: {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// 获取数据类型的友好名称
+ ///
+ /// 数据类型NodeId
+ /// 数据类型的友好名称
+ private CSharpDataType GetDataTypeName(NodeId dataTypeId)
+ {
+ if (dataTypeId == null)
+ return CSharpDataType.Unknown;
+
+ // 使用OPC UA内置的类型映射
+ switch (dataTypeId.Identifier.ToString())
+ {
+ case "1": return CSharpDataType.Bool; // Boolean
+ case "2": return CSharpDataType.SByte; // SByte
+ case "3": return CSharpDataType.Byte; // Byte
+ case "4": return CSharpDataType.Short; // Int16
+ case "5": return CSharpDataType.UShort; // UInt16
+ case "6": return CSharpDataType.Int; // Int32
+ case "7": return CSharpDataType.UInt; // UInt32
+ case "8": return CSharpDataType.Long; // Int64
+ case "9": return CSharpDataType.ULong; // UInt64
+ case "10": return CSharpDataType.Float; // Float
+ case "11": return CSharpDataType.Double; // Double
+ case "12": return CSharpDataType.String; // String
+ case "13": return CSharpDataType.DateTime; // DateTime
+ case "14": return CSharpDataType.Guid; // Guid
+ case "15": return CSharpDataType.ByteArray; // ByteString
+ case "16": return CSharpDataType.Object; // XmlElement
+ case "17": return CSharpDataType.Object; // NodeId
+ case "18": return CSharpDataType.Object; // ExpandedNodeId
+ case "19": return CSharpDataType.Object; // StatusCode
+ case "20": return CSharpDataType.Object; // QualifiedName
+ case "21": return CSharpDataType.Object; // LocalizedText
+ case "22": return CSharpDataType.Object; // ExtensionObject
+ case "23": return CSharpDataType.Object; // DataValue
+ case "24": return CSharpDataType.Object; // Variant
+ case "25": return CSharpDataType.Object; // DiagnosticInfo
+ default:
+ // 对于自定义数据类型,返回Unknown
+ return CSharpDataType.Unknown;
+ }
+ }
+
private ApplicationConfiguration CreateApplicationConfiguration()
{
return new ApplicationConfiguration()
diff --git a/DMS.WPF/ViewModels/Dialogs/ImportOpcUaDialogViewModel.cs b/DMS.WPF/ViewModels/Dialogs/ImportOpcUaDialogViewModel.cs
index 64f752d..0370a67 100644
--- a/DMS.WPF/ViewModels/Dialogs/ImportOpcUaDialogViewModel.cs
+++ b/DMS.WPF/ViewModels/Dialogs/ImportOpcUaDialogViewModel.cs
@@ -116,15 +116,9 @@ public partial class ImportOpcUaDialogViewModel : DialogViewModelBase
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
diff --git a/DMS.WPF/Views/Dialogs/ImportOpcUaDialog.xaml.cs b/DMS.WPF/Views/Dialogs/ImportOpcUaDialog.xaml.cs
index 323c8ca..bb26504 100644
--- a/DMS.WPF/Views/Dialogs/ImportOpcUaDialog.xaml.cs
+++ b/DMS.WPF/Views/Dialogs/ImportOpcUaDialog.xaml.cs
@@ -16,8 +16,8 @@ namespace DMS.WPF.Views.Dialogs;
///
public partial class ImportOpcUaDialog : ContentDialog
{
- private const int ContentAreaMaxWidth = 1200;
- private const int ContentAreaMaxHeight = 800;
+ private const int ContentAreaMaxWidth = 1300;
+ private const int ContentAreaMaxHeight = 900;