Compare commits

...

24 Commits

Author SHA1 Message Date
2248356998 qq.com
179ca0aa0e build: 10.11.23
feat: 增加部分demo
feat: 增加管理软件桌面端
2025-09-03 17:40:19 +08:00
Diego
fc09a52da1 build: 10.11.22
refactor:性能优化
2025-09-02 11:13:54 +08:00
Diego
5436b91c89 10.11.21 2025-09-01 18:40:12 +08:00
Diego
d1c46f51a6 添加api接口 2025-09-01 16:00:53 +08:00
Diego
4539d8d198 fix: 字符串反转功能失效 2025-09-01 11:52:03 +08:00
2248356998 qq.com
9ea9529a5f 10.11.19 2025-08-29 15:43:02 +08:00
2248356998 qq.com
4e6be23aac 10.11.17 2025-08-28 17:15:08 +08:00
2248356998 qq.com
2fabbd236b 10.11.16 2025-08-28 17:05:00 +08:00
2248356998 qq.com
163a66530e fix: dlt645校验和 2025-08-28 17:04:27 +08:00
2248356998 qq.com
29073a00c4 fix: dtu连接注册包解析错误 2025-08-28 15:35:43 +08:00
2248356998 qq.com
c6d4d1ecfa fix: dtu连接注册包解析错误 2025-08-28 15:20:00 +08:00
2248356998 qq.com
ba16889cad 10.11.14 2025-08-28 14:21:58 +08:00
2248356998 qq.com
5aaed35b0f 10.11.12 2025-08-28 01:42:59 +08:00
Diego
df067c91eb 10.11.12 2025-08-28 01:39:39 +08:00
2248356998 qq.com
2078b4a60b 补充忽略的文件 2025-08-26 08:54:09 +08:00
2248356998 qq.com
20a2e3ff8e 示例 2025-08-25 20:39:26 +08:00
Diego
61a973b1b5 !73 10.11.10 2025-08-25 12:21:12 +00:00
2248356998 qq.com
cbd72e2081 添加文本日志配置json文件 2025-08-22 16:17:25 +08:00
2248356998 qq.com
4e0377b20c 添加文本日志配置json文件 2025-08-22 16:15:54 +08:00
2248356998 qq.com
fd318d3cdc 移除警告项 2025-08-22 16:12:14 +08:00
2248356998 qq.com
515bdb9700 10.11.7 2025-08-21 23:35:12 +08:00
2248356998 qq.com
46c1780017 10.11.6 2025-08-21 22:40:53 +08:00
2248356998 qq.com
fe78a4c3ca 更新依赖库 2025-08-21 22:05:30 +08:00
2248356998 qq.com
2d7effadf9 10.11.4 2025-08-21 21:13:18 +08:00
181 changed files with 4650 additions and 4322 deletions

View File

@@ -27,6 +27,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" PrivateAssets="all" Private="false" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all" Private="false" />
</ItemGroup>
</Project>

View File

@@ -37,9 +37,8 @@ public class FileController : ControllerBase
var root = Directory.GetCurrentDirectory();
var wwwroot = Path.Combine(root, "wwwroot");
var filePath = Path.Combine(wwwroot, fileName);
// 防止路径穿越攻击
#pragma warning disable CA3003
if ((!fileName.StartsWith(@"..\Logs\") && filePath.Contains("..")) || !System.IO.File.Exists(filePath))
if ((!(fileName.StartsWith(@"../Logs") || fileName.StartsWith(@"..\Logs")) && filePath.Contains("..")) || !System.IO.File.Exists(filePath))
{
return NotFound();
}

View File

@@ -89,8 +89,8 @@
</div>
</Side>
<Main>
<Tab @ref=_tab ClickTabToNavigation="true" ShowToolbar="true" ShowContextMenu="true" ShowContextMenuFullScreen="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
<Tab @ref=_tab ClickTabToNavigation="true" ShowToolbar="true" ShowContextMenu="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
ShowFullscreenToolbarButton=false ShowContextMenuFullScreen=false ShowFullScreen=false AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
{
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));

View File

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

View File

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

View File

@@ -30,9 +30,27 @@ public class ImportPreviewOutputBase
/// <summary>
/// 返回状态
/// </summary>
public ConcurrentList<(int Row, bool Success, string? ErrorMessage)> Results { get; set; } = new();
public ConcurrentList<ImportPreviewResult> Results { get; set; } = new();
}
public class ImportPreviewResult
{
public ImportPreviewResult()
{
}
public ImportPreviewResult(int row, bool success, string error)
{
this.Row = row;
this.Success = success;
this.ErrorMessage = error;
}
public int Row { get; set; }
public bool Success { get; set; }
public string? ErrorMessage { get; set; }
}
/// <summary>
/// 导入预览
/// </summary>

View File

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

View File

@@ -9,6 +9,7 @@
// ------------------------------------------------------------------------------
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
namespace ThingsGateway.DB;
@@ -41,4 +42,31 @@ public static class FileExtensions
}
return fileName;
}
/// <summary>
/// 存储本地文件
/// </summary>
/// <param name="pPath">存储的第一层目录</param>
/// <param name="file"></param>
/// <returns>文件全路径</returns>
public static async Task<string> StorageLocal(this IFormFile file, string pPath = "imports")
{
string uploadFileFolder = App.WebHostEnvironment?.WebRootPath ?? "wwwroot"!;//赋值路径
var now = CommonUtils.GetSingleId();
var filePath = Path.Combine(uploadFileFolder, pPath);
if (!Directory.Exists(filePath))//如果不存在就创建文件夹
Directory.CreateDirectory(filePath);
//var fileSuffix = Path.GetExtension(file.Name).ToLower();// 文件后缀
var fileObjectName = $"{now}{file.Name}";//存储后的文件名
var fileName = Path.Combine(filePath, fileObjectName);//获取文件全路径
fileName = fileName.Replace("\\", "/");//格式化一系
//存储文件
using (var stream = File.Create(Path.Combine(filePath, fileObjectName)))
{
await file.CopyToAsync(stream).ConfigureAwait(false);
}
return fileName;
}
}

View File

@@ -411,7 +411,7 @@ public static class SpecificationDocumentBuilder
// 本地函数
static string DefaultSchemaIdSelector(Type modelType)
{
var modelName = modelType.Name;
var modelName = modelType.FullName;
// 处理泛型类型问题
if (modelType.IsConstructedGenericType)

View File

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

View File

@@ -60,6 +60,9 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
Name = str;
else
Name = $"Pool<{typeof(T).Name}>";
// 启动定期清理的定时器
StartTimer();
}
/// <summary>销毁</summary>
@@ -227,8 +230,7 @@ public class ObjectPool<T> : DisposeBase, IPool<T> where T : notnull
Interlocked.Increment(ref _FreeCount);
// 启动定期清理的定时器
StartTimer();
return true;
}

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

View File

@@ -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 = 600000)
{
_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();
}
}

View File

@@ -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.0Win8.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

View File

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

View File

@@ -76,6 +76,65 @@ public static class PathHelper
#endregion
#region
public static string GetRelativePath(string relativeTo, string path)
{
if (string.IsNullOrEmpty(relativeTo))
throw new ArgumentNullException(nameof(relativeTo));
if (string.IsNullOrEmpty(path))
throw new ArgumentNullException(nameof(path));
// 转为绝对路径
relativeTo = Path.GetFullPath(relativeTo);
path = Path.GetFullPath(path);
// 末尾确保有分隔符,便于处理目录
if (!relativeTo.EndsWith(Path.DirectorySeparatorChar.ToString()))
relativeTo += Path.DirectorySeparatorChar;
// 相同路径
if (string.Equals(relativeTo, path, StringComparison.OrdinalIgnoreCase))
return ".";
// 检查 UNC 或不同盘符
bool isUnc = relativeTo.StartsWith(@"\\") && path.StartsWith(@"\\");
if (!isUnc)
{
// 不同盘符直接返回绝对路径
if (!string.Equals(Path.GetPathRoot(relativeTo), Path.GetPathRoot(path), StringComparison.OrdinalIgnoreCase))
return path;
}
// 找到共同前缀长度
int length = Math.Min(relativeTo.Length, path.Length);
int lastSeparatorIndex = -1;
int i;
for (i = 0; i < length; i++)
{
if (char.ToLowerInvariant(relativeTo[i]) != char.ToLowerInvariant(path[i]))
break;
if (relativeTo[i] == Path.DirectorySeparatorChar)
lastSeparatorIndex = i;
}
// 计算上退的 ".."
string up = "";
for (int j = lastSeparatorIndex + 1; j < relativeTo.Length; j++)
{
if (relativeTo[j] == Path.DirectorySeparatorChar)
up += ".." + Path.DirectorySeparatorChar;
}
// 获取剩余下行部分
string down = path.Substring(lastSeparatorIndex + 1);
// 拼接结果
string result = up + down;
return result;
}
private static String GetPath(String path, Int32 mode)
{
// 处理路径分隔符兼容Windows和Linux

View File

@@ -1,6 +1,7 @@
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace ThingsGateway.NewLife.Reflection;
@@ -552,19 +553,65 @@ public static class Reflect
// return false;
//}
private static class DelegateCache<TFunc>
{
public static readonly ExpiringDictionary<DelegateCacheKey, TFunc> Cache = 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 = new DelegateCacheKey(method, typeof(TFunc), target);
var func = DelegateCache<TFunc>.Cache.GetOrAdd(
key,
_ => (TFunc)(object)(
target == null
? Delegate.CreateDelegate(typeof(TFunc), method, true)
: Delegate.CreateDelegate(typeof(TFunc), target, method, true)));
return func;
}
private readonly struct DelegateCacheKey : IEquatable<DelegateCacheKey>
{
public readonly MethodInfo Method;
public readonly Type FuncType;
public readonly object? Target;
public DelegateCacheKey(MethodInfo method, Type funcType, object? target)
{
Method = method;
FuncType = funcType;
Target = target;
}
public bool Equals(DelegateCacheKey other) =>
Method.Equals(other.Method)
&& FuncType.Equals(other.FuncType)
&& ReferenceEquals(Target, other.Target);
public override bool Equals(object? obj) =>
obj is DelegateCacheKey other && Equals(other);
public override int GetHashCode()
{
unchecked
{
int hash = Method.GetHashCode();
hash = (hash * 397) ^ FuncType.GetHashCode();
if (Target != null)
hash = (hash * 397) ^ RuntimeHelpers.GetHashCode(Target); // 不受对象重写 GetHashCode 影响
return hash;
}
}
}
#endregion
}

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.1.0" />
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.1.1" />
</ItemGroup>
<ItemGroup>

View File

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

View File

@@ -1,15 +1,17 @@
<Project>
<PropertyGroup>
<PluginVersion>10.11.2</PluginVersion>
<ProPluginVersion>10.11.2</ProPluginVersion>
<DefaultVersion>10.11.2</DefaultVersion>
<AuthenticationVersion>10.11.2</AuthenticationVersion>
<SourceGeneratorVersion>10.11.2</SourceGeneratorVersion>
<PluginVersion>10.11.23</PluginVersion>
<ProPluginVersion>10.11.23</ProPluginVersion>
<DefaultVersion>10.11.23</DefaultVersion>
<AuthenticationVersion>10.11.3</AuthenticationVersion>
<SourceGeneratorVersion>10.11.3</SourceGeneratorVersion>
<NET8Version>8.0.19</NET8Version>
<NET9Version>9.0.8</NET9Version>
<SatelliteResourceLanguages>zh-Hans;en-US</SatelliteResourceLanguages>
<IsTrimmable>false</IsTrimmable>
<ManagementProPluginVersion>10.11.22</ManagementProPluginVersion>
<ManagementPluginVersion>10.11.22</ManagementPluginVersion>
</PropertyGroup>
<PropertyGroup>
@@ -28,7 +30,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

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;
[assembly: SuppressMessage("Reliability", "CA2007:考虑对等待的任务调用 ConfigureAwait", Justification = "<挂起>", Scope = "member", Target = "~M:ThingsGateway.Foundation.Demo.Program.Main(System.String[])~System.Threading.Tasks.Task")]
[assembly: SuppressMessage("Reliability", "CA2007:考虑对等待的任务调用 ConfigureAwait", Justification = "<挂起>", Scope = "member", Target = "~M:ThingsGateway.Foundation.Demo.ModbusMasterDemo.TestRead~System.Threading.Tasks.Task")]

View File

@@ -0,0 +1,11 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
global using TouchSocket.Core;

View File

@@ -0,0 +1,221 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.Foundation.Modbus;
using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Foundation.Demo;
#pragma warning disable CA2007 // 考虑对等待的任务调用 ConfigureAwait
#pragma warning disable CA1861 // 不要将常量数组作为参数
/// <summary>
/// ModbusMaster
/// </summary>
public class ModbusMasterDemo
{
/// <summary>
/// 新建链路
/// </summary>
/// <returns></returns>
public IChannel GetChannel(ChannelOptions channelOptions)
{
TouchSocketConfig touchSocketConfig = new TouchSocketConfig();
return touchSocketConfig.GetChannel(channelOptions);
}
/// <summary>
/// 新建协议对象
/// </summary>
/// <param name="channel"></param>
/// <returns></returns>
public ModbusMaster GetDevice(IChannel channel)
{
var client = new ModbusMaster();
client.InitChannel(channel);
return client;
}
public async Task TestReadWrite()
{
//获取链路对象
using var channel = GetChannel(new ChannelOptions()
{
ChannelType = ChannelTypeEnum.TcpClient,
RemoteUrl = "127.0.0.1:502",
});
//配置其他属性,如日志等
channel.Config.ConfigureContainer(a => a.AddConsoleLogger());
//获取协议对象
using var device = GetDevice(channel);
//读取具体类型数据
var data = await device.ReadDoubleAsync("400001"); //通过字符串转化地址读取保持寄存器地址0
device.Logger?.Info($"读取到的数据:{data.ToJsonNetString()}");
//读取原始字节数组
var bytes = await device.ReadAsync("400001", 10); //通过字符串转化地址读取保持寄存器地址0,10个寄存器
device.Logger?.Info($"读取到的数据:{data.ToJsonNetString()}");
bytes = await device.ModbusReadAsync(new ModbusAddress()
{
StartAddress = 0,
FunctionCode = 3,
Length = 10,
}); //配置地址对象读取保持寄存器地址0,10个寄存器
if (bytes.IsSuccess)
{
//解析bytes字节数组
var byteData = bytes.Content.Span;
var data1 = device.ThingsGatewayBitConverter.ToDouble(byteData, 0);
var data2 = device.ThingsGatewayBitConverter.ToDouble(byteData, 8);
var data3 = device.ThingsGatewayBitConverter.ToUInt16(byteData, 16);
device.Logger?.Info($"读取到的数据:{data1},{data2},{data3}");
}
//写入数据
var write = await device.WriteAsync("400001", (double)123.456); //通过字符串转化地址写入保持寄存器地址0
device.Logger?.Info($"写入结果:{write.ToJsonNetString()}");
write = await device.WriteAsync("400001", new double[] { 123.456, 123.456 }); //通过字符串转化地址写入保持寄存器地址2,2个double寄存器
device.Logger?.Info($"写入结果:{write.ToJsonNetString()}");
write = await device.ModbusRequestAsync(new ModbusAddress()
{
StartAddress = 0,
FunctionCode = 3,
MasterWriteDatas = device.ThingsGatewayBitConverter.GetBytes(new double[] { 123.456, 123.456 })
}, false); //通过字符串转化地址写入保持寄存器地址2,2个double寄存器
device.Logger?.Info($"写入结果:{write.ToJsonNetString()}");
}
public async Task TestMulRead()
{
//获取链路对象
using var channel = GetChannel(new ChannelOptions()
{
ChannelType = ChannelTypeEnum.TcpClient,
RemoteUrl = "127.0.0.1:502",
});
//配置其他属性,如日志等
channel.Config.ConfigureContainer(a => a.AddConsoleLogger());
//获取协议对象
using var device = GetDevice(channel);
//批量打包
var variableRuntimes = new List<VariableClass>()
{
new VariableClass()
{
DataType=DataTypeEnum.Double,
RegisterAddress="40001",
IntervalTime="1000",
},
new VariableClass()
{
DataType=DataTypeEnum.UInt16,
RegisterAddress="40009",
IntervalTime="1000",
},
new VariableClass()
{
DataType=DataTypeEnum.Double,
RegisterAddress="40005",
IntervalTime="1000",
},
};
var deviceVariableSourceReads = device.LoadSourceRead<VariableSourceClass>(variableRuntimes, 125, "1000");
foreach (var item in deviceVariableSourceReads)
{
var result = await device.ReadAsync(item.AddressObject);
if (result.IsSuccess)
{
try
{
var result1 = item.VariableRuntimes.PraseStructContent(device, result.Content.Span, exWhenAny: true);
if (!result1.IsSuccess)
{
item.LastErrorMessage = result1.ErrorMessage;
var time = DateTime.Now;
item.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false));
device.Logger?.Warning(result1.ToString());
}
}
catch (Exception ex)
{
device.Logger?.LogWarning(ex);
}
}
else
{
item.LastErrorMessage = result.ErrorMessage;
var time = DateTime.Now;
item.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false));
device.Logger?.Warning(result.ToString());
}
}
device.Logger?.Info($"批量读取到的数据:{variableRuntimes.Select(a => new { a.RegisterAddress, a.Value }).ToJsonNetString()}");
}
public async Task TestVariableObject()
{
//获取链路对象
using var channel = GetChannel(new ChannelOptions()
{
ChannelType = ChannelTypeEnum.TcpClient,
RemoteUrl = "127.0.0.1:502",
});
//配置其他属性,如日志等
channel.Config.ConfigureContainer(a => a.AddConsoleLogger());
//获取协议对象
using var device = GetDevice(channel);
//使用变量对象读取
var testModbusObject = new TestModbusObject(device, 125);
await testModbusObject.MultiReadAsync();
device.Logger?.Info($"批量读取到的数据:{testModbusObject.ToJsonNetString()}");
//源生成的写入方法
var write = await testModbusObject.WriteDouble1Async(123.456);
device.Logger?.Info($"写入结果:{write.ToJsonNetString()}");
}
}
[GeneratorVariable]
public partial class TestModbusObject : VariableObject
{
public TestModbusObject(IDevice device, int maxPack) : base(device, maxPack)
{
}
[VariableRuntime(RegisterAddress = "400001")]
public double Double1 { get; set; }
[VariableRuntime(RegisterAddress = "400005")]
public double Double2 { get; set; }
[VariableRuntime(RegisterAddress = "400009")]
public ushort UShort3 { get; set; }
[VariableRuntime(RegisterAddress = "4000010")]
public ushort UShort4 { get; set; }
}

View File

@@ -0,0 +1,34 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation.Demo;
#pragma warning disable CA2007 // 考虑对等待的任务调用 ConfigureAwait
public class Program
{
public static async Task Main(string[] args)
{
ModbusMasterDemo modbusMasterDemo = new();
await modbusMasterDemo.TestReadWrite();
await modbusMasterDemo.TestMulRead();
await modbusMasterDemo.TestVariableObject();
Console.ReadKey();
SiemensS7MasterDemo siemensS7MasterDemo = new();
await siemensS7MasterDemo.TestReadWrite();
await siemensS7MasterDemo.TestMulRead();
await siemensS7MasterDemo.TestVariableObject();
Console.ReadKey();
}
}

View File

@@ -0,0 +1,214 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.Foundation.SiemensS7;
using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Foundation.Demo;
#pragma warning disable CA2007 // 考虑对等待的任务调用 ConfigureAwait
#pragma warning disable CA1861 // 不要将常量数组作为参数
/// <summary>
/// SiemensS7Master
/// </summary>
public class SiemensS7MasterDemo
{
/// <summary>
/// 新建链路
/// </summary>
/// <returns></returns>
public IChannel GetChannel(ChannelOptions channelOptions)
{
TouchSocketConfig touchSocketConfig = new TouchSocketConfig();
return touchSocketConfig.GetChannel(channelOptions);
}
/// <summary>
/// 新建协议对象
/// </summary>
/// <param name="channel"></param>
/// <returns></returns>
public SiemensS7Master GetDevice(IChannel channel)
{
var client = new SiemensS7Master();
client.InitChannel(channel);
return client;
}
public async Task TestReadWrite()
{
//获取链路对象
using var channel = GetChannel(new ChannelOptions()
{
ChannelType = ChannelTypeEnum.TcpClient,
RemoteUrl = "127.0.0.1:102",
});
//配置其他属性,如日志等
channel.Config.ConfigureContainer(a => a.AddConsoleLogger());
//获取协议对象
using var device = GetDevice(channel);
//读取具体类型数据
var data = await device.ReadDoubleAsync("V1"); //通过字符串转化地址读取v1
device.Logger?.Info($"读取到的数据:{data.ToJsonNetString()}");
//读取原始字节数组
var bytes = await device.ReadAsync("V1", 20); //通过字符串转化地址读取v1,10个寄存器
device.Logger?.Info($"读取到的数据:{data.ToJsonNetString()}");
if (bytes.IsSuccess)
{
//解析bytes字节数组
var byteData = bytes.Content.Span;
var data1 = device.ThingsGatewayBitConverter.ToDouble(byteData, 0);
var data2 = device.ThingsGatewayBitConverter.ToDouble(byteData, 8);
var data3 = device.ThingsGatewayBitConverter.ToUInt16(byteData, 16);
device.Logger?.Info($"读取到的数据:{data1},{data2},{data3}");
}
//写入数据
var write = await device.WriteAsync("v1", (double)123.456); //通过字符串转化地址写入保持寄存器地址0
device.Logger?.Info($"写入结果:{write.ToJsonNetString()}");
write = await device.WriteAsync("v1", new double[] { 123.456, 123.456 }); //通过字符串转化地址写入保持寄存器地址2,2个double寄存器
device.Logger?.Info($"写入结果:{write.ToJsonNetString()}");
}
public async Task TestMulRead()
{
//获取链路对象
using var channel = GetChannel(new ChannelOptions()
{
ChannelType = ChannelTypeEnum.TcpClient,
RemoteUrl = "127.0.0.1:102",
});
//配置其他属性,如日志等
channel.Config.ConfigureContainer(a => a.AddConsoleLogger());
//获取协议对象
using var device = GetDevice(channel);
//批量打包
var variableRuntimes = new List<VariableClass>()
{
new VariableClass()
{
DataType=DataTypeEnum.Double,
RegisterAddress="v1",
IntervalTime="1000",
},
new VariableClass()
{
DataType=DataTypeEnum.UInt16,
RegisterAddress="v9",
IntervalTime="1000",
},
new VariableClass()
{
DataType=DataTypeEnum.Double,
RegisterAddress="v11",
IntervalTime="1000",
},
};
var deviceVariableSourceReads = device.LoadSourceRead<VariableSourceClass>(variableRuntimes, 125, "1000");
foreach (var item in deviceVariableSourceReads)
{
var result = await device.ReadAsync(item.AddressObject);
if (result.IsSuccess)
{
try
{
var result1 = item.VariableRuntimes.PraseStructContent(device, result.Content.Span, exWhenAny: true);
if (!result1.IsSuccess)
{
item.LastErrorMessage = result1.ErrorMessage;
var time = DateTime.Now;
item.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false));
device.Logger?.Warning(result1.ToString());
}
}
catch (Exception ex)
{
device.Logger?.LogWarning(ex);
}
}
else
{
item.LastErrorMessage = result.ErrorMessage;
var time = DateTime.Now;
item.VariableRuntimes.ForEach(a => a.SetValue(null, time, isOnline: false));
device.Logger?.Warning(result.ToString());
}
}
device.Logger?.Info($"批量读取到的数据:{variableRuntimes.Select(a => new { a.RegisterAddress, a.Value }).ToJsonNetString()}");
}
public async Task TestVariableObject()
{
//获取链路对象
using var channel = GetChannel(new ChannelOptions()
{
ChannelType = ChannelTypeEnum.TcpClient,
RemoteUrl = "127.0.0.1:102",
});
//配置其他属性,如日志等
channel.Config.ConfigureContainer(a => a.AddConsoleLogger());
//获取协议对象
using var device = GetDevice(channel);
//使用变量对象读取
var testS7Object = new TestS7Object(device, 125);
await testS7Object.MultiReadAsync();
device.Logger?.Info($"批量读取到的数据:{testS7Object.ToJsonNetString()}");
//源生成的写入方法
var write = await testS7Object.WriteDouble1Async(123.456);
device.Logger?.Info($"写入结果:{write.ToJsonNetString()}");
}
}
/// <summary>
/// 实体类操作PLC数据
/// </summary>
[GeneratorVariable]
public partial class TestS7Object : VariableObject
{
[VariableRuntime(RegisterAddress = "v1")]
public double Double1 { get; set; }
[VariableRuntime(RegisterAddress = "v9")]
public double Double2 { get; set; }
[VariableRuntime(RegisterAddress = "v17")]
public ushort UShort3 { get; set; }
[VariableRuntime(RegisterAddress = "v19")]
public ushort UShort4 { get; set; }
public TestS7Object(IDevice device, int maxPack) : base(device, maxPack)
{
}
}

View File

@@ -0,0 +1,62 @@
@using Microsoft.AspNetCore.Components.Web;
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop;
@using ThingsGateway.Foundation
@using ThingsGateway.NewLife.Threading
@using ThingsGateway.Extension;
@using BootstrapBlazor.Components
@namespace ThingsGateway.Debug
<div class="w-100" style=@($"height:{HeightString}")>
<Card HeaderText=@HeaderText class=@("w-100 h-100")>
<HeaderTemplate>
<div class="flex-fill">
</div>
@if (LogLevelChanged.HasDelegate)
{
<Select Value="@LogLevel" ValueChanged="LogLevelChanged" IsPopover></Select>
}
<Tooltip class=" col-auto" Title="@RazorLocalizer[Pause?"Play":"Pause"]" Placement="Placement.Bottom">
<Button Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@(Pause?"fa fa-play":"fa fa-pause") OnClick="OnPause" />
</Tooltip>
<Tooltip class=" col-auto" Title="@RazorLocalizer["Export"]" Placement="Placement.Bottom">
<Button IsAsync Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@("fa fa-sign-out") OnClick="HandleOnExportClick" />
</Tooltip>
<Tooltip class=" col-auto" Title="@RazorLocalizer["Delete"]" Placement="Placement.Bottom">
<Button IsAsync Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@("far fa-trash-alt") OnClick="Delete" />
</Tooltip>
</HeaderTemplate>
<BodyTemplate>
<div style=@($"height:calc(100% - 50px);overflow-y:scroll;flex-fill;")>
<Virtualize Items="CurrentMessages??new List<LogMessage>()" Context="itemMessage" ItemSize="60" OverscanCount=2>
<ItemContent>
@* <Tooltip Placement="Placement.Bottom" Title=@itemMessage.Message.Substring(0, Math.Min(itemMessage.Message.Length, 500))> *@
<div class=@(itemMessage.Level<(byte)Microsoft.Extensions.Logging.LogLevel.Information?"":
itemMessage.Level>=(byte)Microsoft.Extensions.Logging.LogLevel.Warning? " red--text text-truncate":"green--text text-truncate")
title=@itemMessage.Message.Substring(0, Math.Min(itemMessage.Message.Length, 500))>
@itemMessage.Message.Substring(0, Math.Min(itemMessage.Message.Length, 150))
</div>
@* </Tooltip> *@
</ItemContent>
</Virtualize>
</div>
</BodyTemplate>
</Card>
</div>

View File

@@ -0,0 +1,203 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Components.Web;
using System.Diagnostics;
using System.Text.RegularExpressions;
using ThingsGateway.Extension;
using ThingsGateway.Foundation;
using ThingsGateway.NewLife;
using TouchSocket.Core;
namespace ThingsGateway.Debug;
public partial class LocalLogConsole : IDisposable
{
private bool Pause;
public bool Disposed { get; set; }
[Parameter, EditorRequired]
public LogLevel LogLevel { get; set; }
[Parameter]
public EventCallback<LogLevel> LogLevelChanged { get; set; }
[Parameter]
public string HeaderText { get; set; } = "Log";
[Parameter]
public string HeightString { get; set; } = "calc(100% - 300px)";
[Parameter, EditorRequired]
public string LogPath { get; set; }
/// <summary>
/// 日志
/// </summary>
public ICollection<LogMessage> Messages { get; set; } = new List<LogMessage>();
private ICollection<LogMessage> CurrentMessages => Pause ? PauseMessagesText : Messages;
[Inject]
private DownloadService DownloadService { get; set; }
[Inject]
private IStringLocalizer<ThingsGateway.Razor._Imports> RazorLocalizer { get; set; }
/// <summary>
/// 暂停缓存
/// </summary>
private ICollection<LogMessage> PauseMessagesText { get; set; } = new List<LogMessage>();
[Inject]
private IPlatformService PlatformService { get; set; }
private string logPath;
protected override async Task OnParametersSetAsync()
{
if (LogPath != logPath)
{
logPath = LogPath;
Messages = new List<LogMessage>();
await ExecuteAsync();
}
await base.OnParametersSetAsync();
}
[Inject]
private ToastService ToastService { get; set; }
[Inject]
TextFileReadService TextFileReadService { get; set; }
public void Dispose()
{
Disposed = true;
GC.SuppressFinalize(this);
}
private WaitLock WaitLock = new(nameof(LogConsole));
protected async Task ExecuteAsync()
{
if (WaitLock.Waited) return;
try
{
await WaitLock.WaitAsync();
await Task.Delay(1000);
if (LogPath != null)
{
var files = await TextFileReadService.GetLogFilesAsync(LogPath);
if (!files.IsSuccess)
{
Messages = new List<LogMessage>();
await Task.Delay(1000);
}
else
{
await Task.Run(async () =>
{
Stopwatch sw = Stopwatch.StartNew();
var result = await TextFileReadService.LastLogDataAsync(files.Content.FirstOrDefault());
if (result.IsSuccess)
{
Messages = result.Content.Where(a => a.LogLevel >= LogLevel).Select(a => new LogMessage((int)a.LogLevel, $"{a.LogTime} - {a.Message}{(a.ExceptionString.IsNullOrWhiteSpace() ? null : $"{Environment.NewLine}{a.ExceptionString}")}")).ToList();
}
else
{
Messages = new List<LogMessage>();
}
sw.Stop();
if (sw.ElapsedMilliseconds > 500)
{
await Task.Delay(1000);
}
});
}
}
}
catch (Exception ex)
{
NewLife.Log.XTrace.WriteException(ex);
}
finally
{
WaitLock.Release();
}
}
protected override void OnInitialized()
{
_ = RunTimerAsync();
base.OnInitialized();
}
private async Task Delete()
{
await TextFileReadService.DeleteLogDataAsync(LogPath);
}
private async Task HandleOnExportClick(MouseEventArgs args)
{
try
{
if (Pause)
{
using var memoryStream = new MemoryStream();
using StreamWriter writer = new(memoryStream);
foreach (var item in PauseMessagesText)
{
await writer.WriteLineAsync(item.Message);
}
await writer.FlushAsync();
memoryStream.Seek(0, SeekOrigin.Begin);
// 定义文件名称规则的正则表达式模式
string pattern = @"[\\/:*?""<>|]";
// 使用正则表达式将不符合规则的部分替换为下划线
string sanitizedFileName = Regex.Replace(HeaderText, pattern, "_");
await DownloadService.DownloadFromStreamAsync($"{sanitizedFileName}{DateTime.Now.ToFileDateTimeFormat()}.txt", memoryStream);
}
else
{
if (PlatformService != null)
await PlatformService.OnLogExport(LogPath);
}
}
catch (Exception ex)
{
await ToastService.Warn(ex);
}
}
private Task OnPause()
{
Pause = !Pause;
if (Pause)
PauseMessagesText = Messages.ToList();
return Task.CompletedTask;
}
private async Task RunTimerAsync()
{
while (!Disposed)
{
try
{
await ExecuteAsync();
await InvokeAsync(StateHasChanged);
}
catch (Exception ex)
{
NewLife.Log.XTrace.WriteException(ex);
}
}
}
}

View File

@@ -142,33 +142,7 @@ public partial class LogConsole : IDisposable
private async Task Delete()
{
if (LogPath != null)
{
var files = await TextFileReadService.GetLogFilesAsync(LogPath);
if (files.IsSuccess)
{
foreach (var item in files.Content)
{
if (File.Exists(item))
{
int error = 0;
while (error < 3)
{
try
{
FileUtil.DeleteFile(item);
break;
}
catch
{
await Task.Delay(3000);
error++;
}
}
}
}
}
}
await TextFileReadService.DeleteLogDataAsync(LogPath);
}
private async Task HandleOnExportClick(MouseEventArgs args)

View File

@@ -26,7 +26,7 @@ public class PlatformService : IPlatformService
public async Task OnLogExport(string logPath)
{
var files = TextFileReader.GetLogFilesAsync(logPath);
var files = TextFileReader.GetLogFiles(logPath);
if (!files.IsSuccess)
{
return;

View File

@@ -34,5 +34,6 @@ public class Startup : AppStartup
services.AddScoped<IPlatformService, PlatformService>();
services.AddSingleton<ITextFileReadService, TextFileReadService>();
services.AddSingleton<TextFileReadService, TextFileReadService>();
}
}

View File

@@ -9,6 +9,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.12.0" PrivateAssets="all" />
</ItemGroup>
</Project>

View File

@@ -154,7 +154,7 @@ public class DDPTcpSessionClientChannel : TcpSessionClientChannel
var id = $"ID={message.Id}";
if (message.Type == 0x09)
{
var reader = new ByteBlockReader(message.Content);
var reader = new ClassBytesReader(message.Content);
if (this.DataHandlingAdapter == null)
{

View File

@@ -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;
@@ -217,7 +219,7 @@ public static class ChannelOptionsExtensions
#if NETSTANDARD || NET6_0_OR_GREATER
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
config.UseUdpConnReset();
config.SetUdpConnReset(true);
}
#endif
return ddpUdp;
@@ -229,7 +231,7 @@ public static class ChannelOptionsExtensions
#if NETSTANDARD || NET6_0_OR_GREATER
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows))
{
config.UseUdpConnReset();
config.SetUdpConnReset(true);
}
#endif
return udpSessionChannel;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 };
}
}
@@ -90,12 +124,12 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter, IDev
}
request1 = request;
var byteBlock = new ByteBlockReader(memory);
var byteBlock = new ClassBytesReader(memory);
byteBlock.BytesRead = 0;
var pos = byteBlock.BytesRead;
if (request.HeaderLength > byteBlock.CanReadLength)
if (request.HeaderLength > byteBlock.BytesRead + byteBlock.BytesRemaining)
{
return false;//当头部都无法解析时,直接缓存
}
@@ -113,7 +147,7 @@ public class DeviceUdpDataHandleAdapter<TRequest> : UdpDataHandlingAdapter, IDev
Logger?.LogWarning($"{ToString()} {request.ErrorMessage}");
return false;
}
if (request.BodyLength + request.HeaderLength > byteBlock.CanReadLength)
if (request.BodyLength + request.HeaderLength > byteBlock.BytesRead + byteBlock.BytesRemaining)
{
return false;
}

View File

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

View File

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

View File

@@ -145,7 +145,6 @@ public static class ByteExtensions
// 如果是奇数,自动补齐 0
int evenLen = (len % 2 == 0) ? len : len + 1;
if (evenLen == len) return inBytes;
Span<byte> result = new byte[evenLen];
inBytes.CopyTo(result);
@@ -177,7 +176,6 @@ public static class ByteExtensions
// 如果是奇数,自动补齐 0
int evenLen = (len % 2 == 0) ? len : len + 1;
if (evenLen == len) return inBytes;
byte[] result = new byte[evenLen];
inBytes.CopyTo(result);

View File

@@ -51,6 +51,7 @@ public class LogDataCache
/// <summary>高性能日志文件读取器(支持倒序读取)</summary>
public static class TextFileReader
{
private static readonly MemoryCache _cache = new() { Expire = 30 };
private static readonly MemoryCache _fileLocks = new();
private static readonly ArrayPool<byte> _bytePool = ArrayPool<byte>.Shared;
@@ -59,7 +60,7 @@ public static class TextFileReader
/// </summary>
/// <param name="directoryPath">目录路径</param>
/// <returns>包含文件信息的列表</returns>
public static OperResult<List<string>> GetLogFilesAsync(string directoryPath)
public static OperResult<List<string>> GetLogFiles(string directoryPath)
{
OperResult<List<string>> result = new(); // 初始化结果对象
// 检查目录是否存在
@@ -81,17 +82,25 @@ public static class TextFileReader
return result;
}
// 获取文件信息并按照最后写入时间降序排序
var fileInfos = files.Select(filePath => new FileInfo(filePath))
.OrderByDescending(x => x.LastWriteTime)
.Select(x => x.FullName)
.Select(x =>
{
#if !NET6_0_OR_GREATER
return PathHelper.GetRelativePath(AppContext.BaseDirectory, x.FullName);
#else
return Path.GetRelativePath(AppContext.BaseDirectory, x.FullName);
#endif
})
.ToList();
result.OperCode = 0;
result.Content = fileInfos;
return result;
}
public static OperResult<List<LogData>> LastLogDataAsync(string file, int lineCount = 200)
public static OperResult<List<LogData>> LastLogData(string file, int lineCount = 200)
{
if (!File.Exists(file))
return new OperResult<List<LogData>>("The file path is invalid");
@@ -104,7 +113,7 @@ public static class TextFileReader
{
var fileInfo = new FileInfo(file);
var length = fileInfo.Length;
var cacheKey = $"{nameof(TextFileReader)}_{nameof(LastLogDataAsync)}_{file})";
var cacheKey = $"{nameof(TextFileReader)}_{nameof(LastLogData)}_{file})";
if (_cache.TryGetValue<LogDataCache>(cacheKey, out var cachedData))
{
if (cachedData != null && cachedData.Length == length)

View File

@@ -13,6 +13,8 @@ namespace ThingsGateway.Foundation;
public interface ITextFileReadService
{
Task DeleteLogDataAsync(string path);
/// <summary>
/// 获取指定目录下所有文件信息
/// </summary>

View File

@@ -10,11 +10,46 @@
using ThingsGateway.NewLife;
namespace ThingsGateway.Foundation;
public class TextFileReadService : ITextFileReadService
{
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath) => Task.FromResult(TextFileReader.GetLogFilesAsync(directoryPath));
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath) => Task.FromResult(TextFileReader.GetLogFiles(directoryPath));
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200) => Task.FromResult(TextFileReader.LastLogDataAsync(file, lineCount));
public Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200) => Task.FromResult(TextFileReader.LastLogData(file, lineCount));
public async Task DeleteLogDataAsync(string path)
{
if (path != null)
{
var files = TextFileReader.GetLogFiles(path);
if (files.IsSuccess)
{
foreach (var item in files.Content)
{
if (File.Exists(item))
{
int error = 0;
while (error < 3)
{
try
{
FileUtil.DeleteFile(item);
break;
}
catch
{
await Task.Delay(3000).ConfigureAwait(false);
error++;
}
}
}
}
}
}
}
}

View File

@@ -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.6" />
<PackageReference Include="TouchSocket.SerialPorts" Version="4.0.0-beta.6" />
</ItemGroup>
<ItemGroup>

View File

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

View File

@@ -8,6 +8,7 @@
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Foundation;
/// <summary>
@@ -60,6 +61,8 @@ public class VariableClass : IVariable
/// <summary>
/// IVariableSource
/// </summary>
[System.Text.Json.Serialization.JsonIgnore]
[Newtonsoft.Json.JsonIgnore]
public IVariableSource? VariableSource { get; set; }
public object? RawValue { get; private set; }

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
namespace ThingsGateway.Gateway.Application
{
public interface IScheduledTask
public interface IScheduledTask : IDisposable
{
bool Change(int dueTime, int period);
void SetNext(int interval);

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,716 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using ThingsGateway.Authentication;
using TouchSocket.Core;
using TouchSocket.Rpc;
namespace ThingsGateway.Gateway.Application;
[ApiDescriptionSettings("ThingsGateway.OpenApi", Order = 200)]
[Route("openApi/management/[action]")]
[RolePermission]
[RequestAudit]
[ApiController]
[Authorize(AuthenticationSchemes = "Bearer")]
[TouchSocket.WebApi.Router("/miniapi/management/[action]")]
[TouchSocket.WebApi.EnableCors("cors")]
public partial class ManagementController : ControllerBase, IRpcServer
{
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<BackendLog>> BackendLogPageAsync(QueryPageOptions option) => App.GetService<IBackendLogService>().BackendLogPageAsync(option);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<List<BackendLogDayStatisticsOutput>> BackendLogStatisticsByDayAsync(int day) => App.GetService<IBackendLogService>().BackendLogStatisticsByDayAsync(day);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> BatchEditChannelAsync([FromBody] BatchEditChannelRequest request) =>
App.GetService<IChannelPageService>()
.BatchEditChannelAsync(request.Models, request.OldModel, request.Model, request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> BatchEditDeviceAsync([FromBody] BatchEditDeviceRequest request) =>
App.GetService<IDevicePageService>()
.BatchEditDeviceAsync(request.Models, request.OldModel, request.Model, request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> BatchEditVariableAsync([FromBody] BatchEditVariableRequest request) =>
App.GetService<IVariablePageService>()
.BatchEditVariableAsync(request.Models, request.OldModel, request.Model, request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> BatchSaveVariableAsync([FromBody] BatchSaveVariableRequest request) =>
App.GetService<IVariablePageService>()
.BatchSaveVariableAsync(request.Input, request.Type, request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<LogLevel> ChannelLogLevelAsync(long id) =>
App.GetService<IChannelPageService>().ChannelLogLevelAsync(id);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> ClearChannelAsync(bool restart) =>
App.GetService<IChannelPageService>().ClearChannelAsync(restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> ClearDeviceAsync(bool restart) =>
App.GetService<IDevicePageService>().ClearDeviceAsync(restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task ClearRulesAsync() => App.GetService<IRulesService>().ClearRulesAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> ClearVariableAsync(bool restart) =>
App.GetService<IVariablePageService>().ClearVariableAsync(restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task CopyChannelAsync([FromBody] CopyChannelRequest request) =>
App.GetService<IChannelPageService>()
.CopyChannelAsync(request.CopyCount,
request.CopyChannelNamePrefix,
request.CopyChannelNameSuffixNumber,
request.CopyDeviceNamePrefix,
request.CopyDeviceNameSuffixNumber,
request.ChannelId,
request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task CopyDeviceAsync([FromBody] CopyDeviceRequest request) =>
App.GetService<IDevicePageService>()
.CopyDeviceAsync(request.CopyCount,
request.CopyDeviceNamePrefix,
request.CopyDeviceNameSuffixNumber,
request.DeviceId,
request.AutoRestartThread);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task CopyVariableAsync([FromBody] CopyVariableRequest request) =>
App.GetService<IVariablePageService>()
.CopyVariableAsync(request.Model,
request.CopyCount,
request.CopyVariableNamePrefix,
request.CopyVariableNameSuffixNumber,
request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task DeleteBackendLogAsync() => App.GetService<IBackendLogService>().DeleteBackendLogAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> DeleteChannelAsync([FromBody] DeleteRequest request) =>
App.GetService<IChannelPageService>().DeleteChannelAsync(request.Ids, request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> DeleteDeviceAsync([FromBody] DeleteRequest request) =>
App.GetService<IDevicePageService>().DeleteDeviceAsync(request.Ids, request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> DeleteVariableAsync([FromBody] DeleteRequest request) =>
App.GetService<IVariablePageService>().DeleteVariableAsync(request.Ids, request.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task DeleteRpcLogAsync() => App.GetService<IRpcLogService>().DeleteRpcLogAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task DeleteRuleRuntimesAsync(List<long> ids) => App.GetService<IRulesEngineHostedService>().DeleteRuleRuntimesAsync(ids);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> DeleteRulesAsync(List<long> ids) => App.GetService<IRulesService>().DeleteRulesAsync(ids);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<LogLevel> DeviceLogLevelAsync(long id) =>
App.GetService<IDevicePageService>().DeviceLogLevelAsync(id);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task DeviceRedundantThreadAsync(long id) =>
App.GetService<IDevicePageService>().DeviceRedundantThreadAsync(id);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task EditRedundancyOptionAsync(RedundancyOptions input) => App.GetService<IRedundancyService>().EditRedundancyOptionAsync(input);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task EditRuleRuntimesAsync(Rules rules) => App.GetService<IRulesEngineHostedService>().EditRuleRuntimesAsync(rules);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<USheetDatas> ExportChannelAsync(List<Channel> channels) =>
App.GetService<IChannelPageService>().ExportChannelAsync(channels);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter) =>
App.GetService<IChannelPageService>().ExportChannelFileAsync(exportFilter);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<USheetDatas> ExportDeviceAsync(List<Device> devices) =>
App.GetService<IDevicePageService>().ExportDeviceAsync(devices);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter) =>
App.GetService<IDevicePageService>().ExportDeviceFileAsync(exportFilter);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder) =>
App.GetService<IVariablePageService>().ExportVariableAsync(models, sortName, sortOrder);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter) => App.GetService<IVariablePageService>().ExportVariableFileAsync(exportFilter);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<List<Channel>> GetChannelListAsync([FromBody] GetListRequest<QueryPageOptions> request) =>
App.GetService<IChannelPageService>().GetChannelListAsync(request.Options, request.Max);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<List<Device>> GetDeviceListAsync([FromBody] GetListRequest<QueryPageOptions> request) =>
App.GetService<IDevicePageService>().GetDeviceListAsync(request.Options, request.Max);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<List<Variable>> GetVariableListAsync([FromBody] GetListRequest<QueryPageOptions> request) =>
App.GetService<IVariablePageService>().GetVariableListAsync(request.Options, request.Max);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<List<Rules>> GetAllRulesAsync() => App.GetService<IRulesService>().GetAllRulesAsync();
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<string> GetChannelNameAsync(long id) =>
App.GetService<IChannelPageService>().GetChannelNameAsync(id);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<IEnumerable<SelectedItem>> GetCurrentUserDeviceSelectedItemsAsync(string searchText, int startIndex, int count) => App.GetService<IGlobalDataService>().GetCurrentUserDeviceSelectedItemsAsync(searchText, startIndex, count);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<QueryData<SelectedItem>> GetCurrentUserDeviceVariableSelectedItemsAsync(string deviceText, string searchText, int startIndex, int count) => App.GetService<IGlobalDataService>().GetCurrentUserDeviceVariableSelectedItemsAsync(deviceText, searchText, startIndex, count);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync() => App.GetService<IRealAlarmService>().GetCurrentUserRealAlarmVariablesAsync();
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync() => App.GetService<IDevicePageService>().GetDeviceIdNamesAsync();
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<string> GetDeviceNameAsync(long redundantDeviceId) =>
App.GetService<IDevicePageService>().GetDeviceNameAsync(redundantDeviceId);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<string> GetDevicePluginNameAsync(long id) =>
App.GetService<IDevicePageService>().GetDevicePluginNameAsync(id);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath) => App.GetService<ITextFileReadService>().GetLogFilesAsync(directoryPath);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<List<BackendLog>> GetNewBackendLogAsync() => App.GetService<IBackendLogService>().GetNewBackendLogAsync();
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<List<RpcLog>> GetNewRpcLogAsync() => App.GetService<IRpcLogService>().GetNewRpcLogAsync();
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<string> GetPluginNameAsync(long channelId) => App.GetService<IChannelPageService>().GetPluginNameAsync(channelId);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null) => App.GetService<IPluginPageService>().GetPluginsAsync(pluginType);
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<RedundancyOptions> GetRedundancyAsync() => App.GetService<IRedundancyService>().GetRedundancyAsync();
[HttpGet]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Get)]
public Task<Rules> GetRuleRuntimesAsync(long rulesId) => App.GetService<IRulesEngineHostedService>().GetRuleRuntimesAsync(rulesId);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task ImportChannelDataAsync([FromBody] ImportChannelInput input) =>
App.GetService<IChannelPageService>().ImportChannelAsync(input.UpData, input.InsertData, input.Restart);
[HttpPost]
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync([FromForm] ImportRequest request)
{
return (await App.GetService<IChannelRuntimeService>().ImportChannelAsync(request.File, request.Restart).ConfigureAwait(false)).AdaptImportPreviewOutputBases();
}
[ApiExplorerSettings(IgnoreApi = true)]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
[TouchSocket.WebApi.Router("/miniapi/management/ImportChannel")]
public async Task<Dictionary<string, ImportPreviewOutputBase>> TSImportChannelAsync(TouchSocket.WebApi.IWebApiCallContext callContext)
{
var path = await callContext.HttpContext.Request.StorageLocalExcel().ConfigureAwait(false);
return (await GlobalData.ChannelRuntimeService.ImportChannelFileAsync(path, true).ConfigureAwait(false)).AdaptImportPreviewOutputBases(); ;
}
[ApiExplorerSettings(IgnoreApi = true)]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
[TouchSocket.WebApi.Router("/miniapi/management/ImportDevice")]
public async Task<Dictionary<string, ImportPreviewOutputBase>> TSImportDeviceAsync(TouchSocket.WebApi.IWebApiCallContext callContext)
{
var path = await callContext.HttpContext.Request.StorageLocalExcel().ConfigureAwait(false);
return (await GlobalData.DeviceRuntimeService.ImportDeviceFileAsync(path, true).ConfigureAwait(false)).AdaptImportPreviewOutputBases(); ;
}
[ApiExplorerSettings(IgnoreApi = true)]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
[TouchSocket.WebApi.Router("/miniapi/management/ImportVariable")]
public async Task<Dictionary<string, ImportPreviewOutputBase>> TSImportVariableAsync(TouchSocket.WebApi.IWebApiCallContext callContext)
{
var path = await callContext.HttpContext.Request.StorageLocalExcel().ConfigureAwait(false);
return (await GlobalData.VariableRuntimeService.ImportVariableFileAsync(path, true).ConfigureAwait(false)).AdaptImportPreviewOutputBases();
}
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync([FromBody] ImportUSheetInput input)
{
return (await App.GetService<IChannelPageService>().ImportChannelUSheetDatasAsync(input.Input, input.Restart).ConfigureAwait(false)).AdaptImportPreviewOutputBases();
}
[HttpPost]
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceAsync([FromForm] ImportRequest request)
{
return (await App.GetService<IDeviceRuntimeService>().ImportDeviceAsync(request.File, request.Restart).ConfigureAwait(false)).AdaptImportPreviewOutputBases();
}
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync([FromBody] ImportUSheetInput input)
{
return (await App.GetService<IDevicePageService>().ImportDeviceUSheetDatasAsync(input.Input, input.Restart).ConfigureAwait(false)).AdaptImportPreviewOutputBases();
}
[HttpPost]
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableAsync([FromForm] ImportRequest request)
{
return (await App.GetService<IVariableRuntimeService>().ImportVariableAsync(request.File, request.Restart).ConfigureAwait(false)).AdaptImportPreviewOutputBases();
}
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync([FromBody] ImportUSheetInput input)
{
return (await App.GetService<IVariablePageService>().ImportVariableUSheetDatasAsync(input.Input, input.Restart).ConfigureAwait(false)).AdaptImportPreviewOutputBases();
}
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task InsertTestDataAsync([FromBody] InsertTestDataInput input) =>
App.GetService<IVariablePageService>().InsertTestDataAsync(input.TestVariableCount, input.TestDeviceCount, input.SlaveUrl, input.BusinessEnable, input.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> IsRedundantDeviceAsync(long id) =>
App.GetService<IDevicePageService>().IsRedundantDeviceAsync(id);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<OperResult<List<LogData>>> LastLogDataAsync([FromBody] LastLogDataInput input) =>
App.GetService<ITextFileReadService>().LastLogDataAsync(input.File, input.LineCount);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<ChannelRuntime>> OnChannelQueryAsync([FromBody] QueryPageOptions options) =>
App.GetService<IChannelPageService>().OnChannelQueryAsync(options);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync([FromBody] VirtualizeQueryOption option) =>
App.GetService<IChannelPageService>().OnChannelSelectedItemQueryAsync(option);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync([FromBody] QueryPageOptions options) =>
App.GetService<IDevicePageService>().OnDeviceQueryAsync(options);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<SelectedItem>> OnDeviceSelectedItemQueryAsync([FromBody] OnDeviceSelectedItemQueryInput input) =>
App.GetService<IDevicePageService>().OnDeviceSelectedItemQueryAsync(input.Option, input.IsCollect);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync([FromBody] OnRedundantDevicesQueryInput input) =>
App.GetService<IDevicePageService>().OnRedundantDevicesQueryAsync(input.Option, input.DeviceId, input.ChannelId);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<VariableRuntime>> OnVariableQueryAsync([FromBody] QueryPageOptions options) =>
App.GetService<IVariablePageService>().OnVariableQueryAsync(options);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData) =>
App.GetService<IVariablePageService>().OnWriteVariableAsync(id, writeData);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task PauseThreadAsync(long id) =>
App.GetService<IDevicePageService>().PauseThreadAsync(id);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<PluginInfo>> PluginPageAsync([FromBody] PluginQueryPageOptions options) =>
App.GetService<IPluginPageService>().PluginPageAsync(options.Options, options.PluginType);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task RedundancyForcedSync() => App.GetService<IRedundancyHostedService>().RedundancyForcedSync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<LogLevel> RedundancyLogLevelAsync() => App.GetService<IRedundancyHostedService>().RedundancyLogLevelAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<string> RedundancyLogPathAsync() => App.GetService<IRedundancyHostedService>().RedundancyLogPathAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task ReloadPluginAsync() => App.GetService<IPluginPageService>().ReloadPluginAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task RestartChannelAsync(long channelId) =>
App.GetService<IChannelPageService>().RestartChannelAsync(channelId);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task RestartChannelsAsync() =>
App.GetService<IChannelPageService>().RestartChannelsAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task RestartDeviceAsync(long id, bool deleteCache) =>
App.GetService<IDevicePageService>().RestartDeviceAsync(id, deleteCache);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task RestartServerAsync() => App.GetService<IRestartService>().RestartServerAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<RpcLog>> RpcLogPageAsync([FromBody] QueryPageOptions option) =>
App.GetService<IRpcLogService>().RpcLogPageAsync(option);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<List<RpcLogDayStatisticsOutput>> RpcLogStatisticsByDayAsync(int day) =>
App.GetService<IRpcLogService>().RpcLogStatisticsByDayAsync(day);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<TouchSocket.Core.LogLevel> RulesLogLevelAsync(long rulesId) =>
App.GetService<IRulesEngineHostedService>().RulesLogLevelAsync(rulesId);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<string> RulesLogPathAsync(long rulesId) =>
App.GetService<IRulesEngineHostedService>().RulesLogPathAsync(rulesId);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<QueryData<Rules>> RulesPageAsync([FromBody] KVQueryPageOptions option) =>
App.GetService<IRulesService>().RulesPageAsync(option.Options, option.FilterKeyValueAction);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> SaveChannelAsync([FromBody] SaveChannelInput input) =>
App.GetService<IChannelPageService>().SaveChannelAsync(input.Input, input.Type, input.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> SaveDeviceAsync([FromBody] SaveDeviceInput input) =>
App.GetService<IDevicePageService>().SaveDeviceAsync(input.Input, input.Type, input.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task SavePluginByPathAsync([FromBody] PluginAddPathInput plugin) =>
App.GetService<IPluginPageService>().SavePluginByPathAsync(plugin);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> SaveRulesAsync([FromBody] Rules input, ItemChangedType type) =>
App.GetService<IRulesService>().SaveRulesAsync(input, type);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> SaveVariableAsync([FromBody] SaveVariableInput input) =>
App.GetService<IVariablePageService>().SaveVariableAsync(input.Input, input.Type, input.Restart);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task SetChannelLogLevelAsync([FromBody] SetChannelLogLevelInput input) =>
App.GetService<IChannelPageService>().SetChannelLogLevelAsync(input.Id, input.LogLevel);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task SetDeviceLogLevelAsync([FromBody] SetDeviceLogLevelInput input) =>
App.GetService<IDevicePageService>().SetDeviceLogLevelAsync(input.Id, input.LogLevel);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task SetRedundancyLogLevelAsync(LogLevel logLevel) =>
App.GetService<IRedundancyHostedService>().SetRedundancyLogLevelAsync(logLevel);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task SetRulesLogLevelAsync([FromBody] SetRulesLogLevelInput input) =>
App.GetService<IRulesEngineHostedService>().SetRulesLogLevelAsync(input.RulesId, input.LogLevel);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> StartBusinessChannelEnableAsync() => App.GetService<IChannelEnableService>().StartBusinessChannelEnableAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<bool> StartCollectChannelEnableAsync() => App.GetService<IChannelEnableService>().StartCollectChannelEnableAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task StartRedundancyTaskAsync() => App.GetService<IRedundancyHostedService>().StartRedundancyTaskAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task StopRedundancyTaskAsync() => App.GetService<IRedundancyHostedService>().StopRedundancyTaskAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<AuthorizeInfo> TryAuthorizeAsync(string password) => App.GetService<IAuthenticationService>().TryAuthorizeAsync(password);
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<AuthorizeInfo> TryGetAuthorizeInfoAsync() => App.GetService<IAuthenticationService>().TryGetAuthorizeInfoAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task UnAuthorizeAsync() => App.GetService<IAuthenticationService>().UnAuthorizeAsync();
[HttpPost]
[TouchSocket.WebApi.WebApi(Method = TouchSocket.WebApi.HttpMethodType.Post)]
public Task<string> UUIDAsync() => App.GetService<IAuthenticationService>().UUIDAsync();
}
// 定义请求 DTO
public class BatchEditChannelRequest
{
public List<Channel> Models { get; set; }
public Channel OldModel { get; set; }
public Channel Model { get; set; }
public bool Restart { get; set; }
}
public class BatchEditDeviceRequest
{
public List<Device> Models { get; set; }
public Device OldModel { get; set; }
public Device Model { get; set; }
public bool Restart { get; set; }
}
public class BatchEditVariableRequest
{
public List<Variable> Models { get; set; }
public Variable OldModel { get; set; }
public Variable Model { get; set; }
public bool Restart { get; set; }
}
public class BatchSaveVariableRequest
{
public List<Variable> Input { get; set; }
public ItemChangedType Type { get; set; }
public bool Restart { get; set; }
}
public class CopyChannelRequest
{
public int CopyCount { get; set; }
public string CopyChannelNamePrefix { get; set; }
public int CopyChannelNameSuffixNumber { get; set; }
public string CopyDeviceNamePrefix { get; set; }
public int CopyDeviceNameSuffixNumber { get; set; }
public long ChannelId { get; set; }
public bool Restart { get; set; }
}
public class CopyDeviceRequest
{
public int CopyCount { get; set; }
public string CopyDeviceNamePrefix { get; set; }
public int CopyDeviceNameSuffixNumber { get; set; }
public long DeviceId { get; set; }
public bool AutoRestartThread { get; set; }
}
public class CopyVariableRequest
{
public List<Variable> Model { get; set; }
public int CopyCount { get; set; }
public string CopyVariableNamePrefix { get; set; }
public int CopyVariableNameSuffixNumber { get; set; }
public bool Restart { get; set; }
}
public class DeleteRequest
{
public List<long> Ids { get; set; }
public bool Restart { get; set; }
}
public class GetListRequest<TOptions>
{
public TOptions Options { get; set; }
public int Max { get; set; }
}
public class ImportChannelInput
{
public List<Channel> UpData { get; set; }
public List<Channel> InsertData { get; set; }
public bool Restart { get; set; }
}
public class ImportFileInput
{
public bool Restart { get; set; }
}
public class ImportUSheetInput
{
public USheetDatas Input { get; set; }
public bool Restart { get; set; }
}
public class InsertTestDataInput
{
public int TestVariableCount { get; set; }
public int TestDeviceCount { get; set; }
public string SlaveUrl { get; set; }
public bool BusinessEnable { get; set; }
public bool Restart { get; set; }
}
public class LastLogDataInput
{
public string File { get; set; }
public int LineCount { get; set; } = 200;
}
public class OnDeviceSelectedItemQueryInput
{
public VirtualizeQueryOption Option { get; set; }
public bool IsCollect { get; set; }
}
public class OnRedundantDevicesQueryInput
{
public VirtualizeQueryOption Option { get; set; }
public long DeviceId { get; set; }
public long ChannelId { get; set; }
}
public class SaveChannelInput
{
public Channel Input { get; set; }
public ItemChangedType Type { get; set; }
public bool Restart { get; set; }
}
public class SaveDeviceInput
{
public Device Input { get; set; }
public ItemChangedType Type { get; set; }
public bool Restart { get; set; }
}
public class SaveVariableInput
{
public Variable Input { get; set; }
public ItemChangedType Type { get; set; }
public bool Restart { get; set; }
}
public class SetChannelLogLevelInput
{
public long Id { get; set; }
public LogLevel LogLevel { get; set; }
}
public class SetDeviceLogLevelInput
{
public long Id { get; set; }
public LogLevel LogLevel { get; set; }
}
public class SetRulesLogLevelInput
{
public long RulesId { get; set; }
public TouchSocket.Core.LogLevel LogLevel { get; set; }
}
public class ImportRequest
{
public IFormFile File { get; set; }
public bool Restart { get; set; }
}
public class PluginQueryPageOptions
{
public QueryPageOptions Options { get; set; }
public PluginTypeEnum PluginType { get; set; }
}
public class KVQueryPageOptions
{
public QueryPageOptions Options { get; set; }
public FilterKeyValueAction FilterKeyValueAction { get; set; }
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,8 +10,6 @@
using BootstrapBlazor.Components;
using LogLevel = Microsoft.Extensions.Logging.LogLevel;
namespace ThingsGateway.Gateway.Application;
/// <summary>
@@ -35,7 +33,7 @@ public class BackendLog : PrimaryIdEntity
/// </summary>
[SugarColumn(ColumnDescription = "日志级别", IsNullable = false)]
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
public LogLevel LogLevel { get; set; }
public Microsoft.Extensions.Logging.LogLevel LogLevel { get; set; }
/// <summary>
/// 日志来源

View File

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

View File

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

View File

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

View File

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

View File

@@ -67,4 +67,9 @@ public static partial class GatewayMapper
public static partial Device AdaptDevice(this Device src);
public static partial Variable AdaptVariable(this Variable src);
public static partial List<PluginInfo> AdaptListPluginInfo(this List<PluginInfo> src);
public static partial Dictionary<string, ImportPreviewOutputBase> AdaptImportPreviewOutputBases(this Dictionary<string, ImportPreviewOutputBase> src);
}

View File

@@ -11,6 +11,7 @@
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using ThingsGateway.Extension.Generic;
@@ -87,6 +88,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))
@@ -395,6 +402,33 @@ public class ChannelRuntimeService : IChannelRuntimeService
}
}
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(IFormFile file, bool restart)
{
try
{
await WaitLock.WaitAsync().ConfigureAwait(false);
var data = await GlobalData.ChannelService.PreviewAsync(file).ConfigureAwait(false);
if (data.Any(a => a.Value.HasError)) return data;
var result = await GlobalData.ChannelService.ImportChannelAsync(data).ConfigureAwait(false);
var newChannelRuntimes = await RuntimeServiceHelper.GetNewChannelRuntimesAsync(result).ConfigureAwait(false);
RuntimeServiceHelper.Init(newChannelRuntimes);
//根据条件重启通道线程
if (restart)
await GlobalData.ChannelThreadManage.RestartChannelAsync(newChannelRuntimes).ConfigureAwait(false);
return data;
}
finally
{
WaitLock.Release();
}
}
public async Task ImportChannelAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart)
{

View File

@@ -11,6 +11,7 @@
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
using MiniExcelLibs;
@@ -409,8 +410,6 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
/// <inheritdoc/>
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile)
{
@@ -418,6 +417,13 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
return await PreviewAsync(path).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IFormFile browserFile)
{
var path = await browserFile.StorageLocal().ConfigureAwait(false);
return await PreviewAsync(path).ConfigureAwait(false);
}
/// <inheritdoc/>
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path)
{
@@ -468,7 +474,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
if (channel == null)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, unportNull));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, unportNull));
return;
}
@@ -491,7 +497,7 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
if (stringBuilder.Length > 0)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, stringBuilder.ToString()));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, stringBuilder.ToString()));
return;
}
@@ -512,19 +518,19 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
if (channel.IsUp && ((dataScope != null && dataScope?.Count > 0 && !dataScope.Contains(channel.CreateOrgId)) || dataScope?.Count == 0 && channel.CreateUserId != UserManager.UserId))
{
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, "Operation not permitted"));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, "Operation not permitted"));
}
else
{
channels.Add(channel);
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), true, null));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), true, null));
}
return;
}
catch (Exception ex)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ex.Message));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, ex.Message));
return;
}
});

View File

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

View File

@@ -11,6 +11,7 @@
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
namespace ThingsGateway.Gateway.Application;
@@ -38,7 +39,5 @@ public interface IChannelRuntimeService : IChannelPageService
Task<bool> CopyAsync(List<Channel> models, Dictionary<Device, List<Variable>> devices, bool restart);
Task<bool> UpdateAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart);
Task<bool> InsertAsync(List<Channel> models, List<Device> devices, List<Variable> variables, bool restart);
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelAsync(IFormFile file, bool restart);
}

View File

@@ -11,6 +11,7 @@
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
using System.Linq.Expressions;
@@ -105,4 +106,5 @@ internal interface IChannelService
Task<HashSet<long>> ImportChannelAsync(List<Channel> upData, List<Channel> insertData);
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path);
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IFormFile browserFile);
}

View File

@@ -11,6 +11,7 @@
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using ThingsGateway.Extension.Generic;
@@ -34,12 +35,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);
@@ -353,6 +357,42 @@ public class DeviceRuntimeService : IDeviceRuntimeService
WaitLock.Release();
}
}
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceAsync(IFormFile file, bool restart)
{
try
{
await WaitLock.WaitAsync().ConfigureAwait(false);
var data = await GlobalData.DeviceService.PreviewAsync(file).ConfigureAwait(false);
if (data.Any(a => a.Value.HasError)) return data;
var deviceids = await GlobalData.DeviceService.ImportDeviceAsync(data).ConfigureAwait(false);
var newDeviceRuntimes = await RuntimeServiceHelper.GetNewDeviceRuntimesAsync(deviceids).ConfigureAwait(false);
if (restart)
{
var newDeciceIds = newDeviceRuntimes.Select(a => a.Id).ToHashSet();
await RuntimeServiceHelper.RemoveDeviceAsync(newDeciceIds).ConfigureAwait(false);
}
//批量修改之后,需要重新加载通道
RuntimeServiceHelper.Init(newDeviceRuntimes);
//根据条件重启通道线程
if (restart)
{
await RuntimeServiceHelper.RestartDeviceAsync(newDeviceRuntimes).ConfigureAwait(false);
}
return data;
}
finally
{
WaitLock.Release();
}
}
public async Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart)
{

View File

@@ -11,6 +11,7 @@
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
using MiniExcelLibs;
@@ -403,14 +404,20 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
return upData.Select(a => a.Id).Concat(insertData.Select(a => a.Id)).ToHashSet();
}
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile)
{
var path = await browserFile.StorageLocal().ConfigureAwait(false); // 上传文件并获取文件路径
return await PreviewAsync(path).ConfigureAwait(false);
}
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IFormFile browserFile)
{
var path = await browserFile.StorageLocal().ConfigureAwait(false); // 上传文件并获取文件路径
return await PreviewAsync(path).ConfigureAwait(false);
}
public async Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path)
{
@@ -495,7 +502,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (device == null)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, ImportNullError));
return;
}
@@ -512,7 +519,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
{
// 如果找不到对应的冗余设备,则添加错误信息到导入预览结果并返回
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, RedundantDeviceError));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, RedundantDeviceError));
return;
}
}
@@ -522,7 +529,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (device.RedundantEnable)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, RedundantDeviceError));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, RedundantDeviceError));
return;
}
}
@@ -536,7 +543,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
{
// 如果找不到对应的通道信息,则添加错误信息到导入预览结果并返回
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ChannelError));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, ChannelError));
return;
}
}
@@ -544,7 +551,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
{
// 如果未提供通道信息,则添加错误信息到导入预览结果并返回
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ChannelError));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, ChannelError));
return;
}
@@ -567,7 +574,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (stringBuilder.Length > 0)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, stringBuilder.ToString()));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, stringBuilder.ToString()));
return;
}
@@ -590,12 +597,12 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
// 将设备添加到设备列表中,并添加成功信息到导入预览结果
if (device.IsUp && ((dataScope != null && dataScope?.Count > 0 && !dataScope.Contains(device.CreateOrgId)) || dataScope?.Count == 0 && device.CreateUserId != UserManager.UserId))
{
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, "Operation not permitted"));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, "Operation not permitted"));
}
else
{
devices.Add(device);
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), true, null));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), true, null));
}
return;
}
@@ -603,7 +610,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
{
// 捕获异常并添加错误信息到导入预览结果
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ex.Message));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, ex.Message));
return;
}
});
@@ -636,7 +643,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (driverPluginType == null)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["NotNull", sheetName]));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, Localizer["NotNull", sheetName]));
return;
}
@@ -677,7 +684,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (propertys.Item1 == null)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, PluginNotNull));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, PluginNotNull));
continue;
}
@@ -685,7 +692,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (!item.TryGetValue(GatewayExportString.DeviceName, out var deviceName))
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, DeviceNotNull));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, DeviceNotNull));
continue;
}
@@ -697,7 +704,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (!hasDevice)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, Localizer["NotNull", value]));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, Localizer["NotNull", value]));
continue;
}
@@ -708,7 +715,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (pluginProp == null)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ImportNullError));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, ImportNullError));
return;
}
@@ -731,7 +738,7 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
if (stringBuilder.Length > 0)
{
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, stringBuilder.ToString()));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, stringBuilder.ToString()));
return;
}
@@ -747,14 +754,14 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
// 更新设备导入预览数据中对应设备的属性信息,并添加成功信息到导入预览结果
deviceImportPreview.Data[value].DevicePropertys = devices;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), true, null));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), true, null));
continue;
}
catch (Exception ex)
{
// 捕获异常并添加错误信息到导入预览结果并返回
importPreviewOutput.HasError = true;
importPreviewOutput.Results.Add((Interlocked.Increment(ref row), false, ex.Message));
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, ex.Message));
return;
}
}

View File

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

View File

@@ -11,6 +11,7 @@
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
namespace ThingsGateway.Gateway.Application
{
@@ -24,5 +25,6 @@ namespace ThingsGateway.Gateway.Application
Task ImportDeviceAsync(Dictionary<string, ImportPreviewOutputBase> input, bool restart);
Task<bool> BatchSaveDeviceAsync(List<Device> input, ItemChangedType type, bool restart);
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IBrowserFile browserFile);
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceAsync(IFormFile file, bool restart);
}
}

View File

@@ -11,6 +11,7 @@
using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
using System.Collections.Concurrent;
using System.Linq.Expressions;
@@ -121,4 +122,5 @@ internal interface IDeviceService
Task UpdateLogAsync(long deviceId, TouchSocket.Core.LogLevel logLevel);
Task<HashSet<long>> ImportDeviceAsync(List<Device> upData, List<Device> insertData);
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(string path);
Task<Dictionary<string, ImportPreviewOutputBase>> PreviewAsync(IFormFile browserFile);
}

View File

@@ -64,9 +64,15 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
CancellationToken.Register(() => GlobalData.DeviceStatusChangeEvent -= GlobalData_DeviceStatusChangeEvent);
_ = Task.Run(() => CheckThreadAsync(CancellationToken));
_ = Task.Run(() => CheckRedundantAsync(CancellationToken));
CheckThreadAsyncTimer = ScheduledTaskHelper.GetTask(ManageHelper.ChannelThreadOptions.CheckInterval.ToString(), CheckThreadAsync, null, LogMessage, CancellationToken);
CheckRedundantAsyncTimer = ScheduledTaskHelper.GetTask("5000", CheckRedundantAsync, null, LogMessage, CancellationToken);
CheckThreadAsyncTimer.Start();
CheckRedundantAsyncTimer.Start();
}
private IScheduledTask CheckThreadAsyncTimer;
private IScheduledTask CheckRedundantAsyncTimer;
private CancellationTokenSource CancellationTokenSource = new();
private CancellationToken CancellationToken;
@@ -674,52 +680,47 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
}
/// <inheritdoc/>
private async Task CheckRedundantAsync(CancellationToken cancellationToken)
private async Task CheckRedundantAsync(object? state, CancellationToken cancellationToken)
{
while (!Disposed)
try
{
try
foreach (var kv in Drivers)
{
await Task.Delay(1000, cancellationToken).ConfigureAwait(false);
foreach (var kv in Drivers)
if (Disposed) return;
var deviceRuntime = kv.Value.CurrentDevice;
if (deviceRuntime != null && GlobalData.IsRedundant(deviceRuntime.Id) && deviceRuntime.Driver != null && deviceRuntime.RedundantSwitchType == RedundantSwitchTypeEnum.Script)
{
if (Disposed) return;
var deviceRuntime = kv.Value.CurrentDevice;
if (deviceRuntime != null && GlobalData.IsRedundant(deviceRuntime.Id) && deviceRuntime.Driver != null && deviceRuntime.RedundantSwitchType == RedundantSwitchTypeEnum.Script)
{
_ = Task.Run(async () =>
if (deviceRuntime.Driver != null)
{
if (deviceRuntime.Driver != null)
if (deviceRuntime.RedundantScript.GetExpressionsResult(deviceRuntime).ToBoolean(true) && (deviceRuntime.Driver?.IsInitSuccess == false || deviceRuntime.Driver?.IsStarted == true) && deviceRuntime.Driver?.DisposedValue != true)
{
if (deviceRuntime.RedundantScript.GetExpressionsResult(deviceRuntime).ToBoolean(true) && (deviceRuntime.Driver?.IsInitSuccess == false || deviceRuntime.Driver?.IsStarted == true) && deviceRuntime.Driver?.DisposedValue != true)
await Task.Delay(deviceRuntime.RedundantScanIntervalTime, cancellationToken).ConfigureAwait(false);//10s后再次检测
if (Disposed) return;
if ((deviceRuntime.RedundantScript.GetExpressionsResult(deviceRuntime).ToBoolean(true) && deviceRuntime.Driver?.IsInitSuccess == false || deviceRuntime.Driver?.IsStarted == true) && deviceRuntime.Driver?.DisposedValue != true && deviceRuntime.RedundantType != RedundantTypeEnum.Standby)
{
await Task.Delay(deviceRuntime.RedundantScanIntervalTime, cancellationToken).ConfigureAwait(false);//10s后再次检测
if (Disposed) return;
if ((deviceRuntime.RedundantScript.GetExpressionsResult(deviceRuntime).ToBoolean(true) && deviceRuntime.Driver?.IsInitSuccess == false || deviceRuntime.Driver?.IsStarted == true) && deviceRuntime.Driver?.DisposedValue != true && deviceRuntime.RedundantType != RedundantTypeEnum.Standby)
//冗余切换
if (GlobalData.IsRedundant(deviceRuntime.Id))
{
//冗余切换
if (GlobalData.IsRedundant(deviceRuntime.Id))
{
if (!cancellationToken.IsCancellationRequested)
await DeviceRedundantThreadAsync(deviceRuntime.Id, cancellationToken).ConfigureAwait(false);
}
if (!cancellationToken.IsCancellationRequested)
await DeviceRedundantThreadAsync(deviceRuntime.Id, cancellationToken).ConfigureAwait(false);
}
}
}
}, cancellationToken);
}
}
}
}
catch (OperationCanceledException)
{
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
LogMessage?.LogError(ex, nameof(CheckRedundantAsync));
}
}
catch (OperationCanceledException)
{
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
LogMessage?.LogError(ex, nameof(CheckRedundantAsync));
}
}
@@ -728,60 +729,57 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
#region
/// <inheritdoc/>
private async Task CheckThreadAsync(CancellationToken cancellationToken)
private async Task CheckThreadAsync(object? state, CancellationToken cancellationToken)
{
while (!Disposed)
{
try
{
//检测设备线程假死
await Task.Delay(ManageHelper.ChannelThreadOptions.CheckInterval, cancellationToken).ConfigureAwait(false);
if (Disposed) return;
var num = Drivers.Count;
foreach (var driver in Drivers.Select(a => a.Value).Where(a => a != null).ToList())
try
{
if (Disposed) return;
var num = Drivers.Count;
foreach (var driver in Drivers.Select(a => a.Value).Where(a => a != null).ToList())
{
try
{
try
if (Disposed) return;
if (driver.CurrentDevice != null)
{
if (Disposed) return;
if (driver.CurrentDevice != null)
//线程卡死/初始化失败检测
if (((driver.IsStarted && driver.CurrentDevice.ActiveTime != DateTime.UnixEpoch.ToLocalTime() && driver.CurrentDevice.ActiveTime.AddMilliseconds(ManageHelper.ChannelThreadOptions.CheckInterval) <= DateTime.Now)
|| (driver.IsInitSuccess == false)) && !driver.DisposedValue)
{
//线程卡死/初始化失败检测
if (((driver.IsStarted && driver.CurrentDevice.ActiveTime != DateTime.UnixEpoch.ToLocalTime() && driver.CurrentDevice.ActiveTime.AddMilliseconds(ManageHelper.ChannelThreadOptions.CheckInterval) <= DateTime.Now)
|| (driver.IsInitSuccess == false)) && !driver.DisposedValue)
{
//如果线程处于暂停状态,跳过
if (driver.CurrentDevice.DeviceStatus == DeviceStatusEnum.Pause)
continue;
//如果初始化失败
if (!driver.IsInitSuccess)
LogMessage?.LogWarning($"Device {driver.CurrentDevice.Name} initialization failed, restarting thread");
else
LogMessage?.LogWarning($"Device {driver.CurrentDevice.Name} thread died, restarting thread");
//重启线程
if (!cancellationToken.IsCancellationRequested)
await RestartDeviceAsync(driver.CurrentDevice, false).ConfigureAwait(false);
break;
}
//如果线程处于暂停状态,跳过
if (driver.CurrentDevice.DeviceStatus == DeviceStatusEnum.Pause)
continue;
//如果初始化失败
if (!driver.IsInitSuccess)
LogMessage?.LogWarning($"Device {driver.CurrentDevice.Name} initialization failed, restarting thread");
else
LogMessage?.LogWarning($"Device {driver.CurrentDevice.Name} thread died, restarting thread");
//重启线程
if (!cancellationToken.IsCancellationRequested)
await RestartDeviceAsync(driver.CurrentDevice, false).ConfigureAwait(false);
break;
}
}
catch (Exception ex)
{
LogMessage?.LogError(ex, nameof(CheckThreadAsync));
}
}
catch (Exception ex)
{
LogMessage?.LogError(ex, nameof(CheckThreadAsync));
}
}
catch (OperationCanceledException)
{
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
LogMessage?.LogError(ex, nameof(CheckThreadAsync));
}
}
catch (OperationCanceledException)
{
}
catch (ObjectDisposedException)
{
}
catch (Exception ex)
{
LogMessage?.LogError(ex, nameof(CheckThreadAsync));
}
}
#endregion
@@ -803,6 +801,9 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
Disposed = true;
try
{
CheckThreadAsyncTimer.SafeDispose();
CheckRedundantAsyncTimer.SafeDispose();
await CancellationTokenSource.SafeCancelAsync().ConfigureAwait(false);
CancellationTokenSource.SafeDispose();
GlobalData.DeviceStatusChangeEvent -= GlobalData_DeviceStatusChangeEvent;

View File

@@ -22,19 +22,61 @@ namespace ThingsGateway.Gateway.Application;
#endif
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]
Task<QueryData<BackendLog>> BackendLogPageAsync(QueryPageOptions option);
[DmtpRpc]
Task<List<BackendLogDayStatisticsOutput>> BackendLogStatisticsByDayAsync(int day);
[DmtpRpc]
Task<bool> BatchEditChannelAsync(List<Channel> models, Channel oldModel, Channel model, bool restart);
[DmtpRpc]
Task<bool> BatchEditDeviceAsync(List<Device> models, Device oldModel, Device model, bool restart);
[DmtpRpc]
Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart);
[DmtpRpc]
Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart);
[DmtpRpc]
Task<TouchSocket.Core.LogLevel> ChannelLogLevelAsync(long id);
[DmtpRpc]
Task<bool> ClearChannelAsync(bool restart);
[DmtpRpc]
Task<bool> ClearDeviceAsync(bool restart);
/// <summary>
/// 清除所有规则
/// </summary>
[DmtpRpc]
Task ClearRulesAsync();
[DmtpRpc]
Task<bool> ClearVariableAsync(bool restart);
[DmtpRpc]
Task CopyChannelAsync(int CopyCount, string CopyChannelNamePrefix, int CopyChannelNameSuffixNumber, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long channelId, bool AutoRestartThread);
[DmtpRpc]
Task CopyDeviceAsync(int CopyCount, string CopyDeviceNamePrefix, int CopyDeviceNameSuffixNumber, long deviceId, bool AutoRestartThread);
[DmtpRpc]
Task CopyVariableAsync(List<Variable> Model, int CopyCount, string CopyVariableNamePrefix, int CopyVariableNameSuffixNumber, bool AutoRestartThread);
[DmtpRpc]
Task DeleteBackendLogAsync();
[DmtpRpc]
Task<bool> DeleteChannelAsync(List<long> ids, bool restart);
[DmtpRpc]
Task<bool> DeleteDeviceAsync(List<long> ids, bool restart);
[DmtpRpc]
Task DeleteLogDataAsync(string path);
/// <summary>
/// 删除 RpcLog 表中的所有记录
@@ -45,6 +87,93 @@ public interface IManagementRpcServer : IRpcServer
[DmtpRpc]
Task DeleteRpcLogAsync();
[DmtpRpc]
Task DeleteRuleRuntimesAsync(List<long> ids);
/// <summary>
/// 删除规则
/// </summary>
/// <param name="ids">待删除规则的ID列表</param>
[DmtpRpc]
Task<bool> DeleteRulesAsync(List<long> ids);
[DmtpRpc]
Task<bool> DeleteVariableAsync(List<long> ids, bool restart);
[DmtpRpc]
Task<TouchSocket.Core.LogLevel> DeviceLogLevelAsync(long id);
[DmtpRpc]
Task DeviceRedundantThreadAsync(long id);
/// <summary>
/// 修改冗余设置
/// </summary>
/// <param name="input"></param>
[DmtpRpc]
Task EditRedundancyOptionAsync(RedundancyOptions input);
[DmtpRpc]
Task EditRuleRuntimesAsync(Rules rules);
[DmtpRpc]
Task<USheetDatas> ExportChannelAsync(List<Channel> channels);
[DmtpRpc]
Task<string> ExportChannelFileAsync(GatewayExportFilter exportFilter);
[DmtpRpc]
Task<USheetDatas> ExportDeviceAsync(List<Device> devices);
[DmtpRpc]
Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter);
[DmtpRpc]
Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder);
[DmtpRpc]
Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter);
/// <summary>
/// 从缓存/数据库获取全部信息
/// </summary>
/// <returns>规则列表</returns>
[DmtpRpc]
Task<List<Rules>> GetAllRulesAsync();
[DmtpRpc]
Task<List<Channel>> GetChannelListAsync(QueryPageOptions options, int max = 0);
[DmtpRpc]
Task<string> GetChannelNameAsync(long channelId);
[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]
Task<IEnumerable<AlarmVariable>> GetCurrentUserRealAlarmVariablesAsync();
[DmtpRpc]
Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync();
[DmtpRpc]
Task<List<Device>> GetDeviceListAsync(QueryPageOptions option, int v);
[DmtpRpc]
Task<string> GetDeviceNameAsync(long redundantDeviceId);
[DmtpRpc]
Task<string> GetDevicePluginNameAsync(long id);
[DmtpRpc]
Task<OperResult<List<string>>> GetLogFilesAsync(string directoryPath);
[DmtpRpc]
Task<List<BackendLog>> GetNewBackendLogAsync();
/// <summary>
/// 获取最新的十条 RpcLog 记录
/// </summary>
@@ -52,6 +181,118 @@ public interface IManagementRpcServer : IRpcServer
[DmtpRpc]
Task<List<RpcLog>> GetNewRpcLogAsync();
[DmtpRpc]
Task<string> GetPluginNameAsync(long channelId);
/// <summary>
/// 根据插件类型获取信息
/// </summary>
/// <param name="pluginType"></param>
/// <returns></returns>
[DmtpRpc]
Task<List<PluginInfo>> GetPluginsAsync(PluginTypeEnum? pluginType = null);
/// <summary>
/// 获取冗余设置
/// </summary>
[DmtpRpc]
Task<RedundancyOptions> GetRedundancyAsync();
[DmtpRpc]
Task<Rules> GetRuleRuntimesAsync(long rulesId);
[DmtpRpc]
Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int v);
[DmtpRpc]
Task ImportChannelAsync(List<Channel> upData, List<Channel> insertData, bool restart);
[DmtpRpc]
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelFileAsync(string filePath, bool restart);
[DmtpRpc]
Task<Dictionary<string, ImportPreviewOutputBase>> ImportChannelUSheetDatasAsync(USheetDatas input, bool restart);
[DmtpRpc]
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceFileAsync(string filePath, bool restart);
[DmtpRpc]
Task<Dictionary<string, ImportPreviewOutputBase>> ImportDeviceUSheetDatasAsync(USheetDatas input, bool restart);
[DmtpRpc]
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart);
[DmtpRpc]
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart);
[DmtpRpc]
Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart);
[DmtpRpc]
Task<bool> IsRedundantDeviceAsync(long id);
[DmtpRpc]
Task<OperResult<List<LogData>>> LastLogDataAsync(string file, int lineCount = 200);
[DmtpRpc]
Task<QueryData<ChannelRuntime>> OnChannelQueryAsync(QueryPageOptions options);
[DmtpRpc]
Task<QueryData<SelectedItem>> OnChannelSelectedItemQueryAsync(VirtualizeQueryOption option);
[DmtpRpc]
Task<QueryData<DeviceRuntime>> OnDeviceQueryAsync(QueryPageOptions options);
[DmtpRpc]
Task<QueryData<SelectedItem>> OnDeviceSelectedItemQueryAsync(VirtualizeQueryOption option, bool isCollect);
[DmtpRpc]
Task<QueryData<SelectedItem>> OnRedundantDevicesQueryAsync(VirtualizeQueryOption option, long deviceId, long channelId);
[DmtpRpc]
Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options);
[DmtpRpc]
Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData);
[DmtpRpc]
Task PauseThreadAsync(long id);
/// <summary>
/// 分页显示插件
/// </summary>
[DmtpRpc]
Task<QueryData<PluginInfo>> PluginPageAsync(QueryPageOptions options, PluginTypeEnum? pluginTypeEnum = null);
[DmtpRpc]
Task RedundancyForcedSync();
[DmtpRpc]
Task<TouchSocket.Core.LogLevel> RedundancyLogLevelAsync();
[DmtpRpc]
Task<string> RedundancyLogPathAsync();
/// <summary>
/// 重载插件
/// </summary>
[DmtpRpc]
Task ReloadPluginAsync();
[DmtpRpc]
Task RestartChannelAsync(long channelId);
[DmtpRpc]
Task RestartChannelsAsync();
[DmtpRpc]
Task RestartDeviceAsync(long id, bool deleteCache);
[DmtpRpc]
Task RestartServerAsync();
[DmtpRpc]
Task<Dictionary<string, Dictionary<string, OperResult<object>>>> RpcAsync(ICallContext callContext, Dictionary<string, Dictionary<string, string>> deviceDatas);
/// <summary>
/// 分页查询 RpcLog 数据
/// </summary>
@@ -67,135 +308,11 @@ public interface IManagementRpcServer : IRpcServer
/// <returns>按天统计的结果列表</returns>
[DmtpRpc]
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]
Task<TouchSocket.Core.LogLevel> RulesLogLevelAsync(long rulesId);
[DmtpRpc]
Task SetRulesLogLevelAsync(long rulesId, TouchSocket.Core.LogLevel logLevel);
[DmtpRpc]
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>
/// 报表查询
@@ -205,6 +322,20 @@ public interface IManagementRpcServer : IRpcServer
[DmtpRpc]
Task<QueryData<Rules>> RulesPageAsync(QueryPageOptions option, FilterKeyValueAction filterKeyValueAction = null);
[DmtpRpc]
Task<bool> SaveChannelAsync(Channel input, ItemChangedType type, bool restart);
[DmtpRpc]
Task<bool> SaveDeviceAsync(Device input, ItemChangedType type, bool restart);
/// <summary>
/// 添加插件
/// </summary>
/// <param name="plugin"></param>
/// <returns></returns>
[DmtpRpc]
Task SavePluginByPathAsync(PluginAddPathInput plugin);
/// <summary>
/// 保存规则
/// </summary>
@@ -213,158 +344,42 @@ public interface IManagementRpcServer : IRpcServer
[DmtpRpc]
Task<bool> SaveRulesAsync(Rules input, ItemChangedType type);
[DmtpRpc]
Task<string> GetPluginNameAsync(long channelId);
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart);
[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);
Task SetRedundancyLogLevelAsync(TouchSocket.Core.LogLevel logLevel);
[DmtpRpc]
Task<string> ExportDeviceFileAsync(GatewayExportFilter exportFilter);
Task SetRulesLogLevelAsync(long rulesId, TouchSocket.Core.LogLevel logLevel);
[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);
Task<bool> StartBusinessChannelEnableAsync();
[DmtpRpc]
Task<List<SelectedItem>> GetDeviceItemsAsync(bool isCollect);
Task<bool> StartCollectChannelEnableAsync();
[DmtpRpc]
Task<string> GetDevicePluginNameAsync(long id);
Task StartRedundancyTaskAsync();
[DmtpRpc]
Task<bool> BatchEditVariableAsync(List<Variable> models, Variable oldModel, Variable model, bool restart);
Task StopRedundancyTaskAsync();
[DmtpRpc]
Task<bool> DeleteVariableAsync(List<long> ids, bool restart);
Task<AuthorizeInfo> TryAuthorizeAsync(string password);
[DmtpRpc]
Task<bool> ClearVariableAsync(bool restart);
Task<AuthorizeInfo> TryGetAuthorizeInfoAsync();
[DmtpRpc]
Task InsertTestDataAsync(int testVariableCount, int testDeviceCount, string slaveUrl, bool businessEnable, bool restart);
Task UnAuthorizeAsync();
[DmtpRpc]
Task<bool> BatchSaveVariableAsync(List<Variable> input, ItemChangedType type, bool restart);
[DmtpRpc]
Task<bool> SaveVariableAsync(Variable input, ItemChangedType type, bool restart);
[DmtpRpc]
Task CopyVariableAsync(List<Variable> Model, int CopyCount, string CopyVariableNamePrefix, int CopyVariableNameSuffixNumber, bool AutoRestartThread);
[DmtpRpc]
Task<QueryData<VariableRuntime>> OnVariableQueryAsync(QueryPageOptions options);
[DmtpRpc]
Task<string> ExportVariableFileAsync(GatewayExportFilter exportFilter);
[DmtpRpc]
Task<List<Variable>> GetVariableListAsync(QueryPageOptions option, int v);
[DmtpRpc]
Task<USheetDatas> ExportVariableAsync(List<Variable> models, string? sortName, SortOrder sortOrder);
[DmtpRpc]
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableUSheetDatasAsync(USheetDatas data, bool restart);
[DmtpRpc]
Task<OperResult<object>> OnWriteVariableAsync(long id, string writeData);
[DmtpRpc]
Task<Dictionary<string, ImportPreviewOutputBase>> ImportVariableFileAsync(string filePath, bool restart);
[DmtpRpc]
Task<Dictionary<long, Tuple<string, string>>> GetDeviceIdNamesAsync();
Task<string> UUIDAsync();
}

View File

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

View File

@@ -30,8 +30,8 @@ internal sealed class ManagementHostedService : BackgroundService
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
await Task.Yield();
_ = RemoteClientManagementTask.StartAsync(stoppingToken);
_ = RemoteServerManagementTask.StartAsync(stoppingToken);
await RemoteClientManagementTask.StartAsync(stoppingToken).ConfigureAwait(false);
await RemoteServerManagementTask.StartAsync(stoppingToken).ConfigureAwait(false);
}
public override async Task StopAsync(CancellationToken cancellationToken)

View File

@@ -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 DeleteLogDataAsync(string path) => App.GetService<ITextFileReadService>().DeleteLogDataAsync(path);
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();
}

View File

@@ -48,6 +48,8 @@ public partial class ManagementTask : AsyncDisposableObject
_logger?.Log_Out(logLevel, source, message, exception);
}
private bool success = true;
private IScheduledTask _scheduledTask;
public async Task StartAsync(CancellationToken cancellationToken)
{
if (!_managementOptions.Enable) return;
@@ -60,25 +62,23 @@ public partial class ManagementTask : AsyncDisposableObject
{
_tcpDmtpClient ??= await GetTcpDmtpClient().ConfigureAwait(false);
}
while (!cancellationToken.IsCancellationRequested)
_scheduledTask = ScheduledTaskHelper.GetTask("10000", OpenTask, null, LogMessage, cancellationToken);
_scheduledTask.Start();
}
private async Task OpenTask(object? state, CancellationToken cancellationToken)
{
try
{
try
{
await EnsureChannelOpenAsync(cancellationToken).ConfigureAwait(false);
success = true;
}
catch (Exception ex)
{
if (success)
LogMessage?.LogWarning(ex, "Start");
success = false;
}
finally
{
await Task.Delay(10000, cancellationToken).ConfigureAwait(false);
}
await EnsureChannelOpenAsync(cancellationToken).ConfigureAwait(false);
success = true;
}
catch (Exception ex)
{
if (success)
LogMessage?.LogWarning(ex, "Start");
success = false;
}
}
@@ -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);
}
});
})
@@ -216,6 +223,7 @@ public partial class ManagementTask : AsyncDisposableObject
protected override async Task DisposeAsync(bool disposing)
{
_scheduledTask?.SafeDispose();
if (_tcpDmtpClient != null)
{
await _tcpDmtpClient.CloseAsync().ConfigureAwait(false);

Some files were not shown because too many files have changed in this diff Show More