mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-28 06:07:10 +08:00
Compare commits
18 Commits
10.11.4.0
...
10.11.19.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ea9529a5f | ||
|
|
4e6be23aac | ||
|
|
2fabbd236b | ||
|
|
163a66530e | ||
|
|
29073a00c4 | ||
|
|
c6d4d1ecfa | ||
|
|
ba16889cad | ||
|
|
5aaed35b0f | ||
|
|
df067c91eb | ||
|
|
2078b4a60b | ||
|
|
20a2e3ff8e | ||
|
|
61a973b1b5 | ||
|
|
cbd72e2081 | ||
|
|
4e0377b20c | ||
|
|
fd318d3cdc | ||
|
|
515bdb9700 | ||
|
|
46c1780017 | ||
|
|
fe78a4c3ca |
@@ -15,6 +15,7 @@ using System.Text;
|
||||
|
||||
using ThingsGateway.Admin.Application;
|
||||
using ThingsGateway.DB;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Log;
|
||||
|
||||
namespace ThingsGateway.AdminServer;
|
||||
@@ -64,7 +65,7 @@ public class Program
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
builder.Host.UseSystemd();
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
if (Runtime.IsLegacyWindows)
|
||||
builder.Logging.ClearProviders(); //去除默认的事件日志提供者,某些情况下会日志输出异常,导致程序崩溃
|
||||
}).ConfigureBuilder(builder =>
|
||||
{
|
||||
|
||||
@@ -183,19 +183,22 @@ public class Startup : AppStartup
|
||||
services.AddScoped<IAuthorizationHandler, BlazorServerAuthenticationHandler>();
|
||||
services.AddScoped<AuthenticationStateProvider, BlazorServerAuthenticationStateProvider>();
|
||||
|
||||
if (!NewLife.Runtime.IsLegacyWindows)
|
||||
{
|
||||
#if NET9_0_OR_GREATER
|
||||
var certificate = X509CertificateLoader.LoadPkcs12FromFile("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
||||
var certificate = X509CertificateLoader.LoadPkcs12FromFile("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
||||
#else
|
||||
var certificate = new X509Certificate2("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
||||
var certificate = new X509Certificate2("ThingsGateway.pfx", "ThingsGateway", X509KeyStorageFlags.EphemeralKeySet);
|
||||
#endif
|
||||
services.AddDataProtection()
|
||||
.PersistKeysToFileSystem(new DirectoryInfo("Keys"))
|
||||
.ProtectKeysWithCertificate(certificate)
|
||||
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
|
||||
{
|
||||
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
|
||||
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
|
||||
});
|
||||
services.AddDataProtection()
|
||||
.PersistKeysToFileSystem(new DirectoryInfo("Keys"))
|
||||
.ProtectKeysWithCertificate(certificate)
|
||||
.UseCryptographicAlgorithms(new AuthenticatedEncryptorConfiguration
|
||||
{
|
||||
EncryptionAlgorithm = EncryptionAlgorithm.AES_256_CBC,
|
||||
ValidationAlgorithm = ValidationAlgorithm.HMACSHA256
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Use(IApplicationBuilder applicationBuilder, IWebHostEnvironment env)
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||
<PackageReference Include="BootstrapBlazor" Version="9.9.2" />
|
||||
<PackageReference Include="BootstrapBlazor" Version="9.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.3" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.4" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="$(NET9Version)" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
45
src/Admin/ThingsGateway.NewLife.X/Common/BoundedQueue.cs
Normal file
45
src/Admin/ThingsGateway.NewLife.X/Common/BoundedQueue.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
namespace ThingsGateway.NewLife;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class BoundedQueue<T> : IEnumerable<T>
|
||||
{
|
||||
private readonly Queue<T> _queue;
|
||||
private readonly int _capacity;
|
||||
private readonly object _syncRoot = new object();
|
||||
|
||||
public BoundedQueue(int capacity)
|
||||
{
|
||||
if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity));
|
||||
_capacity = capacity;
|
||||
_queue = new Queue<T>(capacity);
|
||||
}
|
||||
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
if (_queue.Count == _capacity)
|
||||
_queue.Dequeue();
|
||||
_queue.Enqueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { lock (_syncRoot) return _queue.Count; }
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
lock (_syncRoot)
|
||||
{
|
||||
return new List<T>(_queue).GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using ThingsGateway.NewLife.Threading;
|
||||
namespace ThingsGateway.NewLife;
|
||||
|
||||
public class ExpiringDictionary<TKey, TValue> : IDisposable
|
||||
{
|
||||
private readonly ConcurrentDictionary<TKey, TValue> _dict = new();
|
||||
private readonly TimerX _cleanupTimer;
|
||||
|
||||
public ExpiringDictionary(int cleanupInterval = 60000)
|
||||
{
|
||||
_cleanupTimer = new TimerX(Clear, null, cleanupInterval, cleanupInterval) { Async = true };
|
||||
}
|
||||
|
||||
public void TryAdd(TKey key, TValue value)
|
||||
{
|
||||
_dict.TryAdd(key, value);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
return _dict.TryGetValue(key, out value);
|
||||
}
|
||||
public TValue GetOrAdd(TKey key, Func<TKey, TValue> func)
|
||||
{
|
||||
return _dict.GetOrAdd(key, func);
|
||||
}
|
||||
public TValue GetOrAdd(TKey key, TValue value)
|
||||
{
|
||||
return _dict.GetOrAdd(key, value);
|
||||
}
|
||||
|
||||
public bool TryRemove(TKey key) => _dict.TryRemove(key, out _);
|
||||
|
||||
public void Clear() => _dict.Clear();
|
||||
|
||||
private void Clear(object? state)
|
||||
{
|
||||
_dict.Clear();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_dict.Clear();
|
||||
_cleanupTimer.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -103,6 +103,54 @@ public static class Runtime
|
||||
|
||||
/// <summary>是否OSX环境</summary>
|
||||
public static Boolean OSX => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
|
||||
public static Boolean? isLegacyWindows;
|
||||
|
||||
/// <summary>
|
||||
/// 判断是否老系统 (Vista/2008/7/2008R2)
|
||||
/// </summary>
|
||||
public static Boolean IsLegacyWindows
|
||||
{
|
||||
get
|
||||
{
|
||||
if (isLegacyWindows != null) return isLegacyWindows.Value;
|
||||
|
||||
if (Windows == false)
|
||||
{
|
||||
isLegacyWindows = false;
|
||||
return isLegacyWindows.Value;
|
||||
}
|
||||
var version = Environment.OSVersion.Version;
|
||||
|
||||
// 如果能拿到真实的 6.x 就直接判断
|
||||
if (version.Major == 6 && version.Minor <= 1)
|
||||
{
|
||||
isLegacyWindows = true;
|
||||
return isLegacyWindows.Value;
|
||||
}
|
||||
if (version.Major < 6)
|
||||
{
|
||||
isLegacyWindows = true;
|
||||
return isLegacyWindows.Value;
|
||||
}
|
||||
|
||||
// 如果拿到的是 10.0(Win8.1 之后有虚拟化问题),用 OSDescription 来兜底
|
||||
var desc = RuntimeInformation.OSDescription;
|
||||
// desc 示例: "Microsoft Windows 6.1.7601" (Win7/2008R2)
|
||||
if (desc.Contains("Windows 6.0") || desc.Contains("Windows 6.1"))
|
||||
{
|
||||
isLegacyWindows = true;
|
||||
return isLegacyWindows.Value;
|
||||
}
|
||||
isLegacyWindows = false;
|
||||
return isLegacyWindows.Value;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
/// <summary>是否Web环境</summary>
|
||||
public static Boolean IsWeb => !String.IsNullOrEmpty(System.Web.HttpRuntime.AppDomainAppId);
|
||||
@@ -115,6 +163,8 @@ public static class Runtime
|
||||
|
||||
/// <summary>是否OSX环境</summary>
|
||||
public static Boolean OSX { get; } = Environment.OSVersion.Platform == PlatformID.MacOSX;
|
||||
|
||||
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace ThingsGateway.NewLife.Json.Extension;
|
||||
/// </summary>
|
||||
public static class SystemTextJsonExtension
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 默认Json规则(带缩进)
|
||||
/// </summary>
|
||||
@@ -31,37 +32,51 @@ public static class SystemTextJsonExtension
|
||||
/// </summary>
|
||||
public static JsonSerializerOptions NoneIndentedOptions;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 默认Json规则(带缩进)
|
||||
/// </summary>
|
||||
public static JsonSerializerOptions IgnoreNullIndentedOptions;
|
||||
|
||||
/// <summary>
|
||||
/// 默认Json规则(无缩进)
|
||||
/// </summary>
|
||||
public static JsonSerializerOptions IgnoreNullNoneIndentedOptions;
|
||||
|
||||
public static JsonSerializerOptions GetOptions(bool writeIndented, bool ignoreNull)
|
||||
{
|
||||
var options = new JsonSerializerOptions
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
WriteIndented = writeIndented,
|
||||
DefaultIgnoreCondition = ignoreNull
|
||||
? JsonIgnoreCondition.WhenWritingNull
|
||||
: JsonIgnoreCondition.Never,
|
||||
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
|
||||
};
|
||||
|
||||
options.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
options.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
options.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
options.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
options.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
static SystemTextJsonExtension()
|
||||
{
|
||||
IndentedOptions = new JsonSerializerOptions
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
WriteIndented = true, // 缩进
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // 忽略 null
|
||||
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
|
||||
};
|
||||
// 如有自定义Converter,这里添加
|
||||
// IndentedOptions.Converters.Add(new ByteArrayJsonConverter());
|
||||
IndentedOptions.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
IndentedOptions.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
IndentedOptions.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
IndentedOptions.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
IndentedOptions.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
NoneIndentedOptions = new JsonSerializerOptions
|
||||
{
|
||||
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
|
||||
WriteIndented = false, // 不缩进
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
|
||||
};
|
||||
NoneIndentedOptions.Converters.Add(new ByteArrayToNumberArrayConverterSystemTextJson());
|
||||
NoneIndentedOptions.Converters.Add(new JTokenSystemTextJsonConverter());
|
||||
NoneIndentedOptions.Converters.Add(new JValueSystemTextJsonConverter());
|
||||
NoneIndentedOptions.Converters.Add(new JObjectSystemTextJsonConverter());
|
||||
NoneIndentedOptions.Converters.Add(new JArraySystemTextJsonConverter());
|
||||
// NoneIndentedOptions.Converters.Add(new ByteArrayJsonConverter());
|
||||
|
||||
IndentedOptions = GetOptions(true, false);
|
||||
NoneIndentedOptions = GetOptions(false, false);
|
||||
|
||||
IgnoreNullIndentedOptions = GetOptions(true, true);
|
||||
IgnoreNullNoneIndentedOptions = GetOptions(false, true);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 反序列化
|
||||
/// </summary>
|
||||
@@ -96,17 +111,17 @@ public static class SystemTextJsonExtension
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
public static string ToSystemTextJsonString(this object item, bool indented = true)
|
||||
public static string ToSystemTextJsonString(this object item, bool indented = true, bool ignoreNull = true)
|
||||
{
|
||||
return JsonSerializer.Serialize(item, item?.GetType() ?? typeof(object), indented ? IndentedOptions : NoneIndentedOptions);
|
||||
return JsonSerializer.Serialize(item, item?.GetType() ?? typeof(object), ignoreNull ? indented ? IgnoreNullIndentedOptions : IgnoreNullNoneIndentedOptions : indented ? IndentedOptions : NoneIndentedOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 序列化
|
||||
/// </summary>
|
||||
public static byte[] ToSystemTextJsonUtf8Bytes(this object item, bool indented = true)
|
||||
public static byte[] ToSystemTextJsonUtf8Bytes(this object item, bool indented = true, bool ignoreNull = true)
|
||||
{
|
||||
return JsonSerializer.SerializeToUtf8Bytes(item, item.GetType(), indented ? IndentedOptions : NoneIndentedOptions);
|
||||
return JsonSerializer.SerializeToUtf8Bytes(item, item.GetType(), ignoreNull ? indented ? IgnoreNullIndentedOptions : IgnoreNullNoneIndentedOptions : indented ? IndentedOptions : NoneIndentedOptions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -552,19 +552,29 @@ public static class Reflect
|
||||
// return false;
|
||||
//}
|
||||
|
||||
|
||||
private static readonly ExpiringDictionary<(MethodInfo, Type, object?), Delegate> _delegateCache = new();
|
||||
|
||||
/// <summary>把一个方法转为泛型委托,便于快速反射调用</summary>
|
||||
/// <typeparam name="TFunc"></typeparam>
|
||||
/// <param name="method"></param>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
public static TFunc? As<TFunc>(this MethodInfo method, Object? target = null)
|
||||
public static TFunc? As<TFunc>(this MethodInfo method, object? target = null)
|
||||
{
|
||||
if (method == null) return default;
|
||||
|
||||
if (target == null)
|
||||
return (TFunc?)(Object?)Delegate.CreateDelegate(typeof(TFunc), method, true);
|
||||
else
|
||||
return (TFunc?)(Object?)Delegate.CreateDelegate(typeof(TFunc), target, method, true);
|
||||
var key = (method, typeof(TFunc), target);
|
||||
|
||||
if (_delegateCache.TryGetValue(key, out var del))
|
||||
return (TFunc)(object)del;
|
||||
|
||||
del = target == null
|
||||
? Delegate.CreateDelegate(typeof(TFunc), method, true)
|
||||
: Delegate.CreateDelegate(typeof(TFunc), target, method, true);
|
||||
|
||||
return (TFunc)(object)_delegateCache.GetOrAdd(key, del);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -168,12 +168,13 @@ public class TimerScheduler : ILogFeature
|
||||
_period = 60_000;
|
||||
foreach (var timer in arr)
|
||||
{
|
||||
if (!timer.Calling && CheckTime(timer, now))
|
||||
if ((timer.Reentrant || !timer.Calling) && CheckTime(timer, now))
|
||||
{
|
||||
// 必须在主线程设置状态,否则可能异步线程还没来得及设置开始状态,主线程又开始了新的一轮调度
|
||||
timer.Calling = true;
|
||||
if (timer.IsAsyncTask)
|
||||
Task.Factory.StartNew(ExecuteAsync, timer, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
ExecuteAsync(timer);
|
||||
//Task.Factory.StartNew(ExecuteAsync, timer, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
else if (!timer.Async)
|
||||
Execute(timer);
|
||||
else
|
||||
@@ -310,13 +311,17 @@ public class TimerScheduler : ILogFeature
|
||||
if (timer.IsValueTask)
|
||||
{
|
||||
var func = timer.Method.As<Func<Object?, ValueTask>>(target);
|
||||
await func!(timer.State).ConfigureAwait(false);
|
||||
var task = func!(timer.State);
|
||||
if (!task.IsCompleted)
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
var func = timer.Method.As<Func<Object?, Task>>(target);
|
||||
await func!(timer.State).ConfigureAwait(false);
|
||||
var task = func!(timer.State);
|
||||
if (!task.IsCompleted)
|
||||
await task.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,7 +72,8 @@ public class TimerX : ITimer, ITimerx, IDisposable
|
||||
|
||||
/// <summary>调用中</summary>
|
||||
public Boolean Calling { get; internal set; }
|
||||
|
||||
/// <summary>可重入</summary>
|
||||
public Boolean Reentrant { get; set; } = false;
|
||||
/// <summary>平均耗时。毫秒</summary>
|
||||
public Int32 Cost { get; internal set; }
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.0" />
|
||||
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.728" />
|
||||
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.1" />
|
||||
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.821" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
|
||||
<!--<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET9Version)" />-->
|
||||
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<PluginVersion>10.11.4</PluginVersion>
|
||||
<ProPluginVersion>10.11.4</ProPluginVersion>
|
||||
<DefaultVersion>10.11.4</DefaultVersion>
|
||||
<PluginVersion>10.11.19</PluginVersion>
|
||||
<ProPluginVersion>10.11.19</ProPluginVersion>
|
||||
<DefaultVersion>10.11.19</DefaultVersion>
|
||||
<AuthenticationVersion>10.11.2</AuthenticationVersion>
|
||||
<SourceGeneratorVersion>10.11.2</SourceGeneratorVersion>
|
||||
<NET8Version>8.0.19</NET8Version>
|
||||
@@ -28,7 +28,8 @@
|
||||
<AnalysisModeStyle>None</AnalysisModeStyle>
|
||||
|
||||
<NoWarn>
|
||||
CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;CS8714;CS8619;CS8629;CS8765;CS8634;CS8621;CS8767;CS8633;CS8620;CS8610;CS8631;CS8605;CS8622;CS8613;NU5100;NU5104;NU1903;NU1902;CA1863;CA1812;CA1805;CA1515;CA1508;CA1819;CA1852;CA5394;CA1822;CA1815;CA1813;CA2000;CA5358;CA5384;CA5400;CA5401;CA1814;CA1835;CA5392;CA5350;CA2100;CA1848;CA1810;CA1513;CA5351;CA1510;CA1512;CA1823;NETSDK1206
|
||||
CS8603;CS8618;CS1591;CS8625;CS8602;CS8604;CS8600;CS8601;CS8714;CS8619;CS8629;CS8765;CS8634;CS8621;CS8767;CS8633;CS8620;CS8610;CS8631;CS8605;CS8622;CS8613;NU5100;NU5104;NU1903;NU1902;CA1863;CA1812;CA1805;CA1515;CA1508;CA1819;CA1852;CA5394;CA1822;CA1815;CA1813;CA2000;CA5358;CA5384;CA5400;CA5401;CA1814;CA1835;CA5392;CA5350;CA2100;CA1848;CA1810;CA1513;CA5351;CA1510;CA1512;CA1823;RCS1102;RCS1194;NETSDK1206
|
||||
|
||||
</NoWarn>
|
||||
<TargetFrameworks>net8.0;</TargetFrameworks>
|
||||
<LangVersion>13.0</LangVersion>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -82,13 +82,14 @@ public static class CSharpScriptEngineExtension
|
||||
{
|
||||
if (source.IsNullOrEmpty()) return null;
|
||||
var field = $"{CacheKey}-{source}";
|
||||
var exfield = $"{CacheKey}-Exception-{source}";
|
||||
var runScript = Instance.Get<T>(field);
|
||||
if (runScript == null)
|
||||
{
|
||||
lock (m_waiterLock)
|
||||
{
|
||||
runScript = Instance.Get<T>(field);
|
||||
if (runScript == null)
|
||||
var hasValue = Instance.TryGetValue<T>(field, out runScript);
|
||||
if (hasValue == false)
|
||||
{
|
||||
var src = source.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
var _using = new StringBuilder();
|
||||
@@ -111,8 +112,6 @@ public static class CSharpScriptEngineExtension
|
||||
}
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
// 动态加载并执行代码
|
||||
runScript = evaluator.With(eval => eval.IsAssemblyUnloadingEnabled = true).LoadCode<T>(
|
||||
$@"
|
||||
@@ -140,11 +139,22 @@ public static class CSharpScriptEngineExtension
|
||||
string exString = string.Format(CSScriptResource.CSScriptResource.Error1, typeof(T).FullName);
|
||||
throw new(exString);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//如果编译失败,应该不重复编译,避免oom
|
||||
Instance.Set<T>(field, null, TimeSpan.FromHours(1));
|
||||
Instance.Set(exfield, ex, TimeSpan.FromHours(1));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Instance.SetExpire(field, TimeSpan.FromHours(1));
|
||||
|
||||
Instance.SetExpire(exfield, TimeSpan.FromHours(1));
|
||||
if (runScript == null)
|
||||
{
|
||||
throw (Instance.Get<Exception>(exfield) ?? new Exception("compilation error"));
|
||||
}
|
||||
return runScript;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,30 +93,38 @@ public static class ExpressionEvaluatorExtension
|
||||
public static ReadWriteExpressions GetOrAddScript(string source)
|
||||
{
|
||||
var field = $"{CacheKey}-{source}";
|
||||
var exfield = $"{CacheKey}-Exception-{source}";
|
||||
var runScript = Instance.Get<ReadWriteExpressions>(field);
|
||||
if (runScript == null)
|
||||
{
|
||||
if (!source.Contains("return"))
|
||||
var hasValue = Instance.TryGetValue<ReadWriteExpressions>(field, out runScript);
|
||||
if (!hasValue)
|
||||
{
|
||||
source = $"return {source}";//只判断简单脚本中可省略return字符串
|
||||
}
|
||||
var src = source.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
var _using = new StringBuilder();
|
||||
var _body = new StringBuilder();
|
||||
src.ToList().ForEach(l =>
|
||||
{
|
||||
if (l.StartsWith("using "))
|
||||
|
||||
|
||||
if (!source.Contains("return"))
|
||||
{
|
||||
_using.AppendLine(l);
|
||||
source = $"return {source}";//只判断简单脚本中可省略return字符串
|
||||
}
|
||||
else
|
||||
var src = source.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
|
||||
var _using = new StringBuilder();
|
||||
var _body = new StringBuilder();
|
||||
src.ToList().ForEach(l =>
|
||||
{
|
||||
_body.AppendLine(l);
|
||||
}
|
||||
});
|
||||
// 动态加载并执行代码
|
||||
runScript = CSScript.Evaluator.With(eval => eval.IsAssemblyUnloadingEnabled = true).LoadCode<ReadWriteExpressions>(
|
||||
$@"
|
||||
if (l.StartsWith("using "))
|
||||
{
|
||||
_using.AppendLine(l);
|
||||
}
|
||||
else
|
||||
{
|
||||
_body.AppendLine(l);
|
||||
}
|
||||
});
|
||||
// 动态加载并执行代码
|
||||
try
|
||||
{
|
||||
runScript = CSScript.Evaluator.With(eval => eval.IsAssemblyUnloadingEnabled = true).LoadCode<ReadWriteExpressions>(
|
||||
$@"
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
@@ -137,9 +145,26 @@ public static class ExpressionEvaluatorExtension
|
||||
}}
|
||||
}}
|
||||
");
|
||||
Instance.Set(field, runScript);
|
||||
Instance.Set(field, runScript);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//如果编译失败,应该不重复编译,避免oom
|
||||
Instance.Set<ReadWriteExpressions>(field, null, TimeSpan.FromHours(1));
|
||||
Instance.Set(exfield, ex, TimeSpan.FromHours(1));
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Instance.SetExpire(field, TimeSpan.FromHours(1));
|
||||
Instance.SetExpire(exfield, TimeSpan.FromHours(1));
|
||||
if (runScript == null)
|
||||
{
|
||||
throw (Instance.Get<Exception>(exfield) ?? new Exception("compilation error"));
|
||||
}
|
||||
return runScript;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,9 @@ public static class ChannelOptionsExtensions
|
||||
for (int i = 0; i < funcs.Count; i++)
|
||||
{
|
||||
var func = funcs[i];
|
||||
await func.Invoke(clientChannel, e, i == funcs.Count - 1).ConfigureAwait(false);
|
||||
var task = func.Invoke(clientChannel, e, i == funcs.Count - 1);
|
||||
if (!task.IsCompleted)
|
||||
await task.ConfigureAwait(false);
|
||||
if (e.Handled)
|
||||
{
|
||||
break;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class OtherChannel : SetupConfigObject, IClientChannel
|
||||
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (_deviceDataHandleAdapter == null && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (_deviceDataHandleAdapter != ReadOnlyDataHandlingAdapter && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
_deviceDataHandleAdapter = handleAdapter;
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
{
|
||||
if (!socket.Id.StartsWith("ID="))
|
||||
{
|
||||
var id = DtuIdHex ? $"ID={e.Reader.ToHexString()}" : $"ID={e.Reader.ToString()}";
|
||||
var id = DtuIdHex ? $"ID={e.Reader.ToHexString()}" : $"ID={e.Reader.TotalSequence.ToString(Encoding.UTF8)}";
|
||||
if (tcpServiceChannel.TryGetClient(id, out var oldClient))
|
||||
{
|
||||
try
|
||||
@@ -80,6 +80,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
}
|
||||
await socket.ResetIdAsync(id, client.ClosedToken).ConfigureAwait(false);
|
||||
client.Logger?.Info(string.Format(AppResource.DtuConnected, id));
|
||||
e.Reader.Advance((int)e.Reader.BytesRemaining);
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
@@ -100,7 +101,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
if (HeartbeatByte.Span.SequenceEqual(e.Reader.TotalSequence.Slice(0, len).First.Span))
|
||||
if (HeartbeatByte.Span.SequenceEqual(e.Reader.TotalSequence.Slice(0, (int)Math.Min(len, e.Reader.BytesRemaining + e.Reader.BytesRead)).First.Span))
|
||||
{
|
||||
if (DateTimeOffset.Now - socket.LastSentTime < TimeSpan.FromMilliseconds(200))
|
||||
{
|
||||
@@ -108,6 +109,7 @@ public class DtuPlugin : PluginBase, ITcpReceivingPlugin
|
||||
}
|
||||
//回应心跳包
|
||||
await socket.SendAsync(HeartbeatByte, socket.ClosedToken).ConfigureAwait(false);
|
||||
e.Reader.Advance((int)Math.Min(len, e.Reader.BytesRemaining));
|
||||
e.Handled = true;
|
||||
if (socket.Logger?.LogLevel <= LogLevel.Trace)
|
||||
socket.Logger?.Trace($"{socket}- Heartbeat");
|
||||
|
||||
@@ -181,8 +181,9 @@ internal sealed class HeartbeatAndReceivePlugin : PluginBase, ITcpConnectedPlugi
|
||||
var len = HeartbeatByte.Length;
|
||||
if (len > 0)
|
||||
{
|
||||
if (HeartbeatByte.Span.SequenceEqual(e.Reader.TotalSequence.Slice(0, len).First.Span))
|
||||
if (HeartbeatByte.Span.SequenceEqual(e.Reader.TotalSequence.Slice(0, (int)Math.Min(len, e.Reader.BytesRemaining + e.Reader.BytesRead)).First.Span))
|
||||
{
|
||||
e.Reader.Advance((int)Math.Min(len, e.Reader.BytesRemaining));
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (_deviceDataHandleAdapter == null && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (_deviceDataHandleAdapter != ProtectedDataHandlingAdapter && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
_deviceDataHandleAdapter = handleAdapter;
|
||||
}
|
||||
@@ -192,12 +192,17 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnSerialReceived(ReceivedDataEventArgs e)
|
||||
{
|
||||
await base.OnSerialReceived(e).ConfigureAwait(false);
|
||||
var receivedTask = base.OnSerialReceived(e);
|
||||
if (!receivedTask.IsCompleted)
|
||||
await receivedTask.ConfigureAwait(false);
|
||||
|
||||
if (e.Handled)
|
||||
return;
|
||||
|
||||
await this.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
||||
var channelReceivedTask = this.OnChannelReceivedEvent(e, ChannelReceived);
|
||||
if (!channelReceivedTask.IsCompleted)
|
||||
await channelReceivedTask.ConfigureAwait(false);
|
||||
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
protected override void SafetyDispose(bool disposing)
|
||||
|
||||
@@ -33,7 +33,7 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (_deviceDataHandleAdapter == null && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
_deviceDataHandleAdapter = handleAdapter;
|
||||
}
|
||||
@@ -179,12 +179,16 @@ public class TcpClientChannel : TcpClient, IClientChannel
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
||||
{
|
||||
await base.OnTcpReceived(e).ConfigureAwait(false);
|
||||
var receivedTask = base.OnTcpReceived(e);
|
||||
if (!receivedTask.IsCompleted)
|
||||
await receivedTask.ConfigureAwait(false);
|
||||
|
||||
if (e.Handled)
|
||||
return;
|
||||
|
||||
await this.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
||||
var channelReceivedTask = this.OnChannelReceivedEvent(e, ChannelReceived);
|
||||
if (!channelReceivedTask.IsCompleted)
|
||||
await channelReceivedTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -203,8 +203,8 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
||||
data.ResetSign(MinSign, MaxSign);
|
||||
return data;
|
||||
}
|
||||
public int MaxSign { get; private set; } = 0;
|
||||
public int MinSign { get; private set; } = ushort.MaxValue;
|
||||
public int MinSign { get; private set; } = 0;
|
||||
public int MaxSign { get; private set; } = ushort.MaxValue;
|
||||
public void ResetSign(int minSign = 0, int maxSign = ushort.MaxValue)
|
||||
{
|
||||
MinSign = minSign;
|
||||
@@ -241,12 +241,17 @@ public class TcpServiceChannel<TClient> : TcpServiceChannelBase<TClient>, IChann
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpReceived(TClient socketClient, ReceivedDataEventArgs e)
|
||||
{
|
||||
await base.OnTcpReceived(socketClient, e).ConfigureAwait(false);
|
||||
var receivedTask = base.OnTcpReceived(socketClient, e);
|
||||
if (!receivedTask.IsCompleted)
|
||||
await receivedTask.ConfigureAwait(false);
|
||||
|
||||
if (e.Handled)
|
||||
return;
|
||||
|
||||
await socketClient.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
||||
var channelReceivedTask = socketClient.OnChannelReceivedEvent(e, ChannelReceived);
|
||||
if (!channelReceivedTask.IsCompleted)
|
||||
await channelReceivedTask.ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (_deviceDataHandleAdapter == null && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
_deviceDataHandleAdapter = handleAdapter;
|
||||
}
|
||||
@@ -145,11 +145,15 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnTcpReceived(ReceivedDataEventArgs e)
|
||||
{
|
||||
await base.OnTcpReceived(e).ConfigureAwait(false);
|
||||
var receivedTask = base.OnTcpReceived(e);
|
||||
if (!receivedTask.IsCompleted)
|
||||
await receivedTask.ConfigureAwait(false);
|
||||
|
||||
if (e.Handled)
|
||||
return;
|
||||
|
||||
await this.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
||||
var channelReceivedTask = this.OnChannelReceivedEvent(e, ChannelReceived);
|
||||
if (!channelReceivedTask.IsCompleted)
|
||||
await channelReceivedTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
||||
private IDeviceDataHandleAdapter _deviceDataHandleAdapter;
|
||||
public void SetDataHandlingAdapterLogger(ILog log)
|
||||
{
|
||||
if (_deviceDataHandleAdapter == null && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
if (_deviceDataHandleAdapter != DataHandlingAdapter && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
|
||||
{
|
||||
_deviceDataHandleAdapter = handleAdapter;
|
||||
}
|
||||
@@ -196,12 +196,17 @@ public class UdpSessionChannel : UdpSession, IClientChannel
|
||||
/// <inheritdoc/>
|
||||
protected override async Task OnUdpReceived(UdpReceivedDataEventArgs e)
|
||||
{
|
||||
await base.OnUdpReceived(e).ConfigureAwait(false);
|
||||
var receivedTask = base.OnUdpReceived(e);
|
||||
if (!receivedTask.IsCompleted)
|
||||
await receivedTask.ConfigureAwait(false);
|
||||
|
||||
if (e.Handled)
|
||||
return;
|
||||
|
||||
await this.OnChannelReceivedEvent(e, ChannelReceived).ConfigureAwait(false);
|
||||
var channelReceivedTask = this.OnChannelReceivedEvent(e, ChannelReceived);
|
||||
if (!channelReceivedTask.IsCompleted)
|
||||
await channelReceivedTask.ConfigureAwait(false);
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.NewLife.Collections;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <summary>
|
||||
@@ -17,7 +19,19 @@ namespace ThingsGateway.Foundation;
|
||||
/// </summary>
|
||||
public class DeviceSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingAdapter<TRequest>, IDeviceDataHandleAdapter where TRequest : MessageBase, new()
|
||||
{
|
||||
public new ILog Logger { get; set; }
|
||||
private ILog logger;
|
||||
|
||||
public new ILog Logger
|
||||
{
|
||||
get => logger ?? base.Logger;
|
||||
set
|
||||
{
|
||||
if (value != logger && value != null)
|
||||
{
|
||||
logger = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc cref="DeviceSingleStreamDataHandleAdapter{TRequest}"/>
|
||||
@@ -45,6 +59,13 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingA
|
||||
/// <inheritdoc />
|
||||
public void SetRequest(ISendMessage sendMessage)
|
||||
{
|
||||
if (IsSingleThread)
|
||||
{
|
||||
if (Request != null)
|
||||
{
|
||||
_requestPool.Return(Request);
|
||||
}
|
||||
}
|
||||
var request = GetInstance();
|
||||
request.Sign = sendMessage.Sign;
|
||||
request.SendInfo(sendMessage);
|
||||
@@ -144,6 +165,7 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingA
|
||||
}
|
||||
}
|
||||
|
||||
private static ObjectPool<TRequest> _requestPool { get; } = new ObjectPool<TRequest>();
|
||||
|
||||
/// <summary>
|
||||
/// 获取泛型实例。
|
||||
@@ -151,7 +173,17 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingA
|
||||
/// <returns></returns>
|
||||
protected virtual TRequest GetInstance()
|
||||
{
|
||||
return new TRequest() { OperCode = -1, Sign = -1 };
|
||||
if (IsSingleThread)
|
||||
{
|
||||
var request = _requestPool.Get();
|
||||
request.OperCode = -1;
|
||||
request.Sign = -1;
|
||||
return request;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new TRequest() { OperCode = -1, Sign = -1 };
|
||||
}
|
||||
}
|
||||
|
||||
public override void SendInput<TWriter>(ref TWriter writer, in ReadOnlyMemory<byte> memory)
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.NewLife.Collections;
|
||||
|
||||
namespace ThingsGateway.Foundation;
|
||||
|
||||
/// <summary>
|
||||
@@ -18,7 +20,19 @@ namespace ThingsGateway.Foundation;
|
||||
/// </summary>
|
||||
public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter, IDeviceDataHandleAdapter where TRequest : MessageBase, new()
|
||||
{
|
||||
public new ILog Logger { get; set; }
|
||||
private ILog logger;
|
||||
|
||||
public new ILog Logger
|
||||
{
|
||||
get => logger ?? base.Logger;
|
||||
set
|
||||
{
|
||||
if (value != logger && value != null)
|
||||
{
|
||||
logger = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool CanSendRequestInfo => true;
|
||||
@@ -38,6 +52,13 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter, IDev
|
||||
/// <inheritdoc />
|
||||
public void SetRequest(ISendMessage sendMessage)
|
||||
{
|
||||
if (IsSingleThread)
|
||||
{
|
||||
if (Request != null)
|
||||
{
|
||||
_requestPool.Return(Request);
|
||||
}
|
||||
}
|
||||
var request = GetInstance();
|
||||
request.Sign = sendMessage.Sign;
|
||||
request.SendInfo(sendMessage);
|
||||
@@ -50,13 +71,26 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter, IDev
|
||||
return Owner.ToString();
|
||||
}
|
||||
|
||||
private static ObjectPool<TRequest> _requestPool { get; } = new ObjectPool<TRequest>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取泛型实例。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual TRequest GetInstance()
|
||||
{
|
||||
return new TRequest() { OperCode = -1, Sign = -1 };
|
||||
if (IsSingleThread)
|
||||
{
|
||||
var request = _requestPool.Get();
|
||||
request.OperCode = -1;
|
||||
request.Sign = -1;
|
||||
return request;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new TRequest() { OperCode = -1, Sign = -1 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -348,11 +348,19 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
|
||||
if (channel is IDtuUdpSessionChannel udpSession)
|
||||
{
|
||||
await udpSession.SendAsync(endPoint, sendMessage, token).ConfigureAwait(false);
|
||||
var sendTask = udpSession.SendAsync(endPoint, sendMessage, token);
|
||||
if (!sendTask.IsCompleted)
|
||||
{
|
||||
await sendTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await channel.SendAsync(sendMessage, token).ConfigureAwait(false);
|
||||
var sendTask = channel.SendAsync(sendMessage, token);
|
||||
if (!sendTask.IsCompleted)
|
||||
{
|
||||
await sendTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
return OperResult.Success;
|
||||
@@ -363,13 +371,17 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
}
|
||||
}
|
||||
|
||||
private Task BefortSendAsync(IClientChannel channel, CancellationToken token)
|
||||
private Task BeforeSendAsync(IClientChannel channel, CancellationToken token)
|
||||
{
|
||||
SetDataAdapter(channel);
|
||||
|
||||
return ConnectAsync(token);
|
||||
|
||||
|
||||
if (AutoConnect && Channel != null && Channel?.Online != true)
|
||||
{
|
||||
return ConnectAsync(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private WaitLock connectWaitLock = new(nameof(DeviceBase));
|
||||
@@ -410,7 +422,11 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
|
||||
try
|
||||
{
|
||||
await BefortSendAsync(channelResult.Content, cancellationToken).ConfigureAwait(false);
|
||||
var beforeSendTask = BeforeSendAsync(channelResult.Content, cancellationToken);
|
||||
if (!beforeSendTask.IsCompleted)
|
||||
{
|
||||
await beforeSendTask.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await waitLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
channelResult.Content.SetDataHandlingAdapterLogger(Logger);
|
||||
@@ -508,8 +524,18 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await SendThenReturnMessageAsync(sendMessage, channel, cancellationToken).ConfigureAwait(false);
|
||||
return new OperResult<ReadOnlyMemory<byte>>(result) { Content = result.Content };
|
||||
var sendTask = SendThenReturnMessageAsync(sendMessage, channel, cancellationToken);
|
||||
if (!sendTask.IsCompleted)
|
||||
{
|
||||
var result = await sendTask.ConfigureAwait(false);
|
||||
return new OperResult<ReadOnlyMemory<byte>>(result) { Content = result.Content };
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = sendTask.Result;
|
||||
return new OperResult<ReadOnlyMemory<byte>>(result) { Content = result.Content };
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -541,8 +567,11 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
WaitLock? waitLock = null;
|
||||
try
|
||||
{
|
||||
await BefortSendAsync(clientChannel, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var beforeSendTask = BeforeSendAsync(clientChannel, cancellationToken);
|
||||
if (!beforeSendTask.IsCompleted)
|
||||
{
|
||||
await beforeSendTask.ConfigureAwait(false);
|
||||
}
|
||||
var dtuId = this is IDtu dtu1 ? dtu1.DtuId : null;
|
||||
waitLock = GetWaitLock(clientChannel, dtuId);
|
||||
|
||||
@@ -559,7 +588,17 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
return new MessageBase(new OperationCanceledException());
|
||||
|
||||
var sendOperResult = await SendAsync(command, clientChannel, endPoint, cancellationToken).ConfigureAwait(false);
|
||||
OperResult sendOperResult = default;
|
||||
var sendTask = SendAsync(command, clientChannel, endPoint, cancellationToken);
|
||||
if (!sendTask.IsCompleted)
|
||||
{
|
||||
sendOperResult = await sendTask.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
sendOperResult = sendTask.Result;
|
||||
}
|
||||
|
||||
if (!sendOperResult.IsSuccess)
|
||||
return new MessageBase(sendOperResult);
|
||||
|
||||
@@ -567,7 +606,11 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
|
||||
try
|
||||
{
|
||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ctsTime.Token, Channel.ClosedToken);
|
||||
await waitData.WaitAsync(cts.Token).ConfigureAwait(false);
|
||||
var waitDataTask = waitData.WaitAsync(cts.Token);
|
||||
if (!waitDataTask.IsCompleted)
|
||||
{
|
||||
await waitDataTask.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
|
||||
@@ -385,39 +385,27 @@ public static class ByteBlockExtension
|
||||
}
|
||||
public static void WriteBackValue<TWriter, T>(ref TWriter writer, T value, EndianType endianType, int pos)
|
||||
where T : unmanaged
|
||||
where TWriter : IBytesWriter
|
||||
where TWriter : IByteBlockWriter
|
||||
{
|
||||
if (writer.SupportsRewind)
|
||||
{
|
||||
var nowPos = (int)writer.WrittenCount - pos;
|
||||
writer.Advance(-nowPos);
|
||||
var size = Unsafe.SizeOf<T>();
|
||||
var span = writer.GetSpan(size);
|
||||
TouchSocketBitConverter.GetBitConverter(endianType).WriteBytes(span, value);
|
||||
writer.Advance(nowPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Writer version mismatch or does not support rewind.");
|
||||
}
|
||||
var nowPos = (int)writer.WrittenCount - pos;
|
||||
writer.Advance(-nowPos);
|
||||
var size = Unsafe.SizeOf<T>();
|
||||
var span = writer.GetSpan(size);
|
||||
TouchSocketBitConverter.GetBitConverter(endianType).WriteBytes(span, value);
|
||||
writer.Advance(nowPos);
|
||||
|
||||
}
|
||||
public static void WriteBackValue<TWriter, T>(ref TWriter writer, T value, EndianType endianType, long pos)
|
||||
where T : unmanaged
|
||||
where TWriter : IBytesWriter
|
||||
where TWriter : IByteBlockWriter
|
||||
{
|
||||
if (writer.SupportsRewind)
|
||||
{
|
||||
var nowPos = (int)(writer.WrittenCount - pos);
|
||||
writer.Advance(-nowPos);
|
||||
var size = Unsafe.SizeOf<T>();
|
||||
var span = writer.GetSpan(size);
|
||||
TouchSocketBitConverter.GetBitConverter(endianType).WriteBytes(span, value);
|
||||
writer.Advance(nowPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Writer version mismatch or does not support rewind.");
|
||||
}
|
||||
var nowPos = (int)(writer.WrittenCount - pos);
|
||||
writer.Advance(-nowPos);
|
||||
var size = Unsafe.SizeOf<T>();
|
||||
var span = writer.GetSpan(size);
|
||||
TouchSocketBitConverter.GetBitConverter(endianType).WriteBytes(span, value);
|
||||
writer.Advance(nowPos);
|
||||
|
||||
}
|
||||
|
||||
public static string ReadNormalString<TReader>(ref TReader reader, int length)
|
||||
@@ -429,20 +417,32 @@ where TWriter : IBytesWriter
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
public static void WriteBackNormalString<TWriter>(ref TWriter writer, string value, Encoding encoding, int pos)
|
||||
where TWriter : IBytesWriter
|
||||
where TWriter : IByteBlockWriter
|
||||
{
|
||||
if (writer.SupportsRewind)
|
||||
var nowPos = (int)(writer.WrittenCount - pos);
|
||||
writer.Advance(-nowPos);
|
||||
WriterExtension.WriteNormalString(ref writer, value, encoding);
|
||||
writer.Advance(nowPos);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static int WriteNormalString(this Span<byte> span, string value, Encoding encoding)
|
||||
{
|
||||
var maxSize = encoding.GetMaxByteCount(value.Length);
|
||||
var chars = value.AsSpan();
|
||||
|
||||
unsafe
|
||||
{
|
||||
var nowPos = (int)(writer.WrittenCount - pos);
|
||||
writer.Advance(-nowPos);
|
||||
WriterExtension.WriteNormalString(ref writer, value, encoding);
|
||||
writer.Advance(nowPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Writer version mismatch or does not support rewind.");
|
||||
fixed (char* p = &chars[0])
|
||||
{
|
||||
fixed (byte* p1 = &span[0])
|
||||
{
|
||||
var len = Encoding.UTF8.GetBytes(p, chars.Length, p1, maxSize);
|
||||
return len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(NET9Version)" />
|
||||
<PackageReference Include="TouchSocket" Version="4.0.0-beta.1" />
|
||||
<PackageReference Include="TouchSocket.SerialPorts" Version="4.0.0-beta.1" />
|
||||
<PackageReference Include="TouchSocket" Version="4.0.0-beta.5" />
|
||||
<PackageReference Include="TouchSocket.SerialPorts" Version="4.0.0-beta.5" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -28,7 +28,7 @@ public static class JTokenUtil
|
||||
{
|
||||
try
|
||||
{
|
||||
if (item.IsNullOrWhiteSpace())
|
||||
if (item.IsNullOrEmpty())
|
||||
return JValue.CreateNull();
|
||||
|
||||
if (bool.TryParse(item, out bool parseBool))
|
||||
|
||||
@@ -28,7 +28,7 @@ public class AsyncReadWriteLock : IAsyncDisposable
|
||||
/// <summary>
|
||||
/// 获取读锁,支持多个线程并发读取,但写入时会阻止所有读取。
|
||||
/// </summary>
|
||||
public async Task<CancellationToken> ReaderLockAsync(CancellationToken cancellationToken)
|
||||
public async ValueTask<CancellationToken> ReaderLockAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
if (Interlocked.Read(ref _writerCount) > 0)
|
||||
@@ -51,7 +51,7 @@ public class AsyncReadWriteLock : IAsyncDisposable
|
||||
/// <summary>
|
||||
/// 获取写锁,阻止所有读取。
|
||||
/// </summary>
|
||||
public async Task<IDisposable> WriterLockAsync(CancellationToken cancellationToken)
|
||||
public async ValueTask<IDisposable> WriterLockAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
if (Interlocked.Increment(ref _writerCount) == 1)
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class CronScheduledTask : DisposeBase, IScheduledTask
|
||||
{
|
||||
private int _interval10MS = 10;
|
||||
private int next = 10;
|
||||
private string _interval;
|
||||
private readonly Func<object?, CancellationToken, Task> _taskFunc;
|
||||
private readonly Func<object?, CancellationToken, ValueTask> _valueTaskFunc;
|
||||
@@ -62,9 +62,9 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
||||
_timer?.Dispose();
|
||||
if (Check()) return;
|
||||
if (_taskAction != null)
|
||||
_timer = new TimerX(TimerCallback, _state, _interval, nameof(IScheduledTask)) { Async = true };
|
||||
_timer = new TimerX(TimerCallback, _state, _interval, nameof(CronScheduledTask)) { Async = true, Reentrant = false };
|
||||
else if (_taskFunc != null || _valueTaskFunc != null)
|
||||
_timer = new TimerX(TimerCallbackAsync, _state, _interval, nameof(IScheduledTask)) { Async = true };
|
||||
_timer = new TimerX(TimerCallbackAsync, _state, _interval, nameof(CronScheduledTask)) { Async = true, Reentrant = false };
|
||||
}
|
||||
|
||||
private async ValueTask TimerCallbackAsync(object? state)
|
||||
@@ -107,7 +107,7 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
||||
{
|
||||
if (!Check())
|
||||
{
|
||||
SetNext(_interval10MS);
|
||||
SetNext(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,7 +149,7 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
|
||||
{
|
||||
if (!Check())
|
||||
{
|
||||
SetNext(_interval10MS);
|
||||
SetNext(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntIntervalTask
|
||||
{
|
||||
private int _interval10MS = 10;
|
||||
private int next = 10;
|
||||
public int IntervalMS { get; }
|
||||
private readonly Func<object?, CancellationToken, Task> _taskFunc;
|
||||
private readonly Func<object?, CancellationToken, ValueTask> _valueTaskFunc;
|
||||
@@ -48,7 +48,7 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
||||
{
|
||||
_timer?.Dispose();
|
||||
if (!Check())
|
||||
_timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, nameof(IScheduledTask)) { Async = true };
|
||||
_timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, nameof(ScheduledAsyncTask)) { Async = true, Reentrant = false };
|
||||
}
|
||||
|
||||
private async ValueTask DoAsync(object? state)
|
||||
@@ -70,7 +70,6 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
||||
// 减少一个触发次数
|
||||
Interlocked.Decrement(ref _pendingTriggers);
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (_taskFunc != null)
|
||||
@@ -92,9 +91,9 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
|
||||
|
||||
if (Interlocked.Exchange(ref _pendingTriggers, 0) >= 1)
|
||||
{
|
||||
if (!Check())
|
||||
if (!Check() && IntervalMS > 8)
|
||||
{
|
||||
SetNext(_interval10MS);
|
||||
SetNext(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntIntervalTask
|
||||
{
|
||||
private int _interval10MS = 10;
|
||||
private int next = 10;
|
||||
public int IntervalMS { get; }
|
||||
private readonly Action<object?, CancellationToken> _taskAction;
|
||||
private readonly CancellationToken _token;
|
||||
@@ -40,7 +40,7 @@ public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntInter
|
||||
{
|
||||
_timer?.Dispose();
|
||||
if (!Check())
|
||||
_timer = new TimerX(TimerCallback, _state, IntervalMS, IntervalMS, nameof(IScheduledTask)) { Async = true };
|
||||
_timer = new TimerX(TimerCallback, _state, IntervalMS, IntervalMS, nameof(ScheduledSyncTask)) { Async = true, Reentrant = false };
|
||||
}
|
||||
|
||||
private void TimerCallback(object? state)
|
||||
@@ -78,9 +78,9 @@ public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntInter
|
||||
|
||||
if (Interlocked.Exchange(ref _pendingTriggers, 0) >= 1)
|
||||
{
|
||||
if (!Check())
|
||||
if (!Check() && IntervalMS > 8)
|
||||
{
|
||||
SetNext(_interval10MS);
|
||||
SetNext(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
[RequestAudit]
|
||||
[ApiController]
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
[TouchSocket.WebApi.Router("/miniapi/[api]/[action]")]
|
||||
[TouchSocket.WebApi.Router("/miniapi/control/[action]")]
|
||||
[TouchSocket.WebApi.EnableCors("cors")]
|
||||
public class ControlController : ControllerBase, IRpcServer
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
[ApiController]
|
||||
[RolePermission]
|
||||
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||
[TouchSocket.WebApi.Router("/miniapi/[api]/[action]")]
|
||||
[TouchSocket.WebApi.Router("/miniapi/runtimeinfo/[action]")]
|
||||
[TouchSocket.WebApi.EnableCors("cors")]
|
||||
public class RuntimeInfoController : ControllerBase, IRpcServer
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
[Route("api/[controller]/[action]")]
|
||||
[AllowAnonymous]
|
||||
[ApiController]
|
||||
[TouchSocket.WebApi.Router("/miniapi/[api]/[action]")]
|
||||
[TouchSocket.WebApi.Router("/miniapi/test/[action]")]
|
||||
[TouchSocket.WebApi.EnableCors("cors")]
|
||||
public class TestController : ControllerBase, IRpcServer
|
||||
{
|
||||
|
||||
@@ -26,8 +26,21 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
protected abstract BusinessPropertyWithCache _businessPropertyWithCache { get; }
|
||||
|
||||
#if !Management
|
||||
|
||||
protected override Task DisposeAsync(bool disposing)
|
||||
{
|
||||
// 清空内存队列
|
||||
_memoryPluginEventDataModelQueue.Clear();
|
||||
_memoryAlarmModelQueue.Clear();
|
||||
_memoryDevModelQueue.Clear();
|
||||
_memoryVarModelQueue.Clear();
|
||||
_memoryVarModelsQueue.Clear();
|
||||
return base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
#region 条件
|
||||
|
||||
protected abstract bool PluginEventDataModelEnable { get; }
|
||||
protected abstract bool AlarmModelEnable { get; }
|
||||
protected abstract bool DevModelEnable { get; }
|
||||
protected abstract bool VarModelEnable { get; }
|
||||
@@ -36,6 +49,10 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
if (AlarmModelEnable)
|
||||
DBCacheAlarm = LocalDBCacheAlarmModel();
|
||||
|
||||
if (PluginEventDataModelEnable)
|
||||
DBCachePluginEventData = LocalDBCachePluginEventDataModel();
|
||||
|
||||
|
||||
if (DevModelEnable)
|
||||
DBCacheDev = LocalDBCacheDevModel();
|
||||
|
||||
@@ -68,7 +85,10 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
{
|
||||
await UpdateAlarmModelMemory(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (PluginEventDataModelEnable)
|
||||
{
|
||||
await UpdatePluginEventDataModelMemory(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
if (VarModelEnable)
|
||||
{
|
||||
await UpdateVarModelCache(cancellationToken).ConfigureAwait(false);
|
||||
@@ -83,6 +103,12 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
if (AlarmModelEnable)
|
||||
{
|
||||
await UpdateAlarmModelCache(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
if (PluginEventDataModelEnable)
|
||||
{
|
||||
await UpdatePluginEventDataModelCache(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@@ -90,10 +116,12 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
#region alarm
|
||||
|
||||
protected ConcurrentQueue<CacheDBItem<AlarmVariable>> _memoryAlarmModelQueue = new();
|
||||
protected ConcurrentQueue<CacheDBItem<PluginEventData>> _memoryPluginEventDataModelQueue = new();
|
||||
|
||||
private volatile bool LocalDBCacheAlarmModelInited;
|
||||
private CacheDB DBCacheAlarm;
|
||||
|
||||
private volatile bool LocalDBCachePluginEventDataModelInited;
|
||||
private CacheDB DBCachePluginEventData;
|
||||
/// <summary>
|
||||
/// 入缓存
|
||||
/// </summary>
|
||||
@@ -142,6 +170,55 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 入缓存
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
protected virtual void AddCache(List<CacheDBItem<PluginEventData>> data)
|
||||
{
|
||||
if (_businessPropertyWithCache.CacheEnable && data?.Count > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
LogMessage?.LogInformation($"Add {typeof(PluginEventData).Name} data to file cache, count {data.Count}");
|
||||
foreach (var item in data)
|
||||
{
|
||||
item.Id = CommonUtils.GetSingleId();
|
||||
}
|
||||
var dir = CacheDBUtil.GetCacheFilePath(CurrentDevice.Name.ToString());
|
||||
var fileStart = CacheDBUtil.GetFileName($"{CurrentDevice.PluginName}_{typeof(PluginEventData).FullName}_{nameof(PluginEventData)}");
|
||||
var fullName = dir.CombinePathWithOs($"{fileStart}{CacheDBUtil.EX}");
|
||||
|
||||
lock (cacheLock)
|
||||
{
|
||||
bool s = false;
|
||||
while (!s)
|
||||
{
|
||||
s = CacheDBUtil.DeleteCache(_businessPropertyWithCache.CacheFileMaxLength, fullName);
|
||||
}
|
||||
using var cache = LocalDBCachePluginEventDataModel();
|
||||
cache.DBProvider.Fastest<CacheDBItem<PluginEventData>>().PageSize(50000).BulkCopy(data);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
using var cache = LocalDBCachePluginEventDataModel();
|
||||
lock (cache.CacheDBOption)
|
||||
{
|
||||
cache.DBProvider.Fastest<CacheDBItem<PluginEventData>>().PageSize(50000).BulkCopy(data);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, "Add cache fail");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加队列,超限后会入缓存
|
||||
/// </summary>
|
||||
@@ -183,6 +260,48 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加队列,超限后会入缓存
|
||||
/// </summary>
|
||||
/// <param name="data"></param>
|
||||
protected virtual void AddQueuePluginDataModel(CacheDBItem<PluginEventData> data)
|
||||
{
|
||||
if (_businessPropertyWithCache.CacheEnable)
|
||||
{
|
||||
//检测队列长度,超限存入缓存数据库
|
||||
if (_memoryPluginEventDataModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
|
||||
{
|
||||
List<CacheDBItem<PluginEventData>> list = null;
|
||||
lock (_memoryPluginEventDataModelQueue)
|
||||
{
|
||||
if (_memoryPluginEventDataModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
|
||||
{
|
||||
list = _memoryPluginEventDataModelQueue.ToListWithDequeue();
|
||||
}
|
||||
}
|
||||
AddCache(list);
|
||||
}
|
||||
}
|
||||
if (_memoryPluginEventDataModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
|
||||
{
|
||||
lock (_memoryPluginEventDataModelQueue)
|
||||
{
|
||||
if (_memoryPluginEventDataModelQueue.Count > _businessPropertyWithCache.QueueMaxCount)
|
||||
{
|
||||
LogMessage?.LogWarning($"{typeof(PluginEventData).Name} Queue exceeds limit, clear old data. If it doesn't work as expected, increase {_businessPropertyWithCache.QueueMaxCount} or Enable cache");
|
||||
_memoryPluginEventDataModelQueue.Clear();
|
||||
_memoryPluginEventDataModelQueue.Enqueue(data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_memoryPluginEventDataModelQueue.Enqueue(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取缓存对象,注意每次获取的对象可能不一样,如顺序操作,需固定引用
|
||||
/// </summary>
|
||||
@@ -197,6 +316,20 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
return cacheDb;
|
||||
}
|
||||
/// <summary>
|
||||
/// 获取缓存对象,注意每次获取的对象可能不一样,如顺序操作,需固定引用
|
||||
/// </summary>
|
||||
protected virtual CacheDB LocalDBCachePluginEventDataModel()
|
||||
{
|
||||
var cacheDb = CacheDBUtil.GetCache(typeof(CacheDBItem<PluginEventData>), CurrentDevice.Name.ToString(), $"{CurrentDevice.PluginName}_{typeof(PluginEventData).Name}");
|
||||
|
||||
if (!LocalDBCachePluginEventDataModelInited)
|
||||
{
|
||||
cacheDb.InitDb();
|
||||
LocalDBCachePluginEventDataModelInited = true;
|
||||
}
|
||||
return cacheDb;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 需实现上传到通道
|
||||
@@ -206,6 +339,16 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
/// <returns></returns>
|
||||
protected abstract ValueTask<OperResult> UpdateAlarmModel(List<CacheDBItem<AlarmVariable>> item, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 需实现上传到通道
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
protected abstract ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken);
|
||||
|
||||
|
||||
protected async Task UpdateAlarmModelCache(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_businessPropertyWithCache.CacheEnable)
|
||||
@@ -262,10 +405,69 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion //成功上传时,补上传缓存数据
|
||||
}
|
||||
}
|
||||
protected async Task UpdatePluginEventDataModelCache(CancellationToken cancellationToken)
|
||||
{
|
||||
if (_businessPropertyWithCache.CacheEnable)
|
||||
{
|
||||
#region //成功上传时,补上传缓存数据
|
||||
|
||||
if (IsConnected())
|
||||
{
|
||||
try
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
//循环获取,固定读最大行数量,执行完成需删除行
|
||||
var varList = await DBCachePluginEventData.DBProvider.Queryable<CacheDBItem<PluginEventData>>().Take(_businessPropertyWithCache.SplitSize).ToListAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (varList.Count != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var result = await UpdatePluginEventDataModel(varList, cancellationToken).ConfigureAwait(false);
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
//删除缓存
|
||||
await DBCachePluginEventData.DBProvider.Deleteable<CacheDBItem<PluginEventData>>(varList).ExecuteCommandAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion //成功上传时,补上传缓存数据
|
||||
}
|
||||
}
|
||||
protected async Task UpdateAlarmModelMemory(CancellationToken cancellationToken)
|
||||
{
|
||||
#region //上传设备内存队列中的数据
|
||||
@@ -307,6 +509,47 @@ public abstract class BusinessBaseWithCache : BusinessBase
|
||||
|
||||
#endregion //上传设备内存队列中的数据
|
||||
}
|
||||
protected async Task UpdatePluginEventDataModelMemory(CancellationToken cancellationToken)
|
||||
{
|
||||
#region //上传设备内存队列中的数据
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var list = _memoryPluginEventDataModelQueue.ToListWithDequeue().ChunkBetter(_businessPropertyWithCache.SplitSize);
|
||||
foreach (var item in list)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
var result = await UpdatePluginEventDataModel(item, cancellationToken).ConfigureAwait(false);
|
||||
if (!result.IsSuccess)
|
||||
{
|
||||
AddCache(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex);
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (success)
|
||||
LogMessage?.LogWarning(ex);
|
||||
success = false;
|
||||
}
|
||||
#endregion //上传设备内存队列中的数据
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public abstract class BusinessBaseWithCacheAlarm : BusinessBaseWithCache
|
||||
{
|
||||
#if !Management
|
||||
protected override bool PluginEventDataModelEnable => true;
|
||||
protected override bool AlarmModelEnable => true;
|
||||
|
||||
protected override bool DevModelEnable => false;
|
||||
@@ -57,14 +58,40 @@ public abstract class BusinessBaseWithCacheAlarm : BusinessBaseWithCache
|
||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a => AlarmValueChange(a.Value));
|
||||
GlobalData.AlarmChangedEvent += AlarmValueChange;
|
||||
GlobalData.PluginEventHandler -= PluginEventChange;
|
||||
GlobalData.PluginEventHandler += PluginEventChange;
|
||||
|
||||
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
protected override Task DisposeAsync(bool disposing)
|
||||
{
|
||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||
GlobalData.PluginEventHandler -= PluginEventChange;
|
||||
return base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
|
||||
private void PluginEventChange(PluginEventData value)
|
||||
{
|
||||
if (CurrentDevice?.Pause != false)
|
||||
return;
|
||||
if (TaskSchedulerLoop?.Stoped == true) return;
|
||||
|
||||
if (!PluginEventDataModelEnable) return;
|
||||
// 如果业务属性的缓存为间隔上传,则不执行后续操作
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
PluginChange(value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 当报警状态变化时触发此方法。如果不需要进行报警上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueuePluginDataModel(CacheDBItem{PluginEventData})"/> 方法。
|
||||
/// </summary>
|
||||
protected virtual void PluginChange(PluginEventData value)
|
||||
{
|
||||
// 在报警状态变化时执行的自定义逻辑
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。
|
||||
/// </summary>
|
||||
|
||||
@@ -40,7 +40,13 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a => AlarmValueChange(a.Value));
|
||||
|
||||
GlobalData.AlarmChangedEvent += AlarmValueChange;
|
||||
// 解绑全局数据的事件
|
||||
|
||||
}
|
||||
if (PluginEventDataModelEnable)
|
||||
{
|
||||
GlobalData.PluginEventHandler -= PluginEventChange;
|
||||
GlobalData.PluginEventHandler += PluginEventChange;
|
||||
|
||||
}
|
||||
if (DevModelEnable)
|
||||
{
|
||||
@@ -64,7 +70,7 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
}
|
||||
public override async Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
if (AlarmModelEnable || DevModelEnable || VarModelEnable)
|
||||
if (AlarmModelEnable || DevModelEnable || VarModelEnable || PluginEventDataModelEnable)
|
||||
{
|
||||
// 如果业务属性指定了全部变量,则设置当前设备的变量运行时列表和采集设备列表
|
||||
if (_businessPropertyWithCacheInterval.IsAllVariable)
|
||||
@@ -137,14 +143,12 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
{
|
||||
// 解绑事件
|
||||
GlobalData.AlarmChangedEvent -= AlarmValueChange;
|
||||
GlobalData.PluginEventHandler -= PluginEventChange;
|
||||
|
||||
GlobalData.VariableValueChangeEvent -= VariableValueChange;
|
||||
GlobalData.DeviceStatusChangeEvent -= DeviceStatusChange;
|
||||
|
||||
// 清空内存队列
|
||||
_memoryAlarmModelQueue.Clear();
|
||||
_memoryDevModelQueue.Clear();
|
||||
_memoryVarModelQueue.Clear();
|
||||
_memoryVarModelsQueue.Clear();
|
||||
|
||||
return base.DisposeAsync(disposing);
|
||||
}
|
||||
|
||||
@@ -226,7 +230,26 @@ public abstract class BusinessBaseWithCacheInterval : BusinessBaseWithCache
|
||||
{
|
||||
// 在变量状态变化时执行的自定义逻辑
|
||||
}
|
||||
private void PluginEventChange(PluginEventData value)
|
||||
{
|
||||
if (CurrentDevice?.Pause != false)
|
||||
return;
|
||||
if (TaskSchedulerLoop?.Stoped == true) return;
|
||||
|
||||
if (!PluginEventDataModelEnable) return;
|
||||
// 如果业务属性的缓存为间隔上传,则不执行后续操作
|
||||
//if (_businessPropertyWithCacheInterval?.IsInterval != true)
|
||||
{
|
||||
PluginChange(value);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 当报警状态变化时触发此方法。如果不需要进行报警上传,则可以忽略此方法。通常情况下,需要在此方法中执行 <see cref="BusinessBaseWithCache.AddQueueAlarmModel"/> 方法。
|
||||
/// </summary>
|
||||
protected virtual void PluginChange(PluginEventData value)
|
||||
{
|
||||
// 在报警状态变化时执行的自定义逻辑
|
||||
}
|
||||
/// <summary>
|
||||
/// 当报警值发生变化时触发此事件处理方法。该方法内部会检查是否需要进行报警上传,如果需要,则调用 <see cref="AlarmChange(AlarmVariable)"/> 方法。
|
||||
/// </summary>
|
||||
|
||||
@@ -26,8 +26,17 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
|
||||
protected abstract BusinessPropertyWithCacheIntervalScript _businessPropertyWithCacheIntervalScript { get; }
|
||||
|
||||
#if !Management
|
||||
|
||||
|
||||
#if !Management
|
||||
protected internal override Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
CSharpScriptEngineExtension.Remove(_businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
|
||||
CSharpScriptEngineExtension.Remove(_businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
|
||||
CSharpScriptEngineExtension.Remove(_businessPropertyWithCacheIntervalScript.BigTextScriptPluginEventDataModel);
|
||||
CSharpScriptEngineExtension.Remove(_businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
|
||||
return base.InitChannelAsync(channel, cancellationToken);
|
||||
}
|
||||
public virtual string[] Match(string input)
|
||||
{
|
||||
// 生成缓存键,以确保缓存的唯一性
|
||||
@@ -64,7 +73,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
|
||||
protected IEnumerable<TopicArray> GetAlarmTopicArrays(IEnumerable<AlarmVariable> item)
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<AlarmVariable>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel);
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<AlarmVariable>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptAlarmModel, LogMessage);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.AlarmTopic);
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
@@ -88,16 +97,19 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
|
||||
{
|
||||
var gList = group.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是报警列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
@@ -110,23 +122,92 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsAlarmList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.AlarmTopic, json, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
protected IEnumerable<TopicArray> GetPluginEventDataTopicArrays(IEnumerable<PluginEventData> item)
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<PluginEventData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptPluginEventDataModel, LogMessage);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.PluginEventDataTopic);
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
{
|
||||
//获取分组最终结果
|
||||
var groups = data.GroupByKeys(topics);
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
// 上传主题
|
||||
// 获取预定义的报警主题
|
||||
string topic = _businessPropertyWithCacheIntervalScript.PluginEventDataTopic;
|
||||
|
||||
// 将主题中的占位符替换为分组键对应的值
|
||||
for (int i = 0; i < topics.Length; i++)
|
||||
{
|
||||
topic = topic.Replace(@"${" + topics[i] + @"}", group.Key[i]?.ToString());
|
||||
}
|
||||
|
||||
// 上传内容
|
||||
if (_businessPropertyWithCacheIntervalScript.IsPluginEventDataList)
|
||||
{
|
||||
var gList = group.ToList();
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是报警列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_businessPropertyWithCacheIntervalScript.IsPluginEventDataList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.PluginEventDataTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.PluginEventDataTopic, json, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
protected IEnumerable<TopicArray> GetDeviceTopicArray(IEnumerable<DeviceBasicData> item)
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<DeviceBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel);
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<DeviceBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptDeviceModel, LogMessage);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.DeviceTopic);
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
@@ -152,16 +233,19 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
// 如果是设备列表,则将整个分组转换为 JSON 字符串
|
||||
var gList = group.Select(a => a).ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是设备列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
@@ -175,14 +259,17 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsDeviceList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.DeviceTopic, json, 1);
|
||||
}
|
||||
}
|
||||
@@ -191,7 +278,7 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
|
||||
protected IEnumerable<TopicArray> GetVariableScriptTopicArray(IEnumerable<VariableBasicData> item)
|
||||
{
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<VariableBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel);
|
||||
var data = Application.DynamicModelExtension.GetDynamicModel<VariableBasicData>(item, _businessPropertyWithCacheIntervalScript.BigTextScriptVariableModel, LogMessage);
|
||||
var topics = Match(_businessPropertyWithCacheIntervalScript.VariableTopic);
|
||||
if (topics.Length > 0)
|
||||
{
|
||||
@@ -217,16 +304,20 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
// 如果是变量列表,则将整个分组转换为 JSON 字符串
|
||||
var gList = group.Select(a => a).ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
@@ -240,14 +331,17 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, 1);
|
||||
}
|
||||
}
|
||||
@@ -294,16 +388,19 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
{
|
||||
// 如果是变量列表,则将整个分组转换为 JSON 字符串
|
||||
var gList = group.Select(a => a).ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是变量列表,则将每个分组元素分别转换为 JSON 字符串
|
||||
foreach (var gro in group)
|
||||
{
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = gro.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
// 将主题和 JSON 内容添加到列表中
|
||||
yield return new(topic, json, 1);
|
||||
}
|
||||
@@ -317,14 +414,17 @@ public abstract partial class BusinessBaseWithCacheIntervalScript : BusinessBase
|
||||
if (_businessPropertyWithCacheIntervalScript.IsVariableList)
|
||||
{
|
||||
var gList = data.ToList();
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count);
|
||||
if (gList.Count > 0)
|
||||
{
|
||||
var json = gList.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, gList.Count);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var group in data)
|
||||
{
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented);
|
||||
var json = group.ToSystemTextJsonUtf8Bytes(_businessPropertyWithCacheIntervalScript.JsonFormattingIndented, _businessPropertyWithCacheIntervalScript.JsonIgnoreNull);
|
||||
yield return new(_businessPropertyWithCacheIntervalScript.VariableTopic, json, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public abstract partial class BusinessBaseWithCacheIntervalScriptAll : BusinessBaseWithCacheIntervalScript
|
||||
{
|
||||
#if !Management
|
||||
protected override bool PluginEventDataModelEnable => true;
|
||||
protected override bool AlarmModelEnable => true;
|
||||
|
||||
protected override bool DevModelEnable => true;
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public abstract class BusinessBaseWithCacheIntervalVariable : BusinessBaseWithCacheInterval
|
||||
{
|
||||
#if !Management
|
||||
protected override bool PluginEventDataModelEnable => false;
|
||||
protected override bool AlarmModelEnable => false;
|
||||
|
||||
protected override bool DevModelEnable => false;
|
||||
@@ -30,5 +31,9 @@ public abstract class BusinessBaseWithCacheIntervalVariable : BusinessBaseWithCa
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -28,7 +28,11 @@ public class BusinessPropertyWithCacheIntervalScript : BusinessPropertyWithCache
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool JsonFormattingIndented { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 忽略Null值
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool JsonIgnoreNull { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 设备Topic
|
||||
/// </summary>
|
||||
@@ -46,6 +50,11 @@ public class BusinessPropertyWithCacheIntervalScript : BusinessPropertyWithCache
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool IsAlarmList { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 报警Topic
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
public bool IsPluginEventDataList { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 设备Topic
|
||||
@@ -65,6 +74,12 @@ public class BusinessPropertyWithCacheIntervalScript : BusinessPropertyWithCache
|
||||
[DynamicProperty(Remark = "可使用${key}作为匹配项,key必须是上传实体中的属性,比如ThingsGateway/Alarm/${DeviceName}")]
|
||||
public string AlarmTopic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警Topic
|
||||
/// </summary>
|
||||
[DynamicProperty(Remark = "可使用${key}作为匹配项,key必须是上传实体中的属性,比如ThingsGateway/PluginEventData/${DeviceName}")]
|
||||
public string PluginEventDataTopic { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备实体脚本
|
||||
/// </summary>
|
||||
@@ -85,4 +100,11 @@ public class BusinessPropertyWithCacheIntervalScript : BusinessPropertyWithCache
|
||||
[DynamicProperty]
|
||||
[AutoGenerateColumn(Visible = true, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false)]
|
||||
public string? BigTextScriptAlarmModel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 报警实体脚本
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
[AutoGenerateColumn(Visible = true, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false)]
|
||||
public string? BigTextScriptPluginEventDataModel { get; set; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public class BusinessVariableProperty : VariablePropertyBase
|
||||
{
|
||||
[DynamicProperty]
|
||||
public string Data1 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data2 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data3 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data4 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data5 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data6 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data7 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data8 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data9 { get; set; }
|
||||
[DynamicProperty]
|
||||
public string Data10 { get; set; }
|
||||
}
|
||||
@@ -366,8 +366,17 @@ public abstract partial class CollectBase : DriverBase
|
||||
|
||||
if (Pause) return;
|
||||
if (cancellationToken.IsCancellationRequested) return;
|
||||
CancellationToken readToken = default;
|
||||
var readerLockTask = ReadWriteLock.ReaderLockAsync(cancellationToken);
|
||||
if (!readerLockTask.IsCompleted)
|
||||
{
|
||||
readToken = await readerLockTask.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
readToken = readerLockTask.Result;
|
||||
}
|
||||
|
||||
var readToken = await ReadWriteLock.ReaderLockAsync(cancellationToken).ConfigureAwait(false);
|
||||
if (readToken.IsCancellationRequested)
|
||||
{
|
||||
await ReadVariableSource(state, cancellationToken).ConfigureAwait(false);
|
||||
@@ -379,7 +388,17 @@ public abstract partial class CollectBase : DriverBase
|
||||
|
||||
//if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
// LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
|
||||
var readResult = await ReadSourceAsync(variableSourceRead, allToken).ConfigureAwait(false);
|
||||
|
||||
OperResult<ReadOnlyMemory<byte>> readResult = default;
|
||||
var readTask = ReadSourceAsync(variableSourceRead, allToken);
|
||||
if (!readTask.IsCompleted)
|
||||
{
|
||||
readResult = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
readResult = readTask.Result;
|
||||
}
|
||||
|
||||
var readErrorCount = 0;
|
||||
|
||||
@@ -403,7 +422,16 @@ public abstract partial class CollectBase : DriverBase
|
||||
|
||||
//if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
|
||||
// LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
|
||||
readResult = await ReadSourceAsync(variableSourceRead, allToken).ConfigureAwait(false);
|
||||
var readTask1 = ReadSourceAsync(variableSourceRead, allToken);
|
||||
if (!readTask1.IsCompleted)
|
||||
{
|
||||
readResult = await readTask1.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
readResult = readTask1.Result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (readResult.IsSuccess)
|
||||
|
||||
@@ -159,7 +159,16 @@ public abstract class CollectFoundationBase : CollectBase
|
||||
return new(new OperationCanceledException());
|
||||
|
||||
// 从协议读取数据
|
||||
var read = await FoundationDevice.ReadAsync(variableSourceRead.AddressObject, cancellationToken).ConfigureAwait(false);
|
||||
OperResult<ReadOnlyMemory<byte>> read = default;
|
||||
var readTask = FoundationDevice.ReadAsync(variableSourceRead.AddressObject, cancellationToken);
|
||||
if (!readTask.IsCompleted)
|
||||
{
|
||||
read = await readTask.ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
read = readTask.Result;
|
||||
}
|
||||
|
||||
// 如果读取成功且有有效内容,则解析结构化内容
|
||||
if (read.IsSuccess)
|
||||
|
||||
@@ -23,12 +23,16 @@ public static class DynamicModelExtension
|
||||
/// <summary>
|
||||
/// GetDynamicModel
|
||||
/// </summary>
|
||||
public static IEnumerable<object> GetDynamicModel<T>(this IEnumerable<T> datas, string script)
|
||||
public static IEnumerable<object> GetDynamicModel<T>(this IEnumerable<T> datas, string script, TouchSocket.Core.ILog log)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(script))
|
||||
{
|
||||
//执行脚本,获取新实体
|
||||
var getDeviceModel = CSharpScriptEngineExtension.Do<IDynamicModel>(script);
|
||||
if (getDeviceModel is DynamicModelBase dynamicModelBase)
|
||||
{
|
||||
dynamicModelBase.Logger = log;
|
||||
}
|
||||
return getDeviceModel.GetList(datas?.Cast<object>());
|
||||
}
|
||||
else
|
||||
@@ -50,9 +54,17 @@ public static class DynamicModelExtension
|
||||
if (variableRuntime == null || propertyName.IsNullOrWhiteSpace())
|
||||
return null;
|
||||
|
||||
// 检查是否存在对应的业务设备Id
|
||||
if (variableRuntime.VariablePropertys?.TryGetValue(businessId, out var keyValuePairs) == true)
|
||||
{
|
||||
keyValuePairs.TryGetValue(propertyName, out var value);
|
||||
return value; // 返回属性值
|
||||
}
|
||||
|
||||
|
||||
if (GlobalData.IdDevices.TryGetValue(businessId, out var deviceRuntime))
|
||||
{
|
||||
if (deviceRuntime.Driver?.DriverProperties is IBusinessPropertyAllVariableBase property)
|
||||
if (deviceRuntime.Driver is BusinessBase businessBase && businessBase.DriverProperties is IBusinessPropertyAllVariableBase property)
|
||||
{
|
||||
if (property.IsAllVariable)
|
||||
{
|
||||
@@ -64,18 +76,12 @@ public static class DynamicModelExtension
|
||||
}
|
||||
else
|
||||
{
|
||||
return ThingsGatewayStringConverter.Default.Serialize(null, property.GetValue(propertyName, false));
|
||||
return ThingsGatewayStringConverter.Default.Serialize(null, businessBase.VariablePropertys.GetValue(propertyName, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否存在对应的业务设备Id
|
||||
if (variableRuntime.VariablePropertys?.ContainsKey(businessId) == true)
|
||||
{
|
||||
variableRuntime.VariablePropertys[businessId].TryGetValue(propertyName, out var value);
|
||||
return value; // 返回属性值
|
||||
}
|
||||
|
||||
return null; // 未找到对应的业务设备Id,返回null
|
||||
}
|
||||
@@ -127,3 +133,8 @@ public interface IDynamicModel
|
||||
{
|
||||
IEnumerable<dynamic> GetList(IEnumerable<object> datas);
|
||||
}
|
||||
public abstract class DynamicModelBase : IDynamicModel
|
||||
{
|
||||
public TouchSocket.Core.ILog Logger { get; set; }
|
||||
public abstract IEnumerable<dynamic> GetList(IEnumerable<object> datas);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,28 @@ public delegate void VariableCollectEventHandler(VariableRuntime variableRuntime
|
||||
/// </summary>
|
||||
public delegate void VariableAlarmEventHandler(AlarmVariable alarmVariable);
|
||||
|
||||
public delegate void PluginEventHandler(PluginEventData data);
|
||||
|
||||
public class PluginEventData
|
||||
{
|
||||
public string DeviceName { get; set; }
|
||||
public Newtonsoft.Json.Linq.JObject Value { get; set; }
|
||||
public string ValueType { get; set; }
|
||||
|
||||
[System.Text.Json.Serialization.JsonIgnore]
|
||||
[Newtonsoft.Json.JsonIgnore]
|
||||
public object ObjectValue { get; set; }
|
||||
//public object ObjectValue => ValueType.IsNullOrEmpty() ? Value : Value.ToObject(Type.GetType(ValueType, false) ?? typeof(JObject));
|
||||
|
||||
public PluginEventData(string deviceName, object value)
|
||||
{
|
||||
DeviceName = deviceName;
|
||||
ObjectValue = value;
|
||||
Value = Newtonsoft.Json.Linq.JObject.FromObject(value);
|
||||
ValueType = $"{value?.GetType().FullName},{value?.GetType().Assembly.GetName().Name}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 采集设备值与状态全局提供类,用于提供全局的设备状态和变量数据的管理
|
||||
/// </summary>
|
||||
@@ -64,6 +86,8 @@ public static class GlobalData
|
||||
/// </summary>
|
||||
public static event VariableAlarmEventHandler? AlarmChangedEvent;
|
||||
|
||||
public static event PluginEventHandler? PluginEventHandler;
|
||||
|
||||
public static async Task<IEnumerable<ChannelRuntime>> GetCurrentUserChannels()
|
||||
{
|
||||
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
|
||||
@@ -480,6 +504,14 @@ public static class GlobalData
|
||||
AlarmChangedEvent?.Invoke(alarmVariable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件状态变化处理方法,用于处理事件状态变化时的逻辑
|
||||
/// </summary>
|
||||
public static void PluginEventChange(PluginEventData pluginEventData)
|
||||
{
|
||||
// 触发设备状态变化事件,并将设备运行时对象转换为设备数据对象进行传递
|
||||
PluginEventHandler?.Invoke(pluginEventData);
|
||||
}
|
||||
/// <summary>
|
||||
/// 设备状态变化处理方法,用于处理设备状态变化时的逻辑
|
||||
/// </summary>
|
||||
|
||||
@@ -61,7 +61,7 @@ public class LogJob : IJob
|
||||
|
||||
//网关通道日志以通道id命名
|
||||
var rulesService = App.RootServices.GetService<IRulesService>();
|
||||
var ruleNames = (await rulesService.GetAllAsync().ConfigureAwait(false)).Select(a => a.Name.ToString()).ToHashSet();
|
||||
var ruleNames = (await rulesService.GetAllRulesAsync().ConfigureAwait(false)).Select(a => a.Name.ToString()).ToHashSet();
|
||||
var ruleBaseDir = RulesEngineHostedService.LogDir;
|
||||
Directory.CreateDirectory(ruleBaseDir);
|
||||
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
{
|
||||
|
||||
"ThingsGateway.Gateway.Application.BusinessVariableProperty": {
|
||||
|
||||
"Data1": "Data1",
|
||||
"Data2": "Data2",
|
||||
"Data3": "Data3",
|
||||
"Data4": "Data4",
|
||||
"Data5": "Data5",
|
||||
"Data6": "Data6",
|
||||
"Data7": "Data7",
|
||||
"Data8": "Data8",
|
||||
"Data9": "Data9",
|
||||
"Data10": "Data10"
|
||||
},
|
||||
|
||||
|
||||
|
||||
"ThingsGateway.Management.Application.ManagementExportString": {
|
||||
|
||||
"ManagementConfigName": "ManagementConfigName"
|
||||
@@ -30,6 +47,7 @@
|
||||
|
||||
"Actuator": "Actuator",
|
||||
"AlarmChangedTriggerNode": "AlarmStateTrigger",
|
||||
"PluginEventChangedTriggerNode": "PluginEventTrigger",
|
||||
"BusinessNode": "BusinessDeviceExecution",
|
||||
"BusinessNode.Placeholder": "BusinessDeviceName",
|
||||
"Cancel": "Cancel",
|
||||
@@ -225,15 +243,19 @@
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BusinessPropertyWithCacheIntervalScript": {
|
||||
"AlarmTopic": "AlarmTopic",
|
||||
"PluginEventDataTopic": "PluginEventDataTopic",
|
||||
"BigTextScriptAlarmModel": "BigTextScriptAlarmModel",
|
||||
"BigTextScriptPluginEventDataModel": "BigTextScriptPluginEventDataModel",
|
||||
"BigTextScriptDeviceModel": "BigTextScriptDeviceModel",
|
||||
"BigTextScriptVariableModel": "BigTextScriptVariableModel",
|
||||
"DetailLog": "DetailLog",
|
||||
"DeviceTopic": "DeviceTopic",
|
||||
"IsAlarmList": "IsAlarmList",
|
||||
"IsPluginEventDataList": "IsPluginEventDataList",
|
||||
"IsDeviceList": "IsDeviceList",
|
||||
"IsVariableList": "IsVariableList",
|
||||
"JsonFormattingIndented": "JsonFormattingIndented",
|
||||
"JsonIgnoreNull": "JsonIgnoreNull",
|
||||
"VariableTopic": "VariableTopic"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BusinessUpdateEnum": {
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
{
|
||||
"ThingsGateway.Gateway.Application.BusinessVariableProperty": {
|
||||
|
||||
"Data1": "备注1",
|
||||
"Data2": "备注2",
|
||||
"Data3": "备注3",
|
||||
"Data4": "备注4",
|
||||
"Data5": "备注5",
|
||||
"Data6": "备注6",
|
||||
"Data7": "备注7",
|
||||
"Data8": "备注8",
|
||||
"Data9": "备注9",
|
||||
"Data10": "备注10"
|
||||
},
|
||||
|
||||
"ThingsGateway.Management.Application.ManagementExportString": {
|
||||
|
||||
"ManagementConfigName": "通讯配置"
|
||||
@@ -32,6 +46,7 @@
|
||||
|
||||
"Actuator": "执行",
|
||||
"AlarmChangedTriggerNode": "报警状态触发器",
|
||||
"PluginEventChangedTriggerNode": "插件事件触发器",
|
||||
"BusinessNode": "业务设备执行",
|
||||
"BusinessNode.Placeholder": "设备名称",
|
||||
"Cancel": "取消",
|
||||
@@ -227,15 +242,19 @@
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BusinessPropertyWithCacheIntervalScript": {
|
||||
"AlarmTopic": "报警主题",
|
||||
"PluginEventDataTopic": "插件事件主题",
|
||||
"BigTextScriptAlarmModel": "报警上传脚本",
|
||||
"BigTextScriptPluginEventDataModel": "插件事件上传脚本",
|
||||
"BigTextScriptDeviceModel": "设备上传脚本",
|
||||
"BigTextScriptVariableModel": "变量上传脚本",
|
||||
"DetailLog": "详细日志",
|
||||
"DeviceTopic": "设备主题",
|
||||
"IsAlarmList": "报警列表上传",
|
||||
"IsPluginEventDataList": "插件事件列表上传",
|
||||
"IsDeviceList": "设备状态列表上传",
|
||||
"IsVariableList": "变量列表上传",
|
||||
"JsonFormattingIndented": "Json缩进格式化",
|
||||
"JsonIgnoreNull": "Json忽略null值",
|
||||
"VariableTopic": "变量主题"
|
||||
},
|
||||
"ThingsGateway.Gateway.Application.BusinessUpdateEnum": {
|
||||
|
||||
@@ -87,6 +87,12 @@ public class ChannelRuntimeService : IChannelRuntimeService
|
||||
|
||||
}
|
||||
|
||||
public async Task RestartChannelsAsync()
|
||||
{
|
||||
var data = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
await RestartChannelAsync(data.ToList()).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel)
|
||||
{
|
||||
if (GlobalData.IdChannels.TryGetValue(id, out var ChannelRuntime))
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace ThingsGateway.Gateway.Application
|
||||
Task<string> GetPluginNameAsync(long channelId);
|
||||
|
||||
Task RestartChannelAsync(long channelId);
|
||||
Task RestartChannelsAsync();
|
||||
|
||||
Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id);
|
||||
Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
|
||||
@@ -34,12 +34,15 @@ public class DeviceRuntimeService : IDeviceRuntimeService
|
||||
|
||||
return Task.FromResult(GlobalData.ReadOnlyIdDevices.ToDictionary(a => a.Key, a => Tuple.Create(a.Value.Name, a.Value.PluginName)));
|
||||
}
|
||||
|
||||
public async Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect)
|
||||
public async Task<QueryData<SelectedItem>> OnDeviceSelectedItemQueryAsync(VirtualizeQueryOption option, bool isCollect)
|
||||
{
|
||||
var devices = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
|
||||
return devices.Where(a => a.IsCollect == isCollect).BuildDeviceSelectList().ToList();
|
||||
|
||||
return devices.Where(a => a.IsCollect == isCollect).WhereIf(!option.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(option.SearchText)).GetQueryData(option, GatewayResourceUtil.BuildDeviceSelectList);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Task<string> GetDevicePluginNameAsync(long id)
|
||||
{
|
||||
return Task.FromResult(GlobalData.ReadOnlyIdDevices.TryGetValue(id, out var deviceRuntime) ? deviceRuntime.PluginName : string.Empty);
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace ThingsGateway.Gateway.Application
|
||||
Task<bool> ClearDeviceAsync(bool restart);
|
||||
Task<bool> IsRedundantDeviceAsync(long id);
|
||||
Task<string> GetDeviceNameAsync(long redundantDeviceId);
|
||||
Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect);
|
||||
Task<QueryData<SelectedItem>> OnDeviceSelectedItemQueryAsync(VirtualizeQueryOption option, bool isCollect);
|
||||
Task<string> GetDevicePluginNameAsync(long id);
|
||||
|
||||
Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync();
|
||||
|
||||
@@ -14,27 +14,86 @@ using ThingsGateway.Authentication;
|
||||
|
||||
using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.WebApi;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
#if Management
|
||||
[GeneratorRpcProxy(GeneratorFlag = GeneratorFlag.ExtensionAsync)]
|
||||
#endif
|
||||
[TouchSocket.WebApi.Router("/miniapi/managementrpc/[action]")]
|
||||
public interface IManagementRpcServer : IRpcServer
|
||||
{
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task DeleteBackendLogAsync();
|
||||
[DmtpRpc]
|
||||
Task<List<BackendLog>> GetNewBackendLogAsync();
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<BackendLog>> BackendLogPageAsync(QueryPageOptions option);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<List<BackendLogDayStatisticsOutput>> BackendLogStatisticsByDayAsync(int day);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> ClearChannelAsync(bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> ClearDeviceAsync(bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有规则
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task ClearRulesAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> ClearVariableAsync(bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task CopyChannelAsync(int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long channelId, bool AutoRestartThread);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task CopyVariableAsync(List<Variable> Model, int CopyCount, string CopyVariableNamePrefix, int CopyVariableNameSuffixNumber, bool AutoRestartThread);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task DeleteBackendLogAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> DeleteChannelAsync(List<long> ids, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> DeleteDeviceAsync(List<long> ids, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 删除 RpcLog 表中的所有记录
|
||||
@@ -43,21 +102,281 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// 调用此方法会删除 RpcLog 表中的所有记录。
|
||||
/// </remarks>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task DeleteRpcLogAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task DeleteRuleRuntimesAsync(List<long> ids);
|
||||
|
||||
/// <summary>
|
||||
/// 删除规则
|
||||
/// </summary>
|
||||
/// <param name="ids">待删除规则的ID列表</param>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> DeleteRulesAsync(List<long> ids);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> DeleteVariableAsync(List<long> ids, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<TouchSocket.Core.LogLevel> DeviceLogLevelAsync(long id);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task DeviceRedundantThreadAsync(long id);
|
||||
|
||||
/// <summary>
|
||||
/// 修改冗余设置
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task EditRedundancyOptionAsync(RedundancyOptions input);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task EditRuleRuntimesAsync(Rules rules);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<USheetDatas> ExportChannelAsync(List<Channel> channels);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<USheetDatas> ExportDeviceAsync(List<Device> devices);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存/数据库获取全部信息
|
||||
/// </summary>
|
||||
/// <returns>规则列表</returns>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<List<Rules>> GetAllRulesAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<string> GetChannelNameAsync(long channelId);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<List<Device>> GetDeviceListAsync(QueryPageOptions option, int v);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<string> GetDeviceNameAsync(long redundantDeviceId);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<string> GetDevicePluginNameAsync(long id);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<List<BackendLog>> GetNewBackendLogAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 获取最新的十条 RpcLog 记录
|
||||
/// </summary>
|
||||
/// <returns>最新的十条记录</returns>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<List<RpcLog>> GetNewRpcLogAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<string> GetPluginNameAsync(long channelId);
|
||||
|
||||
/// <summary>
|
||||
/// 根据插件类型获取信息
|
||||
/// </summary>
|
||||
/// <param name="pluginType"></param>
|
||||
/// <returns></returns>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null);
|
||||
|
||||
/// <summary>
|
||||
/// 获取冗余设置
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<RedundancyOptions> GetRedundancyAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<Rules> GetRuleRuntimesAsync(long rulesId);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int v);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<bool> IsRedundantDeviceAsync(long id);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<SelectedItem>> OnDeviceSelectedItemQueryAsync(VirtualizeQueryOption option, bool isCollect);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task PauseThreadAsync(long id);
|
||||
|
||||
/// <summary>
|
||||
/// 分页显示插件
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task RedundancyForcedSync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<TouchSocket.Core.LogLevel> RedundancyLogLevelAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<string> RedundancyLogPathAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 重载插件
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task ReloadPluginAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task RestartChannelAsync(long channelId);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task RestartChannelsAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task RestartDeviceAsync(long id, bool deleteCache);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task RestartServerAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas);
|
||||
/// <summary>
|
||||
/// 分页查询 RpcLog 数据
|
||||
/// </summary>
|
||||
/// <param name="option">查询选项</param>
|
||||
/// <returns>查询到的数据</returns>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<RpcLog>> RpcLogPageAsync(QueryPageOptions option);
|
||||
|
||||
/// <summary>
|
||||
@@ -66,136 +385,15 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// <param name="day">统计的天数</param>
|
||||
/// <returns>按天统计的结果列表</returns>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<List<RpcLogDayStatisticsOutput>> RpcLogStatisticsByDayAsync(int day);
|
||||
|
||||
[DmtpRpc]
|
||||
Task RestartServerAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> UUIDAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
Task<AuthorizeInfo> TryAuthorizeAsync(string password);
|
||||
[DmtpRpc]
|
||||
Task<AuthorizeInfo> TryGetAuthorizeInfoAsync();
|
||||
[DmtpRpc]
|
||||
Task UnAuthorizeAsync();
|
||||
[DmtpRpc]
|
||||
Task<bool> StartBusinessChannelEnableAsync();
|
||||
[DmtpRpc]
|
||||
Task<bool> StartCollectChannelEnableAsync();
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task StartRedundancyTaskAsync();
|
||||
[DmtpRpc]
|
||||
Task StopRedundancyTaskAsync();
|
||||
[DmtpRpc]
|
||||
Task RedundancyForcedSync();
|
||||
|
||||
[DmtpRpc]
|
||||
Task<TouchSocket.Core.LogLevel> RedundancyLogLevelAsync();
|
||||
[DmtpRpc]
|
||||
Task SetRedundancyLogLevelAsync(TouchSocket.Core.LogLevel logLevel);
|
||||
[DmtpRpc]
|
||||
Task<string> RedundancyLogPathAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 修改冗余设置
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
[DmtpRpc]
|
||||
Task EditRedundancyOptionAsync(RedundancyOptions input);
|
||||
/// <summary>
|
||||
/// 获取冗余设置
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
Task<RedundancyOptions> GetRedundancyAsync();
|
||||
[DmtpRpc]
|
||||
Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath);
|
||||
[DmtpRpc]
|
||||
Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据插件类型获取信息
|
||||
/// </summary>
|
||||
/// <param name="pluginType"></param>
|
||||
/// <returns></returns>
|
||||
[DmtpRpc]
|
||||
Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null);
|
||||
|
||||
/// <summary>
|
||||
/// 分页显示插件
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
|
||||
|
||||
/// <summary>
|
||||
/// 重载插件
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
Task ReloadPluginAsync();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加插件
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
[DmtpRpc]
|
||||
Task SavePluginByPathAsync(PluginAddPathInput plugin);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync();
|
||||
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count);
|
||||
[DmtpRpc]
|
||||
Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<TouchSocket.Core.LogLevel> RulesLogLevelAsync(long rulesId);
|
||||
|
||||
[DmtpRpc]
|
||||
Task SetRulesLogLevelAsync(long rulesId, TouchSocket.Core.LogLevel logLevel);
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<string> RulesLogPathAsync(long rulesId);
|
||||
[DmtpRpc]
|
||||
Task<Rules> GetRuleRuntimesAsync(long rulesId);
|
||||
[DmtpRpc]
|
||||
Task DeleteRuleRuntimesAsync(List<long> ids);
|
||||
[DmtpRpc]
|
||||
Task EditRuleRuntimesAsync(Rules rules);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有规则
|
||||
/// </summary>
|
||||
[DmtpRpc]
|
||||
Task ClearRulesAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 删除规则
|
||||
/// </summary>
|
||||
/// <param name="ids">待删除规则的ID列表</param>
|
||||
[DmtpRpc]
|
||||
Task<bool> DeleteRulesAsync(List<long> ids);
|
||||
|
||||
/// <summary>
|
||||
/// 从缓存/数据库获取全部信息
|
||||
/// </summary>
|
||||
/// <returns>规则列表</returns>
|
||||
[DmtpRpc]
|
||||
Task<List<Rules>> GetAllAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 报表查询
|
||||
@@ -203,168 +401,84 @@ public interface IManagementRpcServer : IRpcServer
|
||||
/// <param name="option">查询条件</param>
|
||||
/// <param name="filterKeyValueAction">查询条件</param>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<QueryData<Rules>> RulesPageAsync(QueryPageOptions option, FilterKeyValueAction filterKeyValueAction = null);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart);
|
||||
|
||||
/// <summary>
|
||||
/// 添加插件
|
||||
/// </summary>
|
||||
/// <param name="plugin"></param>
|
||||
/// <returns></returns>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task SavePluginByPathAsync(PluginAddPathInput plugin);
|
||||
|
||||
/// <summary>
|
||||
/// 保存规则
|
||||
/// </summary>
|
||||
/// <param name="input">规则对象</param>
|
||||
/// <param name="type">保存类型</param>
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> SaveRulesAsync(Rules input, ItemChangedType type);
|
||||
|
||||
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> GetPluginNameAsync(long channelId);
|
||||
|
||||
[DmtpRpc]
|
||||
Task RestartChannelAsync(long channelId);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
[DmtpRpc]
|
||||
Task CopyChannelAsync(int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long channelId, bool AutoRestartThread);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options);
|
||||
[DmtpRpc]
|
||||
Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> DeleteChannelAsync(List<long> ids, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> ClearChannelAsync(bool restart);
|
||||
[DmtpRpc]
|
||||
Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<USheetDatas> ExportChannelAsync(List<Channel> channels);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> GetChannelNameAsync(long channelId);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task SetDeviceLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
|
||||
[DmtpRpc]
|
||||
Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread);
|
||||
[DmtpRpc]
|
||||
Task<TouchSocket.Core.LogLevel> DeviceLogLevelAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<bool> DeleteDeviceAsync(List<long> ids, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<USheetDatas> ExportDeviceAsync(List<Device> devices);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter);
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId);
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart);
|
||||
[DmtpRpc]
|
||||
Task DeviceRedundantThreadAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task RestartDeviceAsync(long id, bool deleteCache);
|
||||
[DmtpRpc]
|
||||
Task PauseThreadAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options);
|
||||
[DmtpRpc]
|
||||
Task<List<Device>> GetDeviceListAsync(QueryPageOptions option, int v);
|
||||
[DmtpRpc]
|
||||
Task<bool> ClearDeviceAsync(bool restart);
|
||||
[DmtpRpc]
|
||||
Task<bool> IsRedundantDeviceAsync(long id);
|
||||
[DmtpRpc]
|
||||
Task<string> GetDeviceNameAsync(long redundantDeviceId);
|
||||
|
||||
|
||||
|
||||
[DmtpRpc]
|
||||
Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<string> GetDevicePluginNameAsync(long id);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> DeleteVariableAsync(List<long> ids, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> ClearVariableAsync(bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart);
|
||||
|
||||
[DmtpRpc]
|
||||
Task CopyVariableAsync(List<Variable> Model, int CopyCount, string CopyVariableNamePrefix, int CopyVariableNameSuffixNumber, bool AutoRestartThread);
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task SetChannelLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options);
|
||||
[DmtpRpc]
|
||||
Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter);
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task SetDeviceLogLevelAsync(long id, TouchSocket.Core.LogLevel logLevel);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int v);
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task SetRedundancyLogLevelAsync(TouchSocket.Core.LogLevel logLevel);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder);
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task SetRulesLogLevelAsync(long rulesId, TouchSocket.Core.LogLevel logLevel);
|
||||
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart);
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> StartBusinessChannelEnableAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData);
|
||||
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<bool> StartCollectChannelEnableAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart);
|
||||
[DmtpRpc]
|
||||
Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync();
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task StartRedundancyTaskAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task StopRedundancyTaskAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task<AuthorizeInfo> TryAuthorizeAsync(string password);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<AuthorizeInfo> TryGetAuthorizeInfoAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task UnAuthorizeAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<string> UUIDAsync();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://thingsgateway.cn/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
using TouchSocket.Rpc;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
public interface IPluginRpcServer : IRpcServer
|
||||
{
|
||||
|
||||
}
|
||||
@@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Components.Forms;
|
||||
using ThingsGateway.Authentication;
|
||||
|
||||
using TouchSocket.Core;
|
||||
using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.Sockets;
|
||||
|
||||
@@ -24,188 +23,78 @@ namespace ThingsGateway.Gateway.Application;
|
||||
public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBackendLogService, IRpcLogService, IRestartService, IAuthenticationService, IChannelEnableService, IRedundancyHostedService, IRedundancyService, ITextFileReadService, IPluginPageService, IRealAlarmService, IChannelPageService, IDevicePageService, IVariablePageService
|
||||
{
|
||||
|
||||
[DmtpRpc]
|
||||
public Task DeleteBackendLogAsync() => App.GetService<IBackendLogService>().DeleteBackendLogAsync();
|
||||
[DmtpRpc]
|
||||
public Task<List<BackendLogDayStatisticsOutput>> BackendLogStatisticsByDayAsync(int day) => App.GetService<IBackendLogService>().BackendLogStatisticsByDayAsync(day);
|
||||
|
||||
[DmtpRpc]
|
||||
public Task<List<BackendLog>> GetNewBackendLogAsync() => App.GetService<IBackendLogService>().GetNewBackendLogAsync();
|
||||
|
||||
[DmtpRpc]
|
||||
public Task<QueryData<BackendLog>> BackendLogPageAsync(QueryPageOptions option) => App.GetService<IBackendLogService>().BackendLogPageAsync(option);
|
||||
|
||||
public Task<List<BackendLogDayStatisticsOutput>> BackendLogStatisticsByDayAsync(int day) => App.GetService<IBackendLogService>().BackendLogStatisticsByDayAsync(day);
|
||||
|
||||
[DmtpRpc]
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||
{
|
||||
var data = await GlobalData.RpcService.InvokeDeviceMethodAsync($"Management[{(callContext.Caller is ITcpSession tcpSession ? tcpSession.GetIPPort() : string.Empty)}]", deviceDatas, callContext.Token).ConfigureAwait(false);
|
||||
public Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart) =>
|
||||
App.GetService<IChannelPageService>().BatchEditChannelAsync(models, oldModel, model, restart);
|
||||
|
||||
return data.ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Key, b => b.Value.GetOperResult()));
|
||||
}
|
||||
public Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart) =>
|
||||
App.GetService<IDevicePageService>().BatchEditDeviceAsync(models, oldModel, model, restart);
|
||||
|
||||
public Task DeleteRpcLogAsync() => App.GetService<IRpcLogService>().DeleteRpcLogAsync();
|
||||
public Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart) =>
|
||||
App.GetService<IVariablePageService>().BatchEditVariableAsync(models, oldModel, model, restart);
|
||||
|
||||
public Task<List<RpcLog>> GetNewRpcLogAsync() => App.GetService<IRpcLogService>().GetNewRpcLogAsync();
|
||||
|
||||
public Task<QueryData<RpcLog>> RpcLogPageAsync(QueryPageOptions option) => App.GetService<IRpcLogService>().RpcLogPageAsync(option);
|
||||
|
||||
public Task<List<RpcLogDayStatisticsOutput>> RpcLogStatisticsByDayAsync(int day) => App.GetService<IRpcLogService>().RpcLogStatisticsByDayAsync(day);
|
||||
|
||||
public Task RestartServerAsync() => App.GetService<IRestartService>().RestartServerAsync();
|
||||
|
||||
public Task<string> UUIDAsync() => App.GetService<IAuthenticationService>().UUIDAsync();
|
||||
|
||||
public Task<AuthorizeInfo> TryAuthorizeAsync(string password) => App.GetService<IAuthenticationService>().TryAuthorizeAsync(password);
|
||||
|
||||
public Task<AuthorizeInfo> TryGetAuthorizeInfoAsync() => App.GetService<IAuthenticationService>().TryGetAuthorizeInfoAsync();
|
||||
|
||||
public Task UnAuthorizeAsync() => App.GetService<IAuthenticationService>().UnAuthorizeAsync();
|
||||
|
||||
public Task<bool> StartBusinessChannelEnableAsync() => App.GetService<IChannelEnableService>().StartBusinessChannelEnableAsync();
|
||||
|
||||
|
||||
public Task<bool> StartCollectChannelEnableAsync() => App.GetService<IChannelEnableService>().StartCollectChannelEnableAsync();
|
||||
|
||||
public Task StartRedundancyTaskAsync() => App.GetService<IRedundancyHostedService>().StartRedundancyTaskAsync();
|
||||
|
||||
public Task StopRedundancyTaskAsync() => App.GetService<IRedundancyHostedService>().StopRedundancyTaskAsync();
|
||||
|
||||
public Task RedundancyForcedSync() => App.GetService<IRedundancyHostedService>().RedundancyForcedSync();
|
||||
|
||||
|
||||
public Task EditRedundancyOptionAsync(RedundancyOptions input) => App.GetService<IRedundancyService>().EditRedundancyOptionAsync(input);
|
||||
|
||||
public Task<RedundancyOptions> GetRedundancyAsync() => App.GetService<IRedundancyService>().GetRedundancyAsync();
|
||||
|
||||
public Task<LogLevel> RedundancyLogLevelAsync() => App.GetService<IRedundancyHostedService>().RedundancyLogLevelAsync();
|
||||
|
||||
|
||||
public Task SetRedundancyLogLevelAsync(LogLevel logLevel) => App.GetService<IRedundancyHostedService>().SetRedundancyLogLevelAsync(logLevel);
|
||||
|
||||
public Task<string> RedundancyLogPathAsync() => App.GetService<IRedundancyHostedService>().RedundancyLogPathAsync();
|
||||
|
||||
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath) => App.GetService<ITextFileReadService>().GetLogFilesAsync(directoryPath);
|
||||
|
||||
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200) => App.GetService<ITextFileReadService>().LastLogDataAsync(file, lineCount);
|
||||
|
||||
public Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null) => App.GetService<IPluginPageService>().GetPluginsAsync(pluginType);
|
||||
|
||||
public Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null) => App.GetService<IPluginPageService>().PluginPageAsync(options, pluginTypeEnum);
|
||||
|
||||
public Task ReloadPluginAsync() => App.GetService<IPluginPageService>().ReloadPluginAsync();
|
||||
|
||||
|
||||
|
||||
public Task SavePluginByPathAsync(PluginAddPathInput plugin) => App.GetService<IPluginPageService>().SavePluginByPathAsync(plugin);
|
||||
|
||||
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync() => App.GetService<IRealAlarmService>().GetCurrentUserRealAlarmVariablesAsync();
|
||||
|
||||
public Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count) => App.GetService<IGlobalDataService>().GetCurrentUserDeviceSelectedItemsAsync(searchText, startIndex, count);
|
||||
|
||||
public Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count) => App.GetService<IGlobalDataService>().GetCurrentUserDeviceVariableSelectedItemsAsync(deviceText, searchText, startIndex, count);
|
||||
|
||||
public Task<TouchSocket.Core.LogLevel> RulesLogLevelAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().RulesLogLevelAsync(rulesId);
|
||||
public Task SetRulesLogLevelAsync(long rulesId, TouchSocket.Core.LogLevel logLevel) => App.GetService<IRulesEngineHostedService>().SetRulesLogLevelAsync(rulesId, logLevel);
|
||||
public Task<string> RulesLogPathAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().RulesLogPathAsync(rulesId);
|
||||
public Task<Rules> GetRuleRuntimesAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().GetRuleRuntimesAsync(rulesId);
|
||||
|
||||
|
||||
public Task DeleteRuleRuntimesAsync(List<long> ids) => App.GetService<IRulesEngineHostedService>().DeleteRuleRuntimesAsync(ids);
|
||||
|
||||
public Task EditRuleRuntimesAsync(Rules rules) => App.GetService<IRulesEngineHostedService>().EditRuleRuntimesAsync(rules);
|
||||
|
||||
public Task ClearRulesAsync() => App.GetService<IRulesService>().ClearRulesAsync();
|
||||
|
||||
|
||||
public Task<bool> DeleteRulesAsync(List<long> ids) => App.GetService<IRulesService>().DeleteRulesAsync(ids);
|
||||
|
||||
public Task<List<Rules>> GetAllAsync() => App.GetService<IRulesService>().GetAllAsync();
|
||||
|
||||
|
||||
public Task<QueryData<Rules>> RulesPageAsync(QueryPageOptions option, FilterKeyValueAction filterKeyValueAction = null) => App.GetService<IRulesService>().RulesPageAsync(option, filterKeyValueAction);
|
||||
|
||||
|
||||
public Task<bool> SaveRulesAsync(Rules input, ItemChangedType type) => App.GetService<IRulesService>().SaveRulesAsync(input, type);
|
||||
|
||||
public Task<string> GetPluginNameAsync(long channelId) => App.GetService<IChannelPageService>().GetPluginNameAsync(channelId);
|
||||
|
||||
public Task RestartChannelAsync(long channelId) =>
|
||||
App.GetService<IChannelPageService>().RestartChannelAsync(channelId);
|
||||
public Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IVariablePageService>().BatchSaveVariableAsync(input, type, restart);
|
||||
|
||||
public Task<LogLevel> ChannelLogLevelAsync(long id) =>
|
||||
App.GetService<IChannelPageService>().ChannelLogLevelAsync(id);
|
||||
|
||||
public Task SetChannelLogLevelAsync(long id, LogLevel logLevel) =>
|
||||
App.GetService<IChannelPageService>().SetChannelLogLevelAsync(id, logLevel);
|
||||
public Task<bool> ClearChannelAsync(bool restart) =>
|
||||
App.GetService<IChannelPageService>().ClearChannelAsync(restart);
|
||||
|
||||
public Task<bool> ClearDeviceAsync(bool restart) =>
|
||||
App.GetService<IDevicePageService>().ClearDeviceAsync(restart);
|
||||
|
||||
public Task ClearRulesAsync() => App.GetService<IRulesService>().ClearRulesAsync();
|
||||
|
||||
public Task<bool> ClearVariableAsync(bool restart) =>
|
||||
App.GetService<IVariablePageService>().ClearVariableAsync(restart);
|
||||
|
||||
public Task CopyChannelAsync(int copyCount, string copyChannelNamePrefix, int copyChannelNameSuffixNumber,
|
||||
string copyDeviceNamePrefix, int copyDeviceNameSuffixNumber, long channelId, bool restart) =>
|
||||
App.GetService<IChannelPageService>().CopyChannelAsync(copyCount, copyChannelNamePrefix, copyChannelNameSuffixNumber,
|
||||
copyDeviceNamePrefix, copyDeviceNameSuffixNumber, channelId, restart);
|
||||
|
||||
public Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IChannelPageService>().OnChannelQueryAsync(options);
|
||||
|
||||
public Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0) =>
|
||||
App.GetService<IChannelPageService>().GetChannelListAsync(options, max);
|
||||
|
||||
public Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IChannelPageService>().SaveChannelAsync(input, type, restart);
|
||||
|
||||
public Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart) =>
|
||||
App.GetService<IChannelPageService>().BatchEditChannelAsync(models, oldModel, model, restart);
|
||||
|
||||
public Task<bool> DeleteChannelAsync(List<long> ids, bool restart) =>
|
||||
App.GetService<IChannelPageService>().DeleteChannelAsync(ids, restart);
|
||||
|
||||
public Task<bool> ClearChannelAsync(bool restart) =>
|
||||
App.GetService<IChannelPageService>().ClearChannelAsync(restart);
|
||||
|
||||
public Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelAsync(upData, insertData, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelUSheetDatasAsync(input, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelFileAsync(filePath, restart);
|
||||
|
||||
public Task<USheetDatas> ExportChannelAsync(List<Channel> channels) =>
|
||||
App.GetService<IChannelPageService>().ExportChannelAsync(channels);
|
||||
|
||||
public Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option) =>
|
||||
App.GetService<IChannelPageService>().OnChannelSelectedItemQueryAsync(option);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(IBrowserFile file, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelAsync(file, restart);
|
||||
|
||||
public Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter) =>
|
||||
App.GetService<IChannelPageService>().ExportChannelFileAsync(exportFilter);
|
||||
|
||||
public Task<string> GetChannelNameAsync(long id) =>
|
||||
App.GetService<IChannelPageService>().GetChannelNameAsync(id);
|
||||
|
||||
public Task SetDeviceLogLevelAsync(long id, LogLevel logLevel) =>
|
||||
App.GetService<IDevicePageService>().SetDeviceLogLevelAsync(id, logLevel);
|
||||
|
||||
public Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread) =>
|
||||
App.GetService<IDevicePageService>().CopyDeviceAsync(CopyCount, CopyDeviceNamePrefix, CopyDeviceNameSuffixNumber, deviceId, AutoRestartThread);
|
||||
|
||||
public Task<LogLevel> DeviceLogLevelAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().DeviceLogLevelAsync(id);
|
||||
public Task CopyVariableAsync(List<Variable> model, int copyCount, string copyVariableNamePrefix, int copyVariableNameSuffixNumber, bool restart) =>
|
||||
App.GetService<IVariablePageService>().CopyVariableAsync(model, copyCount, copyVariableNamePrefix, copyVariableNameSuffixNumber, restart);
|
||||
|
||||
public Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart) =>
|
||||
App.GetService<IDevicePageService>().BatchEditDeviceAsync(models, oldModel, model, restart);
|
||||
|
||||
public Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IDevicePageService>().SaveDeviceAsync(input, type, restart);
|
||||
public Task DeleteBackendLogAsync() => App.GetService<IBackendLogService>().DeleteBackendLogAsync();
|
||||
public Task<bool> DeleteChannelAsync(List<long> ids, bool restart) =>
|
||||
App.GetService<IChannelPageService>().DeleteChannelAsync(ids, restart);
|
||||
|
||||
public Task<bool> DeleteDeviceAsync(List<long> ids, bool restart) =>
|
||||
App.GetService<IDevicePageService>().DeleteDeviceAsync(ids, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceUSheetDatasAsync(input, restart);
|
||||
public Task DeleteRpcLogAsync() => App.GetService<IRpcLogService>().DeleteRpcLogAsync();
|
||||
|
||||
public Task DeleteRuleRuntimesAsync(List<long> ids) => App.GetService<IRulesEngineHostedService>().DeleteRuleRuntimesAsync(ids);
|
||||
|
||||
public Task<bool> DeleteRulesAsync(List<long> ids) => App.GetService<IRulesService>().DeleteRulesAsync(ids);
|
||||
|
||||
public Task<bool> DeleteVariableAsync(List<long> ids, bool restart) =>
|
||||
App.GetService<IVariablePageService>().DeleteVariableAsync(ids, restart);
|
||||
|
||||
public Task<LogLevel> DeviceLogLevelAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().DeviceLogLevelAsync(id);
|
||||
|
||||
public Task DeviceRedundantThreadAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().DeviceRedundantThreadAsync(id);
|
||||
|
||||
public Task EditRedundancyOptionAsync(RedundancyOptions input) => App.GetService<IRedundancyService>().EditRedundancyOptionAsync(input);
|
||||
|
||||
public Task EditRuleRuntimesAsync(Rules rules) => App.GetService<IRulesEngineHostedService>().EditRuleRuntimesAsync(rules);
|
||||
|
||||
public Task<USheetDatas> ExportChannelAsync(List<Channel> channels) =>
|
||||
App.GetService<IChannelPageService>().ExportChannelAsync(channels);
|
||||
|
||||
public Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter) =>
|
||||
App.GetService<IChannelPageService>().ExportChannelFileAsync(exportFilter);
|
||||
|
||||
public Task<USheetDatas> ExportDeviceAsync(List<Device> devices) =>
|
||||
App.GetService<IDevicePageService>().ExportDeviceAsync(devices);
|
||||
@@ -213,80 +102,72 @@ public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBa
|
||||
public Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter) =>
|
||||
App.GetService<IDevicePageService>().ExportDeviceFileAsync(exportFilter);
|
||||
|
||||
public Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId) =>
|
||||
App.GetService<IDevicePageService>().OnRedundantDevicesQueryAsync(option, deviceId, channelId);
|
||||
public Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder) =>
|
||||
App.GetService<IVariablePageService>().ExportVariableAsync(models, sortName, sortOrder);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceFileAsync(filePath, restart);
|
||||
public Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter) => App.GetService<IVariablePageService>().ExportVariableFileAsync(exportFilter);
|
||||
|
||||
public Task DeviceRedundantThreadAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().DeviceRedundantThreadAsync(id);
|
||||
public Task<List<Rules>> GetAllRulesAsync() => App.GetService<IRulesService>().GetAllRulesAsync();
|
||||
|
||||
public Task RestartDeviceAsync(long id, bool deleteCache) =>
|
||||
App.GetService<IDevicePageService>().RestartDeviceAsync(id, deleteCache);
|
||||
public Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0) =>
|
||||
App.GetService<IChannelPageService>().GetChannelListAsync(options, max);
|
||||
|
||||
public Task PauseThreadAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().PauseThreadAsync(id);
|
||||
public Task<string> GetChannelNameAsync(long id) =>
|
||||
App.GetService<IChannelPageService>().GetChannelNameAsync(id);
|
||||
|
||||
public Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IDevicePageService>().OnDeviceQueryAsync(options);
|
||||
public Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count) => App.GetService<IGlobalDataService>().GetCurrentUserDeviceSelectedItemsAsync(searchText, startIndex, count);
|
||||
|
||||
public Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count) => App.GetService<IGlobalDataService>().GetCurrentUserDeviceVariableSelectedItemsAsync(deviceText, searchText, startIndex, count);
|
||||
|
||||
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync() => App.GetService<IRealAlarmService>().GetCurrentUserRealAlarmVariablesAsync();
|
||||
|
||||
public Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync() => App.GetService<IDevicePageService>().GetDeviceIdNamesAsync();
|
||||
|
||||
public Task<List<Device>> GetDeviceListAsync(QueryPageOptions option, int v) =>
|
||||
App.GetService<IDevicePageService>().GetDeviceListAsync(option, v);
|
||||
|
||||
public Task<bool> ClearDeviceAsync(bool restart) =>
|
||||
App.GetService<IDevicePageService>().ClearDeviceAsync(restart);
|
||||
|
||||
public Task<bool> IsRedundantDeviceAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().IsRedundantDeviceAsync(id);
|
||||
|
||||
public Task<string> GetDeviceNameAsync(long redundantDeviceId) =>
|
||||
App.GetService<IDevicePageService>().GetDeviceNameAsync(redundantDeviceId);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceAsync(IBrowserFile file, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceAsync(file, restart);
|
||||
|
||||
public Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect) =>
|
||||
App.GetService<IDevicePageService>().GetDeviceItemsAsync(isCollect);
|
||||
|
||||
public Task<string> GetDevicePluginNameAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().GetDevicePluginNameAsync(id);
|
||||
|
||||
public Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart) =>
|
||||
App.GetService<IVariablePageService>().BatchEditVariableAsync(models, oldModel, model, restart);
|
||||
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath) => App.GetService<ITextFileReadService>().GetLogFilesAsync(directoryPath);
|
||||
|
||||
public Task<bool> DeleteVariableAsync(List<long> ids, bool restart) =>
|
||||
App.GetService<IVariablePageService>().DeleteVariableAsync(ids, restart);
|
||||
public Task<List<BackendLog>> GetNewBackendLogAsync() => App.GetService<IBackendLogService>().GetNewBackendLogAsync();
|
||||
public Task<List<RpcLog>> GetNewRpcLogAsync() => App.GetService<IRpcLogService>().GetNewRpcLogAsync();
|
||||
|
||||
public Task<bool> ClearVariableAsync(bool restart) =>
|
||||
App.GetService<IVariablePageService>().ClearVariableAsync(restart);
|
||||
public Task<string> GetPluginNameAsync(long channelId) => App.GetService<IChannelPageService>().GetPluginNameAsync(channelId);
|
||||
|
||||
public Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart) =>
|
||||
App.GetService<IVariablePageService>().InsertTestDataAsync(testVariableCount, testDeviceCount, slaveUrl, businessEnable, restart);
|
||||
public Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null) => App.GetService<IPluginPageService>().GetPluginsAsync(pluginType);
|
||||
|
||||
public Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IVariablePageService>().BatchSaveVariableAsync(input, type, restart);
|
||||
public Task<RedundancyOptions> GetRedundancyAsync() => App.GetService<IRedundancyService>().GetRedundancyAsync();
|
||||
|
||||
public Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IVariablePageService>().SaveVariableAsync(input, type, restart);
|
||||
|
||||
public Task CopyVariableAsync(List<Variable> model, int copyCount, string copyVariableNamePrefix, int copyVariableNameSuffixNumber, bool restart) =>
|
||||
App.GetService<IVariablePageService>().CopyVariableAsync(model, copyCount, copyVariableNamePrefix, copyVariableNameSuffixNumber, restart);
|
||||
|
||||
public Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IVariablePageService>().OnVariableQueryAsync(options);
|
||||
public Task<Rules> GetRuleRuntimesAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().GetRuleRuntimesAsync(rulesId);
|
||||
|
||||
public Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int v) =>
|
||||
App.GetService<IVariablePageService>().GetVariableListAsync(option, v);
|
||||
|
||||
public Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder) =>
|
||||
App.GetService<IVariablePageService>().ExportVariableAsync(models, sortName, sortOrder);
|
||||
public Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelAsync(upData, insertData, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart) =>
|
||||
App.GetService<IVariablePageService>().ImportVariableUSheetDatasAsync(data, restart);
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(IBrowserFile file, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelAsync(file, restart);
|
||||
|
||||
public Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData) =>
|
||||
App.GetService<IVariablePageService>().OnWriteVariableAsync(id, writeData);
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelFileAsync(filePath, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart) =>
|
||||
App.GetService<IChannelPageService>().ImportChannelUSheetDatasAsync(input, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceAsync(IBrowserFile file, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceAsync(file, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceFileAsync(filePath, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart) =>
|
||||
App.GetService<IDevicePageService>().ImportDeviceUSheetDatasAsync(input, restart);
|
||||
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableAsync(IBrowserFile a, bool restart) =>
|
||||
App.GetService<IVariablePageService>().ImportVariableAsync(a, restart);
|
||||
@@ -294,7 +175,113 @@ public partial class ManagementRpcServer : IRpcServer, IManagementRpcServer, IBa
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart) =>
|
||||
App.GetService<IVariablePageService>().ImportVariableFileAsync(filePath, restart);
|
||||
|
||||
public Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter) => App.GetService<IVariablePageService>().ExportVariableFileAsync(exportFilter);
|
||||
public Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart) =>
|
||||
App.GetService<IVariablePageService>().ImportVariableUSheetDatasAsync(data, restart);
|
||||
|
||||
public Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync() => App.GetService<IDevicePageService>().GetDeviceIdNamesAsync();
|
||||
public Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart) =>
|
||||
App.GetService<IVariablePageService>().InsertTestDataAsync(testVariableCount, testDeviceCount, slaveUrl, businessEnable, restart);
|
||||
|
||||
public Task<bool> IsRedundantDeviceAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().IsRedundantDeviceAsync(id);
|
||||
|
||||
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200) => App.GetService<ITextFileReadService>().LastLogDataAsync(file, lineCount);
|
||||
|
||||
public Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IChannelPageService>().OnChannelQueryAsync(options);
|
||||
|
||||
public Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option) =>
|
||||
App.GetService<IChannelPageService>().OnChannelSelectedItemQueryAsync(option);
|
||||
|
||||
public Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IDevicePageService>().OnDeviceQueryAsync(options);
|
||||
|
||||
public Task<QueryData<SelectedItem>> OnDeviceSelectedItemQueryAsync(VirtualizeQueryOption option, bool isCollect) =>
|
||||
App.GetService<IDevicePageService>().OnDeviceSelectedItemQueryAsync(option, isCollect);
|
||||
|
||||
public Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId) =>
|
||||
App.GetService<IDevicePageService>().OnRedundantDevicesQueryAsync(option, deviceId, channelId);
|
||||
|
||||
public Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options) =>
|
||||
App.GetService<IVariablePageService>().OnVariableQueryAsync(options);
|
||||
|
||||
public Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData) =>
|
||||
App.GetService<IVariablePageService>().OnWriteVariableAsync(id, writeData);
|
||||
|
||||
public Task PauseThreadAsync(long id) =>
|
||||
App.GetService<IDevicePageService>().PauseThreadAsync(id);
|
||||
|
||||
public Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null) => App.GetService<IPluginPageService>().PluginPageAsync(options, pluginTypeEnum);
|
||||
|
||||
public Task RedundancyForcedSync() => App.GetService<IRedundancyHostedService>().RedundancyForcedSync();
|
||||
|
||||
public Task<LogLevel> RedundancyLogLevelAsync() => App.GetService<IRedundancyHostedService>().RedundancyLogLevelAsync();
|
||||
|
||||
public Task<string> RedundancyLogPathAsync() => App.GetService<IRedundancyHostedService>().RedundancyLogPathAsync();
|
||||
|
||||
public Task ReloadPluginAsync() => App.GetService<IPluginPageService>().ReloadPluginAsync();
|
||||
|
||||
public Task RestartChannelAsync(long channelId) =>
|
||||
App.GetService<IChannelPageService>().RestartChannelAsync(channelId);
|
||||
|
||||
public Task RestartChannelsAsync() =>
|
||||
App.GetService<IChannelPageService>().RestartChannelsAsync();
|
||||
|
||||
public Task RestartDeviceAsync(long id, bool deleteCache) =>
|
||||
App.GetService<IDevicePageService>().RestartDeviceAsync(id, deleteCache);
|
||||
|
||||
public Task RestartServerAsync() => App.GetService<IRestartService>().RestartServerAsync();
|
||||
|
||||
public async Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas)
|
||||
{
|
||||
var data = await GlobalData.RpcService.InvokeDeviceMethodAsync($"Management[{(callContext.Caller is ITcpSession tcpSession ? tcpSession.GetIPPort() : string.Empty)}]", deviceDatas, callContext.Token).ConfigureAwait(false);
|
||||
|
||||
return data.ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Key, b => b.Value.GetOperResult()));
|
||||
}
|
||||
public Task<QueryData<RpcLog>> RpcLogPageAsync(QueryPageOptions option) => App.GetService<IRpcLogService>().RpcLogPageAsync(option);
|
||||
|
||||
public Task<List<RpcLogDayStatisticsOutput>> RpcLogStatisticsByDayAsync(int day) => App.GetService<IRpcLogService>().RpcLogStatisticsByDayAsync(day);
|
||||
public Task<TouchSocket.Core.LogLevel> RulesLogLevelAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().RulesLogLevelAsync(rulesId);
|
||||
|
||||
public Task<string> RulesLogPathAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().RulesLogPathAsync(rulesId);
|
||||
|
||||
public Task<QueryData<Rules>> RulesPageAsync(QueryPageOptions option, FilterKeyValueAction filterKeyValueAction = null) => App.GetService<IRulesService>().RulesPageAsync(option, filterKeyValueAction);
|
||||
|
||||
public Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IChannelPageService>().SaveChannelAsync(input, type, restart);
|
||||
|
||||
public Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IDevicePageService>().SaveDeviceAsync(input, type, restart);
|
||||
|
||||
public Task SavePluginByPathAsync(PluginAddPathInput plugin) => App.GetService<IPluginPageService>().SavePluginByPathAsync(plugin);
|
||||
|
||||
public Task<bool> SaveRulesAsync(Rules input, ItemChangedType type) => App.GetService<IRulesService>().SaveRulesAsync(input, type);
|
||||
|
||||
public Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart) =>
|
||||
App.GetService<IVariablePageService>().SaveVariableAsync(input, type, restart);
|
||||
|
||||
public Task SetChannelLogLevelAsync(long id, LogLevel logLevel) =>
|
||||
App.GetService<IChannelPageService>().SetChannelLogLevelAsync(id, logLevel);
|
||||
|
||||
public Task SetDeviceLogLevelAsync(long id, LogLevel logLevel) =>
|
||||
App.GetService<IDevicePageService>().SetDeviceLogLevelAsync(id, logLevel);
|
||||
|
||||
public Task SetRedundancyLogLevelAsync(LogLevel logLevel) => App.GetService<IRedundancyHostedService>().SetRedundancyLogLevelAsync(logLevel);
|
||||
|
||||
public Task SetRulesLogLevelAsync(long rulesId, TouchSocket.Core.LogLevel logLevel) => App.GetService<IRulesEngineHostedService>().SetRulesLogLevelAsync(rulesId, logLevel);
|
||||
|
||||
public Task<bool> StartBusinessChannelEnableAsync() => App.GetService<IChannelEnableService>().StartBusinessChannelEnableAsync();
|
||||
|
||||
public Task<bool> StartCollectChannelEnableAsync() => App.GetService<IChannelEnableService>().StartCollectChannelEnableAsync();
|
||||
|
||||
public Task StartRedundancyTaskAsync() => App.GetService<IRedundancyHostedService>().StartRedundancyTaskAsync();
|
||||
|
||||
public Task StopRedundancyTaskAsync() => App.GetService<IRedundancyHostedService>().StopRedundancyTaskAsync();
|
||||
|
||||
public Task<AuthorizeInfo> TryAuthorizeAsync(string password) => App.GetService<IAuthenticationService>().TryAuthorizeAsync(password);
|
||||
|
||||
public Task<AuthorizeInfo> TryGetAuthorizeInfoAsync() => App.GetService<IAuthenticationService>().TryGetAuthorizeInfoAsync();
|
||||
|
||||
public Task UnAuthorizeAsync() => App.GetService<IAuthenticationService>().UnAuthorizeAsync();
|
||||
|
||||
public Task<string> UUIDAsync() => App.GetService<IAuthenticationService>().UUIDAsync();
|
||||
}
|
||||
|
||||
@@ -103,6 +103,10 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
store.RegisterServer<IManagementRpcServer>(new ManagementRpcServer());
|
||||
store.RegisterServer<IUpgradeRpcServer>(new UpgradeRpcServer());
|
||||
|
||||
foreach (var type in App.EffectiveTypes.Where(p => typeof(IPluginRpcServer).IsAssignableFrom(p) && !p.IsAbstract && p.IsClass))
|
||||
{
|
||||
store.RegisterServer(type);
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
@@ -162,7 +166,10 @@ public partial class ManagementTask : AsyncDisposableObject
|
||||
{
|
||||
store.RegisterServer<IManagementRpcServer>(new ManagementRpcServer());
|
||||
store.RegisterServer<IUpgradeRpcServer>(new UpgradeRpcServer());
|
||||
|
||||
foreach (var type in App.EffectiveTypes.Where(p => typeof(IPluginRpcServer).IsAssignableFrom(p) && !p.IsAbstract && p.IsClass))
|
||||
{
|
||||
store.RegisterServer(type);
|
||||
}
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
@@ -30,12 +30,13 @@ public static class FileServerHelpers
|
||||
|
||||
var metadata = new Metadata();//传递到服务器的元数据
|
||||
metadata.Add(FileConst.FilePathKey, path);
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(300));
|
||||
var fileOperator = new FileOperator//实例化本次传输的控制器,用于获取传输进度、速度、状态等。
|
||||
{
|
||||
SavePath = savePath,//客户端本地保存路径
|
||||
ResourcePath = path,//请求文件的资源路径
|
||||
Metadata = metadata,//传递到服务器的元数据
|
||||
Timeout = TimeSpan.FromSeconds(60),//传输超时时长
|
||||
Token = cts.Token,
|
||||
TryCount = 10,//当遇到失败时,尝试次数
|
||||
FileSectionSize = 1024 * 512//分包大小,当网络较差时,应该适当减小该值
|
||||
};
|
||||
@@ -75,12 +76,13 @@ public static class FileServerHelpers
|
||||
var metadata = new Metadata();//传递到服务器的元数据
|
||||
metadata.Add(FileConst.FilePathKey, serverPath);
|
||||
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(300));
|
||||
var fileOperator = new FileOperator//实例化本次传输的控制器,用于获取传输进度、速度、状态等。
|
||||
{
|
||||
SavePath = serverPath,//服务器本地保存路径
|
||||
ResourcePath = resourcePath,//客户端本地即将上传文件的资源路径
|
||||
Metadata = metadata,//传递到服务器的元数据
|
||||
Timeout = TimeSpan.FromSeconds(60),//传输超时时长
|
||||
Token = cts.Token,
|
||||
TryCount = 10,//当遇到失败时,尝试次数
|
||||
FileSectionSize = 1024 * 512//分包大小,当网络较差时,应该适当减小该值
|
||||
};
|
||||
|
||||
@@ -11,18 +11,22 @@
|
||||
|
||||
using TouchSocket.Dmtp.Rpc;
|
||||
using TouchSocket.Rpc;
|
||||
using TouchSocket.WebApi;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
#if Management
|
||||
[GeneratorRpcProxy(GeneratorFlag = GeneratorFlag.ExtensionAsync)]
|
||||
#endif
|
||||
[TouchSocket.WebApi.Router("/miniapi/upgrade/[action]")]
|
||||
public interface IUpgradeRpcServer : IRpcServer
|
||||
{
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Post)]
|
||||
Task UpgradeAsync(ICallContext callContext, UpdateZipFile updateZipFile);
|
||||
|
||||
[DmtpRpc]
|
||||
[WebApi(Method = HttpMethodType.Get)]
|
||||
Task<UpdateZipFileInput> GetUpdateZipFileInputAsync(ICallContext callContext);
|
||||
|
||||
}
|
||||
@@ -11,7 +11,6 @@
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using TouchSocket.Core;
|
||||
@@ -89,6 +88,14 @@ public partial class WebApiTask : AsyncDisposableObject
|
||||
store.RegisterServer<ControlController>();
|
||||
store.RegisterServer<RuntimeInfoController>();
|
||||
store.RegisterServer<TestController>();
|
||||
|
||||
store.RegisterServer<IManagementRpcServer>(new ManagementRpcServer());
|
||||
store.RegisterServer<IUpgradeRpcServer>(new UpgradeRpcServer());
|
||||
|
||||
foreach (var type in App.EffectiveTypes.Where(p => typeof(IPluginRpcServer).IsAssignableFrom(p) && !p.IsAbstract && p.IsClass))
|
||||
{
|
||||
store.RegisterServer(type);
|
||||
}
|
||||
});
|
||||
|
||||
//添加跨域服务
|
||||
@@ -112,9 +119,12 @@ public partial class WebApiTask : AsyncDisposableObject
|
||||
|
||||
a.UseWebApi();
|
||||
|
||||
if (App.WebHostEnvironment.IsDevelopment() || Debugger.IsAttached)
|
||||
a.UseSwagger();
|
||||
|
||||
#if DEBUG
|
||||
a.UseSwagger().SetPrefix("api");
|
||||
#else
|
||||
if (App.WebHostEnvironment.IsDevelopment())
|
||||
a.UseSwagger().SetPrefix("api");
|
||||
#endif
|
||||
a.UseDefaultHttpServicePlugin();
|
||||
});
|
||||
|
||||
@@ -194,7 +204,6 @@ class AuthenticationPlugin : PluginBase, IHttpPlugin
|
||||
var username = credentials[0];
|
||||
var password = credentials[1];
|
||||
|
||||
// 这里验证用户名和密码,实际项目中应该从数据库验证
|
||||
if (username != _webApiOptions.UserName || password != _webApiOptions.Password)
|
||||
{
|
||||
e.Context.Response.Headers.Add("WWW-Authenticate", "Basic realm=\"ThingsGateway\"");
|
||||
|
||||
@@ -53,6 +53,7 @@ public class AlarmChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
GlobalData.ReadOnlyRealAlarmIdVariables?.ForEach(a => AlarmHostedService_OnAlarmChanged(a.Value));
|
||||
GlobalData.AlarmChangedEvent += AlarmHostedService_OnAlarmChanged;
|
||||
}
|
||||
|
||||
private static void AlarmHostedService_OnAlarmChanged(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (AlarmChangedTriggerNodeDict.TryGetValue(alarmVariable.DeviceName, out var alarmNodeDict) &&
|
||||
|
||||
@@ -39,6 +39,7 @@ public class DeviceChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
|
||||
static DeviceChangedTriggerNode()
|
||||
{
|
||||
GlobalData.DeviceStatusChangeEvent -= GlobalData_DeviceStatusChangeEvent;
|
||||
Task.Factory.StartNew(RunAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
GlobalData.DeviceStatusChangeEvent += GlobalData_DeviceStatusChangeEvent;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
using ThingsGateway.Blazor.Diagrams.Core.Geometry;
|
||||
using ThingsGateway.Common.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Application;
|
||||
|
||||
[CategoryNode(Category = "Trigger", ImgUrl = $"{INode.ModuleBasePath}img/ValueChanged.svg", Desc = nameof(PluginEventChangedTriggerNode), LocalizerType = typeof(ThingsGateway.Gateway.Application.INode), WidgetType = $"ThingsGateway.Gateway.Razor.DeviceWidget,{INode.FileModulePath}")]
|
||||
public class PluginEventChangedTriggerNode : TextNode, ITriggerNode, IDisposable
|
||||
{
|
||||
public PluginEventChangedTriggerNode(string id, Point? position = null) : base(id, position) { Title = "PluginEventChangedTriggerNode"; Placeholder = "Device.Placeholder"; }
|
||||
|
||||
|
||||
#if !Management
|
||||
private Func<NodeOutput, CancellationToken, Task> Func { get; set; }
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
Func = func;
|
||||
FuncDict.Add(this, func);
|
||||
if (!DeviceChangedTriggerNodeDict.TryGetValue(Text ?? string.Empty, out var list))
|
||||
{
|
||||
var deviceChangedTriggerNodes = new ConcurrentList<PluginEventChangedTriggerNode>();
|
||||
deviceChangedTriggerNodes.Add(this);
|
||||
DeviceChangedTriggerNodeDict.Add(Text, deviceChangedTriggerNodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(this);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
public static Dictionary<string, ConcurrentList<PluginEventChangedTriggerNode>> DeviceChangedTriggerNodeDict = new();
|
||||
public static Dictionary<PluginEventChangedTriggerNode, Func<NodeOutput, CancellationToken, Task>> FuncDict = new();
|
||||
|
||||
public static BlockingCollection<PluginEventData> DeviceDatas = new();
|
||||
|
||||
static PluginEventChangedTriggerNode()
|
||||
{
|
||||
GlobalData.PluginEventHandler -= GlobalData_PluginEventHandler;
|
||||
Task.Factory.StartNew(RunAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
GlobalData.PluginEventHandler += GlobalData_PluginEventHandler;
|
||||
}
|
||||
|
||||
private static void GlobalData_PluginEventHandler(PluginEventData pluginEventData)
|
||||
{
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(pluginEventData.DeviceName ?? string.Empty, out var deviceChangedTriggerNodes) && deviceChangedTriggerNodes?.Count > 0)
|
||||
{
|
||||
if (!DeviceDatas.IsAddingCompleted)
|
||||
{
|
||||
try
|
||||
{
|
||||
DeviceDatas.Add(pluginEventData);
|
||||
return;
|
||||
}
|
||||
catch (InvalidOperationException) { }
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static Task RunAsync()
|
||||
{
|
||||
return DeviceDatas.GetConsumingEnumerable().ParallelForEachStreamedAsync((async (plgunEventData, token) =>
|
||||
{
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(plgunEventData.DeviceName ?? string.Empty, out var valueChangedTriggerNodes))
|
||||
{
|
||||
await valueChangedTriggerNodes.ParallelForEachAsync(async (item, token) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (FuncDict.TryGetValue(item, out var func))
|
||||
{
|
||||
item.Logger?.Trace($"Device changed: {item.Text}");
|
||||
await func.Invoke(new NodeOutput() { Value = plgunEventData }, token).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
item.Logger?.LogWarning(ex);
|
||||
}
|
||||
}, token).ConfigureAwait(false);
|
||||
}
|
||||
}), default);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FuncDict.Remove(this);
|
||||
if (DeviceChangedTriggerNodeDict.TryGetValue(Text ?? string.Empty, out var list))
|
||||
{
|
||||
list.Remove(this);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
Task ITriggerNode.StartAsync(Func<NodeOutput, CancellationToken, Task> func, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
@@ -46,6 +46,7 @@ public class ValueChangedTriggerNode : VariableNode, ITriggerNode, IDisposable
|
||||
public static BlockingCollection<VariableBasicData> VariableBasicDatas = new();
|
||||
static ValueChangedTriggerNode()
|
||||
{
|
||||
GlobalData.VariableValueChangeEvent -= GlobalData_VariableValueChangeEvent;
|
||||
Task.Factory.StartNew(RunAsync, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default);
|
||||
GlobalData.VariableValueChangeEvent += GlobalData_VariableValueChangeEvent;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public interface IRulesService
|
||||
/// 从缓存/数据库获取全部信息
|
||||
/// </summary>
|
||||
/// <returns>规则列表</returns>
|
||||
Task<List<Rules>> GetAllAsync();
|
||||
Task<List<Rules>> GetAllRulesAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 报表查询
|
||||
|
||||
@@ -240,7 +240,7 @@ internal sealed class RulesEngineHostedService : BackgroundService, IRulesEngine
|
||||
TokenSource ??= new CancellationTokenSource();
|
||||
Clear();
|
||||
|
||||
Rules = await App.GetService<IRulesService>().GetAllAsync().ConfigureAwait(false);
|
||||
Rules = await App.GetService<IRulesService>().GetAllRulesAsync().ConfigureAwait(false);
|
||||
Diagrams = new();
|
||||
foreach (var rules in Rules.Where(a => a.Status))
|
||||
{
|
||||
|
||||
@@ -39,7 +39,7 @@ internal sealed class RulesService : BaseService<Rules>, IRulesService
|
||||
|
||||
using var db = GetDB();
|
||||
|
||||
var data = (await GetAllAsync().ConfigureAwait(false))
|
||||
var data = (await GetAllRulesAsync().ConfigureAwait(false))
|
||||
.WhereIf(dataScope != null && dataScope?.Count > 0, u => dataScope.Contains(u.CreateOrgId))//在指定机构列表查询
|
||||
.WhereIf(dataScope?.Count == 0, u => u.CreateUserId == UserManager.UserId)
|
||||
.Select(a => a.Id).ToList();
|
||||
@@ -75,7 +75,7 @@ internal sealed class RulesService : BaseService<Rules>, IRulesService
|
||||
/// 从缓存/数据库获取全部信息
|
||||
/// </summary>
|
||||
/// <returns>列表</returns>
|
||||
public async Task<List<Rules>> GetAllAsync()
|
||||
public async Task<List<Rules>> GetAllRulesAsync()
|
||||
{
|
||||
var channels = App.CacheService.Get<List<Rules>>(cacheKey);
|
||||
if (channels == null)
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
|
||||
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.3" />
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="4.0.0-beta.1" />
|
||||
<PackageReference Include="TouchSocket.WebApi.Swagger" Version="4.0.0-beta.1" />
|
||||
<PackageReference Include="TouchSocket.Dmtp" Version="4.0.0-beta.5" />
|
||||
<PackageReference Include="TouchSocket.WebApi.Swagger" Version="4.0.0-beta.5" />
|
||||
<PackageReference Include="ThingsGateway.Authentication" Version="$(AuthenticationVersion)" />
|
||||
<!--<ProjectReference Include="..\..\PluginPro\ThingsGateway.Authentication\ThingsGateway.Authentication.csproj" />-->
|
||||
|
||||
|
||||
@@ -73,12 +73,22 @@ public partial class QuickActions
|
||||
|
||||
private List<SelectedItem> AutoRestartThreadBoolItems;
|
||||
|
||||
private static async Task Restart()
|
||||
[Inject]
|
||||
IChannelPageService ChannelPageService { get; set; }
|
||||
|
||||
#if Management
|
||||
[Inject]
|
||||
Management.Application.DmtpActorContext DmtpActorContext { get; set; }
|
||||
#endif
|
||||
private async Task Restart()
|
||||
{
|
||||
#if Management
|
||||
if (DmtpActorContext.Current == null)
|
||||
return;
|
||||
#endif
|
||||
await Task.Run(async () =>
|
||||
{
|
||||
var data = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
|
||||
await GlobalData.ChannelRuntimeService.RestartChannelAsync(data.ToList());
|
||||
await ChannelPageService.RestartChannelsAsync();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
|
||||
"Actuator": "Actuator",
|
||||
"AlarmChangedTriggerNode": "AlarmStateTrigger",
|
||||
"PluginEventChangedTriggerNode": "PluginEventTrigger",
|
||||
"BusinessNode": "BusinessDeviceExecution",
|
||||
"BusinessNode.Placeholder": "BusinessDeviceName",
|
||||
"Cancel": "Cancel",
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
|
||||
"Actuator": "执行",
|
||||
"AlarmChangedTriggerNode": "报警状态触发器",
|
||||
"PluginEventChangedTriggerNode": "插件事件触发器",
|
||||
"BusinessNode": "业务设备执行",
|
||||
"BusinessNode.Placeholder": "设备名称",
|
||||
"Cancel": "取消",
|
||||
|
||||
@@ -8,8 +8,12 @@
|
||||
// QQ群:605534569
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
using System.Text;
|
||||
|
||||
using ThingsGateway.NewLife.Json.Extension;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Gateway.Razor;
|
||||
public partial class PropertyComponent : IPropertyUIBase
|
||||
{
|
||||
@@ -69,11 +73,16 @@ public partial class PropertyComponent : IPropertyUIBase
|
||||
{nameof(ScriptCheck.Script),script },
|
||||
{nameof(ScriptCheck.GetResult), (string input,string script)=>
|
||||
{
|
||||
StringBuilder stringBuilder=new();
|
||||
var logger=new EasyLogger((a)=>stringBuilder.AppendLine(a));
|
||||
|
||||
|
||||
var type= script == businessProperty.BigTextScriptDeviceModel?typeof(List<DeviceBasicData>):script ==businessProperty.BigTextScriptAlarmModel?typeof(List<AlarmVariable>):typeof(List<VariableBasicData>);
|
||||
|
||||
var data = (IEnumerable<object>)Newtonsoft.Json.JsonConvert.DeserializeObject(input, type);
|
||||
var value = data.GetDynamicModel(script);
|
||||
return Task.FromResult( value.ToSystemTextJsonString());
|
||||
var value = data.GetDynamicModel(script,logger);
|
||||
stringBuilder.AppendLine(value.ToSystemTextJsonString());
|
||||
return Task.FromResult( stringBuilder.ToString());
|
||||
}},
|
||||
|
||||
{nameof(ScriptCheck.OnGetDemo),()=>
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
<EditTemplate Context="value">
|
||||
<div class="col-12 col-md-6 ">
|
||||
<BootstrapInputGroup>
|
||||
<Select IsVirtualize @bind-Value="@value.DeviceId" DefaultVirtualizeItemText=@DeviceName IsDisabled=BatchEditEnable Items="@CollectDeviceItems" OnSelectedItemChanged=OnDeviceChanged ShowSearch="true" ShowLabel="true" />
|
||||
<Select IsVirtualize @bind-Value="@value.DeviceId" DefaultVirtualizeItemText=@DeviceName IsDisabled=BatchEditEnable OnQueryAsync=OnCollectDeviceSelectedItemQueryAsync OnSelectedItemChanged=OnDeviceChanged ShowSearch="true" ShowLabel="true" />
|
||||
<Button class="text-end" Icon="fa-solid fa-plus" OnClick="AddDevice" IsDisabled=BatchEditEnable></Button>
|
||||
</BootstrapInputGroup>
|
||||
</div>
|
||||
@@ -198,7 +198,7 @@
|
||||
<div class="row g-2 mx-1 form-inline">
|
||||
|
||||
<div class="col-12 col-md-8">
|
||||
<Select SkipValidate IsVirtualize DefaultVirtualizeItemText=@(ChoiceBusinessDeviceName) @bind-Value="@ChoiceBusinessDeviceId" Items="@BusinessDeviceItems" ShowSearch="true" ShowLabel="true" />
|
||||
<Select SkipValidate IsVirtualize @bind-Value="@ChoiceBusinessDeviceId" DefaultVirtualizeItemText=@ChoiceBusinessDeviceName IsDisabled=BatchEditEnable OnQueryAsync=OnBusinessDeviceSelectedItemQueryAsync ShowSearch="true" ShowLabel="true" />
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<Button OnClick="async() =>{
|
||||
|
||||
@@ -41,9 +41,9 @@ public partial class VariableEditComponent
|
||||
[Parameter]
|
||||
public bool BatchEditEnable { get; set; }
|
||||
|
||||
private IEnumerable<SelectedItem> BusinessDeviceItems { get; set; }
|
||||
//private IEnumerable<SelectedItem> BusinessDeviceItems { get; set; }
|
||||
|
||||
private IEnumerable<SelectedItem> CollectDeviceItems { get; set; }
|
||||
//private IEnumerable<SelectedItem> CollectDeviceItems { get; set; }
|
||||
[Inject]
|
||||
IDevicePageService DevicePageService { get; set; }
|
||||
private string DeviceName;
|
||||
@@ -59,10 +59,14 @@ public partial class VariableEditComponent
|
||||
OnInitialized();
|
||||
await OnInitializedAsync();
|
||||
OnParametersSet();
|
||||
StateHasChanged();
|
||||
|
||||
ChoiceBusinessDeviceId = ChoiceBusinessDeviceId > 0 ? ChoiceBusinessDeviceId : (await DevicePageService.OnDeviceSelectedItemQueryAsync(new VirtualizeQueryOption() { Count = 1 }, false).ConfigureAwait(false)).Items.FirstOrDefault()?.Value?.ToLong() ?? 0;
|
||||
ChoiceBusinessDeviceName = (await DevicePageService.GetDeviceNameAsync(ChoiceBusinessDeviceId)) ?? string.Empty;
|
||||
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
await OnParametersSetAsync();
|
||||
ChoiceBusinessDeviceId = ChoiceBusinessDeviceId > 0 ? ChoiceBusinessDeviceId : BusinessDeviceItems.FirstOrDefault()?.Value?.ToLong() ?? 0;
|
||||
ChoiceBusinessDeviceName = await DevicePageService.GetDeviceNameAsync(ChoiceBusinessDeviceId);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -72,8 +76,6 @@ public partial class VariableEditComponent
|
||||
|
||||
protected override async Task OnParametersSetAsync()
|
||||
{
|
||||
CollectDeviceItems = await DevicePageService.GetDeviceItemsAsync(true);
|
||||
BusinessDeviceItems = await DevicePageService.GetDeviceItemsAsync(false);
|
||||
|
||||
if (Model.DeviceId > 0 && AddressUIType == null)
|
||||
{
|
||||
@@ -82,6 +84,14 @@ public partial class VariableEditComponent
|
||||
|
||||
await base.OnParametersSetAsync();
|
||||
}
|
||||
private Task<QueryData<SelectedItem>> OnCollectDeviceSelectedItemQueryAsync(VirtualizeQueryOption option)
|
||||
{
|
||||
return DevicePageService.OnDeviceSelectedItemQueryAsync(option, true);
|
||||
}
|
||||
private Task<QueryData<SelectedItem>> OnBusinessDeviceSelectedItemQueryAsync(VirtualizeQueryOption option)
|
||||
{
|
||||
return DevicePageService.OnDeviceSelectedItemQueryAsync(option, false);
|
||||
}
|
||||
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
|
||||
@@ -111,9 +111,12 @@
|
||||
{
|
||||
"Url": "/InovanceMaster",
|
||||
"Text": "InovanceMaster"
|
||||
},
|
||||
{
|
||||
"Url": "/OpcAeMaster",
|
||||
"Text": "OpcAeMaster"
|
||||
}
|
||||
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ public class Dlt645_2007Send : ISendMessage
|
||||
lenSpan.WriteValue<byte>((byte)(length - 1));//数据域长度
|
||||
|
||||
int num = 0;
|
||||
for (int index = 0; index < byteBlock.WrittenCount; ++index)
|
||||
for (int index = 0; index < byteBlock.WrittenCount - SendHeadCodeIndex; ++index)
|
||||
num += span[index];
|
||||
WriterExtension.WriteValue(ref byteBlock, (byte)num);//校验码,总加和
|
||||
WriterExtension.WriteValue(ref byteBlock, (byte)0x16);//结束符
|
||||
|
||||
@@ -144,6 +144,7 @@ public class S7Send : ISendMessage
|
||||
|
||||
internal void GetWriteByteCommand<TByteBlock>(ref TByteBlock byteBlock, SiemensS7Address[] addresss) where TByteBlock : IBytesWriter
|
||||
{
|
||||
var span = byteBlock.GetSpan(1024);
|
||||
byte itemLen = (byte)addresss.Length;
|
||||
ushort parameterLen = (ushort)(itemLen * 12 + 2);
|
||||
//TPKT
|
||||
@@ -201,7 +202,10 @@ public class S7Send : ISendMessage
|
||||
}
|
||||
ushort telegramLen = (ushort)(itemLen * 12 + 19 + dataLen);
|
||||
|
||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (ushort)telegramLen, EndianType.Big, 2);//长度
|
||||
ByteBlockExtension.WriteBackValue(ref byteBlock, (ushort)dataLen, EndianType.Big, 15);//长度
|
||||
TouchSocketBitConverter.GetBitConverter(EndianType.Big).WriteBytes(span.Slice(2), (ushort)telegramLen);
|
||||
//ByteBlockExtension.WriteBackValue(ref byteBlock, (ushort)telegramLen, EndianType.Big, 2);//长度
|
||||
TouchSocketBitConverter.GetBitConverter(EndianType.Big).WriteBytes(span.Slice(15), (ushort)dataLen);
|
||||
|
||||
//ByteBlockExtension.WriteBackValue(ref byteBlock, (ushort)dataLen, EndianType.Big, 15);//长度
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public abstract class DynamicSQLBase
|
||||
/// <returns></returns>
|
||||
public virtual Task DBInit(ISqlSugarClient db, CancellationToken cancellationToken)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1,60 +0,0 @@
|
||||
////------------------------------------------------------------------------------
|
||||
////此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
//// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
//// 使用文档:https://thingsgateway.cn/
|
||||
//// QQ群:605534569
|
||||
//// ------------------------------------------------------------------------------
|
||||
|
||||
//using ThingsGateway.SqlSugar;
|
||||
|
||||
//using ThingsGateway;
|
||||
//using ThingsGateway.Foundation;
|
||||
//using ThingsGateway.NewLife.Extension;
|
||||
//using ThingsGateway.Plugin.DB;
|
||||
//using ThingsGateway.Plugin.SqlDB;
|
||||
|
||||
//public class TestHistorySQL : DynamicSQLBase
|
||||
//{
|
||||
// public override IEnumerable<dynamic> GetList(IEnumerable<object> datas)
|
||||
// {
|
||||
// var sqlHistoryValues = datas.Cast<SQLHistoryValue>().OrderByDescending(a => a.CollectTime).DistinctBy(a => a.Name);
|
||||
// List<HistoryModel1> demoDatas = new List<HistoryModel1>();
|
||||
// HistoryModel1 demoData = new HistoryModel1();
|
||||
// demoData.IsOnline = !sqlHistoryValues.Any(a => !a.IsOnline);
|
||||
// demoData.CreateTime = DateTime.Now;
|
||||
// var dict = sqlHistoryValues.ToDictionary(a => a.Name);
|
||||
// demoData.Temp1 = dict["Device1_Temp1"].Value;
|
||||
// demoData.Temp2 = dict["Device1_Temp2"].Value;
|
||||
// demoData.Temp3 = dict["Device1_Temp3"].Value;
|
||||
// demoDatas.Add(demoData);
|
||||
// return demoDatas;
|
||||
// }
|
||||
|
||||
// public override Type GetModelType()
|
||||
// {
|
||||
// return typeof(HistoryModel1);
|
||||
// }
|
||||
// [SplitTable(SplitType.Month)]//(自带分表支持 年、季、月、周、日)
|
||||
// [SugarTable("HistoryModel1_{year}{month}{day}", TableDescription = "设备采集历史表")]//3个变量必须要有
|
||||
// [SugarIndex("index_CreateTime", nameof(SQLHistoryValue.CreateTime), OrderByType.Desc)]
|
||||
// public class HistoryModel1
|
||||
// {
|
||||
// [SplitField] //分表字段 在插入的时候会根据这个字段插入哪个表,在更新删除的时候用这个字段找出相关表
|
||||
// public DateTime CreateTime { get; set; }
|
||||
|
||||
// ///<summary>
|
||||
// ///是否在线
|
||||
// ///</summary>
|
||||
// [SugarColumn(ColumnDescription = "是否在线")]
|
||||
// public bool IsOnline { get; set; }
|
||||
|
||||
// public string Temp1 { get; set; }
|
||||
|
||||
// public string Temp2 { get; set; }
|
||||
|
||||
// public string Temp3 { get; set; }
|
||||
// }
|
||||
//}
|
||||
@@ -1,65 +0,0 @@
|
||||
//// ------------------------------------------------------------------------------
|
||||
//// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
//// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
//// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
//// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
//// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
//// 使用文档:https://thingsgateway.cn/
|
||||
//// QQ群:605534569
|
||||
//// ------------------------------------------------------------------------------
|
||||
|
||||
//using ThingsGateway.SqlSugar;
|
||||
|
||||
//using ThingsGateway;
|
||||
//using ThingsGateway.Foundation;
|
||||
//using ThingsGateway.NewLife.Extension;
|
||||
//using ThingsGateway.Plugin.DB;
|
||||
//using ThingsGateway.Plugin.SqlDB;
|
||||
|
||||
//public class TestRealSQL : DynamicSQLBase
|
||||
//{
|
||||
// public override IEnumerable<dynamic> GetList(IEnumerable<object> datas)
|
||||
// {
|
||||
// var sqlRealValues = datas.Cast<SQLRealValue>().OrderByDescending(a => a.CollectTime).DistinctBy(a => a.Name);
|
||||
// List<RealModel1> demoDatas = new List<RealModel1>();
|
||||
// RealModel1 demoData = new RealModel1();
|
||||
// demoData.IsOnline = !sqlRealValues.Any(a => !a.IsOnline);
|
||||
// demoData.CollectTime = sqlRealValues.Select(a => a.CollectTime).Max();
|
||||
// demoData.EmptyId = 1;
|
||||
// var dict = sqlRealValues.ToDictionary(a => a.Name);
|
||||
// demoData.Temp1 = dict["Device1_Temp1"].Value;
|
||||
// demoData.Temp2 = dict["Device1_Temp2"].Value;
|
||||
// demoData.Temp3 = dict["Device1_Temp3"].Value;
|
||||
// demoDatas.Add(demoData);
|
||||
// return demoDatas;
|
||||
// }
|
||||
|
||||
// public override Type GetModelType()
|
||||
// {
|
||||
// return typeof(RealModel1);
|
||||
|
||||
// }
|
||||
|
||||
// [SugarTable(TableName = "RealModel1", TableDescription = "设备采集实时表")]
|
||||
// [SugarIndex("{table}_index_CollectTime", nameof(SQLRealValue.CollectTime), OrderByType.Desc)]
|
||||
// public class RealModel1
|
||||
// {
|
||||
// [SugarColumn(IsPrimaryKey = true)]
|
||||
// public long EmptyId { get; set; }
|
||||
|
||||
// [SugarColumn(ColumnDescription = "采集时间")]
|
||||
// public DateTime CollectTime { get; set; }
|
||||
|
||||
// ///<summary>
|
||||
// ///是否在线
|
||||
// ///</summary>
|
||||
// [SugarColumn(ColumnDescription = "是否在线")]
|
||||
// public bool IsOnline { get; set; }
|
||||
|
||||
// public string Temp1 { get; set; }
|
||||
|
||||
// public string Temp2 { get; set; }
|
||||
|
||||
// public string Temp3 { get; set; }
|
||||
// }
|
||||
//}
|
||||
@@ -64,7 +64,7 @@
|
||||
"Name": "Name",
|
||||
"Value": "Value"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlHistoryAlarm.HistoryAlarm": {
|
||||
"ThingsGateway.Plugin.DB.HistoryAlarm": {
|
||||
"AlarmCode": "AlarmCode",
|
||||
"AlarmLimit": "AlarmLimit",
|
||||
"AlarmText": "AlarmText",
|
||||
@@ -86,10 +86,14 @@
|
||||
"Remark4": "Remark4",
|
||||
"Remark5": "Remark5"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlHistoryAlarm.SQLHistoryAlarmProperty": {
|
||||
"ThingsGateway.Plugin.DB.SQLHistoryAlarmProperty": {
|
||||
"BigTextConnectStr": "ConnectString",
|
||||
"DbType": "DbType",
|
||||
"TableName": "TableName"
|
||||
"TableName": "TableName",
|
||||
"VariableAlarmEnable": "VariableAlarmEnable",
|
||||
"PluginEventEnable": "PluginEventEnable",
|
||||
"BigTextScriptHistoryTable": "BigTextScriptHistoryTable",
|
||||
"BigTextScriptPluginEventDataHistoryTable": "BigTextScriptPluginEventDataHistoryTable"
|
||||
},
|
||||
"ThingsGateway.Plugin.TDengineDB.TDengineDBNumberHistoryValue": {
|
||||
"CollectTime": "CollectTime",
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"Name": "变量名称",
|
||||
"Value": "变量值"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlHistoryAlarm.HistoryAlarm": {
|
||||
"ThingsGateway.Plugin.DB.HistoryAlarm": {
|
||||
"AlarmCode": "报警值",
|
||||
"AlarmLimit": "报警限值",
|
||||
"AlarmText": "报警文本",
|
||||
@@ -86,10 +86,14 @@
|
||||
"Remark4": "备用4",
|
||||
"Remark5": "备用5"
|
||||
},
|
||||
"ThingsGateway.Plugin.SqlHistoryAlarm.SQLHistoryAlarmProperty": {
|
||||
"ThingsGateway.Plugin.DB.SQLHistoryAlarmProperty": {
|
||||
"BigTextConnectStr": "链接字符串",
|
||||
"DbType": "数据库类型",
|
||||
"TableName": "表名称"
|
||||
"TableName": "表名称",
|
||||
"VariableAlarmEnable": "变量报警存储",
|
||||
"PluginEventEnable": "插件事件存储",
|
||||
"BigTextScriptHistoryTable": "变量报警脚本",
|
||||
"BigTextScriptPluginEventDataHistoryTable": "插件事件脚本"
|
||||
},
|
||||
"ThingsGateway.Plugin.TDengineDB.TDengineDBNumberHistoryValue": {
|
||||
"CollectTime": "采集时间",
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
using Riok.Mapperly.Abstractions;
|
||||
|
||||
using ThingsGateway.DB;
|
||||
using ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
[Mapper(UseDeepCloning = true, EnumMappingStrategy = EnumMappingStrategy.ByName, RequiredMappingStrategy = RequiredMappingStrategy.None)]
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
|
||||
namespace ThingsGateway.Plugin.QuestDB;
|
||||
|
||||
public class QuestDBProducerVariableProperty : VariablePropertyBase
|
||||
public class QuestDBProducerVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
||||
@@ -15,19 +15,19 @@
|
||||
@ref=Model.ValidateForm
|
||||
Id=@($"DeviceEditValidateForm{Id}{Model.Value.GetType().TypeHandle.Value}")>
|
||||
|
||||
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems IsDisplay="!CanWrite" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=@(CanWrite?2:3) ShowLabelTooltip=true LabelWidth=@(CanWrite?240:120) Model="Model.Value" ShowLabel="true" @key=@($"DeviceEditEditorFormObject{Id}{Model.Value.GetType().TypeHandle.Value}")>
|
||||
<EditorFormObject class="p-2" Items=PluginPropertyEditorItems IsDisplay="!CanWrite" AutoGenerateAllItem="false" RowType=RowType.Inline ItemsPerRow=@(CanWrite ? 2 : 3) ShowLabelTooltip=true LabelWidth=@(CanWrite ? 240 : 120) Model="Model.Value" ShowLabel="true" @key=@($"DeviceEditEditorFormObject{Id}{Model.Value.GetType().TypeHandle.Value}")>
|
||||
|
||||
<FieldItems>
|
||||
@if (Model.Value is SqlDBProducerProperty businessProperty)
|
||||
{
|
||||
<EditorItem FieldExpression=@(()=>context) Field=@(context)>
|
||||
<EditorItem FieldExpression=@(() => context) Field=@(context)>
|
||||
|
||||
<EditTemplate Context="value">
|
||||
<div class="col-12 col-md-12">
|
||||
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
|
||||
<CodeEditor @bind-Value=@businessProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" />
|
||||
<div class="ms-2 d-flex justify-content-center align-items-center">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptHistoryTable))">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick="() => CheckScript(businessProperty, nameof(businessProperty.BigTextScriptHistoryTable))">
|
||||
@RazorLocalizer["Check"]
|
||||
</Button>
|
||||
</div>
|
||||
@@ -36,7 +36,7 @@
|
||||
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptRealTable"] ShowLabelTooltip="true" />
|
||||
<CodeEditor @bind-Value=@businessProperty.BigTextScriptRealTable Language="csharp" Theme="vs-dark" />
|
||||
<div class="ms-2 d-flex justify-content-center align-items-center">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick="()=>CheckScript(businessProperty,nameof(businessProperty.BigTextScriptRealTable))">
|
||||
<Button IsDisabled=@(!CanWrite) OnClick="() => CheckScript(businessProperty, nameof(businessProperty.BigTextScriptRealTable))">
|
||||
@RazorLocalizer["Check"]
|
||||
</Button>
|
||||
</div>
|
||||
@@ -62,7 +62,22 @@
|
||||
</EditTemplate>
|
||||
</EditorItem>
|
||||
}
|
||||
else if (Model.Value is SqlHistoryAlarmProperty sqlHistoryAlarmProperty)
|
||||
{
|
||||
<EditorItem FieldExpression=@(() => context) Field=@(context)>
|
||||
|
||||
<EditTemplate Context="value">
|
||||
<div class="col-12 col-md-12">
|
||||
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptHistoryTable"] ShowLabelTooltip="true" />
|
||||
<CodeEditor @bind-Value=@sqlHistoryAlarmProperty.BigTextScriptHistoryTable Language="csharp" Theme="vs-dark" />
|
||||
</div>
|
||||
<div class="col-12 col-md-12">
|
||||
<BootstrapLabel Value=@ProducerPropertyLocalizer["BigTextScriptPluginEventDataHistoryTable"] ShowLabelTooltip="true" />
|
||||
<CodeEditor @bind-Value=@sqlHistoryAlarmProperty.BigTextScriptPluginEventDataHistoryTable Language="csharp" Theme="vs-dark" />
|
||||
</div>
|
||||
</EditTemplate>
|
||||
</EditorItem>
|
||||
}
|
||||
</FieldItems>
|
||||
</EditorFormObject>
|
||||
</ValidateForm>
|
||||
|
||||
@@ -13,6 +13,6 @@ namespace ThingsGateway.Plugin.SqlDB;
|
||||
/// <summary>
|
||||
/// SqlDBProducerVariableProperty
|
||||
/// </summary>
|
||||
public class SqlDBProducerVariableProperty : VariablePropertyBase
|
||||
public class SqlDBProducerVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
||||
@@ -12,7 +12,7 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using ThingsGateway.Extension.Generic;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
public class HistoryAlarmPageInput : ITableSearchModel
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
[SugarTable("historyAlarm", TableDescription = "历史报警表")]
|
||||
public class HistoryAlarm : AlarmVariable
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
@using ThingsGateway.Admin.Razor
|
||||
@using ThingsGateway.Extension
|
||||
@using ThingsGateway.Gateway.Application
|
||||
@namespace ThingsGateway.Plugin.SqlHistoryAlarm
|
||||
@namespace ThingsGateway.Plugin.DB
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ using Microsoft.AspNetCore.Components;
|
||||
|
||||
using ThingsGateway.Common;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// HistoryAlarmPage
|
||||
|
||||
@@ -12,12 +12,13 @@ using BootstrapBlazor.Components;
|
||||
|
||||
using ThingsGateway.Common;
|
||||
using ThingsGateway.DB;
|
||||
using ThingsGateway.Debug;
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.NewLife;
|
||||
using ThingsGateway.NewLife.Extension;
|
||||
using ThingsGateway.SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// SqlHistoryAlarm
|
||||
@@ -55,7 +56,7 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
|
||||
private SqlSugarClient _db;
|
||||
|
||||
|
||||
public override Type DriverPropertyUIType => typeof(SqlDBProducerPropertyRazor);
|
||||
|
||||
protected override Task DisposeAsync(bool disposing)
|
||||
{
|
||||
@@ -66,6 +67,7 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
#if !Management
|
||||
protected override async Task InitChannelAsync(IChannel? channel, CancellationToken cancellationToken)
|
||||
{
|
||||
|
||||
_db = BusinessDatabaseUtil.GetDb((DbType)_driverPropertys.DbType, _driverPropertys.BigTextConnectStr);
|
||||
|
||||
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
|
||||
@@ -76,11 +78,33 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override bool IsConnected() => success;
|
||||
protected override Task ProtectedStartAsync(CancellationToken cancellationToken)
|
||||
protected override async Task ProtectedStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_db.DbMaintenance.CreateDatabase();
|
||||
_db.CodeFirst.AS<HistoryAlarm>(_driverPropertys.TableName).InitTables<HistoryAlarm>();
|
||||
return base.ProtectedStartAsync(cancellationToken);
|
||||
if (_driverPropertys.VariableAlarmEnable)
|
||||
{
|
||||
if (!_driverPropertys.BigTextScriptHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var hisModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptHistoryTable);
|
||||
|
||||
await hisModel.DBInit(_db, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
_db.CodeFirst.AS<HistoryAlarm>(_driverPropertys.TableName).InitTables<HistoryAlarm>();
|
||||
}
|
||||
}
|
||||
if (_driverPropertys.PluginEventEnable)
|
||||
{
|
||||
if (!_driverPropertys.BigTextScriptPluginEventDataHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var hisModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptPluginEventDataHistoryTable);
|
||||
|
||||
await hisModel.DBInit(_db, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
await base.ProtectedStartAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
#region 数据查询
|
||||
|
||||
@@ -11,11 +11,10 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
using ThingsGateway.Foundation;
|
||||
using ThingsGateway.Plugin.DB;
|
||||
|
||||
using TouchSocket.Core;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// SqlHistoryAlarm
|
||||
@@ -23,8 +22,61 @@ namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
{
|
||||
#if !Management
|
||||
|
||||
protected override void PluginChange(PluginEventData value)
|
||||
{
|
||||
if (_driverPropertys.PluginEventEnable == false)
|
||||
return;
|
||||
AddQueuePluginDataModel(new CacheDBItem<PluginEventData>(value));
|
||||
}
|
||||
|
||||
protected override ValueTask<OperResult> UpdatePluginEventDataModel(List<CacheDBItem<PluginEventData>> item, CancellationToken cancellationToken)
|
||||
{
|
||||
return UpdatePluginEventDataModel(item.Select(a => a.Value), cancellationToken);
|
||||
}
|
||||
private async ValueTask<OperResult> UpdatePluginEventDataModel(IEnumerable<PluginEventData> item, CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await InserableAsync(item.ToList(), cancellationToken).ConfigureAwait(false);
|
||||
if (success != result.IsSuccess)
|
||||
{
|
||||
if (!result.IsSuccess)
|
||||
LogMessage?.LogWarning(result.ToString());
|
||||
success = result.IsSuccess;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
private async ValueTask<OperResult> InserableAsync(List<PluginEventData> dbInserts, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
_db.Ado.CancellationToken = cancellationToken;
|
||||
if (!_driverPropertys.BigTextScriptPluginEventDataHistoryTable.IsNullOrEmpty())
|
||||
{
|
||||
var getDeviceModel = CSharpScriptEngineExtension.Do<DynamicSQLBase>(_driverPropertys.BigTextScriptPluginEventDataHistoryTable);
|
||||
|
||||
getDeviceModel.Logger = LogMessage;
|
||||
|
||||
await getDeviceModel.DBInsertable(_db, dbInserts, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new OperResult("Script must be configured");
|
||||
}
|
||||
|
||||
return OperResult.Success;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new OperResult(ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void AlarmChange(AlarmVariable alarmVariable)
|
||||
{
|
||||
if (_driverPropertys.VariableAlarmEnable == false)
|
||||
return;
|
||||
AddQueueAlarmModel(new CacheDBItem<AlarmVariable>(alarmVariable));
|
||||
}
|
||||
|
||||
@@ -87,5 +139,7 @@ public partial class SqlHistoryAlarm : BusinessBaseWithCacheAlarm
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using ThingsGateway.Plugin.SqlDB;
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
@@ -26,10 +26,17 @@ public class SqlHistoryAlarmProperty : BusinessPropertyWithCache
|
||||
[DynamicProperty]
|
||||
[Required]
|
||||
public string TableName { get; set; } = "historyAlarm";
|
||||
|
||||
[DynamicProperty]
|
||||
public bool VariableAlarmEnable { get; set; } = true;
|
||||
|
||||
[DynamicProperty]
|
||||
public bool PluginEventEnable { get; set; } = false;
|
||||
|
||||
[DynamicProperty]
|
||||
[Required]
|
||||
[AutoGenerateColumn(ComponentType = typeof(Textarea), Rows = 1)]
|
||||
public string BigTextConnectStr { get; set; } = "server=.;uid=sa;pwd=111111;database=test;";
|
||||
public string BigTextConnectStr { get; set; } = "server=.;uid=sa;pwd=111111;database=test;Encrypt=True;TrustServerCertificate=True;";
|
||||
|
||||
/// <summary>
|
||||
/// 历史表脚本
|
||||
@@ -38,5 +45,15 @@ public class SqlHistoryAlarmProperty : BusinessPropertyWithCache
|
||||
[AutoGenerateColumn(Visible = true, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false)]
|
||||
public string? BigTextScriptHistoryTable { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 历史表脚本
|
||||
/// </summary>
|
||||
[DynamicProperty]
|
||||
[AutoGenerateColumn(Visible = true, IsVisibleWhenEdit = false, IsVisibleWhenAdd = false)]
|
||||
public string? BigTextScriptPluginEventDataHistoryTable { get; set; }
|
||||
|
||||
public override bool OnlineFilter { get; set; } = false;
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace ThingsGateway.Plugin.SqlHistoryAlarm;
|
||||
namespace ThingsGateway.Plugin.DB;
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public class SqlHistoryAlarmVariableProperty : VariablePropertyBase
|
||||
public class SqlHistoryAlarmVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
||||
@@ -10,6 +10,6 @@
|
||||
|
||||
namespace ThingsGateway.Plugin.TDengineDB;
|
||||
|
||||
public class TDengineDBProducerVariableProperty : VariablePropertyBase
|
||||
public class TDengineDBProducerVariableProperty : BusinessVariableProperty
|
||||
{
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user