mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-21 11:08:13 +08:00
Compare commits
8 Commits
10.11.58.0
...
10.11.68.0
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6d7d0e468a | ||
![]() |
ff1f632de2 | ||
![]() |
fc3d7015ee | ||
![]() |
40c5acb522 | ||
![]() |
f9cc1cbb05 | ||
![]() |
cf6e8b58f0 | ||
![]() |
615e3bb24c | ||
![]() |
4a7534b210 |
@@ -5,7 +5,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
|
||||
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.0" />
|
||||
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.1" />
|
||||
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -24,7 +24,7 @@ public static class ParallelExtensions
|
||||
/// <typeparam name="T">集合元素类型</typeparam>
|
||||
/// <param name="source">要操作的集合</param>
|
||||
/// <param name="body">要执行的操作</param>
|
||||
public static void ParallelForEach<T>(this IList<T> source, Action<T> body)
|
||||
public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T> body)
|
||||
{
|
||||
ParallelOptions options = new();
|
||||
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
|
||||
@@ -38,7 +38,7 @@ public static class ParallelExtensions
|
||||
/// <typeparam name="T">集合元素类型</typeparam>
|
||||
/// <param name="source">要操作的集合</param>
|
||||
/// <param name="body">要执行的操作</param>
|
||||
public static void ParallelForEach<T>(this IList<T> source, Action<T, ParallelLoopState, long> body)
|
||||
public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T, ParallelLoopState, long> body)
|
||||
{
|
||||
ParallelOptions options = new();
|
||||
options.MaxDegreeOfParallelism = Environment.ProcessorCount;
|
||||
@@ -53,7 +53,7 @@ public static class ParallelExtensions
|
||||
/// <param name="source">要操作的集合</param>
|
||||
/// <param name="body">要执行的操作</param>
|
||||
/// <param name="parallelCount">最大并行度</param>
|
||||
public static void ParallelForEach<T>(this IList<T> source, Action<T> body, int parallelCount)
|
||||
public static void ParallelForEach<T>(this IEnumerable<T> source, Action<T> body, int parallelCount)
|
||||
{
|
||||
// 创建并行操作的选项对象,设置最大并行度为指定的值
|
||||
var options = new ParallelOptions();
|
||||
@@ -109,7 +109,7 @@ public static class ParallelExtensions
|
||||
/// <param name="parallelCount">最大并行度</param>
|
||||
/// <param name="cancellationToken">取消操作的标志</param>
|
||||
/// <returns>表示异步操作的任务</returns>
|
||||
public static Task ParallelForEachAsync<T>(this IList<T> source, Func<T, CancellationToken, ValueTask> body, int parallelCount, CancellationToken cancellationToken = default)
|
||||
public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, CancellationToken, ValueTask> body, int parallelCount, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 创建并行操作的选项对象,设置最大并行度和取消标志
|
||||
var options = new ParallelOptions();
|
||||
@@ -126,7 +126,7 @@ public static class ParallelExtensions
|
||||
/// <param name="body">异步执行的操作</param>
|
||||
/// <param name="cancellationToken">取消操作的标志</param>
|
||||
/// <returns>表示异步操作的任务</returns>
|
||||
public static Task ParallelForEachAsync<T>(this IList<T> source, Func<T, CancellationToken, ValueTask> body, CancellationToken cancellationToken = default)
|
||||
public static Task ParallelForEachAsync<T>(this IEnumerable<T> source, Func<T, CancellationToken, ValueTask> body, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return ParallelForEachAsync(source, body, Environment.ProcessorCount, cancellationToken);
|
||||
}
|
||||
|
@@ -27,10 +27,15 @@ public abstract class PrimaryIdEntity : IPrimaryIdEntity
|
||||
public virtual long Id { get; set; }
|
||||
}
|
||||
|
||||
public interface IPrimaryKeyEntity
|
||||
{
|
||||
string ExtJson { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 主键实体基类
|
||||
/// </summary>
|
||||
public abstract class PrimaryKeyEntity : PrimaryIdEntity
|
||||
public abstract class PrimaryKeyEntity : PrimaryIdEntity, IPrimaryKeyEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 拓展信息
|
||||
|
@@ -2,5 +2,5 @@
|
||||
|
||||
@if (show)
|
||||
{
|
||||
<Spinner class="ms-auto"></Spinner>
|
||||
<Spinner Size="Size" class="ms-auto"></Spinner>
|
||||
}
|
||||
|
@@ -18,4 +18,7 @@ public partial class SpinnerComponent
|
||||
StateHasChanged();
|
||||
}
|
||||
private bool show;
|
||||
|
||||
[Parameter]
|
||||
public Size Size { get; set; } = Size.Small;
|
||||
}
|
||||
|
@@ -1,11 +1,11 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.11.58</PluginVersion>
|
||||
<ProPluginVersion>10.11.58</ProPluginVersion>
|
||||
<DefaultVersion>10.11.58</DefaultVersion>
|
||||
<AuthenticationVersion>10.11.5</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.11.4</SourceGeneratorVersion>
|
||||
<PluginVersion>10.11.68</PluginVersion>
|
||||
<ProPluginVersion>10.11.68</ProPluginVersion>
|
||||
<DefaultVersion>10.11.68</DefaultVersion>
|
||||
<AuthenticationVersion>10.11.6</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.20</NET8Version>
|
||||
<NET9Version>9.0.9</NET9Version>
|
||||
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
|
||||
|
@@ -435,5 +435,311 @@ public static class ThingsGatewayBitConverterExtension
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据数据类型获取实际值
|
||||
/// </summary>
|
||||
public static bool GetChangedDataFormJToken(
|
||||
JToken jToken,
|
||||
DataTypeEnum dataType,
|
||||
int arrayLength,
|
||||
object? oldValue,
|
||||
out object? result)
|
||||
{
|
||||
switch (dataType)
|
||||
{
|
||||
case DataTypeEnum.Boolean:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<Boolean[]>();
|
||||
if (oldValue is bool[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<Boolean>();
|
||||
if (oldValue is bool oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.Byte:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<Byte[]>();
|
||||
if (oldValue is byte[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<Byte>();
|
||||
if (oldValue is byte oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.Int16:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<Int16[]>();
|
||||
if (oldValue is short[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<Int16>();
|
||||
if (oldValue is short oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.UInt16:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<UInt16[]>();
|
||||
if (oldValue is ushort[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<UInt16>();
|
||||
if (oldValue is ushort oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.Int32:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<Int32[]>();
|
||||
if (oldValue is int[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<Int32>();
|
||||
if (oldValue is int oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.UInt32:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<UInt32[]>();
|
||||
if (oldValue is uint[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<UInt32>();
|
||||
if (oldValue is uint oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.Int64:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<Int64[]>();
|
||||
if (oldValue is long[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<Int64>();
|
||||
if (oldValue is long oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.UInt64:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<UInt64[]>();
|
||||
if (oldValue is ulong[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<UInt64>();
|
||||
if (oldValue is ulong oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.Float:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<Single[]>();
|
||||
if (oldValue is float[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<Single>();
|
||||
if (oldValue is float oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
case DataTypeEnum.Double:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<Double[]>();
|
||||
if (oldValue is double[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<Double>();
|
||||
if (oldValue is double oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
case DataTypeEnum.Decimal:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newVal = jToken.ToObject<Decimal[]>();
|
||||
if (oldValue is decimal[] oldArr && newVal.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newVal = jToken.ToObject<Decimal>();
|
||||
if (oldValue is decimal oldVal && oldVal == newVal)
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newVal;
|
||||
return true;
|
||||
}
|
||||
case DataTypeEnum.String:
|
||||
default:
|
||||
if (arrayLength > 1)
|
||||
{
|
||||
var newArr = new string[arrayLength];
|
||||
for (int i = 0; i < arrayLength; i++)
|
||||
{
|
||||
newArr[i] = jToken.ToObject<string>();
|
||||
}
|
||||
|
||||
if (oldValue is string[] oldArr && newArr.SequenceEqual(oldArr))
|
||||
{
|
||||
result = oldValue;
|
||||
return false;
|
||||
}
|
||||
result = newArr;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var str = jToken.ToObject<string>();
|
||||
if (oldValue is string oldStr && oldStr == str)
|
||||
{
|
||||
result = oldStr;
|
||||
return false;
|
||||
}
|
||||
result = str;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion 获取对应数据类型的数据
|
||||
}
|
||||
|
@@ -0,0 +1,30 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
|
||||
public class StringNotMemoryAttribute : ValidationAttribute
|
||||
{
|
||||
public StringNotMemoryAttribute()
|
||||
: base("The field {0} cannot be 'Memory'.")
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsValid(object value)
|
||||
{
|
||||
var str = value as string;
|
||||
return !string.Equals(str, "Memory", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -77,7 +77,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
IdVariableRuntimes.Clear();
|
||||
IdVariableRuntimes.AddRange(GlobalData.GetEnableVariables().ToDictionary(a => a.Id));
|
||||
IdVariableRuntimes.AddRange(GlobalData.GetEnableVariables().Where(a => a.IsInternalMemoryVariable == false).ToDictionary(a => a.Id));
|
||||
CollectDevices = GlobalData.GetEnableDevices().Where(a => a.IsCollect == true).ToDictionary(a => a.Id);
|
||||
|
||||
VariableRuntimeGroups = IdVariableRuntimes.GroupBy(a => a.Value.BusinessGroup ?? string.Empty).ToDictionary(a => a.Key, a => a.Select(a => a.Value).ToList());
|
||||
|
@@ -540,7 +540,7 @@ public abstract partial class CollectBase : DriverBase
|
||||
// 如果成功,每个变量都读取一次最新值,再次比较写入值
|
||||
var successfulWriteNames = operResults.Where(a => a.Value.IsSuccess).Select(a => a.Key).ToHashSet();
|
||||
|
||||
var groups = writeInfoLists.Select(a => a.Key).Where(a => a.RpcWriteCheck && a.ProtectType != ProtectTypeEnum.WriteOnly && successfulWriteNames.Contains(a.Name) && a.VariableSource != null).GroupBy(a => a.VariableSource as VariableSourceRead).Where(a => a.Key != null).ToList();
|
||||
var groups = writeInfoLists.Select(a => a.Key).Where(a => a.RpcWriteCheck && a.ProtectType != ProtectTypeEnum.WriteOnly && successfulWriteNames.Contains(a.Name) && a.VariableSource != null).GroupBy(a => a.VariableSource as VariableSourceRead).Where(a => a.Key != null).ToArray();
|
||||
|
||||
await groups.ParallelForEachAsync(async (varRead, token) =>
|
||||
{
|
||||
@@ -610,7 +610,7 @@ public abstract partial class CollectBase : DriverBase
|
||||
using var writeLock = await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
|
||||
var list = writeInfoLists
|
||||
.Where(a => !results.Any(b => b.Key == a.Key.Name))
|
||||
.ToDictionary(item => item.Key, item => item.Value).ToArray();
|
||||
.ToArray();
|
||||
// 使用并发方式遍历写入信息列表,并进行异步写入操作
|
||||
await list.ParallelForEachAsync(async (writeInfo, cancellationToken) =>
|
||||
{
|
||||
|
@@ -200,9 +200,8 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
|
||||
// 创建用于存储操作结果的并发字典
|
||||
ConcurrentDictionary<string, OperResult> operResults = new();
|
||||
var list = writeInfoLists.ToArray();
|
||||
// 使用并发方式遍历写入信息列表,并进行异步写入操作
|
||||
await list.ParallelForEachAsync(async (writeInfo, cancellationToken) =>
|
||||
await writeInfoLists.ParallelForEachAsync(async (writeInfo, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@@ -62,13 +62,15 @@ public static class DynamicModelExtension
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (GlobalData.IdDevices.TryGetValue(businessId, out var deviceRuntime))
|
||||
{
|
||||
if (deviceRuntime.Driver is BusinessBase businessBase)
|
||||
{
|
||||
if (businessBase.DriverProperties is IBusinessPropertyAllVariableBase property && property.IsAllVariable)
|
||||
{
|
||||
return GetVariableProperty(variableRuntime, businessId, propertyName, businessBase);
|
||||
if (variableRuntime.IsInternalMemoryVariable == false)
|
||||
return GetVariableProperty(variableRuntime, businessId, propertyName, businessBase);
|
||||
}
|
||||
else if (businessBase.RefreshRuntimeAlways)
|
||||
{
|
||||
@@ -84,9 +86,9 @@ public static class DynamicModelExtension
|
||||
static string? GetVariableProperty(VariableRuntime variableRuntime, long businessId, string propertyName, BusinessBase businessBase)
|
||||
{
|
||||
// 检查是否存在对应的业务设备Id
|
||||
if (variableRuntime.VariablePropertys?.ContainsKey(businessId) == true)
|
||||
if (variableRuntime.VariablePropertys?.TryGetValue(businessId, out var kv) == true)
|
||||
{
|
||||
variableRuntime.VariablePropertys[businessId].TryGetValue(propertyName, out var value);
|
||||
kv.TryGetValue(propertyName, out var value);
|
||||
return value; // 返回属性值
|
||||
}
|
||||
else
|
||||
|
@@ -43,6 +43,7 @@ public class Device : BaseDataEntity, IValidatableObject
|
||||
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
|
||||
[Required]
|
||||
[RegularExpression(@"^[^.]*$", ErrorMessage = "The field {0} cannot contain a dot ('.')")]
|
||||
[StringNotMemoryAttribute]
|
||||
public virtual string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@@ -304,13 +304,11 @@ public class Variable : BaseDataEntity, IValidatableObject
|
||||
|
||||
#endregion 备用字段
|
||||
|
||||
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
|
||||
{
|
||||
if (string.IsNullOrEmpty(RegisterAddress) && string.IsNullOrEmpty(OtherMethod))
|
||||
{
|
||||
yield return new ValidationResult("Both RegisterAddress and OtherMethod cannot be empty or null.", new[] { nameof(OtherMethod), nameof(RegisterAddress) });
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -23,5 +23,5 @@ public enum PluginTypeEnum
|
||||
/// <summary>
|
||||
/// 业务
|
||||
/// </summary>
|
||||
Business,
|
||||
Business
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ public delegate void DelegateOnDeviceChanged(DeviceRuntime deviceRuntime, Device
|
||||
/// <param name="variableRuntime">变量运行时对象</param>
|
||||
/// <param name="variableData">变量数据对象</param>
|
||||
public delegate void VariableChangeEventHandler(VariableRuntime variableRuntime, VariableBasicData variableData);
|
||||
|
||||
/// <summary>
|
||||
/// 变量采集事件委托,用于通知变量进行采集时的事件
|
||||
/// </summary>
|
||||
@@ -100,10 +101,11 @@ public static class GlobalData
|
||||
return ReadOnlyIdDevices.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
|
||||
}
|
||||
|
||||
public static async Task<IEnumerable<VariableRuntime>> GetCurrentUserIdVariables()
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
return IdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
return IdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
|
||||
}
|
||||
|
||||
@@ -117,7 +119,7 @@ public static class GlobalData
|
||||
public static async Task<IEnumerable<VariableRuntime>> GetCurrentUserAlarmEnableVariables()
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
return AlarmEnableIdVariables.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
return AlarmEnableIdVariables.Where(a => a.Value.IsInternalMemoryVariable == false).WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.Value.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.Value.CreateUserId == UserManager.UserId).Select(a => a.Value);
|
||||
}
|
||||
|
||||
@@ -130,7 +132,8 @@ public static class GlobalData
|
||||
{
|
||||
if (businessBase.DriverProperties is IBusinessPropertyAllVariableBase property && property.IsAllVariable)
|
||||
{
|
||||
return true;
|
||||
if (a.IsInternalMemoryVariable == false)
|
||||
return true;
|
||||
}
|
||||
else if (businessBase.RefreshRuntimeAlways)
|
||||
{
|
||||
@@ -206,6 +209,32 @@ public static class GlobalData
|
||||
return null;
|
||||
}
|
||||
|
||||
public static VariableRuntime GetVariable(string variableName)
|
||||
{
|
||||
|
||||
if (string.IsNullOrEmpty(variableName))
|
||||
return null;
|
||||
var names = variableName.Split('.');
|
||||
if (names.Length > 2)
|
||||
return null;
|
||||
|
||||
if (names.Length == 1)
|
||||
{
|
||||
if (MemoryVariables.TryGetValue(names[0], out var variable))
|
||||
{
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
else if (Devices.TryGetValue(names[0], out var device))
|
||||
{
|
||||
if (device.VariableRuntimes.TryGetValue(names[1], out var variable))
|
||||
{
|
||||
return variable;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<DeviceRuntime> GetEnableDevices()
|
||||
{
|
||||
var idSet = GetRedundantDeviceIds();
|
||||
@@ -233,7 +262,7 @@ public static class GlobalData
|
||||
|
||||
public static IEnumerable<VariableRuntime> GetEnableVariables()
|
||||
{
|
||||
return IdDevices.SelectMany(a => a.Value.VariableRuntimes).Where(a => a.Value?.Enable == true).Select(a => a.Value);
|
||||
return IdVariables.Where(a => a.Value.DeviceRuntime?.Enable != false && a.Value.DeviceRuntime?.ChannelRuntime?.Enable != false && a.Value?.Enable == true).Select(a => a.Value);
|
||||
}
|
||||
|
||||
public static bool TryGetDeviceThreadManage(DeviceRuntime deviceRuntime, out IDeviceThreadManage deviceThreadManage)
|
||||
@@ -527,6 +556,9 @@ public static class GlobalData
|
||||
/// </summary>
|
||||
internal static ConcurrentDictionary<long, VariableRuntime> IdVariables { get; } = new();
|
||||
|
||||
internal static ConcurrentDictionary<string, VariableRuntime> MemoryVariables { get; } = new();
|
||||
public static IReadOnlyDictionary<string, VariableRuntime> ReadOnlyMemoryVariables => MemoryVariables;
|
||||
|
||||
/// <summary>
|
||||
/// 实时报警列表
|
||||
/// </summary>
|
||||
|
@@ -131,8 +131,10 @@ public class VariableBasicData
|
||||
//[System.Text.Json.Serialization.JsonIgnore]
|
||||
//[Newtonsoft.Json.JsonIgnore]
|
||||
//public DeviceBasicData DeviceRuntime { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.DeviceId"/>
|
||||
public long DeviceId { get; set; }
|
||||
|
||||
/// <inheritdoc cref="VariableRuntime.LastErrorMessage"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
@@ -142,6 +144,11 @@ public class VariableBasicData
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? RegisterAddress { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.OtherMethod"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
public string? OtherMethod { get; set; }
|
||||
|
||||
/// <inheritdoc cref="Variable.Unit"/>
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
[System.Text.Json.Serialization.JsonIgnore(Condition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)]
|
||||
@@ -190,5 +197,6 @@ public class VariableBasicData
|
||||
|
||||
/// <inheritdoc cref="VariableRuntime.ValueInited"/>
|
||||
public bool ValueInited { get; set; }
|
||||
|
||||
/// <inheritdoc cref="VariableRuntime.IsMemoryVariable"/>
|
||||
public bool IsMemoryVariable { get; set; }
|
||||
}
|
||||
|
@@ -0,0 +1,16 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IMemoryVariableRpc
|
||||
{
|
||||
OperResult MemoryVariableRpc(string value, CancellationToken cancellationToken = default);
|
||||
}
|
@@ -14,6 +14,7 @@ using Newtonsoft.Json.Linq;
|
||||
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
|
||||
#if !Management
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
#endif
|
||||
@@ -32,6 +33,13 @@ public partial class VariableRuntime : Variable
|
||||
IDisposable
|
||||
#endif
|
||||
{
|
||||
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual bool IsMemoryVariable { get => _isMemoryVariable; set => _isMemoryVariable = value; }
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public virtual bool IsInternalMemoryVariable { get => _isInternalMemoryVariable; set => _isInternalMemoryVariable = value; }
|
||||
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public bool ValueInited { get => _valueInited; set => _valueInited = value; }
|
||||
|
||||
@@ -74,15 +82,22 @@ public partial class VariableRuntime : Variable
|
||||
public object RawValue { get => rawValue; set => rawValue = value; }
|
||||
|
||||
|
||||
|
||||
#if !Management
|
||||
|
||||
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public TouchSocket.Core.ILog? LogMessage => DeviceRuntime?.Driver?.LogMessage;
|
||||
|
||||
/// <summary>
|
||||
/// 所在采集设备
|
||||
/// </summary>
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[AutoGenerateColumn(Ignore = true)]
|
||||
public DeviceRuntime? DeviceRuntime { get => deviceRuntime; set => deviceRuntime = value; }
|
||||
public DeviceRuntime? DeviceRuntime { get => deviceRuntime; private set => deviceRuntime = value; }
|
||||
|
||||
/// <summary>
|
||||
/// VariableSource
|
||||
@@ -239,6 +254,8 @@ public partial class VariableRuntime : Variable
|
||||
#pragma warning restore CS0169
|
||||
#pragma warning restore CS0414
|
||||
private bool _valueInited;
|
||||
private bool _isMemoryVariable;
|
||||
private bool _isInternalMemoryVariable;
|
||||
|
||||
private object _value;
|
||||
private object lastSetValue;
|
||||
@@ -272,7 +289,7 @@ public partial class VariableRuntime : Variable
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = ReadExpressions.GetExpressionsResult(RawValue, DeviceRuntime?.Driver?.LogMessage);
|
||||
var data = ReadExpressions.GetExpressionsResult(RawValue, LogMessage);
|
||||
Set(data, dateTime);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -291,7 +308,7 @@ public partial class VariableRuntime : Variable
|
||||
}
|
||||
if (oldMessage != _lastErrorMessage)
|
||||
{
|
||||
DeviceRuntime?.Driver?.LogMessage?.LogWarning(_lastErrorMessage);
|
||||
LogMessage?.LogWarning(_lastErrorMessage);
|
||||
}
|
||||
return new($"{Name} Conversion expression failed", ex);
|
||||
}
|
||||
@@ -394,6 +411,12 @@ public partial class VariableRuntime : Variable
|
||||
|
||||
DeviceRuntime = deviceRuntime;
|
||||
|
||||
if (deviceRuntime == null)
|
||||
{
|
||||
GlobalData.MemoryVariables.Remove(Name);
|
||||
GlobalData.MemoryVariables.TryAdd(Name, this);
|
||||
}
|
||||
|
||||
DeviceRuntime?.VariableRuntimes?.TryAdd(Name, this);
|
||||
GlobalData.IdVariables.Remove(Id);
|
||||
GlobalData.IdVariables.TryAdd(Id, this);
|
||||
@@ -407,7 +430,10 @@ public partial class VariableRuntime : Variable
|
||||
public void Dispose()
|
||||
{
|
||||
DeviceRuntime?.VariableRuntimes?.Remove(Name);
|
||||
|
||||
if (DeviceRuntime == null)
|
||||
{
|
||||
GlobalData.MemoryVariables.Remove(Name);
|
||||
}
|
||||
GlobalData.IdVariables.Remove(Id);
|
||||
|
||||
GlobalData.AlarmEnableIdVariables.Remove(Id);
|
||||
@@ -422,7 +448,7 @@ public partial class VariableRuntime : Variable
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async ValueTask<IOperResult> RpcAsync(string value, string executive = "brower", CancellationToken cancellationToken = default)
|
||||
public virtual async ValueTask<IOperResult> RpcAsync(string value, string executive = "brower", CancellationToken cancellationToken = default)
|
||||
{
|
||||
var data = await GlobalData.RpcService.InvokeDeviceMethodAsync(executive, new Dictionary<string, Dictionary<string, string>>()
|
||||
{
|
||||
|
@@ -10,7 +10,6 @@
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using ThingsGateway.Common.Extension;
|
||||
using ThingsGateway.Gateway.Application.Extensions;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
|
||||
@@ -110,7 +109,7 @@ internal sealed class AlarmTask : IDisposable
|
||||
if (tag.AlarmPropertys.CustomAlarmEnable) // 检查是否启用了自定义报警功能
|
||||
{
|
||||
// 调用变量的CustomAlarmCode属性的GetExpressionsResult方法,传入变量的值,获取报警表达式的计算结果
|
||||
var result = tag.AlarmPropertys.CustomAlarmCode.GetExpressionsResult(tag.Value, tag.DeviceRuntime?.Driver?.LogMessage);
|
||||
var result = tag.AlarmPropertys.CustomAlarmCode.GetExpressionsResult(tag.Value, tag.LogMessage);
|
||||
|
||||
if (result is bool boolResult) // 检查计算结果是否为布尔类型
|
||||
{
|
||||
@@ -227,7 +226,7 @@ internal sealed class AlarmTask : IDisposable
|
||||
if (!string.IsNullOrEmpty(ex))
|
||||
{
|
||||
// 如果存在报警约束表达式,则计算表达式结果,以确定是否触发报警事件
|
||||
var data = ex.GetExpressionsResult(item.Value, item.DeviceRuntime?.Driver?.LogMessage);
|
||||
var data = ex.GetExpressionsResult(item.Value, item.LogMessage);
|
||||
if (data is bool result)
|
||||
{
|
||||
if (result)
|
||||
@@ -487,6 +486,11 @@ internal sealed class AlarmTask : IDisposable
|
||||
|
||||
#endregion 核心实现
|
||||
|
||||
ParallelOptions ParallelOptions = new()
|
||||
{
|
||||
MaxDegreeOfParallelism = Math.Max(1, Environment.ProcessorCount / 2)
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 执行工作任务,对设备变量进行报警分析。
|
||||
/// </summary>
|
||||
@@ -499,32 +503,36 @@ internal sealed class AlarmTask : IDisposable
|
||||
if (!GlobalData.StartBusinessChannelEnable)
|
||||
return;
|
||||
|
||||
//Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
// 遍历设备变量列表
|
||||
//System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
||||
|
||||
if (scheduledTask.Period < 100 && scheduledTask.Period > 1 && GlobalData.AlarmEnableIdVariables.Count > 50000)
|
||||
{
|
||||
scheduledTask.Change(100, 100);
|
||||
}
|
||||
|
||||
// 遍历设备变量列表
|
||||
if (!GlobalData.AlarmEnableIdVariables.IsEmpty)
|
||||
{
|
||||
var list = GlobalData.AlarmEnableIdVariables.Select(a => a.Value).ToArray();
|
||||
list.ParallelForEach((item, state, index) =>
|
||||
// 使用 Parallel.ForEach 执行指定的操作
|
||||
Parallel.ForEach(GlobalData.AlarmEnableIdVariables, ParallelOptions, (item, state, index) =>
|
||||
{
|
||||
{
|
||||
// 如果取消请求已经被触发,则结束任务
|
||||
if (cancellation.IsCancellationRequested)
|
||||
return;
|
||||
// 如果取消请求已经被触发,则结束任务
|
||||
if (cancellation.IsCancellationRequested)
|
||||
return;
|
||||
|
||||
// 如果该变量的报警功能未启用,则跳过该变量
|
||||
if (!item.AlarmEnable)
|
||||
return;
|
||||
// 如果该变量的报警功能未启用,则跳过该变量
|
||||
if (!item.Value.AlarmEnable)
|
||||
return;
|
||||
|
||||
// 如果该变量离线,则跳过该变量
|
||||
if (!item.IsOnline)
|
||||
return;
|
||||
// 如果该变量离线,则跳过该变量
|
||||
if (!item.Value.IsOnline)
|
||||
return;
|
||||
|
||||
// 对该变量进行报警分析
|
||||
AlarmAnalysis(item);
|
||||
}
|
||||
// 对该变量进行报警分析
|
||||
AlarmAnalysis(item.Value);
|
||||
});
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
//if (scheduledTask.Period != 5000)
|
||||
|
@@ -573,8 +573,7 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
|
||||
{
|
||||
//传入变量
|
||||
//newDeviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.SafeDispose());
|
||||
var list = deviceRuntime.VariableRuntimes.Select(a => a.Value).ToArray();
|
||||
list.ParallelForEach(a => a.Init(newDeviceRuntime));
|
||||
deviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
GlobalData.VariableRuntimeDispatchService.Dispatch(null);
|
||||
}
|
||||
|
||||
|
@@ -54,7 +54,7 @@ internal sealed class GatewayMonitorHostedService : BackgroundService, IGatewayM
|
||||
{
|
||||
item.Init(channelRuntime);
|
||||
|
||||
var varRuntimes = variableRuntimes.Where(x => x.DeviceId == item.Id).ToArray();
|
||||
var varRuntimes = variableRuntimes.Where(x => x.DeviceId == item.Id);
|
||||
|
||||
varRuntimes.ParallelForEach(varItem => varItem.Init(item));
|
||||
}
|
||||
|
@@ -479,7 +479,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
|
||||
{
|
||||
int maxBatchSize = GetBatchSize() / 10;
|
||||
|
||||
var groups = GlobalData.IdVariables.Select(a => a.Value).GroupBy(a => a.DeviceRuntime);
|
||||
var groups = GlobalData.IdVariables.Select(a => a.Value).GroupBy(a => a.DeviceRuntime).Where(a => a.Key != null);
|
||||
|
||||
var channelBatch = new HashSet<Channel>();
|
||||
var deviceBatch = new HashSet<Device>();
|
||||
|
@@ -30,6 +30,10 @@ internal sealed class RpcService : IRpcService
|
||||
{
|
||||
private readonly ConcurrentQueue<RpcLog> _logQueues = new();
|
||||
private readonly RpcLogOptions? _rpcLogOptions;
|
||||
private SqlSugarClient _db = DbContext.GetDB<RpcLog>(); // 创建一个新的数据库上下文实例
|
||||
|
||||
private IStringLocalizer Localizer { get; }
|
||||
|
||||
/// <inheritdoc cref="RpcService"/>
|
||||
public RpcService(IStringLocalizer<RpcService> localizer)
|
||||
{
|
||||
@@ -38,50 +42,118 @@ internal sealed class RpcService : IRpcService
|
||||
_rpcLogOptions = App.GetOptions<RpcLogOptions>();
|
||||
}
|
||||
|
||||
private IStringLocalizer Localizer { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<Dictionary<string, Dictionary<string, IOperResult>>> InvokeDeviceMethodAsync(string sourceDes, Dictionary<string, Dictionary<string, string>> deviceDatas, CancellationToken cancellationToken = default)
|
||||
public async Task<Dictionary<string, Dictionary<string, IOperResult>>> InvokeDeviceMethodAsync(
|
||||
string sourceDes,
|
||||
Dictionary<string, Dictionary<string, string>> deviceDatas,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 初始化用于存储将要写入的变量和方法的字典
|
||||
Dictionary<IRpcDriver, Dictionary<VariableRuntime, JToken>> writeVariables = new();
|
||||
Dictionary<IRpcDriver, Dictionary<VariableRuntime, JToken>> writeMethods = new();
|
||||
Dictionary<VariableRuntime, string> memoryVariables = new();
|
||||
|
||||
// 用于存储结果的并发字典
|
||||
ConcurrentDictionary<string, Dictionary<string, IOperResult>> results = new();
|
||||
deviceDatas.ForEach(a => results.TryAdd(a.Key, new()));
|
||||
|
||||
// 对每个要操作的变量进行检查和处理(内存变量)
|
||||
foreach (var item in deviceDatas.Where(a => a.Key.IsNullOrEmpty() || a.Key.Equals("Memory", StringComparison.OrdinalIgnoreCase)).SelectMany(a => a.Value))
|
||||
{
|
||||
// 查找变量是否存在
|
||||
if (!(GlobalData.MemoryVariables.TryGetValue(item.Key, out var tag) &&
|
||||
tag is IMemoryVariableRpc memoryVariableRuntime))
|
||||
{
|
||||
// 如果变量不存在,则添加错误信息到结果中并继续下一个变量的处理
|
||||
results["Memory"].TryAdd(item.Key, new OperResult(Localizer["VariableNotNull", item.Key]));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查变量的保护类型和远程写入权限
|
||||
if (tag.ProtectType == ProtectTypeEnum.ReadOnly)
|
||||
{
|
||||
results["Memory"].TryAdd(item.Key, new OperResult(Localizer["VariableReadOnly", item.Key]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tag.RpcWriteEnable)
|
||||
{
|
||||
results["Memory"].TryAdd(item.Key, new OperResult(Localizer["VariableWriteDisable", item.Key]));
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
|
||||
var variableResult = memoryVariableRuntime.MemoryVariableRpc(item.Value, cancellationToken);
|
||||
var end = DateTime.Now;
|
||||
|
||||
string operObj = tag.Name;
|
||||
string parJson = deviceDatas["Memory"][tag.Name];
|
||||
|
||||
if (!variableResult.IsSuccess || _rpcLogOptions.SuccessLog)
|
||||
{
|
||||
_logQueues.Enqueue(
|
||||
new RpcLog()
|
||||
{
|
||||
LogTime = start,
|
||||
ExecutionTime = (int)(end - start).TotalMilliseconds,
|
||||
OperateMessage = variableResult.IsSuccess ? null : variableResult.ToString(),
|
||||
IsSuccess = variableResult.IsSuccess,
|
||||
OperateMethod = AppResource.WriteVariable,
|
||||
OperateDevice = string.Empty,
|
||||
OperateObject = operObj,
|
||||
OperateSource = sourceDes,
|
||||
ParamJson = parJson,
|
||||
ResultJson = null
|
||||
});
|
||||
}
|
||||
|
||||
// 不返回详细错误
|
||||
if (!variableResult.IsSuccess)
|
||||
{
|
||||
var result1 = variableResult;
|
||||
result1.Exception = null;
|
||||
variableResult = result1;
|
||||
}
|
||||
|
||||
results["Memory"].Add(tag.Name, variableResult);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 将异常信息添加到结果字典中
|
||||
results["Memory"].Add(tag.Name, new OperResult(ex));
|
||||
}
|
||||
}
|
||||
|
||||
var deviceDict = GlobalData.Devices;
|
||||
|
||||
// 对每个要操作的变量进行检查和处理
|
||||
foreach (var deviceData in deviceDatas)
|
||||
// 对每个要操作的变量进行检查和处理(设备变量)
|
||||
foreach (var deviceData in deviceDatas.Where(a => (!a.Key.IsNullOrEmpty() && !a.Key.Equals("Memory", StringComparison.OrdinalIgnoreCase))))
|
||||
{
|
||||
// 查找设备是否存在
|
||||
if (!deviceDict.TryGetValue(deviceData.Key, out var device))
|
||||
{
|
||||
// 如果设备不存在,则添加错误信息到结果中并继续下一个设备的处理
|
||||
deviceData.Value.ForEach(a =>
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DeviceNotNull", deviceData.Key]))
|
||||
);
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DeviceNotNull", deviceData.Key])));
|
||||
continue;
|
||||
}
|
||||
|
||||
// 查找变量对应的设备
|
||||
var collect = device.Driver as IRpcDriver;
|
||||
collect ??= device.RpcDriver;
|
||||
var collect = device.Driver as IRpcDriver ?? device.RpcDriver;
|
||||
if (collect == null)
|
||||
{
|
||||
// 如果设备不存在,则添加错误信息到结果中并继续下一个设备的处理
|
||||
deviceData.Value.ForEach(a =>
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DriverNotNull", deviceData.Key]))
|
||||
);
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DriverNotNull", deviceData.Key])));
|
||||
continue;
|
||||
}
|
||||
// 检查设备状态,如果设备处于暂停状态,则添加相应的错误信息到结果中并继续下一个变量的处理
|
||||
|
||||
// 检查设备状态
|
||||
if (device.DeviceStatus == DeviceStatusEnum.Pause)
|
||||
{
|
||||
deviceData.Value.ForEach(a =>
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DevicePause", deviceData.Key]))
|
||||
);
|
||||
results[deviceData.Key].TryAdd(a.Key, new OperResult(Localizer["DevicePause", deviceData.Key])));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -90,7 +162,6 @@ internal sealed class RpcService : IRpcService
|
||||
// 查找变量是否存在
|
||||
if (!device.VariableRuntimes.TryGetValue(item.Key, out var tag))
|
||||
{
|
||||
// 如果变量不存在,则添加错误信息到结果中并继续下一个变量的处理
|
||||
results[deviceData.Key].TryAdd(item.Key, new OperResult(Localizer["VariableNotNull", item.Key]));
|
||||
continue;
|
||||
}
|
||||
@@ -101,6 +172,7 @@ internal sealed class RpcService : IRpcService
|
||||
results[deviceData.Key].TryAdd(item.Key, new OperResult(Localizer["VariableReadOnly", item.Key]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tag.RpcWriteEnable)
|
||||
{
|
||||
results[deviceData.Key].TryAdd(item.Key, new OperResult(Localizer["VariableWriteDisable", item.Key]));
|
||||
@@ -110,6 +182,7 @@ internal sealed class RpcService : IRpcService
|
||||
JToken tagValue = JTokenUtil.GetJTokenFromString(item.Value);
|
||||
bool isOtherMethodEmpty = string.IsNullOrEmpty(tag.OtherMethod);
|
||||
var collection = isOtherMethodEmpty ? writeVariables : writeMethods;
|
||||
|
||||
if (collection.TryGetValue(collect, out var value))
|
||||
{
|
||||
value.Add(tag, tagValue);
|
||||
@@ -121,26 +194,26 @@ internal sealed class RpcService : IRpcService
|
||||
}
|
||||
}
|
||||
}
|
||||
var writeVariableArrays = writeVariables.ToArray();
|
||||
|
||||
// 使用并行方式写入变量
|
||||
var writeVariableArrays = writeVariables;
|
||||
await writeVariableArrays.ParallelForEachAsync(async (driverData, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
// 调用设备的写入方法
|
||||
var result = await driverData.Key.InVokeWriteAsync(driverData.Value, cancellationToken).ConfigureAwait(false);
|
||||
var end = DateTime.Now;
|
||||
// 写入日志
|
||||
|
||||
foreach (var resultItem in result)
|
||||
{
|
||||
foreach (var variableResult in resultItem.Value)
|
||||
{
|
||||
string operObj = variableResult.Key;
|
||||
|
||||
string parJson = deviceDatas[resultItem.Key][variableResult.Key];
|
||||
|
||||
if (!variableResult.Value.IsSuccess || _rpcLogOptions.SuccessLog)
|
||||
{
|
||||
_logQueues.Enqueue(
|
||||
new RpcLog()
|
||||
{
|
||||
@@ -154,10 +227,9 @@ internal sealed class RpcService : IRpcService
|
||||
OperateSource = sourceDes,
|
||||
ParamJson = parJson,
|
||||
ResultJson = null
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 不返回详细错误
|
||||
if (!variableResult.Value.IsSuccess)
|
||||
{
|
||||
var result1 = variableResult.Value;
|
||||
@@ -166,44 +238,42 @@ internal sealed class RpcService : IRpcService
|
||||
}
|
||||
}
|
||||
|
||||
// 将结果添加到结果字典中
|
||||
results[resultItem.Key].AddRange(resultItem.Value);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 将异常信息添加到结果字典中
|
||||
foreach (var item in driverData.Value)
|
||||
{
|
||||
results[item.Key.DeviceName].Add(item.Key.Name, new OperResult(ex));
|
||||
}
|
||||
}
|
||||
}, cancellationToken).ConfigureAwait(false);
|
||||
var writeMethodArrays = writeMethods.ToArray();
|
||||
|
||||
// 使用并行方式执行方法
|
||||
var writeMethodArrays = writeMethods;
|
||||
await writeMethodArrays.ParallelForEachAsync(async (driverData, cancellationToken) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var start = DateTime.Now;
|
||||
// 调用设备的写入方法
|
||||
var result = await driverData.Key.InvokeMethodAsync(driverData.Value, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
Dictionary<string, string> operateMethods = driverData.Value.Select(a => a.Key).ToDictionary(a => a.Name, a => a.OtherMethod!);
|
||||
Dictionary<string, string> operateMethods = driverData.Value
|
||||
.Select(a => a.Key)
|
||||
.ToDictionary(a => a.Name, a => a.OtherMethod!);
|
||||
|
||||
var end = DateTime.Now;
|
||||
|
||||
// 写入日志
|
||||
foreach (var resultItem in result)
|
||||
{
|
||||
foreach (var variableResult in resultItem.Value)
|
||||
{
|
||||
string operObj = variableResult.Key;
|
||||
|
||||
string parJson = deviceDatas[resultItem.Key][variableResult.Key];
|
||||
|
||||
// 写入日志
|
||||
if (!variableResult.Value.IsSuccess || _rpcLogOptions.SuccessLog)
|
||||
{
|
||||
_logQueues.Enqueue(
|
||||
new RpcLog()
|
||||
{
|
||||
@@ -216,11 +286,12 @@ internal sealed class RpcService : IRpcService
|
||||
OperateObject = operObj,
|
||||
OperateSource = sourceDes,
|
||||
ParamJson = parJson?.ToString(),
|
||||
ResultJson = variableResult.Value is IOperResult<object> operResult ? operResult.Content?.ToSystemTextJsonString() : string.Empty
|
||||
}
|
||||
);
|
||||
ResultJson = variableResult.Value is IOperResult<object> operResult
|
||||
? operResult.Content?.ToSystemTextJsonString()
|
||||
: string.Empty
|
||||
});
|
||||
}
|
||||
|
||||
// 不返回详细错误
|
||||
if (!variableResult.Value.IsSuccess)
|
||||
{
|
||||
var result1 = variableResult.Value;
|
||||
@@ -234,7 +305,6 @@ internal sealed class RpcService : IRpcService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 将异常信息添加到结果字典中
|
||||
foreach (var item in driverData.Value)
|
||||
{
|
||||
results[item.Key.DeviceName].Add(item.Key.Name, new OperResult(ex));
|
||||
@@ -246,8 +316,6 @@ internal sealed class RpcService : IRpcService
|
||||
return new(results);
|
||||
}
|
||||
|
||||
private SqlSugarClient _db = DbContext.GetDB<RpcLog>(); // 创建一个新的数据库上下文实例
|
||||
|
||||
/// <summary>
|
||||
/// 异步执行RPC日志插入操作的方法。
|
||||
/// </summary>
|
||||
@@ -258,11 +326,12 @@ internal sealed class RpcService : IRpcService
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = _logQueues.ToListWithDequeue(); // 从日志队列中获取数据
|
||||
var data = _logQueues.ToListWithDequeue();
|
||||
if (data.Count > 0)
|
||||
{
|
||||
// 将数据插入到数据库中
|
||||
await _db.InsertableWithAttr(data).ExecuteCommandAsync(appLifetime.ApplicationStopping).ConfigureAwait(false);
|
||||
await _db.InsertableWithAttr(data)
|
||||
.ExecuteCommandAsync(appLifetime.ApplicationStopping)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -271,7 +340,7 @@ internal sealed class RpcService : IRpcService
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(3000).ConfigureAwait(false); // 在finally块中等待一段时间后继续下一次循环
|
||||
await Task.Delay(3000).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ internal static class RuntimeServiceHelper
|
||||
{
|
||||
newDeviceRuntime.Init(newChannelRuntime);
|
||||
|
||||
var newVariableRuntimes = (await GlobalData.VariableService.GetAllAsync(newDeviceRuntime.Id).ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
var newVariableRuntimes = (await GlobalData.VariableService.GetAllAsync(newDeviceRuntime.Id).ConfigureAwait(false)).AdaptEnumerableVariableRuntime();
|
||||
|
||||
newVariableRuntimes.ParallelForEach(item => item.Init(newDeviceRuntime));
|
||||
}
|
||||
@@ -79,7 +79,7 @@ internal static class RuntimeServiceHelper
|
||||
{
|
||||
newDeviceRuntime.Init(newChannelRuntime);
|
||||
|
||||
var newVariableRuntimes = (await GlobalData.VariableService.GetAllAsync(newDeviceRuntime.Id).ConfigureAwait(false)).AdaptListVariableRuntime();
|
||||
var newVariableRuntimes = (await GlobalData.VariableService.GetAllAsync(newDeviceRuntime.Id).ConfigureAwait(false)).AdaptEnumerableVariableRuntime();
|
||||
|
||||
newVariableRuntimes.ParallelForEach(item => item.Init(newDeviceRuntime));
|
||||
}
|
||||
@@ -113,8 +113,7 @@ internal static class RuntimeServiceHelper
|
||||
}
|
||||
if (deviceRuntime != null)
|
||||
{
|
||||
var list = deviceRuntime.VariableRuntimes.Select(a => a.Value).ToArray();
|
||||
list.ParallelForEach(a => a.Init(newDeviceRuntime));
|
||||
deviceRuntime.VariableRuntimes.ParallelForEach(a => a.Value.Init(newDeviceRuntime));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +140,7 @@ internal static class RuntimeServiceHelper
|
||||
{
|
||||
var channels = oldChannelRuntimes.ToArray();
|
||||
var devs = channels.SelectMany(a => a.DeviceRuntimes).Select(a => a.Value).ToArray();
|
||||
devs.SelectMany(a => a.VariableRuntimes).Select(a => a.Value).ToArray().ParallelForEach(a => a.Dispose());
|
||||
devs.SelectMany(a => a.VariableRuntimes).Select(a => a.Value).ParallelForEach(a => a.Dispose());
|
||||
devs.ParallelForEach(a => a.Dispose());
|
||||
channels.ParallelForEach(a => a.Dispose());
|
||||
|
||||
@@ -168,7 +167,7 @@ internal static class RuntimeServiceHelper
|
||||
foreach (var deviceRuntime in deviceRuntimes)
|
||||
{
|
||||
//也需要删除变量
|
||||
var vars = deviceRuntime.VariableRuntimes.Select(a => a.Value).ToArray();
|
||||
var vars = deviceRuntime.VariableRuntimes.Select(a => a.Value);
|
||||
vars.ParallelForEach(v =>
|
||||
{
|
||||
//需要重启业务线程
|
||||
@@ -205,7 +204,7 @@ internal static class RuntimeServiceHelper
|
||||
//也需要删除设备和变量
|
||||
devs.ParallelForEach((a =>
|
||||
{
|
||||
var vars = a.VariableRuntimes.Select(b => b.Value).ToArray();
|
||||
var vars = a.VariableRuntimes.Select(b => b.Value);
|
||||
ParallelExtensions.ParallelForEach(vars, (v =>
|
||||
{
|
||||
//需要重启业务线程
|
||||
@@ -305,7 +304,7 @@ internal static class RuntimeServiceHelper
|
||||
|
||||
public static void AddBusinessChangedDriver(HashSet<long> variableIds, ConcurrentHashSet<IDriver> changedDriver)
|
||||
{
|
||||
var data = GlobalData.IdVariables.FilterByKeys(variableIds).GroupBy(a => a.Value.DeviceRuntime);
|
||||
var data = GlobalData.IdVariables.FilterByKeys(variableIds).GroupBy(a => a.Value.DeviceRuntime).Where(a => a.Key != null);
|
||||
|
||||
foreach (var group in data)
|
||||
{
|
||||
|
@@ -167,7 +167,9 @@
|
||||
"Date": "Date"
|
||||
},
|
||||
"ThingsGateway.Gateway.Razor.ChannelDeviceTree": {
|
||||
"ShowType": "ShowType"
|
||||
"ChannelTable": "ChannelTable",
|
||||
"DeviceTable": "DeviceTable",
|
||||
"LogInfo": "LogInfo"
|
||||
},
|
||||
"ThingsGateway.Gateway.Razor.GatewayAbout": {
|
||||
"AuthName": "AuthName",
|
||||
|
@@ -167,7 +167,9 @@
|
||||
"Date": "日期"
|
||||
},
|
||||
"ThingsGateway.Gateway.Razor.ChannelDeviceTree": {
|
||||
"ShowType": "显示类型"
|
||||
"ChannelTable": "通道表格",
|
||||
"DeviceTable": "设备表格",
|
||||
"LogInfo": "日志信息"
|
||||
},
|
||||
"ThingsGateway.Gateway.Razor.GatewayAbout": {
|
||||
"AuthName": "公司名称",
|
||||
|
@@ -13,7 +13,7 @@ namespace ThingsGateway.Gateway.Razor;
|
||||
public partial class ChannelRuntimeInfo
|
||||
{
|
||||
#if !Management
|
||||
private string Height { get; set; } = "calc(100% - 270px)";
|
||||
private string Height { get; set; } = "calc(100% - 300px)";
|
||||
#else
|
||||
private string Height { get; set; } = "calc(100% - 330px)";
|
||||
|
||||
|
@@ -6,12 +6,11 @@
|
||||
<div class="listtree-view">
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<RadioList class="m-2" IsButton="true" AutoSelectFirstWhenValueIsNull="false" TValue="ShowTypeEnum?" ValueExpression=@(() => ShowType) Value="ShowType" ValueChanged="OnShowTypeChanged" ShowLabel="false" />
|
||||
<span style="color:var(--bs-body-color);opacity: 0.5;" class="text-h6 py-1">@GatewayLocalizer["DeviceList"]</span>
|
||||
|
||||
<SpinnerComponent @ref=Spinner></SpinnerComponent>
|
||||
<SpinnerComponent Size="Size.Small" @ref=Spinner></SpinnerComponent>
|
||||
</div>
|
||||
|
||||
<span style="color:var(--bs-body-color);opacity: 0.5;" class="text-h6 mb-2">@GatewayLocalizer["DeviceList"]</span>
|
||||
|
||||
<ContextMenuZone>
|
||||
<TreeView TItem="ChannelDeviceTreeItem" Items="Items" ShowIcon="false" ShowSearch IsAccordion=false IsVirtualize="true" OnTreeItemClick="OnTreeItemClick" OnSearchAsync="OnClickSearch" ModelEqualityComparer=ModelEqualityComparer ShowToolbar="true" >
|
||||
@@ -37,7 +36,7 @@
|
||||
@if (context.ChannelDevicePluginType == ChannelDevicePluginTypeEnum.Channel)
|
||||
{
|
||||
@* <Tooltip Title=@(GatewayLocalizer["AddDevice"]) Placement="Placement.Bottom"> *@
|
||||
<Button Size=Size.ExtraSmall Icon="fa-solid fa-plus" Color=Color.None OnClickWithoutRender=@(() => EditDevice(GatewayLocalizer["AddDevice"], context, ItemChangedType.Add))>
|
||||
<Button Size=Size.ExtraSmall Icon="fa-solid fa-circle-info" Color=Color.None OnClickWithoutRender=@(() => ShowLogInfo(context))>
|
||||
</Button>
|
||||
@* </Tooltip> *@
|
||||
|
||||
@@ -58,6 +57,10 @@
|
||||
@* //更新设备 *@
|
||||
@if (context.ChannelDevicePluginType == ChannelDevicePluginTypeEnum.Device)
|
||||
{
|
||||
|
||||
<Button Size=Size.ExtraSmall Icon="fa-solid fa-circle-info" Color=Color.None OnClickWithoutRender=@(() => ShowLogInfo(context))>
|
||||
</Button>
|
||||
|
||||
@* <Tooltip Title=@(GatewayLocalizer["UpdateDevice"]) Placement="Placement.Bottom"> *@
|
||||
<Button Size=Size.ExtraSmall Icon="fa-regular fa-pen-to-square" Color=Color.None OnClickWithoutRender=@(() => EditDevice(GatewayLocalizer["UpdateDevice"], context, ItemChangedType.Update))>
|
||||
</Button>
|
||||
@@ -77,6 +80,14 @@
|
||||
<ContextMenu style="max-height:800px" class="tgTree" OnBeforeShowCallback="OnBeforeShowCallback">
|
||||
@if (SelectModel != null)
|
||||
{
|
||||
<ContextMenuItem Icon="fa-solid fa-table" Text="@Localizer["ChannelTable"]" OnClick=@((a, b) => ShowChannelRuntimeTable(b as ChannelDeviceTreeItem))></ContextMenuItem>
|
||||
<ContextMenuItem Icon="fa-solid fa-table" Text="@Localizer["DeviceTable"]" OnClick=@((a, b) => ShowDeviceRuntimeTable(b as ChannelDeviceTreeItem))></ContextMenuItem>
|
||||
|
||||
|
||||
<ContextMenuItem Icon="fa-solid fa-circle-info" Disabled="SelectModel.ChannelDevicePluginType < ChannelDevicePluginTypeEnum.Channel" Text ="@Localizer["LogInfo"]" OnClick=@((a, b) => ShowLogInfo(b as ChannelDeviceTreeItem))></ContextMenuItem>
|
||||
|
||||
<ContextMenuDivider></ContextMenuDivider>
|
||||
|
||||
<ContextMenuItem Icon="fa-solid fa-plus" Disabled="(SelectModel.ChannelDevicePluginType > ChannelDevicePluginTypeEnum.Channel || !AuthorizeButton(AdminOperConst.Add))" Text="@GatewayLocalizer["AddChannel"]" OnClick=@((a, b) => EditChannel(a, b, ItemChangedType.Add))></ContextMenuItem>
|
||||
|
||||
<ContextMenuItem Icon="fa-regular fa-pen-to-square" Disabled="(SelectModel.ChannelDevicePluginType >= ChannelDevicePluginTypeEnum.Channel || !AuthorizeButton(AdminOperConst.Edit))" Text="@GatewayLocalizer["BatchEditChannel"]" OnClick=@((a, b) => BatchEditChannel(a, b))></ContextMenuItem>
|
||||
|
@@ -10,13 +10,11 @@
|
||||
|
||||
using Microsoft.AspNetCore.Components.Forms;
|
||||
using Microsoft.AspNetCore.Components.Web;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.Admin.Razor;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.Razor.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Gateway.Razor;
|
||||
@@ -39,32 +37,258 @@ public partial class ChannelDeviceTree : IDisposable
|
||||
return AppContext.IsHasButtonWithRole(RouteName, operate);
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<ShowTypeEnum?> ShowTypeChanged { get; set; }
|
||||
[Parameter]
|
||||
public ShowTypeEnum? ShowType { get; set; }
|
||||
[Inject]
|
||||
IJSRuntime JSRuntime { get; set; }
|
||||
private async Task OnShowTypeChanged(ShowTypeEnum? showType)
|
||||
#if !Management
|
||||
private async Task ShowChannelRuntimeTable(ChannelDeviceTreeItem channelDeviceTreeItem)
|
||||
{
|
||||
ShowType = showType;
|
||||
if (showType != null)
|
||||
await JSRuntime.SetLocalStorage("showType", ShowType);
|
||||
if (ShowTypeChanged.HasDelegate)
|
||||
await ShowTypeChanged.InvokeAsync(showType);
|
||||
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
|
||||
{
|
||||
var ChannelRuntimes = Enumerable.Repeat(channelRuntime, 1);
|
||||
await ShowChannelTable(ChannelRuntimes);
|
||||
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime))
|
||||
{
|
||||
|
||||
var ChannelRuntimes = Enumerable.Repeat(deviceRuntime.ChannelRuntime, 1);
|
||||
await ShowChannelTable(ChannelRuntimes);
|
||||
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName))
|
||||
{
|
||||
|
||||
var channels = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
var ChannelRuntimes = channels.Where(a => a.PluginName == pluginName);
|
||||
await ShowChannelTable(ChannelRuntimes);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var channels = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
|
||||
if (channelDeviceTreeItem.TryGetPluginType(out var pluginTypeEnum))
|
||||
{
|
||||
if (pluginTypeEnum != null)
|
||||
{
|
||||
var ChannelRuntimes = channels.Where(a => a.PluginType == pluginTypeEnum);
|
||||
await ShowChannelTable(ChannelRuntimes);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var ChannelRuntimes = channels;
|
||||
await ShowChannelTable(ChannelRuntimes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
|
||||
private async Task ShowDeviceRuntimeTable(ChannelDeviceTreeItem channelDeviceTreeItem)
|
||||
{
|
||||
if (firstRender)
|
||||
|
||||
|
||||
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
|
||||
{
|
||||
var showType = await JSRuntime!.GetLocalStorage<ShowTypeEnum>("showType");
|
||||
await OnShowTypeChanged(showType);
|
||||
StateHasChanged();
|
||||
|
||||
var DeviceRuntimes = channelRuntime.ReadDeviceRuntimes.Select(a => a.Value);
|
||||
|
||||
await ShowDeviceTable(DeviceRuntimes);
|
||||
|
||||
}
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
else if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime))
|
||||
{
|
||||
|
||||
var DeviceRuntimes = Enumerable.Repeat(deviceRuntime, 1);
|
||||
await ShowDeviceTable(DeviceRuntimes);
|
||||
|
||||
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName))
|
||||
{
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
|
||||
var DeviceRuntimes = devices.Where(a => a.PluginName == pluginName);
|
||||
await ShowDeviceTable(DeviceRuntimes);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (channelDeviceTreeItem.TryGetPluginType(out var pluginTypeEnum))
|
||||
{
|
||||
if (pluginTypeEnum != null)
|
||||
{
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
|
||||
var DeviceRuntimes = devices.Where(a => a.PluginType == pluginTypeEnum);
|
||||
await ShowDeviceTable(DeviceRuntimes);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
var DeviceRuntimes = devices;
|
||||
await ShowDeviceTable(DeviceRuntimes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private async Task ShowLogInfo(ChannelDeviceTreeItem channelDeviceTreeItem)
|
||||
{
|
||||
|
||||
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
|
||||
{
|
||||
await ShowLogInfo(channelRuntime);
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime))
|
||||
{
|
||||
|
||||
await ShowLogInfo(deviceRuntime);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private async Task ShowLogInfo(ChannelRuntime channel)
|
||||
{
|
||||
|
||||
var renderFragment = BootstrapDynamicComponent.CreateComponent(typeof(ChannelRuntimeInfo), new Dictionary<string, object?>()
|
||||
{
|
||||
{nameof(ChannelRuntimeInfo.ChannelRuntime),channel},
|
||||
}).Render();
|
||||
if (renderFragment != null)
|
||||
{
|
||||
var option = new WinBoxOption()
|
||||
{
|
||||
Title = Localizer["LogInfo"],
|
||||
ContentTemplate = renderFragment,
|
||||
Max = false,
|
||||
Width = "60%",
|
||||
Height = "80%",
|
||||
Top = "0%",
|
||||
Left = "10%",
|
||||
Background = "var(--bb-primary-color)",
|
||||
Overflow = true
|
||||
};
|
||||
await WinBoxService.Show(option);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
private async Task ShowLogInfo(DeviceRuntime device)
|
||||
{
|
||||
var renderFragment = BootstrapDynamicComponent.CreateComponent(typeof(DeviceRuntimeInfo), new Dictionary<string, object?>()
|
||||
{
|
||||
{nameof(DeviceRuntimeInfo.DeviceRuntime),device},
|
||||
}).Render();
|
||||
if (renderFragment != null)
|
||||
{
|
||||
var option = new WinBoxOption()
|
||||
{
|
||||
Title = Localizer["LogInfo"],
|
||||
ContentTemplate = renderFragment,
|
||||
Max = false,
|
||||
Width = "60%",
|
||||
Height = "80%",
|
||||
Top = "0%",
|
||||
Left = "10%",
|
||||
Background = "var(--bb-primary-color)",
|
||||
Overflow = true
|
||||
};
|
||||
await WinBoxService.Show(option);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private async Task ShowChannelTable(IEnumerable<ChannelRuntime> ChannelRuntimes)
|
||||
{
|
||||
var renderFragment = BootstrapDynamicComponent.CreateComponent(typeof(ChannelTable), new Dictionary<string, object?>()
|
||||
{
|
||||
{nameof(ChannelTable.SelectModel),SelectModel},
|
||||
{nameof(ChannelTable.Items),ChannelRuntimes},
|
||||
{nameof(ChannelTable.AutoRestartThread),AutoRestartThread},
|
||||
}).Render();
|
||||
if (renderFragment != null)
|
||||
{
|
||||
var option = new WinBoxOption()
|
||||
{
|
||||
Title = Localizer["ChannelTable"],
|
||||
ContentTemplate = renderFragment,
|
||||
Max = false,
|
||||
Width = "60%",
|
||||
Height = "60%",
|
||||
Top = "0%",
|
||||
Left = "10%",
|
||||
Background = "var(--bb-primary-color)",
|
||||
Overflow = true
|
||||
};
|
||||
await WinBoxService.Show(option);
|
||||
}
|
||||
}
|
||||
private async Task ShowDeviceTable(IEnumerable<DeviceRuntime> DeviceRuntimes)
|
||||
{
|
||||
var renderFragment = BootstrapDynamicComponent.CreateComponent(typeof(DeviceTable), new Dictionary<string, object?>()
|
||||
{
|
||||
{nameof(DeviceTable.SelectModel),SelectModel},
|
||||
{nameof(DeviceTable.Items),DeviceRuntimes},
|
||||
{nameof(DeviceTable.AutoRestartThread),AutoRestartThread},
|
||||
}).Render();
|
||||
if (renderFragment != null)
|
||||
{
|
||||
var option = new WinBoxOption()
|
||||
{
|
||||
Title = Localizer["DeviceTable"],
|
||||
ContentTemplate = renderFragment,
|
||||
Max = false,
|
||||
Width = "60%",
|
||||
Height = "60%",
|
||||
Top = "0%",
|
||||
Left = "10%",
|
||||
Background = "var(--bb-primary-color)",
|
||||
Overflow = true
|
||||
};
|
||||
await WinBoxService.Show(option);
|
||||
}
|
||||
}
|
||||
|
||||
[Inject]
|
||||
WinBoxService WinBoxService { get; set; }
|
||||
#endif
|
||||
|
||||
//[Parameter]
|
||||
//public EventCallback<ShowTypeEnum?> ShowTypeChanged { get; set; }
|
||||
//[Parameter]
|
||||
//public ShowTypeEnum? ShowType { get; set; }
|
||||
//[Inject]
|
||||
//IJSRuntime JSRuntime { get; set; }
|
||||
//private async Task OnShowTypeChanged(ShowTypeEnum? showType)
|
||||
//{
|
||||
// ShowType = showType;
|
||||
// if (showType != null)
|
||||
// await JSRuntime.SetLocalStorage("showType", ShowType);
|
||||
// if (ShowTypeChanged.HasDelegate)
|
||||
// await ShowTypeChanged.InvokeAsync(showType);
|
||||
//}
|
||||
|
||||
//protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
//{
|
||||
// if (firstRender)
|
||||
// {
|
||||
// var showType = await JSRuntime!.GetLocalStorage<ShowTypeEnum>("showType");
|
||||
// await OnShowTypeChanged(showType);
|
||||
// StateHasChanged();
|
||||
// }
|
||||
// await base.OnAfterRenderAsync(firstRender);
|
||||
//}
|
||||
|
||||
|
||||
[Parameter]
|
||||
public bool AutoRestartThread { get; set; }
|
||||
@@ -99,8 +323,6 @@ public partial class ChannelDeviceTree : IDisposable
|
||||
[Inject]
|
||||
DialogService DialogService { get; set; }
|
||||
|
||||
[Inject]
|
||||
WinBoxService WinBoxService { get; set; }
|
||||
|
||||
[Inject]
|
||||
[NotNull]
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
<DeviceRuntimeInfo1 DeviceRuntime="DeviceRuntime" />
|
||||
|
||||
<LogConsole HeightString="calc(100% - 270px)" LogLevel=@(LogLevel)
|
||||
<LogConsole HeightString=@Height LogLevel=@(LogLevel)
|
||||
LogLevelChanged="async (logLevel)=>
|
||||
{
|
||||
LogLevel = logLevel;
|
||||
|
@@ -12,6 +12,13 @@ namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
public partial class DeviceRuntimeInfo
|
||||
{
|
||||
|
||||
#if !Management
|
||||
private string Height { get; set; } = "calc(100% - 300px)";
|
||||
#else
|
||||
private string Height { get; set; } = "calc(100% - 330px)";
|
||||
#endif
|
||||
|
||||
[Parameter, EditorRequired]
|
||||
public DeviceRuntime DeviceRuntime { get; set; }
|
||||
|
||||
|
@@ -1,31 +0,0 @@
|
||||
@inherits ComponentDefault
|
||||
@using ThingsGateway.Admin.Application
|
||||
@using ThingsGateway.Admin.Razor
|
||||
@using ThingsGateway.Gateway.Application
|
||||
@namespace ThingsGateway.Gateway.Razor
|
||||
|
||||
|
||||
@if (ShowType == ShowTypeEnum.VariableTable)
|
||||
{
|
||||
<VariableRuntimeInfo Items="VariableRuntimes" SelectModel="SelectModel" AutoRestartThread="AutoRestartThread" />
|
||||
}
|
||||
else if (ShowType == ShowTypeEnum.LogInfo)
|
||||
{
|
||||
if (GlobalData.ReadOnlyIdDevices.TryGetValue(ShowDeviceRuntime, out var device))
|
||||
{
|
||||
<DeviceRuntimeInfo DeviceRuntime="device" />
|
||||
}
|
||||
if (GlobalData.ReadOnlyIdChannels.TryGetValue(ShowChannelRuntime, out var channel))
|
||||
{
|
||||
<ChannelRuntimeInfo ChannelRuntime="channel" />
|
||||
}
|
||||
}
|
||||
else if (ShowType == ShowTypeEnum.ChannelTable)
|
||||
{
|
||||
<ChannelTable SelectModel="SelectModel" Items="ChannelRuntimes" AutoRestartThread=AutoRestartThread />
|
||||
}
|
||||
|
||||
else if (ShowType == ShowTypeEnum.DeviceTable)
|
||||
{
|
||||
<DeviceTable SelectModel="SelectModel" Items="DeviceRuntimes" AutoRestartThread=AutoRestartThread />
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Gateway.Razor;
|
||||
|
||||
public partial class GatewayInfo : IDisposable
|
||||
{
|
||||
[Parameter]
|
||||
public ChannelDeviceTreeItem SelectModel { get; set; } = new() { ChannelDevicePluginType = ChannelDevicePluginTypeEnum.PluginType, PluginType = null };
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<VariableRuntime> VariableRuntimes { get; set; } = Enumerable.Empty<VariableRuntime>();
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<ChannelRuntime> ChannelRuntimes { get; set; } = Enumerable.Empty<ChannelRuntime>();
|
||||
|
||||
[Parameter]
|
||||
public IEnumerable<DeviceRuntime> DeviceRuntimes { get; set; } = Enumerable.Empty<DeviceRuntime>();
|
||||
[Parameter]
|
||||
public long ShowChannelRuntime { get; set; }
|
||||
[Parameter]
|
||||
public long ShowDeviceRuntime { get; set; }
|
||||
[Parameter]
|
||||
public ShowTypeEnum? ShowType { get; set; }
|
||||
[Parameter]
|
||||
public bool AutoRestartThread { get; set; } = true;
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_ = RunTimerAsync();
|
||||
base.OnInitialized();
|
||||
}
|
||||
|
||||
private bool Disposed;
|
||||
private async Task RunTimerAsync()
|
||||
{
|
||||
while (!Disposed)
|
||||
{
|
||||
try
|
||||
{
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
NewLife.Log.XTrace.WriteException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Disposed = true;
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
@@ -15,7 +15,7 @@
|
||||
|
||||
<Card IsShadow=true class="h-100 me-1" Color="Color.Primary">
|
||||
<BodyTemplate>
|
||||
<ChannelDeviceTree @bind-ShowType=ShowType AutoRestartThread="AutoRestartThread"
|
||||
<ChannelDeviceTree AutoRestartThread="AutoRestartThread"
|
||||
ChannelDeviceChanged="TreeChangedAsync" Value="SelectModel"></ChannelDeviceTree>
|
||||
</BodyTemplate>
|
||||
</Card>
|
||||
@@ -24,7 +24,7 @@
|
||||
</FirstPaneTemplate>
|
||||
<SecondPaneTemplate>
|
||||
|
||||
<GatewayInfo AutoRestartThread=AutoRestartThread SelectModel=SelectModel ShowChannelRuntime=ShowChannelRuntime ShowDeviceRuntime=ShowDeviceRuntime ShowType=ShowType VariableRuntimes=VariableRuntimes ChannelRuntimes="ChannelRuntimes" DeviceRuntimes="DeviceRuntimes" />
|
||||
<VariableRuntimeInfo Items="VariableRuntimes" SelectModel="SelectModel" AutoRestartThread="AutoRestartThread" />
|
||||
|
||||
</SecondPaneTemplate>
|
||||
</Split>
|
||||
|
@@ -18,15 +18,15 @@ public partial class GatewayMonitorPage
|
||||
|
||||
private async Task TreeChangedAsync(ChannelDeviceTreeItem channelDeviceTreeItem)
|
||||
{
|
||||
ShowChannelRuntime = 0;
|
||||
ShowDeviceRuntime = 0;
|
||||
//ShowChannelRuntime = 0;
|
||||
//ShowDeviceRuntime = 0;
|
||||
SelectModel = channelDeviceTreeItem;
|
||||
var variables = await GlobalData.GetCurrentUserIdVariables().ConfigureAwait(false);
|
||||
var channels = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
if (channelDeviceTreeItem.TryGetChannelRuntime(out var channelRuntime))
|
||||
{
|
||||
ShowChannelRuntime = channelRuntime.Id;
|
||||
//ShowChannelRuntime = channelRuntime.Id;
|
||||
|
||||
if (channelRuntime.IsCollect == true)
|
||||
{
|
||||
@@ -36,12 +36,12 @@ public partial class GatewayMonitorPage
|
||||
{
|
||||
VariableRuntimes = channelRuntime.ReadDeviceRuntimes.Where(a => a.Value?.Driver?.IdVariableRuntimes != null).SelectMany(a => a.Value?.Driver?.IdVariableRuntimes?.Where(a => a.Value != null)?.Select(a => a.Value)).Where(a => a != null);
|
||||
}
|
||||
ChannelRuntimes = Enumerable.Repeat(channelRuntime, 1);
|
||||
DeviceRuntimes = channelRuntime.ReadDeviceRuntimes.Select(a => a.Value);
|
||||
//ChannelRuntimes = Enumerable.Repeat(channelRuntime, 1);
|
||||
//DeviceRuntimes = channelRuntime.ReadDeviceRuntimes.Select(a => a.Value);
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetDeviceRuntime(out var deviceRuntime))
|
||||
{
|
||||
ShowDeviceRuntime = deviceRuntime.Id;
|
||||
//ShowDeviceRuntime = deviceRuntime.Id;
|
||||
if (deviceRuntime.IsCollect == true)
|
||||
{
|
||||
VariableRuntimes = deviceRuntime.ReadOnlyVariableRuntimes.Select(a => a.Value).Where(a => a != null);
|
||||
@@ -65,8 +65,8 @@ public partial class GatewayMonitorPage
|
||||
}
|
||||
|
||||
}
|
||||
ChannelRuntimes = Enumerable.Repeat(deviceRuntime.ChannelRuntime, 1);
|
||||
DeviceRuntimes = Enumerable.Repeat(deviceRuntime, 1);
|
||||
//ChannelRuntimes = Enumerable.Repeat(deviceRuntime.ChannelRuntime, 1);
|
||||
//DeviceRuntimes = Enumerable.Repeat(deviceRuntime, 1);
|
||||
}
|
||||
else if (channelDeviceTreeItem.TryGetPluginName(out var pluginName))
|
||||
{
|
||||
@@ -80,26 +80,26 @@ public partial class GatewayMonitorPage
|
||||
VariableRuntimes = channels.Where(a => a.PluginName == pluginName).SelectMany(a => a.ReadDeviceRuntimes).Where(a => a.Value.Driver?.IdVariableRuntimes != null).SelectMany(a => a.Value.Driver?.IdVariableRuntimes).Select(a => a.Value);
|
||||
}
|
||||
|
||||
ChannelRuntimes = channels.Where(a => a.PluginName == pluginName);
|
||||
DeviceRuntimes = devices.Where(a => a.PluginName == pluginName);
|
||||
//ChannelRuntimes = channels.Where(a => a.PluginName == pluginName);
|
||||
//DeviceRuntimes = devices.Where(a => a.PluginName == pluginName);
|
||||
}
|
||||
else
|
||||
{
|
||||
VariableRuntimes = variables.Where(a => a != null);
|
||||
|
||||
if (channelDeviceTreeItem.TryGetPluginType(out var pluginTypeEnum))
|
||||
{
|
||||
if (pluginTypeEnum != null)
|
||||
{
|
||||
ChannelRuntimes = channels.Where(a => a.PluginType == pluginTypeEnum);
|
||||
DeviceRuntimes = devices.Where(a => a.PluginType == pluginTypeEnum);
|
||||
}
|
||||
else
|
||||
{
|
||||
ChannelRuntimes = channels;
|
||||
DeviceRuntimes = devices;
|
||||
}
|
||||
}
|
||||
//if (channelDeviceTreeItem.TryGetPluginType(out var pluginTypeEnum))
|
||||
//{
|
||||
// if (pluginTypeEnum != null)
|
||||
// {
|
||||
// ChannelRuntimes = channels.Where(a => a.PluginType == pluginTypeEnum);
|
||||
// DeviceRuntimes = devices.Where(a => a.PluginType == pluginTypeEnum);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ChannelRuntimes = channels;
|
||||
// DeviceRuntimes = devices;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
@@ -113,11 +113,11 @@ public partial class GatewayMonitorPage
|
||||
}
|
||||
public IEnumerable<VariableRuntime> VariableRuntimes { get; set; } = Enumerable.Empty<VariableRuntime>();
|
||||
|
||||
public IEnumerable<ChannelRuntime> ChannelRuntimes { get; set; } = Enumerable.Empty<ChannelRuntime>();
|
||||
public IEnumerable<DeviceRuntime> DeviceRuntimes { get; set; } = Enumerable.Empty<DeviceRuntime>();
|
||||
//public IEnumerable<ChannelRuntime> ChannelRuntimes { get; set; } = Enumerable.Empty<ChannelRuntime>();
|
||||
//public IEnumerable<DeviceRuntime> DeviceRuntimes { get; set; } = Enumerable.Empty<DeviceRuntime>();
|
||||
|
||||
private long ShowChannelRuntime { get; set; }
|
||||
private long ShowDeviceRuntime { get; set; }
|
||||
public ShowTypeEnum? ShowType { get; set; }
|
||||
//private long ShowChannelRuntime { get; set; }
|
||||
//private long ShowDeviceRuntime { get; set; }
|
||||
//public ShowTypeEnum? ShowType { get; set; }
|
||||
private bool AutoRestartThread { get; set; } = true;
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@
|
||||
LogLevel=a;
|
||||
if(LogLevelChanged!=null)
|
||||
{
|
||||
await LogLevelChanged?.Invoke(a);
|
||||
await LogLevelChanged.Invoke(a);
|
||||
}
|
||||
}" LogPath=@LogPath HeaderText=@HeaderText></ThingsGateway.Debug.LogConsole>
|
||||
|
||||
|
@@ -47,7 +47,7 @@ public partial class RedundancyOptionsPage
|
||||
LogLevel = logLevel;
|
||||
if (LogLevelChanged != null)
|
||||
{
|
||||
await LogLevelChanged?.Invoke(LogLevel);
|
||||
await LogLevelChanged.Invoke(LogLevel);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
@@ -15,8 +15,8 @@ public partial class DragAndDrop
|
||||
{
|
||||
private readonly BlazorDiagram _blazorDiagram = new(new BlazorDiagramOptions
|
||||
{
|
||||
GridSize = 75,
|
||||
GridSnapToCenter = true,
|
||||
GridSize = null,
|
||||
GridSnapToCenter = false,
|
||||
});
|
||||
private string? _draggedType;
|
||||
[Inject]
|
||||
|
@@ -77,7 +77,7 @@ ShowDesign=true;}" />
|
||||
</RowButtonTemplate>
|
||||
</AdminTable>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 h-100">
|
||||
<div class="col-12 col-sm-6 h-100 ps-2">
|
||||
|
||||
<RulesStatus RulesId="RulesId"></RulesStatus>
|
||||
</div>
|
||||
|
@@ -11,7 +11,7 @@
|
||||
|
||||
@if (_rules != null)
|
||||
{
|
||||
<LogConsole HeightString="100%" LogLevel=@(LogLevel) LogLevelChanged="(a)=>{
|
||||
<LogConsole HeightString="calc(100% - 50px)" LogLevel=@(LogLevel) LogLevelChanged="(a)=>{
|
||||
LogLevel=a;
|
||||
return RulesEngineHostedService.SetRulesLogLevelAsync(RulesId, a);
|
||||
}" LogPath=@LogPath HeaderText="@_rules.Name"></LogConsole>
|
||||
|
@@ -20,9 +20,9 @@ namespace BenchmarkConsoleApp
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
public static int ClientCount = 30;
|
||||
public static int TaskNumberOfItems = 30;
|
||||
public static int NumberOfItems = 30;
|
||||
public static int ClientCount = 3;
|
||||
public static int TaskNumberOfItems = 1;
|
||||
public static int NumberOfItems = 3;
|
||||
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
|
@@ -11,6 +11,9 @@
|
||||
//修改自https://github.com/dathlin/OpcUaHelper 与OPC基金会net库
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
#if NET8_0_OR_GREATER
|
||||
using System.Collections.Frozen;
|
||||
#endif
|
||||
|
||||
namespace ThingsGateway.Foundation.OpcUa;
|
||||
|
||||
@@ -28,7 +31,7 @@ public delegate void LogEventHandler(byte level, object sender, string message,
|
||||
/// <summary>
|
||||
/// OpcUaMaster
|
||||
/// </summary>
|
||||
public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
public class OpcUaMaster : IAsyncDisposable
|
||||
{
|
||||
#region 属性,变量等
|
||||
|
||||
@@ -157,9 +160,9 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
},
|
||||
};
|
||||
|
||||
certificateValidator.Update(m_configuration);
|
||||
Task.Run(() => certificateValidator.UpdateAsync(m_configuration)).GetAwaiter().GetResult();
|
||||
|
||||
m_configuration.Validate(ApplicationType.Client);
|
||||
Task.Run(() => m_configuration.ValidateAsync(ApplicationType.Client)).GetAwaiter().GetResult();
|
||||
m_application.ApplicationConfiguration = m_configuration;
|
||||
}
|
||||
|
||||
@@ -209,10 +212,13 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
/// </summary>
|
||||
public ApplicationConfiguration AppConfig => m_configuration;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 连接状态
|
||||
/// </summary>
|
||||
public bool Connected => m_session?.Connected == true;
|
||||
public bool Connected => m_session?.Connected == true && connected;
|
||||
|
||||
private bool connected = false;
|
||||
|
||||
/// <summary>
|
||||
/// OpcUaMaster
|
||||
@@ -367,19 +373,21 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
/// </summary>
|
||||
/// <param name="tagParent">方法的父节点tag</param>
|
||||
/// <param name="tag">方法的节点tag</param>
|
||||
/// <param name="cancellationToken">cancellationToken</param>
|
||||
/// <param name="args">传递的参数</param>
|
||||
/// <returns>输出的结果值</returns>
|
||||
public object[] CallMethodByNodeId(string tagParent, string tag, params object[] args)
|
||||
public async Task<IList<object>> CallMethodByNodeIdAsync(string tagParent, string tag, CancellationToken cancellationToken, params object[] args)
|
||||
{
|
||||
if (m_session == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
IList<object> outputArguments = m_session.Call(
|
||||
IList<object> outputArguments = await m_session.CallAsync(
|
||||
new NodeId(tagParent),
|
||||
new NodeId(tag),
|
||||
args);
|
||||
cancellationToken,
|
||||
args).ConfigureAwait(false);
|
||||
|
||||
return outputArguments.ToArray();
|
||||
}
|
||||
@@ -392,39 +400,15 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
await ConnectAsync(OpcUaProperty.OpcUrl, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接。
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
PrivateDisconnect();
|
||||
// disconnect any existing session.
|
||||
if (m_session != null)
|
||||
{
|
||||
m_session = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 断开连接。
|
||||
/// </summary>
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
await PrivateDisconnectAsync().ConfigureAwait(false);
|
||||
// disconnect any existing session.
|
||||
if (m_session != null)
|
||||
{
|
||||
m_session = null;
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
Disconnect();
|
||||
_variableDicts?.Clear();
|
||||
_subscriptionDicts?.Clear();
|
||||
waitLock?.Dispose();
|
||||
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await DisconnectAsync().ConfigureAwait(false);
|
||||
@@ -573,8 +557,9 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
/// 读取一个节点的所有属性
|
||||
/// </summary>
|
||||
/// <param name="tag">节点信息</param>
|
||||
/// <param name="cancellationToken">cancellationToken</param>
|
||||
/// <returns>节点的特性值</returns>
|
||||
public OPCNodeAttribute[] ReadNoteAttributes(string tag)
|
||||
public async Task<OPCNodeAttribute[]> ReadNoteAttributesAsync(string tag, CancellationToken cancellationToken)
|
||||
{
|
||||
NodeId sourceId = new(tag);
|
||||
ReadValueIdCollection nodesToRead = new();
|
||||
@@ -608,7 +593,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
};
|
||||
|
||||
// fetch property references from the server.
|
||||
ReferenceDescriptionCollection references = OpcUaUtils.Browse(m_session, nodesToBrowse, false);
|
||||
ReferenceDescriptionCollection references = await OpcUaUtils.BrowseAsync(m_session, nodesToBrowse, false, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (references == null)
|
||||
{
|
||||
@@ -631,15 +616,15 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
nodesToRead.Add(nodeToRead);
|
||||
}
|
||||
|
||||
// read all values.
|
||||
|
||||
m_session.Read(
|
||||
null,
|
||||
0,
|
||||
TimestampsToReturn.Neither,
|
||||
nodesToRead,
|
||||
out DataValueCollection results,
|
||||
out DiagnosticInfoCollection diagnosticInfos);
|
||||
// 读取当前的值
|
||||
var result = await m_session.ReadAsync(
|
||||
null,
|
||||
0,
|
||||
TimestampsToReturn.Neither,
|
||||
nodesToRead,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
var results = result.Results;
|
||||
var diagnosticInfos = result.DiagnosticInfos;
|
||||
|
||||
ClientBase.ValidateResponse(results, nodesToRead);
|
||||
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToRead);
|
||||
@@ -661,7 +646,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
}
|
||||
|
||||
// get the name of the attribute.
|
||||
item.Name = Attributes.GetBrowseName(nodesToRead[ii].AttributeId);
|
||||
item.Name = GetBrowseName(nodesToRead[ii].AttributeId);
|
||||
|
||||
// display any unexpected error.
|
||||
if (StatusCode.IsBad(results[ii].StatusCode))
|
||||
@@ -726,7 +711,34 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
|
||||
return nodeAttribute.ToArray();
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the browse name for the attribute.
|
||||
/// </summary>
|
||||
public static string GetBrowseName(uint identifier)
|
||||
{
|
||||
return s_attributesIdToName.Value.TryGetValue(identifier, out string name)
|
||||
? name : string.Empty;
|
||||
}
|
||||
/// <summary>
|
||||
/// Creates a dictionary of identifiers to browse names for the attributes.
|
||||
/// </summary>
|
||||
private static readonly Lazy<IReadOnlyDictionary<long, string>> s_attributesIdToName =
|
||||
new(() =>
|
||||
{
|
||||
System.Reflection.FieldInfo[] fields = typeof(Attributes).GetFields(
|
||||
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||
|
||||
var keyValuePairs = new Dictionary<long, string>();
|
||||
foreach (System.Reflection.FieldInfo field in fields)
|
||||
{
|
||||
keyValuePairs.TryAdd(Convert.ToInt64(field.GetValue(typeof(Attributes))), field.Name);
|
||||
}
|
||||
#if NET8_0_OR_GREATER
|
||||
return keyValuePairs.ToFrozenDictionary();
|
||||
#else
|
||||
return new System.Collections.ObjectModel.ReadOnlyDictionary<long, string>(keyValuePairs);
|
||||
#endif
|
||||
});
|
||||
/// <summary>
|
||||
/// 移除所有的订阅消息
|
||||
/// </summary>
|
||||
@@ -825,7 +837,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
{
|
||||
foreach (var value in monitoreditem.DequeueValues())
|
||||
{
|
||||
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false, StatusCode.IsGood(value.StatusCode));
|
||||
var variableNode = ReadNode(monitoreditem.StartNodeId.ToString(), false, StatusCode.IsGood(value.StatusCode)).GetAwaiter().GetResult();
|
||||
|
||||
if (value.Value != null)
|
||||
{
|
||||
@@ -861,7 +873,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
/// <returns></returns>
|
||||
public async Task CheckApplicationInstanceCertificate()
|
||||
{
|
||||
await m_application.CheckApplicationInstanceCertificates(true, 1200).ConfigureAwait(false);
|
||||
await m_application.CheckApplicationInstanceCertificatesAsync(true, 1200).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SemaphoreSlim waitLock = new(1, 1);
|
||||
@@ -910,11 +922,12 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
}
|
||||
//创建本地证书
|
||||
if (useSecurity)
|
||||
await m_application.CheckApplicationInstanceCertificates(true, 1200, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
m_session = await Opc.Ua.Client.Session.Create(
|
||||
await m_application.CheckApplicationInstanceCertificatesAsync(true, 1200, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
m_session = await Opc.Ua.Client.Session.CreateAsync(
|
||||
DefaultSessionFactory.Instance,
|
||||
m_configuration,
|
||||
(ITransportWaitingConnection)null,
|
||||
endpoint,
|
||||
false,
|
||||
OpcUaProperty.CheckDomain,
|
||||
@@ -923,6 +936,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
userIdentity,
|
||||
null, cancellationToken
|
||||
).ConfigureAwait(false);
|
||||
connected = true;
|
||||
|
||||
m_session.KeepAliveInterval = OpcUaProperty.KeepAliveInterval == 0 ? 60000 : OpcUaProperty.KeepAliveInterval;
|
||||
m_session.KeepAlive += Session_KeepAlive;
|
||||
@@ -940,33 +954,11 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void PrivateDisconnect()
|
||||
{
|
||||
bool state = m_session?.Connected == true;
|
||||
|
||||
if (m_reConnectHandler != null)
|
||||
{
|
||||
try { m_reConnectHandler.Dispose(); } catch { }
|
||||
m_reConnectHandler = null;
|
||||
}
|
||||
if (m_session != null)
|
||||
{
|
||||
m_session.KeepAlive -= Session_KeepAlive;
|
||||
m_session.Close(10000);
|
||||
m_session.Dispose();
|
||||
m_session = null;
|
||||
}
|
||||
|
||||
if (state)
|
||||
{
|
||||
Log(2, null, "Disconnected");
|
||||
DoConnectComplete(false);
|
||||
}
|
||||
}
|
||||
private async Task PrivateDisconnectAsync()
|
||||
{
|
||||
bool state = m_session?.Connected == true;
|
||||
|
||||
connected = false;
|
||||
if (m_reConnectHandler != null)
|
||||
{
|
||||
try { m_reConnectHandler.Dispose(); } catch { }
|
||||
@@ -1035,7 +1027,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
/// <summary>
|
||||
/// 从服务器或缓存读取节点
|
||||
/// </summary>
|
||||
private VariableNode ReadNode(string nodeIdStr, bool isOnlyServer = true, bool cache = true)
|
||||
private async Task<VariableNode> ReadNode(string nodeIdStr, bool isOnlyServer = true, bool cache = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (!isOnlyServer)
|
||||
{
|
||||
@@ -1061,16 +1053,17 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
}
|
||||
|
||||
// read from server.
|
||||
DataValueCollection values = null;
|
||||
DiagnosticInfoCollection diagnosticInfos = null;
|
||||
|
||||
ResponseHeader responseHeader = m_session.Read(
|
||||
null,
|
||||
0,
|
||||
TimestampsToReturn.Neither,
|
||||
itemsToRead,
|
||||
out values,
|
||||
out diagnosticInfos);
|
||||
var result = await m_session.ReadAsync(
|
||||
null,
|
||||
0,
|
||||
TimestampsToReturn.Neither,
|
||||
itemsToRead,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
var values = result.Results;
|
||||
var diagnosticInfos = result.DiagnosticInfos;
|
||||
var responseHeader = result.ResponseHeader;
|
||||
|
||||
|
||||
VariableNode variableNode = GetVariableNodes(itemsToRead, values, diagnosticInfos, responseHeader).FirstOrDefault();
|
||||
|
||||
@@ -1121,7 +1114,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
VariableNode variableNode = GetVariableNodes(itemsToRead, values, diagnosticInfos, responseHeader).FirstOrDefault();
|
||||
|
||||
if (OpcUaProperty.LoadType && variableNode.DataType != NodeId.Null && (await TypeInfo.GetBuiltInTypeAsync(variableNode.DataType, m_session.SystemContext.TypeTable, cancellationToken).ConfigureAwait(false)) == BuiltInType.ExtensionObject)
|
||||
await typeSystem.LoadType(variableNode.DataType, ct: cancellationToken).ConfigureAwait(false);
|
||||
await typeSystem.LoadTypeAsync(variableNode.DataType, ct: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (cache)
|
||||
_variableDicts.AddOrUpdate(nodeIdStr, a => variableNode, (a, b) => variableNode);
|
||||
@@ -1222,7 +1215,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
_variableDicts.AddOrUpdate(nodeIdStrs[i], a => node, (a, b) => node);
|
||||
if (node.DataType != NodeId.Null && (await TypeInfo.GetBuiltInTypeAsync(node.DataType, m_session.SystemContext.TypeTable, cancellationToken).ConfigureAwait(false)) == BuiltInType.ExtensionObject)
|
||||
{
|
||||
await typeSystem.LoadType(node.DataType, ct: cancellationToken).ConfigureAwait(false);
|
||||
await typeSystem.LoadTypeAsync(node.DataType, ct: cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
result.Add(node);
|
||||
@@ -1314,7 +1307,7 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
continue;
|
||||
}
|
||||
|
||||
item.Name = Attributes.GetBrowseName(nodesToRead[ii].AttributeId);
|
||||
item.Name = GetBrowseName(nodesToRead[ii].AttributeId);
|
||||
if (StatusCode.IsBad(nodeValue.StatusCode))
|
||||
{
|
||||
item.Type = Utils.Format("{0}", Attributes.GetDataTypeId(nodesToRead[ii].AttributeId));
|
||||
@@ -1361,6 +1354,8 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
Log(2, null, "Reconnected : success");
|
||||
|
||||
if (!Object.ReferenceEquals(sender, m_reConnectHandler))
|
||||
{
|
||||
return;
|
||||
@@ -1374,6 +1369,8 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
{
|
||||
var session = m_session;
|
||||
m_session = m_reConnectHandler.Session;
|
||||
connected = true;
|
||||
|
||||
Utils.SilentDispose(session);
|
||||
}
|
||||
}
|
||||
@@ -1401,6 +1398,8 @@ public class OpcUaMaster : IDisposable, IAsyncDisposable
|
||||
|
||||
if (ServiceResult.IsBad(e.Status))
|
||||
{
|
||||
connected = false;
|
||||
|
||||
if (m_reConnectHandler == null)
|
||||
{
|
||||
Log(3, null, "Reconnecting : {0}", e.Status.ToString());
|
||||
|
@@ -13,7 +13,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.244" />
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.377.21" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@@ -56,133 +56,6 @@ public static class OpcUaUtils
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browses the address space and returns the references found.
|
||||
/// </summary>
|
||||
/// <param name="session">The session.</param>
|
||||
/// <param name="nodesToBrowse">The set of browse operations to perform.</param>
|
||||
/// <param name="throwOnError">if set to <c>true</c> a exception will be thrown on an error.</param>
|
||||
/// <returns>
|
||||
/// The references found. Null if an error occurred.
|
||||
/// </returns>
|
||||
public static ReferenceDescriptionCollection Browse(ISession session, BrowseDescriptionCollection nodesToBrowse, bool throwOnError)
|
||||
{
|
||||
try
|
||||
{
|
||||
ReferenceDescriptionCollection references = new();
|
||||
BrowseDescriptionCollection unprocessedOperations = new();
|
||||
|
||||
while (nodesToBrowse.Count > 0)
|
||||
{
|
||||
// start the browse operation.
|
||||
|
||||
session.Browse(
|
||||
null,
|
||||
null,
|
||||
0,
|
||||
nodesToBrowse,
|
||||
out BrowseResultCollection results,
|
||||
out DiagnosticInfoCollection diagnosticInfos);
|
||||
|
||||
ClientBase.ValidateResponse(results, nodesToBrowse);
|
||||
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, nodesToBrowse);
|
||||
|
||||
ByteStringCollection continuationPoints = new();
|
||||
|
||||
for (int ii = 0; ii < nodesToBrowse.Count; ii++)
|
||||
{
|
||||
// check for error.
|
||||
if (StatusCode.IsBad(results[ii].StatusCode))
|
||||
{
|
||||
// this error indicates that the server does not have enough simultaneously active
|
||||
// continuation points. This request will need to be resent after the other operations
|
||||
// have been completed and their continuation points released.
|
||||
if (results[ii].StatusCode == StatusCodes.BadNoContinuationPoints)
|
||||
{
|
||||
unprocessedOperations.Add(nodesToBrowse[ii]);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if all references have been fetched.
|
||||
if (results[ii].References.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// save results.
|
||||
references.AddRange(results[ii].References);
|
||||
|
||||
// check for continuation point.
|
||||
if (results[ii].ContinuationPoint != null)
|
||||
{
|
||||
continuationPoints.Add(results[ii].ContinuationPoint);
|
||||
}
|
||||
}
|
||||
|
||||
// process continuation points.
|
||||
ByteStringCollection revisedContiuationPoints = new();
|
||||
|
||||
while (continuationPoints.Count > 0)
|
||||
{
|
||||
// continue browse operation.
|
||||
session.BrowseNext(
|
||||
null,
|
||||
true,
|
||||
continuationPoints,
|
||||
out results,
|
||||
out diagnosticInfos);
|
||||
|
||||
ClientBase.ValidateResponse(results, continuationPoints);
|
||||
ClientBase.ValidateDiagnosticInfos(diagnosticInfos, continuationPoints);
|
||||
|
||||
for (int ii = 0; ii < continuationPoints.Count; ii++)
|
||||
{
|
||||
// check for error.
|
||||
if (StatusCode.IsBad(results[ii].StatusCode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if all references have been fetched.
|
||||
if (results[ii].References.Count == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// save results.
|
||||
references.AddRange(results[ii].References);
|
||||
|
||||
// check for continuation point.
|
||||
if (results[ii].ContinuationPoint != null)
|
||||
{
|
||||
revisedContiuationPoints.Add(results[ii].ContinuationPoint);
|
||||
}
|
||||
}
|
||||
|
||||
// check if browsing must continue;
|
||||
revisedContiuationPoints = continuationPoints;
|
||||
}
|
||||
|
||||
// check if unprocessed results exist.
|
||||
nodesToBrowse = unprocessedOperations;
|
||||
}
|
||||
|
||||
// return complete list.
|
||||
return references;
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
if (throwOnError)
|
||||
{
|
||||
throw new ServiceResultException(exception, StatusCodes.BadUnexpectedError);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 浏览地址空间
|
||||
/// </summary>
|
||||
@@ -570,7 +443,7 @@ public static class OpcUaUtils
|
||||
/// </summary>
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
/// <returns>A list of server urls.</returns>
|
||||
public static IList<string> DiscoverServers(ApplicationConfiguration configuration)
|
||||
public static async Task<IList<string>> DiscoverServers(ApplicationConfiguration configuration)
|
||||
{
|
||||
List<string> serverUrls = new();
|
||||
|
||||
@@ -581,7 +454,7 @@ public static class OpcUaUtils
|
||||
// Connect to the local discovery server and find the available servers.
|
||||
using (DiscoveryClient client = DiscoveryClient.Create(new Uri("opc.tcp://localhost:4840"), endpointConfiguration))
|
||||
{
|
||||
ApplicationDescriptionCollection servers = client.FindServers(null);
|
||||
ApplicationDescriptionCollection servers = await client.FindServersAsync(null).ConfigureAwait(false);
|
||||
|
||||
// populate the drop down list with the discovery URLs for the available servers.
|
||||
for (int ii = 0; ii < servers.Count; ii++)
|
||||
@@ -641,7 +514,7 @@ public static class OpcUaUtils
|
||||
/// <summary>
|
||||
/// 指定的属性的显示文本。
|
||||
/// </summary>
|
||||
public static string GetAttributeDisplayText(ISession session, uint attributeId, Variant value)
|
||||
public static async Task<string> GetAttributeDisplayTextAsync(ISession session, uint attributeId, Variant value)
|
||||
{
|
||||
if (value == Variant.Null)
|
||||
{
|
||||
@@ -677,7 +550,7 @@ public static class OpcUaUtils
|
||||
|
||||
case Attributes.DataType:
|
||||
{
|
||||
return session.NodeCache.GetDisplayText(value.Value as NodeId);
|
||||
return await session.NodeCache.GetDisplayTextAsync(value.Value as NodeId).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
case Attributes.ValueRank:
|
||||
@@ -727,102 +600,6 @@ public static class OpcUaUtils
|
||||
return value.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the endpoint that best matches the current settings.
|
||||
/// </summary>
|
||||
/// <param name="discoveryUrl">The discovery URL.</param>
|
||||
/// <param name="useSecurity">if set to <c>true</c> select an endpoint that uses security.</param>
|
||||
/// <returns>The best available endpoint.</returns>
|
||||
public static EndpointDescription SelectEndpoint(string discoveryUrl, bool useSecurity)
|
||||
{
|
||||
// needs to add the '/discovery' back onto non-UA TCP URLs.
|
||||
if (!discoveryUrl.StartsWith(Utils.UriSchemeOpcTcp))
|
||||
{
|
||||
if (!discoveryUrl.EndsWith("/discovery"))
|
||||
{
|
||||
discoveryUrl += "/discovery";
|
||||
}
|
||||
}
|
||||
|
||||
// parse the selected URL.
|
||||
Uri uri = new(discoveryUrl);
|
||||
|
||||
// set a short timeout because this is happening in the drop down event.
|
||||
EndpointConfiguration configuration = EndpointConfiguration.Create();
|
||||
configuration.OperationTimeout = 5000;
|
||||
|
||||
EndpointDescription selectedEndpoint = null;
|
||||
|
||||
// Connect to the server's discovery endpoint and find the available configuration.
|
||||
using (DiscoveryClient client = DiscoveryClient.Create(uri, configuration))
|
||||
{
|
||||
EndpointDescriptionCollection endpoints = client.GetEndpoints(null);
|
||||
|
||||
// select the best endpoint to use based on the selected URL and the UseSecurity checkbox.
|
||||
for (int ii = 0; ii < endpoints.Count; ii++)
|
||||
{
|
||||
EndpointDescription endpoint = endpoints[ii];
|
||||
|
||||
// check for a match on the URL scheme.
|
||||
if (endpoint.EndpointUrl.StartsWith(uri.Scheme))
|
||||
{
|
||||
// check if security was requested.
|
||||
if (useSecurity)
|
||||
{
|
||||
if (endpoint.SecurityMode == MessageSecurityMode.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (endpoint.SecurityMode != MessageSecurityMode.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// pick the first available endpoint by default.
|
||||
selectedEndpoint ??= endpoint;
|
||||
|
||||
// The security level is a relative measure assigned by the server to the
|
||||
// endpoints that it returns. Clients should always pick the highest level
|
||||
// unless they have a reason not too.
|
||||
if (endpoint.SecurityLevel > selectedEndpoint.SecurityLevel)
|
||||
{
|
||||
selectedEndpoint = endpoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pick the first available endpoint by default.
|
||||
if (selectedEndpoint == null && endpoints.Count > 0)
|
||||
{
|
||||
selectedEndpoint = endpoints[0];
|
||||
}
|
||||
}
|
||||
|
||||
// if a server is behind a firewall it may return URLs that are not accessible to the client.
|
||||
// This problem can be avoided by assuming that the domain in the URL used to call
|
||||
// GetEndpoints can be used to access any of the endpoints. This code makes that conversion.
|
||||
// Note that the conversion only makes sense if discovery uses the same protocol as the endpoint.
|
||||
|
||||
Uri endpointUrl = Utils.ParseUri(selectedEndpoint.EndpointUrl);
|
||||
|
||||
if (endpointUrl != null && endpointUrl.Scheme == uri.Scheme)
|
||||
{
|
||||
UriBuilder builder = new(endpointUrl)
|
||||
{
|
||||
Host = uri.DnsSafeHost,
|
||||
Port = uri.Port
|
||||
};
|
||||
selectedEndpoint.EndpointUrl = builder.ToString();
|
||||
}
|
||||
|
||||
// return the selected endpoint.
|
||||
return selectedEndpoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回一组相对路径的节点id
|
||||
/// </summary>
|
||||
|
@@ -31,6 +31,15 @@ public abstract class DynamicSQLBase
|
||||
/// <returns></returns>
|
||||
public virtual Task DBInsertable(ISqlSugarClient db, IEnumerable<object> datas, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除n天前数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual Task<int> DBDeleteable(ISqlSugarClient db, int days, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
|
@@ -33,6 +33,9 @@ public class RealDBProducerProperty : BusinessPropertyWithCacheInterval
|
||||
[Required]
|
||||
public string StringTableName { get; set; } = "historyStringValue";
|
||||
|
||||
[DynamicProperty]
|
||||
public int SaveDays { get; set; } = 3650;
|
||||
|
||||
public string NumberTableNameLow => NumberTableName.ToLower();
|
||||
public string StringTableNameLow => StringTableName.ToLower();
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"ThingsGateway.Plugin.DB.RealDBProducerProperty": {
|
||||
"BigTextConnectStr": "ConnectString",
|
||||
"SaveDays": "SaveDays",
|
||||
"BigTextScriptHistoryTable": "DynamicScriptHistoryTable",
|
||||
"RealTableBusinessInterval": "RealTableBusinessInterval",
|
||||
"StringTableName": "StringTableName",
|
||||
@@ -25,6 +26,7 @@
|
||||
"Value": "Value"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlDB.SqlDBProducerProperty": {
|
||||
"SaveDays": "SaveDays",
|
||||
"BigTextConnectStr": "ConnectString",
|
||||
"BigTextScriptHistoryTable": "DynamicScriptHistoryTable",
|
||||
"BigTextScriptRealTable": "DynamicScriptRealTable",
|
||||
@@ -87,6 +89,7 @@
|
||||
"Remark5": "Remark5"
|
||||
},
|
||||
"ThingsGateway.Plugin.DB.SQLHistoryAlarmProperty": {
|
||||
"SaveDays": "SaveDays",
|
||||
"BigTextConnectStr": "ConnectString",
|
||||
"DbType": "DbType",
|
||||
"TableName": "TableName",
|
||||
|
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"ThingsGateway.Plugin.DB.RealDBProducerProperty": {
|
||||
"BigTextConnectStr": "连接字符串",
|
||||
"SaveDays": "保留天数",
|
||||
"BigTextScriptHistoryTable": "历史表动态脚本",
|
||||
"RealTableBusinessInterval": "实时表定时上传间隔",
|
||||
"NumberTableName": "数值变量历史表名称",
|
||||
@@ -25,6 +26,7 @@
|
||||
"Value": "变量值"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlDB.SqlDBProducerProperty": {
|
||||
"SaveDays": "保留天数",
|
||||
"BigTextConnectStr": "链接字符串",
|
||||
"BigTextScriptHistoryTable": "历史表动态脚本",
|
||||
"BigTextScriptRealTable": "实时表动态脚本",
|
||||
@@ -87,6 +89,7 @@
|
||||
"Remark5": "备用5"
|
||||
},
|
||||
"ThingsGateway.Plugin.DB.SQLHistoryAlarmProperty": {
|
||||
"SaveDays": "保留天数",
|
||||
"BigTextConnectStr": "链接字符串",
|
||||
"DbType": "数据库类型",
|
||||
"TableName": "表名称",
|
||||
|
@@ -16,6 +16,7 @@ using ThingsGateway.Debug;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.NewLife.Threading;
|
||||
using ThingsGateway.Plugin.DB;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
@@ -69,6 +70,56 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariable
|
||||
|
||||
#if !Management
|
||||
|
||||
|
||||
protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken)
|
||||
{
|
||||
var list = base.ProtectedGetTasks(cancellationToken);
|
||||
list.Add(ScheduledTaskHelper.GetTask("0 0 * * *", DeleteByDayAsync, null, LogMessage, cancellationToken));
|
||||
|
||||
return list;
|
||||
}
|
||||
private async Task DeleteByDayAsync(object? state, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = BusinessDatabaseUtil.GetDb(_driverPropertys.DbType, _driverPropertys.BigTextConnectStr);
|
||||
if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var hisModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptHistoryTable);
|
||||
|
||||
await hisModel.DBDeleteable(db, _driverPropertys.SaveDays, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
var time = TimerX.Now - TimeSpan.FromDays(-_driverPropertys.SaveDays);
|
||||
|
||||
string sql = $"""
|
||||
ALTER TABLE {_driverPropertys.NumberTableNameLow}
|
||||
DROP PARTITION
|
||||
WHERE createtime < to_timestamp('{time.ToString("yyyy-MM-dd:HH:mm:ss")}', 'yyyy-MM-dd:HH:mm:ss');
|
||||
""";
|
||||
await db.Ado.ExecuteCommandAsync("", default, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
sql = $"""
|
||||
ALTER TABLE {_driverPropertys.StringTableNameLow}
|
||||
DROP PARTITION
|
||||
WHERE createtime < to_timestamp('{time.ToString("yyyy-MM-dd:HH:mm:ss")}', 'yyyy-MM-dd:HH:mm:ss');
|
||||
""";
|
||||
await db.Ado.ExecuteCommandAsync("", default, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
LogMessage?.LogInformation($"Clean up historical data from {_driverPropertys.SaveDays} days ago");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, "Clearing historical data error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
_db = BusinessDatabaseUtil.GetDb(_driverPropertys.DbType, _driverPropertys.BigTextConnectStr);
|
||||
|
@@ -18,6 +18,7 @@ using ThingsGateway.Foundation;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.DictionaryExtensions;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.NewLife.Threading;
|
||||
using ThingsGateway.Plugin.DB;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
@@ -70,7 +71,82 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariable
|
||||
return $" {nameof(SqlDBProducer)}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if !Management
|
||||
protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken)
|
||||
{
|
||||
var list = base.ProtectedGetTasks(cancellationToken);
|
||||
list.Add(ScheduledTaskHelper.GetTask("0 0 * * *", DeleteByDayAsync, null, LogMessage, cancellationToken));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private async Task DeleteByDayAsync(object? state, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = SqlDBBusinessDatabaseUtil.GetDb(_driverPropertys);
|
||||
if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var hisModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptHistoryTable);
|
||||
|
||||
if (_driverPropertys.IsHistoryDB)
|
||||
{
|
||||
await hisModel.DBDeleteable(db, _driverPropertys.SaveDays, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_driverPropertys.IsHistoryDB)
|
||||
{
|
||||
{
|
||||
var time = TimerX.Now - TimeSpan.FromDays(-_driverPropertys.SaveDays);
|
||||
var tableNames = db.SplitHelper<SQLHistoryValue>().GetTables();//根据时间获取表名
|
||||
var filtered = tableNames.Where(a => a.Date < time).ToList();
|
||||
// 去掉最后一个
|
||||
var oldTable = filtered.Take(filtered.Count - 1);
|
||||
|
||||
foreach (var table in oldTable)
|
||||
{
|
||||
db.DbMaintenance.DropTable(table.TableName);
|
||||
}
|
||||
var deldata = filtered.LastOrDefault();
|
||||
if (deldata != null)
|
||||
{
|
||||
await db.Deleteable<SQLHistoryValue>().AS(deldata.TableName).Where(a => a.CreateTime < time).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
var time = TimerX.Now - TimeSpan.FromDays(-_driverPropertys.SaveDays);
|
||||
var tableNames = db.SplitHelper<SQLNumberHistoryValue>().GetTables();//根据时间获取表名
|
||||
var filtered = tableNames.Where(a => a.Date < time).ToList();
|
||||
// 去掉最后一个
|
||||
var oldTable = filtered.Take(filtered.Count - 1);
|
||||
|
||||
foreach (var table in oldTable)
|
||||
{
|
||||
db.DbMaintenance.DropTable(table.TableName);
|
||||
}
|
||||
var deldata = filtered.LastOrDefault();
|
||||
if (deldata != null)
|
||||
{
|
||||
await db.Deleteable<SQLNumberHistoryValue>().AS(deldata.TableName).Where(a => a.CreateTime < time).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
LogMessage?.LogInformation($"Clean up historical data from {_driverPropertys.SaveDays} days ago");
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, "Clearing historical data error");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<SqlSugarPagedList<IDBHistoryValue>> GetDBHistoryValuePagesAsync(DBHistoryValuePageInput input)
|
||||
{
|
||||
|
@@ -58,6 +58,9 @@ public class SqlDBProducerProperty : BusinessPropertyWithCacheInterval
|
||||
[DynamicProperty]
|
||||
public DbType DbType { get; set; } = DbType.SqlServer;
|
||||
|
||||
[DynamicProperty]
|
||||
public int SaveDays { get; set; } = 3650;
|
||||
|
||||
[DynamicProperty]
|
||||
public SqlDBSplitType SqlDBSplitType { get; set; } = SqlDBSplitType.Week;
|
||||
|
||||
|
@@ -16,6 +16,7 @@ using ThingsGateway.Debug;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.NewLife.Threading;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
@@ -65,6 +66,44 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
}
|
||||
|
||||
#if !Management
|
||||
protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken)
|
||||
{
|
||||
var list = base.ProtectedGetTasks(cancellationToken);
|
||||
list.Add(ScheduledTaskHelper.GetTask("0 0 * * *", DeleteByDayAsync, null, LogMessage, cancellationToken));
|
||||
|
||||
return list;
|
||||
}
|
||||
private async Task DeleteByDayAsync(object? state, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var db = BusinessDatabaseUtil.GetDb((DbType)_driverPropertys.DbType, _driverPropertys.BigTextConnectStr);
|
||||
if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var hisModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptHistoryTable);
|
||||
|
||||
await hisModel.DBDeleteable(db, _driverPropertys.SaveDays, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
var time = TimerX.Now - TimeSpan.FromDays(-_driverPropertys.SaveDays);
|
||||
|
||||
await db.Deleteable<HistoryAlarm>().Where(a => a.EventTime < time).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
LogMessage?.LogInformation($"Clean up historical data from {_driverPropertys.SaveDays} days ago");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, "Clearing historical data error");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
|
@@ -23,6 +23,10 @@ public class SqlHistoryAlarmProperty : BusinessPropertyWithCache
|
||||
{
|
||||
[DynamicProperty]
|
||||
public DbType DbType { get; set; } = DbType.SqlServer;
|
||||
|
||||
[DynamicProperty]
|
||||
public int SaveDays { get; set; } = 3650;
|
||||
|
||||
[DynamicProperty]
|
||||
[Required]
|
||||
public string TableName { get; set; } = "historyAlarm";
|
||||
|
@@ -130,6 +130,12 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariable
|
||||
`value` DOUBLE ) TAGS(`devicename` VARCHAR(100) ,`name` VARCHAR(100))
|
||||
""";
|
||||
await _db.Ado.ExecuteCommandAsync(sql, default, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
|
||||
|
||||
await _db.Ado.ExecuteCommandAsync($"ALTER STABLE `{_driverPropertys.StringTableNameLow}` KEEP 10;", default, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
await _db.Ado.ExecuteCommandAsync($"ALTER STABLE `{_driverPropertys.NumberTableNameLow}` KEEP 10;", default, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ public class TDengineDBProducerProperty : RealDBProducerProperty
|
||||
|
||||
public TDengineDBProducerProperty()
|
||||
{
|
||||
BigTextConnectStr = "protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata;db=power";
|
||||
BigTextConnectStr = "protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata;db=power;autoReconnect=true";
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<ProjectReference Include="..\..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj">
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation.Dlt645\ThingsGateway.Foundation.Dlt645.csproj">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -11,7 +11,7 @@
|
||||
<ProjectReference Include="..\..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj">
|
||||
</ProjectReference>
|
||||
<PackageReference Include="Confluent.Kafka" Version="2.11.1" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<ProjectReference Include="..\..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj">
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation.Modbus\ThingsGateway.Foundation.Modbus.csproj">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -27,21 +27,21 @@
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net6.0' ">
|
||||
<PackageReference Include="MQTTnet.AspNetCore" Version="4.3.7.1207" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MQTTnet" Version="4.3.7.1207" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition=" '$(TargetFramework)' != 'net6.0' ">
|
||||
<PackageReference Include="MQTTnet.Server" Version="5.0.1.1416" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MQTTnet.AspNetCore" Version="5.0.1.1416" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="MQTTnet" Version="5.0.1.1416" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -24,7 +24,7 @@
|
||||
<ProjectReference Include="..\..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj">
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation.OpcDa\ThingsGateway.Foundation.OpcDa.csproj">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -250,7 +250,7 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
/// <param name="disposing"></param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
NodeIdTags.Clear();
|
||||
NodeIdTags?.Clear();
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
@@ -472,7 +472,29 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
|
||||
return folder;
|
||||
}
|
||||
return null;
|
||||
else
|
||||
{
|
||||
var name = "Memory";
|
||||
var description = "Memory";
|
||||
FolderState folder = new(parent)
|
||||
{
|
||||
SymbolicName = name,
|
||||
ReferenceTypeId = ReferenceTypes.Organizes,
|
||||
TypeDefinitionId = ObjectTypeIds.FolderType,
|
||||
Description = description,
|
||||
NodeId = new NodeId(name, NamespaceIndex),
|
||||
BrowseName = new QualifiedName(name, NamespaceIndex),
|
||||
DisplayName = new LocalizedText(name),
|
||||
WriteMask = AttributeWriteMask.None,
|
||||
UserWriteMask = AttributeWriteMask.None,
|
||||
EventNotifier = EventNotifiers.None
|
||||
};
|
||||
|
||||
|
||||
parent?.AddChild(folder);
|
||||
|
||||
return folder;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -950,7 +972,7 @@ public class ThingsGatewayNodeManager : CustomNodeManager2
|
||||
{
|
||||
var str = variableRuntime.GetPropertyValue(_businessBase.DeviceId, nameof(OpcUaServerVariableProperty.DataType)) ?? "";
|
||||
Type tp;
|
||||
if (Enum.TryParse(str, out DataTypeEnum result))
|
||||
if (Enum.TryParse(str, out DataTypeEnum result) && result != DataTypeEnum.Object)
|
||||
{
|
||||
tp = result.GetSystemType();
|
||||
return DataNodeType(tp);
|
||||
|
@@ -57,7 +57,7 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
/// <inheritdoc/>
|
||||
protected override ResourceManager CreateResourceManager(IServerInternal server, ApplicationConfiguration configuration)
|
||||
{
|
||||
ResourceManager resourceManager = new(server, configuration);
|
||||
ResourceManager resourceManager = new(configuration);
|
||||
|
||||
System.Reflection.FieldInfo[] fields = typeof(StatusCodes).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
|
||||
|
||||
@@ -117,7 +117,6 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
|
||||
// 由应用程序决定如何验证用户身份令牌。
|
||||
// 此函数为 X509 身份令牌创建验证器。
|
||||
CreateUserIdentityValidators(configuration);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -127,7 +126,7 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
base.OnServerStopping();
|
||||
}
|
||||
|
||||
private void CreateUserIdentityValidators(ApplicationConfiguration configuration)
|
||||
public async Task CreateUserIdentityValidators(ApplicationConfiguration configuration)
|
||||
{
|
||||
for (int ii = 0; ii < configuration.ServerConfiguration.UserTokenPolicies.Count; ii++)
|
||||
{
|
||||
@@ -141,7 +140,7 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
configuration.SecurityConfiguration.UserIssuerCertificates != null)
|
||||
{
|
||||
CertificateValidator certificateValidator = new();
|
||||
certificateValidator.Update(configuration).GetAwaiter().GetResult();
|
||||
await certificateValidator.UpdateAsync(configuration).ConfigureAwait(false);
|
||||
certificateValidator.Update(configuration.SecurityConfiguration.UserIssuerCertificates,
|
||||
configuration.SecurityConfiguration.TrustedUserCertificates,
|
||||
configuration.SecurityConfiguration.RejectedCertificateStore);
|
||||
@@ -153,7 +152,7 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
}
|
||||
}
|
||||
|
||||
private void SessionManager_ImpersonateUser(Session session, ImpersonateEventArgs args)
|
||||
private void SessionManager_ImpersonateUser(ISession session, ImpersonateEventArgs args)
|
||||
{
|
||||
// check for a user name cancellationToken.
|
||||
|
||||
@@ -226,7 +225,7 @@ public partial class ThingsGatewayServer : StandardServer
|
||||
"Security cancellationToken is not a valid username cancellationToken. An empty password is not accepted.");
|
||||
}
|
||||
var sysUserService = App.RootServices.GetService<ISysUserService>();
|
||||
var userInfo = sysUserService.GetUserByAccountAsync(userName, null).ConfigureAwait(true).GetAwaiter().GetResult();//获取用户信息
|
||||
var userInfo = sysUserService.GetUserByAccountAsync(userName, null).ConfigureAwait(false).GetAwaiter().GetResult();//获取用户信息
|
||||
if (userInfo == null)
|
||||
{
|
||||
// construct translation object with default text.
|
||||
|
@@ -74,7 +74,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
{
|
||||
LogMessage?.LogInformation("Refresh variable");
|
||||
IdVariableRuntimes.Clear();
|
||||
IdVariableRuntimes.AddRange(GlobalData.GetEnableVariables().ToDictionary(a => a.Id));
|
||||
IdVariableRuntimes.AddRange(GlobalData.GetEnableVariables().Where(a => a.IsInternalMemoryVariable == false).ToDictionary(a => a.Id));
|
||||
|
||||
CollectDevices = GlobalData.GetEnableDevices().Where(a => a.IsCollect == true).ToDictionary(a => a.Id);
|
||||
}
|
||||
@@ -112,8 +112,8 @@ public partial class OpcUaServer : BusinessBase
|
||||
|
||||
//Utils.SetLogger(new OpcUaLogger(LogMessage)); //调试用途
|
||||
m_application = new ApplicationInstance();
|
||||
m_configuration = GetDefaultConfiguration();
|
||||
await m_configuration.Validate(ApplicationType.Server).ConfigureAwait(false);
|
||||
m_configuration = await GetDefaultConfigurationAsync().ConfigureAwait(false);
|
||||
await m_configuration.ValidateAsync(ApplicationType.Server).ConfigureAwait(false);
|
||||
m_application.ApplicationConfiguration = m_configuration;
|
||||
if (m_configuration.SecurityConfiguration.AutoAcceptUntrustedCertificates)
|
||||
{
|
||||
@@ -121,6 +121,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
}
|
||||
|
||||
m_server = new(this);
|
||||
await m_server.CreateUserIdentityValidators(m_configuration).ConfigureAwait(false);
|
||||
}
|
||||
private void UaDispose()
|
||||
{
|
||||
@@ -142,7 +143,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
typeof(EncodeableFactory).GetField("s_globalFactory", BindingFlags.NonPublic | BindingFlags.Static)?.SetValue(null, new EncodeableFactory());
|
||||
typeof(ServiceMessageContext).GetField("s_globalContext", BindingFlags.NonPublic | BindingFlags.Static)?.SetValue(null, typeof(ServiceMessageContext).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(bool) }, null).Invoke(new object[] { true }));
|
||||
|
||||
var listeners = m_server.GetValue("m_listeners") as List<ITransportListener>;
|
||||
var listeners = m_server.GetValue("TransportListeners") as List<ITransportListener>;
|
||||
if (listeners != null)
|
||||
{
|
||||
foreach (var item in listeners)
|
||||
@@ -189,9 +190,9 @@ public partial class OpcUaServer : BusinessBase
|
||||
protected override async Task ProtectedStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// 启动服务器。
|
||||
await m_application.CheckApplicationInstanceCertificates(true, 1200, cancellationToken).ConfigureAwait(false);
|
||||
await m_application.CheckApplicationInstanceCertificatesAsync(true, 1200, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await m_application.Start(m_server).ConfigureAwait(false);
|
||||
await m_application.StartAsync(m_server).ConfigureAwait(false);
|
||||
IdVariableRuntimes.ForEach(a => VariableValueChange(a.Value, a.Value.AdaptVariableBasicData()));
|
||||
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
@@ -205,8 +206,8 @@ public partial class OpcUaServer : BusinessBase
|
||||
try
|
||||
{
|
||||
await Task.Delay(2000, cancellationToken).ConfigureAwait(false);
|
||||
await m_application.CheckApplicationInstanceCertificates(true, 1200, cancellationToken).ConfigureAwait(false);
|
||||
await m_application.Start(m_server).ConfigureAwait(false);
|
||||
await m_application.CheckApplicationInstanceCertificatesAsync(true, 1200, cancellationToken).ConfigureAwait(false);
|
||||
await m_application.StartAsync(m_server).ConfigureAwait(false);
|
||||
connect_success = true;
|
||||
await Task.Delay(2000, cancellationToken).ConfigureAwait(false);
|
||||
IdVariableRuntimes.ForEach(a => VariableValueChange(a.Value, a.Value.AdaptVariableBasicData()));
|
||||
@@ -249,7 +250,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
}
|
||||
}
|
||||
|
||||
private ApplicationConfiguration GetDefaultConfiguration()
|
||||
private async Task<ApplicationConfiguration> GetDefaultConfigurationAsync()
|
||||
{
|
||||
ApplicationConfiguration config = new();
|
||||
var urls = _driverPropertys.OpcUaStringUrl.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
||||
@@ -428,7 +429,7 @@ public partial class OpcUaServer : BusinessBase
|
||||
config.TraceConfiguration = new TraceConfiguration();
|
||||
|
||||
config.CertificateValidator = new CertificateValidator();
|
||||
config.CertificateValidator.Update(config).ConfigureAwait(false);
|
||||
await config.CertificateValidator.UpdateAsync(config).ConfigureAwait(false);
|
||||
config.Extensions = new XmlElementCollection();
|
||||
|
||||
return config;
|
||||
|
@@ -10,15 +10,7 @@
|
||||
|
||||
<div class="col-12 col-md-6 p-1" style="min-height:500px;max-height:80vh;overflow: auto;">
|
||||
<TreeView TItem="OpcUaTagModel" Items="Items" ShowIcon="true" ShowCheckbox="true" AutoCheckParent="true" AutoCheckChildren="true" IsVirtualize="true"
|
||||
OnExpandNodeAsync=OnExpandNodeAsync OnTreeItemChecked="OnTreeItemChecked" OnTreeItemClick=@(async a=>
|
||||
{
|
||||
if(a?.Value?.Tag?.NodeId!=null)
|
||||
{
|
||||
ClickItem=a;
|
||||
NodeAttributes = Plc.ReadNoteAttributes(ClickItem.Value.NodeId.ToString());
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}) ShowSkeleton=ShowSkeleton
|
||||
OnExpandNodeAsync=OnExpandNodeAsync OnTreeItemChecked="OnTreeItemChecked" OnTreeItemClick=@(OnTreeItemClick) ShowSkeleton=ShowSkeleton
|
||||
IsAccordion ClickToggleNode ModelEqualityComparer="OpcUaImportVariable.ModelEqualityComparer" />
|
||||
</div>
|
||||
<div class="col-12 col-md-6 p-2" style="min-height:500px;max-height:80vh;overflow: auto;">
|
||||
|
@@ -46,6 +46,8 @@ public partial class OpcUaImportVariable
|
||||
private IEnumerable<OpcUaTagModel> Nodes;
|
||||
private bool ShowSkeleton = true;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Opc对象
|
||||
/// </summary>
|
||||
@@ -77,6 +79,17 @@ public partial class OpcUaImportVariable
|
||||
await base.OnAfterRenderAsync(firstRender);
|
||||
}
|
||||
|
||||
private async Task OnTreeItemClick(TreeViewItem<OpcUaTagModel> item)
|
||||
{
|
||||
if (item?.Value?.Tag?.NodeId != null && Plc != null)
|
||||
{
|
||||
ClickItem = item;
|
||||
NodeAttributes = await Plc.ReadNoteAttributesAsync(ClickItem.Value.NodeId.ToString(), default).ConfigureAwait(false);
|
||||
}
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 构建树节点,传入的列表已经是树结构
|
||||
/// </summary>
|
||||
|
@@ -34,7 +34,7 @@
|
||||
<Buttons>
|
||||
<Button IsAsync Color="Color.Primary" OnClick="Connect">@OpcUaPropertyLocalizer["Connect"]</Button>
|
||||
|
||||
<Button IsAsync Color="Color.Warning" OnClick="Disconnect">@OpcUaPropertyLocalizer["Disconnect"]</Button>
|
||||
<Button IsAsync Color="Color.Warning" OnClick="DisconnectAsync">@OpcUaPropertyLocalizer["Disconnect"]</Button>
|
||||
|
||||
<Button IsAsync Color="Color.Primary" OnClick="Export">@OpcUaPropertyLocalizer["ExportC"]</Button>
|
||||
</Buttons>
|
||||
|
@@ -24,7 +24,7 @@ using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Debug;
|
||||
|
||||
public partial class OpcUaMaster : IDisposable
|
||||
public partial class OpcUaMaster : IAsyncDisposable
|
||||
{
|
||||
public LoggerGroup? LogMessage;
|
||||
private readonly OpcUaProperty OpcUaProperty = new();
|
||||
@@ -37,7 +37,8 @@ public partial class OpcUaMaster : IDisposable
|
||||
/// <inheritdoc/>
|
||||
~OpcUaMaster()
|
||||
{
|
||||
this.SafeDispose();
|
||||
if (_plc != null)
|
||||
_ = _plc.SafeDisposeAsync();
|
||||
}
|
||||
|
||||
private DeviceComponent DeviceComponent { get; set; }
|
||||
@@ -49,9 +50,10 @@ public partial class OpcUaMaster : IDisposable
|
||||
private IStringLocalizer<OpcUaProperty> OpcUaPropertyLocalizer { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
_plc?.SafeDispose();
|
||||
if (_plc != null)
|
||||
await _plc.SafeDisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
[Inject]
|
||||
@@ -129,11 +131,11 @@ public partial class OpcUaMaster : IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
private void Disconnect()
|
||||
private async Task DisconnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
_plc.Disconnect();
|
||||
await _plc.DisconnectAsync().ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@@ -25,32 +25,32 @@
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation.OpcUa\ThingsGateway.Foundation.OpcUa.csproj" />
|
||||
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.376.244" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Server" Version="1.5.377.21" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.376.244" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes" Version="1.5.377.21" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.5.376.244" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Client" Version="1.5.377.21" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.5.376.244" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Core" Version="1.5.377.21" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.5.376.244" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Configuration" Version="1.5.377.21" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.376.244" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.377.21" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--<PackageReference Include="System.Formats.Asn1" Version="8.0.2" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>-->
|
||||
|
||||
</ItemGroup>
|
||||
|
@@ -15,7 +15,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RabbitMQ.Client" Version="7.1.2" GeneratePathProperty="true">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
</ItemGroup>
|
||||
|
@@ -13,7 +13,7 @@
|
||||
<ProjectReference Include="..\..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj">
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\ThingsGateway.Foundation.SiemensS7\ThingsGateway.Foundation.SiemensS7.csproj">
|
||||
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
52
src/ThingsGateway.ScriptDebug/Test/TestClientRpc.cs
Normal file
52
src/ThingsGateway.ScriptDebug/Test/TestClientRpc.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using MQTTnet;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.Plugin.Mqtt;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
public class TestClientRpc : DynamicMqttClientRpcBase
|
||||
{
|
||||
public override async Task RPCInvokeAsync(ILog logMessage, MqttApplicationMessageReceivedEventArgs args, MqttClientProperty driverPropertys, IMqttClient mqttClient, Func<string, Dictionary<string, Dictionary<string, JToken>>, ValueTask<Dictionary<string, Dictionary<string, IOperResult>>>> getRpcResult, Func<CancellationToken, ValueTask<OperResult>> tryMqttClientAsync, CancellationToken cancellationToken)
|
||||
{
|
||||
if (driverPropertys.RpcWriteTopic.IsNullOrWhiteSpace()) return;
|
||||
var t = string.Format(null, "{0}/+", driverPropertys.RpcWriteTopic);
|
||||
if (MqttTopicFilterComparer.Compare(args.ApplicationMessage.Topic, t) != MqttTopicFilterCompareResult.IsMatch)
|
||||
return;
|
||||
|
||||
var rpcDatas = Encoding.UTF8.GetString(args.ApplicationMessage.Payload).FromJsonNetString<Dictionary<string, Dictionary<string, JToken>>>();
|
||||
if (rpcDatas == null)
|
||||
return;
|
||||
|
||||
var mqttRpcResult = await getRpcResult(args.ClientId, rpcDatas).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var isConnect = await tryMqttClientAsync(CancellationToken.None).ConfigureAwait(false);
|
||||
if (isConnect.IsSuccess)
|
||||
{
|
||||
var variableMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic($"{args.ApplicationMessage.Topic}/Response")
|
||||
.WithPayload(mqttRpcResult.ToSystemTextJsonString(driverPropertys.JsonFormattingIndented)).Build();
|
||||
await mqttClient.PublishAsync(variableMessage, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,263 +1,263 @@
|
||||
|
||||
//using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
//using ThingsGateway.Foundation;
|
||||
//using ThingsGateway.Gateway.Application;
|
||||
//namespace ThingsGateway.Server;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
namespace ThingsGateway.Server;
|
||||
|
||||
///// <summary>
|
||||
///// 插件类,默认实现了<see cref="IDevice"/>接口,继承<see cref="CollectFoundationBase"/> 实现采集插件
|
||||
///// </summary>
|
||||
//public class TestCollectPlugin : CollectFoundationBase
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// 调试UI Type,如果不存在无需重写
|
||||
// /// </summary>
|
||||
// public override Type DriverDebugUIType => base.DriverDebugUIType;
|
||||
// /// <summary>
|
||||
// /// 插件属性UI Type,继承<see cref="IPropertyUIBase"/>如果不存在无需重写
|
||||
// /// </summary>
|
||||
// public override Type DriverPropertyUIType => base.DriverPropertyUIType;
|
||||
// /// <summary>
|
||||
// /// 插件UI Type,继承<see cref="IDriverUIBase"/>如果不存在无需重写
|
||||
// /// </summary>
|
||||
// public override Type DriverUIType => base.DriverUIType;
|
||||
// /// <summary>
|
||||
// /// 插件变量寄存器UI Type,继承<see cref="IAddressUIBase"/>如果不存在无需重写
|
||||
// /// </summary>
|
||||
// public override Type DriverVariableAddressUIType => base.DriverVariableAddressUIType;
|
||||
/// <summary>
|
||||
/// 插件类,默认实现了<see cref="IDevice"/>接口,继承<see cref="CollectFoundationBase"/> 实现采集插件
|
||||
/// </summary>
|
||||
public class TestCollectPlugin : CollectFoundationBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 调试UI Type,如果不存在无需重写
|
||||
/// </summary>
|
||||
public override Type DriverDebugUIType => base.DriverDebugUIType;
|
||||
/// <summary>
|
||||
/// 插件属性UI Type,继承<see cref="IPropertyUIBase"/>如果不存在无需重写
|
||||
/// </summary>
|
||||
public override Type DriverPropertyUIType => base.DriverPropertyUIType;
|
||||
/// <summary>
|
||||
/// 插件UI Type,继承<see cref="IDriverUIBase"/>如果不存在无需重写
|
||||
/// </summary>
|
||||
public override Type DriverUIType => base.DriverUIType;
|
||||
/// <summary>
|
||||
/// 插件变量寄存器UI Type,继承<see cref="IAddressUIBase"/>如果不存在无需重写
|
||||
/// </summary>
|
||||
public override Type DriverVariableAddressUIType => base.DriverVariableAddressUIType;
|
||||
|
||||
// /// <summary>
|
||||
// /// 插件配置项,继承<see cref="CollectPropertyBase"/> 返回类实例
|
||||
// /// </summary>
|
||||
// public override CollectPropertyBase CollectProperties => _property;
|
||||
// private TestCollectProperty? _property = new();
|
||||
/// <summary>
|
||||
/// 插件配置项,继承<see cref="CollectPropertyBase"/> 返回类实例
|
||||
/// </summary>
|
||||
public override CollectPropertyBase CollectProperties => _property;
|
||||
private TestCollectProperty? _property = new();
|
||||
|
||||
// /// <summary>
|
||||
// /// 在插件初始化时调用,只会执行一次,参数为插件默认的链路通道类,如未实现可忽略l
|
||||
// /// </summary>
|
||||
// protected override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
// {
|
||||
// //做一些初始化操作
|
||||
/// <summary>
|
||||
/// 在插件初始化时调用,只会执行一次,参数为插件默认的链路通道类,如未实现可忽略l
|
||||
/// </summary>
|
||||
protected override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
//做一些初始化操作
|
||||
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 变量打包操作,会在默认的AfterVariablesChangedAsync方法里执行,参数为设备变量列表,返回源读取变量列表
|
||||
// /// </summary>
|
||||
// protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
|
||||
// {
|
||||
// //实现将设备变量打包成源读取变量
|
||||
// //比如如果需要实现MC中的字多读功能,需将多个变量地址打包成一个源读取地址和读取长度,根据一系列规则,添加解析标识,然后在返回的整个字节数组中解析出原来的变量地址代表的数据字节
|
||||
/// <summary>
|
||||
/// 变量打包操作,会在默认的AfterVariablesChangedAsync方法里执行,参数为设备变量列表,返回源读取变量列表
|
||||
/// </summary>
|
||||
protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
|
||||
{
|
||||
//实现将设备变量打包成源读取变量
|
||||
//比如如果需要实现MC中的字多读功能,需将多个变量地址打包成一个源读取地址和读取长度,根据一系列规则,添加解析标识,然后在返回的整个字节数组中解析出原来的变量地址代表的数据字节
|
||||
|
||||
// //一般可操作 VariableRuntime 类中的 index, thingsgatewaybitconvter 等属性
|
||||
// //一般可操作 VariableSourceRead 类中的 address, length 等属性
|
||||
//一般可操作 VariableRuntime 类中的 index, thingsgatewaybitconvter 等属性
|
||||
//一般可操作 VariableSourceRead 类中的 address, length 等属性
|
||||
|
||||
// return Task.FromResult(new List<VariableSourceRead>());
|
||||
// }
|
||||
return Task.FromResult(new List<VariableSourceRead>());
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 实现<see cref="IDevice"/>
|
||||
// /// </summary>
|
||||
// public override IDevice? FoundationDevice => base.FoundationDevice;
|
||||
/// <summary>
|
||||
/// 实现<see cref="IDevice"/>
|
||||
/// </summary>
|
||||
public override IDevice? FoundationDevice => base.FoundationDevice;
|
||||
|
||||
// /// <summary>
|
||||
// /// 特殊方法,添加<see cref="DynamicMethodAttribute"/> 特性 ,返回IOperResult
|
||||
// /// 支持<see cref="CancellationToken"/> 参数,需放到最后
|
||||
// /// 默认解析方式为英文分号
|
||||
// /// 比如rpc参数为 test1;test2,解析query1="test1",query2="test2"
|
||||
// /// 也可以在变量地址中填入test1,rpc参数传入test2,解析query1="test1",query2="test2"
|
||||
// /// </summary>
|
||||
// [DynamicMethod("测试特殊方法")]
|
||||
// public IOperResult<string> TestMethod(string query1, string query2, CancellationToken cancellationToken)
|
||||
// {
|
||||
// return new OperResult<string>() { Content = "测试特殊方法" };
|
||||
// }
|
||||
//}
|
||||
/// <summary>
|
||||
/// 特殊方法,添加<see cref="DynamicMethodAttribute"/> 特性 ,返回IOperResult
|
||||
/// 支持<see cref="CancellationToken"/> 参数,需放到最后
|
||||
/// 默认解析方式为英文分号
|
||||
/// 比如rpc参数为 test1;test2,解析query1="test1",query2="test2"
|
||||
/// 也可以在变量地址中填入test1,rpc参数传入test2,解析query1="test1",query2="test2"
|
||||
/// </summary>
|
||||
[DynamicMethod("测试特殊方法")]
|
||||
public IOperResult<string> TestMethod(string query1, string query2, CancellationToken cancellationToken)
|
||||
{
|
||||
return new OperResult<string>() { Content = "测试特殊方法" };
|
||||
}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 插件类配置
|
||||
///// </summary>
|
||||
//public class TestCollectProperty : CollectFoundationPackPropertyBase
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// 添加<see cref="DynamicPropertyAttribute"/> 特性,如需多语言配置,可添加json资源,参考其他插件
|
||||
// /// </summary>
|
||||
// [DynamicProperty(Description = null, Remark = null)]
|
||||
// public string TestString { get; set; }
|
||||
/// <summary>
|
||||
/// 插件类配置
|
||||
/// </summary>
|
||||
public class TestCollectProperty : CollectFoundationPackPropertyBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加<see cref="DynamicPropertyAttribute"/> 特性,如需多语言配置,可添加json资源,参考其他插件
|
||||
/// </summary>
|
||||
[DynamicProperty(Description = null, Remark = null)]
|
||||
public string TestString { get; set; }
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 插件类,完全自定义
|
||||
///// </summary>
|
||||
//public class TestCollectPlugin1 : CollectBase
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// 调试UI Type,如果不存在无需重写
|
||||
// /// </summary>
|
||||
// public override Type DriverDebugUIType => base.DriverDebugUIType;
|
||||
// /// <summary>
|
||||
// /// 插件属性UI Type,继承<see cref="IPropertyUIBase"/>如果不存在无需重写
|
||||
// /// </summary>
|
||||
// public override Type DriverPropertyUIType => base.DriverPropertyUIType;
|
||||
// /// <summary>
|
||||
// /// 插件UI Type,继承<see cref="IDriverUIBase"/>如果不存在无需重写
|
||||
// /// </summary>
|
||||
// public override Type DriverUIType => base.DriverUIType;
|
||||
// /// <summary>
|
||||
// /// 插件变量寄存器UI Type,继承<see cref="IAddressUIBase"/>如果不存在无需重写
|
||||
// /// </summary>
|
||||
// public override Type DriverVariableAddressUIType => base.DriverVariableAddressUIType;
|
||||
/// <summary>
|
||||
/// 插件类,完全自定义
|
||||
/// </summary>
|
||||
public class TestCollectPlugin1 : CollectBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 调试UI Type,如果不存在无需重写
|
||||
/// </summary>
|
||||
public override Type DriverDebugUIType => base.DriverDebugUIType;
|
||||
/// <summary>
|
||||
/// 插件属性UI Type,继承<see cref="IPropertyUIBase"/>如果不存在无需重写
|
||||
/// </summary>
|
||||
public override Type DriverPropertyUIType => base.DriverPropertyUIType;
|
||||
/// <summary>
|
||||
/// 插件UI Type,继承<see cref="IDriverUIBase"/>如果不存在无需重写
|
||||
/// </summary>
|
||||
public override Type DriverUIType => base.DriverUIType;
|
||||
/// <summary>
|
||||
/// 插件变量寄存器UI Type,继承<see cref="IAddressUIBase"/>如果不存在无需重写
|
||||
/// </summary>
|
||||
public override Type DriverVariableAddressUIType => base.DriverVariableAddressUIType;
|
||||
|
||||
// /// <summary>
|
||||
// /// 插件配置项,继承<see cref="CollectPropertyBase"/> 返回类实例
|
||||
// /// </summary>
|
||||
// public override CollectPropertyBase CollectProperties => _property;
|
||||
// private TestCollectProperty1? _property = new();
|
||||
/// <summary>
|
||||
/// 插件配置项,继承<see cref="CollectPropertyBase"/> 返回类实例
|
||||
/// </summary>
|
||||
public override CollectPropertyBase CollectProperties => _property;
|
||||
private TestCollectProperty1? _property = new();
|
||||
|
||||
// /// <summary>
|
||||
// /// 在插件初始化时调用,只会执行一次,参数为插件默认的链路通道类,如未实现可忽略l
|
||||
// /// </summary>
|
||||
// protected override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
// {
|
||||
// //做一些初始化操作
|
||||
/// <summary>
|
||||
/// 在插件初始化时调用,只会执行一次,参数为插件默认的链路通道类,如未实现可忽略l
|
||||
/// </summary>
|
||||
protected override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
//做一些初始化操作
|
||||
|
||||
// return Task.CompletedTask;
|
||||
// }
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 变量打包操作,会在默认的AfterVariablesChangedAsync方法里执行,参数为设备变量列表,返回源读取变量列表
|
||||
// /// </summary>
|
||||
// protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
|
||||
// {
|
||||
// //实现将设备变量打包成源读取变量
|
||||
// //比如如果需要实现MC中的字多读功能,需将多个变量地址打包成一个源读取地址和读取长度,根据一系列规则,添加解析标识,然后在返回的整个字节数组中解析出原来的变量地址代表的数据字节
|
||||
/// <summary>
|
||||
/// 变量打包操作,会在默认的AfterVariablesChangedAsync方法里执行,参数为设备变量列表,返回源读取变量列表
|
||||
/// </summary>
|
||||
protected override Task<List<VariableSourceRead>> ProtectedLoadSourceReadAsync(List<VariableRuntime> deviceVariables)
|
||||
{
|
||||
//实现将设备变量打包成源读取变量
|
||||
//比如如果需要实现MC中的字多读功能,需将多个变量地址打包成一个源读取地址和读取长度,根据一系列规则,添加解析标识,然后在返回的整个字节数组中解析出原来的变量地址代表的数据字节
|
||||
|
||||
// //一般可操作 VariableRuntime 类中的 index, thingsgatewaybitconvter 等属性
|
||||
// //一般可操作 VariableSourceRead 类中的 address, length 等属性
|
||||
//一般可操作 VariableRuntime 类中的 index, thingsgatewaybitconvter 等属性
|
||||
//一般可操作 VariableSourceRead 类中的 address, length 等属性
|
||||
|
||||
// return Task.FromResult(new List<VariableSourceRead>());
|
||||
// }
|
||||
return Task.FromResult(new List<VariableSourceRead>());
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 如果不实现ReadSourceAsync方法,可以返回flase
|
||||
// /// </summary>
|
||||
// protected override bool VariableSourceReadsEnable => base.VariableSourceReadsEnable;
|
||||
/// <summary>
|
||||
/// 如果不实现ReadSourceAsync方法,可以返回flase
|
||||
/// </summary>
|
||||
protected override bool VariableSourceReadsEnable => base.VariableSourceReadsEnable;
|
||||
|
||||
// /// <summary>
|
||||
// /// 获取任务列表,默认会实现 TestOnline任务,SetDeviceStatus任务以及 VariableSourceRead等任务,VariableSourceRead任务启用条件为<see cref="VariableSourceReadsEnable"/> 为true。任务即是timer实现,可通过<see cref="ScheduledTaskHelper.GetTask(string, Func{object?, CancellationToken, Task}, object?, TouchSocket.Core.ILog, CancellationToken)"/> 方法实现定时任务
|
||||
// /// </summary>
|
||||
// /// <param name="cancellationToken"></param>
|
||||
// /// <returns></returns>
|
||||
// protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken)
|
||||
// {
|
||||
// return base.ProtectedGetTasks(cancellationToken);
|
||||
// }
|
||||
/// <summary>
|
||||
/// 获取任务列表,默认会实现 TestOnline任务,SetDeviceStatus任务以及 VariableSourceRead等任务,VariableSourceRead任务启用条件为<see cref="VariableSourceReadsEnable"/> 为true。任务即是timer实现,可通过<see cref="ScheduledTaskHelper.GetTask(string, Func{object?, CancellationToken, Task}, object?, TouchSocket.Core.ILog, CancellationToken)"/> 方法实现定时任务
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
protected override List<IScheduledTask> ProtectedGetTasks(CancellationToken cancellationToken)
|
||||
{
|
||||
return base.ProtectedGetTasks(cancellationToken);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 实现离线重连任务
|
||||
// /// </summary>
|
||||
// /// <param name="state"></param>
|
||||
// /// <param name="cancellationToken"></param>
|
||||
// /// <returns></returns>
|
||||
// protected override Task TestOnline(object? state, CancellationToken cancellationToken)
|
||||
// {
|
||||
// return base.TestOnline(state, cancellationToken);
|
||||
// }
|
||||
/// <summary>
|
||||
/// 实现离线重连任务
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
protected override Task TestOnline(object? state, CancellationToken cancellationToken)
|
||||
{
|
||||
return base.TestOnline(state, cancellationToken);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 返回是否成功连接设备
|
||||
// /// </summary>
|
||||
// /// <returns></returns>
|
||||
// public override bool IsConnected()
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
/// <summary>
|
||||
/// 返回是否成功连接设备
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override bool IsConnected()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 在变量发生组态变化后执行,默认会执行<see cref="ProtectedLoadSourceReadAsync"/> 方法,重新获取源读取变量列表,并且重新启动VariableSourceRead等任务
|
||||
// /// </summary>
|
||||
// /// <param name="cancellationToken"></param>
|
||||
// /// <returns></returns>
|
||||
// public override Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
|
||||
// {
|
||||
// return base.AfterVariablesChangedAsync(cancellationToken);
|
||||
// }
|
||||
/// <summary>
|
||||
/// 在变量发生组态变化后执行,默认会执行<see cref="ProtectedLoadSourceReadAsync"/> 方法,重新获取源读取变量列表,并且重新启动VariableSourceRead等任务
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
public override Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return base.AfterVariablesChangedAsync(cancellationToken);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 变量寄存器的字符串描述
|
||||
// /// </summary>
|
||||
// /// <returns></returns>
|
||||
// public override string GetAddressDescription()
|
||||
// {
|
||||
// return base.GetAddressDescription();
|
||||
// }
|
||||
/// <summary>
|
||||
/// 变量寄存器的字符串描述
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string GetAddressDescription()
|
||||
{
|
||||
return base.GetAddressDescription();
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 设备暂停时执行,默认会暂停所有任务
|
||||
// /// </summary>
|
||||
// /// <param name="pause"></param>
|
||||
// public override void PauseThread(bool pause)
|
||||
// {
|
||||
// base.PauseThread(pause);
|
||||
// }
|
||||
/// <summary>
|
||||
/// 设备暂停时执行,默认会暂停所有任务
|
||||
/// </summary>
|
||||
/// <param name="pause"></param>
|
||||
public override void PauseThread(bool pause)
|
||||
{
|
||||
base.PauseThread(pause);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 开始前执行
|
||||
// /// </summary>
|
||||
// protected override Task ProtectedStartAsync(CancellationToken cancellationToken)
|
||||
// {
|
||||
// //一般实现PLC长连接
|
||||
// return base.ProtectedStartAsync(cancellationToken);
|
||||
// }
|
||||
/// <summary>
|
||||
/// 开始前执行
|
||||
/// </summary>
|
||||
protected override Task ProtectedStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
//一般实现PLC长连接
|
||||
return base.ProtectedStartAsync(cancellationToken);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 写入变量,实现设备写入操作,注意执行写锁, using var writeLock =await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
|
||||
// /// </summary>
|
||||
// protected override ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
|
||||
// {
|
||||
// return base.WriteValuesAsync(writeInfoLists, cancellationToken);
|
||||
// }
|
||||
/// <summary>
|
||||
/// 写入变量,实现设备写入操作,注意执行写锁, using var writeLock =await ReadWriteLock.WriterLockAsync(cancellationToken).ConfigureAwait(false);
|
||||
/// </summary>
|
||||
protected override ValueTask<Dictionary<string, OperResult>> WriteValuesAsync(Dictionary<VariableRuntime, JToken> writeInfoLists, CancellationToken cancellationToken)
|
||||
{
|
||||
return base.WriteValuesAsync(writeInfoLists, cancellationToken);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 读取源变量,在<see cref="VariableSourceReadsEnable"/> 为true时,添加源读取任务,任务启动时会执行
|
||||
// /// 一般需要更新设备变量值,调用<see cref="VariableRuntime.SetValue(object?, DateTime, bool)"/>
|
||||
// /// </summary>
|
||||
// protected override ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
|
||||
// {
|
||||
// return base.ReadSourceAsync(variableSourceRead, cancellationToken);
|
||||
// }
|
||||
/// <summary>
|
||||
/// 读取源变量,在<see cref="VariableSourceReadsEnable"/> 为true时,添加源读取任务,任务启动时会执行
|
||||
/// 一般需要更新设备变量值,调用<see cref="VariableRuntime.SetValue(object?, DateTime, bool)"/>
|
||||
/// </summary>
|
||||
protected override ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
|
||||
{
|
||||
return base.ReadSourceAsync(variableSourceRead, cancellationToken);
|
||||
}
|
||||
|
||||
// protected override Task DisposeAsync(bool disposing)
|
||||
// {
|
||||
// return base.DisposeAsync(disposing);
|
||||
// }
|
||||
protected override Task DisposeAsync(bool disposing)
|
||||
{
|
||||
return base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
// /// <summary>
|
||||
// /// 特殊方法,添加<see cref="DynamicMethodAttribute"/> 特性 ,返回IOperResult
|
||||
// /// 支持<see cref="CancellationToken"/> 参数,需放到最后
|
||||
// /// 默认解析方式为英文分号
|
||||
// /// 比如rpc参数为 test1;test2,解析query1="test1",query2="test2"
|
||||
// /// 也可以在变量地址中填入test1,rpc参数传入test2,解析query1="test1",query2="test2"
|
||||
// /// </summary>
|
||||
// [DynamicMethod("测试特殊方法")]
|
||||
// public IOperResult<string> TestMethod(string query1, string query2, CancellationToken cancellationToken)
|
||||
// {
|
||||
// return new OperResult<string>() { Content = "测试特殊方法" };
|
||||
// }
|
||||
//}
|
||||
/// <summary>
|
||||
/// 特殊方法,添加<see cref="DynamicMethodAttribute"/> 特性 ,返回IOperResult
|
||||
/// 支持<see cref="CancellationToken"/> 参数,需放到最后
|
||||
/// 默认解析方式为英文分号
|
||||
/// 比如rpc参数为 test1;test2,解析query1="test1",query2="test2"
|
||||
/// 也可以在变量地址中填入test1,rpc参数传入test2,解析query1="test1",query2="test2"
|
||||
/// </summary>
|
||||
[DynamicMethod("测试特殊方法")]
|
||||
public IOperResult<string> TestMethod(string query1, string query2, CancellationToken cancellationToken)
|
||||
{
|
||||
return new OperResult<string>() { Content = "测试特殊方法" };
|
||||
}
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 插件类配置
|
||||
///// </summary>
|
||||
//public class TestCollectProperty1 : CollectPropertyBase
|
||||
//{
|
||||
// /// <summary>
|
||||
// /// 添加<see cref="DynamicPropertyAttribute"/> 特性,如需多语言配置,可添加json资源,参考其他插件
|
||||
// /// </summary>
|
||||
// [DynamicProperty(Description = null, Remark = null)]
|
||||
// public string TestString { get; set; }
|
||||
/// <summary>
|
||||
/// 插件类配置
|
||||
/// </summary>
|
||||
public class TestCollectProperty1 : CollectPropertyBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 添加<see cref="DynamicPropertyAttribute"/> 特性,如需多语言配置,可添加json资源,参考其他插件
|
||||
/// </summary>
|
||||
[DynamicProperty(Description = null, Remark = null)]
|
||||
public string TestString { get; set; }
|
||||
|
||||
//}
|
||||
}
|
||||
|
@@ -1,56 +1,56 @@
|
||||
//////------------------------------------------------------------------------------
|
||||
//////此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
////// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
////// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
////// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
////// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
////// 使用文档:https://thingsgateway.cn/
|
||||
////// QQ群:605534569
|
||||
////// ------------------------------------------------------------------------------
|
||||
////------------------------------------------------------------------------------
|
||||
////此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
//// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
//// 使用文档:https://thingsgateway.cn/
|
||||
//// QQ群:605534569
|
||||
//// ------------------------------------------------------------------------------
|
||||
|
||||
//using System.Dynamic;
|
||||
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);
|
||||
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);
|
||||
variableObj.TryAdd(tag.Name, alarmObj);
|
||||
}
|
||||
expando.TryAdd("alarms", variableObj);
|
||||
|
||||
// expandos.Add(expando);
|
||||
// }
|
||||
// deviceObj.TryAdd(group.Key, expandos);
|
||||
// deviceObjs.Add(deviceObj);
|
||||
// }
|
||||
expandos.Add(expando);
|
||||
}
|
||||
deviceObj.TryAdd(group.Key, expandos);
|
||||
deviceObjs.Add(deviceObj);
|
||||
}
|
||||
|
||||
// return deviceObjs;
|
||||
// }
|
||||
//}
|
||||
return deviceObjs;
|
||||
}
|
||||
}
|
@@ -1,46 +1,46 @@
|
||||
|
||||
//using System.Text;
|
||||
using System.Text;
|
||||
|
||||
//using ThingsGateway.Gateway.Application;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
|
||||
//public class TestExexcuteExpressions : IExexcuteExpressions
|
||||
//{
|
||||
public class TestExexcuteExpressions : IExexcuteExpressions
|
||||
{
|
||||
|
||||
// public TouchSocket.Core.ILog Logger { get; set; }
|
||||
public TouchSocket.Core.ILog Logger { get; set; }
|
||||
|
||||
// public async System.Threading.Tasks.Task<NodeOutput> ExecuteAsync(NodeInput input, System.Threading.CancellationToken cancellationToken)
|
||||
// {
|
||||
// //想上传mqtt,可以自己写mqtt上传代码,或者通过mqtt插件的公开方法上传
|
||||
public async System.Threading.Tasks.Task<NodeOutput> ExecuteAsync(NodeInput input, System.Threading.CancellationToken cancellationToken)
|
||||
{
|
||||
//想上传mqtt,可以自己写mqtt上传代码,或者通过mqtt插件的公开方法上传
|
||||
|
||||
// //直接获取mqttclient插件类型的第一个设备
|
||||
// var mqttClient = GlobalData.ReadOnlyChannels.FirstOrDefault(a => a.Value.PluginName == "ThingsGateway.Plugin.Mqtt.MqttClient").Value?.ReadDeviceRuntimes?.FirstOrDefault().Value?.Driver as ThingsGateway.Plugin.Mqtt.MqttClient;
|
||||
// if (mqttClient == null)
|
||||
// throw new("mqttClient NOT FOUND");
|
||||
//直接获取mqttclient插件类型的第一个设备
|
||||
//var mqttClient = GlobalData.ReadOnlyChannels.FirstOrDefault(a => a.Value.PluginName == "ThingsGateway.Plugin.Mqtt.MqttClient").Value?.ReadDeviceRuntimes?.FirstOrDefault().Value?.Driver as ThingsGateway.Plugin.Mqtt.MqttClient;
|
||||
//if (mqttClient == null)
|
||||
// throw new("mqttClient NOT FOUND");
|
||||
|
||||
// TopicArray topicArray = new()
|
||||
// {
|
||||
// Topic = "test",
|
||||
// Payload = Encoding.UTF8.GetBytes("test")
|
||||
// };
|
||||
// var result = await mqttClient.MqttUpAsync(topicArray, default).ConfigureAwait(false);// 主题 和 负载
|
||||
// if (!result.IsSuccess)
|
||||
// throw new(result.ErrorMessage);
|
||||
// return new NodeOutput() { Value = result };
|
||||
//TopicArray topicArray = new()
|
||||
//{
|
||||
// Topic = "test",
|
||||
// Payload = Encoding.UTF8.GetBytes("test")
|
||||
//};
|
||||
//var result = await mqttClient.MqttUpAsync(topicArray, default).ConfigureAwait(false);// 主题 和 负载
|
||||
//if (!result.IsSuccess)
|
||||
// throw new(result.ErrorMessage);
|
||||
//return new NodeOutput() { Value = result };
|
||||
|
||||
// //通过设备名称找出mqttClient插件
|
||||
// //var mqttClient = GlobalData.ReadOnlyDevices.FirstOrDefault(a => a.Value.Name == "mqttDevice1").Value?.Driver as ThingsGateway.Plugin.Mqtt.MqttClient;
|
||||
//通过设备名称找出mqttClient插件
|
||||
var mqttClient = GlobalData.ReadOnlyDevices.FirstOrDefault(a => a.Value.Name == "mqttDevice1").Value?.Driver as ThingsGateway.Plugin.Mqtt.MqttClient;
|
||||
|
||||
// //if (mqttClient == null)
|
||||
// // throw new("mqttClient NOT FOUND");
|
||||
if (mqttClient == null)
|
||||
throw new("mqttClient NOT FOUND");
|
||||
|
||||
// //TopicArray topicArray = new()
|
||||
// //{
|
||||
// // Topic = "test",
|
||||
// // Payload = Encoding.UTF8.GetBytes("test")
|
||||
// //};
|
||||
// //var result = await mqttClient.MqttUpAsync(topicArray, default).ConfigureAwait(false);// 主题 和 负载
|
||||
// //if (!result.IsSuccess)
|
||||
// // throw new(result.ErrorMessage);
|
||||
// //return new NodeOutput() { Value = result };
|
||||
// }
|
||||
//}
|
||||
TopicArray topicArray = new()
|
||||
{
|
||||
Topic = "test",
|
||||
Payload = Encoding.UTF8.GetBytes("test")
|
||||
};
|
||||
var result = await mqttClient.MqttUpAsync(topicArray, default).ConfigureAwait(false);// 主题 和 负载
|
||||
if (!result.IsSuccess)
|
||||
throw new(result.ErrorMessage);
|
||||
return new NodeOutput() { Value = result };
|
||||
}
|
||||
}
|
@@ -1,223 +1,236 @@
|
||||
//////------------------------------------------------------------------------------
|
||||
//////此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
////// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
////// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
////// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
////// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
////// 使用文档:https://thingsgateway.cn/
|
||||
////// QQ群:605534569
|
||||
////// ------------------------------------------------------------------------------
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
//using ThingsGateway.Foundation;
|
||||
//using ThingsGateway.Gateway.Application;
|
||||
//using ThingsGateway.NewLife.Extension;
|
||||
//public class TestKafkaDynamicModel : DynamicModelBase
|
||||
//{
|
||||
// private Dictionary<string, VariableRuntime> variableRuntimes = new();
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Gateway.Application;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
public class TestKafkaDynamicModel1 : DynamicModelBase
|
||||
{
|
||||
private Dictionary<string, VariableRuntime> variableRuntimes = new();
|
||||
|
||||
// private long id = 0;
|
||||
private long id = 0;
|
||||
|
||||
// public TestKafkaDynamicModel()
|
||||
// {
|
||||
// var name = "kafka1";
|
||||
// if (GlobalData.ReadOnlyDevices.TryGetValue(name, out var kafka1))
|
||||
// {
|
||||
// id = kafka1.Id;
|
||||
public TestKafkaDynamicModel1()
|
||||
{
|
||||
var name = "测试MqttServer";
|
||||
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);
|
||||
// }
|
||||
// }
|
||||
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}");
|
||||
// }
|
||||
}
|
||||
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;
|
||||
// }
|
||||
private ConcurrentDictionary<Tuple<string, string, int>, DateTime> EventKeyTimes = new();
|
||||
|
||||
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);
|
||||
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))
|
||||
{
|
||||
|
||||
// bool isRecover = opcAeEventData.EventType != Opc.Ae.EventType.Condition ? false : !((Opc.Ae.ConditionState)opcAeEventData.NewState).HasFlag(Opc.Ae.ConditionState.Active);
|
||||
var ack = opcAeEventData.EventType != Opc.Ae.EventType.Condition ? false : ((Opc.Ae.ConditionState)opcAeEventData.NewState).HasFlag(Opc.Ae.ConditionState.Acknowledged);
|
||||
|
||||
// //构建告警实体
|
||||
// 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 告警类型,子系统产生告警的类型,可能需要固定备注值
|
||||
bool isRecover = opcAeEventData.EventType != Opc.Ae.EventType.Condition ? false : !((Opc.Ae.ConditionState)opcAeEventData.NewState).HasFlag(Opc.Ae.ConditionState.Active);
|
||||
if (opcAeEventData.EventType != Opc.Ae.EventType.Condition)
|
||||
{
|
||||
bool alarm = (opcAeEventData.Message).Contains("raised");
|
||||
if (alarm)
|
||||
{
|
||||
opcAeEventData.AlarmTime = opcAeEventData.Time;
|
||||
EventKeyTimes.AddOrUpdate(Tuple.Create(opcAeEventData.SourceID, opcAeEventData.ConditionName, opcAeEventData.Cookie), opcAeEventData.Time, (k, v) => opcAeEventData.Time);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (EventKeyTimes.TryGetValue(Tuple.Create(opcAeEventData.SourceID, opcAeEventData.ConditionName, opcAeEventData.Cookie), out var time))
|
||||
opcAeEventData.AlarmTime = time;
|
||||
else
|
||||
opcAeEventData.AlarmTime = opcAeEventData.Time;
|
||||
}
|
||||
isRecover = !alarm;
|
||||
}
|
||||
|
||||
// ConfirmedTime = ack ? opcAeEventData.Time.DateTimeToUnixTimestamp() : null, //告警确认时间
|
||||
// FixTime = isRecover ? opcAeEventData.Time.DateTimeToUnixTimestamp() : null, //解除告警时间
|
||||
// LastTime = opcAeEventData.AlarmTime.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}");
|
||||
// }
|
||||
// }
|
||||
//构建告警实体
|
||||
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))}{variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data6))}{opcAeEventData.Message}", //告警内容,设备名称+告警内容(包含阈值信息),可能opcae里没有带阈值信息,那么就需要录入固定值,可选Data10
|
||||
alarmType = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data7)), // opcAeEventData.Severity 告警类型,子系统产生告警的类型,可能需要固定备注值
|
||||
|
||||
// return alarmEntities;
|
||||
// }
|
||||
//}
|
||||
confirmedTime = ack ? opcAeEventData.Time.DateTimeToUnixTimestamp() : null, //告警确认时间
|
||||
fixTime = isRecover ? opcAeEventData.Time : null, //解除告警时间
|
||||
lastTime = opcAeEventData.AlarmTime, //产生告警时间
|
||||
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>
|
||||
/// 告警实体
|
||||
/// </summary>
|
||||
public class KafkaAlarmEntity
|
||||
{
|
||||
/// <summary>
|
||||
/// 告警编码唯一 (非空)
|
||||
/// 示例:"8e8a118ac452fd04da8c26fa588a7cab"
|
||||
/// </summary>
|
||||
public string alarmCode { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 资源编码,唯一编码,需要按照映射表上传 (非空)
|
||||
// /// 示例:"RS_A6K9MUSG19V"
|
||||
// /// </summary>
|
||||
// public string ResourceCode { get; set; }
|
||||
/// <summary>
|
||||
/// 资源编码,唯一编码,需要按照映射表上传 (非空)
|
||||
/// 示例:"RS_A6K9MUSG19V"
|
||||
/// </summary>
|
||||
public string resourceCode { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 资源名称,需要按照映射表上传 (非空)
|
||||
// /// 示例:"MB-A7"
|
||||
// /// </summary>
|
||||
// public string ResourceName { get; set; }
|
||||
/// <summary>
|
||||
/// 资源名称,需要按照映射表上传 (非空)
|
||||
/// 示例:"MB-A7"
|
||||
/// </summary>
|
||||
public string resourceName { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 指标编码唯一,需要按照映射表上传 (非空)
|
||||
// /// 示例:"ActivePowerPa"
|
||||
// /// </summary>
|
||||
// public string MetricCode { get; set; }
|
||||
/// <summary>
|
||||
/// 指标编码唯一,需要按照映射表上传 (非空)
|
||||
/// 示例:"ActivePowerPa"
|
||||
/// </summary>
|
||||
public string metricCode { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 指标名称,需要按照映射表上传 (非空)
|
||||
// /// 示例:"有功功率Pa"
|
||||
// /// </summary>
|
||||
// public string MetricName { get; set; }
|
||||
/// <summary>
|
||||
/// 指标名称,需要按照映射表上传 (非空)
|
||||
/// 示例:"有功功率Pa"
|
||||
/// </summary>
|
||||
public string metricName { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 告警内容:设备名称+告警内容(包含阈值信息) (非空)
|
||||
// /// 示例:"MB-A7,有功功率Pa > 30"
|
||||
// /// </summary>
|
||||
// public string Content { get; set; }
|
||||
/// <summary>
|
||||
/// 告警内容:设备名称+告警内容(包含阈值信息) (非空)
|
||||
/// 示例:"MB-A7,有功功率Pa > 30"
|
||||
/// </summary>
|
||||
public string content { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 告警类型,子系统产生告警的类型 (非空)
|
||||
// /// 示例:"0101" 表示高限报警
|
||||
// /// </summary>
|
||||
// public string AlarmType { get; set; }
|
||||
/// <summary>
|
||||
/// 告警类型,子系统产生告警的类型 (非空)
|
||||
/// 示例:"0101" 表示高限报警
|
||||
/// </summary>
|
||||
public string alarmType { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 告警确认时间 (可空,时间戳)
|
||||
// /// 示例:1586152800000
|
||||
// /// </summary>
|
||||
// public long? ConfirmedTime { get; set; }
|
||||
/// <summary>
|
||||
/// 告警确认时间 (可空,时间戳)
|
||||
/// 示例:1586152800000
|
||||
/// </summary>
|
||||
public long? confirmedTime { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 解除告警时间 (可空,时间戳)
|
||||
// /// 示例:1586152800000
|
||||
// /// </summary>
|
||||
// public long? FixTime { get; set; }
|
||||
/// <summary>
|
||||
/// 解除告警时间 (可空,时间戳)
|
||||
/// 示例:1586152800000
|
||||
/// </summary>
|
||||
public DateTime? fixTime { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 产生告警时间 (非空,时间戳)
|
||||
// /// 示例:1586152800000
|
||||
// /// </summary>
|
||||
// public long LastTime { get; set; }
|
||||
/// <summary>
|
||||
/// 产生告警时间 (非空,时间戳)
|
||||
/// 示例:1586152800000
|
||||
/// </summary>
|
||||
public DateTime lastTime { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 告警状态 (非空)
|
||||
// /// 可选值:UNFIXED(新增告警)、FIXED(解除告警)
|
||||
// /// </summary>
|
||||
// public string Status { get; set; }
|
||||
/// <summary>
|
||||
/// 告警状态 (非空)
|
||||
/// 可选值:UNFIXED(新增告警)、FIXED(解除告警)
|
||||
/// </summary>
|
||||
public string status { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 告警等级,需要按照映射表上传 (非空)
|
||||
// /// 示例:"1"
|
||||
// /// </summary>
|
||||
// public string AlarmLevel { get; set; }
|
||||
/// <summary>
|
||||
/// 告警等级,需要按照映射表上传 (非空)
|
||||
/// 示例:"1"
|
||||
/// </summary>
|
||||
public string alarmLevel { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 子系统编码 (非空)
|
||||
// /// 示例:"MS_NEW_PD_DCIM_001"
|
||||
// /// </summary>
|
||||
// public string SubSystemCode { 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>
|
||||
/// 默认填写字段 (非空)
|
||||
/// 固定值:"SUB_SYSTEM_ALARM"
|
||||
/// </summary>
|
||||
public string type { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 告警确认人 (可空)
|
||||
// /// 示例:"admin3"
|
||||
// /// </summary>
|
||||
// public string ConfirmAccount { get; set; }
|
||||
/// <summary>
|
||||
/// 告警确认人 (可空)
|
||||
/// 示例:"admin3"
|
||||
/// </summary>
|
||||
public string confirmAccount { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 告警清除人 (可空)
|
||||
// /// 示例:"admin"
|
||||
// /// </summary>
|
||||
// public string ClearAccount { get; set; }
|
||||
/// <summary>
|
||||
/// 告警清除人 (可空)
|
||||
/// 示例:"admin"
|
||||
/// </summary>
|
||||
public string clearAccount { get; set; }
|
||||
|
||||
// /// <summary>
|
||||
// /// 告警处理说明 (可空)
|
||||
// /// 示例:"admin"
|
||||
// /// </summary>
|
||||
// public string ProcessInstruction { get; set; }
|
||||
//}
|
||||
/// <summary>
|
||||
/// 告警处理说明 (可空)
|
||||
/// 示例:"admin"
|
||||
/// </summary>
|
||||
public string processInstruction { get; set; }
|
||||
}
|
@@ -1,46 +1,46 @@
|
||||
////------------------------------------------------------------------------------
|
||||
////此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
//// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
//// 使用文档:https://thingsgateway.cn/
|
||||
//// QQ群:605534569
|
||||
//// ------------------------------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
//此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人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 ThingsGateway.Gateway.Application;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.Plugin.DB;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
//using TouchSocket.Core;
|
||||
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 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());
|
||||
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);
|
||||
// }
|
||||
//}
|
||||
await db.Insertable(opcDatas).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
49
src/ThingsGateway.ScriptDebug/Test/TestServerRpc.cs
Normal file
49
src/ThingsGateway.ScriptDebug/Test/TestServerRpc.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
//------------------------------------------------------------------------------
|
||||
//此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using MQTTnet;
|
||||
using MQTTnet.Server;
|
||||
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
using ThingsGateway.Plugin.Mqtt;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
public class TestServerRpc : DynamicMqttServerRpcBase
|
||||
{
|
||||
public override async Task RPCInvokeAsync(ILog logMessage, InterceptingPublishEventArgs args, MqttServerProperty driverPropertys, MQTTnet.Server.MqttServer mqttServer, Func<string, Dictionary<string, Dictionary<string, JToken>>, ValueTask<Dictionary<string, Dictionary<string, IOperResult>>>> getRpcResult, CancellationToken cancellationToken)
|
||||
{
|
||||
if (driverPropertys.RpcWriteTopic.IsNullOrWhiteSpace()) return;
|
||||
|
||||
var t = string.Format(null, "{0}/+", driverPropertys.RpcWriteTopic);
|
||||
if (MqttTopicFilterComparer.Compare(args.ApplicationMessage.Topic, t) != MqttTopicFilterCompareResult.IsMatch)
|
||||
return;
|
||||
var rpcDatas = Encoding.UTF8.GetString(args.ApplicationMessage.Payload).FromJsonNetString<Dictionary<string, Dictionary<string, JToken>>>();
|
||||
if (rpcDatas == null)
|
||||
return;
|
||||
var mqttRpcResult = await getRpcResult(args.ClientId, rpcDatas).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
var variableMessage = new MqttApplicationMessageBuilder()
|
||||
.WithTopic($"{args.ApplicationMessage.Topic}/Response")
|
||||
.WithPayload(mqttRpcResult.ToSystemTextJsonString(driverPropertys.JsonFormattingIndented)).Build();
|
||||
await mqttServer.InjectApplicationMessage(new InjectedMqttApplicationMessage(variableMessage), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,43 +3,24 @@
|
||||
<Import Project="..\Version.props" />
|
||||
|
||||
|
||||
<ItemGroup Condition=" '$(SolutionName)' != 'ThingsGatewayRelease'">
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
|
||||
<ProjectReference Include="..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
<!--发布版-->
|
||||
<Import Project="targets\Gateway.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " />
|
||||
<Import Project="targets\Admin.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayRelease' " />
|
||||
|
||||
<Import Project="targets\PluginContext.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' != 'Debug' " />
|
||||
|
||||
<!--nuget包解压复制文件,上下文动态加载-->
|
||||
<Import Project="targets\PluginContext.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro' AND '$(Configuration)' != 'Debug' " />
|
||||
<!--直接引用-->
|
||||
<Import Project="targets\PluginDebug.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug' " />
|
||||
<Import Project="targets\PluginDebug.targets"/>
|
||||
|
||||
|
||||
<Import Project="targets\ProPluginDebug.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' "/>
|
||||
|
||||
|
||||
<!--nuget包解压复制文件,插件域隔离动态加载-->
|
||||
<!--<Import Project="targets\Plugin.targets" />-->
|
||||
|
||||
<!--nuget包解压复制文件,上下文动态加载,Pro插件-->
|
||||
<Import Project="targets\Pro2.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro' OR '$(Configuration)' != 'Debug'" />
|
||||
<Import Project="targets\Pro2.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro'" />
|
||||
|
||||
<!--直接引用Pro-->
|
||||
<Import Project="targets\PluginDebug.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug'" />
|
||||
<Import Project="targets\Pro7.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro'" />
|
||||
|
||||
|
||||
<Import Project="targets\ProPluginDebug.targets" Condition=" '$(SolutionName)' == 'ThingsGatewayPro' AND '$(Configuration)' == 'Debug'" />
|
||||
|
||||
<!--<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'" />-->
|
||||
<!--nuget包解压复制文件,上下文动态加载,Pro插件-->
|
||||
<Import Project="targets\Pro7.targets" Condition=" '$(SolutionName)' != 'ThingsGatewayPro' OR '$(Configuration)' != 'Debug'" />
|
||||
|
||||
<!--打包复制-->
|
||||
<Import Project="targets\PluginPublish.targets" />
|
||||
<PropertyGroup>
|
||||
|
||||
<GenerateDocumentationFile>false</GenerateDocumentationFile>
|
||||
|
@@ -5,61 +5,61 @@
|
||||
<!--Modbus 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Modbus" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--SiemensS7 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.SiemensS7" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--Dlt645 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Dlt645" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--OpcDa 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.OpcDa" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--OpcUa 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.OpcUa" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--DB 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.DB" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--Kafka 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Kafka" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--Mqtt 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Mqtt" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--RabbitMQ 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.RabbitMQ" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--webhook 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Http" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
|
||||
|
@@ -6,62 +6,62 @@
|
||||
<!--Modbus 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Modbus" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--SiemensS7 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.SiemensS7" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--Dlt645 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Dlt645" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--OpcDa 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.OpcDa" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--OpcUa 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.OpcUa" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--DB 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.DB" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--Kafka 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Kafka" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--Mqtt 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Mqtt" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<!--RabbitMQ 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.RabbitMQ" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
|
||||
<!--webhook 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Http" Version="$(PluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
|
||||
|
@@ -3,39 +3,39 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ThingsGateway.Plugin.SyncBridge" Version="$(ProPluginVersion)" GeneratePathProperty="true">
|
||||
<Private>false</Private>
|
||||
<IncludeAssets> native;</IncludeAssets>
|
||||
<IncludeAssets> all;</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="ThingsGateway.Plugin.Inovance" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.TIANXIN" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.HJ212" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.BACnet" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.Inovance" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.TIANXIN" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.HJ212" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.BACnet" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--AllenBradleyCip 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.AllenBradleyCip" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.AllenBradleyCip" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--DCON 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.DCON" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.DCON" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--EDPF_NT 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.EDPF_NT" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.EDPF_NT" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--KELID2008 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.KELID2008" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.KELID2008" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--LKSIS 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.LKSIS" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.LKSIS" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--Melsec 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Melsec" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.Melsec" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--Omron 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.Omron" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.Omron" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--DKQ_A16D 插件-->
|
||||
<!--<PackageReference Include="ThingsGateway.Plugin.DKQ_A16D" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />-->
|
||||
<!--<PackageReference Include="ThingsGateway.Plugin.DKQ_A16D" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />-->
|
||||
<!--IDR210 插件-->
|
||||
<!--<PackageReference Include="ThingsGateway.Plugin.IDR210" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />-->
|
||||
<!--<PackageReference Include="ThingsGateway.Plugin.IDR210" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />-->
|
||||
<!--URF_R330 插件-->
|
||||
<!--<PackageReference Include="ThingsGateway.Plugin.URF_R330" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />-->
|
||||
<!--<PackageReference Include="ThingsGateway.Plugin.URF_R330" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />-->
|
||||
<!--USBScaner 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.USBScaner" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.USBScaner" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
|
||||
|
||||
<PackageReference Include="ThingsGateway.Plugin.SECS" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.TS550" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.Vigor" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets=" native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.SECS" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.TS550" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.Vigor" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -2,9 +2,9 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!--HUANANSFSK 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.HUANANSFSK" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.HUANANSFSK" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<!--YPSFSK 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.YPSFSK" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.YPSFSK" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyOtherPlugin3NugetPackages" AfterTargets="Build">
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<Project>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ThingsGateway.Plugin.ModbusC1" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.ModbusGY" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.ModbusC1" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.ModbusGY" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!--MqttYINGKE 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.MqttYINGKE" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.MqttYINGKE" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyOtherPlugin5NugetPackages" AfterTargets="Build">
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!--MqttYINGKE 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.IXCom29s" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.IXCom29s" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyOtherPlugin6NugetPackages" AfterTargets="Build">
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<!--MqttYINGKE 插件-->
|
||||
<PackageReference Include="ThingsGateway.Plugin.OpcAe" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="native;" />
|
||||
<PackageReference Include="ThingsGateway.Plugin.OpcAe" Version="$(ProPluginVersion)" GeneratePathProperty="true" Private="false" IncludeAssets="all;" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target Name="CopyOtherPlugin7NugetPackages" AfterTargets="Build">
|
||||
|
@@ -33,6 +33,7 @@
|
||||
<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" />
|
||||
<ProjectReference Include="..\PluginPro\ThingsGateway.Gateway.VariableCore\ThingsGateway.Gateway.VariableCore.csproj" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
|
Reference in New Issue
Block a user