!70 fix: questdb restapi多实例

This commit is contained in:
Diego
2025-08-07 01:58:32 +00:00
parent e84f42ce14
commit a8a9453611
37 changed files with 510 additions and 223 deletions

View File

@@ -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;
}

View File

@@ -108,7 +108,7 @@ namespace ThingsGateway.SqlSugar
return result;
}
private static readonly HttpClient client = new HttpClient();
private readonly HttpClient client = new HttpClient();
/// <summary>
/// 异步批量快速插入数据

View File

@@ -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>

View File

@@ -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>

View File

@@ -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()

View File

@@ -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>

View File

@@ -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>

View File

@@ -39,4 +39,9 @@ public enum EventTypeEnum
/// 准备恢复
/// </summary>
PrepareFinish,
/// <summary>
/// 报警确认并恢复
/// </summary>
ConfirmAndFinish,
}

View File

@@ -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",

View File

@@ -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": "布尔关报警文本",

View File

@@ -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)]

View File

@@ -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;

View File

@@ -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());
}
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -149,4 +149,9 @@ public interface IManagementRpcServer : IRpcServer
/// <returns></returns>
[DmtpRpc]
Task SavePluginByPath(PluginAddPathInput plugin);
[DmtpRpc]
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariables();
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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();
}

View File

@@ -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)]

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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"; }

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
{

View File

@@ -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; }
}

View File

@@ -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>();

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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 />

View File

@@ -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;