1 feat: 将 OpcUaEndpointUrl 移动到 Device 并更新 OpcUaBackgroundService
2 将 OpcUaEndpointUrl 属性从 VariableData 和 DbVariableData 移动到 Device 和 DbDevice。 3 更新 OpcUaBackgroundService 以从关联的 Device 对象中检索 OpcUaEndpointUrl。 4 确保 DataServices 和 VarDataRepository 正确加载关联的 VariableTable 和 Device 数据。 5 在 VariableData 模型中添加 VariableTable 属性以正确解析。
This commit is contained in:
13
.idea/.idea.PMSWPF/.idea/.gitignore
generated
vendored
Normal file
13
.idea/.idea.PMSWPF/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider 忽略的文件
|
||||
/.idea.PMSWPF.iml
|
||||
/projectSettingsUpdater.xml
|
||||
/modules.xml
|
||||
/contentModel.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
4
.idea/.idea.PMSWPF/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.PMSWPF/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
||||
8
.idea/.idea.PMSWPF/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.PMSWPF/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/.idea.PMSWPF/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.PMSWPF/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -79,6 +79,12 @@ public class DbDevice
|
||||
[SugarColumn(ColumnDataType = "varchar(20)", SqlParameterDbType = typeof(EnumToStringConvert))]
|
||||
public ProtocolType ProtocolType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OPC UA Endpoint URL。
|
||||
/// </summary>
|
||||
[SugarColumn(IsNullable = true)]
|
||||
public string? OpcUaEndpointUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备关联的变量表列表。
|
||||
/// </summary>
|
||||
|
||||
@@ -89,7 +89,7 @@ public class DeviceRepository
|
||||
/// </summary>
|
||||
/// <param name="id">设备ID。</param>
|
||||
/// <returns>对应的DbDevice对象。</returns>
|
||||
public async Task<DbDevice> GetById(int id)
|
||||
public async Task<Device> GetById(int id)
|
||||
{
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
@@ -99,7 +99,7 @@ public class DeviceRepository
|
||||
.FirstAsync(p => p.Id == id);
|
||||
stopwatch.Stop();
|
||||
NlogHelper.Info($"根据ID '{id}' 获取设备耗时:{stopwatch.ElapsedMilliseconds}ms");
|
||||
return result;
|
||||
return result.CopyTo<Device>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,4 +252,6 @@ public class DeviceRepository
|
||||
await _menuRepository.AddVarTableMenu(addDevice, addDeviceMenuId, db);
|
||||
return addDevice.CopyTo<Device>();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -46,6 +46,8 @@ public class VarDataRepository
|
||||
using (var _db = DbContext.GetInstance())
|
||||
{
|
||||
var result = await _db.Queryable<DbVariableData>()
|
||||
.Includes(d => d.VariableTable)
|
||||
.Includes(d => d.VariableTable.Device)
|
||||
.ToListAsync();
|
||||
stopwatch.Stop();
|
||||
NlogHelper.Info($"获取所有VariableData耗时:{stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
@@ -88,6 +88,12 @@ public partial class Device : ObservableObject
|
||||
/// </summary>
|
||||
public ProtocolType ProtocolType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OPC UA Endpoint URL。
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private string? opcUaEndpointUrl;
|
||||
|
||||
/// <summary>
|
||||
/// 设备关联的变量表列表。
|
||||
/// </summary>
|
||||
|
||||
@@ -154,6 +154,11 @@ public partial class VariableData : ObservableObject
|
||||
/// </summary>
|
||||
public int VariableTableId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联的变量表实体。
|
||||
/// </summary>
|
||||
public VariableTable? VariableTable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 关联的MQTT配置列表。
|
||||
/// </summary>
|
||||
|
||||
@@ -193,6 +193,16 @@ public partial class DataServices : ObservableRecipient, IRecipient<LoadMessage>
|
||||
return await _varDataRepository.GetAllAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步根据ID获取设备数据。
|
||||
/// </summary>
|
||||
/// <param name="id">设备ID。</param>
|
||||
/// <returns>设备对象,如果不存在则为null。</returns>
|
||||
public async Task<Device> GetDeviceByIdAsync(int id)
|
||||
{
|
||||
return await _deviceRepository.GetById(id);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步加载变量数据。
|
||||
/// </summary>
|
||||
|
||||
@@ -150,9 +150,14 @@ namespace PMSWPF.Services
|
||||
foreach (var id in variablesToRemove)
|
||||
{
|
||||
if (_opcUaVariables.TryGetValue(id, out var variable))
|
||||
{
|
||||
// 获取关联的设备信息
|
||||
var device = await _dataServices.GetDeviceByIdAsync(variable.VariableTable.DeviceId??0);
|
||||
if (device != null)
|
||||
{
|
||||
// 断开与该变量相关的 OPC UA 会话。
|
||||
await DisconnectOpcUaSession(variable.OpcUaEndpointUrl);
|
||||
await DisconnectOpcUaSession(device.OpcUaEndpointUrl);
|
||||
}
|
||||
_opcUaVariables.Remove(id);
|
||||
}
|
||||
}
|
||||
@@ -160,20 +165,28 @@ namespace PMSWPF.Services
|
||||
// 处理新增或更新的变量。
|
||||
foreach (var variable in opcUaVariables)
|
||||
{
|
||||
// 获取关联的设备信息
|
||||
var device = await _dataServices.GetDeviceByIdAsync(variable.VariableTable.DeviceId??0);
|
||||
if (device == null)
|
||||
{
|
||||
NlogHelper.Warn($"变量 '{variable.Name}' (ID: {variable.Id}) 关联的设备不存在。");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_opcUaVariables.ContainsKey(variable.Id))
|
||||
{
|
||||
// 如果是新变量,则添加到字典并建立连接和订阅。
|
||||
_opcUaVariables.Add(variable.Id, variable);
|
||||
await ConnectAndSubscribeOpcUa(variable);
|
||||
await ConnectAndSubscribeOpcUa(variable, device);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果变量已存在,则更新其信息。
|
||||
_opcUaVariables[variable.Id] = variable;
|
||||
// 如果终结点 URL 对应的会话已断开,则尝试重新连接。
|
||||
if (_opcUaSessions.ContainsKey(variable.OpcUaEndpointUrl) && !_opcUaSessions[variable.OpcUaEndpointUrl].Connected)
|
||||
if (_opcUaSessions.ContainsKey(device.OpcUaEndpointUrl) && !_opcUaSessions[device.OpcUaEndpointUrl].Connected)
|
||||
{
|
||||
await ConnectAndSubscribeOpcUa(variable);
|
||||
await ConnectAndSubscribeOpcUa(variable, device);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -183,10 +196,11 @@ namespace PMSWPF.Services
|
||||
/// 连接到 OPC UA 服务器并订阅指定的变量。
|
||||
/// </summary>
|
||||
/// <param name="variable">要订阅的变量信息。</param>
|
||||
private async Task ConnectAndSubscribeOpcUa(VariableData variable)
|
||||
/// <param name="device">变量所属的设备信息。</param>
|
||||
private async Task ConnectAndSubscribeOpcUa(VariableData variable, Device device)
|
||||
{
|
||||
NlogHelper.Info($"正在为变量 '{variable.Name}' 连接和订阅 OPC UA 服务器...");
|
||||
if (string.IsNullOrEmpty(variable.OpcUaEndpointUrl) || string.IsNullOrEmpty(variable.OpcUaNodeId))
|
||||
if (string.IsNullOrEmpty(device.OpcUaEndpointUrl) || string.IsNullOrEmpty(variable.OpcUaNodeId))
|
||||
{
|
||||
NlogHelper.Warn($"OPC UA variable {variable.Name} has invalid EndpointUrl or NodeId.");
|
||||
return;
|
||||
@@ -194,9 +208,9 @@ namespace PMSWPF.Services
|
||||
|
||||
Session session = null;
|
||||
// 检查是否已存在到该终结点的活动会话。
|
||||
if (_opcUaSessions.TryGetValue(variable.OpcUaEndpointUrl, out session) && session.Connected)
|
||||
if (_opcUaSessions.TryGetValue(device.OpcUaEndpointUrl, out session) && session.Connected)
|
||||
{
|
||||
NlogHelper.Info($"Already connected to OPC UA endpoint: {variable.OpcUaEndpointUrl}");
|
||||
NlogHelper.Info($"Already connected to OPC UA endpoint: {device.OpcUaEndpointUrl}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -221,12 +235,12 @@ namespace PMSWPF.Services
|
||||
}
|
||||
|
||||
// 4. 发现服务器提供的终结点。
|
||||
DiscoveryClient discoveryClient = DiscoveryClient.Create(new Uri(variable.OpcUaEndpointUrl));
|
||||
EndpointDescriptionCollection endpoints = discoveryClient.GetEndpoints(new Opc.Ua.StringCollection { variable.OpcUaEndpointUrl });
|
||||
DiscoveryClient discoveryClient = DiscoveryClient.Create(new Uri(device.OpcUaEndpointUrl));
|
||||
EndpointDescriptionCollection endpoints = discoveryClient.GetEndpoints(new Opc.Ua.StringCollection { device.OpcUaEndpointUrl });
|
||||
|
||||
// 简化处理:选择第一个无安全策略的终结点。在生产环境中应选择合适的安全策略。
|
||||
// ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(null, endpoints.First(e => e.SecurityMode == MessageSecurityMode.None), config);
|
||||
EndpointDescription selectedEndpoint = CoreClientUtils.SelectEndpoint(application.ApplicationConfiguration, variable.OpcUaEndpointUrl, false);
|
||||
EndpointDescription selectedEndpoint = CoreClientUtils.SelectEndpoint(application.ApplicationConfiguration, device.OpcUaEndpointUrl, false);
|
||||
EndpointConfiguration endpointConfiguration = EndpointConfiguration.Create(application.ApplicationConfiguration);
|
||||
ConfiguredEndpoint configuredEndpoint = new ConfiguredEndpoint(null, selectedEndpoint, endpointConfiguration);
|
||||
|
||||
@@ -240,9 +254,9 @@ namespace PMSWPF.Services
|
||||
new UserIdentity(new AnonymousIdentityToken()), // 使用匿名用户身份
|
||||
null);
|
||||
|
||||
_opcUaSessions[variable.OpcUaEndpointUrl] = session;
|
||||
NlogHelper.Info($"Connected to OPC UA server: {variable.OpcUaEndpointUrl}");
|
||||
NotificationHelper.ShowSuccess($"已连接到 OPC UA 服务器: {variable.OpcUaEndpointUrl}");
|
||||
_opcUaSessions[device.OpcUaEndpointUrl] = session;
|
||||
NlogHelper.Info($"Connected to OPC UA server: {device.OpcUaEndpointUrl}");
|
||||
NotificationHelper.ShowSuccess($"已连接到 OPC UA 服务器: {device.OpcUaEndpointUrl}");
|
||||
|
||||
// 6. 创建订阅。
|
||||
Subscription subscription = new Subscription(session.DefaultSubscription);
|
||||
@@ -250,7 +264,7 @@ namespace PMSWPF.Services
|
||||
session.AddSubscription(subscription);
|
||||
subscription.Create();
|
||||
|
||||
_opcUaSubscriptions[variable.OpcUaEndpointUrl] = subscription;
|
||||
_opcUaSubscriptions[device.OpcUaEndpointUrl] = subscription;
|
||||
|
||||
// 7. 创建监控项并添加到订阅中。
|
||||
MonitoredItem monitoredItem = new MonitoredItem(subscription.DefaultItem);
|
||||
@@ -268,8 +282,8 @@ namespace PMSWPF.Services
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NlogHelper.Error($"连接或订阅 OPC UA 服务器失败: {variable.OpcUaEndpointUrl} - {ex.Message}", ex);
|
||||
NotificationHelper.ShowError($"连接或订阅 OPC UA 服务器失败: {variable.OpcUaEndpointUrl} - {ex.Message}", ex);
|
||||
NlogHelper.Error($"连接或订阅 OPC UA 服务器失败: {device.OpcUaEndpointUrl} - {ex.Message}", ex);
|
||||
NotificationHelper.ShowError($"连接或订阅 OPC UA 服务器失败: {device.OpcUaEndpointUrl} - {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user