mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-20 10:50:48 +08:00
!73 10.11.10
This commit is contained in:
45
src/Admin/ThingsGateway.NewLife.X/Common/BoundedQueue.cs
Normal file
45
src/Admin/ThingsGateway.NewLife.X/Common/BoundedQueue.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace ThingsGateway.NewLife;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class BoundedQueue<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly Queue<T> _queue;
|
||||
private readonly int _capacity;
|
||||
private readonly object _syncRoot = new object();
|
||||
|
||||
public BoundedQueue(int capacity)
|
||||
{
|
||||
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
_capacity = capacity;
|
||||
_queue = new Queue<T>(capacity);
|
||||
}
|
||||
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_queue.Count == _capacity)
|
||||
_queue.Dequeue();
|
||||
_queue.Enqueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { lock (_syncRoot) return _queue.Count; }
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return new List<T>(_queue).GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ namespace ThingsGateway.NewLife.Json.Extension;
|
||||
/// </summary>
|
||||
public static class SystemTextJsonExtension
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 默认Json规则(带缩进)
|
||||
/// </summary>
|
||||
@@ -31,37 +32,51 @@ public static class SystemTextJsonExtension
|
||||
/// </summary>
|
||||
public static JsonSerializerOptions NoneIndentedOptions;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 默认Json规则(带缩进)
|
||||
/// </summary>
|
||||
public static JsonSerializerOptions IgnoreNullIndentedOptions;
|
||||
|
||||
/// <summary>
|
||||
/// 默认Json规则(无缩进)
|
||||
/// </summary>
|
||||
public static JsonSerializerOptions IgnoreNullNoneIndentedOptions;
|
||||
|
||||
public static JsonSerializerOptions GetOptions(bool writeIndented, bool ignoreNull)
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
WriteIndented = writeIndented,
|
||||
DefaultIgnoreCondition = ignoreNull
|
||||
? JsonIgnoreCondition.WhenWritingNull
|
||||
: JsonIgnoreCondition.Never,
|
||||
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
|
||||
};
|
||||
|
||||
options.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
options.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
options.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
options.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
options.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
static SystemTextJsonExtension()
|
||||
{
|
||||
IndentedOptions = new JsonSerializerOptions
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
WriteIndented = true, // 缩进
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // 忽略 null
|
||||
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
|
||||
};
|
||||
// 如有自定义Converter,这里添加
|
||||
// IndentedOptions.Converters.Add(new ByteArrayJsonConverter());
|
||||
IndentedOptions.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
IndentedOptions.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
IndentedOptions.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
IndentedOptions.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
IndentedOptions.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
NoneIndentedOptions = new JsonSerializerOptions
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
WriteIndented = false, // 不缩进
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
|
||||
};
|
||||
NoneIndentedOptions.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
NoneIndentedOptions.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
NoneIndentedOptions.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
NoneIndentedOptions.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
NoneIndentedOptions.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
// NoneIndentedOptions.Converters.Add(new ByteArrayJsonConverter());
|
||||
|
||||
IndentedOptions = GetOptions(true, false);
|
||||
NoneIndentedOptions = GetOptions(false, false);
|
||||
|
||||
IgnoreNullIndentedOptions = GetOptions(true, true);
|
||||
IgnoreNullNoneIndentedOptions = GetOptions(false, true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化
|
||||
/// </summary>
|
||||
@@ -96,17 +111,17 @@ public static class SystemTextJsonExtension
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
public static string ToSystemTextJsonString(this object item, bool indented = true)
|
||||
public static string ToSystemTextJsonString(this object item, bool indented = true, bool ignoreNull = true)
|
||||
{
|
||||
return JsonSerializer.Serialize(item, item?.GetType() ?? typeof(object), indented ? IndentedOptions : NoneIndentedOptions);
|
||||
return JsonSerializer.Serialize(item, item?.GetType() ?? typeof(object), ignoreNull ? indented ? IgnoreNullIndentedOptions : IgnoreNullNoneIndentedOptions : indented ? IndentedOptions : NoneIndentedOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
public static byte[] ToSystemTextJsonUtf8Bytes(this object item, bool indented = true)
|
||||
public static byte[] ToSystemTextJsonUtf8Bytes(this object item, bool indented = true, bool ignoreNull = true)
|
||||
{
|
||||
return JsonSerializer.SerializeToUtf8Bytes(item, item.GetType(), indented ? IndentedOptions : NoneIndentedOptions);
|
||||
return JsonSerializer.SerializeToUtf8Bytes(item, item.GetType(), ignoreNull ? indented ? IgnoreNullIndentedOptions : IgnoreNullNoneIndentedOptions : indented ? IndentedOptions : NoneIndentedOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.11.6</PluginVersion>
|
||||
<ProPluginVersion>10.11.6</ProPluginVersion>
|
||||
<DefaultVersion>10.11.7</DefaultVersion>
|
||||
<PluginVersion>10.11.10</PluginVersion>
|
||||
<ProPluginVersion>10.11.10</ProPluginVersion>
|
||||
<DefaultVersion>10.11.10</DefaultVersion>
|
||||
<AuthenticationVersion>10.11.2</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.11.2</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.19</NET8Version>
|
||||
|
@@ -11,8 +11,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(NET9Version)" />
|
||||
<PackageReference Include="TouchSocket" Version="4.0.0-beta.1" />
|
||||
<PackageReference Include="TouchSocket.SerialPorts" Version="4.0.0-beta.1" />
|
||||
<PackageReference Include="TouchSocket" Version="4.0.0-beta.3" />
|
||||
<PackageReference Include="TouchSocket.SerialPorts" Version="4.0.0-beta.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -26,8 +26,21 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
protected abstract BusinessPropertyWithCache _businessPropertyWithCache { get; }
|
||||
|
||||
#if !Management
|
||||
|
||||
protected override Task DisposeAsync(bool disposing)
|
||||
{
|
||||
// 清空内存队列
|
||||
_memoryPluginEventDataModelQueue.Clear();
|
||||
_memoryAlarmModelQueue.Clear();
|
||||
_memoryDevModelQueue.Clear();
|
||||
_memoryVarModelQueue.Clear();
|
||||
_memoryVarModelsQueue.Clear();
|
||||
return base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
#region 条件
|
||||
|
||||
protected abstract bool PluginEventDataModelEnable { get; }
|
||||
protected abstract bool AlarmModelEnable { get; }
|
||||
protected abstract bool DevModelEnable { get; }
|
||||
protected abstract bool VarModelEnable { get; }
|
||||
@@ -36,6 +49,10 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
if (AlarmModelEnable)
|
||||
DBCacheAlarm = LocalDBCacheAlarmModel();
|
||||
|
||||
if (PluginEventDataModelEnable)
|
||||
DBCachePluginEventData = LocalDBCachePluginEventDataModel();
|
||||
|
||||
|
||||
if (DevModelEnable)
|
||||
DBCacheDev = LocalDBCacheDevModel();
|
||||
|
||||
@@ -68,7 +85,10 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
{
|
||||
await UpdateAlarmModelMemory(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (PluginEventDataModelEnable)
|
||||
{
|
||||
await UpdatePluginEventDataModelMemory(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
if (VarModelEnable)
|
||||
{
|
||||
await UpdateVarModelCache(cancellationToken).ConfigureAwait(false);
|
||||
@@ -83,6 +103,12 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
if (AlarmModelEnable)
|
||||
{
|
||||
await UpdateAlarmModelCache(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
if (PluginEventDataModelEnable)
|
||||
{
|
||||
await UpdatePluginEventDataModelCache(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -90,10 +116,12 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
#region alarm
|
||||
|
||||
protected ConcurrentQueue<CacheDBItem<AlarmVariable>> _memoryAlarmModelQueue = new();
|
||||
protected ConcurrentQueue<CacheDBItem<PluginEventData>> _memoryPluginEventDataModelQueue = new();
|
||||
|
||||
private volatile bool LocalDBCacheAlarmModelInited;
|
||||
private CacheDB DBCacheAlarm;
|
||||
|
||||
private volatile bool LocalDBCachePluginEventDataModelInited;
|
||||
private CacheDB DBCachePluginEventData;
|
||||
/// <summary>
|
||||
/// 入缓存
|
||||
/// </summary>
|
||||
@@ -142,6 +170,55 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 入缓存
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
protected virtual void AddCache(List<CacheDBItem<PluginEventData>> data)
|
||||
{
|
||||
if (_businessPropertyWithCache.CacheEnable && data?.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogMessage?.LogInformation($"Add {typeof(PluginEventData).Name} data to file cache, count {data.Count}");
|
||||
foreach (var item in data)
|
||||
{
|
||||
item.Id = CommonUtils.GetSingleId();
|
||||
}
|
||||
var dir = CacheDBUtil.GetCacheFilePath(CurrentDevice.Name.ToString());
|
||||
var fileStart = CacheDBUtil.GetFileName($"{CurrentDevice.PluginName}_{typeof(PluginEventData).FullName}_{nameof(PluginEventData)}");
|
||||
var fullName = dir.CombinePathWithOs($"{fileStart}{CacheDBUtil.EX}");
|
||||
|
||||
lock (cacheLock)
|
||||
{
|
||||
bool s = false;
|
||||
while (!s)
|
||||
{
|
||||
s = CacheDBUtil.DeleteCache(_businessPropertyWithCache.CacheFileMaxLength, fullName);
|
||||
}
|
||||
using var cache = LocalDBCachePluginEventDataModel();
|
||||
cache.DBProvider.Fastest<CacheDBItem<PluginEventData>>().PageSize(50000).BulkCopy(data);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
using var cache = LocalDBCachePluginEventDataModel();
|
||||
lock (cache.CacheDBOption)
|
||||
{
|
||||
cache.DBProvider.Fastest<CacheDBItem<PluginEventData>>().PageSize(50000).BulkCopy(data);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, "Add cache fail");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加队列,超限后会入缓存
|
||||
/// </summary>
|
||||
@@ -183,6 +260,48 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加队列,超限后会入缓存
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
protected virtual void AddQueuePluginDataModel(CacheDBItem<PluginEventData> data)
|
||||
{
|
||||
if (_businessPropertyWithCache.CacheEnable)
|
||||
{
|
||||
//检测队列长度,超限存入缓存数据库
|
||||
if (_memoryPluginEventDataModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
|
||||
{
|
||||
List<CacheDBItem<PluginEventData>> list = null;
|
||||
lock (_memoryPluginEventDataModelQueue)
|
||||
{
|
||||
if (_memoryPluginEventDataModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
|
||||
{
|
||||
list = _memoryPluginEventDataModelQueue.ToListWithDequeue();
|
||||
}
|
||||
}
|
||||
AddCache(list);
|
||||
}
|
||||
}
|
||||
if (_memoryPluginEventDataModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
|
||||
{
|
||||
lock (_memoryPluginEventDataModelQueue)
|
||||
{
|
||||
if (_memoryPluginEventDataModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
|
||||
{
|
||||
LogMessage?.LogWarning($"{typeof(PluginEventData).Name} Queue exceeds limit, clear old data. If it doesn't work as expected, increase {_businessPropertyWithCache.QueueMaxCount} or Enable cache");
|
||||
_memoryPluginEventDataModelQueue.Clear();
|
||||
_memoryPluginEventDataModelQueue.Enqueue(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_memoryPluginEventDataModelQueue.Enqueue(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存对象,注意每次获取的对象可能不一样,如顺序操作,需固定引用
|
||||
/// </summary>
|
||||
@@ -197,6 +316,20 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
return cacheDb;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取缓存对象,注意每次获取的对象可能不一样,如顺序操作,需固定引用
|
||||
/// </summary>
|
||||
protected virtual CacheDB LocalDBCachePluginEventDataModel()
|
||||
{
|
||||
var cacheDb = CacheDBUtil.GetCache(typeof(CacheDBItem<PluginEventData>), CurrentDevice.Name.ToString(), $"{CurrentDevice.PluginName}_{typeof(PluginEventData).Name}");
|
||||
|
||||
if (!LocalDBCachePluginEventDataModelInited)
|
||||
{
|
||||
cacheDb.InitDb();
|
||||
LocalDBCachePluginEventDataModelInited = true;
|
||||
}
|
||||
return cacheDb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 需实现上传到通道
|
||||
@@ -206,6 +339,16 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
/// <returns></returns>
|
||||
protected abstract ValueTask<OperResult> UpdateAlarmModel(List<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 需实现上传到通道
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
protected async Task UpdateAlarmModelCache(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_businessPropertyWithCache.CacheEnable)
|
||||
@@ -262,10 +405,69 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion //成功上传时,补上传缓存数据
|
||||
}
|
||||
}
|
||||
protected async Task UpdatePluginEventDataModelCache(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_businessPropertyWithCache.CacheEnable)
|
||||
{
|
||||
#region //成功上传时,补上传缓存数据
|
||||
|
||||
if (IsConnected())
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
//循环获取,固定读最大行数量,执行完成需删除行
|
||||
var varList = await DBCachePluginEventData.DBProvider.Queryable<CacheDBItem<PluginEventData>>().Take(_businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (varList.Count != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var result = await UpdatePluginEventDataModel(varList, cancellationToken).ConfigureAwait(false);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
//删除缓存
|
||||
await DBCachePluginEventData.DBProvider.Deleteable<CacheDBItem<PluginEventData>>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion //成功上传时,补上传缓存数据
|
||||
}
|
||||
}
|
||||
protected async Task UpdateAlarmModelMemory(CancellationToken cancellationToken)
|
||||
{
|
||||
#region //上传设备内存队列中的数据
|
||||
@@ -307,6 +509,47 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
|
||||
#endregion //上传设备内存队列中的数据
|
||||
}
|
||||
protected async Task UpdatePluginEventDataModelMemory(CancellationToken cancellationToken)
|
||||
{
|
||||
#region //上传设备内存队列中的数据
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var list = _memoryPluginEventDataModelQueue.ToListWithDequeue().ChunkBetter(_businessPropertyWithCache.SplitSize);
|
||||
foreach (var item in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var result = await UpdatePluginEventDataModel(item, cancellationToken).ConfigureAwait(false);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
AddCache(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex);
|
||||
success = false;
|
||||
}
|
||||
#endregion //上传设备内存队列中的数据
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
@@ -20,6 +20,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public abstract class BusinessBaseWithCacheAlarm : BusinessBaseWithCache
|
||||
{
|
||||
#if !Management
|
||||
protected override bool PluginEventDataModelEnable => true;
|
||||
protected override bool AlarmModelEnable => true;
|
||||
|
||||
protected override bool DevModelEnable => false;
|
||||
@@ -57,14 +58,40 @@ public abstract class BusinessBaseWithCacheAlarm : BusinessBaseWithCache
|
||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a => AlarmValueChange(a.Value));
|
||||
GlobalData.AlarmChangedEvent += AlarmValueChange;
|
||||
GlobalData.PluginEventHandler -= PluginEventChange;
|
||||
GlobalData.PluginEventHandler += PluginEventChange;
|
||||
|
||||
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
protected override Task DisposeAsync(bool disposing)
|
||||
{
|
||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||
GlobalData.PluginEventHandler -= PluginEventChange;
|
||||
return base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
|
||||
private void PluginEventChange(PluginEventData value)
|
||||
{
|
||||
if (CurrentDevice?.Pause != false)
|
||||
return;
|
||||
if (TaskSchedulerLoop?.Stoped == true) return;
|
||||
|
||||
if (!PluginEventDataModelEnable) return;
|
||||
// 如果业务属性的缓存为间隔上传,则不执行后续操作
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
PluginChange(value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 当报警状态变化时触发此方法。如果不需要进行报警上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueuePluginDataModel(CacheDBItem{PluginEventData})"/> 方法。
|
||||
/// </summary>
|
||||
protected virtual void PluginChange(PluginEventData value)
|
||||
{
|
||||
// 在报警状态变化时执行的自定义逻辑
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。
|
||||
/// </summary>
|
||||
|
@@ -40,7 +40,13 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a => AlarmValueChange(a.Value));
|
||||
|
||||
GlobalData.AlarmChangedEvent += AlarmValueChange;
|
||||
// 解绑全局数据的事件
|
||||
|
||||
}
|
||||
if (PluginEventDataModelEnable)
|
||||
{
|
||||
GlobalData.PluginEventHandler -= PluginEventChange;
|
||||
GlobalData.PluginEventHandler += PluginEventChange;
|
||||
|
||||
}
|
||||
if (DevModelEnable)
|
||||
{
|
||||
@@ -64,7 +70,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
}
|
||||
public override async Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (AlarmModelEnable || DevModelEnable || VarModelEnable)
|
||||
if (AlarmModelEnable || DevModelEnable || VarModelEnable || PluginEventDataModelEnable)
|
||||
{
|
||||
// 如果业务属性指定了全部变量,则设置当前设备的变量运行时列表和采集设备列表
|
||||
if (_businessPropertyWithCacheInterval.IsAllVariable)
|
||||
@@ -137,14 +143,12 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
{
|
||||
// 解绑事件
|
||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||
GlobalData.PluginEventHandler -= PluginEventChange;
|
||||
|
||||
GlobalData.VariableValueChangeEvent -= VariableValueChange;
|
||||
GlobalData.DeviceStatusChangeEvent -= DeviceStatusChange;
|
||||
|
||||
// 清空内存队列
|
||||
_memoryAlarmModelQueue.Clear();
|
||||
_memoryDevModelQueue.Clear();
|
||||
_memoryVarModelQueue.Clear();
|
||||
_memoryVarModelsQueue.Clear();
|
||||
|
||||
return base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
@@ -226,7 +230,26 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
{
|
||||
// 在变量状态变化时执行的自定义逻辑
|
||||
}
|
||||
private void PluginEventChange(PluginEventData value)
|
||||
{
|
||||
if (CurrentDevice?.Pause != false)
|
||||
return;
|
||||
if (TaskSchedulerLoop?.Stoped == true) return;
|
||||
|
||||
if (!PluginEventDataModelEnable) return;
|
||||
// 如果业务属性的缓存为间隔上传,则不执行后续操作
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
PluginChange(value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 当报警状态变化时触发此方法。如果不需要进行报警上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueueAlarmModel"/> 方法。
|
||||
/// </summary>
|
||||
protected virtual void PluginChange(PluginEventData value)
|
||||
{
|
||||
// 在报警状态变化时执行的自定义逻辑
|
||||
}
|
||||
/// <summary>
|
||||
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。
|
||||
/// </summary>
|
||||
|
@@ -26,8 +26,17 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
|
||||
protected abstract BusinessPropertyWithCacheIntervalScript _businessPropertyWithCacheIntervalScript { get; }
|
||||
|
||||
#if !Management
|
||||
|
||||
|
||||
#if !Management
|
||||
protected internal override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
CSharpScriptEngineExtension.Remove(_businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
|
||||
CSharpScriptEngineExtension.Remove(_businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
|
||||
CSharpScriptEngineExtension.Remove(_businessPropertyWithCacheIntervalScript.BigTextScriptPluginEventDataModel);
|
||||
CSharpScriptEngineExtension.Remove(_businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
|
||||
return base.InitChannelAsync(channel, cancellationToken);
|
||||
}
|
||||
public virtual string[] Match(string input)
|
||||
{
|
||||
// 生成缓存键,以确保缓存的唯一性
|
||||
@@ -64,7 +73,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
|
||||
protected IEnumerable<TopicArray> GetAlarmTopicArrays(IEnumerable<AlarmVariable> item)
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<AlarmVariable>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<AlarmVariable>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel, LogMessage);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic);
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
@@ -88,16 +97,19 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
|
||||
{
|
||||
var gList = group.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是报警列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
@@ -110,23 +122,92 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
protected IEnumerable<TopicArray> GetPluginEventDataTopicArrays(IEnumerable<PluginEventData> item)
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<PluginEventData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptPluginEventDataModel, LogMessage);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.PluginEventDataTopic);
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics);
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的报警主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.PluginEventDataTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Length; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsPluginEventDataList)
|
||||
{
|
||||
var gList = group.ToList();
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是报警列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsPluginEventDataList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.PluginEventDataTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.PluginEventDataTopic, json, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
protected IEnumerable<TopicArray> GetDeviceTopicArray(IEnumerable<DeviceBasicData> item)
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<DeviceBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<DeviceBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel, LogMessage);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic);
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
@@ -152,16 +233,19 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
// 如果是设备列表,则将整个分组转换为 JSON 字符串
|
||||
var gList = group.Select(a => a).ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是设备列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
@@ -175,14 +259,17 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, 1);
|
||||
}
|
||||
}
|
||||
@@ -191,7 +278,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
|
||||
protected IEnumerable<TopicArray> GetVariableScriptTopicArray(IEnumerable<VariableBasicData> item)
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<VariableBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<VariableBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel, LogMessage);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
@@ -217,16 +304,20 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
// 如果是变量列表,则将整个分组转换为 JSON 字符串
|
||||
var gList = group.Select(a => a).ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
@@ -240,14 +331,17 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, 1);
|
||||
}
|
||||
}
|
||||
@@ -294,16 +388,19 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
// 如果是变量列表,则将整个分组转换为 JSON 字符串
|
||||
var gList = group.Select(a => a).ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
@@ -317,14 +414,17 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, 1);
|
||||
}
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public abstract partial class BusinessBaseWithCacheIntervalScriptAll : BusinessBaseWithCacheIntervalScript
|
||||
{
|
||||
#if !Management
|
||||
protected override bool PluginEventDataModelEnable => true;
|
||||
protected override bool AlarmModelEnable => true;
|
||||
|
||||
protected override bool DevModelEnable => true;
|
||||
|
@@ -16,6 +16,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public abstract class BusinessBaseWithCacheIntervalVariable : BusinessBaseWithCacheInterval
|
||||
{
|
||||
#if !Management
|
||||
protected override bool PluginEventDataModelEnable => false;
|
||||
protected override bool AlarmModelEnable => false;
|
||||
|
||||
protected override bool DevModelEnable => false;
|
||||
@@ -30,5 +31,9 @@ public abstract class BusinessBaseWithCacheIntervalVariable : BusinessBaseWithCa
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@@ -28,7 +28,11 @@ public class BusinessPropertyWithCacheIntervalScript : BusinessPropertyWithCache
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool JsonFormattingIndented { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 忽略Null值
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool JsonIgnoreNull { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 设备Topic
|
||||
/// </summary>
|
||||
@@ -46,6 +50,11 @@ public class BusinessPropertyWithCacheIntervalScript : BusinessPropertyWithCache
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool IsAlarmList { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 报警Topic
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool IsPluginEventDataList { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 设备Topic
|
||||
@@ -65,6 +74,12 @@ public class BusinessPropertyWithCacheIntervalScript : BusinessPropertyWithCache
|
||||
[DynamicProperty(Remark = "可使用${key}作为匹配项,key必须是上传实体中的属性,比如ThingsGateway/Alarm/${DeviceName}")]
|
||||
public string AlarmTopic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警Topic
|
||||
/// </summary>
|
||||
[DynamicProperty(Remark = "可使用${key}作为匹配项,key必须是上传实体中的属性,比如ThingsGateway/PluginEventData/${DeviceName}")]
|
||||
public string PluginEventDataTopic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备实体脚本
|
||||
/// </summary>
|
||||
@@ -85,4 +100,11 @@ public class BusinessPropertyWithCacheIntervalScript : BusinessPropertyWithCache
|
||||
[DynamicProperty]
|
||||
[AutoGenerateColumn(Visible = true, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false)]
|
||||
public string? BigTextScriptAlarmModel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警实体脚本
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
[AutoGenerateColumn(Visible = true, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false)]
|
||||
public string? BigTextScriptPluginEventDataModel { get; set; }
|
||||
}
|
||||
|
@@ -23,12 +23,16 @@ public static class DynamicModelExtension
|
||||
/// <summary>
|
||||
/// GetDynamicModel
|
||||
/// </summary>
|
||||
public static IEnumerable<object> GetDynamicModel<T>(this IEnumerable<T> datas, string script)
|
||||
public static IEnumerable<object> GetDynamicModel<T>(this IEnumerable<T> datas, string script, TouchSocket.Core.ILog log)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(script))
|
||||
{
|
||||
//执行脚本,获取新实体
|
||||
var getDeviceModel = CSharpScriptEngineExtension.Do<IDynamicModel>(script);
|
||||
if (getDeviceModel is DynamicModelBase dynamicModelBase)
|
||||
{
|
||||
dynamicModelBase.Logger = log;
|
||||
}
|
||||
return getDeviceModel.GetList(datas?.Cast<object>());
|
||||
}
|
||||
else
|
||||
@@ -50,9 +54,17 @@ public static class DynamicModelExtension
|
||||
if (variableRuntime == null || propertyName.IsNullOrWhiteSpace())
|
||||
return null;
|
||||
|
||||
// 检查是否存在对应的业务设备Id
|
||||
if (variableRuntime.VariablePropertys?.TryGetValue(businessId, out var keyValuePairs) == true)
|
||||
{
|
||||
keyValuePairs.TryGetValue(propertyName, out var value);
|
||||
return value; // 返回属性值
|
||||
}
|
||||
|
||||
|
||||
if (GlobalData.IdDevices.TryGetValue(businessId, out var deviceRuntime))
|
||||
{
|
||||
if (deviceRuntime.Driver?.DriverProperties is IBusinessPropertyAllVariableBase property)
|
||||
if (deviceRuntime.Driver is BusinessBase businessBase && businessBase.DriverProperties is IBusinessPropertyAllVariableBase property)
|
||||
{
|
||||
if (property.IsAllVariable)
|
||||
{
|
||||
@@ -64,18 +76,12 @@ public static class DynamicModelExtension
|
||||
}
|
||||
else
|
||||
{
|
||||
return ThingsGatewayStringConverter.Default.Serialize(null, property.GetValue(propertyName, false));
|
||||
return ThingsGatewayStringConverter.Default.Serialize(null, businessBase.VariablePropertys.GetValue(propertyName, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否存在对应的业务设备Id
|
||||
if (variableRuntime.VariablePropertys?.ContainsKey(businessId) == true)
|
||||
{
|
||||
variableRuntime.VariablePropertys[businessId].TryGetValue(propertyName, out var value);
|
||||
return value; // 返回属性值
|
||||
}
|
||||
|
||||
return null; // 未找到对应的业务设备Id,返回null
|
||||
}
|
||||
@@ -127,3 +133,8 @@ public interface IDynamicModel
|
||||
{
|
||||
IEnumerable<dynamic> GetList(IEnumerable<object> datas);
|
||||
}
|
||||
public abstract class DynamicModelBase : IDynamicModel
|
||||
{
|
||||
public TouchSocket.Core.ILog Logger { get; set; }
|
||||
public abstract IEnumerable<dynamic> GetList(IEnumerable<object> datas);
|
||||
}
|
||||
|
@@ -40,6 +40,28 @@ public delegate void VariableCollectEventHandler(VariableRuntime variableRuntime
|
||||
/// </summary>
|
||||
public delegate void VariableAlarmEventHandler(AlarmVariable alarmVariable);
|
||||
|
||||
public delegate void PluginEventHandler(PluginEventData data);
|
||||
|
||||
public class PluginEventData
|
||||
{
|
||||
public string DeviceName { get; set; }
|
||||
public Newtonsoft.Json.Linq.JObject Value { get; set; }
|
||||
public string ValueType { get; set; }
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public object ObjectValue { get; set; }
|
||||
//public object ObjectValue => ValueType.IsNullOrEmpty() ? Value : Value.ToObject(Type.GetType(ValueType, false) ?? typeof(JObject));
|
||||
|
||||
public PluginEventData(string deviceName, object value)
|
||||
{
|
||||
DeviceName = deviceName;
|
||||
ObjectValue = value;
|
||||
Value = Newtonsoft.Json.Linq.JObject.FromObject(value);
|
||||
ValueType = $"{value?.GetType().FullName},{value?.GetType().Assembly.GetName().Name}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 采集设备值与状态全局提供类,用于提供全局的设备状态和变量数据的管理
|
||||
/// </summary>
|
||||
@@ -64,6 +86,8 @@ public static class GlobalData
|
||||
/// </summary>
|
||||
public static event VariableAlarmEventHandler? AlarmChangedEvent;
|
||||
|
||||
public static event PluginEventHandler? PluginEventHandler;
|
||||
|
||||
public static async Task<IEnumerable<ChannelRuntime>> GetCurrentUserChannels()
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
@@ -480,6 +504,14 @@ public static class GlobalData
|
||||
AlarmChangedEvent?.Invoke(alarmVariable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件状态变化处理方法,用于处理事件状态变化时的逻辑
|
||||
/// </summary>
|
||||
public static void PluginEventChange(PluginEventData pluginEventData)
|
||||
{
|
||||
// 触发设备状态变化事件,并将设备运行时对象转换为设备数据对象进行传递
|
||||
PluginEventHandler?.Invoke(pluginEventData);
|
||||
}
|
||||
/// <summary>
|
||||
/// 设备状态变化处理方法,用于处理设备状态变化时的逻辑
|
||||
/// </summary>
|
||||
|
@@ -1,4 +1,21 @@
|
||||
{
|
||||
|
||||
"ThingsGateway.Gateway.Application.BusinessVariableProperty": {
|
||||
|
||||
"Data1": "Data1",
|
||||
"Data2": "Data2",
|
||||
"Data3": "Data3",
|
||||
"Data4": "Data4",
|
||||
"Data5": "Data5",
|
||||
"Data6": "Data6",
|
||||
"Data7": "Data7",
|
||||
"Data8": "Data8",
|
||||
"Data9": "Data9",
|
||||
"Data10": "Data10"
|
||||
},
|
||||
|
||||
|
||||
|
||||
"ThingsGateway.Management.Application.ManagementExportString": {
|
||||
|
||||
"ManagementConfigName": "ManagementConfigName"
|
||||
@@ -30,6 +47,7 @@
|
||||
|
||||
"Actuator": "Actuator",
|
||||
"AlarmChangedTriggerNode": "AlarmStateTrigger",
|
||||
"PluginEventChangedTriggerNode": "PluginEventTrigger",
|
||||
"BusinessNode": "BusinessDeviceExecution",
|
||||
"BusinessNode.Placeholder": "BusinessDeviceName",
|
||||
"Cancel": "Cancel",
|
||||
@@ -225,15 +243,19 @@
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BusinessPropertyWithCacheIntervalScript": {
|
||||
"AlarmTopic": "AlarmTopic",
|
||||
"PluginEventDataTopic": "PluginEventDataTopic",
|
||||
"BigTextScriptAlarmModel": "BigTextScriptAlarmModel",
|
||||
"BigTextScriptPluginEventDataModel": "BigTextScriptPluginEventDataModel",
|
||||
"BigTextScriptDeviceModel": "BigTextScriptDeviceModel",
|
||||
"BigTextScriptVariableModel": "BigTextScriptVariableModel",
|
||||
"DetailLog": "DetailLog",
|
||||
"DeviceTopic": "DeviceTopic",
|
||||
"IsAlarmList": "IsAlarmList",
|
||||
"IsPluginEventDataList": "IsPluginEventDataList",
|
||||
"IsDeviceList": "IsDeviceList",
|
||||
"IsVariableList": "IsVariableList",
|
||||
"JsonFormattingIndented": "JsonFormattingIndented",
|
||||
"JsonIgnoreNull": "JsonIgnoreNull",
|
||||
"VariableTopic": "VariableTopic"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BusinessUpdateEnum": {
|
||||
|
@@ -1,4 +1,18 @@
|
||||
{
|
||||
"ThingsGateway.Gateway.Application.BusinessVariableProperty": {
|
||||
|
||||
"Data1": "备注1",
|
||||
"Data2": "备注2",
|
||||
"Data3": "备注3",
|
||||
"Data4": "备注4",
|
||||
"Data5": "备注5",
|
||||
"Data6": "备注6",
|
||||
"Data7": "备注7",
|
||||
"Data8": "备注8",
|
||||
"Data9": "备注9",
|
||||
"Data10": "备注10"
|
||||
},
|
||||
|
||||
"ThingsGateway.Management.Application.ManagementExportString": {
|
||||
|
||||
"ManagementConfigName": "通讯配置"
|
||||
@@ -32,6 +46,7 @@
|
||||
|
||||
"Actuator": "执行",
|
||||
"AlarmChangedTriggerNode": "报警状态触发器",
|
||||
"PluginEventChangedTriggerNode": "插件事件触发器",
|
||||
"BusinessNode": "业务设备执行",
|
||||
"BusinessNode.Placeholder": "设备名称",
|
||||
"Cancel": "取消",
|
||||
@@ -227,15 +242,19 @@
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BusinessPropertyWithCacheIntervalScript": {
|
||||
"AlarmTopic": "报警主题",
|
||||
"PluginEventDataTopic": "插件事件主题",
|
||||
"BigTextScriptAlarmModel": "报警上传脚本",
|
||||
"BigTextScriptPluginEventDataModel": "插件事件上传脚本",
|
||||
"BigTextScriptDeviceModel": "设备上传脚本",
|
||||
"BigTextScriptVariableModel": "变量上传脚本",
|
||||
"DetailLog": "详细日志",
|
||||
"DeviceTopic": "设备主题",
|
||||
"IsAlarmList": "报警列表上传",
|
||||
"IsPluginEventDataList": "插件事件列表上传",
|
||||
"IsDeviceList": "设备状态列表上传",
|
||||
"IsVariableList": "变量列表上传",
|
||||
"JsonFormattingIndented": "Json缩进格式化",
|
||||
"JsonIgnoreNull": "Json忽略null值",
|
||||
"VariableTopic": "变量主题"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BusinessUpdateEnum": {
|
||||
|
@@ -53,6 +53,7 @@ public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a => AlarmHostedService_OnAlarmChanged(a.Value));
|
||||
GlobalData.AlarmChangedEvent += AlarmHostedService_OnAlarmChanged;
|
||||
}
|
||||
|
||||
private static void AlarmHostedService_OnAlarmChanged(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(alarmVariable.DeviceName, out var alarmNodeDict) &&
|
||||
|
@@ -39,6 +39,7 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
|
||||
static DeviceChangedTriggerNode()
|
||||
{
|
||||
GlobalData.DeviceStatusChangeEvent -= GlobalData_DeviceStatusChangeEvent;
|
||||
Task.Factory.StartNew(RunAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
GlobalData.DeviceStatusChangeEvent += GlobalData_DeviceStatusChangeEvent;
|
||||
}
|
||||
|
@@ -0,0 +1,112 @@
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using ThingsGateway.Blazor.Diagrams.Core.Geometry;
|
||||
using ThingsGateway.Common.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = $"{INode.ModuleBasePath}img/ValueChanged.svg", Desc = nameof(PluginEventChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.DeviceWidget,{INode.FileModulePath}")]
|
||||
public class PluginEventChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public PluginEventChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "PluginEventChangedTriggerNode"; Placeholder = "Device.Placeholder"; }
|
||||
|
||||
|
||||
#if !Management
|
||||
private Func<NodeOutput, CancellationToken, Task> Func { get; set; }
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
Func = func;
|
||||
FuncDict.Add(this, func);
|
||||
if (!DeviceChangedTriggerNodeDict.TryGetValue(Text ?? string.Empty, out var list))
|
||||
{
|
||||
var deviceChangedTriggerNodes = new ConcurrentList<PluginEventChangedTriggerNode>();
|
||||
deviceChangedTriggerNodes.Add(this);
|
||||
DeviceChangedTriggerNodeDict.Add(Text, deviceChangedTriggerNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(this);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public static Dictionary<string, ConcurrentList<PluginEventChangedTriggerNode>> DeviceChangedTriggerNodeDict = new();
|
||||
public static Dictionary<PluginEventChangedTriggerNode, Func<NodeOutput, CancellationToken, Task>> FuncDict = new();
|
||||
|
||||
public static BlockingCollection<PluginEventData> DeviceDatas = new();
|
||||
|
||||
static PluginEventChangedTriggerNode()
|
||||
{
|
||||
GlobalData.PluginEventHandler -= GlobalData_PluginEventHandler;
|
||||
Task.Factory.StartNew(RunAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
GlobalData.PluginEventHandler += GlobalData_PluginEventHandler;
|
||||
}
|
||||
|
||||
private static void GlobalData_PluginEventHandler(PluginEventData pluginEventData)
|
||||
{
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(pluginEventData.DeviceName ?? string.Empty, out var deviceChangedTriggerNodes) && deviceChangedTriggerNodes?.Count > 0)
|
||||
{
|
||||
if (!DeviceDatas.IsAddingCompleted)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeviceDatas.Add(pluginEventData);
|
||||
return;
|
||||
}
|
||||
catch (InvalidOperationException) { }
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Task RunAsync()
|
||||
{
|
||||
return DeviceDatas.GetConsumingEnumerable().ParallelForEachStreamedAsync((async (plgunEventData, token) =>
|
||||
{
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(plgunEventData.DeviceName ?? string.Empty, out var valueChangedTriggerNodes))
|
||||
{
|
||||
await valueChangedTriggerNodes.ParallelForEachAsync(async (item, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (FuncDict.TryGetValue(item, out var func))
|
||||
{
|
||||
item.Logger?.Trace($"Device changed: {item.Text}");
|
||||
await func.Invoke(new NodeOutput() { Value = plgunEventData }, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
item.Logger?.LogWarning(ex);
|
||||
}
|
||||
}, token).ConfigureAwait(false);
|
||||
}
|
||||
}), default);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FuncDict.Remove(this);
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(Text ?? string.Empty, out var list))
|
||||
{
|
||||
list.Remove(this);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
@@ -46,6 +46,7 @@ public class ValueChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
public static BlockingCollection<VariableBasicData> VariableBasicDatas = new();
|
||||
static ValueChangedTriggerNode()
|
||||
{
|
||||
GlobalData.VariableValueChangeEvent -= GlobalData_VariableValueChangeEvent;
|
||||
Task.Factory.StartNew(RunAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
GlobalData.VariableValueChangeEvent += GlobalData_VariableValueChangeEvent;
|
||||
}
|
||||
|
@@ -11,8 +11,8 @@
|
||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="4.0.0-beta.1" />
|
||||
<PackageReference Include="TouchSocket.WebApi.Swagger" Version="4.0.0-beta.1" />
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="4.0.0-beta.3" />
|
||||
<PackageReference Include="TouchSocket.WebApi.Swagger" Version="4.0.0-beta.3" />
|
||||
<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" />
|
||||
<!--<ProjectReference Include="..\..\PluginPro\ThingsGateway.Authentication\ThingsGateway.Authentication.csproj" />-->
|
||||
|
||||
|
@@ -55,6 +55,7 @@
|
||||
|
||||
"Actuator": "Actuator",
|
||||
"AlarmChangedTriggerNode": "AlarmStateTrigger",
|
||||
"PluginEventChangedTriggerNode": "PluginEventTrigger",
|
||||
"BusinessNode": "BusinessDeviceExecution",
|
||||
"BusinessNode.Placeholder": "BusinessDeviceName",
|
||||
"Cancel": "Cancel",
|
||||
|
@@ -55,6 +55,7 @@
|
||||
|
||||
"Actuator": "执行",
|
||||
"AlarmChangedTriggerNode": "报警状态触发器",
|
||||
"PluginEventChangedTriggerNode": "插件事件触发器",
|
||||
"BusinessNode": "业务设备执行",
|
||||
"BusinessNode.Placeholder": "设备名称",
|
||||
"Cancel": "取消",
|
||||
|
@@ -8,8 +8,12 @@
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Razor;
|
||||
public partial class PropertyComponent : IPropertyUIBase
|
||||
{
|
||||
@@ -69,11 +73,16 @@ public partial class PropertyComponent : IPropertyUIBase
|
||||
{nameof(ScriptCheck.Script),script },
|
||||
{nameof(ScriptCheck.GetResult), (string input,string script)=>
|
||||
{
|
||||
StringBuilder stringBuilder=new();
|
||||
var logger=new EasyLogger((a)=>stringBuilder.AppendLine(a));
|
||||
|
||||
|
||||
var type= script == businessProperty.BigTextScriptDeviceModel?typeof(List<DeviceBasicData>):script ==businessProperty.BigTextScriptAlarmModel?typeof(List<AlarmVariable>):typeof(List<VariableBasicData>);
|
||||
|
||||
var data = (IEnumerable<object>)Newtonsoft.Json.JsonConvert.DeserializeObject(input, type);
|
||||
var value = data.GetDynamicModel(script);
|
||||
return Task.FromResult( value.ToSystemTextJsonString());
|
||||
var value = data.GetDynamicModel(script,logger);
|
||||
stringBuilder.AppendLine(value.ToSystemTextJsonString());
|
||||
return Task.FromResult( stringBuilder.ToString());
|
||||
}},
|
||||
|
||||
{nameof(ScriptCheck.OnGetDemo),()=>
|
||||
|
@@ -111,9 +111,12 @@
|
||||
{
|
||||
"Url": "/InovanceMaster",
|
||||
"Text": "InovanceMaster"
|
||||
},
|
||||
{
|
||||
"Url": "/OpcAeMaster",
|
||||
"Text": "OpcAeMaster"
|
||||
}
|
||||
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -22,7 +22,7 @@ public abstract class DynamicSQLBase
|
||||
/// <returns></returns>
|
||||
public virtual Task DBInit(ISqlSugarClient db, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
@@ -1,60 +0,0 @@
|
||||
////------------------------------------------------------------------------------
|
||||
////此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
//// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
//// 使用文档:https://thingsgateway.cn/
|
||||
//// QQ群:605534569
|
||||
//// ------------------------------------------------------------------------------
|
||||
|
||||
//using ThingsGateway.SqlSugar;
|
||||
|
||||
//using ThingsGateway;
|
||||
//using ThingsGateway.Foundation;
|
||||
//using ThingsGateway.NewLife.Extension;
|
||||
//using ThingsGateway.Plugin.DB;
|
||||
//using ThingsGateway.Plugin.SqlDB;
|
||||
|
||||
//public class TestHistorySQL : DynamicSQLBase
|
||||
//{
|
||||
// public override IEnumerable<dynamic> GetList(IEnumerable<object> datas)
|
||||
// {
|
||||
// var sqlHistoryValues = datas.Cast<SQLHistoryValue>().OrderByDescending(a => a.CollectTime).DistinctBy(a => a.Name);
|
||||
// List<HistoryModel1> demoDatas = new List<HistoryModel1>();
|
||||
// HistoryModel1 demoData = new HistoryModel1();
|
||||
// demoData.IsOnline = !sqlHistoryValues.Any(a => !a.IsOnline);
|
||||
// demoData.CreateTime = DateTime.Now;
|
||||
// var dict = sqlHistoryValues.ToDictionary(a => a.Name);
|
||||
// demoData.Temp1 = dict["Device1_Temp1"].Value;
|
||||
// demoData.Temp2 = dict["Device1_Temp2"].Value;
|
||||
// demoData.Temp3 = dict["Device1_Temp3"].Value;
|
||||
// demoDatas.Add(demoData);
|
||||
// return demoDatas;
|
||||
// }
|
||||
|
||||
// public override Type GetModelType()
|
||||
// {
|
||||
// return typeof(HistoryModel1);
|
||||
// }
|
||||
// [SplitTable(SplitType.Month)]//(自带分表支持 年、季、月、周、日)
|
||||
// [SugarTable("HistoryModel1_{year}{month}{day}", TableDescription = "设备采集历史表")]//3个变量必须要有
|
||||
// [SugarIndex("index_CreateTime", nameof(SQLHistoryValue.CreateTime), OrderByType.Desc)]
|
||||
// public class HistoryModel1
|
||||
// {
|
||||
// [SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表
|
||||
// public DateTime CreateTime { get; set; }
|
||||
|
||||
// ///<summary>
|
||||
// ///是否在线
|
||||
// ///</summary>
|
||||
// [SugarColumn(ColumnDescription = "是否在线")]
|
||||
// public bool IsOnline { get; set; }
|
||||
|
||||
// public string Temp1 { get; set; }
|
||||
|
||||
// public string Temp2 { get; set; }
|
||||
|
||||
// public string Temp3 { get; set; }
|
||||
// }
|
||||
//}
|
@@ -1,65 +0,0 @@
|
||||
//// ------------------------------------------------------------------------------
|
||||
//// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
//// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
//// 使用文档:https://thingsgateway.cn/
|
||||
//// QQ群:605534569
|
||||
//// ------------------------------------------------------------------------------
|
||||
|
||||
//using ThingsGateway.SqlSugar;
|
||||
|
||||
//using ThingsGateway;
|
||||
//using ThingsGateway.Foundation;
|
||||
//using ThingsGateway.NewLife.Extension;
|
||||
//using ThingsGateway.Plugin.DB;
|
||||
//using ThingsGateway.Plugin.SqlDB;
|
||||
|
||||
//public class TestRealSQL : DynamicSQLBase
|
||||
//{
|
||||
// public override IEnumerable<dynamic> GetList(IEnumerable<object> datas)
|
||||
// {
|
||||
// var sqlRealValues = datas.Cast<SQLRealValue>().OrderByDescending(a => a.CollectTime).DistinctBy(a => a.Name);
|
||||
// List<RealModel1> demoDatas = new List<RealModel1>();
|
||||
// RealModel1 demoData = new RealModel1();
|
||||
// demoData.IsOnline = !sqlRealValues.Any(a => !a.IsOnline);
|
||||
// demoData.CollectTime = sqlRealValues.Select(a => a.CollectTime).Max();
|
||||
// demoData.EmptyId = 1;
|
||||
// var dict = sqlRealValues.ToDictionary(a => a.Name);
|
||||
// demoData.Temp1 = dict["Device1_Temp1"].Value;
|
||||
// demoData.Temp2 = dict["Device1_Temp2"].Value;
|
||||
// demoData.Temp3 = dict["Device1_Temp3"].Value;
|
||||
// demoDatas.Add(demoData);
|
||||
// return demoDatas;
|
||||
// }
|
||||
|
||||
// public override Type GetModelType()
|
||||
// {
|
||||
// return typeof(RealModel1);
|
||||
|
||||
// }
|
||||
|
||||
// [SugarTable(TableName = "RealModel1", TableDescription = "设备采集实时表")]
|
||||
// [SugarIndex("{table}_index_CollectTime", nameof(SQLRealValue.CollectTime), OrderByType.Desc)]
|
||||
// public class RealModel1
|
||||
// {
|
||||
// [SugarColumn(IsPrimaryKey = true)]
|
||||
// public long EmptyId { get; set; }
|
||||
|
||||
// [SugarColumn(ColumnDescription = "采集时间")]
|
||||
// public DateTime CollectTime { get; set; }
|
||||
|
||||
// ///<summary>
|
||||
// ///是否在线
|
||||
// ///</summary>
|
||||
// [SugarColumn(ColumnDescription = "是否在线")]
|
||||
// public bool IsOnline { get; set; }
|
||||
|
||||
// public string Temp1 { get; set; }
|
||||
|
||||
// public string Temp2 { get; set; }
|
||||
|
||||
// public string Temp3 { get; set; }
|
||||
// }
|
||||
//}
|
@@ -64,7 +64,7 @@
|
||||
"Name": "Name",
|
||||
"Value": "Value"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlHistoryAlarm.HistoryAlarm": {
|
||||
"ThingsGateway.Plugin.DB.HistoryAlarm": {
|
||||
"AlarmCode": "AlarmCode",
|
||||
"AlarmLimit": "AlarmLimit",
|
||||
"AlarmText": "AlarmText",
|
||||
@@ -86,10 +86,14 @@
|
||||
"Remark4": "Remark4",
|
||||
"Remark5": "Remark5"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlHistoryAlarm.SQLHistoryAlarmProperty": {
|
||||
"ThingsGateway.Plugin.DB.SQLHistoryAlarmProperty": {
|
||||
"BigTextConnectStr": "ConnectString",
|
||||
"DbType": "DbType",
|
||||
"TableName": "TableName"
|
||||
"TableName": "TableName",
|
||||
"VariableAlarmEnable": "VariableAlarmEnable",
|
||||
"PluginEventEnable": "PluginEventEnable",
|
||||
"BigTextScriptHistoryTable": "BigTextScriptHistoryTable",
|
||||
"BigTextScriptPluginEventDataHistoryTable": "BigTextScriptPluginEventDataHistoryTable"
|
||||
},
|
||||
"ThingsGateway.Plugin.TDengineDB.TDengineDBNumberHistoryValue": {
|
||||
"CollectTime": "CollectTime",
|
||||
|
@@ -64,7 +64,7 @@
|
||||
"Name": "变量名称",
|
||||
"Value": "变量值"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlHistoryAlarm.HistoryAlarm": {
|
||||
"ThingsGateway.Plugin.DB.HistoryAlarm": {
|
||||
"AlarmCode": "报警值",
|
||||
"AlarmLimit": "报警限值",
|
||||
"AlarmText": "报警文本",
|
||||
@@ -86,10 +86,14 @@
|
||||
"Remark4": "备用4",
|
||||
"Remark5": "备用5"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlHistoryAlarm.SQLHistoryAlarmProperty": {
|
||||
"ThingsGateway.Plugin.DB.SQLHistoryAlarmProperty": {
|
||||
"BigTextConnectStr": "链接字符串",
|
||||
"DbType": "数据库类型",
|
||||
"TableName": "表名称"
|
||||
"TableName": "表名称",
|
||||
"VariableAlarmEnable": "变量报警存储",
|
||||
"PluginEventEnable": "插件事件存储",
|
||||
"BigTextScriptHistoryTable": "变量报警脚本",
|
||||
"BigTextScriptPluginEventDataHistoryTable": "插件事件脚本"
|
||||
},
|
||||
"ThingsGateway.Plugin.TDengineDB.TDengineDBNumberHistoryValue": {
|
||||
"CollectTime": "采集时间",
|
||||
|
@@ -11,7 +11,6 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
using ThingsGateway.DB;
|
||||
using ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)]
|
||||
|
@@ -10,6 +10,6 @@
|
||||
|
||||
namespace ThingsGateway.Plugin.QuestDB;
|
||||
|
||||
public class QuestDBProducerVariableProperty : VariablePropertyBase
|
||||
public class QuestDBProducerVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
@@ -15,19 +15,19 @@
|
||||
@ref=Model.ValidateForm
|
||||
Id=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")>
|
||||
|
||||
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems IsDisplay="!CanWrite" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=@(CanWrite?2:3) ShowLabelTooltip=true LabelWidth=@(CanWrite?240:120) Model="Model.Value" ShowLabel="true" @key=@($"DeviceEditEditorFormObject{Id}{Model.Value.GetType().TypeHandle.Value}")>
|
||||
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems IsDisplay="!CanWrite" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=@(CanWrite ? 2 : 3) ShowLabelTooltip=true LabelWidth=@(CanWrite ? 240 : 120) Model="Model.Value" ShowLabel="true" @key=@($"DeviceEditEditorFormObject{Id}{Model.Value.GetType().TypeHandle.Value}")>
|
||||
|
||||
<FieldItems>
|
||||
@if (Model.Value is SqlDBProducerProperty businessProperty)
|
||||
{
|
||||
<EditorItem FieldExpression=@(()=>context) Field=@(context)>
|
||||
<EditorItem FieldExpression=@(() => context) Field=@(context)>
|
||||
|
||||
<EditTemplate Context="value">
|
||||
<div class="col-12 col-md-12">
|
||||
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
|
||||
<CodeEditor @bind-Value=@businessProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" />
|
||||
<div class="ms-2 d-flex justify-content-center align-items-center">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptHistoryTable))">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick="() => CheckScript(businessProperty, nameof(businessProperty.BigTextScriptHistoryTable))">
|
||||
@RazorLocalizer["Check"]
|
||||
</Button>
|
||||
</div>
|
||||
@@ -36,7 +36,7 @@
|
||||
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptRealTable"] ShowLabelTooltip="true" />
|
||||
<CodeEditor @bind-Value=@businessProperty.BigTextScriptRealTable Language="csharp" Theme="vs-dark" />
|
||||
<div class="ms-2 d-flex justify-content-center align-items-center">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptRealTable))">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick="() => CheckScript(businessProperty, nameof(businessProperty.BigTextScriptRealTable))">
|
||||
@RazorLocalizer["Check"]
|
||||
</Button>
|
||||
</div>
|
||||
@@ -62,7 +62,22 @@
|
||||
</EditTemplate>
|
||||
</EditorItem>
|
||||
}
|
||||
else if (Model.Value is SqlHistoryAlarmProperty sqlHistoryAlarmProperty)
|
||||
{
|
||||
<EditorItem FieldExpression=@(() => context) Field=@(context)>
|
||||
|
||||
<EditTemplate Context="value">
|
||||
<div class="col-12 col-md-12">
|
||||
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
|
||||
<CodeEditor @bind-Value=@sqlHistoryAlarmProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" />
|
||||
</div>
|
||||
<div class="col-12 col-md-12">
|
||||
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptPluginEventDataHistoryTable"] ShowLabelTooltip="true" />
|
||||
<CodeEditor @bind-Value=@sqlHistoryAlarmProperty.BigTextScriptPluginEventDataHistoryTable Language="csharp" Theme="vs-dark" />
|
||||
</div>
|
||||
</EditTemplate>
|
||||
</EditorItem>
|
||||
}
|
||||
</FieldItems>
|
||||
</EditorFormObject>
|
||||
</ValidateForm>
|
||||
|
@@ -13,6 +13,6 @@ namespace ThingsGateway.Plugin.SqlDB;
|
||||
/// <summary>
|
||||
/// SqlDBProducerVariableProperty
|
||||
/// </summary>
|
||||
public class SqlDBProducerVariableProperty : VariablePropertyBase
|
||||
public class SqlDBProducerVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
@@ -12,7 +12,7 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
public class HistoryAlarmPageInput : ITableSearchModel
|
||||
{
|
||||
|
@@ -10,7 +10,7 @@
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
[SugarTable("historyAlarm", TableDescription = "历史报警表")]
|
||||
public class HistoryAlarm : AlarmVariable
|
||||
|
@@ -6,7 +6,7 @@
|
||||
@using ThingsGateway.Admin.Razor
|
||||
@using ThingsGateway.Extension
|
||||
@using ThingsGateway.Gateway.Application
|
||||
@namespace ThingsGateway.Plugin.SqlHistoryAlarm
|
||||
@namespace ThingsGateway.Plugin.DB
|
||||
|
||||
|
||||
|
||||
|
@@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Components;
|
||||
|
||||
using ThingsGateway.Common;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// HistoryAlarmPage
|
||||
|
@@ -12,12 +12,13 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using ThingsGateway.Common;
|
||||
using ThingsGateway.DB;
|
||||
using ThingsGateway.Debug;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// SqlHistoryAlarm
|
||||
@@ -55,7 +56,7 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
|
||||
private SqlSugarClient _db;
|
||||
|
||||
|
||||
public override Type DriverPropertyUIType => typeof(SqlDBProducerPropertyRazor);
|
||||
|
||||
protected override Task DisposeAsync(bool disposing)
|
||||
{
|
||||
@@ -66,6 +67,7 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
#if !Management
|
||||
protected override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
_db = BusinessDatabaseUtil.GetDb((DbType)_driverPropertys.DbType, _driverPropertys.BigTextConnectStr);
|
||||
|
||||
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
|
||||
@@ -76,11 +78,33 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override bool IsConnected() => success;
|
||||
protected override Task ProtectedStartAsync(CancellationToken cancellationToken)
|
||||
protected override async Task ProtectedStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_db.DbMaintenance.CreateDatabase();
|
||||
_db.CodeFirst.AS<HistoryAlarm>(_driverPropertys.TableName).InitTables<HistoryAlarm>();
|
||||
return base.ProtectedStartAsync(cancellationToken);
|
||||
if (_driverPropertys.VariableAlarmEnable)
|
||||
{
|
||||
if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var hisModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptHistoryTable);
|
||||
|
||||
await hisModel.DBInit(_db, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_db.CodeFirst.AS<HistoryAlarm>(_driverPropertys.TableName).InitTables<HistoryAlarm>();
|
||||
}
|
||||
}
|
||||
if (_driverPropertys.PluginEventEnable)
|
||||
{
|
||||
if (!_driverPropertys.BigTextScriptPluginEventDataHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var hisModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptPluginEventDataHistoryTable);
|
||||
|
||||
await hisModel.DBInit(_db, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#region 数据查询
|
||||
|
@@ -11,11 +11,10 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Plugin.DB;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// SqlHistoryAlarm
|
||||
@@ -23,8 +22,61 @@ namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
{
|
||||
#if !Management
|
||||
|
||||
protected override void PluginChange(PluginEventData value)
|
||||
{
|
||||
if (_driverPropertys.PluginEventEnable == false)
|
||||
return;
|
||||
AddQueuePluginDataModel(new CacheDBItem<PluginEventData>(value));
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdatePluginEventDataModel(item.Select(a => a.Value), cancellationToken);
|
||||
}
|
||||
private async ValueTask<OperResult> UpdatePluginEventDataModel(IEnumerable<PluginEventData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await InserableAsync(item.ToList(), cancellationToken).ConfigureAwait(false);
|
||||
if (success != result.IsSuccess)
|
||||
{
|
||||
if (!result.IsSuccess)
|
||||
LogMessage?.LogWarning(result.ToString());
|
||||
success = result.IsSuccess;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
private async ValueTask<OperResult> InserableAsync(List<PluginEventData> dbInserts, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_db.Ado.CancellationToken = cancellationToken;
|
||||
if (!_driverPropertys.BigTextScriptPluginEventDataHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptPluginEventDataHistoryTable);
|
||||
|
||||
getDeviceModel.Logger = LogMessage;
|
||||
|
||||
await getDeviceModel.DBInsertable(_db, dbInserts, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult("Script must be configured");
|
||||
}
|
||||
|
||||
return OperResult.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void AlarmChange(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (_driverPropertys.VariableAlarmEnable == false)
|
||||
return;
|
||||
AddQueueAlarmModel(new CacheDBItem<AlarmVariable>(alarmVariable));
|
||||
}
|
||||
|
||||
@@ -87,5 +139,7 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@@ -14,7 +14,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using ThingsGateway.Plugin.SqlDB;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
@@ -26,10 +26,17 @@ public class SqlHistoryAlarmProperty : BusinessPropertyWithCache
|
||||
[DynamicProperty]
|
||||
[Required]
|
||||
public string TableName { get; set; } = "historyAlarm";
|
||||
|
||||
[DynamicProperty]
|
||||
public bool VariableAlarmEnable { get; set; } = true;
|
||||
|
||||
[DynamicProperty]
|
||||
public bool PluginEventEnable { get; set; } = false;
|
||||
|
||||
[DynamicProperty]
|
||||
[Required]
|
||||
[AutoGenerateColumn(ComponentType = typeof(Textarea), Rows = 1)]
|
||||
public string BigTextConnectStr { get; set; } = "server=.;uid=sa;pwd=111111;database=test;";
|
||||
public string BigTextConnectStr { get; set; } = "server=.;uid=sa;pwd=111111;database=test;Encrypt=True;TrustServerCertificate=True;";
|
||||
|
||||
/// <summary>
|
||||
/// 历史表脚本
|
||||
@@ -38,5 +45,15 @@ public class SqlHistoryAlarmProperty : BusinessPropertyWithCache
|
||||
[AutoGenerateColumn(Visible = true, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false)]
|
||||
public string? BigTextScriptHistoryTable { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 历史表脚本
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
[AutoGenerateColumn(Visible = true, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false)]
|
||||
public string? BigTextScriptPluginEventDataHistoryTable { get; set; }
|
||||
|
||||
public override bool OnlineFilter { get; set; } = false;
|
||||
}
|
||||
|
@@ -8,11 +8,11 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public class SqlHistoryAlarmVariableProperty : VariablePropertyBase
|
||||
public class SqlHistoryAlarmVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
@@ -10,6 +10,6 @@
|
||||
|
||||
namespace ThingsGateway.Plugin.TDengineDB;
|
||||
|
||||
public class TDengineDBProducerVariableProperty : VariablePropertyBase
|
||||
public class TDengineDBProducerVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
@@ -23,13 +23,41 @@ namespace ThingsGateway.Plugin.Webhook;
|
||||
public partial class Webhook : BusinessBaseWithCacheIntervalScriptAll
|
||||
{
|
||||
#if !Management
|
||||
|
||||
protected override void AlarmChange(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.AlarmTopic.IsNullOrWhiteSpace())
|
||||
AddQueueAlarmModel(new(alarmVariable));
|
||||
base.AlarmChange(alarmVariable);
|
||||
}
|
||||
protected override ValueTask<OperResult> UpdateAlarmModel(List<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateAlarmModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var topicArrayList = GetAlarmTopicArrays(item);
|
||||
return Update(topicArrayList, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void PluginChange(PluginEventData pluginEventData)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.PluginEventDataTopic.IsNullOrWhiteSpace())
|
||||
AddQueuePluginDataModel(new(pluginEventData));
|
||||
base.PluginChange(pluginEventData);
|
||||
}
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdatePluginEventDataModel(item.Select(a => a.Value), cancellationToken);
|
||||
}
|
||||
private ValueTask<OperResult> UpdatePluginEventDataModel(IEnumerable<PluginEventData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var topicArrayList = GetPluginEventDataTopicArrays(item);
|
||||
return Update(topicArrayList, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void DeviceTimeInterval(DeviceRuntime deviceRunTime, DeviceBasicData deviceData)
|
||||
{
|
||||
@@ -46,10 +74,7 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScriptAll
|
||||
base.DeviceChange(deviceRunTime, deviceData);
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdateAlarmModel(List<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdateAlarmModel(item.Select(a => a.Value).OrderBy(a => a.Id), cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
protected override ValueTask<OperResult> UpdateDevModel(List<CacheDBItem<DeviceBasicData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -189,11 +214,7 @@ public partial class Webhook : BusinessBaseWithCacheIntervalScriptAll
|
||||
return OperResult.Success;
|
||||
}
|
||||
|
||||
private ValueTask<OperResult> UpdateAlarmModel(IEnumerable<AlarmVariable> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var topicArrayList = GetAlarmTopicArrays(item);
|
||||
return Update(topicArrayList, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
private ValueTask<OperResult> UpdateDevModel(IEnumerable<DeviceBasicData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@@ -13,6 +13,6 @@ namespace ThingsGateway.Plugin.Webhook;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public class WebhookVariableProperty : VariablePropertyBase
|
||||
public class WebhookVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
||||
|
@@ -28,6 +28,27 @@ public partial class KafkaProducer : BusinessBaseWithCacheIntervalScriptAll
|
||||
private ProducerConfig _producerconfig;
|
||||
private volatile bool producerSuccess = true;
|
||||
|
||||
|
||||
|
||||
|
||||
protected override void PluginChange(PluginEventData pluginEventData)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.PluginEventDataTopic.IsNullOrWhiteSpace())
|
||||
AddQueuePluginDataModel(new(pluginEventData));
|
||||
base.PluginChange(pluginEventData);
|
||||
}
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdatePluginEventDataModel(item.Select(a => a.Value), cancellationToken);
|
||||
}
|
||||
private ValueTask<OperResult> UpdatePluginEventDataModel(IEnumerable<PluginEventData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var topicArrayList = GetPluginEventDataTopicArrays(item);
|
||||
return Update(topicArrayList, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void AlarmChange(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.AlarmTopic.IsNullOrWhiteSpace())
|
||||
|
@@ -13,6 +13,6 @@ namespace ThingsGateway.Plugin.Kafka;
|
||||
/// <summary>
|
||||
/// kafka 生产者可变属性
|
||||
/// </summary>
|
||||
public class KafkaProducerVariableProperty : VariablePropertyBase
|
||||
public class KafkaProducerVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
@@ -49,6 +49,26 @@ public partial class MqttClient : BusinessBaseWithCacheIntervalScriptAll
|
||||
|
||||
#if !Management
|
||||
|
||||
|
||||
|
||||
|
||||
protected override void PluginChange(PluginEventData pluginEventData)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.PluginEventDataTopic.IsNullOrWhiteSpace())
|
||||
AddQueuePluginDataModel(new(pluginEventData));
|
||||
base.PluginChange(pluginEventData);
|
||||
}
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdatePluginEventDataModel(item.Select(a => a.Value), cancellationToken);
|
||||
}
|
||||
private ValueTask<OperResult> UpdatePluginEventDataModel(IEnumerable<PluginEventData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var topicArrayList = GetPluginEventDataTopicArrays(item);
|
||||
return Update(topicArrayList, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
protected override void AlarmChange(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.AlarmTopic.IsNullOrWhiteSpace())
|
||||
|
@@ -13,7 +13,7 @@ namespace ThingsGateway.Plugin.Mqtt;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public class MqttClientVariableProperty : VariablePropertyBase
|
||||
public class MqttClientVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
/// <summary>
|
||||
/// 允许写入
|
||||
|
@@ -82,6 +82,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12 col-md-12 min-height-500">
|
||||
<BootstrapLabel Value=@Localizer["BigTextScriptPluginEventDataModel"] ShowLabelTooltip="true" />
|
||||
<CodeEditor IsReadonly=@(!CanWrite) ShowLineNo @bind-Value=@businessProperty.BigTextScriptPluginEventDataModel Language="csharp" Theme="vs-dark" />
|
||||
|
||||
</div>
|
||||
|
||||
</EditTemplate>
|
||||
</EditorItem>
|
||||
}
|
||||
|
@@ -41,6 +41,27 @@ public partial class MqttServer : BusinessBaseWithCacheIntervalScriptAll
|
||||
private MQTTnet.Server.MqttServer _mqttServer;
|
||||
private IWebHost _webHost { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
protected override void PluginChange(PluginEventData pluginEventData)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.PluginEventDataTopic.IsNullOrWhiteSpace())
|
||||
AddQueuePluginDataModel(new(pluginEventData));
|
||||
base.PluginChange(pluginEventData);
|
||||
}
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdatePluginEventDataModel(item.Select(a => a.Value), cancellationToken);
|
||||
}
|
||||
private ValueTask<OperResult> UpdatePluginEventDataModel(IEnumerable<PluginEventData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var topicArrayList = GetPluginEventDataTopicArrays(item);
|
||||
return Update(topicArrayList, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected override void AlarmChange(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.AlarmTopic.IsNullOrWhiteSpace())
|
||||
|
@@ -31,6 +31,26 @@ public partial class RabbitMQProducer : BusinessBaseWithCacheIntervalScriptAll
|
||||
private ConnectionFactory _connectionFactory;
|
||||
private IChannel _channel;
|
||||
|
||||
|
||||
|
||||
|
||||
protected override void PluginChange(PluginEventData pluginEventData)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.PluginEventDataTopic.IsNullOrWhiteSpace())
|
||||
AddQueuePluginDataModel(new(pluginEventData));
|
||||
base.PluginChange(pluginEventData);
|
||||
}
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdatePluginEventDataModel(item.Select(a => a.Value), cancellationToken);
|
||||
}
|
||||
private ValueTask<OperResult> UpdatePluginEventDataModel(IEnumerable<PluginEventData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var topicArrayList = GetPluginEventDataTopicArrays(item);
|
||||
return Update(topicArrayList, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
protected override void AlarmChange(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (!_businessPropertyWithCacheIntervalScript.AlarmTopic.IsNullOrWhiteSpace())
|
||||
|
@@ -13,6 +13,6 @@ namespace ThingsGateway.Plugin.RabbitMQ;
|
||||
/// <summary>
|
||||
/// RabbitMQProducerVariableProperty
|
||||
/// </summary>
|
||||
public class RabbitMQProducerVariableProperty : VariablePropertyBase
|
||||
public class RabbitMQProducerVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
@@ -3,7 +3,6 @@
|
||||
|
||||
//using ThingsGateway.Foundation;
|
||||
//using ThingsGateway.Gateway.Application;
|
||||
//using ThingsGateway.Razor;
|
||||
//namespace ThingsGateway.Server;
|
||||
|
||||
///// <summary>
|
||||
@@ -231,13 +230,9 @@
|
||||
// return base.ReadSourceAsync(variableSourceRead, cancellationToken);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
||||
// /// 插件释放
|
||||
// /// </summary>
|
||||
// /// <param name="disposing"></param>
|
||||
// protected override void Dispose(bool disposing)
|
||||
// protected override Task DisposeAsync(bool disposing)
|
||||
// {
|
||||
// base.Dispose(disposing);
|
||||
// return base.DisposeAsync(disposing);
|
||||
// }
|
||||
|
||||
// /// <summary>
|
56
src/ThingsGateway.Server/Test/TestDynamicModel.cs
Normal file
56
src/ThingsGateway.Server/Test/TestDynamicModel.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
////------------------------------------------------------------------------------
|
||||
////此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
//// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
//// 使用文档:https://thingsgateway.cn/
|
||||
//// QQ群:605534569
|
||||
//// ------------------------------------------------------------------------------
|
||||
|
||||
using System.Dynamic;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
public class TestDynamicModel : IDynamicModel
|
||||
{
|
||||
public IEnumerable<dynamic> GetList(IEnumerable<object> datas)
|
||||
{
|
||||
if (datas == null) return null;
|
||||
List<ExpandoObject> deviceObjs = new List<ExpandoObject>();
|
||||
//按设备名称分组
|
||||
var groups = datas.Where(a => !string.IsNullOrEmpty(((AlarmVariable)a).DeviceName)).GroupBy(a => ((AlarmVariable)a).DeviceName, a => ((AlarmVariable)a));
|
||||
foreach (var group in groups)
|
||||
{
|
||||
//按采集时间分组
|
||||
var data = group.GroupBy(a => a.AlarmTime.DateTimeToUnixTimestamp());
|
||||
var deviceObj = new ExpandoObject();
|
||||
List<ExpandoObject> expandos = new List<ExpandoObject>();
|
||||
foreach (var item in data)
|
||||
{
|
||||
var expando = new ExpandoObject();
|
||||
expando.TryAdd("ts", item.Key);
|
||||
var variableObj = new ExpandoObject();
|
||||
foreach (var tag in item)
|
||||
{
|
||||
var alarmObj = new ExpandoObject();
|
||||
alarmObj.TryAdd(nameof(tag.AlarmCode), tag.AlarmCode);
|
||||
alarmObj.TryAdd(nameof(tag.AlarmText), tag.AlarmText);
|
||||
alarmObj.TryAdd(nameof(tag.AlarmType), tag.AlarmType);
|
||||
alarmObj.TryAdd(nameof(tag.AlarmLimit), tag.AlarmLimit);
|
||||
alarmObj.TryAdd(nameof(tag.EventTime), tag.EventTime);
|
||||
alarmObj.TryAdd(nameof(tag.EventType), tag.EventType);
|
||||
|
||||
variableObj.TryAdd(tag.Name, alarmObj);
|
||||
}
|
||||
expando.TryAdd("alarms", variableObj);
|
||||
|
||||
expandos.Add(expando);
|
||||
}
|
||||
deviceObj.TryAdd(group.Key, expandos);
|
||||
deviceObjs.Add(deviceObj);
|
||||
}
|
||||
|
||||
return deviceObjs;
|
||||
}
|
||||
}
|
@@ -2,7 +2,6 @@
|
||||
//using System.Text;
|
||||
|
||||
//using ThingsGateway.Gateway.Application;
|
||||
//using ThingsGateway.RulesEngine;
|
||||
|
||||
//public class TestExexcuteExpressions : IExexcuteExpressions
|
||||
//{
|
223
src/ThingsGateway.Server/Test/TestKafkaDynamicModel.cs
Normal file
223
src/ThingsGateway.Server/Test/TestKafkaDynamicModel.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
////------------------------------------------------------------------------------
|
||||
////此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
//// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
//// 使用文档:https://thingsgateway.cn/
|
||||
//// QQ群:605534569
|
||||
//// ------------------------------------------------------------------------------
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
public class TestKafkaDynamicModel : DynamicModelBase
|
||||
{
|
||||
private Dictionary<string, VariableRuntime> variableRuntimes = new();
|
||||
|
||||
private long id = 0;
|
||||
|
||||
public TestKafkaDynamicModel()
|
||||
{
|
||||
var name = "kafka1";
|
||||
if (GlobalData.ReadOnlyDevices.TryGetValue(name, out var kafka1))
|
||||
{
|
||||
id = kafka1.Id;
|
||||
|
||||
foreach (var item in kafka1.Driver?.IdVariableRuntimes)
|
||||
{
|
||||
//变量备注1作为Key(AE报警SourceId)
|
||||
var data1 = item.Value.GetPropertyValue(id, nameof(BusinessVariableProperty.Data1));
|
||||
if (!data1.IsNullOrEmpty())
|
||||
{
|
||||
variableRuntimes.Add(data1, item.Value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"找不到设备 {name}");
|
||||
}
|
||||
|
||||
}
|
||||
public override IEnumerable<dynamic> GetList(IEnumerable<object> datas)
|
||||
{
|
||||
if (datas == null) return null;
|
||||
var pluginEventDatas = datas.Cast<PluginEventData>();
|
||||
var opcDatas = pluginEventDatas.Select(
|
||||
a =>
|
||||
{
|
||||
if (a.ObjectValue == null)
|
||||
{
|
||||
a.ObjectValue = a.Value.ToObject(Type.GetType(a.ValueType));
|
||||
}
|
||||
return a.ObjectValue is ThingsGateway.Plugin.OpcAe.OpcAeEventData opcData ? opcData : null;
|
||||
}
|
||||
).Where(a => a != null).ToList();
|
||||
|
||||
List<KafkaAlarmEntity> alarmEntities = new List<KafkaAlarmEntity>();
|
||||
if (opcDatas.Count == 0)
|
||||
{
|
||||
Logger?.LogInformation("没有OPCAE数据");
|
||||
return alarmEntities;
|
||||
}
|
||||
|
||||
|
||||
foreach (var opcAeEventData in opcDatas)
|
||||
{
|
||||
//一般只需要条件报警
|
||||
//if (opcAeEventData.EventType != Opc.Ae.EventType.Condition)
|
||||
// continue;
|
||||
//重连时触发的事件,可以跳过不处理
|
||||
//if(opcAeEventData.Refresh)
|
||||
// continue;
|
||||
var sourceName = opcAeEventData.SourceID;
|
||||
if (variableRuntimes.TryGetValue(sourceName, out var variableRuntime))
|
||||
{
|
||||
var ack = opcAeEventData.EventType != Opc.Ae.EventType.Condition ? false : ((Opc.Ae.ConditionState)opcAeEventData.NewState).HasFlag(Opc.Ae.ConditionState.Acknowledged);
|
||||
|
||||
bool isRecover = opcAeEventData.EventType != Opc.Ae.EventType.Condition ? false : !((Opc.Ae.ConditionState)opcAeEventData.NewState).HasFlag(Opc.Ae.ConditionState.Active);
|
||||
|
||||
//构建告警实体
|
||||
KafkaAlarmEntity alarmEntity = new KafkaAlarmEntity
|
||||
{
|
||||
AlarmCode = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data2)), //唯一编码
|
||||
ResourceCode = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data3)), //资源编码
|
||||
ResourceName = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data4)), //资源名称
|
||||
MetricCode = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data5)), //指标编码
|
||||
MetricName = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data6)), //指标名称
|
||||
Content = $"{variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data4))},{opcAeEventData.Message}", //告警内容,设备名称+告警内容(包含阈值信息),可能opcae里没有带阈值信息,那么就需要录入固定值,可选Data10
|
||||
AlarmType = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data7)), // opcAeEventData.Severity 告警类型,子系统产生告警的类型,可能需要固定备注值
|
||||
|
||||
ConfirmedTime = ack ? opcAeEventData.Time.DateTimeToUnixTimestamp() : null, //告警确认时间
|
||||
FixTime = isRecover ? opcAeEventData.Time.DateTimeToUnixTimestamp() : null, //解除告警时间
|
||||
LastTime = opcAeEventData.ActiveTime.DateTimeToUnixTimestamp(), //产生告警时间
|
||||
Status = isRecover ? "FIXED" : "UNFIXED", //告警状态
|
||||
AlarmLevel = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data8)), //opcAeEventData.Severity.ToString(), //告警等级,可能需要固定备注值
|
||||
SubSystemCode = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data9)), //子系统编码
|
||||
Type = "SUB_SYSTEM_ALARM", //默认填写字段
|
||||
ConfirmAccount = opcAeEventData.ActorID, //告警确认人
|
||||
ClearAccount = opcAeEventData.ActorID, //告警清除人
|
||||
ProcessInstruction = null //告警处理说明,OPCAE不带有
|
||||
};
|
||||
alarmEntities.Add(alarmEntity);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger?.LogInformation($"找不到相关变量{sourceName}");
|
||||
}
|
||||
}
|
||||
|
||||
return alarmEntities;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 告警实体
|
||||
/// </summary>
|
||||
public class KafkaAlarmEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 告警编码唯一 (非空)
|
||||
/// 示例:"8e8a118ac452fd04da8c26fa588a7cab"
|
||||
/// </summary>
|
||||
public string AlarmCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源编码,唯一编码,需要按照映射表上传 (非空)
|
||||
/// 示例:"RS_A6K9MUSG19V"
|
||||
/// </summary>
|
||||
public string ResourceCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源名称,需要按照映射表上传 (非空)
|
||||
/// 示例:"MB-A7"
|
||||
/// </summary>
|
||||
public string ResourceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 指标编码唯一,需要按照映射表上传 (非空)
|
||||
/// 示例:"ActivePowerPa"
|
||||
/// </summary>
|
||||
public string MetricCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 指标名称,需要按照映射表上传 (非空)
|
||||
/// 示例:"有功功率Pa"
|
||||
/// </summary>
|
||||
public string MetricName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 告警内容:设备名称+告警内容(包含阈值信息) (非空)
|
||||
/// 示例:"MB-A7,有功功率Pa > 30"
|
||||
/// </summary>
|
||||
public string Content { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 告警类型,子系统产生告警的类型 (非空)
|
||||
/// 示例:"0101" 表示高限报警
|
||||
/// </summary>
|
||||
public string AlarmType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 告警确认时间 (可空,时间戳)
|
||||
/// 示例:1586152800000
|
||||
/// </summary>
|
||||
public long? ConfirmedTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 解除告警时间 (可空,时间戳)
|
||||
/// 示例:1586152800000
|
||||
/// </summary>
|
||||
public long? FixTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 产生告警时间 (非空,时间戳)
|
||||
/// 示例:1586152800000
|
||||
/// </summary>
|
||||
public long LastTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 告警状态 (非空)
|
||||
/// 可选值:UNFIXED(新增告警)、FIXED(解除告警)
|
||||
/// </summary>
|
||||
public string Status { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 告警等级,需要按照映射表上传 (非空)
|
||||
/// 示例:"1"
|
||||
/// </summary>
|
||||
public string AlarmLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子系统编码 (非空)
|
||||
/// 示例:"MS_NEW_PD_DCIM_001"
|
||||
/// </summary>
|
||||
public string SubSystemCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 默认填写字段 (非空)
|
||||
/// 固定值:"SUB_SYSTEM_ALARM"
|
||||
/// </summary>
|
||||
public string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 告警确认人 (可空)
|
||||
/// 示例:"admin3"
|
||||
/// </summary>
|
||||
public string ConfirmAccount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 告警清除人 (可空)
|
||||
/// 示例:"admin"
|
||||
/// </summary>
|
||||
public string ClearAccount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 告警处理说明 (可空)
|
||||
/// 示例:"admin"
|
||||
/// </summary>
|
||||
public string ProcessInstruction { get; set; }
|
||||
}
|
46
src/ThingsGateway.Server/Test/TestSQL.cs
Normal file
46
src/ThingsGateway.Server/Test/TestSQL.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using ThingsGateway.Gateway.Application;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.Plugin.DB;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
public class TestSQL : DynamicSQLBase
|
||||
{
|
||||
public override Task DBInit(ISqlSugarClient db, CancellationToken cancellationToken)
|
||||
{
|
||||
db.DbMaintenance.CreateDatabase();
|
||||
db.CodeFirst.InitTables<ThingsGateway.Plugin.OpcAe.OpcAeEventData>();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task DBInsertable(ISqlSugarClient db, IEnumerable<object> datas, CancellationToken cancellationToken)
|
||||
{
|
||||
var pluginEventDatas = datas.Cast<PluginEventData>();
|
||||
var opcDatas = pluginEventDatas.Select(
|
||||
a =>
|
||||
{
|
||||
if (a.ObjectValue == null)
|
||||
{
|
||||
a.ObjectValue = a.Value.ToObject(Type.GetType(a.ValueType));
|
||||
}
|
||||
return a.ObjectValue is ThingsGateway.Plugin.OpcAe.OpcAeEventData opcData ? opcData : null;
|
||||
}
|
||||
).Where(a => a != null).ToList();
|
||||
if (opcDatas.Count == 0)
|
||||
return;
|
||||
Logger?.Info(opcDatas.ToSystemTextJsonString());
|
||||
|
||||
await db.Insertable(opcDatas).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
@@ -35,6 +35,7 @@
|
||||
<!--<Import Project="targets\Pro3.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' " />
|
||||
<Import Project="targets\Pro5.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' " />-->
|
||||
<!--<Import Project="targets\Pro6.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' != 'Debug'" />-->
|
||||
<Import Project="targets\Pro7.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' != 'Debug'" />
|
||||
|
||||
<!--打包复制-->
|
||||
<Import Project="targets\PluginPublish.targets" />
|
||||
|
@@ -92,7 +92,7 @@
|
||||
<PkgThingsGateway_Plugin_HttpPackageFiles Include="$(PkgThingsGateway_Plugin_Http)\Content\$(PluginTargetFramework)\**\*.*" />
|
||||
</ItemGroup>
|
||||
|
||||
<Message Text="将插件复制到插件目录 $(GatewayPluginFolder)" Importance="high" />
|
||||
<Message Text="将开源插件复制到插件目录 $(GatewayPluginFolder)" Importance="high" />
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_ModbusPackageFiles)" DestinationFolder="$(GatewayPluginFolder)ThingsGateway.Plugin.Modbus%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_SiemensS7PackageFiles)" DestinationFolder="$(GatewayPluginFolder)ThingsGateway.Plugin.SiemensS7%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_Dlt645PackageFiles)" DestinationFolder="$(GatewayPluginFolder)ThingsGateway.Plugin.Dlt645%(RecursiveDir)" />
|
||||
|
@@ -96,7 +96,7 @@
|
||||
|
||||
|
||||
|
||||
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
<Message Text="将开源插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_ModbusPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_SiemensS7PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_Dlt645PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
|
||||
|
@@ -70,7 +70,7 @@
|
||||
<PropertyGroup>
|
||||
<PluginFolder>$(TargetDir)GatewayPlugins\</PluginFolder>
|
||||
</PropertyGroup>
|
||||
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
<Message Text="将PRO插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_InovancePackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.Inovance%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_TIANXINPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.TIANXIN%(RecursiveDir)" />
|
||||
|
@@ -20,7 +20,7 @@
|
||||
<PropertyGroup>
|
||||
<PluginFolder>$(TargetDir)Plugins\</PluginFolder>
|
||||
</PropertyGroup>
|
||||
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
<Message Text="将SFSK插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
|
||||
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_HUANANSFSKPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
|
||||
|
@@ -18,7 +18,7 @@
|
||||
<PropertyGroup>
|
||||
<PluginFolder>$(TargetDir)Plugins\</PluginFolder>
|
||||
</PropertyGroup>
|
||||
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
<Message Text="将Modbus定制插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_ModbusC1PackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_ModbusGYPackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<PropertyGroup>
|
||||
<PluginFolder>$(TargetDir)GatewayPlugins\</PluginFolder>
|
||||
</PropertyGroup>
|
||||
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
<Message Text="将MQTTYINGKE插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_MqttYINGKEPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.MqttYINGKE%(RecursiveDir)" />
|
||||
|
||||
|
@@ -17,7 +17,7 @@
|
||||
<PropertyGroup>
|
||||
<PluginFolder>$(TargetDir)GatewayPlugins\</PluginFolder>
|
||||
</PropertyGroup>
|
||||
<Message Text="将插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
<Message Text="将IXCom29s插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_IXCom29sPackageFiles)" DestinationFolder="$(PluginFolder)ThingsGateway.Plugin.IXCom29s%(RecursiveDir)" />
|
||||
|
||||
|
28
src/ThingsGateway.Server/targets/Pro7.targets
Normal file
28
src/ThingsGateway.Server/targets/Pro7.targets
Normal file
@@ -0,0 +1,28 @@
|
||||
<Project>
|
||||
|
||||
<ItemGroup>
|
||||
<!--MqttYINGKE 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.OpcAe" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyOtherPlugin7NugetPackages" AfterTargets="Build">
|
||||
<PropertyGroup>
|
||||
<PluginTargetFramework>net8.0</PluginTargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- setting up the variable for convenience -->
|
||||
<PkgThingsGateway_Plugin_OpcAePackageFiles Include="$(PkgThingsGateway_Plugin_OpcAe)\Content\$(PluginTargetFramework)\**\*.*" />
|
||||
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<PluginFolder>$(TargetDir)Plugins\</PluginFolder>
|
||||
</PropertyGroup>
|
||||
<Message Text="将OpcAe插件复制到插件目录 $(PluginFolder)" Importance="high" />
|
||||
|
||||
<Copy SourceFiles="@(PkgThingsGateway_Plugin_OpcAePackageFiles)" DestinationFolder="$(PluginFolder)%(RecursiveDir)" />
|
||||
|
||||
|
||||
</Target>
|
||||
|
||||
|
||||
</Project>
|
@@ -31,6 +31,8 @@
|
||||
|
||||
<ProjectReference Include="..\PluginPro\ThingsGateway.Plugin.ModbusGY\ThingsGateway.Plugin.ModbusGY.csproj" />
|
||||
<ProjectReference Include="..\PluginPro\ThingsGateway.Plugin.IXCom29s\ThingsGateway.Plugin.IXCom29s.csproj" />
|
||||
<ProjectReference Include="..\PluginPro\ThingsGateway.Plugin.OpcAe\ThingsGateway.Plugin.OpcAe.csproj" />
|
||||
<ProjectReference Include="..\PluginPro\ThingsGateway.Foundation.OpcAe\ThingsGateway.Foundation.OpcAe.csproj" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
Reference in New Issue
Block a user