mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 10:50:48 +08:00
!70 fix: questdb restapi多实例
This commit is contained in:
@@ -123,6 +123,15 @@ public static class QueryPageOptionsExtensions
|
||||
};
|
||||
var items = datas.GetData(option, out var totalCount, where);
|
||||
ret.TotalCount = totalCount;
|
||||
|
||||
if (totalCount > 0)
|
||||
{
|
||||
if (!items.Any() && option.PageIndex != 1)
|
||||
{
|
||||
option.PageIndex = 1;
|
||||
items = datas.GetData(option, out totalCount, where);
|
||||
}
|
||||
}
|
||||
ret.Items = items.ToList();
|
||||
return ret;
|
||||
}
|
||||
|
@@ -108,7 +108,7 @@ namespace ThingsGateway.SqlSugar
|
||||
return result;
|
||||
}
|
||||
|
||||
private static readonly HttpClient client = new HttpClient();
|
||||
private readonly HttpClient client = new HttpClient();
|
||||
|
||||
/// <summary>
|
||||
/// 异步批量快速插入数据
|
||||
|
@@ -17,8 +17,10 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 后台日志表
|
||||
///</summary>
|
||||
#if !Management
|
||||
[SugarTable("backend_log", TableDescription = "后台日志表")]
|
||||
[Tenant(SqlSugarConst.DB_Log)]
|
||||
#endif
|
||||
public class BackendLog : PrimaryIdEntity
|
||||
{
|
||||
/// <summary>
|
||||
|
@@ -22,9 +22,11 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 通道表
|
||||
/// </summary>
|
||||
#if !Management
|
||||
[SugarTable("channel", TableDescription = "通道表")]
|
||||
[Tenant(SqlSugarConst.DB_Custom)]
|
||||
[SugarIndex("unique_channel_name", nameof(Channel.Name), OrderByType.Asc, true)]
|
||||
#endif
|
||||
public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IBaseEntity
|
||||
{
|
||||
/// <summary>
|
||||
|
@@ -22,9 +22,11 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 设备表
|
||||
/// </summary>
|
||||
#if !Management
|
||||
[SugarTable("device", TableDescription = "设备表")]
|
||||
[Tenant(SqlSugarConst.DB_Custom)]
|
||||
[SugarIndex("unique_device_name", nameof(Device.Name), OrderByType.Asc, true)]
|
||||
#endif
|
||||
public class Device : BaseDataEntity, IValidatableObject
|
||||
{
|
||||
public override string ToString()
|
||||
|
@@ -15,8 +15,10 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// Rpc写入日志
|
||||
///</summary>
|
||||
#if !Management
|
||||
[SugarTable("rpc_log", TableDescription = "RPC操作日志")]
|
||||
[Tenant(SqlSugarConst.DB_Log)]
|
||||
#endif
|
||||
public class RpcLog : PrimaryIdEntity
|
||||
{
|
||||
/// <summary>
|
||||
|
@@ -21,10 +21,12 @@ namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// 设备变量表
|
||||
/// </summary>
|
||||
#if !Management
|
||||
[SugarTable("variable", TableDescription = "设备变量表")]
|
||||
[Tenant(SqlSugarConst.DB_Custom)]
|
||||
[SugarIndex("index_device", nameof(Variable.DeviceId), OrderByType.Asc)]
|
||||
[SugarIndex("unique_deviceid_variable_name", nameof(Variable.Name), OrderByType.Asc, nameof(Variable.DeviceId), OrderByType.Asc, true)]
|
||||
#endif
|
||||
public class Variable : BaseDataEntity, IValidatableObject
|
||||
{
|
||||
/// <summary>
|
||||
@@ -47,7 +49,8 @@ public class Variable : BaseDataEntity, IValidatableObject
|
||||
private double lLAlarmCode = 0;
|
||||
private long deviceId;
|
||||
private int? arrayLength;
|
||||
private int alarmDelay;
|
||||
private int alarmDelay;
|
||||
private int alarmLevel;
|
||||
private ProtectTypeEnum protectType = ProtectTypeEnum.ReadWrite;
|
||||
private DataTypeEnum dataType = DataTypeEnum.Int16;
|
||||
|
||||
@@ -56,7 +59,7 @@ public class Variable : BaseDataEntity, IValidatableObject
|
||||
/// </summary>
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
internal bool IsUp;
|
||||
internal bool IsUp;
|
||||
private bool enable = true;
|
||||
public bool DynamicVariable;
|
||||
private bool rpcWriteEnable = true;
|
||||
@@ -274,6 +277,15 @@ public class Variable : BaseDataEntity, IValidatableObject
|
||||
public Dictionary<long, Dictionary<string, string>>? VariablePropertys { get => variablePropertys; set => variablePropertys = value; }
|
||||
|
||||
#region 报警
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 报警等级
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDescription = "报警等级")]
|
||||
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
|
||||
public int AlarmLevel { get => alarmLevel; set => alarmLevel = value; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警延时
|
||||
/// </summary>
|
||||
|
@@ -39,4 +39,9 @@ public enum EventTypeEnum
|
||||
/// 准备恢复
|
||||
/// </summary>
|
||||
PrepareFinish,
|
||||
|
||||
/// <summary>
|
||||
/// 报警确认并恢复
|
||||
/// </summary>
|
||||
ConfirmAndFinish,
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"Version": "Version"
|
||||
},
|
||||
|
||||
"ThingsGateway.Gateway.Application.DefaultDiagram": {
|
||||
"ThingsGateway.Gateway.Application.INode": {
|
||||
|
||||
"Actuator": "Actuator",
|
||||
"AlarmChangedTriggerNode": "AlarmStateTrigger",
|
||||
@@ -120,6 +120,7 @@
|
||||
"ThingsGateway.Gateway.Application.AlarmVariable": {
|
||||
"AlarmCode": "AlarmCode",
|
||||
"AlarmDelay": "AlarmDelay",
|
||||
"AlarmLevel": "AlarmLevel",
|
||||
"AlarmEnable": "AlarmEnable",
|
||||
"AlarmLimit": "AlarmLimit",
|
||||
"AlarmText": "AlarmText",
|
||||
@@ -451,6 +452,7 @@
|
||||
"ThingsGateway.Gateway.Application.Variable": {
|
||||
"AddressOrOtherMethodNotNull": "Variable address or special method cannot be empty at the same time",
|
||||
"AlarmDelay": "AlarmDelay",
|
||||
"AlarmLevel": "AlarmLevel",
|
||||
"ArrayLength": "ArrayLength",
|
||||
"BoolCloseAlarmEnable": "BoolCloseAlarmEnable",
|
||||
"BoolCloseAlarmText": "BoolCloseAlarmText",
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"Version": "版本"
|
||||
},
|
||||
|
||||
"ThingsGateway.Gateway.Application.DefaultDiagram": {
|
||||
"ThingsGateway.Gateway.Application.INode": {
|
||||
|
||||
"Actuator": "执行",
|
||||
"AlarmChangedTriggerNode": "报警状态触发器",
|
||||
@@ -120,6 +120,7 @@
|
||||
"ThingsGateway.Gateway.Application.AlarmVariable": {
|
||||
"AlarmCode": "报警值",
|
||||
"AlarmDelay": "报警延时",
|
||||
"AlarmLevel": "报警等级",
|
||||
"AlarmEnable": "报警使能",
|
||||
"AlarmLimit": "报警限值",
|
||||
"AlarmText": "报警文本",
|
||||
@@ -453,6 +454,7 @@
|
||||
"ThingsGateway.Gateway.Application.Variable": {
|
||||
"AddressOrOtherMethodNotNull": " 变量地址或特殊方法不能同时为空 ",
|
||||
"AlarmDelay": "报警延时",
|
||||
"AlarmLevel": "报警等级",
|
||||
"ArrayLength": "数组长度",
|
||||
"BoolCloseAlarmEnable": "布尔关报警使能",
|
||||
"BoolCloseAlarmText": "布尔关报警文本",
|
||||
|
@@ -63,6 +63,11 @@ public class AlarmVariable : PrimaryIdEntity, IDBHistoryAlarm
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
|
||||
public DataTypeEnum DataType { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.AlarmLevel"/>
|
||||
[SugarColumn(ColumnDescription = "报警等级", IsNullable = false)]
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
|
||||
public int AlarmLevel { get; set; }
|
||||
|
||||
/// <inheritdoc cref="VariableRuntime.AlarmCode"/>
|
||||
[SugarColumn(ColumnDescription = "报警值", IsNullable = false)]
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
|
||||
|
@@ -46,6 +46,8 @@ public partial class VariableRuntime : Variable, IVariable, IDisposable
|
||||
private object _value;
|
||||
private object lastSetValue;
|
||||
private object rawValue;
|
||||
internal object AlarmLockObject=new();
|
||||
internal bool AlarmConfirm;
|
||||
private DeviceRuntime? deviceRuntime;
|
||||
private IVariableSource? variableSource;
|
||||
private VariableMethod? variableMethod;
|
||||
|
@@ -218,7 +218,7 @@ internal sealed class AlarmTask : IDisposable
|
||||
if (alarmEnum == null)
|
||||
{
|
||||
// 如果仍未获取到报警类型,则触发需恢复报警事件(如果存在)
|
||||
AlarmChange(item, null, text, EventTypeEnum.Finish, alarmEnum, delay);
|
||||
AlarmChange(item, null, text, true, alarmEnum, delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -233,14 +233,14 @@ internal sealed class AlarmTask : IDisposable
|
||||
if (result)
|
||||
{
|
||||
// 如果表达式结果为true,则触发报警事件
|
||||
AlarmChange(item, limit, text, EventTypeEnum.Alarm, alarmEnum, delay);
|
||||
AlarmChange(item, limit, text,false, alarmEnum, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不存在报警约束表达式,则直接触发报警事件
|
||||
AlarmChange(item, limit, text, EventTypeEnum.Alarm, alarmEnum, delay);
|
||||
AlarmChange(item, limit, text, false, alarmEnum, delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,193 +251,227 @@ internal sealed class AlarmTask : IDisposable
|
||||
/// <param name="item">要处理的变量</param>
|
||||
/// <param name="limit">报警限制值</param>
|
||||
/// <param name="text">报警文本</param>
|
||||
/// <param name="eventEnum">报警事件类型枚举</param>
|
||||
/// <param name="finish">是否恢复</param>
|
||||
/// <param name="alarmEnum">报警类型枚举</param>
|
||||
/// <param name="delay">报警延时</param>
|
||||
private static void AlarmChange(VariableRuntime item, object limit, string text, EventTypeEnum eventEnum, AlarmTypeEnum? alarmEnum, int delay)
|
||||
private static void AlarmChange(VariableRuntime item, object limit, string text, bool finish, AlarmTypeEnum? alarmEnum, int delay)
|
||||
{
|
||||
bool changed = false;
|
||||
if (eventEnum == EventTypeEnum.Finish)
|
||||
lock (item.AlarmLockObject)
|
||||
{
|
||||
// 如果是需恢复报警事件
|
||||
// 如果实时报警列表中不存在该变量,则直接返回
|
||||
if (!GlobalData.RealAlarmIdVariables.ContainsKey(item.Id))
|
||||
bool changed = false;
|
||||
if (finish)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (eventEnum == EventTypeEnum.Alarm)
|
||||
{
|
||||
// 如果是触发报警事件
|
||||
// 在实时报警列表中查找该变量
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var variable))
|
||||
{
|
||||
// 如果变量已经处于相同的报警类型,则直接返回
|
||||
if (item.AlarmType == alarmEnum)
|
||||
// 如果是需恢复报警事件
|
||||
// 如果实时报警列表中不存在该变量,则直接返回
|
||||
if (!GlobalData.RealAlarmIdVariables.ContainsKey(item.Id))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新变量的报警信息和事件时间
|
||||
if (eventEnum == EventTypeEnum.Alarm)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
//添加报警延时策略
|
||||
if (delay > 0)
|
||||
{
|
||||
if (item.EventType != EventTypeEnum.Alarm && item.EventType != EventTypeEnum.PrepareAlarm)
|
||||
{
|
||||
item.EventType = EventTypeEnum.PrepareAlarm;//准备报警
|
||||
item.PrepareAlarmEventTime = now;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.EventType == EventTypeEnum.PrepareAlarm)
|
||||
{
|
||||
if ((now - item.PrepareAlarmEventTime!.Value).TotalMilliseconds > delay)
|
||||
{
|
||||
//超过延时时间,触发报警
|
||||
item.EventType = EventTypeEnum.Alarm;
|
||||
item.AlarmTime = now;
|
||||
item.EventTime = now;
|
||||
item.AlarmType = alarmEnum;
|
||||
item.AlarmLimit = limit.ToString();
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.RecoveryCode = string.Empty;
|
||||
item.AlarmText = text;
|
||||
item.PrepareAlarmEventTime = null;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else if (item.EventType == EventTypeEnum.Alarm && item.AlarmType != alarmEnum)
|
||||
{
|
||||
//报警类型改变,重新计时
|
||||
if (item.PrepareAlarmEventTime == null)
|
||||
item.PrepareAlarmEventTime = now;
|
||||
if ((now - item.PrepareAlarmEventTime!.Value).TotalMilliseconds > delay)
|
||||
{
|
||||
//超过延时时间,触发报警
|
||||
item.EventType = EventTypeEnum.Alarm;
|
||||
item.AlarmTime = now;
|
||||
item.EventTime = now;
|
||||
item.AlarmType = alarmEnum;
|
||||
item.AlarmLimit = limit.ToString();
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.RecoveryCode = string.Empty;
|
||||
item.AlarmText = text;
|
||||
item.PrepareAlarmEventTime = null;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.EventType != EventTypeEnum.Confirm)
|
||||
item.AlarmConfirm = false;
|
||||
// 如果是触发报警事件
|
||||
item.EventType = eventEnum;
|
||||
item.AlarmTime = now;
|
||||
item.EventTime = now;
|
||||
item.AlarmType = alarmEnum;
|
||||
item.AlarmLimit = limit.ToString();
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.RecoveryCode = string.Empty;
|
||||
item.AlarmText = text;
|
||||
item.PrepareAlarmEventTime = null;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else if (eventEnum == EventTypeEnum.Finish)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
//添加报警延时策略
|
||||
if (delay > 0)
|
||||
{
|
||||
if (item.EventType != EventTypeEnum.Finish && item.EventType != EventTypeEnum.PrepareFinish)
|
||||
// 在实时报警列表中查找该变量
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var variable))
|
||||
{
|
||||
item.EventType = EventTypeEnum.PrepareFinish;//准备报警
|
||||
item.PrepareFinishEventTime = now;
|
||||
// 如果变量已经处于相同的报警类型,则直接返回
|
||||
if (item.AlarmType == alarmEnum)
|
||||
return;
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
// 更新变量的报警信息和事件时间
|
||||
if (!finish)
|
||||
{
|
||||
var now = DateTime.Now;
|
||||
//添加报警延时策略
|
||||
if (delay > 0)
|
||||
{
|
||||
if (item.EventType == EventTypeEnum.PrepareFinish)
|
||||
if (item.EventType != EventTypeEnum.Alarm && item.EventType != EventTypeEnum.PrepareAlarm)
|
||||
{
|
||||
if ((now - item.PrepareFinishEventTime!.Value).TotalMilliseconds > delay)
|
||||
item.EventType = EventTypeEnum.PrepareAlarm;//准备报警
|
||||
item.PrepareAlarmEventTime = now;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.EventType == EventTypeEnum.PrepareAlarm)
|
||||
{
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var oldAlarm))
|
||||
if ((now - item.PrepareAlarmEventTime!.Value).TotalMilliseconds > delay)
|
||||
{
|
||||
item.AlarmType = oldAlarm.AlarmType;
|
||||
item.EventType = eventEnum;
|
||||
item.AlarmLimit = oldAlarm.AlarmLimit;
|
||||
item.AlarmCode = oldAlarm.AlarmCode;
|
||||
item.RecoveryCode = item.Value.ToString();
|
||||
item.AlarmText = oldAlarm.AlarmText;
|
||||
item.EventTime = DateTime.Now;
|
||||
item.PrepareFinishEventTime = null;
|
||||
//超过延时时间,触发报警
|
||||
item.EventType = EventTypeEnum.Alarm;
|
||||
item.AlarmTime = now;
|
||||
item.EventTime = now;
|
||||
item.AlarmType = alarmEnum;
|
||||
item.AlarmLimit = limit.ToString();
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.RecoveryCode = string.Empty;
|
||||
item.AlarmText = text;
|
||||
item.PrepareAlarmEventTime = null;
|
||||
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else if (item.EventType == EventTypeEnum.Alarm && item.AlarmType != alarmEnum)
|
||||
{
|
||||
//报警类型改变,重新计时
|
||||
if (item.PrepareAlarmEventTime == null)
|
||||
item.PrepareAlarmEventTime = now;
|
||||
if ((now - item.PrepareAlarmEventTime!.Value).TotalMilliseconds > delay)
|
||||
{
|
||||
//超过延时时间,触发报警
|
||||
item.EventType = EventTypeEnum.Alarm;
|
||||
item.AlarmTime = now;
|
||||
item.EventTime = now;
|
||||
item.AlarmType = alarmEnum;
|
||||
item.AlarmLimit = limit.ToString();
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.RecoveryCode = string.Empty;
|
||||
item.AlarmText = text;
|
||||
item.PrepareAlarmEventTime = null;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果是触发报警事件
|
||||
item.EventType = EventTypeEnum.Alarm;
|
||||
item.AlarmTime = now;
|
||||
item.EventTime = now;
|
||||
item.AlarmType = alarmEnum;
|
||||
item.AlarmLimit = limit.ToString();
|
||||
item.AlarmCode = item.Value.ToString();
|
||||
item.RecoveryCode = string.Empty;
|
||||
item.AlarmText = text;
|
||||
item.PrepareAlarmEventTime = null;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果是需恢复报警事件
|
||||
// 获取旧的报警信息
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var oldAlarm))
|
||||
var now = DateTime.Now;
|
||||
//添加报警延时策略
|
||||
if (delay > 0)
|
||||
{
|
||||
item.AlarmType = oldAlarm.AlarmType;
|
||||
item.EventType = eventEnum;
|
||||
item.AlarmLimit = oldAlarm.AlarmLimit;
|
||||
item.AlarmCode = oldAlarm.AlarmCode;
|
||||
item.RecoveryCode = item.Value.ToString();
|
||||
item.AlarmText = oldAlarm.AlarmText;
|
||||
item.EventTime = DateTime.Now;
|
||||
item.PrepareFinishEventTime = null;
|
||||
changed = true;
|
||||
if (item.EventType != EventTypeEnum.Finish && item.EventType != EventTypeEnum.PrepareFinish)
|
||||
{
|
||||
item.EventType = EventTypeEnum.PrepareFinish;
|
||||
item.PrepareFinishEventTime = now;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.EventType == EventTypeEnum.PrepareFinish)
|
||||
{
|
||||
if ((now - item.PrepareFinishEventTime!.Value).TotalMilliseconds > delay)
|
||||
{
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var oldAlarm))
|
||||
{
|
||||
item.AlarmType = oldAlarm.AlarmType;
|
||||
item.AlarmLimit = oldAlarm.AlarmLimit;
|
||||
item.AlarmCode = oldAlarm.AlarmCode;
|
||||
item.RecoveryCode = item.Value.ToString();
|
||||
item.AlarmText = oldAlarm.AlarmText;
|
||||
if (item.EventType != EventTypeEnum.Finish)
|
||||
{
|
||||
item.EventTime = now;
|
||||
}
|
||||
item.EventType = EventTypeEnum.Finish;
|
||||
item.PrepareFinishEventTime = null;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果是需恢复报警事件
|
||||
// 获取旧的报警信息
|
||||
if (GlobalData.RealAlarmIdVariables.TryGetValue(item.Id, out var oldAlarm))
|
||||
{
|
||||
item.AlarmType = oldAlarm.AlarmType;
|
||||
item.AlarmLimit = oldAlarm.AlarmLimit;
|
||||
item.AlarmCode = oldAlarm.AlarmCode;
|
||||
item.RecoveryCode = item.Value.ToString();
|
||||
item.AlarmText = oldAlarm.AlarmText;
|
||||
if (item.EventType != EventTypeEnum.Finish)
|
||||
{
|
||||
item.EventTime = now;
|
||||
}
|
||||
item.EventType = EventTypeEnum.Finish;
|
||||
item.PrepareFinishEventTime = null;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 触发报警变化事件
|
||||
if (changed)
|
||||
{
|
||||
if (item.EventType == EventTypeEnum.Alarm)
|
||||
// 触发报警变化事件
|
||||
if (changed)
|
||||
{
|
||||
// 如果是触发报警事件
|
||||
//lock (GlobalData. RealAlarmVariables)
|
||||
if (item.EventType == EventTypeEnum.Alarm)
|
||||
{
|
||||
// 从实时报警列表中移除旧的报警信息,并添加新的报警信息
|
||||
GlobalData.RealAlarmIdVariables.AddOrUpdate(item.Id, a => item.AdaptAlarmVariable(), (a, b) => item.AdaptAlarmVariable());
|
||||
// 如果是触发报警事件
|
||||
//lock (GlobalData. RealAlarmVariables)
|
||||
{
|
||||
// 从实时报警列表中移除旧的报警信息,并添加新的报警信息
|
||||
GlobalData.RealAlarmIdVariables.AddOrUpdate(item.Id, a => item.AdaptAlarmVariable(), (a, b) => item.AdaptAlarmVariable());
|
||||
}
|
||||
}
|
||||
else if (item.EventType == EventTypeEnum.Finish)
|
||||
{
|
||||
|
||||
// 如果是需恢复报警事件,则从实时报警列表中移除该变量
|
||||
if(item.AlarmConfirm)
|
||||
{
|
||||
GlobalData.RealAlarmIdVariables.TryRemove(item.Id, out _);
|
||||
item.EventType = EventTypeEnum.ConfirmAndFinish;
|
||||
}
|
||||
else
|
||||
{
|
||||
GlobalData.RealAlarmIdVariables.AddOrUpdate(item.Id, a => item.AdaptAlarmVariable(), (a, b) => item.AdaptAlarmVariable());
|
||||
}
|
||||
|
||||
}
|
||||
GlobalData.AlarmChange(item.AdaptAlarmVariable());
|
||||
}
|
||||
else if (item.EventType == EventTypeEnum.Finish)
|
||||
{
|
||||
// 如果是需恢复报警事件,则从实时报警列表中移除该变量
|
||||
GlobalData.RealAlarmIdVariables.TryRemove(item.Id, out _);
|
||||
//GlobalData.RealAlarmIdVariables.AddOrUpdate(item.Id, a => item.AdaptAlarmVariable(), (a, b) => item.AdaptAlarmVariable());
|
||||
}
|
||||
GlobalData.AlarmChange(item.AdaptAlarmVariable());
|
||||
}
|
||||
}
|
||||
|
||||
public void ConfirmAlarm(long variableId)
|
||||
{
|
||||
// 如果是确认报警事件
|
||||
if (GlobalData.AlarmEnableIdVariables.TryGetValue(variableId, out var variableRuntime))
|
||||
if (GlobalData.AlarmEnableIdVariables.TryGetValue(variableId, out var item))
|
||||
{
|
||||
variableRuntime.EventType = EventTypeEnum.Confirm;
|
||||
variableRuntime.EventTime = DateTime.Now;
|
||||
GlobalData.RealAlarmIdVariables.AddOrUpdate(variableId, a => variableRuntime.AdaptAlarmVariable(), (a, b) => variableRuntime.AdaptAlarmVariable());
|
||||
GlobalData.AlarmChange(variableRuntime.AdaptAlarmVariable());
|
||||
lock (item.AlarmLockObject)
|
||||
{
|
||||
item.AlarmConfirm = true;
|
||||
item.EventTime = DateTime.Now;
|
||||
|
||||
if (item.EventType == EventTypeEnum.Finish)
|
||||
{
|
||||
item.EventType = EventTypeEnum.ConfirmAndFinish;
|
||||
GlobalData.RealAlarmIdVariables.TryRemove(variableId, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.EventType = EventTypeEnum.Confirm;
|
||||
GlobalData.RealAlarmIdVariables.AddOrUpdate(variableId, a => item.AdaptAlarmVariable(), (a, b) => item.AdaptAlarmVariable());
|
||||
}
|
||||
|
||||
GlobalData.AlarmChange(item.AdaptAlarmVariable());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
using TouchSocket.Dmtp.Rpc;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application
|
||||
{
|
||||
|
||||
public interface IRealAlarmService
|
||||
{
|
||||
[DmtpRpc]
|
||||
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables();
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
/// <summary>
|
||||
/// 设备采集报警后台服务
|
||||
/// </summary>
|
||||
internal sealed class RealAlarmService : IRealAlarmService
|
||||
{
|
||||
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables()
|
||||
{
|
||||
return GlobalData.GetCurrentUserRealAlarmVariables();
|
||||
}
|
||||
|
||||
}
|
@@ -149,4 +149,9 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// <returns></returns>
|
||||
[DmtpRpc]
|
||||
Task SavePluginByPath(PluginAddPathInput plugin);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables();
|
||||
|
||||
}
|
@@ -19,7 +19,7 @@ using TouchSocket.Sockets;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBackendLogService, IRpcLogService, IRestartService, IAuthenticationService, IChannelEnableService, IRedundancyHostedService, IRedundancyService, ITextFileReadService, IPluginPageService
|
||||
public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBackendLogService, IRpcLogService, IRestartService, IAuthenticationService, IChannelEnableService, IRedundancyHostedService, IRedundancyService, ITextFileReadService, IPluginPageService, IRealAlarmService
|
||||
{
|
||||
|
||||
|
||||
@@ -97,4 +97,6 @@ public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBa
|
||||
|
||||
|
||||
public Task SavePluginByPath(PluginAddPathInput plugin) => App.GetService<IPluginPageService>().SavePluginByPath(plugin);
|
||||
|
||||
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables() => App.GetService<IRealAlarmService>().GetCurrentUserRealAlarmVariables();
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ public static class PluginInfoUtil
|
||||
/// <summary>
|
||||
/// 异步保存驱动程序信息。
|
||||
/// </summary>
|
||||
public static async Task SavePlugin(PluginAddInput plugin)
|
||||
public static async Task SavePlugin(PluginAddInput plugin, IPluginPageService pluginPageService)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -31,11 +31,11 @@ public static class PluginInfoUtil
|
||||
string tempDir = TempDirName;
|
||||
// 获取主程序集文件名
|
||||
var mainFileName = Path.GetFileNameWithoutExtension(plugin.MainFile.Name);
|
||||
string fullDir = string.Empty;
|
||||
//判定是否上下文程序集
|
||||
|
||||
// 构建插件文件夹绝对路径
|
||||
fullDir = AppContext.BaseDirectory.CombinePathWithOs(tempDir, mainFileName);
|
||||
string fullDir = AppContext.BaseDirectory.CombinePathWithOs(tempDir, mainFileName);
|
||||
|
||||
Directory.CreateDirectory(fullDir);
|
||||
|
||||
PluginAddPathInput pluginAddPathInput = new();
|
||||
|
||||
@@ -46,26 +46,34 @@ public static class PluginInfoUtil
|
||||
var fullPath = fullDir.CombinePathWithOs(plugin.MainFile.Name);
|
||||
|
||||
// 获取主程序集文件流
|
||||
using var stream = plugin.MainFile.OpenReadStream(maxFileSize);
|
||||
using (var stream = plugin.MainFile.OpenReadStream(maxFileSize))
|
||||
{
|
||||
FileStream fs = new(fullPath, FileMode.Create);
|
||||
|
||||
using FileStream fs = new($"{fullPath}", FileMode.Create);
|
||||
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
||||
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
||||
await fs.SafeDisposeAsync().ConfigureAwait(false);
|
||||
}
|
||||
pluginAddPathInput.MainFilePath = fullPath;
|
||||
|
||||
foreach (var item in plugin.OtherFiles ?? new())
|
||||
{
|
||||
// 获取附属文件流
|
||||
using var otherStream = item.OpenReadStream(maxFileSize);
|
||||
var otherFullPath = $"{fullDir.CombinePathWithOs(item.Name)}";
|
||||
using FileStream otherFs = new(otherFullPath, FileMode.Create);
|
||||
await stream.CopyToAsync(otherFs).ConfigureAwait(false);
|
||||
using (var otherStream = item.OpenReadStream(maxFileSize))
|
||||
{
|
||||
|
||||
pluginAddPathInput.OtherFilePaths.Add(otherFullPath);
|
||||
var otherFullPath = $"{fullDir.CombinePathWithOs(item.Name)}";
|
||||
FileStream otherFs = new(otherFullPath, FileMode.Create);
|
||||
|
||||
await otherStream.CopyToAsync(otherFs).ConfigureAwait(false);
|
||||
await otherFs.SafeDisposeAsync().ConfigureAwait(false);
|
||||
|
||||
pluginAddPathInput.OtherFilePaths.Add(otherFullPath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
await App.GetService<IPluginPageService>().SavePluginByPath(pluginAddPathInput).ConfigureAwait(false);
|
||||
await pluginPageService.SavePluginByPath(pluginAddPathInput).ConfigureAwait(false);
|
||||
|
||||
|
||||
}
|
||||
|
@@ -366,9 +366,9 @@ internal sealed class PluginService : IPluginService
|
||||
/// <summary>
|
||||
/// 异步保存驱动程序信息。
|
||||
/// </summary>
|
||||
/// <param name="plugin">要保存的插件信息。</param>
|
||||
/// <param name="pluginAddPathInput">要保存的插件信息。</param>
|
||||
[OperDesc("SavePlugin", isRecordPar: false, localizerType: typeof(PluginAddInput))]
|
||||
public async Task SavePluginByPath(PluginAddPathInput plugin)
|
||||
public async Task SavePluginByPath(PluginAddPathInput pluginAddPathInput)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -381,7 +381,7 @@ internal sealed class PluginService : IPluginService
|
||||
List<(string Name, MemoryStream MemoryStream)> otherFilesStreams = new();
|
||||
|
||||
// 获取主程序集文件名
|
||||
var mainFileName = Path.GetFileNameWithoutExtension(plugin.MainFilePath);
|
||||
var mainFileName = Path.GetFileNameWithoutExtension(pluginAddPathInput.MainFilePath);
|
||||
string fullDir = string.Empty;
|
||||
bool isDefaultDriver = false;
|
||||
//判定是否上下文程序集
|
||||
@@ -403,34 +403,40 @@ internal sealed class PluginService : IPluginService
|
||||
try
|
||||
{
|
||||
// 构建主程序集绝对路径
|
||||
var fullPath = fullDir.CombinePathWithOs(Path.GetFileName(plugin.MainFilePath));
|
||||
var fullPath = fullDir.CombinePathWithOs(Path.GetFileName(pluginAddPathInput.MainFilePath));
|
||||
|
||||
|
||||
using var stream = File.Open(plugin.MainFilePath, FileMode.Open, FileAccess.Read);
|
||||
await stream.CopyToAsync(mainMemoryStream).ConfigureAwait(false);
|
||||
using (var stream = File.Open(pluginAddPathInput.MainFilePath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
|
||||
await stream.CopyToAsync(mainMemoryStream).ConfigureAwait(false);
|
||||
}
|
||||
mainMemoryStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
#region
|
||||
// 先加载到内存,如果成功添加后再装载到文件
|
||||
// 加载主程序集
|
||||
var assembly = assemblyLoadContext.LoadFromStream(mainMemoryStream);
|
||||
foreach (var item in plugin.OtherFilePaths ?? new())
|
||||
foreach (var item in pluginAddPathInput.OtherFilePaths ?? new())
|
||||
{
|
||||
// 获取附属文件流
|
||||
using var otherStream = File.Open(plugin.MainFilePath, FileMode.Open, FileAccess.Read);
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
await otherStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
otherFilesStreams.Add((Path.GetFileName(item), memoryStream));
|
||||
try
|
||||
using (var otherStream = File.Open(pluginAddPathInput.MainFilePath, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
// 尝试加载附属程序集
|
||||
assemblyLoadContext.LoadFromStream(memoryStream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 加载失败时记录警告信息
|
||||
_logger?.LogWarning(ex, string.Format(AppResource.LoadOtherFileFail, item));
|
||||
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
await otherStream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||
otherFilesStreams.Add((Path.GetFileName(item), memoryStream));
|
||||
try
|
||||
{
|
||||
// 尝试加载附属程序集
|
||||
assemblyLoadContext.LoadFromStream(memoryStream);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 加载失败时记录警告信息
|
||||
_logger?.LogWarning(ex, string.Format(AppResource.LoadOtherFileFail, item));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -503,6 +509,17 @@ internal sealed class PluginService : IPluginService
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(pluginAddPathInput.MainFilePath))
|
||||
{
|
||||
File.Delete(pluginAddPathInput.MainFilePath);
|
||||
}
|
||||
foreach (var item in pluginAddPathInput.OtherFilePaths)
|
||||
{
|
||||
if (File.Exists(item))
|
||||
{
|
||||
File.Delete(item);
|
||||
}
|
||||
}
|
||||
// 释放锁资源
|
||||
SaveLock.Release();
|
||||
}
|
||||
|
@@ -9,9 +9,11 @@ using ThingsGateway.Blazor.Diagrams.Core.Models;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
#if !Management
|
||||
[SugarTable("rules", TableDescription = "规则引擎")]
|
||||
[SugarIndex("unique_rules_name", nameof(Rules.Name), OrderByType.Asc, true)]
|
||||
[Tenant(SqlSugarConst.DB_Custom)]
|
||||
#endif
|
||||
public class Rules : BaseDataEntity
|
||||
{
|
||||
[SugarColumn(ColumnDescription = "名称", Length = 200)]
|
||||
|
@@ -6,7 +6,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(ExecuteScriptNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(ExecuteScriptNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBase, IDisposable
|
||||
{
|
||||
public ExecuteScriptNode(string id, Point? position = null) : base(id, position)
|
||||
@@ -84,6 +84,7 @@ public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBa
|
||||
}
|
||||
set
|
||||
{
|
||||
#if !Management
|
||||
if (text != value)
|
||||
{
|
||||
try
|
||||
@@ -96,11 +97,11 @@ public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBa
|
||||
}
|
||||
CSharpScriptEngineExtension.Remove(text);
|
||||
}
|
||||
|
||||
#endif
|
||||
text = value;
|
||||
}
|
||||
}
|
||||
|
||||
#if !Management
|
||||
async Task<OperResult<NodeOutput>> IActuatorNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -121,9 +122,15 @@ public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBa
|
||||
return new OperResult<NodeOutput>(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
Task<OperResult<NodeOutput>> IActuatorNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new OperResult<NodeOutput>());
|
||||
}
|
||||
#endif
|
||||
public void Dispose()
|
||||
{
|
||||
#if !Management
|
||||
if (!text.IsNullOrWhiteSpace())
|
||||
{
|
||||
try
|
||||
@@ -136,6 +143,7 @@ public class ExecuteScriptNode : TextNode, IActuatorNode, IExexcuteExpressionsBa
|
||||
}
|
||||
CSharpScriptEngineExtension.Remove(text);
|
||||
}
|
||||
#endif
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
@@ -5,12 +5,14 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Rpc.svg", Desc = nameof(VariableRpcNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.VariableWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Actuator", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Rpc.svg", Desc = nameof(VariableRpcNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.VariableWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class VariableRpcNode : VariableNode, IActuatorNode
|
||||
{
|
||||
public VariableRpcNode(string id, Point? position = null) : base(id, position)
|
||||
{ Title = "VariableRpcNode"; }
|
||||
|
||||
|
||||
#if !Management
|
||||
async Task<OperResult<NodeOutput>> IActuatorNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
@@ -37,4 +39,10 @@ public class VariableRpcNode : VariableNode, IActuatorNode
|
||||
return new OperResult<NodeOutput>(ex);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Task<OperResult<NodeOutput>> IActuatorNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new OperResult<NodeOutput>());
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -1,13 +1,15 @@
|
||||
|
||||
using ThingsGateway.Blazor.Diagrams.Core.Geometry;
|
||||
#if !Management
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
#endif
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(ConditionNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(ConditionNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class ConditionNode : TextNode, IConditionNode
|
||||
{
|
||||
public ConditionNode(string id, Point? position = null) : base(id, position)
|
||||
@@ -15,6 +17,7 @@ public class ConditionNode : TextNode, IConditionNode
|
||||
Title = "ConditionNode"; Placeholder = "ConditionNode.Placeholder";
|
||||
Text = "return true;";
|
||||
}
|
||||
#if !Management
|
||||
|
||||
Task<bool> IConditionNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -23,4 +26,10 @@ public class ConditionNode : TextNode, IConditionNode
|
||||
Logger?.Trace($"Condition result: {next}");
|
||||
return Task.FromResult(next);
|
||||
}
|
||||
#else
|
||||
Task<bool> IConditionNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -1,12 +1,13 @@
|
||||
|
||||
using ThingsGateway.Blazor.Diagrams.Core.Geometry;
|
||||
#if !Management
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
|
||||
#endif
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(DataNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/CSharpScript.svg", Desc = nameof(DataNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.CSharpScriptWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class DataNode : TextNode, IExpressionNode
|
||||
{
|
||||
public DataNode(string id, Point? position = null) : base(id, position)
|
||||
@@ -14,6 +15,7 @@ public class DataNode : TextNode, IExpressionNode
|
||||
Title = "DataNode"; Placeholder = "DataNode.Placeholder";
|
||||
Text = "return 1;";
|
||||
}
|
||||
#if !Management
|
||||
|
||||
Task<OperResult<NodeOutput>> IExpressionNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -31,4 +33,10 @@ public class DataNode : TextNode, IExpressionNode
|
||||
return Task.FromResult(new OperResult<NodeOutput>(ex));
|
||||
}
|
||||
}
|
||||
#else
|
||||
Task<OperResult<NodeOutput>> IExpressionNode.ExecuteAsync(NodeInput input, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(new OperResult<NodeOutput>());
|
||||
}
|
||||
#endif
|
||||
}
|
@@ -5,7 +5,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Delay.svg", Desc = nameof(DelayNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.NumberWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Expression", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Delay.svg", Desc = nameof(DelayNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.NumberWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class DelayNode : NumberNode, IExpressionNode
|
||||
{
|
||||
public DelayNode(string id, Point? position = null) : base(id, position) { Title = "DelayNode"; Placeholder = "DelayNode.Placeholder"; }
|
@@ -3,7 +3,7 @@ using ThingsGateway.Blazor.Diagrams.Core.Geometry;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Start/End", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Start.svg", Desc = nameof(StartNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.DefaultWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Start/End", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/Start.svg", Desc = nameof(StartNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.DefaultWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class StartNode : BaseNode, IStartNode
|
||||
{
|
||||
public StartNode(string id, Point? position = null) : base(id, position)
|
@@ -8,11 +8,13 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/ValueChanged.svg", Desc = nameof(AlarmChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.VariableWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/ValueChanged.svg", Desc = nameof(AlarmChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.VariableWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public AlarmChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "AlarmChangedTriggerNode"; }
|
||||
|
||||
#if !Management
|
||||
|
||||
private Func<NodeOutput, CancellationToken, Task> Func { get; set; }
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -104,4 +106,20 @@ public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
alarmChangedTriggerNodes.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
@@ -8,11 +8,13 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/ValueChanged.svg", Desc = nameof(DeviceChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.DeviceWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/ValueChanged.svg", Desc = nameof(DeviceChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.DeviceWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public DeviceChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "DeviceChangedTriggerNode"; Placeholder = "Device.Placeholder"; }
|
||||
|
||||
|
||||
#if !Management
|
||||
private Func<NodeOutput, CancellationToken, Task> Func { get; set; }
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -90,4 +92,18 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
list.Remove(this);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
@@ -5,7 +5,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/TimeInterval.svg", Desc = nameof(TimeIntervalTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.TextWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/TimeInterval.svg", Desc = nameof(TimeIntervalTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.TextWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class TimeIntervalTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
{
|
||||
~TimeIntervalTriggerNode()
|
||||
@@ -14,6 +14,7 @@ public class TimeIntervalTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
}
|
||||
public TimeIntervalTriggerNode(string id, Point? position = null) : base(id, position) { Title = "TimeIntervalTriggerNode"; Placeholder = "TimeIntervalTriggerNode.Placeholder"; }
|
||||
|
||||
#if !Management
|
||||
private IScheduledTask _task;
|
||||
private Func<NodeOutput, CancellationToken, Task> Func { get; set; }
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
@@ -51,4 +52,21 @@ public class TimeIntervalTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#else
|
||||
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
@@ -8,11 +8,13 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/ValueChanged.svg", Desc = nameof(ValueChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.DefaultDiagram), WidgetType = "ThingsGateway.Gateway.Razor.VariableWidget,ThingsGateway.Gateway.Razor")]
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = "_content/ThingsGateway.Gateway.Razor/img/ValueChanged.svg", Desc = nameof(ValueChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = "ThingsGateway.Gateway.Razor.VariableWidget,ThingsGateway.Gateway.Razor")]
|
||||
public class ValueChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public ValueChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "ValueChangedTriggerNode"; }
|
||||
|
||||
|
||||
#if !Management
|
||||
private Func<NodeOutput, CancellationToken, Task> Func { get; set; }
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -100,4 +102,20 @@ public class ValueChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
valueChangedTriggerNodes.Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
@@ -20,17 +20,6 @@ using ThingsGateway.NewLife;
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
public class RulesLog
|
||||
{
|
||||
public RulesLog(Rules rules, TextFileLogger log)
|
||||
{
|
||||
Log = log;
|
||||
Rules = rules;
|
||||
}
|
||||
|
||||
public TextFileLogger Log { get; set; }
|
||||
public Rules Rules { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class RulesEngineHostedService : BackgroundService, IRulesEngineHostedService
|
||||
{
|
||||
|
@@ -0,0 +1,23 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class RulesLog
|
||||
{
|
||||
public RulesLog(Rules rules, TextFileLogger log)
|
||||
{
|
||||
Log = log;
|
||||
Rules = rules;
|
||||
}
|
||||
|
||||
public TextFileLogger Log { get; set; }
|
||||
public Rules Rules { get; set; }
|
||||
}
|
@@ -86,6 +86,7 @@ public class Startup : AppStartup
|
||||
services.AddSingleton<IAuthenticationService, AuthenticationService>();
|
||||
services.AddSingleton<IRestartService, RestartService>();
|
||||
services.AddSingleton<IChannelEnableService, ChannelEnableService>();
|
||||
services.AddSingleton<IRealAlarmService, RealAlarmService>();
|
||||
|
||||
services.AddGatewayHostedService<IAlarmHostedService, AlarmHostedService>();
|
||||
services.AddGatewayHostedService<IGatewayMonitorHostedService, GatewayMonitorHostedService>();
|
||||
|
@@ -187,6 +187,8 @@
|
||||
<EditorItem @bind-Field="@context.CustomRestrainExpressions" />
|
||||
<EditorItem @bind-Field="@context.CustomAlarmEnable" />
|
||||
<EditorItem @bind-Field="@context.AlarmDelay" />
|
||||
<EditorItem @bind-Field="@context.AlarmLevel" />
|
||||
|
||||
</FieldItems>
|
||||
</EditorForm>
|
||||
</TabItem>
|
||||
|
@@ -69,7 +69,7 @@ public partial class PluginPage
|
||||
};
|
||||
op.Component = BootstrapDynamicComponent.CreateComponent<SavePlugin>(new Dictionary<string, object?>
|
||||
{
|
||||
[nameof(SavePlugin.OnSavePlugin)] = new Func<PluginAddInput, Task>(PluginInfoUtil.SavePlugin),
|
||||
[nameof(SavePlugin.OnSavePlugin)] = new Func<PluginAddInput, Task>((a) => PluginInfoUtil.SavePlugin(a, PluginService)),
|
||||
});
|
||||
await DialogService.Show(op);
|
||||
}
|
||||
|
@@ -1,10 +1,19 @@
|
||||
@page "/gateway/realalarm"
|
||||
|
||||
@{
|
||||
#if !Management
|
||||
}
|
||||
@page "/gateway/realalarm"
|
||||
@attribute [Authorize]
|
||||
@attribute [RolePermission]
|
||||
|
||||
@{
|
||||
#endif
|
||||
}
|
||||
|
||||
@namespace ThingsGateway.Gateway.Razor
|
||||
@using ThingsGateway.Admin.Application
|
||||
@using ThingsGateway.Admin.Razor
|
||||
@using ThingsGateway.Gateway.Application
|
||||
@attribute [Authorize]
|
||||
@attribute [RolePermission]
|
||||
@inherits ComponentDefault
|
||||
|
||||
|
||||
@@ -17,7 +26,6 @@
|
||||
AllowResizing="true"
|
||||
IsFixedHeader=true
|
||||
IsMultipleSelect=false
|
||||
SearchModel=SearchModel
|
||||
ShowExtendButtons=false
|
||||
ShowExportButton=false
|
||||
ShowExtendDeleteButton=false
|
||||
@@ -31,13 +39,15 @@
|
||||
IsPagination=true>
|
||||
|
||||
<TableColumns>
|
||||
<TableColumn Field="@context.DeviceName" FieldExpression=@(()=>context.DeviceName) Filterable="true" Sortable="true" Visible="true" />
|
||||
<TableColumn @bind-Field="@context.Id" Filterable=true Sortable=true Visible=false />
|
||||
<TableColumn Field="@context.DeviceName" FieldExpression=@(() => context.DeviceName) Filterable="true" Sortable="true" Visible="true" />
|
||||
<TableColumn @bind-Field="@context.Name" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.Description" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.DataType" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.AlarmTime" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.AlarmCode" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.AlarmLimit" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.AlarmLevel" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.AlarmText" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.AlarmType" Filterable=true Sortable=true Visible=true />
|
||||
<TableColumn @bind-Field="@context.RecoveryCode" Filterable=true Sortable=true Visible=true />
|
||||
|
@@ -14,13 +14,14 @@ namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
public partial class RealAlarmPage
|
||||
{
|
||||
private AlarmVariable? SearchModel { get; set; } = new();
|
||||
[Inject]
|
||||
IRealAlarmService RealAlarmService { get; set; }
|
||||
|
||||
#region 查询
|
||||
|
||||
private static async Task<QueryData<AlarmVariable>> OnQueryAsync(QueryPageOptions options)
|
||||
private async Task<QueryData<AlarmVariable>> OnQueryAsync(QueryPageOptions options)
|
||||
{
|
||||
var realAlarmVariables = await GlobalData.GetCurrentUserRealAlarmVariables().ConfigureAwait(false);
|
||||
var realAlarmVariables = await RealAlarmService.GetCurrentUserRealAlarmVariables().ConfigureAwait(false);
|
||||
var data = realAlarmVariables
|
||||
.GetQueryData(options);
|
||||
return data;
|
||||
|
Reference in New Issue
Block a user