mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-27 13:47:10 +08:00
build: 10.1.0.1
feat: 变量名称 由 全局唯一 更改为 采集设备内唯一 fix: opcda订阅方式初始化时未更新变量状态,导致后续变了状态一直是offline feat: mqttServer 允许的连接ID前缀 为空时可为任意id 破坏性更新: 1、兼容旧版本数据库,但不兼容旧版本excel文件,不同点在于变量excel 业务属性页,添加了设备名称和业务设备名称,旧版本只有业务设备名称 2、业务上传内容更改为 字典类型,键为设备名称,值为变量列表。变量列表内容与旧版本相同
This commit is contained in:
@@ -125,7 +125,7 @@ public abstract class BaseEntity : PrimaryKeyEntity, IBaseEntity
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, DefaultSort = true, Sortable = true, DefaultSortOrder = SortOrder.Asc)]
|
||||
[IgnoreExcel]
|
||||
public int? SortCode { get; set; }
|
||||
public virtual int? SortCode { get; set; }
|
||||
}
|
||||
|
||||
public interface IBaseDataEntity
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.0.2.18</PluginVersion>
|
||||
<ProPluginVersion>10.0.2.18</ProPluginVersion>
|
||||
<PluginVersion>10.1.0.1</PluginVersion>
|
||||
<ProPluginVersion>10.1.0.1</ProPluginVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -13,7 +13,7 @@ using System.Text;
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
[PluginOption(Singleton = true)]
|
||||
internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugin, ITcpReceivingPlugin
|
||||
internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugin, ITcpReceivingPlugin, ITcpClosingPlugin
|
||||
{
|
||||
public string DtuId
|
||||
{
|
||||
@@ -48,8 +48,9 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
private string _heartbeat;
|
||||
private ArraySegment<byte> HeartbeatByte;
|
||||
|
||||
|
||||
public int HeartbeatTime { get; set; } = 3;
|
||||
private Task Task;
|
||||
private bool SendHeartbeat;
|
||||
public int HeartbeatTime { get; set; } = 3000;
|
||||
|
||||
public async Task OnTcpConnected(ITcpSession client, ConnectedEventArgs e)
|
||||
{
|
||||
@@ -62,40 +63,46 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
|
||||
if (client is ITcpClient tcpClient)
|
||||
{
|
||||
SendHeartbeat = true;
|
||||
await tcpClient.SendAsync(DtuIdByte).ConfigureAwait(false);
|
||||
|
||||
_ = Task.Factory.StartNew(async () =>
|
||||
{
|
||||
var failedCount = 0;
|
||||
while (client.Online)
|
||||
if (Task == null)
|
||||
{
|
||||
Task = Task.Factory.StartNew(async () =>
|
||||
{
|
||||
await Task.Delay(HeartbeatTime).ConfigureAwait(false);
|
||||
if (!client.Online)
|
||||
var failedCount = 0;
|
||||
while (SendHeartbeat)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (DateTime.UtcNow - tcpClient.LastSentTime.ToUniversalTime() < TimeSpan.FromMilliseconds(200))
|
||||
await Task.Delay(HeartbeatTime).ConfigureAwait(false);
|
||||
if (!client.Online)
|
||||
{
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
await tcpClient.SendAsync(HeartbeatByte).ConfigureAwait(false);
|
||||
tcpClient.Logger?.Trace($"{tcpClient}- Heartbeat");
|
||||
failedCount = 0;
|
||||
try
|
||||
{
|
||||
if (DateTime.UtcNow - tcpClient.LastSentTime.ToUniversalTime() < TimeSpan.FromMilliseconds(200))
|
||||
{
|
||||
await Task.Delay(200).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await tcpClient.SendAsync(HeartbeatByte).ConfigureAwait(false);
|
||||
tcpClient.Logger?.Trace($"{tcpClient}- Heartbeat");
|
||||
failedCount = 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
failedCount++;
|
||||
}
|
||||
if (failedCount > 3)
|
||||
{
|
||||
await client.CloseAsync("The automatic heartbeat has failed more than 3 times and has been disconnected.").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
failedCount++;
|
||||
}
|
||||
if (failedCount > 3)
|
||||
{
|
||||
await client.CloseAsync("The automatic heartbeat has failed more than 3 times and has been disconnected.").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Task = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await e.InvokeNext().ConfigureAwait(false);
|
||||
@@ -123,4 +130,10 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
await e.InvokeNext().ConfigureAwait(false);//如果本插件无法处理当前数据,请将数据转至下一个插件。
|
||||
}
|
||||
}
|
||||
|
||||
public Task OnTcpClosing(ITcpSession client, ClosingEventArgs e)
|
||||
{
|
||||
SendHeartbeat = false;
|
||||
return EasyTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,7 +363,6 @@ public struct OperResult : IOperResult
|
||||
#if NET6_0_OR_GREATER
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
#endif
|
||||
|
||||
[JsonIgnore]
|
||||
public Exception? Exception { get; set; }
|
||||
|
||||
@@ -433,7 +432,6 @@ public struct OperResult : IOperResult
|
||||
#if NET6_0_OR_GREATER
|
||||
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
|
||||
#endif
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public ErrorTypeEnum? ErrorType { get; set; }
|
||||
|
||||
|
||||
@@ -23,7 +23,10 @@ public static class ExportString
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
public static string DeviceName = Localizer["DeviceName"];
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
public static string BusinessDeviceName = Localizer["BusinessDeviceName"];
|
||||
/// <summary>
|
||||
/// 冗余设备名称
|
||||
/// </summary>
|
||||
|
||||
@@ -117,13 +117,17 @@ public class ControlController : ControllerBase
|
||||
/// </summary>
|
||||
[HttpPost("writeVariables")]
|
||||
[DisplayName("写入变量")]
|
||||
public async Task<Dictionary<string, OperResult>> WriteVariablesAsync(Dictionary<string, string> objs)
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult>>> WriteVariablesAsync(Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||
{
|
||||
var data = GlobalData.ReadOnlyVariables.Where(a => objs.ContainsKey(a.Key));
|
||||
if (data != null)
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(data.Select(a => a.Value.CreateOrgId), data.Select(a => a.Value.CreateUserId)).ConfigureAwait(false);
|
||||
foreach (var deviceData in deviceDatas)
|
||||
{
|
||||
if (GlobalData.Devices.TryGetValue(deviceData.Key, out var device))
|
||||
{
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(device.IdVariableRuntimes.Select(a => a.Value.CreateOrgId), device.IdVariableRuntimes.Select(a => a.Value.CreateUserId)).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return await GlobalData.RpcService.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", objs).ConfigureAwait(false);
|
||||
return await GlobalData.RpcService.InvokeDeviceMethodAsync($"WebApi-{UserManager.UserAccount}-{App.HttpContext.Connection.RemoteIpAddress.MapToIPv4()}", deviceDatas).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -76,7 +74,7 @@ public class RuntimeInfoController : ControllerBase
|
||||
/// <returns></returns>
|
||||
[HttpGet("realAlarmList")]
|
||||
[DisplayName("获取实时报警变量信息")]
|
||||
public async Task<SqlSugarPagedList<AlarmVariable>> GetRealAlarmList(VariablePageInput input)
|
||||
public async Task<SqlSugarPagedList<AlarmVariable>> GetRealAlarmList(AlarmVariablePageInput input)
|
||||
{
|
||||
var realAlarmVariables = await GlobalData.GetCurrentUserRealAlarmVariables().ConfigureAwait(false);
|
||||
|
||||
@@ -85,13 +83,8 @@ public class RuntimeInfoController : ControllerBase
|
||||
.WhereIF(!input.RegisterAddress.IsNullOrEmpty(), a => a.RegisterAddress == input.RegisterAddress)
|
||||
.WhereIF(!input.Name.IsNullOrEmpty(), a => a.Name == input.Name)
|
||||
.WhereIF(!input.DeviceName.IsNullOrEmpty(), a => a.DeviceName == input.DeviceName)
|
||||
.WhereIF(input.BusinessDeviceId > 0, a =>
|
||||
{
|
||||
return GlobalData.ContainsVariable(input.BusinessDeviceId, a);
|
||||
}
|
||||
)
|
||||
.ToPagedList(input);
|
||||
return data.Adapt<SqlSugarPagedList<AlarmVariable>>();
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,12 +93,12 @@ public class RuntimeInfoController : ControllerBase
|
||||
/// <returns></returns>
|
||||
[HttpPost("checkRealAlarm")]
|
||||
[DisplayName("确认实时报警")]
|
||||
public async Task CheckRealAlarm(string variableName)
|
||||
public async Task CheckRealAlarm(long variableId)
|
||||
{
|
||||
if (GlobalData.ReadOnlyRealAlarmVariables.TryGetValue(variableName, out var variable))
|
||||
if (GlobalData.ReadOnlyRealAlarmIdVariables.TryGetValue(variableId, out var variable))
|
||||
{
|
||||
await GlobalData.SysUserService.CheckApiDataScopeAsync(variable.CreateOrgId, variable.CreateUserId).ConfigureAwait(false);
|
||||
GlobalData.AlarmHostedService.ConfirmAlarm(variableName);
|
||||
GlobalData.AlarmHostedService.ConfirmAlarm(variableId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +156,17 @@ public class DevicePageInput : BasePageInput
|
||||
public PluginTypeEnum? PluginType { get; set; }
|
||||
}
|
||||
|
||||
public class AlarmVariablePageInput : BasePageInput
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public string? DeviceName { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public string RegisterAddress { get; set; }
|
||||
}
|
||||
|
||||
public class VariablePageInput : BasePageInput
|
||||
{
|
||||
|
||||
@@ -70,7 +70,7 @@ public abstract class BusinessBase : DriverBase
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
// 获取与当前设备相关的变量,CurrentDevice.VariableRuntimes并不适用于业务插件
|
||||
var variableRuntimes = GlobalData.Variables.Where(a =>
|
||||
var variableRuntimes = GlobalData.IdVariables.Where(a =>
|
||||
{
|
||||
if (!a.Value.Enable) return false;
|
||||
if (a.Value.VariablePropertys?.TryGetValue(DeviceId, out var values) == true)
|
||||
@@ -95,10 +95,10 @@ public abstract class BusinessBase : DriverBase
|
||||
}
|
||||
);
|
||||
|
||||
VariableRuntimes = variableRuntimes.ToDictionary();
|
||||
IdVariableRuntimes = variableRuntimes.ToDictionary();
|
||||
|
||||
// 获取当前设备需要采集的设备
|
||||
CollectDevices = GlobalData.GetEnableDevices().Where(a => VariableRuntimes.Select(b => b.Value.DeviceId).ToHashSet().Contains(a.Key)).ToDictionary(a => a.Key, a => a.Value);
|
||||
CollectDevices = GlobalData.GetEnableDevices().Where(a => IdVariableRuntimes.Select(b => b.Value.DeviceId).ToHashSet().Contains(a.Key)).ToDictionary(a => a.Key, a => a.Value);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,11 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
_exT2TimerTick = new(_businessPropertyWithCacheInterval.BusinessInterval);
|
||||
|
||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a =>
|
||||
{
|
||||
AlarmValueChange(a.Value);
|
||||
});
|
||||
|
||||
GlobalData.AlarmChangedEvent += AlarmValueChange;
|
||||
// 解绑全局数据的事件
|
||||
GlobalData.VariableValueChangeEvent -= VariableValueChange;
|
||||
@@ -63,7 +68,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
if (_businessPropertyWithCacheInterval.IsAllVariable)
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
VariableRuntimes = new(GlobalData.GetEnableVariables());
|
||||
IdVariableRuntimes = new(GlobalData.GetEnableVariables());
|
||||
|
||||
CollectDevices = GlobalData.GetEnableDevices().Where(a => a.Value.IsCollect == true).ToDictionary();
|
||||
}
|
||||
@@ -79,7 +84,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine)
|
||||
DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>());
|
||||
});
|
||||
VariableRuntimes.ForEach(a =>
|
||||
IdVariableRuntimes.ForEach(a =>
|
||||
{
|
||||
if (a.Value.IsOnline)
|
||||
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
|
||||
@@ -151,7 +156,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
if (_exTTimerTick.IsTickHappen())
|
||||
{
|
||||
// 间隔推送全部变量
|
||||
foreach (var variableRuntime in VariableRuntimes.Select(a => a.Value))
|
||||
foreach (var variableRuntime in IdVariableRuntimes.Select(a => a.Value))
|
||||
{
|
||||
VariableTimeInterval(variableRuntime, variableRuntime.Adapt<VariableBasicData>());
|
||||
}
|
||||
@@ -226,7 +231,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
// 检查当前设备的变量是否包含此报警变量,如果包含,则触发报警变量的变化处理方法
|
||||
if (VariableRuntimes.ContainsKey(alarmVariable.Name))
|
||||
if (IdVariableRuntimes.ContainsKey(alarmVariable.Id))
|
||||
AlarmChange(alarmVariable);
|
||||
}
|
||||
}
|
||||
@@ -262,7 +267,7 @@ public abstract class BusinessBaseWithCacheIntervalAlarmModel<VarModel, DevModel
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
// 检查当前设备的变量是否包含此变量,如果包含,则触发变量的变化处理方法
|
||||
if (VariableRuntimes.ContainsKey(variable.Name))
|
||||
if (IdVariableRuntimes.ContainsKey(variable.Id))
|
||||
VariableChange(variableRuntime, variable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
|
||||
if (_businessPropertyWithCacheInterval.IsAllVariable)
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
VariableRuntimes = new(GlobalData.GetEnableVariables());
|
||||
IdVariableRuntimes = GlobalData.GetEnableVariables().ToDictionary();
|
||||
CollectDevices = GlobalData.GetEnableDevices().Where(a => a.Value.IsCollect == true).ToDictionary();
|
||||
}
|
||||
else
|
||||
@@ -83,7 +83,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
|
||||
if (a.Value.DeviceStatus == DeviceStatusEnum.OnLine)
|
||||
DeviceStatusChange(a.Value, a.Value.Adapt<DeviceBasicData>());
|
||||
});
|
||||
VariableRuntimes.ForEach(a =>
|
||||
IdVariableRuntimes.ForEach(a =>
|
||||
{
|
||||
if (a.Value.IsOnline)
|
||||
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
|
||||
@@ -147,7 +147,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
|
||||
if (_exTTimerTick.IsTickHappen())
|
||||
{
|
||||
// 上传所有变量信息
|
||||
foreach (var variableRuntime in VariableRuntimes.Select(a => a.Value))
|
||||
foreach (var variableRuntime in IdVariableRuntimes.Select(a => a.Value))
|
||||
{
|
||||
VariableTimeInterval(variableRuntime, variableRuntime.Adapt<VariableBasicData>());
|
||||
}
|
||||
@@ -198,7 +198,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
|
||||
/// </summary>
|
||||
/// <param name="variableRuntime">变量运行时对象</param>
|
||||
/// <param name="variable">变量数据对象</param>
|
||||
protected virtual void VariableChange(VariableRuntime variableRuntime, VariableData variable)
|
||||
protected virtual void VariableChange(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
@@ -245,7 +245,7 @@ public abstract class BusinessBaseWithCacheIntervalDeviceModel<VarModel, DevMode
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
// 检查当前设备是否包含该变量,并进行相应处理
|
||||
if (VariableRuntimes.ContainsKey(variableRuntime.Name))
|
||||
if (IdVariableRuntimes.ContainsKey(variableRuntime.Id))
|
||||
VariableChange(variableRuntime, variable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,276 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
|
||||
}
|
||||
|
||||
#region 封装方法
|
||||
|
||||
protected List<TopicJson> GetAlarms(IEnumerable<AlarmModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<AlarmModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
|
||||
List<TopicJson> topicJsonList = new List<TopicJson>();
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic);
|
||||
if (topics.Count > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics);
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的报警主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.AlarmTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
|
||||
{
|
||||
// 如果是报警列表,则将整个分组转换为 JSON 字符串
|
||||
string json = group.Select(a => a).ToList().ToJsonNetString();
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是报警列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(gro);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
|
||||
{
|
||||
string json = data.Select(a => a).ToList().ToJsonNetString();
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(group);
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
|
||||
protected List<TopicJson> GetDeviceData(IEnumerable<DevModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<DevModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
|
||||
List<TopicJson> topicJsonList = new List<TopicJson>();
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic);
|
||||
if (topics.Count > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics).ToList();
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的设备主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.DeviceTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
|
||||
{
|
||||
// 如果是设备列表,则将整个分组转换为 JSON 字符串
|
||||
string json = group.Select(a => a).ToList().ToJsonNetString();
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是设备列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(gro);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
|
||||
{
|
||||
string json = data.Select(a => a).ToList().ToJsonNetString();
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(group);
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
protected List<TopicJson> GetVariable(IEnumerable<VarModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<VarModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
|
||||
List<TopicJson> topicJsonList = new List<TopicJson>();
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
|
||||
if (topics.Count > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics).ToList();
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的变量主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.VariableTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
// 如果是变量列表,则将整个分组转换为 JSON 字符串
|
||||
string json = group.Select(a => a).ToList().ToJsonNetString();
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(gro);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
string json = data.Select(a => a).ToList().ToJsonNetString();
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(group);
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
|
||||
protected List<TopicJson> GetVariableBasicData(IEnumerable<VariableBasicData> item)
|
||||
{
|
||||
IEnumerable<VariableBasicData>? data = null;
|
||||
if (!_businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel.IsNullOrWhiteSpace())
|
||||
{
|
||||
return GetVariable(item.Cast<VarModel>());
|
||||
}
|
||||
else
|
||||
{
|
||||
data = item;
|
||||
}
|
||||
List<TopicJson> topicJsonList = new List<TopicJson>();
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
|
||||
if (topics.Count > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics).ToList();
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的变量主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.VariableTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
// 如果是变量列表,则将整个分组转换为 JSON 字符串
|
||||
string json = group.Select(a => a).GroupBy(a => a.DeviceName, b => b).ToDictionary(a => a.Key, b => b.ToList()).ToJsonNetString();
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(gro);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
string json = data.Select(a => a).GroupBy(a => a.DeviceName, b => b).ToDictionary(a => a.Key, b => b.ToList()).ToJsonNetString();
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(group);
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
protected List<TopicArray> GetAlarmTopicArrays(IEnumerable<AlarmModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<AlarmModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
|
||||
@@ -138,135 +408,6 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
|
||||
}
|
||||
}
|
||||
|
||||
protected List<TopicJson> GetAlarms(IEnumerable<AlarmModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<AlarmModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
|
||||
List<TopicJson> topicJsonList = new List<TopicJson>();
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic);
|
||||
if (topics.Count > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics);
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的报警主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.AlarmTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
|
||||
{
|
||||
// 如果是报警列表,则将整个分组转换为 JSON 字符串
|
||||
string json = group.Select(a => a).ToList().ToJsonNetString();
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是报警列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(gro);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
|
||||
{
|
||||
string json = data.Select(a => a).ToList().ToJsonNetString();
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(group);
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
protected List<TopicJson> GetDeviceData(IEnumerable<DevModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<DevModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
|
||||
List<TopicJson> topicJsonList = new List<TopicJson>();
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic);
|
||||
if (topics.Count > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics).ToList();
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的设备主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.DeviceTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
|
||||
{
|
||||
// 如果是设备列表,则将整个分组转换为 JSON 字符串
|
||||
string json = group.Select(a => a).ToList().ToJsonNetString();
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是设备列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(gro);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
|
||||
{
|
||||
string json = data.Select(a => a).ToList().ToJsonNetString();
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(group);
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
protected List<TopicArray> GetDeviceTopicArray(IEnumerable<DevModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<DevModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
|
||||
@@ -332,72 +473,6 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
protected List<TopicJson> GetVariable(IEnumerable<VarModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<VarModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
|
||||
List<TopicJson> topicJsonList = new List<TopicJson>();
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
|
||||
if (topics.Count > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics).ToList();
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的变量主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.VariableTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
// 如果是变量列表,则将整个分组转换为 JSON 字符串
|
||||
string json = group.Select(a => a).ToList().ToJsonNetString();
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(gro);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
string json = data.Select(a => a).ToList().ToJsonNetString();
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
string json = JsonExtensions.ToJsonNetString(group);
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
|
||||
protected List<TopicArray> GetVariableTopicArray(IEnumerable<VarModel> item)
|
||||
{
|
||||
IEnumerable<dynamic>? data = Application.DynamicModelExtension.GetDynamicModel<VarModel>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
|
||||
@@ -463,5 +538,81 @@ public abstract partial class BusinessBaseWithCacheIntervalScript<VarModel, DevM
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
|
||||
protected List<TopicArray> GetVariableBasicDataTopicArray(IEnumerable<VariableBasicData> item)
|
||||
{
|
||||
IEnumerable<VariableBasicData>? data = null;
|
||||
if (!_businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel.IsNullOrWhiteSpace())
|
||||
{
|
||||
return GetVariableTopicArray(item.Cast<VarModel>());
|
||||
}
|
||||
else
|
||||
{
|
||||
data = item;
|
||||
}
|
||||
|
||||
List<TopicArray> topicJsonList = new List<TopicArray>();
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
|
||||
if (topics.Count > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics).ToList();
|
||||
if (groups.Count > 0)
|
||||
{
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的变量主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.VariableTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Count; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
// 如果是变量列表,则将整个分组转换为 JSON 字符串
|
||||
var json = Serialize(group.Select(a => a).ToList());
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = Serialize(gro);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
topicJsonList.Add(new(topic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
var json = Serialize(data.Select(a => a).ToList());
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = Serialize(group);
|
||||
topicJsonList.Add(new(_businessPropertyWithCacheIntervalScript.VariableTopic, json));
|
||||
}
|
||||
}
|
||||
}
|
||||
return topicJsonList;
|
||||
}
|
||||
|
||||
|
||||
#endregion 封装方法
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
|
||||
if (_businessPropertyWithCacheInterval.IsAllVariable)
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
VariableRuntimes = new(GlobalData.GetEnableVariables());
|
||||
IdVariableRuntimes = new(GlobalData.GetEnableVariables());
|
||||
CollectDevices = GlobalData.GetEnableDevices().Where(a => a.Value.IsCollect == true).ToDictionary();
|
||||
}
|
||||
else
|
||||
@@ -66,7 +66,7 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
|
||||
}
|
||||
|
||||
// 触发一次变量值变化事件
|
||||
VariableRuntimes.ForEach(a =>
|
||||
IdVariableRuntimes.ForEach(a =>
|
||||
{
|
||||
if (a.Value.IsOnline)
|
||||
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
|
||||
@@ -105,7 +105,7 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
|
||||
if (_exTTimerTick.IsTickHappen())
|
||||
{
|
||||
//间隔推送全部变量
|
||||
foreach (var variableRuntime in VariableRuntimes.Select(a => a.Value))
|
||||
foreach (var variableRuntime in IdVariableRuntimes.Select(a => a.Value))
|
||||
{
|
||||
VariableTimeInterval(variableRuntime, variableRuntime.Adapt<VariableBasicData>());
|
||||
}
|
||||
@@ -165,7 +165,7 @@ public abstract class BusinessBaseWithCacheIntervalVariableModel<T> : BusinessBa
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
//筛选
|
||||
if (VariableRuntimes.ContainsKey(variableRuntime.Name))
|
||||
if (IdVariableRuntimes.ContainsKey(variableRuntime.Id))
|
||||
VariableChange(variableRuntime, variable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +53,10 @@ public abstract class CollectBase : DriverBase
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
var currentDevice = CurrentDevice;
|
||||
VariableRuntimes = currentDevice.VariableRuntimes.Where(a => a.Value.Enable).ToDictionary(a => a.Value.Name, a => a.Value);
|
||||
IdVariableRuntimes = currentDevice.IdVariableRuntimes.Where(a => a.Value.Enable).ToDictionary();
|
||||
|
||||
//预热脚本,加速编译
|
||||
VariableRuntimes.Where(a => !string.IsNullOrWhiteSpace(a.Value.ReadExpressions))
|
||||
IdVariableRuntimes.Where(a => !string.IsNullOrWhiteSpace(a.Value.ReadExpressions))
|
||||
.Select(b => b.Value.ReadExpressions).ToHashSet().ParallelForEach(script =>
|
||||
{
|
||||
try
|
||||
@@ -72,7 +72,7 @@ public abstract class CollectBase : DriverBase
|
||||
{
|
||||
// 连读打包
|
||||
// 从收集的变量运行时信息中筛选需要读取的变量
|
||||
var tags = VariableRuntimes.Select(a => a.Value)
|
||||
var tags = IdVariableRuntimes.Select(a => a.Value)
|
||||
.Where(it => it.ProtectType != ProtectTypeEnum.WriteOnly
|
||||
&& string.IsNullOrEmpty(it.OtherMethod)
|
||||
&& !string.IsNullOrEmpty(it.RegisterAddress));
|
||||
@@ -109,7 +109,7 @@ public abstract class CollectBase : DriverBase
|
||||
try
|
||||
{
|
||||
// 初始化动态方法
|
||||
var variablesMethod = VariableRuntimes.Select(a => a.Value).Where(it => !string.IsNullOrEmpty(it.OtherMethod));
|
||||
var variablesMethod = IdVariableRuntimes.Select(a => a.Value).Where(it => !string.IsNullOrEmpty(it.OtherMethod));
|
||||
|
||||
// 处理可读的动态方法
|
||||
{
|
||||
@@ -472,7 +472,7 @@ public abstract class CollectBase : DriverBase
|
||||
}
|
||||
else if (variableRuntime.RegisterAddress.Equals("ScriptRead", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
variableRuntime.SetValue(default, dateTime);
|
||||
variableRuntime.SetValue(variableRuntime, dateTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -446,7 +446,7 @@ public abstract class DriverBase : DisposableObject, IDriver
|
||||
/// <summary>
|
||||
/// 当前关联的变量
|
||||
/// </summary>
|
||||
public Dictionary<string, VariableRuntime> VariableRuntimes { get; protected set; } = new();
|
||||
public Dictionary<long, VariableRuntime> IdVariableRuntimes { get; protected set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 是否连接成功
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace ThingsGateway.Gateway.Application
|
||||
bool Pause { get; }
|
||||
string PluginDirectory { get; }
|
||||
List<IEditorItem> PluginPropertyEditorItems { get; }
|
||||
Dictionary<string, VariableRuntime> VariableRuntimes { get; }
|
||||
Dictionary<long, VariableRuntime> IdVariableRuntimes { get; }
|
||||
IDeviceThreadManage DeviceThreadManage { get; }
|
||||
|
||||
bool IsConnected();
|
||||
|
||||
@@ -34,6 +34,7 @@ public class Device : BaseDataEntity, IValidatableObject
|
||||
[SugarColumn(ColumnDescription = "名称", Length = 200)]
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
|
||||
[Required]
|
||||
[RegularExpression(@"^[^.]*$", ErrorMessage = "The field {0} cannot contain a dot ('.')")]
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -35,6 +35,13 @@ public class RpcLog : PrimaryIdEntity
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
|
||||
public string OperateSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作设备
|
||||
///</summary>
|
||||
[SugarColumn(ColumnDescription = "操作设备", IsNullable = false)]
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
|
||||
public string OperateDevice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作对象
|
||||
///</summary>
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
[SugarTable("variable", TableDescription = "设备变量表")]
|
||||
[Tenant(SqlSugarConst.DB_Custom)]
|
||||
[SugarIndex("index_device", nameof(Variable.DeviceId), OrderByType.Asc)]
|
||||
[SugarIndex("unique_variable_name", nameof(Variable.Name), OrderByType.Asc, true)]
|
||||
[SugarIndex("unique_variable_name", nameof(Variable.Name), OrderByType.Asc, nameof(Variable.DeviceId), OrderByType.Asc, true)]
|
||||
public class Variable : BaseDataEntity, IValidatableObject
|
||||
{
|
||||
/// <summary>
|
||||
@@ -47,6 +47,7 @@ public class Variable : BaseDataEntity, IValidatableObject
|
||||
[SugarColumn(ColumnDescription = "变量名称", IsNullable = false)]
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true, Order = 1)]
|
||||
[Required]
|
||||
[RegularExpression(@"^[^.]*$", ErrorMessage = "The field {0} cannot contain a dot ('.')")]
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -64,10 +64,6 @@ public static class GlobalData
|
||||
/// </summary>
|
||||
public static event VariableAlarmEventHandler? AlarmChangedEvent;
|
||||
|
||||
/// <summary>
|
||||
/// 只读的通道字典,提供对通道的只读访问
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<long, ChannelRuntime> ReadOnlyChannels => Channels;
|
||||
|
||||
public static async Task<IEnumerable<KeyValuePair<long, ChannelRuntime>>> GetCurrentUserChannels()
|
||||
{
|
||||
@@ -87,22 +83,19 @@ public static class GlobalData
|
||||
return IdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId);
|
||||
}
|
||||
public static async Task<IEnumerable<KeyValuePair<string, VariableRuntime>>> GetCurrentUserVariables()
|
||||
|
||||
public static async Task<IEnumerable<KeyValuePair<long, AlarmVariable>>> GetCurrentUserRealAlarmVariables()
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
return Variables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId);
|
||||
}
|
||||
public static async Task<IEnumerable<KeyValuePair<string, VariableRuntime>>> GetCurrentUserRealAlarmVariables()
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
return RealAlarmVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
return RealAlarmIdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId);
|
||||
}
|
||||
|
||||
|
||||
public static async Task<IEnumerable<KeyValuePair<long, VariableRuntime>>> GetCurrentUserAlarmEnableVariables()
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
return AlarmEnableVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
return AlarmEnableIdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId);
|
||||
}
|
||||
|
||||
@@ -130,15 +123,17 @@ public static class GlobalData
|
||||
return Channels.Where(a => a.Value.Enable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 只读的设备字典,提供对设备的只读访问
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<long, DeviceRuntime> ReadOnlyIdDevices => IdDevices;
|
||||
|
||||
/// <summary>
|
||||
/// 只读的设备字典,提供对设备的只读访问
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, DeviceRuntime> ReadOnlyDevices => Devices;
|
||||
public static VariableRuntime GetVariable(string deviceName, string variableName)
|
||||
{
|
||||
if (Devices.TryGetValue(deviceName, out var device))
|
||||
{
|
||||
if (device.VariableRuntimes.TryGetValue(variableName, out var variable))
|
||||
{
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 只读的通道字典,提供对通道的只读访问
|
||||
@@ -168,28 +163,10 @@ public static class GlobalData
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 报警列表
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<long, VariableRuntime> ReadOnlyAlarmEnableVariables => AlarmEnableVariables;
|
||||
|
||||
/// <summary>
|
||||
/// 实时报警列表
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, VariableRuntime> ReadOnlyRealAlarmVariables => RealAlarmVariables;
|
||||
/// <summary>
|
||||
/// 只读的变量字典
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, VariableRuntime> ReadOnlyVariables => Variables;
|
||||
/// <summary>
|
||||
/// 只读的变量字典
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<long, VariableRuntime> ReadOnlyIdVariables => IdVariables;
|
||||
|
||||
public static IEnumerable<KeyValuePair<string, VariableRuntime>> GetEnableVariables()
|
||||
public static IEnumerable<KeyValuePair<long, VariableRuntime>> GetEnableVariables()
|
||||
{
|
||||
return Variables.Where(a => a.Value.Enable);
|
||||
return IdDevices.SelectMany(a => a.Value.IdVariableRuntimes).Where(a => a.Value.Enable);
|
||||
}
|
||||
|
||||
|
||||
@@ -407,13 +384,29 @@ public static class GlobalData
|
||||
public static bool StartBusinessChannelEnable => GatewayRedundantSerivce?.StartBusinessChannelEnable ?? true;
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 只读的通道字典,提供对通道的只读访问
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<long, ChannelRuntime> ReadOnlyChannels => Channels;
|
||||
|
||||
/// <summary>
|
||||
/// 内部使用的通道字典,用于存储通道对象
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<long, ChannelRuntime> Channels { get; } = new();
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 只读的设备字典,提供对设备的只读访问
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<long, DeviceRuntime> ReadOnlyIdDevices => IdDevices;
|
||||
|
||||
/// <summary>
|
||||
/// 只读的设备字典,提供对设备的只读访问
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<string, DeviceRuntime> ReadOnlyDevices => Devices;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 内部使用的设备字典,用于存储设备对象
|
||||
/// </summary>
|
||||
@@ -422,24 +415,36 @@ public static class GlobalData
|
||||
/// 内部使用的设备字典,用于存储设备对象
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<long, DeviceRuntime> IdDevices { get; } = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 内部使用的报警配置变量字典
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<long, VariableRuntime> AlarmEnableIdVariables { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 内部使用的报警配置变量字典
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<long, AlarmVariable> RealAlarmIdVariables { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 内部使用的变量字典,用于存储变量对象
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<long, VariableRuntime> IdVariables { get; } = new();
|
||||
/// <summary>
|
||||
/// 内部使用的变量字典,用于存储变量对象
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<string, VariableRuntime> Variables { get; } = new();
|
||||
/// <summary>
|
||||
/// 内部使用的报警配置变量字典
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<long, VariableRuntime> AlarmEnableVariables { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 内部使用的报警配置变量字典
|
||||
/// 实时报警列表
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<string, VariableRuntime> RealAlarmVariables { get; } = new();
|
||||
public static IReadOnlyDictionary<long, AlarmVariable> ReadOnlyRealAlarmIdVariables => RealAlarmIdVariables;
|
||||
|
||||
/// <summary>
|
||||
/// 只读的变量字典
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<long, VariableRuntime> ReadOnlyIdVariables => IdVariables;
|
||||
|
||||
|
||||
|
||||
#region 变化事件
|
||||
/// <summary>
|
||||
/// 报警状态变化处理方法,用于处理报警状态变化时的逻辑
|
||||
/// </summary>
|
||||
@@ -490,4 +495,6 @@ public static class GlobalData
|
||||
VariableCollectChangeEvent.Invoke(variableRuntime);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -223,6 +223,7 @@
|
||||
"LogTime": "LogTime",
|
||||
"OperateSource": "OperationSource",
|
||||
"OperateObject": "OperationObject",
|
||||
"OperateDevice": "OperateDevice",
|
||||
"OperateMethod": "RPCMethod",
|
||||
"IsSuccess": "OperationResult",
|
||||
"ParamJson": "RequestParameters",
|
||||
@@ -406,6 +407,7 @@
|
||||
"ThingsGateway.Gateway.Application.RpcLogPageInput": {
|
||||
"SearchDate": "SearchDate",
|
||||
"OperateObject": "OperateObject",
|
||||
"OperateDevice": "OperateDevice",
|
||||
"OperateSource": "OperateSource"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BackendLogPageInput": {
|
||||
@@ -415,6 +417,7 @@
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.RpcService": {
|
||||
"VariableNotNull": "{0} variable does not exist",
|
||||
"DeviceNotNull": "Device name does not exist",
|
||||
"VariableReadOnly": "{0} variable is read-only",
|
||||
"VariableWriteDisable": "{0} variable is not allowed to write",
|
||||
"DriverNotNull": "System error, corresponding collection device does not exist, please try again later",
|
||||
@@ -425,7 +428,8 @@
|
||||
"VariableName": "Variable",
|
||||
"RedundantDeviceName": "Redundant Device",
|
||||
"ChannelName": "Channel",
|
||||
"DeviceName": "Device"
|
||||
"DeviceName": "Device",
|
||||
"BusinessDeviceName": "BusinessDevice"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.ProtectTypeEnum": {
|
||||
"ReadOnly": "ReadOnly",
|
||||
|
||||
@@ -228,6 +228,7 @@
|
||||
"LogTime": "时间",
|
||||
"OperateSource": "操作源",
|
||||
"OperateObject": "操作对象",
|
||||
"OperateDevice": "操作设备",
|
||||
"OperateMethod": "RPC方法",
|
||||
"IsSuccess": "操作结果",
|
||||
"ParamJson": "请求参数",
|
||||
@@ -444,6 +445,7 @@
|
||||
"ThingsGateway.Gateway.Application.RpcLogPageInput": {
|
||||
"SearchDate": "时间区间",
|
||||
"OperateObject": "操作对象",
|
||||
"OperateDevice": "操作设备",
|
||||
"OperateSource": "操作源"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BackendLogPageInput": {
|
||||
@@ -452,6 +454,7 @@
|
||||
"LogSource": "日志源"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.RpcService": {
|
||||
"DeviceNotNull": "设备名称不存在",
|
||||
"VariableNotNull": " {0} 变量不存在",
|
||||
"VariableReadOnly": " {0} 变量只读",
|
||||
"VariableWriteDisable": " {0} 变量不允许写入",
|
||||
@@ -463,7 +466,8 @@
|
||||
"VariableName": "变量",
|
||||
"RedundantDeviceName": "冗余设备",
|
||||
"ChannelName": "通道",
|
||||
"DeviceName": "设备"
|
||||
"DeviceName": "设备",
|
||||
"BusinessDeviceName": "业务设备"
|
||||
},
|
||||
|
||||
"ThingsGateway.Gateway.Application.ProtectTypeEnum": {
|
||||
|
||||
@@ -33,6 +33,21 @@ public class AlarmVariable : PrimaryIdEntity, IDBHistoryAlarm
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IBaseDataEntity.CreateOrgId"/>
|
||||
[SugarColumn(ColumnDescription = "组织Id", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public long CreateOrgId { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IBaseEntity.CreateUserId"/>
|
||||
[SugarColumn(ColumnDescription = "创建用户Id", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public long CreateUserId { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.DeviceId"/>
|
||||
[SugarColumn(ColumnDescription = "设备Id", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public long DeviceId { get; set; }
|
||||
|
||||
/// <inheritdoc cref="VariableRuntime.DeviceName"/>
|
||||
[SugarColumn(ColumnDescription = "设备名称", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
|
||||
|
||||
@@ -8,14 +8,74 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 设备业务变化数据
|
||||
/// </summary>
|
||||
public class DeviceBasicData : IPrimaryIdEntity
|
||||
{
|
||||
/// <inheritdoc cref="PrimaryIdEntity.Id"/>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Name"/>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc cref="DeviceRuntime.ActiveTime"/>
|
||||
public DateTime ActiveTime { get; set; }
|
||||
|
||||
/// <inheritdoc cref="DeviceRuntime.DeviceStatus"/>
|
||||
public DeviceStatusEnum DeviceStatus { get; set; }
|
||||
|
||||
/// <inheritdoc cref="DeviceRuntime.LastErrorMessage"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string LastErrorMessage { get; set; }
|
||||
|
||||
/// <inheritdoc cref="DeviceRuntime.PluginName"/>
|
||||
public string PluginName { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Description"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark1"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark1 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark2"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark2 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark3"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark3 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark4"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark4 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark5"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark5 { get; set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 变量业务变化数据
|
||||
/// </summary>
|
||||
public class VariableData : IPrimaryIdEntity
|
||||
public class VariableBasicData : IPrimaryIdEntity
|
||||
{
|
||||
/// <inheritdoc cref="PrimaryIdEntity.Id"/>
|
||||
public long Id { get; set; }
|
||||
@@ -42,10 +102,36 @@ public class VariableData : IPrimaryIdEntity
|
||||
/// <inheritdoc cref="VariableRuntime.IsOnline"/>
|
||||
public bool IsOnline { get; set; }
|
||||
|
||||
/// <inheritdoc cref="VariableRuntime.DeviceRuntime"/>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public DeviceBasicData DeviceRuntime { get; set; }
|
||||
|
||||
/// <inheritdoc cref="VariableRuntime.LastErrorMessage"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? LastErrorMessage { get; set; }
|
||||
/// <inheritdoc cref="Variable.RegisterAddress"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? RegisterAddress { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.Unit"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Unit { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.Description"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.ProtectType"/>
|
||||
public ProtectTypeEnum ProtectType { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.DataType"/>
|
||||
public DataTypeEnum DataType { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark1"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
@@ -73,30 +159,4 @@ public class VariableData : IPrimaryIdEntity
|
||||
public string Remark5 { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 变量基础数据
|
||||
/// </summary>
|
||||
public class VariableBasicData : VariableData
|
||||
{
|
||||
/// <inheritdoc cref="Variable.RegisterAddress"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? RegisterAddress { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.Unit"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Unit { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.Description"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.ProtectType"/>
|
||||
public ProtectTypeEnum ProtectType { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.DataType"/>
|
||||
public DataTypeEnum DataType { get; set; }
|
||||
}
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 设备业务变化数据
|
||||
/// </summary>
|
||||
public class DeviceData : IPrimaryIdEntity
|
||||
{
|
||||
/// <inheritdoc cref="PrimaryIdEntity.Id"/>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Name"/>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <inheritdoc cref="DeviceRuntime.ActiveTime"/>
|
||||
public DateTime ActiveTime { get; set; }
|
||||
|
||||
/// <inheritdoc cref="DeviceRuntime.DeviceStatus"/>
|
||||
public DeviceStatusEnum DeviceStatus { get; set; }
|
||||
|
||||
/// <inheritdoc cref="DeviceRuntime.LastErrorMessage"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string LastErrorMessage { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark1"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark1 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark2"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark2 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark3"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark3 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark4"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark4 { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Remark5"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string Remark5 { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设备业务基础数据
|
||||
/// </summary>
|
||||
public class DeviceBasicData : DeviceData
|
||||
{
|
||||
/// <inheritdoc cref="DeviceRuntime.PluginName"/>
|
||||
public string PluginName { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Device.Description"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? Description { get; set; }
|
||||
}
|
||||
@@ -91,7 +91,7 @@ public class DeviceRuntime : Device, IDisposable
|
||||
/// <summary>
|
||||
/// 设备变量数量
|
||||
/// </summary>
|
||||
public int DeviceVariableCount { get => VariableRuntimes == null ? 0 : VariableRuntimes.Count; }
|
||||
public int DeviceVariableCount { get => IdVariableRuntimes == null ? 0 : IdVariableRuntimes.Count; }
|
||||
|
||||
/// <summary>
|
||||
/// 暂停
|
||||
@@ -129,9 +129,16 @@ public class DeviceRuntime : Device, IDisposable
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[AdaptIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IReadOnlyDictionary<long, VariableRuntime>? ReadVariableRuntimes => VariableRuntimes;
|
||||
public IReadOnlyDictionary<long, VariableRuntime>? ReadOnlyIdVariableRuntimes => IdVariableRuntimes;
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public IReadOnlyDictionary<string, VariableRuntime>? ReadOnlyVariableRuntimes => VariableRuntimes;
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量
|
||||
@@ -140,7 +147,17 @@ public class DeviceRuntime : Device, IDisposable
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[AdaptIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
internal ConcurrentDictionary<long, VariableRuntime>? VariableRuntimes { get; set; } = new(Environment.ProcessorCount, 1000);
|
||||
internal ConcurrentDictionary<long, VariableRuntime>? IdVariableRuntimes { get; set; } = new(Environment.ProcessorCount, 1000);
|
||||
|
||||
/// <summary>
|
||||
/// 设备变量
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[AdaptIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
internal ConcurrentDictionary<string, VariableRuntime>? VariableRuntimes { get; set; } = new(Environment.ProcessorCount, 1000);
|
||||
|
||||
|
||||
#region 采集
|
||||
/// <summary>
|
||||
|
||||
@@ -12,6 +12,8 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
@@ -22,9 +24,14 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// </summary>
|
||||
public class VariableRuntime : Variable, IVariable, IDisposable
|
||||
{
|
||||
[SugarColumn(ColumnDescription = "排序码", IsNullable = true)]
|
||||
[AutoGenerateColumn(Visible = false, DefaultSort = false, Sortable = true)]
|
||||
[IgnoreExcel]
|
||||
public override int? SortCode { get; set; }
|
||||
private bool _isOnline;
|
||||
private bool? _isOnlineChanged;
|
||||
protected object? _value;
|
||||
|
||||
/// <summary>
|
||||
/// 动态变量
|
||||
/// </summary>
|
||||
@@ -42,7 +49,6 @@ public class VariableRuntime : Variable, IVariable, IDisposable
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AdaptIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public DeviceRuntime? DeviceRuntime { get; set; }
|
||||
|
||||
@@ -321,26 +327,28 @@ public class VariableRuntime : Variable, IVariable, IDisposable
|
||||
#endregion 报警
|
||||
public void Init(DeviceRuntime deviceRuntime)
|
||||
{
|
||||
GlobalData.AlarmEnableVariables.TryRemove(Id, out _);
|
||||
if (GlobalData.RealAlarmVariables.TryRemove(Name, out var oldAlarm))
|
||||
GlobalData.AlarmEnableIdVariables.TryRemove(Id, out _);
|
||||
if (GlobalData.RealAlarmIdVariables.TryRemove(Id, out var oldAlarm))
|
||||
{
|
||||
oldAlarm.EventType = EventTypeEnum.Finish;
|
||||
oldAlarm.EventTime = DateTime.Now;
|
||||
GlobalData.AlarmChange(this.Adapt<AlarmVariable>());
|
||||
}
|
||||
|
||||
DeviceRuntime?.VariableRuntimes?.TryRemove(Id, out _);
|
||||
|
||||
DeviceRuntime?.VariableRuntimes?.TryRemove(Name, out _);
|
||||
|
||||
DeviceRuntime?.IdVariableRuntimes?.TryRemove(Id, out _);
|
||||
|
||||
DeviceRuntime = deviceRuntime;
|
||||
|
||||
DeviceRuntime.VariableRuntimes.TryAdd(Id, this);
|
||||
DeviceRuntime.IdVariableRuntimes.TryAdd(Id, this);
|
||||
DeviceRuntime?.VariableRuntimes?.TryAdd(Name, this);
|
||||
GlobalData.IdVariables.TryRemove(Id, out _);
|
||||
GlobalData.IdVariables.TryAdd(Id, this);
|
||||
GlobalData.Variables.TryRemove(Name, out _);
|
||||
GlobalData.Variables.TryAdd(Name, this);
|
||||
if (AlarmEnable)
|
||||
{
|
||||
GlobalData.AlarmEnableVariables.TryAdd(Id, this);
|
||||
GlobalData.AlarmEnableIdVariables.TryAdd(Id, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,13 +356,13 @@ public class VariableRuntime : Variable, IVariable, IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
DeviceRuntime?.VariableRuntimes?.TryRemove(Id, out _);
|
||||
DeviceRuntime?.IdVariableRuntimes?.TryRemove(Id, out _);
|
||||
DeviceRuntime?.VariableRuntimes?.TryRemove(Name, out _);
|
||||
|
||||
GlobalData.IdVariables.TryRemove(Id, out _);
|
||||
GlobalData.Variables.TryRemove(Name, out _);
|
||||
|
||||
GlobalData.AlarmEnableVariables.TryRemove(Id, out _);
|
||||
if (GlobalData.RealAlarmVariables.TryRemove(Name, out var oldAlarm))
|
||||
GlobalData.AlarmEnableIdVariables.TryRemove(Id, out _);
|
||||
if (GlobalData.RealAlarmIdVariables.TryRemove(Id, out var oldAlarm))
|
||||
{
|
||||
oldAlarm.EventType = EventTypeEnum.Finish;
|
||||
oldAlarm.EventTime = DateTime.Now;
|
||||
@@ -367,8 +375,11 @@ public class VariableRuntime : Variable, IVariable, IDisposable
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<OperResult> RpcAsync(string value, string? executive = "brower", CancellationToken cancellationToken = default)
|
||||
{
|
||||
var data = await GlobalData.RpcService.InvokeDeviceMethodAsync(executive, new Dictionary<string, string>() { { Name, value } }, cancellationToken).ConfigureAwait(false);
|
||||
return data.FirstOrDefault().Value;
|
||||
var data = await GlobalData.RpcService.InvokeDeviceMethodAsync(executive, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
{ DeviceName, new Dictionary<string, string>() { { Name,value} } }
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
return data.FirstOrDefault().Value.FirstOrDefault().Value;
|
||||
}
|
||||
|
||||
public void SetErrorMessage(string? lastErrorMessage)
|
||||
|
||||
@@ -92,7 +92,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
channelRuntime.DeviceRuntimes.ParallelForEach(a =>
|
||||
{
|
||||
|
||||
a.Value.VariableRuntimes.ParallelForEach(v => v.Value.Dispose());
|
||||
a.Value.IdVariableRuntimes.ParallelForEach(v => v.Value.Dispose());
|
||||
a.Value.Dispose();
|
||||
|
||||
});
|
||||
@@ -197,7 +197,7 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
|
||||
public async Task RestartChannelAsync(IEnumerable<ChannelRuntime> oldChannelRuntimes)
|
||||
{
|
||||
oldChannelRuntimes.SelectMany(a => a.DeviceRuntimes.SelectMany(a => a.Value.VariableRuntimes)).ParallelForEach(a => a.Value.SafeDispose());
|
||||
oldChannelRuntimes.SelectMany(a => a.DeviceRuntimes.SelectMany(a => a.Value.IdVariableRuntimes)).ParallelForEach(a => a.Value.SafeDispose());
|
||||
oldChannelRuntimes.SelectMany(a => a.DeviceRuntimes).ParallelForEach(a => a.Value.SafeDispose());
|
||||
oldChannelRuntimes.ParallelForEach(a => a.SafeDispose());
|
||||
var ids = oldChannelRuntimes.Select(a => a.Id).ToHashSet();
|
||||
|
||||
@@ -156,7 +156,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
if (channels == null)
|
||||
{
|
||||
db ??= GetDB();
|
||||
channels = await db.Queryable<Channel>().ToListAsync().ConfigureAwait(false);
|
||||
channels = await db.Queryable<Channel>().OrderBy(a => a.Id).ToListAsync().ConfigureAwait(false);
|
||||
App.CacheService.Set(key, channels);
|
||||
}
|
||||
return channels;
|
||||
@@ -301,8 +301,8 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
|
||||
{
|
||||
if (item.Key == ExportString.ChannelName)
|
||||
{
|
||||
var collectChannelImports = ((ImportPreviewOutput<Channel>)item.Value).Data;
|
||||
channels = new List<Channel>(collectChannelImports.Values);
|
||||
var channelImports = ((ImportPreviewOutput<Channel>)item.Value).Data;
|
||||
channels = channelImports.Select(a => a.Value).ToList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
}
|
||||
if (deviceRuntime != null)
|
||||
{
|
||||
deviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
deviceRuntime.IdVariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
foreach (var deviceRuntime in deviceRuntimes)
|
||||
{
|
||||
//也需要删除变量
|
||||
deviceRuntime.VariableRuntimes.ParallelForEach(a =>
|
||||
deviceRuntime.IdVariableRuntimes.ParallelForEach(a =>
|
||||
{
|
||||
a.Value.Dispose();
|
||||
});
|
||||
@@ -165,7 +165,7 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
}
|
||||
if (deviceRuntime != null)
|
||||
{
|
||||
deviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
deviceRuntime.IdVariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
await deviceThreadManage.RemoveDeviceAsync(deviceRuntime.Id).ConfigureAwait(false);
|
||||
}
|
||||
deviceRuntime.Dispose();
|
||||
deviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
deviceRuntime.IdVariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
}
|
||||
|
||||
if (GlobalData.Channels.TryGetValue(newDeviceRuntime.ChannelId, out var channelRuntime))
|
||||
|
||||
@@ -169,7 +169,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
if (devices == null)
|
||||
{
|
||||
db ??= GetDB();
|
||||
devices = await db.Queryable<Device>().ToListAsync().ConfigureAwait(false);
|
||||
devices = await db.Queryable<Device>().OrderBy(a => a.Id).ToListAsync().ConfigureAwait(false);
|
||||
App.CacheService.Set(key, devices);
|
||||
}
|
||||
return devices;
|
||||
@@ -437,8 +437,8 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
|
||||
{
|
||||
if (item.Key == ExportString.DeviceName)
|
||||
{
|
||||
var collectDeviceImports = ((ImportPreviewOutput<Device>)item.Value).Data;
|
||||
devices = new List<Device>(collectDeviceImports.Values);
|
||||
var deviceImports = ((ImportPreviewOutput<Device>)item.Value).Data;
|
||||
devices = deviceImports.Select(a => a.Value).ToList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,10 @@ public class RpcLogPageInput : ITableSearchModel
|
||||
/// 操作对象
|
||||
/// </summary>
|
||||
public string? OperateObject { get; set; }
|
||||
/// <summary>
|
||||
/// 操作设备
|
||||
/// </summary>
|
||||
public string? OperateDevice { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 操作源
|
||||
@@ -70,11 +74,14 @@ public class RpcLogPageInput : ITableSearchModel
|
||||
/// </summary>
|
||||
public DateTimeRangeValue? SearchDate { get; set; }
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerable<IFilterAction> GetSearches()
|
||||
{
|
||||
var ret = new List<IFilterAction>();
|
||||
ret.AddIF(!string.IsNullOrEmpty(OperateSource), () => new SearchFilterAction(nameof(RpcLog.OperateSource), OperateSource));
|
||||
|
||||
ret.AddIF(!string.IsNullOrEmpty(OperateDevice), () => new SearchFilterAction(nameof(RpcLog.OperateDevice), OperateDevice));
|
||||
ret.AddIF(!string.IsNullOrEmpty(OperateObject), () => new SearchFilterAction(nameof(RpcLog.OperateObject), OperateObject));
|
||||
ret.AddIF(SearchDate != null, () => new SearchFilterAction(nameof(RpcLog.LogTime), SearchDate!.Start, FilterAction.GreaterThanOrEqual));
|
||||
ret.AddIF(SearchDate != null, () => new SearchFilterAction(nameof(RpcLog.LogTime), SearchDate!.End, FilterAction.LessThanOrEqual));
|
||||
|
||||
@@ -249,7 +249,7 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
|
||||
{
|
||||
// 如果是需恢复报警事件
|
||||
// 如果实时报警列表中不存在该变量,则直接返回
|
||||
if (!GlobalData.RealAlarmVariables.ContainsKey(item.Name))
|
||||
if (!GlobalData.RealAlarmIdVariables.ContainsKey(item.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -258,7 +258,7 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
|
||||
{
|
||||
// 如果是触发报警事件
|
||||
// 在实时报警列表中查找该变量
|
||||
if (GlobalData.RealAlarmVariables.TryGetValue(item.Name, out var variable))
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var variable))
|
||||
{
|
||||
// 如果变量已经处于相同的报警类型,则直接返回
|
||||
if (item.AlarmType == alarmEnum)
|
||||
@@ -342,7 +342,7 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
|
||||
{
|
||||
// 如果是需恢复报警事件
|
||||
// 获取旧的报警信息
|
||||
if (GlobalData.RealAlarmVariables.TryGetValue(item.Name, out var oldAlarm))
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var oldAlarm))
|
||||
{
|
||||
item.AlarmType = oldAlarm.AlarmType;
|
||||
item.EventType = eventEnum;
|
||||
@@ -363,23 +363,23 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
|
||||
//lock (GlobalData. RealAlarmVariables)
|
||||
{
|
||||
// 从实时报警列表中移除旧的报警信息,并添加新的报警信息
|
||||
GlobalData.RealAlarmVariables.AddOrUpdate(item.Name, a => item, (a, b) => item);
|
||||
GlobalData.RealAlarmIdVariables.AddOrUpdate(item.Id, a => item.Adapt<AlarmVariable>(), (a, b) => item.Adapt<AlarmVariable>());
|
||||
}
|
||||
}
|
||||
else if (item.EventType == EventTypeEnum.Finish)
|
||||
{
|
||||
// 如果是需恢复报警事件,则从实时报警列表中移除该变量
|
||||
GlobalData.RealAlarmVariables.TryRemove(item.Name, out _);
|
||||
GlobalData.RealAlarmIdVariables.TryRemove(item.Id, out _);
|
||||
}
|
||||
GlobalData.AlarmChange(item.Adapt<AlarmVariable>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void ConfirmAlarm(string variableName)
|
||||
public void ConfirmAlarm(long variableId)
|
||||
{
|
||||
// 如果是确认报警事件
|
||||
if (GlobalData.RealAlarmVariables.TryGetValue(variableName, out var variableRuntime))
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(variableId, out var variableRuntime))
|
||||
{
|
||||
variableRuntime.EventType = EventTypeEnum.Confirm;
|
||||
variableRuntime.EventTime = DateTime.Now;
|
||||
@@ -403,7 +403,7 @@ internal sealed class AlarmHostedService : BackgroundService, IAlarmHostedServic
|
||||
//Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
// 遍历设备变量列表
|
||||
|
||||
GlobalData.AlarmEnableVariables.ParallelForEach((kv, state, index) =>
|
||||
GlobalData.AlarmEnableIdVariables.ParallelForEach((kv, state, index) =>
|
||||
{
|
||||
{
|
||||
// 如果取消请求已经被触发,则结束任务
|
||||
|
||||
@@ -17,5 +17,5 @@ public interface IAlarmHostedService : IHostedService
|
||||
/// <summary>
|
||||
/// 确认报警
|
||||
/// </summary>
|
||||
void ConfirmAlarm(string variableName);
|
||||
void ConfirmAlarm(long variableId);
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ using Microsoft.Extensions.Localization;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
@@ -507,7 +507,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
|
||||
|
||||
if (IsCollectChannel == true)
|
||||
{
|
||||
saveDevices.AddRange(driver.VariableRuntimes.Where(a => a.Value.SaveValue && !a.Value.DynamicVariable).Select(a => a.Value));
|
||||
saveDevices.AddRange(driver.IdVariableRuntimes.Where(a => a.Value.SaveValue && !a.Value.DynamicVariable).Select(a => a.Value));
|
||||
}
|
||||
|
||||
// 取消驱动程序的操作
|
||||
@@ -685,7 +685,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
|
||||
{
|
||||
//传入变量
|
||||
//newDeviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.SafeDispose());
|
||||
deviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
deviceRuntime.IdVariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -16,8 +16,8 @@ public interface IRpcService
|
||||
/// 反向RPC入口方法
|
||||
/// </summary>
|
||||
/// <param name="sourceDes">触发该方法的源说明</param>
|
||||
/// <param name="items">指定键为变量名称,值为附带方法参数或写入值,方法参数会按逗号分割解析</param>
|
||||
/// <param name="deviceDatas">指定键为变量名称,值为附带方法参数或写入值,方法参数会按逗号分割解析</param>
|
||||
/// <param name="cancellationToken"><see cref="CancellationToken"/> 取消令箭</param>
|
||||
/// <returns></returns>
|
||||
Task<Dictionary<string, OperResult>> InvokeDeviceMethodAsync(string sourceDes, Dictionary<string, string> items, CancellationToken cancellationToken = default);
|
||||
Task<Dictionary<string, Dictionary<string, OperResult>>> InvokeDeviceMethodAsync(string sourceDes, Dictionary<string, Dictionary<string, string>> deviceDatas, CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ using ThingsGateway.Extension;
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
@@ -40,83 +42,104 @@ internal sealed class RpcService : IRpcService
|
||||
private IStringLocalizer Localizer { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<string, OperResult>> InvokeDeviceMethodAsync(string sourceDes, Dictionary<string, string> items, CancellationToken cancellationToken = default)
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult>>> InvokeDeviceMethodAsync(string sourceDes, Dictionary<string, Dictionary<string, string>> deviceDatas, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 初始化用于存储将要写入的变量和方法的字典
|
||||
Dictionary<CollectBase, Dictionary<VariableRuntime, JToken>> writeVariables = new();
|
||||
Dictionary<CollectBase, Dictionary<VariableRuntime, JToken>> writeMethods = new();
|
||||
// 用于存储结果的并发字典
|
||||
ConcurrentDictionary<string, OperResult> results = new();
|
||||
var dict = GlobalData.Variables;
|
||||
ConcurrentDictionary<string, Dictionary<string, OperResult>> results = new();
|
||||
deviceDatas.ForEach(a => results.TryAdd(a.Key, new()));
|
||||
|
||||
|
||||
var deviceDict = GlobalData.Devices;
|
||||
|
||||
// 对每个要操作的变量进行检查和处理
|
||||
foreach (var item in items)
|
||||
foreach (var deviceData in deviceDatas)
|
||||
{
|
||||
// 查找变量是否存在
|
||||
if (!dict.TryGetValue(item.Key, out var tag))
|
||||
// 查找设备是否存在
|
||||
if (!deviceDict.TryGetValue(deviceData.Key, out var device))
|
||||
{
|
||||
// 如果变量不存在,则添加错误信息到结果中并继续下一个变量的处理
|
||||
results.TryAdd(item.Key, new OperResult(Localizer["VariableNotNull", item.Key]));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查变量的保护类型和远程写入权限
|
||||
if (tag.ProtectType == ProtectTypeEnum.ReadOnly)
|
||||
{
|
||||
results.TryAdd(item.Key, new OperResult(Localizer["VariableReadOnly", item.Key]));
|
||||
continue;
|
||||
}
|
||||
if (!tag.RpcWriteEnable)
|
||||
{
|
||||
results.TryAdd(item.Key, new OperResult(Localizer["VariableWriteDisable", item.Key]));
|
||||
// 如果设备不存在,则添加错误信息到结果中并继续下一个设备的处理
|
||||
deviceData.Value.ForEach(a =>
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DeviceNotNull", deviceData.Key]))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找变量对应的设备
|
||||
var collect = tag.DeviceRuntime.Driver as CollectBase;
|
||||
var collect = device.Driver as CollectBase;
|
||||
if (collect == null)
|
||||
{
|
||||
// 如果设备不存在,则添加错误信息到结果中并继续下一个变量的处理
|
||||
results.TryAdd(item.Key, new OperResult(Localizer["DriverNotNull"]));
|
||||
// 如果设备不存在,则添加错误信息到结果中并继续下一个设备的处理
|
||||
deviceData.Value.ForEach(a =>
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DriverNotNull", deviceData.Key]))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
// 检查设备状态,如果设备处于暂停状态,则添加相应的错误信息到结果中并继续下一个变量的处理
|
||||
if (collect.CurrentDevice.DeviceStatus == DeviceStatusEnum.Pause)
|
||||
{
|
||||
results.TryAdd(item.Key, new OperResult(Localizer["DevicePause", collect.CurrentDevice.Name]));
|
||||
deviceData.Value.ForEach(a =>
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DevicePause", deviceData.Key]))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
JToken tagValue = JTokenUtil.GetJTokenFromString(item.Value);
|
||||
bool isOtherMethodEmpty = string.IsNullOrEmpty(tag.OtherMethod);
|
||||
var collection = isOtherMethodEmpty ? writeVariables : writeMethods;
|
||||
if (collection.TryGetValue(collect, out var value))
|
||||
foreach (var item in deviceData.Value)
|
||||
{
|
||||
value.Add(tag, tagValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
collection.Add(collect, new());
|
||||
collection[collect].Add(tag, tagValue);
|
||||
// 查找变量是否存在
|
||||
if (!device.VariableRuntimes.TryGetValue(item.Key, out var tag))
|
||||
{
|
||||
// 如果变量不存在,则添加错误信息到结果中并继续下一个变量的处理
|
||||
results[deviceData.Key].TryAdd(item.Key, new OperResult(Localizer["VariableNotNull", item.Key]));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查变量的保护类型和远程写入权限
|
||||
if (tag.ProtectType == ProtectTypeEnum.ReadOnly)
|
||||
{
|
||||
results[deviceData.Key].TryAdd(item.Key, new OperResult(Localizer["VariableReadOnly", item.Key]));
|
||||
continue;
|
||||
}
|
||||
if (!tag.RpcWriteEnable)
|
||||
{
|
||||
results[deviceData.Key].TryAdd(item.Key, new OperResult(Localizer["VariableWriteDisable", item.Key]));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
JToken tagValue = JTokenUtil.GetJTokenFromString(item.Value);
|
||||
bool isOtherMethodEmpty = string.IsNullOrEmpty(tag.OtherMethod);
|
||||
var collection = isOtherMethodEmpty ? writeVariables : writeMethods;
|
||||
if (collection.TryGetValue(collect, out var value))
|
||||
{
|
||||
value.Add(tag, tagValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
collection.Add(collect, new());
|
||||
collection[collect].Add(tag, tagValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 使用并行方式写入变量
|
||||
await writeVariables.ParallelForEachAsync(async (item, cancellationToken) =>
|
||||
await writeVariables.ParallelForEachAsync(async (driverData, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 调用设备的写入方法
|
||||
var result = await item.Key.InVokeWriteAsync(item.Value, cancellationToken).ConfigureAwait(false);
|
||||
var result = await driverData.Key.InVokeWriteAsync(driverData.Value, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// 写入日志
|
||||
foreach (var resultItem in result)
|
||||
{
|
||||
var empty = string.IsNullOrEmpty(resultItem.Key);
|
||||
string operObj = empty ? items.Select(x => x.Key).ToJsonNetString() : resultItem.Key;
|
||||
string operObj = empty ? deviceDatas[driverData.Key.DeviceName].Select(x => x.Key).ToJsonNetString() : resultItem.Key;
|
||||
|
||||
string parJson = empty ? items.Select(x => x.Value).ToJsonNetString() : items[resultItem.Key];
|
||||
string parJson = empty ? deviceDatas.Select(x => x.Value).ToJsonNetString() : deviceDatas[driverData.Key.DeviceName][resultItem.Key];
|
||||
|
||||
if (_rpcLogOptions.SuccessLog)
|
||||
_logQueues.Enqueue(
|
||||
@@ -126,6 +149,7 @@ internal sealed class RpcService : IRpcService
|
||||
OperateMessage = resultItem.Value.IsSuccess ? null : resultItem.Value.ToString(),
|
||||
IsSuccess = resultItem.Value.IsSuccess,
|
||||
OperateMethod = Localizer["WriteVariable"],
|
||||
OperateDevice = driverData.Key.DeviceName,
|
||||
OperateObject = operObj,
|
||||
OperateSource = sourceDes,
|
||||
ParamJson = parJson,
|
||||
@@ -143,12 +167,12 @@ internal sealed class RpcService : IRpcService
|
||||
}
|
||||
|
||||
// 将结果添加到结果字典中
|
||||
results.AddRange(result);
|
||||
results[driverData.Key.DeviceName].AddRange(result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 将异常信息添加到结果字典中
|
||||
results.AddRange(item.Value.Select((KeyValuePair<VariableRuntime, JToken> a) =>
|
||||
results[driverData.Key.DeviceName].AddRange(driverData.Value.Select((KeyValuePair<VariableRuntime, JToken> a) =>
|
||||
{
|
||||
return new KeyValuePair<string, OperResult>(a.Key.Name, new OperResult(ex));
|
||||
}));
|
||||
@@ -156,14 +180,14 @@ internal sealed class RpcService : IRpcService
|
||||
}, Environment.ProcessorCount, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// 使用并行方式执行方法
|
||||
await writeMethods.ParallelForEachAsync(async (item, cancellationToken) =>
|
||||
await writeMethods.ParallelForEachAsync(async (driverData, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 调用设备的写入方法
|
||||
var result = await item.Key.InvokeMethodAsync(item.Value, cancellationToken).ConfigureAwait(false);
|
||||
var result = await driverData.Key.InvokeMethodAsync(driverData.Value, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Dictionary<string, string> operateMethods = item.Value.Select(a => a.Key).ToDictionary(a => a.Name, a => a.OtherMethod!);
|
||||
Dictionary<string, string> operateMethods = driverData.Value.Select(a => a.Key).ToDictionary(a => a.Name, a => a.OtherMethod!);
|
||||
|
||||
// 写入日志
|
||||
foreach (var resultItem in result)
|
||||
@@ -177,10 +201,11 @@ internal sealed class RpcService : IRpcService
|
||||
OperateMessage = resultItem.Value.IsSuccess ? null : resultItem.Value.ToString(),
|
||||
IsSuccess = resultItem.Value.IsSuccess,
|
||||
OperateMethod = operateMethods[resultItem.Key],
|
||||
OperateDevice = driverData.Key.DeviceName,
|
||||
OperateObject = resultItem.Key,
|
||||
OperateSource = sourceDes,
|
||||
ParamJson = items[resultItem.Key]?.ToString(),
|
||||
ResultJson = resultItem.Value.Content?.ToString()
|
||||
ParamJson = deviceDatas[driverData.Key.DeviceName][resultItem.Key]?.ToString(),
|
||||
ResultJson = resultItem.Value.Content?.ToJsonNetString()
|
||||
}
|
||||
);
|
||||
|
||||
@@ -192,11 +217,14 @@ internal sealed class RpcService : IRpcService
|
||||
result[resultItem.Key] = result1;
|
||||
}
|
||||
}
|
||||
|
||||
results[driverData.Key.DeviceName].AddRange(result.ToDictionary(a => a.Key, a => (OperResult)a.Value));
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 将异常信息添加到结果字典中
|
||||
results.AddRange(item.Value.Select((KeyValuePair<VariableRuntime, JToken> a) =>
|
||||
results[driverData.Key.DeviceName].AddRange(driverData.Value.Select((KeyValuePair<VariableRuntime, JToken> a) =>
|
||||
{
|
||||
return new KeyValuePair<string, OperResult>(a.Key.Name, new OperResult(ex));
|
||||
}));
|
||||
|
||||
@@ -28,11 +28,11 @@ namespace ThingsGateway.Gateway.Application
|
||||
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile);
|
||||
|
||||
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart );
|
||||
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart);
|
||||
void PreheatCache();
|
||||
|
||||
Task<MemoryStream> ExportMemoryStream(List<Variable> data, string devName);
|
||||
Task AddDynamicVariable(IEnumerable<VariableRuntime> newVariableRuntimes, bool restart );
|
||||
Task DeleteDynamicVariable(IEnumerable<long> variableIds, bool restart );
|
||||
Task AddDynamicVariable(IEnumerable<VariableRuntime> newVariableRuntimes, bool restart);
|
||||
Task DeleteDynamicVariable(IEnumerable<long> variableIds, bool restart);
|
||||
}
|
||||
}
|
||||
@@ -403,7 +403,7 @@ public class VariableRuntimeService : IVariableRuntimeService
|
||||
}
|
||||
if (deviceRuntime != null)
|
||||
{
|
||||
deviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
deviceRuntime.IdVariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,6 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
List<Channel> newChannels = new();
|
||||
List<Device> newDevices = new();
|
||||
List<Variable> newVariables = new();
|
||||
var addressNum = 1;
|
||||
// 计算每个设备分配的默认变量数
|
||||
var groupVariableCount = (int)Math.Ceiling((decimal)variableCount / deviceCount);
|
||||
|
||||
@@ -101,13 +100,15 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
? variableCount - (deviceCount - 1) * groupVariableCount // 最后一个设备分配剩余的变量
|
||||
: groupVariableCount;
|
||||
|
||||
var addressNum = 1;
|
||||
|
||||
for (int i1 = 0; i1 < currentGroupVariableCount; i1++)
|
||||
{
|
||||
if (addressNum > 65535) addressNum = 1;
|
||||
var address = $"4{addressNum}";
|
||||
addressNum++;
|
||||
var id = CommonUtils.GetSingleId();
|
||||
var name = $"modbusVariable{address}_{id}";
|
||||
var name = $"modbus{address}";
|
||||
Variable variable = new Variable();
|
||||
variable.DataType = DataTypeEnum.Int16;
|
||||
variable.Name = name;
|
||||
@@ -177,6 +178,38 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
};
|
||||
newDevices.Add(mqttDevice);
|
||||
}
|
||||
|
||||
|
||||
Channel opcuaChannel = new Channel();
|
||||
Device opcuaDevice = new Device();
|
||||
|
||||
{
|
||||
var id = CommonUtils.GetSingleId();
|
||||
var name = $"opcuaChannel{id}";
|
||||
opcuaChannel.ChannelType = ChannelTypeEnum.Other;
|
||||
opcuaChannel.Name = name;
|
||||
opcuaChannel.Id = id;
|
||||
opcuaChannel.CreateUserId = UserManager.UserId;
|
||||
opcuaChannel.CreateOrgId = UserManager.OrgId;
|
||||
opcuaChannel.PluginName = "ThingsGateway.Plugin.OpcUa.OpcUaServer";
|
||||
newChannels.Add(opcuaChannel);
|
||||
}
|
||||
{
|
||||
var id = CommonUtils.GetSingleId();
|
||||
var name = $"opcuaDevice{id}";
|
||||
opcuaDevice.Name = name;
|
||||
opcuaDevice.Id = id;
|
||||
opcuaDevice.CreateUserId = UserManager.UserId;
|
||||
opcuaDevice.CreateOrgId = UserManager.OrgId;
|
||||
opcuaDevice.ChannelId = opcuaChannel.Id;
|
||||
opcuaDevice.IntervalTime = "1000";
|
||||
opcuaDevice.DevicePropertys = new Dictionary<string, string>
|
||||
{
|
||||
{"IsAllVariable", "true"}
|
||||
};
|
||||
newDevices.Add(opcuaDevice);
|
||||
}
|
||||
|
||||
using var db = GetDB();
|
||||
|
||||
var result = await db.UseTranAsync(async () =>
|
||||
@@ -295,12 +328,12 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
using var db = GetDB();
|
||||
if (devId == null)
|
||||
{
|
||||
var deviceVariables = await db.Queryable<Variable>().Where(a => a.DeviceId > 0).ToListAsync().ConfigureAwait(false);
|
||||
var deviceVariables = await db.Queryable<Variable>().OrderBy(a => a.Id).ToListAsync().ConfigureAwait(false);
|
||||
return deviceVariables;
|
||||
}
|
||||
else
|
||||
{
|
||||
var deviceVariables = await db.Queryable<Variable>().Where(a => a.DeviceId == devId).ToListAsync().ConfigureAwait(false);
|
||||
var deviceVariables = await db.Queryable<Variable>().Where(a => a.DeviceId == devId).OrderBy(a => a.Id).ToListAsync().ConfigureAwait(false);
|
||||
return deviceVariables;
|
||||
}
|
||||
}
|
||||
@@ -458,14 +491,14 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
//插件属性
|
||||
//单个设备的行数据
|
||||
Dictionary<string, object> driverInfo = new();
|
||||
var has = deviceDicts.TryGetValue(item.Key, out var businessDevice);
|
||||
if (!has)
|
||||
if (!(deviceDicts.TryGetValue(item.Key, out var businessDevice) && deviceDicts.TryGetValue(variable.DeviceId, out var collectDevice)))
|
||||
continue;
|
||||
|
||||
channelDicts.TryGetValue(businessDevice.ChannelId, out var channel);
|
||||
|
||||
//没有包含设备名称,手动插入
|
||||
driverInfo.TryAdd(ExportString.DeviceName, businessDevice.Name);
|
||||
driverInfo.TryAdd(ExportString.DeviceName, collectDevice.Name);
|
||||
driverInfo.TryAdd(ExportString.BusinessDeviceName, businessDevice.Name);
|
||||
driverInfo.TryAdd(ExportString.VariableName, variable.Name);
|
||||
|
||||
var propDict = item.Value;
|
||||
@@ -588,8 +621,8 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
{
|
||||
if (item.Key == ExportString.VariableName)
|
||||
{
|
||||
var variableImports = ((ImportPreviewOutput<Variable>)item.Value).Data;
|
||||
variables = new List<Variable>(variableImports.Values);
|
||||
var variableImports = ((ImportPreviewOutput<Dictionary<string, Variable>>)item.Value).Data;
|
||||
variables = variableImports.SelectMany(a => a.Value.Select(a => a.Value)).ToList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -605,27 +638,21 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
|
||||
private static readonly WaitLock _cacheLock = new();
|
||||
|
||||
private async Task<Dictionary<string, VariableImportData>> GetVariableImportData()
|
||||
private async Task<Dictionary<long, Dictionary<string, DeviecIdVariableImportData>>> GetVariableImportData()
|
||||
{
|
||||
var key = ThingsGatewayCacheConst.Cache_Variable;
|
||||
var datas = App.CacheService.Get<Dictionary<string, VariableImportData>>(key);
|
||||
var datas = App.CacheService.Get<Dictionary<long, Dictionary<string, DeviecIdVariableImportData>>>(key);
|
||||
|
||||
if (datas == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _cacheLock.WaitAsync().ConfigureAwait(false);
|
||||
datas = App.CacheService.Get<Dictionary<string, VariableImportData>>(key);
|
||||
datas = App.CacheService.Get<Dictionary<long, Dictionary<string, DeviecIdVariableImportData>>>(key);
|
||||
if (datas == null)
|
||||
{
|
||||
using var db = GetDB();
|
||||
datas = (await db.Queryable<Variable>().Select(it => new VariableImportData()
|
||||
{
|
||||
Id = it.Id,
|
||||
Name = it.Name,
|
||||
CreateOrgId = it.CreateOrgId,
|
||||
CreateUserId = it.CreateUserId
|
||||
}).ToListAsync().ConfigureAwait(false)).ToDictionary(a => a.Name);
|
||||
datas = (await db.Queryable<Variable>().Select<DeviecIdVariableImportData>().ToListAsync().ConfigureAwait(false)).GroupBy(a => a.DeviceId).ToDictionary(a => a.Key, a => a.ToDictionary(a => a.Name));
|
||||
|
||||
App.CacheService.Set(key, datas);
|
||||
}
|
||||
@@ -642,14 +669,15 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
{
|
||||
return GetVariableImportData();
|
||||
}
|
||||
|
||||
private sealed class VariableImportData
|
||||
private sealed class DeviecIdVariableImportData
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public long DeviceId { get; set; }
|
||||
public long CreateOrgId { get; set; }
|
||||
public long CreateUserId { get; set; }
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile)
|
||||
{
|
||||
// 上传文件并获取文件路径
|
||||
@@ -668,7 +696,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
Dictionary<string, ImportPreviewOutputBase> ImportPreviews = new();
|
||||
|
||||
// 设备页导入预览输出
|
||||
ImportPreviewOutput<Variable> deviceImportPreview = new();
|
||||
ImportPreviewOutput<Dictionary<string, Variable>> deviceImportPreview = new();
|
||||
|
||||
// 获取驱动插件的全名和名称的字典
|
||||
var driverPluginFullNameDict = _pluginService.GetList().ToDictionary(a => a.FullName);
|
||||
@@ -685,7 +713,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
if (sheetName == ExportString.VariableName)
|
||||
{
|
||||
int row = 0;
|
||||
ImportPreviewOutput<Variable> importPreviewOutput = new();
|
||||
ImportPreviewOutput<Dictionary<string, Variable>> importPreviewOutput = new();
|
||||
ImportPreviews.Add(sheetName, importPreviewOutput);
|
||||
deviceImportPreview = importPreviewOutput;
|
||||
|
||||
@@ -746,7 +774,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbVariableDicts.TryGetValue(variable.Name, out var dbvar1))
|
||||
if (dbVariableDicts.TryGetValue(variable.DeviceId, out var dbvar1s) && dbvar1s.TryGetValue(variable.Name, out var dbvar1))
|
||||
{
|
||||
variable.Id = dbvar1.Id;
|
||||
variable.CreateOrgId = dbvar1.CreateOrgId;
|
||||
@@ -787,7 +815,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
}
|
||||
|
||||
// 将变量列表转换为字典,并赋值给导入预览输出对象的 Data 属性
|
||||
importPreviewOutput.Data = variables.OrderBy(a => a.Row).ToDictionary(a => a.Name);
|
||||
importPreviewOutput.Data = variables.OrderBy(a => a.Row).GroupBy(a => a.DeviceId.ToString()).ToDictionary(a => a.Key, b => b.ToDictionary(a => a.Name));
|
||||
}
|
||||
|
||||
// 其他工作表处理
|
||||
@@ -861,11 +889,13 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
|
||||
// 转化插件名称和变量名称
|
||||
item.TryGetValue(ExportString.VariableName, out var variableNameObj);
|
||||
item.TryGetValue(ExportString.DeviceName, out var businessDevName);
|
||||
item.TryGetValue(ExportString.BusinessDeviceName, out var businessDevName);
|
||||
item.TryGetValue(ExportString.DeviceName, out var collectDevName);
|
||||
deviceDicts.TryGetValue(businessDevName?.ToString(), out var businessDevice);
|
||||
deviceDicts.TryGetValue(collectDevName?.ToString(), out var collectDevice);
|
||||
|
||||
// 如果设备名称或变量名称为空,或者找不到对应的设备,则添加错误信息到导入预览结果并返回
|
||||
if (businessDevName == null || businessDevice == null)
|
||||
if (businessDevName == null || businessDevice == null || collectDevName == null || collectDevice == null)
|
||||
{
|
||||
importPreviewOutput.HasError = true;
|
||||
importPreviewOutput.Results.Add((Interlocked.Add(ref row, 1), false, Localizer["DeviceNotNull"]));
|
||||
@@ -913,10 +943,8 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
|
||||
|
||||
// 获取变量名称并检查是否存在于设备导入预览数据中
|
||||
var variableName = variableNameObj?.ToString();
|
||||
var has = deviceImportPreview.Data.TryGetValue(variableName, out var deviceVariable);
|
||||
|
||||
// 如果存在,则更新变量属性字典,并添加成功信息到导入预览结果;否则,添加错误信息到导入预览结果并返回
|
||||
if (has)
|
||||
if (deviceImportPreview.Data.TryGetValue(collectDevice.Id.ToString(), out var deviceVariables) && deviceVariables.TryGetValue(variableName, out var deviceVariable))
|
||||
{
|
||||
deviceVariable.VariablePropertys ??= new();
|
||||
deviceVariable.VariablePropertys?.AddOrUpdate(businessDevice.Id, dependencyProperties);
|
||||
|
||||
@@ -1055,25 +1055,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
private TreeViewItem<ChannelDeviceTreeItem> BusinessTreeViewItem;
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
while (!Disposed)
|
||||
{
|
||||
try
|
||||
{
|
||||
await OnClickSearch(SearchText);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
BusinessTreeViewItem = new TreeViewItem<ChannelDeviceTreeItem>(UnknownItem)
|
||||
{
|
||||
@@ -1127,6 +1109,28 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
|
||||
context = ExecutionContext.Capture();
|
||||
ChannelRuntimeDispatchService.Subscribe(Refresh);
|
||||
DeviceRuntimeDispatchService.Subscribe(Refresh);
|
||||
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
while (!Disposed)
|
||||
{
|
||||
try
|
||||
{
|
||||
await OnClickSearch(SearchText);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await base.OnInitializedAsync();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ public enum ChannelDevicePluginTypeEnum
|
||||
}
|
||||
public class ChannelDeviceTreeItem : IEqualityComparer<ChannelDeviceTreeItem>
|
||||
{
|
||||
|
||||
public long Id { get; set; }
|
||||
public ChannelDevicePluginTypeEnum ChannelDevicePluginType { get; set; }
|
||||
|
||||
public DeviceRuntime DeviceRuntime { get; set; }
|
||||
|
||||
@@ -28,11 +28,11 @@ public partial class GatewayMonitorPage
|
||||
ShowChannelRuntime = channelRuntime;
|
||||
if (channelRuntime.IsCollect == true)
|
||||
{
|
||||
VariableRuntimes = channelRuntime.ReadDeviceRuntimes.SelectMany(a => a.Value.ReadVariableRuntimes.Select(a => a.Value));
|
||||
VariableRuntimes = channelRuntime.ReadDeviceRuntimes.SelectMany(a => a.Value.ReadOnlyIdVariableRuntimes.Select(a => a.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
VariableRuntimes = channelRuntime.ReadDeviceRuntimes.SelectMany(a => a.Value.Driver?.VariableRuntimes?.Select(a => a.Value)).Where(a => a != null);
|
||||
VariableRuntimes = channelRuntime.ReadDeviceRuntimes.SelectMany(a => a.Value.Driver?.IdVariableRuntimes?.Select(a => a.Value)).Where(a => a != null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,11 +41,11 @@ public partial class GatewayMonitorPage
|
||||
ShowDeviceRuntime = deviceRuntime;
|
||||
if (deviceRuntime.IsCollect == true)
|
||||
{
|
||||
VariableRuntimes = deviceRuntime.ReadVariableRuntimes.Select(a => a.Value);
|
||||
VariableRuntimes = deviceRuntime.ReadOnlyIdVariableRuntimes.Select(a => a.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
VariableRuntimes = deviceRuntime.Driver?.VariableRuntimes?
|
||||
VariableRuntimes = deviceRuntime.Driver?.IdVariableRuntimes?
|
||||
.Select(a => a.Value) ?? Enumerable.Empty<VariableRuntime>();
|
||||
}
|
||||
|
||||
@@ -56,12 +56,12 @@ public partial class GatewayMonitorPage
|
||||
if (pluginType == PluginTypeEnum.Collect)
|
||||
{
|
||||
var channels = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
VariableRuntimes = channels.Where(a => a.Value.PluginName == pluginName).SelectMany(a => a.Value.ReadDeviceRuntimes).SelectMany(a => a.Value.ReadVariableRuntimes).Select(a => a.Value);
|
||||
VariableRuntimes = channels.Where(a => a.Value.PluginName == pluginName).SelectMany(a => a.Value.ReadDeviceRuntimes).SelectMany(a => a.Value.ReadOnlyIdVariableRuntimes).Select(a => a.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
var channels = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
VariableRuntimes = channels.Where(a => a.Value.PluginName == pluginName).SelectMany(a => a.Value.ReadDeviceRuntimes).SelectMany(a => a.Value.Driver?.VariableRuntimes).Select(a => a.Value).Where(a => a != null);
|
||||
VariableRuntimes = channels.Where(a => a.Value.PluginName == pluginName).SelectMany(a => a.Value.ReadDeviceRuntimes).SelectMany(a => a.Value.Driver?.IdVariableRuntimes).Select(a => a.Value).Where(a => a != null);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<TableColumn @bind-Field="@context.Unit" Filterable=true Sortable=true Visible="false" />
|
||||
|
||||
<TableColumn @bind-Field="@context.Index" Filterable=true Sortable=true Visible="false" />
|
||||
<TableColumn @bind-Field="@context.Id" Visible="false" Sortable=true DefaultSort=true DefaultSortOrder="SortOrder.Asc" />
|
||||
<TableColumn @bind-Field="@context.Id" Filterable=true Sortable=true Visible="false" DefaultSort=true DefaultSortOrder="SortOrder.Asc" />
|
||||
@* <TableColumn Field="@context.AlarmEnable" FieldExpression=@(()=>context.AlarmEnable) Filterable Sortable="false" Visible="false" />
|
||||
|
||||
<TableColumn @bind-Field="@context.BoolOpenAlarmEnable" Filterable Sortable="false" Visible="false" />
|
||||
|
||||
@@ -79,10 +79,9 @@ public partial class VariableRuntimeInfo : IDisposable
|
||||
|
||||
private Task<QueryData<VariableRuntime>> OnQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
|
||||
var data = Items
|
||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
||||
.GetQueryData(options);
|
||||
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
|
||||
.GetQueryData(options);
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@
|
||||
<div class="col-12 ">
|
||||
<DateTimeRange @bind-Value="model.SearchDate" ShowLabel="true" />
|
||||
</div>
|
||||
<div class="col-12 ">
|
||||
<BootstrapInput @bind-Value="model.OperateDevice" ShowLabel="true" />
|
||||
</div>
|
||||
<div class="col-12 ">
|
||||
<BootstrapInput @bind-Value="model.OperateObject" ShowLabel="true" />
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<div class="h-100">
|
||||
|
||||
<AdminTable @ref=table TItem="VariableRuntime"
|
||||
<AdminTable @ref=table TItem="AlarmVariable"
|
||||
AutoGenerateColumns="false"
|
||||
ShowAdvancedSearch=false
|
||||
EditDialogSize="Size.Large"
|
||||
@@ -103,5 +103,5 @@
|
||||
</div>
|
||||
|
||||
@code {
|
||||
AdminTable<VariableRuntime> table;
|
||||
AdminTable<AlarmVariable> table;
|
||||
}
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
public partial class RealAlarmPage
|
||||
{
|
||||
private VariableRuntime? SearchModel { get; set; } = new();
|
||||
private AlarmVariable? SearchModel { get; set; } = new();
|
||||
|
||||
#region 查询
|
||||
|
||||
private static async Task<QueryData<VariableRuntime>> OnQueryAsync(QueryPageOptions options)
|
||||
private static async Task<QueryData<AlarmVariable>> OnQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
var realAlarmVariables = await GlobalData.GetCurrentUserRealAlarmVariables().ConfigureAwait(false);
|
||||
var data = realAlarmVariables
|
||||
|
||||
@@ -120,7 +120,7 @@ public static class ResourceUtil
|
||||
{
|
||||
|
||||
|
||||
var channelRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Channel, ChannelRuntime = channelRuntime };
|
||||
var channelRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Channel, ChannelRuntime = channelRuntime, Id = channelRuntime.Id };
|
||||
var channelTreeItemItem = new TreeViewItem<ChannelDeviceTreeItem>(channelRuntimeTreeItem)
|
||||
{
|
||||
Text = channelRuntime.ToString(),
|
||||
@@ -145,7 +145,7 @@ public static class ResourceUtil
|
||||
foreach (var keyValue in channelRuntime.ReadDeviceRuntimes.OrderBy(a => a.Value.DeviceStatus))
|
||||
{
|
||||
var deviceRuntime = keyValue.Value;
|
||||
var deviceRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Device, DeviceRuntime = deviceRuntime };
|
||||
var deviceRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Device, DeviceRuntime = deviceRuntime, Id = deviceRuntime.Id };
|
||||
var deviceTreeItemItem = new TreeViewItem<ChannelDeviceTreeItem>(deviceRuntimeTreeItem)
|
||||
{
|
||||
Text = keyValue.Value.Name,
|
||||
@@ -165,10 +165,10 @@ public static class ResourceUtil
|
||||
channelTreeItemItem.Items.Add(deviceTreeItemItem);
|
||||
}
|
||||
|
||||
|
||||
channelTreeItemItem.Items = channelTreeItemItem.Items.OrderBy(a => a.Value.Id).ToList();
|
||||
pluginItem.Items.Add(channelTreeItemItem);
|
||||
}
|
||||
|
||||
pluginItem.Items = pluginItem.Items.OrderBy(a => a.Value.Id).ToList();
|
||||
trees.Add(pluginItem);
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ public static class ResourceUtil
|
||||
|
||||
foreach (var channelRuntime in dict.Where(a => a.Key.PluginName == pluginName))
|
||||
{
|
||||
var channelRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Channel, ChannelRuntime = channelRuntime.Key };
|
||||
var channelRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Channel, ChannelRuntime = channelRuntime.Key, Id = channelRuntime.Key.Id };
|
||||
var channelTreeItemItem = new TreeViewItem<ChannelDeviceTreeItem>(channelRuntimeTreeItem)
|
||||
{
|
||||
Text = channelRuntime.ToString(),
|
||||
@@ -230,7 +230,7 @@ public static class ResourceUtil
|
||||
|
||||
foreach (var deviceRuntime in channelRuntime.Value.OrderBy(a => a.DeviceStatus))
|
||||
{
|
||||
var deviceRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Device, DeviceRuntime = deviceRuntime };
|
||||
var deviceRuntimeTreeItem = new ChannelDeviceTreeItem() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.Device, DeviceRuntime = deviceRuntime, Id = deviceRuntime.Id };
|
||||
var deviceTreeItemItem = new TreeViewItem<ChannelDeviceTreeItem>(deviceRuntimeTreeItem)
|
||||
{
|
||||
Text = deviceRuntime.Name,
|
||||
@@ -251,10 +251,11 @@ public static class ResourceUtil
|
||||
channelTreeItemItem.Items.Add(deviceTreeItemItem);
|
||||
}
|
||||
|
||||
|
||||
channelTreeItemItem.Items = channelTreeItemItem.Items.OrderBy(a => a.Value.Id).ToList();
|
||||
pluginItem.Items.Add(channelTreeItemItem);
|
||||
}
|
||||
|
||||
pluginItem.Items = pluginItem.Items.OrderBy(a => a.Value.Id).ToList();
|
||||
trees.Add(pluginItem);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,4 +30,7 @@ public class DeviceDataWithValue
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string LastErrorMessage { get; set; }
|
||||
|
||||
/// <inheritdoc cref="DeviceRuntime.ReadOnlyVariableRuntimes"/>
|
||||
public Dictionary<string, VariableDataWithValue> ReadOnlyVariableRuntimes { get; set; }
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ using Mapster;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
using ThingsGateway.NewLife;
|
||||
|
||||
@@ -23,10 +22,8 @@ using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
|
||||
namespace ThingsGateway.Management;
|
||||
|
||||
|
||||
internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHostedService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
@@ -158,30 +155,11 @@ internal sealed class RedundancyHostedService : BackgroundService, IRedundancyHo
|
||||
// 如果 online 为 true,表示设备在线
|
||||
if (online)
|
||||
{
|
||||
var deviceRunTimes = GlobalData.ReadOnlyIdDevices.Select(a => a.Value).Where(a => a.IsCollect == true).Adapt<List<DeviceDataWithValue>>();
|
||||
var variableRuntimes = GlobalData.ReadOnlyVariables.Select(a => a.Value).Adapt<List<VariableDataWithValue>>();
|
||||
var variableRuntimes1 = variableRuntimes.ChunkBetter(80000);
|
||||
var variableRuntimes1Count = variableRuntimes1.Count();
|
||||
int itemsPerList = (int)Math.Ceiling((double)deviceRunTimes.Count / variableRuntimes1Count);
|
||||
var deviceRunTimes1 = deviceRunTimes.ChunkBetter(itemsPerList, true).ToList();
|
||||
var deviceRunTimes = GlobalData.ReadOnlyIdDevices.Where(a => a.Value.IsCollect == true).Select(a => a.Value).Adapt<List<DeviceDataWithValue>>();
|
||||
|
||||
int i = 0;
|
||||
List<Task> tasks = new List<Task>();
|
||||
foreach (var item in variableRuntimes1)
|
||||
{
|
||||
List<DeviceDataWithValue> devices = new();
|
||||
if (deviceRunTimes1.Count >= i + 1)
|
||||
{
|
||||
devices = deviceRunTimes1[i].ToList();
|
||||
}
|
||||
var variables = item.ToList();
|
||||
// 将 GlobalData.CollectDevices 和 GlobalData.Variables 同步到从站
|
||||
Task task = tcpDmtpService.Clients.FirstOrDefault().GetDmtpRpcActor().InvokeAsync(
|
||||
nameof(ReverseCallbackServer.UpdateGatewayData), null, waitInvoke, devices, variables);
|
||||
tasks.Add(task);
|
||||
i++;
|
||||
}
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
// 将 GlobalData.CollectDevices 和 GlobalData.Variables 同步到从站
|
||||
await tcpDmtpService.Clients.FirstOrDefault().GetDmtpRpcActor().InvokeAsync(
|
||||
nameof(ReverseCallbackServer.UpdateGatewayData), null, waitInvoke, deviceRunTimes).ConfigureAwait(false);
|
||||
_log?.LogTrace($"Update StandbyStation data success");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,22 +18,24 @@ namespace ThingsGateway.Management;
|
||||
public partial class ReverseCallbackServer : RpcServer
|
||||
{
|
||||
[DmtpRpc(MethodInvoke = true)]
|
||||
public void UpdateGatewayData(List<DeviceDataWithValue> deviceDatas, List<VariableDataWithValue> variableDatas)
|
||||
public void UpdateGatewayData(List<DeviceDataWithValue> deviceDatas)
|
||||
{
|
||||
|
||||
foreach (var deviceData in deviceDatas)
|
||||
{
|
||||
if (GlobalData.ReadOnlyDevices.TryGetValue(deviceData.Name, out var value))
|
||||
if (GlobalData.ReadOnlyDevices.TryGetValue(deviceData.Name, out var device))
|
||||
{
|
||||
value.SetDeviceStatus(deviceData.ActiveTime, deviceData.DeviceStatus == DeviceStatusEnum.OnLine ? false : true, lastErrorMessage: deviceData.LastErrorMessage);
|
||||
}
|
||||
}
|
||||
foreach (var variableData in variableDatas)
|
||||
{
|
||||
if (GlobalData.ReadOnlyVariables.TryGetValue(variableData.Name, out var value))
|
||||
{
|
||||
value.SetValue(variableData.RawValue, variableData.CollectTime, variableData.IsOnline);
|
||||
value.SetErrorMessage(variableData.LastErrorMessage);
|
||||
device.SetDeviceStatus(deviceData.ActiveTime, deviceData.DeviceStatus == DeviceStatusEnum.OnLine ? false : true, lastErrorMessage: deviceData.LastErrorMessage);
|
||||
|
||||
foreach (var variableData in deviceData.ReadOnlyVariableRuntimes)
|
||||
{
|
||||
if (device.ReadOnlyVariableRuntimes.TryGetValue(variableData.Key, out var value))
|
||||
{
|
||||
value.SetValue(variableData.Value.RawValue, variableData.Value.CollectTime, variableData.Value.IsOnline);
|
||||
value.SetErrorMessage(variableData.Value.LastErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
@namespace ThingsGateway.RulesEngine
|
||||
|
||||
@using ThingsGateway.Blazor.Diagrams.Components.Renderers
|
||||
@using ThingsGateway.Extension.Generic
|
||||
@using ThingsGateway.Gateway.Application
|
||||
@using ThingsGateway.NewLife.Extension
|
||||
<div class="custom-node @(Node.Selected ? " selected" : "")">
|
||||
<div class="card" style="width: 400px;">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title mb-2">@Localizer[Node.Title]</h6>
|
||||
<div class="form-group">
|
||||
<Select class="form-control"
|
||||
@bind-Value="@Node.Text"
|
||||
IsVirtualize
|
||||
OnQueryAsync="(a)=>OnRedundantDevicesQuery(a)"
|
||||
ShowSearch="true"
|
||||
PlaceHolder="@Localizer[Node.Placeholder]"
|
||||
@onpointerdown:stopPropagation
|
||||
@onpointerup:stopPropagation
|
||||
@onpointermove:stopPropagation />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@foreach (var port in Node.Ports)
|
||||
{
|
||||
<PortRenderer @key="port" Port="port"></PortRenderer>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Inject]
|
||||
IStringLocalizer<ThingsGateway.RulesEngine._Imports> Localizer { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TextNode Node { get; set; }
|
||||
|
||||
private static async Task<QueryData<SelectedItem>> OnRedundantDevicesQuery(VirtualizeQueryOption option)
|
||||
{
|
||||
var ret = new QueryData<SelectedItem>()
|
||||
{
|
||||
IsSorted = false,
|
||||
IsFiltered = false,
|
||||
IsAdvanceSearch = false,
|
||||
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||
};
|
||||
|
||||
var devices = await GlobalData.GetCurrentUserAlarmEnableVariables().ConfigureAwait(false);
|
||||
var items = devices.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Value.Name.Contains(option.SearchText)).Select(a => a.Value).Take(20)
|
||||
.Select(a => new SelectedItem(a.Name, a.Name));
|
||||
|
||||
ret.TotalCount = items.Count();
|
||||
ret.Items = items;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace ThingsGateway.RulesEngine;
|
||||
|
||||
public abstract class VariableNode : TextNode
|
||||
{
|
||||
|
||||
public VariableNode(string id, Point? position = null) : base(id, position)
|
||||
{
|
||||
}
|
||||
|
||||
[ModelValue]
|
||||
public string DeviceText { get; set; }
|
||||
}
|
||||
@@ -4,20 +4,30 @@
|
||||
@using ThingsGateway.Extension.Generic
|
||||
@using ThingsGateway.Gateway.Application
|
||||
@using ThingsGateway.NewLife.Extension
|
||||
<div class="custom-node @(Node.Selected ? " selected" : "")">
|
||||
<div class="custom-node variable @(Node.Selected ? " selected" : "")">
|
||||
<div class="card" style="width: 400px;">
|
||||
<div class="card-body">
|
||||
<h6 class="card-title mb-2">@Localizer[Node.Title]</h6>
|
||||
<div class="form-group">
|
||||
<Select class="form-control"
|
||||
@bind-Value="@Node.DeviceText"
|
||||
IsVirtualize
|
||||
OnQueryAsync="OnRedundantDevicesQuery"
|
||||
ShowSearch="true"
|
||||
@onpointerdown:stopPropagation
|
||||
@onpointerup:stopPropagation
|
||||
@onpointermove:stopPropagation />
|
||||
|
||||
<Select class="form-control mt-2"
|
||||
@bind-Value="@Node.Text"
|
||||
IsVirtualize
|
||||
OnQueryAsync="(a)=>OnRedundantDevicesQuery(a)"
|
||||
OnQueryAsync="OnRedundantVariablesQuery"
|
||||
ShowSearch="true"
|
||||
PlaceHolder="@Localizer[Node.Placeholder]"
|
||||
@onpointerdown:stopPropagation
|
||||
@onpointerup:stopPropagation
|
||||
@onpointermove:stopPropagation />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,31 +38,4 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Inject]
|
||||
IStringLocalizer<ThingsGateway.RulesEngine._Imports> Localizer { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public TextNode Node { get; set; }
|
||||
|
||||
private static async Task<QueryData<SelectedItem>> OnRedundantDevicesQuery(VirtualizeQueryOption option)
|
||||
{
|
||||
var ret = new QueryData<SelectedItem>()
|
||||
{
|
||||
IsSorted = false,
|
||||
IsFiltered = false,
|
||||
IsAdvanceSearch = false,
|
||||
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||
};
|
||||
|
||||
var devices = await GlobalData.GetCurrentUserVariables().ConfigureAwait(false);
|
||||
var items = devices.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Value.Name.Contains(option.SearchText)).Select(a => a.Value).Take(20)
|
||||
.Select(a => new SelectedItem(a.Name, a.Name));
|
||||
|
||||
ret.TotalCount = items.Count();
|
||||
ret.Items = items;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// ------------------------------------------------------------------------------
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD>Ϊȫ<CEAA>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ǣ<EFBFBD><C7A3><EFBFBD><EFBFBD><EFBFBD>ԭ<EFBFBD><D4AD><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>·<EFBFBD><C2B7>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>
|
||||
// <20>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD>Ȩ<EFBFBD><C8A8><EFBFBD><EFBFBD><EFBFBD>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ĵ<EFBFBD><C4B4>룩<EFBFBD><EBA3A9><EFBFBD><EFBFBD><EFBFBD>߱<EFBFBD><DFB1><EFBFBD>Diego<67><6F><EFBFBD><EFBFBD>
|
||||
// Դ<><D4B4><EFBFBD><EFBFBD>ʹ<EFBFBD><CAB9>Э<EFBFBD><D0AD><EFBFBD><EFBFBD>ѭ<EFBFBD><D1AD><EFBFBD>ֿ<EFBFBD><D6BF>Ŀ<EFBFBD>ԴЭ<D4B4>鼰<EFBFBD><E9BCB0><EFBFBD><EFBFBD>Э<EFBFBD><D0AD>
|
||||
// GiteeԴ<65><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://gitee.com/diego2098/ThingsGateway
|
||||
// GithubԴ<62><D4B4><EFBFBD><EFBFBD><EFBFBD>ֿ⣺https://github.com/kimdiego2098/ThingsGateway
|
||||
// ʹ<><CAB9><EFBFBD>ĵ<EFBFBD><C4B5><EFBFBD>https://thingsgateway.cn/
|
||||
// QQȺ<51><C8BA>605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
namespace ThingsGateway.RulesEngine
|
||||
{
|
||||
public partial class VariableWidget
|
||||
{
|
||||
[Inject]
|
||||
IStringLocalizer<ThingsGateway.RulesEngine._Imports> Localizer { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public VariableNode Node { get; set; }
|
||||
|
||||
private static async Task<QueryData<SelectedItem>> OnRedundantDevicesQuery(VirtualizeQueryOption option)
|
||||
{
|
||||
var ret = new QueryData<SelectedItem>()
|
||||
{
|
||||
IsSorted = false,
|
||||
IsFiltered = false,
|
||||
IsAdvanceSearch = false,
|
||||
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||
};
|
||||
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
var items = devices.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Value.Name.Contains(option.SearchText)).Select(a => a.Value).Take(20)
|
||||
.Select(a => new SelectedItem(a.Name, a.Name));
|
||||
|
||||
ret.TotalCount = items.Count();
|
||||
ret.Items = items;
|
||||
return ret;
|
||||
}
|
||||
private async Task<QueryData<SelectedItem>> OnRedundantVariablesQuery(VirtualizeQueryOption option)
|
||||
{
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
var ret = new QueryData<SelectedItem>()
|
||||
{
|
||||
IsSorted = false,
|
||||
IsFiltered = false,
|
||||
IsAdvanceSearch = false,
|
||||
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
|
||||
};
|
||||
|
||||
if (GlobalData.ReadOnlyDevices.TryGetValue(Node.DeviceText, out var device))
|
||||
{
|
||||
var items = device.ReadOnlyIdVariableRuntimes.WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Value.Name.Contains(option.SearchText)).Select(a => a.Value).Take(20)
|
||||
.Select(a => new SelectedItem(a.Name, a.Name));
|
||||
|
||||
ret.TotalCount = items.Count();
|
||||
ret.Items = items;
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.TotalCount = 0;
|
||||
ret.Items = new List<SelectedItem>();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ using TouchSocket.Core;
|
||||
namespace ThingsGateway.RulesEngine;
|
||||
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = "_content/ThingsGateway.RulesEngine/img/Rpc.svg", Desc = nameof(VariableRpcNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(VariableWidget))]
|
||||
public class VariableRpcNode : TextNode, IActuatorNode
|
||||
public class VariableRpcNode : VariableNode, IActuatorNode
|
||||
{
|
||||
|
||||
public VariableRpcNode(string id, Point? position = null) : base(id, position)
|
||||
@@ -13,20 +13,20 @@ public class VariableRpcNode : TextNode, IActuatorNode
|
||||
|
||||
async Task<NodeOutput> IActuatorNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
if (GlobalData.ReadOnlyVariables.TryGetValue(Text, out var value))
|
||||
if (GlobalData.ReadOnlyDevices.TryGetValue(DeviceText, out var device))
|
||||
{
|
||||
var data = await value.RpcAsync(input.JToken.ToString(), $"RulesEngine: {RulesEngineName}", cancellationToken).ConfigureAwait(false);
|
||||
if (data.IsSuccess)
|
||||
LogMessage?.Trace($" VariableRpcNode - VariableName {Text} : execute success");
|
||||
else
|
||||
LogMessage?.Warning($" VariableRpcNode - VariableName {Text} : {data.ErrorMessage}");
|
||||
return new NodeOutput() { Value = data };
|
||||
}
|
||||
else
|
||||
{
|
||||
LogMessage?.Warning($" VariableRpcNode - VariableName {Text} : not found");
|
||||
return new NodeOutput() { };
|
||||
if (device.ReadOnlyVariableRuntimes.TryGetValue(Text, out var value))
|
||||
{
|
||||
var data = await value.RpcAsync(input.JToken.ToString(), $"RulesEngine: {RulesEngineName}", cancellationToken).ConfigureAwait(false);
|
||||
if (data.IsSuccess)
|
||||
LogMessage?.Trace($" VariableRpcNode - VariableName {Text} : execute success");
|
||||
else
|
||||
LogMessage?.Warning($" VariableRpcNode - VariableName {Text} : {data.ErrorMessage}");
|
||||
return new NodeOutput() { Value = data };
|
||||
}
|
||||
}
|
||||
LogMessage?.Warning($" VariableRpcNode - VariableName {Text} : not found");
|
||||
return new NodeOutput() { };
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.RulesEngine;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.RulesEngine/img/ValueChanged.svg", Desc = nameof(AlarmChangedTriggerNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(AlarmVariableWidget))]
|
||||
public class AlarmChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.RulesEngine/img/ValueChanged.svg", Desc = nameof(AlarmChangedTriggerNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(VariableWidget))]
|
||||
public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public AlarmChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "AlarmChangedTriggerNode"; Placeholder = "AlarmChangedTriggerNode.Placeholder"; }
|
||||
|
||||
@@ -19,31 +19,50 @@ public class AlarmChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func)
|
||||
{
|
||||
Func = func;
|
||||
FuncDict.Add(this, func);
|
||||
if (!AlarmChangedTriggerNodeDict.TryGetValue(Text, out var list))
|
||||
FuncDict.TryAdd(this, func);
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(DeviceText, out var deviceVariableDict))
|
||||
{
|
||||
var list1 = new ConcurrentList<AlarmChangedTriggerNode>();
|
||||
list1.Add(this);
|
||||
AlarmChangedTriggerNodeDict.Add(Text, list1);
|
||||
|
||||
if (deviceVariableDict.TryGetValue(Text, out var alarmChangedTriggerNodes))
|
||||
{
|
||||
alarmChangedTriggerNodes.Add(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceVariableDict.TryAdd(Text, new());
|
||||
deviceVariableDict[Text].Add(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(this);
|
||||
AlarmChangedTriggerNodeDict.TryAdd(DeviceText, new());
|
||||
AlarmChangedTriggerNodeDict[DeviceText].TryAdd(Text, new());
|
||||
AlarmChangedTriggerNodeDict[DeviceText][Text].Add(this);
|
||||
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public static Dictionary<string, ConcurrentList<AlarmChangedTriggerNode>> AlarmChangedTriggerNodeDict = new();
|
||||
public static Dictionary<AlarmChangedTriggerNode, Func<NodeOutput, Task>> FuncDict = new();
|
||||
|
||||
public static ConcurrentDictionary<string, ConcurrentDictionary<string, ConcurrentList<AlarmChangedTriggerNode>>> AlarmChangedTriggerNodeDict = new();
|
||||
|
||||
public static ConcurrentDictionary<AlarmChangedTriggerNode, Func<NodeOutput, Task>> FuncDict = new();
|
||||
|
||||
public static BlockingCollection<AlarmVariable> AlarmVariables = new();
|
||||
static AlarmChangedTriggerNode()
|
||||
{
|
||||
_ = RunAsync();
|
||||
GlobalData.AlarmChangedEvent -= AlarmHostedService_OnAlarmChanged;
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a =>
|
||||
{
|
||||
AlarmHostedService_OnAlarmChanged(a.Value);
|
||||
});
|
||||
GlobalData.AlarmChangedEvent += AlarmHostedService_OnAlarmChanged;
|
||||
}
|
||||
private static void AlarmHostedService_OnAlarmChanged(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(alarmVariable.Name, out var list) && list?.Count > 0)
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(alarmVariable.DeviceName, out var alarmNodeDict) &&
|
||||
alarmNodeDict.TryGetValue(alarmVariable.Name, out var alarmChangedTriggerNodes) &&
|
||||
alarmChangedTriggerNodes?.Count > 0)
|
||||
{
|
||||
if (!AlarmVariables.IsAddingCompleted)
|
||||
{
|
||||
@@ -59,27 +78,26 @@ public class AlarmChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
}
|
||||
static Task RunAsync()
|
||||
{
|
||||
return AlarmVariables.GetConsumingEnumerable().ParallelForEachAsync((async (alarmVariables, token) =>
|
||||
return AlarmVariables.GetConsumingEnumerable().ParallelForEachAsync((async (alarmVariable, token) =>
|
||||
{
|
||||
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(alarmVariables.Name, out var valueChangedTriggerNodes))
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(alarmVariable.DeviceName, out var alarmNodeDict) &&
|
||||
alarmNodeDict.TryGetValue(alarmVariable.Name, out var alarmChangedTriggerNodes))
|
||||
{
|
||||
foreach (var item in valueChangedTriggerNodes)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (FuncDict.TryGetValue(item, out var func))
|
||||
{
|
||||
item.LogMessage?.Trace($"Alarm changed: {item.Text}");
|
||||
await func.Invoke(new NodeOutput() { Value = alarmVariables }).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
item.LogMessage?.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
await alarmChangedTriggerNodes.ParallelForEachAsync(async (item, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (FuncDict.TryGetValue(item, out var func))
|
||||
{
|
||||
item.LogMessage?.Trace($"Alarm changed: {item.Text}");
|
||||
await func.Invoke(new NodeOutput() { Value = alarmVariable }).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
item.LogMessage?.LogWarning(ex);
|
||||
}
|
||||
}, Environment.ProcessorCount, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}), Environment.ProcessorCount / 2 <= 1 ? 2 : Environment.ProcessorCount / 2, default);
|
||||
@@ -87,45 +105,11 @@ public class AlarmChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FuncDict.Remove(this);
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(Text, out var list))
|
||||
FuncDict.TryRemove(this, out _);
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(DeviceText, out var alarmNodeDict) &&
|
||||
alarmNodeDict.TryGetValue(Text, out var alarmChangedTriggerNodes))
|
||||
{
|
||||
list.Remove(this);
|
||||
alarmChangedTriggerNodes.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//using ThingsGateway.Gateway.Application;
|
||||
|
||||
//using TouchSocket.Core;
|
||||
|
||||
//namespace ThingsGateway.RulesEngine;
|
||||
|
||||
//[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.RulesEngine/img/ValueChanged.svg", Desc = nameof(AlarmChangedTriggerNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(TextWidget))]
|
||||
//public class AlarmChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
//{
|
||||
// public AlarmChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "AlarmChangedTriggerNode"; Placeholder = "AlarmChangedTriggerNode.Placeholder"; }
|
||||
|
||||
// private Func<NodeOutput, Task> Func { get; set; }
|
||||
// Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func)
|
||||
// {
|
||||
// Func = func;
|
||||
// GlobalData.AlarmHostedService.OnAlarmChanged += AlarmHostedService_OnAlarmChanged;
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
|
||||
// private void AlarmHostedService_OnAlarmChanged(AlarmVariable alarmVariable)
|
||||
// {
|
||||
// if (alarmVariable.Name == Text)
|
||||
// {
|
||||
// LogMessage?.Trace($"Alarm changed: {Text}");
|
||||
// _ = Func?.Invoke(new NodeOutput() { Value = alarmVariable });
|
||||
// }
|
||||
// }
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// GlobalData.AlarmHostedService.OnAlarmChanged -= AlarmHostedService_OnAlarmChanged;
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -21,9 +21,9 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
FuncDict.Add(this, func);
|
||||
if (!DeviceChangedTriggerNodeDict.TryGetValue(Text, out var list))
|
||||
{
|
||||
var list1 = new ConcurrentList<DeviceChangedTriggerNode>();
|
||||
list1.Add(this);
|
||||
DeviceChangedTriggerNodeDict.Add(Text, list1);
|
||||
var deviceChangedTriggerNodes = new ConcurrentList<DeviceChangedTriggerNode>();
|
||||
deviceChangedTriggerNodes.Add(this);
|
||||
DeviceChangedTriggerNodeDict.Add(Text, deviceChangedTriggerNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -34,15 +34,17 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
public static Dictionary<string, ConcurrentList<DeviceChangedTriggerNode>> DeviceChangedTriggerNodeDict = new();
|
||||
public static Dictionary<DeviceChangedTriggerNode, Func<NodeOutput, Task>> FuncDict = new();
|
||||
|
||||
public static BlockingCollection<DeviceData> DeviceDatas = new();
|
||||
public static BlockingCollection<DeviceBasicData> DeviceDatas = new();
|
||||
|
||||
static DeviceChangedTriggerNode()
|
||||
{
|
||||
_ = RunAsync();
|
||||
GlobalData.DeviceStatusChangeEvent += GlobalData_DeviceStatusChangeEvent;
|
||||
}
|
||||
|
||||
private static void GlobalData_DeviceStatusChangeEvent(DeviceRuntime deviceRunTime, DeviceBasicData deviceData)
|
||||
{
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(deviceData.Name, out var list) && list?.Count > 0)
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(deviceData.Name, out var deviceChangedTriggerNodes) && deviceChangedTriggerNodes?.Count > 0)
|
||||
{
|
||||
if (!DeviceDatas.IsAddingCompleted)
|
||||
{
|
||||
@@ -59,27 +61,26 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
static Task RunAsync()
|
||||
{
|
||||
return DeviceDatas.GetConsumingEnumerable().ParallelForEachAsync((async (deviceDatas, token) =>
|
||||
|
||||
{
|
||||
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(deviceDatas.Name, out var valueChangedTriggerNodes))
|
||||
{
|
||||
foreach (var item in valueChangedTriggerNodes)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (FuncDict.TryGetValue(item, out var func))
|
||||
{
|
||||
item.LogMessage?.Trace($"Device changed: {item.Text}");
|
||||
await func.Invoke(new NodeOutput() { Value = deviceDatas }).ConfigureAwait(false);
|
||||
await valueChangedTriggerNodes.ParallelForEachAsync(async (item, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (FuncDict.TryGetValue(item, out var func))
|
||||
{
|
||||
item.LogMessage?.Trace($"Device changed: {item.Text}");
|
||||
await func.Invoke(new NodeOutput() { Value = deviceDatas }).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
item.LogMessage?.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
item.LogMessage?.LogWarning(ex);
|
||||
}
|
||||
}, Environment.ProcessorCount, token).ConfigureAwait(false);
|
||||
}
|
||||
}), Environment.ProcessorCount / 2 <= 1 ? 2 : Environment.ProcessorCount / 2, default);
|
||||
}
|
||||
@@ -95,40 +96,3 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//using ThingsGateway.Gateway.Application;
|
||||
|
||||
//using TouchSocket.Core;
|
||||
|
||||
//namespace ThingsGateway.RulesEngine;
|
||||
|
||||
//[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.RulesEngine/img/ValueChanged.svg", Desc = nameof(DeviceChangedTriggerNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(TextWidget))]
|
||||
//public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
//{
|
||||
// public DeviceChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "DeviceChangedTriggerNode"; Placeholder = "DeviceChangedTriggerNode.Placeholder"; }
|
||||
|
||||
|
||||
// private Func<NodeOutput, Task> Func { get; set; }
|
||||
// Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func)
|
||||
// {
|
||||
// Func = func;
|
||||
// GlobalData.DeviceStatusChangeEvent += GlobalData_DeviceStatusChangeEvent;
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
|
||||
// private void GlobalData_DeviceStatusChangeEvent(DeviceRuntime deviceRunTime, DeviceBasicData deviceData)
|
||||
// {
|
||||
// if (deviceRunTime.Name == Text)
|
||||
// {
|
||||
// LogMessage?.Trace($"Device changed: {Text}");
|
||||
// _ = Func?.Invoke(new NodeOutput() { Value = deviceRunTime });
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// GlobalData.DeviceStatusChangeEvent -= GlobalData_DeviceStatusChangeEvent;
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -8,7 +8,7 @@ using TouchSocket.Core;
|
||||
namespace ThingsGateway.RulesEngine;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.RulesEngine/img/ValueChanged.svg", Desc = nameof(ValueChangedTriggerNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(VariableWidget))]
|
||||
public class ValueChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
public class ValueChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public ValueChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "ValueChangedTriggerNode"; Placeholder = "ValueChangedTriggerNode.Placeholder"; }
|
||||
|
||||
@@ -16,21 +16,31 @@ public class ValueChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func)
|
||||
{
|
||||
Func = func;
|
||||
FuncDict.Add(this, func);
|
||||
if (!ValueChangedTriggerNodeDict.TryGetValue(Text, out var list))
|
||||
FuncDict.TryAdd(this, func);
|
||||
if (ValueChangedTriggerNodeDict.TryGetValue(DeviceText, out var deviceVariableDict))
|
||||
{
|
||||
var list1 = new ConcurrentList<ValueChangedTriggerNode>();
|
||||
list1.Add(this);
|
||||
ValueChangedTriggerNodeDict.Add(Text, list1);
|
||||
|
||||
if (deviceVariableDict.TryGetValue(Text, out var valueChangedTriggerNodes))
|
||||
{
|
||||
valueChangedTriggerNodes.Add(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceVariableDict.TryAdd(Text, new());
|
||||
deviceVariableDict[Text].Add(this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(this);
|
||||
ValueChangedTriggerNodeDict.TryAdd(DeviceText, new());
|
||||
ValueChangedTriggerNodeDict[DeviceText].TryAdd(Text, new());
|
||||
ValueChangedTriggerNodeDict[DeviceText][Text].Add(this);
|
||||
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public static Dictionary<string, ConcurrentList<ValueChangedTriggerNode>> ValueChangedTriggerNodeDict = new();
|
||||
public static Dictionary<ValueChangedTriggerNode, Func<NodeOutput, Task>> FuncDict = new();
|
||||
public static ConcurrentDictionary<string, ConcurrentDictionary<string, ConcurrentList<ValueChangedTriggerNode>>> ValueChangedTriggerNodeDict = new();
|
||||
public static ConcurrentDictionary<ValueChangedTriggerNode, Func<NodeOutput, Task>> FuncDict = new();
|
||||
|
||||
public static BlockingCollection<VariableBasicData> VariableBasicDatas = new();
|
||||
static ValueChangedTriggerNode()
|
||||
@@ -40,7 +50,9 @@ public class ValueChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
}
|
||||
private static void GlobalData_VariableValueChangeEvent(VariableRuntime variableRuntime, VariableBasicData variableData)
|
||||
{
|
||||
if (ValueChangedTriggerNodeDict.TryGetValue(variableData.Name, out var list) && list?.Count > 0)
|
||||
if (ValueChangedTriggerNodeDict.TryGetValue(variableRuntime.DeviceName, out var valueNodeDict) &&
|
||||
valueNodeDict.TryGetValue(variableRuntime.Name, out var valueChangedTriggerNodes) &&
|
||||
valueChangedTriggerNodes?.Count > 0)
|
||||
{
|
||||
if (!VariableBasicDatas.IsAddingCompleted)
|
||||
{
|
||||
@@ -59,24 +71,25 @@ public class ValueChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
return VariableBasicDatas.GetConsumingEnumerable().ParallelForEachAsync((async (variableBasicData, token) =>
|
||||
{
|
||||
|
||||
if (ValueChangedTriggerNodeDict.TryGetValue(variableBasicData.Name, out var valueChangedTriggerNodes))
|
||||
if (ValueChangedTriggerNodeDict.TryGetValue(variableBasicData.DeviceName, out var valueNodeDict) &&
|
||||
valueNodeDict.TryGetValue(variableBasicData.Name, out var valueChangedTriggerNodes))
|
||||
{
|
||||
foreach (var item in valueChangedTriggerNodes)
|
||||
await valueChangedTriggerNodes.ParallelForEachAsync(async (item, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
if (FuncDict.TryGetValue(item, out var func))
|
||||
{
|
||||
if (FuncDict.TryGetValue(item, out var func))
|
||||
{
|
||||
item.LogMessage?.Trace($"Variable changed: {item.Text}");
|
||||
await func.Invoke(new NodeOutput() { Value = variableBasicData }).ConfigureAwait(false);
|
||||
item.LogMessage?.Trace($"Variable changed: {item.Text}");
|
||||
await func.Invoke(new NodeOutput() { Value = variableBasicData }).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
item.LogMessage?.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
item.LogMessage?.LogWarning(ex);
|
||||
}
|
||||
}, Environment.ProcessorCount, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}), Environment.ProcessorCount / 2 <= 1 ? 2 : Environment.ProcessorCount / 2, default);
|
||||
@@ -85,44 +98,11 @@ public class ValueChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FuncDict.Remove(this);
|
||||
if (ValueChangedTriggerNodeDict.TryGetValue(Text, out var list))
|
||||
FuncDict.TryRemove(this, out _);
|
||||
if (ValueChangedTriggerNodeDict.TryGetValue(DeviceText, out var valueNodeDict) &&
|
||||
valueNodeDict.TryGetValue(Text, out var valueChangedTriggerNodes))
|
||||
{
|
||||
list.Remove(this);
|
||||
valueChangedTriggerNodes.Remove(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
//using ThingsGateway.Gateway.Application;
|
||||
|
||||
//using TouchSocket.Core;
|
||||
|
||||
//namespace ThingsGateway.RulesEngine;
|
||||
|
||||
//[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.RulesEngine/img/ValueChanged.svg", Desc = nameof(ValueChangedTriggerNode), LocalizerType = typeof(ThingsGateway.RulesEngine._Imports), WidgetType = typeof(TextWidget))]
|
||||
//public class ValueChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
//{
|
||||
// public ValueChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "ValueChangedTriggerNode"; Placeholder = "ValueChangedTriggerNode.Placeholder"; }
|
||||
|
||||
// private Func<NodeOutput, Task> Func { get; set; }
|
||||
// Task ITriggerNode.StartAsync(Func<NodeOutput, Task> func)
|
||||
// {
|
||||
// Func = func;
|
||||
// GlobalData.VariableValueChangeEvent += GlobalData_VariableValueChangeEvent; ;
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
|
||||
// private void GlobalData_VariableValueChangeEvent(VariableRuntime variableRuntime, VariableBasicData variableData)
|
||||
// {
|
||||
// if (variableRuntime.Name == Text)
|
||||
// {
|
||||
// LogMessage?.Trace($"Variable changed: {Text}");
|
||||
// _ = Func?.Invoke(new NodeOutput() { Value = variableRuntime });
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// public void Dispose()
|
||||
// {
|
||||
// GlobalData.VariableValueChangeEvent -= GlobalData_VariableValueChangeEvent; ;
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -68,6 +68,18 @@
|
||||
left: 115px;
|
||||
}
|
||||
|
||||
.rulesengine ::deep .custom-node .variable .diagram-port.bottom {
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
left: 185px;
|
||||
}
|
||||
|
||||
.rulesengine ::deep .custom-node .variable .diagram-port.top {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
left: 185px;
|
||||
}
|
||||
|
||||
.rulesengine ::deep .diagram-canvas.grid {
|
||||
background-size: 50px 50px;
|
||||
background-image: linear-gradient(to right, rgb(0, 0, 0, 0.05) 1px, transparent 1px), linear-gradient(to bottom, rgb(0, 0, 0, 0.05) 1px, transparent 1px);
|
||||
|
||||
@@ -454,7 +454,7 @@ public class OpcDaMaster : IDisposable
|
||||
checkTimer.Enabled = false;
|
||||
checkTimer.Stop();
|
||||
}
|
||||
|
||||
ItemDicts.Clear();
|
||||
try
|
||||
{
|
||||
m_server?.Dispose();
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
@@ -26,7 +26,7 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariableMode
|
||||
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<QuestDBHistoryValue>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateVarModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateVarModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
protected override void VariableTimeInterval(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
{
|
||||
|
||||
@@ -219,7 +219,7 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
|
||||
{
|
||||
try
|
||||
{
|
||||
var varList = VariableRuntimes.Select(a => a.Value).Adapt<List<SQLRealValue>>();
|
||||
var varList = IdVariableRuntimes.Select(a => a.Value).Adapt<List<SQLRealValue>>();
|
||||
|
||||
var result = await UpdateAsync(varList, cancellationToken).ConfigureAwait(false);
|
||||
if (success != result.IsSuccess)
|
||||
|
||||
@@ -29,7 +29,7 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
|
||||
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<SQLHistoryValue>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateVarModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateVarModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
protected override void VariableTimeInterval(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,8 @@ using SqlSugar;
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Foundation;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
|
||||
/// <summary>
|
||||
@@ -38,6 +40,11 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheVariableModel<Histor
|
||||
{
|
||||
|
||||
_config.ForType<AlarmVariable, HistoryAlarm>().Map(dest => dest.Id, (src) => CommonUtils.GetSingleId());
|
||||
GlobalData.AlarmChangedEvent -= AlarmWorker_OnAlarmChanged;
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a =>
|
||||
{
|
||||
AlarmWorker_OnAlarmChanged(a.Value);
|
||||
});
|
||||
GlobalData.AlarmChangedEvent += AlarmWorker_OnAlarmChanged;
|
||||
|
||||
await base.InitChannelAsync(channel).ConfigureAwait(false);
|
||||
@@ -45,10 +52,10 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheVariableModel<Histor
|
||||
public override async Task AfterVariablesChangedAsync()
|
||||
{
|
||||
await base.AfterVariablesChangedAsync().ConfigureAwait(false);
|
||||
VariableRuntimes = GlobalData.ReadOnlyVariables.Where(a => a.Value.AlarmEnable).ToDictionary(a => a.Key, a => a.Value);
|
||||
IdVariableRuntimes = GlobalData.ReadOnlyIdVariables.Where(a => a.Value.AlarmEnable).ToDictionary();
|
||||
|
||||
CollectDevices = GlobalData.ReadOnlyIdDevices
|
||||
.Where(a => VariableRuntimes.Select(b => b.Value.DeviceId).Contains(a.Value.Id))
|
||||
.Where(a => IdVariableRuntimes.Select(b => b.Value.DeviceId).Contains(a.Value.Id))
|
||||
.ToDictionary(a => a.Key, a => a.Value);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheVariableModel<Histor
|
||||
{
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<HistoryAlarm>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateT(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateT(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
private void AlarmWorker_OnAlarmChanged(AlarmVariable alarmVariable)
|
||||
|
||||
@@ -29,7 +29,7 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariableM
|
||||
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<TDengineDBHistoryValue>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateVarModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateVarModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override void VariableTimeInterval(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace ThingsGateway.Plugin.Webhook;
|
||||
/// <summary>
|
||||
/// WebhookClient,RPC方法适配mqttNet
|
||||
/// </summary>
|
||||
public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableData, DeviceBasicData, AlarmVariable>
|
||||
public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private readonly WebhookProperty _driverPropertys = new();
|
||||
private readonly WebhookVariableProperty _variablePropertys = new();
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ThingsGateway.Plugin.Webhook;
|
||||
/// <summary>
|
||||
/// WebhookClient
|
||||
/// </summary>
|
||||
public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableData, DeviceBasicData, AlarmVariable>
|
||||
public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
|
||||
protected override void AlarmChange(AlarmVariable alarmVariable)
|
||||
@@ -52,19 +52,19 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableData,
|
||||
|
||||
protected override ValueTask<OperResult> UpdateAlarmModel(IEnumerable<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateAlarmModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateAlarmModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateDevModel(IEnumerable<CacheDBItem<DeviceBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
|
||||
return UpdateDevModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateDevModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableData>> item, CancellationToken cancellationToken)
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateVarModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateVarModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override void VariableTimeInterval(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
@@ -160,9 +160,9 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScript<VariableData,
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableData> item, CancellationToken cancellationToken)
|
||||
private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetVariable(item);
|
||||
List<TopicJson> topicJsonList = GetVariableBasicData(item);
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ThingsGateway.Plugin.Kafka;
|
||||
/// <summary>
|
||||
/// Kafka消息生产
|
||||
/// </summary>
|
||||
public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<VariableData, DeviceData, AlarmVariable>
|
||||
public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private readonly KafkaProducerProperty _driverPropertys = new();
|
||||
private readonly KafkaProducerVariableProperty _variablePropertys = new();
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace ThingsGateway.Plugin.Kafka;
|
||||
/// <summary>
|
||||
/// Kafka消息生产
|
||||
/// </summary>
|
||||
public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<VariableData, DeviceData, AlarmVariable>
|
||||
public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private IProducer<Null, string> _producer;
|
||||
private ProducerBuilder<Null, string> _producerBuilder;
|
||||
@@ -52,17 +52,17 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
|
||||
|
||||
protected override ValueTask<OperResult> UpdateAlarmModel(IEnumerable<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateAlarmModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateAlarmModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateDevModel(IEnumerable<CacheDBItem<DeviceData>> item, CancellationToken cancellationToken)
|
||||
protected override ValueTask<OperResult> UpdateDevModel(IEnumerable<CacheDBItem<DeviceBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateDevModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateDevModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableData>> item, CancellationToken cancellationToken)
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateVarModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateVarModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override void VariableTimeInterval(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
@@ -107,15 +107,15 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
|
||||
return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceData> item, CancellationToken cancellationToken)
|
||||
private async ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetDeviceData(item);
|
||||
return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableData> item, CancellationToken cancellationToken)
|
||||
private async ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetVariable(item);
|
||||
List<TopicJson> topicJsonList = GetVariableBasicData(item);
|
||||
return await Update(topicJsonList, item.Count(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -127,9 +127,9 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScript<Variabl
|
||||
{
|
||||
//保留消息
|
||||
//分解List,避免超出字节大小限制
|
||||
var varData = VariableRuntimes.Select(a => a.Value).Adapt<List<VariableData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var varData = IdVariableRuntimes.Select(a => a.Value).Adapt<List<VariableBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
foreach (var item in varData)
|
||||
{
|
||||
if (!success)
|
||||
|
||||
@@ -109,12 +109,12 @@ public class ModbusSlave : BusinessBase
|
||||
{
|
||||
await base.AfterVariablesChangedAsync().ConfigureAwait(false);
|
||||
_modbusVariableQueue.Clear();
|
||||
VariableRuntimes.ForEach(a =>
|
||||
IdVariableRuntimes.ForEach(a =>
|
||||
{
|
||||
VariableValueChange(a.Value, null);
|
||||
});
|
||||
|
||||
ModbusVariables = VariableRuntimes.ToDictionary(a =>
|
||||
ModbusVariables = IdVariableRuntimes.ToDictionary(a =>
|
||||
{
|
||||
ModbusAddress address = ModbusAddress.ParseFrom(
|
||||
a.Value.GetPropertyValue(DeviceId,
|
||||
@@ -234,7 +234,7 @@ public class ModbusSlave : BusinessBase
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
private void VariableValueChange(VariableRuntime variableRuntime, VariableData variableData)
|
||||
private void VariableValueChange(VariableRuntime variableRuntime, VariableBasicData variableData)
|
||||
{
|
||||
if (CurrentDevice.Pause == true)
|
||||
return;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace ThingsGateway.Plugin.Mqtt;
|
||||
/// <summary>
|
||||
/// MqttClient,RPC方法适配mqttNet
|
||||
/// </summary>
|
||||
public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableData, DeviceBasicData, AlarmVariable>
|
||||
public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private readonly MqttClientProperty _driverPropertys = new();
|
||||
private readonly MqttClientVariableProperty _variablePropertys = new();
|
||||
|
||||
@@ -8,10 +8,15 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using BootstrapBlazor.Components;
|
||||
|
||||
using CSScripting;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using MQTTnet;
|
||||
|
||||
|
||||
#if NET6_0
|
||||
using MQTTnet.Client;
|
||||
#endif
|
||||
@@ -21,7 +26,6 @@ using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
using ThingsGateway.NewLife;
|
||||
@@ -33,7 +37,7 @@ namespace ThingsGateway.Plugin.Mqtt;
|
||||
/// <summary>
|
||||
/// MqttClient
|
||||
/// </summary>
|
||||
public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableData, DeviceBasicData, AlarmVariable>
|
||||
public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private static readonly CompositeFormat RpcTopic = CompositeFormat.Parse("{0}/+");
|
||||
public const string ThingsBoardRpcTopic = "v1/gateway/rpc";
|
||||
@@ -120,19 +124,19 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
|
||||
protected override ValueTask<OperResult> UpdateAlarmModel(IEnumerable<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateAlarmModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateAlarmModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateDevModel(IEnumerable<CacheDBItem<DeviceBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
|
||||
return UpdateDevModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateDevModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableData>> item, CancellationToken cancellationToken)
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateVarModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateVarModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
protected override void VariableTimeInterval(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
{
|
||||
@@ -187,9 +191,9 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableData> item, CancellationToken cancellationToken)
|
||||
private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetVariable(item);
|
||||
List<TopicJson> topicJsonList = GetVariableBasicData(item);
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
@@ -199,9 +203,9 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
{
|
||||
//保留消息
|
||||
//分解List,避免超出mqtt字节大小限制
|
||||
var varData = VariableRuntimes.Select(a => a.Value).Adapt<List<VariableData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var varData = IdVariableRuntimes.Select(a => a.Value).Adapt<List<VariableBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
foreach (var item in varData)
|
||||
{
|
||||
if (!success)
|
||||
@@ -226,33 +230,62 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
}
|
||||
}
|
||||
|
||||
private async ValueTask<Dictionary<string, OperResult>> GetResult(MqttApplicationMessageReceivedEventArgs args, Dictionary<string, JToken> rpcDatas)
|
||||
private async ValueTask<Dictionary<string, Dictionary<string, OperResult>>> GetResult(MqttApplicationMessageReceivedEventArgs args, Dictionary<string, Dictionary<string, JToken>> rpcDatas)
|
||||
{
|
||||
var mqttRpcResult = new Dictionary<string, OperResult>();
|
||||
var mqttRpcResult = new Dictionary<string, Dictionary<string, OperResult>>();
|
||||
rpcDatas.ForEach(a => mqttRpcResult.Add(a.Key, new()));
|
||||
try
|
||||
{
|
||||
foreach (var rpcData in rpcDatas)
|
||||
{
|
||||
VariableRuntimes.TryGetValue(rpcData.Key, out var tag);
|
||||
if (tag != null)
|
||||
if (GlobalData.ReadOnlyDevices.TryGetValue(rpcData.Key, out var device))
|
||||
{
|
||||
var rpcEnable = tag.GetPropertyValue(DeviceId, nameof(_variablePropertys.VariableRpcEnable))?.ToBoolean();
|
||||
if (rpcEnable == false)
|
||||
foreach (var item in rpcData.Value)
|
||||
{
|
||||
mqttRpcResult.Add(rpcData.Key, new OperResult("RPCEnable is False"));
|
||||
|
||||
if (device.ReadOnlyVariableRuntimes.TryGetValue(item.Key, out var variable) && IdVariableRuntimes.TryGetValue(variable.Id, out var tag))
|
||||
{
|
||||
var rpcEnable = tag.GetPropertyValue(DeviceId, nameof(_variablePropertys.VariableRpcEnable))?.ToBoolean();
|
||||
if (rpcEnable == false)
|
||||
{
|
||||
mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("RPCEnable is False"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("The variable does not exist"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mqttRpcResult.Add(rpcData.Key, new OperResult("The variable does not exist"));
|
||||
}
|
||||
}
|
||||
|
||||
var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(ToString() + "-" + args.ClientId,
|
||||
rpcDatas.Where(
|
||||
a => !mqttRpcResult.Any(b => b.Key == a.Key)).ToDictionary(a => a.Key, a => a.Value.ToString())).ConfigureAwait(false);
|
||||
Dictionary<string, Dictionary<string, string>> writeData = new();
|
||||
foreach (var item in rpcDatas)
|
||||
{
|
||||
writeData.Add(item.Key, new());
|
||||
|
||||
mqttRpcResult.AddRange(result);
|
||||
foreach (var kv in item.Value)
|
||||
{
|
||||
|
||||
if (!mqttRpcResult[item.Key].ContainsKey(kv.Key))
|
||||
{
|
||||
writeData[item.Key].Add(kv.Key, kv.Value?.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(ToString() + "-" + args.ClientId,
|
||||
writeData).ConfigureAwait(false);
|
||||
|
||||
foreach (var dictKv in result)
|
||||
{
|
||||
foreach (var item in dictKv.Value)
|
||||
{
|
||||
mqttRpcResult[dictKv.Key].TryAdd(item.Key, item.Value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -285,7 +318,7 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
if (!_driverPropertys.DeviceRpcEnable)
|
||||
return;
|
||||
|
||||
Dictionary<string, JToken> rpcDatas = null;
|
||||
Dictionary<string, Dictionary<string, JToken>> rpcDatas = new();
|
||||
|
||||
//适配 ThingsBoardRp
|
||||
if (args.ApplicationMessage.Topic == ThingsBoardRpcTopic)
|
||||
@@ -293,11 +326,12 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
var thingsBoardRpcData = Encoding.UTF8.GetString(payload).FromJsonNetString<ThingsBoardRpcData>();
|
||||
if (thingsBoardRpcData == null)
|
||||
return;
|
||||
rpcDatas = thingsBoardRpcData.data.@params.ToDictionary(a => a.Key, a => JToken.Parse(a.Value));
|
||||
rpcDatas.Add(thingsBoardRpcData.device, thingsBoardRpcData.data.@params.ToDictionary(a => a.Key, a => JToken.Parse(a.Value)));
|
||||
|
||||
if (rpcDatas == null)
|
||||
return;
|
||||
|
||||
Dictionary<string, OperResult> mqttRpcResult = await GetResult(args, rpcDatas).ConfigureAwait(false);
|
||||
var mqttRpcResult = await GetResult(args, rpcDatas).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var isConnect = await TryMqttClientAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
@@ -306,8 +340,8 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
ThingsBoardRpcResponseData thingsBoardRpcResponseData = new();
|
||||
thingsBoardRpcResponseData.device = thingsBoardRpcData.device;
|
||||
thingsBoardRpcResponseData.id = thingsBoardRpcData.data.id;
|
||||
thingsBoardRpcResponseData.data.success = mqttRpcResult.All(a => a.Value.IsSuccess);
|
||||
thingsBoardRpcResponseData.data.message = mqttRpcResult.Select(a => a.Value.ErrorMessage).ToJsonNetString();
|
||||
thingsBoardRpcResponseData.data.success = mqttRpcResult[thingsBoardRpcResponseData.device].All(b => b.Value.IsSuccess);
|
||||
thingsBoardRpcResponseData.data.message = mqttRpcResult[thingsBoardRpcResponseData.device].Select(a => a.Value.ErrorMessage).ToJsonNetString();
|
||||
|
||||
var variableMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic($"{args.ApplicationMessage.Topic}")
|
||||
@@ -326,11 +360,11 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
var t = string.Format(null, RpcTopic, _driverPropertys.RpcWriteTopic);
|
||||
if (MqttTopicFilterComparer.Compare(args.ApplicationMessage.Topic, t) != MqttTopicFilterCompareResult.IsMatch)
|
||||
return;
|
||||
rpcDatas = Encoding.UTF8.GetString(payload).FromJsonNetString<Dictionary<string, JToken>>();
|
||||
rpcDatas = Encoding.UTF8.GetString(payload).FromJsonNetString<Dictionary<string, Dictionary<string, JToken>>>();
|
||||
if (rpcDatas == null)
|
||||
return;
|
||||
|
||||
Dictionary<string, OperResult> mqttRpcResult = await GetResult(args, rpcDatas).ConfigureAwait(false);
|
||||
var mqttRpcResult = await GetResult(args, rpcDatas).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var isConnect = await TryMqttClientAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace ThingsGateway.Plugin.Mqtt;
|
||||
/// <summary>
|
||||
/// MqttServer,RPC方法适配mqttNet
|
||||
/// </summary>
|
||||
public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableData, DeviceData, AlarmVariable>
|
||||
public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private readonly MqttServerProperty _driverPropertys = new();
|
||||
private readonly MqttClientVariableProperty _variablePropertys = new();
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using CSScripting;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
@@ -23,7 +25,6 @@ using Newtonsoft.Json.Linq;
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Extension.Generic;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Foundation.Extension.Generic;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
@@ -34,7 +35,7 @@ namespace ThingsGateway.Plugin.Mqtt;
|
||||
/// <summary>
|
||||
/// MqttServer
|
||||
/// </summary>
|
||||
public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableData, DeviceData, AlarmVariable>
|
||||
public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private static readonly CompositeFormat RpcTopic = CompositeFormat.Parse("{0}/+");
|
||||
private MQTTnet.Server.MqttServer _mqttServer;
|
||||
@@ -63,17 +64,17 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
|
||||
protected override ValueTask<OperResult> UpdateAlarmModel(IEnumerable<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateAlarmModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateAlarmModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateDevModel(IEnumerable<CacheDBItem<DeviceData>> item, CancellationToken cancellationToken)
|
||||
protected override ValueTask<OperResult> UpdateDevModel(IEnumerable<CacheDBItem<DeviceBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateDevModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateDevModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableData>> item, CancellationToken cancellationToken)
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateVarModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateVarModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
protected override void VariableTimeInterval(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
{
|
||||
@@ -118,47 +119,76 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceData> item, CancellationToken cancellationToken)
|
||||
private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetDeviceData(item);
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableData> item, CancellationToken cancellationToken)
|
||||
private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetVariable(item);
|
||||
List<TopicJson> topicJsonList = GetVariableBasicData(item);
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
#endregion private
|
||||
|
||||
private async ValueTask<Dictionary<string, OperResult>> GetResult(InterceptingPublishEventArgs args, Dictionary<string, JToken> rpcDatas)
|
||||
private async ValueTask<Dictionary<string, Dictionary<string, OperResult>>> GetResult(InterceptingPublishEventArgs args, Dictionary<string, Dictionary<string, JToken>> rpcDatas)
|
||||
{
|
||||
var mqttRpcResult = new Dictionary<string, OperResult>();
|
||||
var mqttRpcResult = new Dictionary<string, Dictionary<string, OperResult>>();
|
||||
rpcDatas.ForEach(a => mqttRpcResult.Add(a.Key, new()));
|
||||
try
|
||||
{
|
||||
foreach (var rpcData in rpcDatas)
|
||||
{
|
||||
VariableRuntimes.TryGetValue(rpcData.Key, out var tag);
|
||||
if (tag != null)
|
||||
if (GlobalData.ReadOnlyDevices.TryGetValue(rpcData.Key, out var device))
|
||||
{
|
||||
var rpcEnable = tag.GetPropertyValue(DeviceId, nameof(_variablePropertys.VariableRpcEnable))?.ToBoolean();
|
||||
if (rpcEnable == false)
|
||||
foreach (var item in rpcData.Value)
|
||||
{
|
||||
mqttRpcResult.Add(rpcData.Key, new OperResult("RPCEnable is False"));
|
||||
|
||||
if (device.ReadOnlyVariableRuntimes.TryGetValue(item.Key, out var variable) && IdVariableRuntimes.TryGetValue(variable.Id, out var tag))
|
||||
{
|
||||
var rpcEnable = tag.GetPropertyValue(DeviceId, nameof(_variablePropertys.VariableRpcEnable))?.ToBoolean();
|
||||
if (rpcEnable == false)
|
||||
{
|
||||
mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("RPCEnable is False"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mqttRpcResult[rpcData.Key].Add(item.Key, new OperResult("The variable does not exist"));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
Dictionary<string, Dictionary<string, string>> writeData = new();
|
||||
foreach (var item in rpcDatas)
|
||||
{
|
||||
writeData.Add(item.Key, new());
|
||||
|
||||
foreach (var kv in item.Value)
|
||||
{
|
||||
mqttRpcResult.Add(rpcData.Key, new OperResult("The variable does not exist"));
|
||||
|
||||
if (!mqttRpcResult[item.Key].ContainsKey(kv.Key))
|
||||
{
|
||||
writeData[item.Key].Add(kv.Key, kv.Value?.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var result = await GlobalData.RpcService.InvokeDeviceMethodAsync(ToString() + "-" + args.ClientId,
|
||||
rpcDatas.Where(
|
||||
a => !mqttRpcResult.Any(b => b.Key == a.Key)).ToDictionary(a => a.Key, a => a.Value.ToString())).ConfigureAwait(false);
|
||||
writeData).ConfigureAwait(false);
|
||||
|
||||
mqttRpcResult.AddRange(result);
|
||||
|
||||
foreach (var dictKv in result)
|
||||
{
|
||||
foreach (var item in dictKv.Value)
|
||||
{
|
||||
mqttRpcResult[dictKv.Key].TryAdd(item.Key, item.Value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -172,16 +202,16 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
{
|
||||
//首次连接时的保留消息
|
||||
//分解List,避免超出mqtt字节大小限制
|
||||
var varData = VariableRuntimes.Select(a => a.Value).Adapt<List<VariableData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var varData = IdVariableRuntimes.Select(a => a.Value).Adapt<List<VariableBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
List<MqttApplicationMessage> Messages = new();
|
||||
|
||||
if (!_businessPropertyWithCacheIntervalScript.VariableTopic.IsNullOrEmpty())
|
||||
{
|
||||
foreach (var item in varData)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetVariable(item);
|
||||
List<TopicJson> topicJsonList = GetVariableBasicData(item);
|
||||
foreach (var topicJson in topicJsonList)
|
||||
{
|
||||
Messages.Add(new MqttApplicationMessageBuilder()
|
||||
@@ -246,10 +276,10 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
var t = string.Format(null, RpcTopic, _driverPropertys.RpcWriteTopic);
|
||||
if (MqttTopicFilterComparer.Compare(args.ApplicationMessage.Topic, t) != MqttTopicFilterCompareResult.IsMatch)
|
||||
return;
|
||||
var rpcDatas = Encoding.UTF8.GetString(payload).FromJsonNetString<Dictionary<string, JToken>>();
|
||||
var rpcDatas = Encoding.UTF8.GetString(payload).FromJsonNetString<Dictionary<string, Dictionary<string, JToken>>>();
|
||||
if (rpcDatas == null)
|
||||
return;
|
||||
Dictionary<string, OperResult> mqttRpcResult = await GetResult(args, rpcDatas).ConfigureAwait(false);
|
||||
var mqttRpcResult = await GetResult(args, rpcDatas).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -273,7 +303,7 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScript<VariableDa
|
||||
|
||||
private async Task MqttServer_ValidatingConnectionAsync(ValidatingConnectionEventArgs arg)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_driverPropertys.StartWithId) || !arg.ClientId.StartsWith(_driverPropertys.StartWithId))
|
||||
if (!string.IsNullOrEmpty(_driverPropertys.StartWithId) && !arg.ClientId.StartsWith(_driverPropertys.StartWithId))
|
||||
{
|
||||
arg.ReasonCode = MqttConnectReasonCode.ClientIdentifierNotValid;
|
||||
return;
|
||||
|
||||
@@ -49,7 +49,7 @@ public class MqttServerProperty : BusinessPropertyWithCacheIntervalScript
|
||||
/// 允许匿名登录
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool AnonymousEnable { get; set; } = false;
|
||||
public bool AnonymousEnable { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 允许Rpc写入
|
||||
|
||||
@@ -125,11 +125,8 @@ public class OpcDaMaster : CollectBase
|
||||
protected override async Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
|
||||
{
|
||||
await Task.CompletedTask.ConfigureAwait(false);
|
||||
_plc?.Disconnect();
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
if (deviceVariables.Count > 0)
|
||||
{
|
||||
var result = _plc.AddItemsWithSave(deviceVariables.Where(a => !string.IsNullOrEmpty(a.RegisterAddress)).Select(a => a.RegisterAddress!).ToList());
|
||||
@@ -157,7 +154,6 @@ public class OpcDaMaster : CollectBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
_plc?.Connect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,9 +200,12 @@ public class OpcDaMaster : CollectBase
|
||||
}
|
||||
public override async Task AfterVariablesChangedAsync()
|
||||
{
|
||||
_plc?.Disconnect();
|
||||
await base.AfterVariablesChangedAsync().ConfigureAwait(false);
|
||||
|
||||
VariableAddresDicts = VariableRuntimes.Select(a => a.Value).Where(it => !it.RegisterAddress.IsNullOrEmpty()).GroupBy(a => a.RegisterAddress).ToDictionary(a => a.Key!, b => b.ToList());
|
||||
VariableAddresDicts = IdVariableRuntimes.Select(a => a.Value).Where(it => !it.RegisterAddress.IsNullOrEmpty()).GroupBy(a => a.RegisterAddress).ToDictionary(a => a.Key!, b => b.ToList());
|
||||
|
||||
_plc?.Connect();
|
||||
}
|
||||
|
||||
private Dictionary<string, List<VariableRuntime>> VariableAddresDicts { get; set; } = new();
|
||||
|
||||
@@ -274,7 +274,7 @@ public partial class OpcDaImportVariable
|
||||
var id = Admin.Application.CommonUtils.GetSingleId();
|
||||
return new Variable()
|
||||
{
|
||||
Name = a.Name + "-" + id,
|
||||
Name = a.Name,
|
||||
RegisterAddress = a.ItemName,
|
||||
DeviceId = device.Id,
|
||||
Enable = true,
|
||||
|
||||
@@ -155,7 +155,7 @@ public class OpcUaMaster : CollectBase
|
||||
{
|
||||
await _plc.AddSubscriptionAsync(variableSourceRead.RegisterAddress, variableSourceRead.VariableRuntimes.Where(a => !a.RegisterAddress.IsNullOrEmpty()).Select(a => a.RegisterAddress!).ToHashSet().ToArray(), _plc.OpcUaProperty.LoadType, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
LogMessage?.LogInformation($"AddSubscription {CurrentDevice.VariableSourceReads.IndexOf(variableSourceRead)} done");
|
||||
LogMessage?.LogInformation($"AddSubscription index {CurrentDevice.VariableSourceReads.IndexOf(variableSourceRead)} done");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -309,7 +309,7 @@ public class OpcUaMaster : CollectBase
|
||||
}
|
||||
finally
|
||||
{
|
||||
VariableAddresDicts = VariableRuntimes.Select(a => a.Value).Where(it => !it.RegisterAddress.IsNullOrEmpty()).GroupBy(a => a.RegisterAddress).ToDictionary(a => a.Key!, b => b.ToList());
|
||||
VariableAddresDicts = IdVariableRuntimes.Select(a => a.Value).Where(it => !it.RegisterAddress.IsNullOrEmpty()).GroupBy(a => a.RegisterAddress).ToDictionary(a => a.Key!, b => b.ToList());
|
||||
await _plc.ConnectAsync(default).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
AddRootNotifier(rootFolder);
|
||||
|
||||
//创建设备树
|
||||
var _geviceGroup = _businessBase.VariableRuntimes.Select(a => a.Value)
|
||||
var _geviceGroup = _businessBase.IdVariableRuntimes.Select(a => a.Value)
|
||||
.GroupBy(a => a.DeviceName);
|
||||
// 开始寻找设备信息,并计算一些节点信息
|
||||
foreach (var item in _geviceGroup)
|
||||
@@ -124,7 +124,7 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
var historyRead = nodesToRead[i];
|
||||
if (NodeIdTags.TryGetValue(historyRead.NodeId.Identifier.ToString(), out OpcUaTag tag))
|
||||
{
|
||||
if (!GlobalData.ReadOnlyVariables.TryGetValue(tag.SymbolicName, out var variableRuntime))
|
||||
if (!GlobalData.ReadOnlyIdVariables.TryGetValue(tag.Id, out var variableRuntime))
|
||||
{
|
||||
results[i] = new HistoryReadResult()
|
||||
{
|
||||
@@ -186,28 +186,13 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override NodeId New(ISystemContext context, NodeState node)
|
||||
{
|
||||
if (node is BaseInstanceState instance && instance.Parent != null)
|
||||
{
|
||||
string id = instance.Parent.NodeId.Identifier?.ToString();
|
||||
if (id != null)
|
||||
{
|
||||
//用下划线分割
|
||||
return new NodeId(id + "_" + instance.SymbolicName, instance.Parent.NodeId.NamespaceIndex);
|
||||
}
|
||||
}
|
||||
return node.NodeId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新变量
|
||||
/// </summary>
|
||||
/// <param name="variable"></param>
|
||||
public void UpVariable(VariableData variable)
|
||||
public void UpVariable(VariableBasicData variable)
|
||||
{
|
||||
if (!NodeIdTags.TryGetValue(variable.Name, out var uaTag))
|
||||
if (!NodeIdTags.TryGetValue($"{variable.DeviceName}.{variable.Name}", out var uaTag))
|
||||
return;
|
||||
object initialItemValue = null;
|
||||
initialItemValue = variable.Value;
|
||||
@@ -389,7 +374,7 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
SymbolicName = variableRuntime.Name,
|
||||
ReferenceTypeId = ReferenceTypes.Organizes,
|
||||
TypeDefinitionId = VariableTypeIds.BaseDataVariableType,
|
||||
NodeId = new NodeId(variableRuntime.Name, NamespaceIndex),
|
||||
NodeId = new NodeId($"{variableRuntime.DeviceName}.{variableRuntime.Name}", NamespaceIndex),
|
||||
Description = variableRuntime.Description,
|
||||
BrowseName = new QualifiedName(variableRuntime.Name, NamespaceIndex),
|
||||
DisplayName = new LocalizedText(variableRuntime.Name),
|
||||
@@ -411,7 +396,7 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
variable.Timestamp = variableRuntime.CollectTime ?? DateTime.MinValue;
|
||||
variable.OnWriteValue = OnWriteDataValue;
|
||||
parent?.AddChild(variable);
|
||||
NodeIdTags.AddOrUpdate(variable.SymbolicName, variable);
|
||||
NodeIdTags.AddOrUpdate($"{variableRuntime.DeviceName}.{variableRuntime.Name}", variable);
|
||||
return variable;
|
||||
}
|
||||
|
||||
@@ -447,28 +432,29 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
//{
|
||||
// return StatusCodes.BadUserAccessDenied;
|
||||
//}
|
||||
OpcUaTag variable = node as OpcUaTag;
|
||||
if (NodeIdTags.TryGetValue(variable.SymbolicName, out OpcUaTag tag))
|
||||
OpcUaTag opcuaTag = node as OpcUaTag;
|
||||
if (NodeIdTags.TryGetValue(opcuaTag.NodeId.Identifier.ToString(), out OpcUaTag tag) && GlobalData.ReadOnlyIdVariables.TryGetValue(tag.Id, out var variableRuntime))
|
||||
{
|
||||
if (StatusCode.IsGood(variable.StatusCode))
|
||||
if (StatusCode.IsGood(opcuaTag.StatusCode))
|
||||
{
|
||||
//仅当指定了值时才将值写入
|
||||
if (variable.Value != null)
|
||||
if (opcuaTag.Value != null)
|
||||
{
|
||||
var result = GlobalData.RpcService.InvokeDeviceMethodAsync("OpcUaSlave - " + context1?.OperationContext?.Session?.Identity?.DisplayName,
|
||||
var result = GlobalData.RpcService.InvokeDeviceMethodAsync("OpcUaServer - " + context1?.OperationContext?.Session?.Identity?.DisplayName,
|
||||
new()
|
||||
{
|
||||
{ variable.SymbolicName, value?.ToString() }
|
||||
{
|
||||
variableRuntime.DeviceName, new Dictionary<string, string>() { {opcuaTag.SymbolicName, value?.ToString() } }
|
||||
}
|
||||
}
|
||||
|
||||
).ConfigureAwait(true).GetAwaiter().GetResult();
|
||||
if (result.Values.FirstOrDefault().IsSuccess)
|
||||
if (result.Values.FirstOrDefault()?.FirstOrDefault().Value.IsSuccess == true)
|
||||
{
|
||||
return StatusCodes.Good;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new(StatusCodes.BadWaitingForResponse, result.Values.FirstOrDefault().ErrorMessage);
|
||||
return new(StatusCodes.BadWaitingForResponse, result.Values.FirstOrDefault()?.FirstOrDefault().Value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
protected override BusinessPropertyBase _businessPropertyBase => _driverPropertys;
|
||||
|
||||
protected IStringLocalizer Localizer { get; private set; }
|
||||
private ConcurrentQueue<VariableData> CollectVariableRuntimes { get; set; } = new();
|
||||
private ConcurrentQueue<VariableBasicData> CollectVariableRuntimes { get; set; } = new();
|
||||
|
||||
private static readonly string[] separator = new string[] { ";" };
|
||||
|
||||
@@ -59,7 +59,6 @@ public partial class OpcUaServer : BusinessBase
|
||||
|
||||
ApplicationInstance.MessageDlg = new ApplicationMessageDlg(LogMessage);//默认返回true
|
||||
|
||||
CollectVariableRuntimes?.Clear();
|
||||
//Utils.SetLogger(new OpcUaLogger(LogMessage)); //调试用途
|
||||
m_application = new ApplicationInstance();
|
||||
m_configuration = GetDefaultConfiguration();
|
||||
@@ -83,7 +82,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
if (_driverPropertys.IsAllVariable)
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
VariableRuntimes = new(GlobalData.GetEnableVariables());
|
||||
IdVariableRuntimes = new(GlobalData.GetEnableVariables());
|
||||
|
||||
CollectDevices = GlobalData.GetEnableDevices().Where(a => a.Value.IsCollect == true).ToDictionary();
|
||||
}
|
||||
@@ -91,9 +90,12 @@ public partial class OpcUaServer : BusinessBase
|
||||
{
|
||||
await base.AfterVariablesChangedAsync().ConfigureAwait(false);
|
||||
}
|
||||
VariableRuntimes.ForEach(a =>
|
||||
|
||||
CollectVariableRuntimes.Clear();
|
||||
|
||||
IdVariableRuntimes.ForEach(a =>
|
||||
{
|
||||
VariableValueChange(a.Value, a.Value.Adapt<VariableData>());
|
||||
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
|
||||
});
|
||||
|
||||
|
||||
@@ -104,7 +106,6 @@ public partial class OpcUaServer : BusinessBase
|
||||
{
|
||||
ApplicationInstance.MessageDlg = new ApplicationMessageDlg(LogMessage);//默认返回true
|
||||
|
||||
CollectVariableRuntimes?.Clear();
|
||||
//Utils.SetLogger(new OpcUaLogger(LogMessage)); //调试用途
|
||||
m_application = new ApplicationInstance();
|
||||
m_configuration = GetDefaultConfiguration();
|
||||
@@ -119,7 +120,6 @@ public partial class OpcUaServer : BusinessBase
|
||||
}
|
||||
|
||||
m_server = new(this);
|
||||
CollectVariableRuntimes.Clear();
|
||||
|
||||
|
||||
GlobalData.VariableValueChangeEvent += VariableValueChange;
|
||||
@@ -151,7 +151,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
m_application?.Stop();
|
||||
m_server?.SafeDispose();
|
||||
CollectVariableRuntimes?.Clear();
|
||||
VariableRuntimes?.Clear();
|
||||
IdVariableRuntimes?.Clear();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@@ -181,9 +181,9 @@ public partial class OpcUaServer : BusinessBase
|
||||
await m_application.CheckApplicationInstanceCertificates(true, 1200, cancellationToken).ConfigureAwait(false);
|
||||
await m_application.Start(m_server).ConfigureAwait(false);
|
||||
success = true;
|
||||
VariableRuntimes.ForEach(a =>
|
||||
IdVariableRuntimes.ForEach(a =>
|
||||
{
|
||||
VariableValueChange(a.Value, a.Value.Adapt<VariableData>());
|
||||
VariableValueChange(a.Value, a.Value.Adapt<VariableBasicData>());
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -195,32 +195,35 @@ public partial class OpcUaServer : BusinessBase
|
||||
}
|
||||
}
|
||||
var data = CollectVariableRuntimes.ToListWithDequeue();
|
||||
data.Reverse();
|
||||
////变化推送
|
||||
var varList = data.DistinctBy(a => a.Name).ToList();
|
||||
|
||||
if (varList?.Count > 0)
|
||||
if (data.Count > 0)
|
||||
{
|
||||
foreach (var item in varList)
|
||||
data.Reverse();
|
||||
////变化推送
|
||||
var varList = data.DistinctBy(a => a.Id).ToList();
|
||||
|
||||
if (varList?.Count > 0)
|
||||
{
|
||||
try
|
||||
foreach (var item in varList)
|
||||
{
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
try
|
||||
{
|
||||
m_server?.NodeManager?.UpVariable(item);
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
m_server?.NodeManager?.UpVariable(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
break;
|
||||
LogMessage.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -419,12 +422,16 @@ public partial class OpcUaServer : BusinessBase
|
||||
return config;
|
||||
}
|
||||
|
||||
private void VariableValueChange(VariableRuntime variableRuntime, VariableData variableData)
|
||||
private void VariableValueChange(VariableRuntime variableRuntime, VariableBasicData variableData)
|
||||
{
|
||||
if (CurrentDevice.Pause)
|
||||
return;
|
||||
if (DisposedValue) return;
|
||||
if (VariableRuntimes.ContainsKey(variableData.Name))
|
||||
if (IdVariableRuntimes.ContainsKey(variableData.Id))
|
||||
CollectVariableRuntimes.Enqueue(variableData);
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ public partial class OpcUaImportVariable
|
||||
|
||||
variables.Add(new Variable()
|
||||
{
|
||||
Name = a.DisplayName.Text + "-" + id,
|
||||
Name = a.NodeId.ToString().Replace('.', '/'),
|
||||
RegisterAddress = a.NodeId.ToString(),
|
||||
DeviceId = device.Id,
|
||||
DataType = dataTypeEnum,
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace ThingsGateway.Plugin.RabbitMQ;
|
||||
/// <summary>
|
||||
/// RabbitMQProducer
|
||||
/// </summary>
|
||||
public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<VariableData, DeviceData, AlarmVariable>
|
||||
public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private readonly RabbitMQProducerProperty _driverPropertys = new();
|
||||
private readonly RabbitMQProducerVariableProperty _variablePropertys = new();
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace ThingsGateway.Plugin.RabbitMQ;
|
||||
/// <summary>
|
||||
/// RabbitMQProducer
|
||||
/// </summary>
|
||||
public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<VariableData, DeviceData, AlarmVariable>
|
||||
public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<VariableBasicData, DeviceBasicData, AlarmVariable>
|
||||
{
|
||||
private IConnection _connection;
|
||||
private ConnectionFactory _connectionFactory;
|
||||
@@ -55,17 +55,17 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
|
||||
|
||||
protected override ValueTask<OperResult> UpdateAlarmModel(IEnumerable<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateAlarmModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateAlarmModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateDevModel(IEnumerable<CacheDBItem<DeviceData>> item, CancellationToken cancellationToken)
|
||||
protected override ValueTask<OperResult> UpdateDevModel(IEnumerable<CacheDBItem<DeviceBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateDevModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateDevModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableData>> item, CancellationToken cancellationToken)
|
||||
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<VariableBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateVarModel(item.Select(a => a.Value), cancellationToken);
|
||||
return UpdateVarModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
protected override void VariableTimeInterval(VariableRuntime variableRuntime, VariableBasicData variable)
|
||||
{
|
||||
@@ -110,15 +110,15 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceData> item, CancellationToken cancellationToken)
|
||||
private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetDeviceData(item);
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableData> item, CancellationToken cancellationToken)
|
||||
private ValueTask<OperResult> UpdateVarModel(IEnumerable<VariableBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
List<TopicJson> topicJsonList = GetVariable(item);
|
||||
List<TopicJson> topicJsonList = GetVariableBasicData(item);
|
||||
return Update(topicJsonList, item.Count(), cancellationToken);
|
||||
}
|
||||
|
||||
@@ -130,9 +130,9 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScript<Vari
|
||||
{
|
||||
//保留消息
|
||||
//分解List,避免超出字节大小限制
|
||||
var varData = VariableRuntimes.Select(a => a.Value).Adapt<List<VariableData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var varData = IdVariableRuntimes.Select(a => a.Value).Adapt<List<VariableBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var devData = CollectDevices?.Select(a => a.Value).Adapt<List<DeviceBasicData>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
var alramData = GlobalData.ReadOnlyRealAlarmIdVariables.Select(a => a.Value).Adapt<List<AlarmVariable>>().ChunkBetter(_driverPropertys.SplitSize);
|
||||
foreach (var item in varData)
|
||||
{
|
||||
if (!success)
|
||||
|
||||
@@ -199,7 +199,7 @@ public partial class GatewayIndex : IDisposable
|
||||
{
|
||||
return new TimelineItem()
|
||||
{
|
||||
Content = $"{a.OperateObject} [Source] {a.OperateSource} ",
|
||||
Content = $"{a.OperateDevice} : {a.OperateObject} [Source] {a.OperateSource} ",
|
||||
|
||||
Description = a.LogTime.ToDefaultDateTimeFormat()
|
||||
};
|
||||
|
||||
@@ -95,12 +95,12 @@ public partial class GatewayIndexComponent : IDisposable
|
||||
AlarmChartDataSource.Labels = data.Select(a => Localizer["AlarmCount"].Value);
|
||||
AlarmChartDataSource.Data.Add(new ChartDataset()
|
||||
{
|
||||
Data = new List<object>() { GlobalData.ReadOnlyRealAlarmVariables.Count }
|
||||
Data = new List<object>() { GlobalData.ReadOnlyRealAlarmIdVariables.Count }
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
AlarmChartDataSource.Data[0].Data = new List<object>() { GlobalData.ReadOnlyRealAlarmVariables.Count };
|
||||
AlarmChartDataSource.Data[0].Data = new List<object>() { GlobalData.ReadOnlyRealAlarmIdVariables.Count };
|
||||
}
|
||||
return Task.FromResult(AlarmChartDataSource!);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
|
||||
{
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"dotnetRunMessages": true,
|
||||
"applicationUrl": "http://localhost:5000"
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:59494/",
|
||||
"sslPort": 44372
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>10.0.2.18</Version>
|
||||
<Version>10.1.0.1</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user