Compare commits

...

9 Commits

Author SHA1 Message Date
2248356998 qq.com
7d406de29f 调整 runtimeconfig.template.json 文件 2025-10-22 00:47:33 +08:00
2248356998 qq.com
81f0ef466a perf: 优化文本日志性能 2025-10-21 17:40:21 +08:00
2248356998 qq.com
3f2d6b133c 更新依赖包 2025-10-21 17:26:46 +08:00
Diego
e776dc67eb Remove language support from wiki.json
Removed supported languages and default language settings.
2025-10-21 15:58:01 +08:00
devin-ai-integration[bot]
bc5827d140 Update wiki.json (#26)
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-10-21 15:54:04 +08:00
2248356998 qq.com
21838bf4af perf: 异步池化性能 2025-10-21 10:56:11 +08:00
2248356998 qq.com
6090108597 build: 10.11.118 2025-10-20 22:15:24 +08:00
2248356998 qq.com
b47b9e6f43 build: 10.11.118 2025-10-20 22:15:10 +08:00
2248356998 qq.com
18d1cffb2d perf: 异步池化性能 2025-10-20 20:32:31 +08:00
29 changed files with 435 additions and 530 deletions

View File

@@ -19,12 +19,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Riok.Mapperly" Version="4.3.0" ExcludeAssets="runtime" PrivateAssets="all">
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Rougamo.Fody" Version="5.0.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
<PackageReference Include="System.Formats.Asn1" Version="8.0.2" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.10" />
<PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" />
</ItemGroup>

View File

@@ -92,7 +92,8 @@ public class Startup : AppStartup
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
options.MaxBufferedUnacknowledgedRenderBatches = 20;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10);
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
})
.AddHubOptions(options =>
{
@@ -103,6 +104,7 @@ public class Startup : AppStartup
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
#else
@@ -112,7 +114,8 @@ public class Startup : AppStartup
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
options.MaxBufferedUnacknowledgedRenderBatches = 20;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10);
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
}).AddHubOptions(options =>
{
//单个传入集线器消息的最大大小。默认 32 KB

View File

@@ -15,15 +15,11 @@
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>wwwroot\favicon.ico</ApplicationIcon>
<CETCompat>false</CETCompat>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<CETCompat>false</CETCompat>
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>

View File

@@ -27,7 +27,7 @@ internal class CacheManager
{
private IMemoryCache Cache { get; set; }
private IServiceProvider Provider { get; set; }
private static IServiceProvider Provider => App.RootServices;
[NotNull]
private static CacheManager? Instance { get; set; }
@@ -40,8 +40,7 @@ internal class CacheManager
static CacheManager()
{
Instance = new();
Instance.Provider = App.RootServices;
Instance.Cache = Instance.Provider.GetRequiredService<IMemoryCache>();
Instance.Cache = Provider.GetRequiredService<IMemoryCache>();
Options = App.RootServices.GetRequiredService<IOptions<BootstrapBlazorOptions>>().Value;
}
@@ -236,7 +235,7 @@ internal class CacheManager
/// <returns></returns>
public static IStringLocalizer? CreateLocalizerByType(Type resourceSource) => resourceSource.Assembly.IsDynamic
? null
: Instance.Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource);
: Provider.GetRequiredService<IStringLocalizerFactory>().Create(resourceSource);
/// <summary>
/// 获得 <see cref="JsonLocalizationOptions"/> 值
@@ -244,7 +243,7 @@ internal class CacheManager
/// <returns></returns>
private static JsonLocalizationOptions GetJsonLocalizationOption()
{
var localizationOptions = Instance.Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>();
var localizationOptions = Provider.GetRequiredService<IOptions<JsonLocalizationOptions>>();
return localizationOptions.Value;
}
/// <summary>
@@ -253,7 +252,7 @@ internal class CacheManager
/// <returns></returns>
private static BootstrapBlazorOptions GetBootstrapBlazorOption()
{
var localizationOptions = Instance.Provider.GetRequiredService<IOptions<BootstrapBlazorOptions>>();
var localizationOptions = Provider.GetRequiredService<IOptions<BootstrapBlazorOptions>>();
return localizationOptions.Value;
}
/// <summary>
@@ -269,7 +268,7 @@ internal class CacheManager
return null;
}
IStringLocalizer? ret = null;
var factories = Instance.Provider.GetServices<IStringLocalizerFactory>();
var factories = Provider.GetServices<IStringLocalizerFactory>();
var factory = factories.LastOrDefault(a => a is not JsonStringLocalizerFactory);
if (factory != null)
{
@@ -345,7 +344,7 @@ internal class CacheManager
/// <param name="typeName"></param>
/// <param name="includeParentCultures"></param>
/// <returns></returns>
public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
#endregion
#region DisplayName

View File

@@ -14,7 +14,7 @@
<ItemGroup>
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.7" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="BootstrapBlazor" Version="9.11.2" />
<PackageReference Include="BootstrapBlazor" Version="9.11.4" />
</ItemGroup>
<ItemGroup>

View File

@@ -25,17 +25,11 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
/// <summary>最大个数。默认00表示无上限</summary>
public Int32 Max { get; set; } = 0;
/// <summary>最小个数。默认1</summary>
public Int32 Min { get; set; } = 1;
private readonly object _syncRoot = new();
/// <summary>基础空闲集合。只保存最小个数,最热部分</summary>
private readonly Stack<T> _free = new();
/// <summary>扩展空闲集合。保存最小个数以外部分</summary>
private readonly Queue<T> _free2 = new();
/// <summary>借出去的放在这</summary>
private readonly HashSet<T> _busy = new();
@@ -79,7 +73,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
if (_inited) return;
_inited = true;
WriteLog($"Init {typeof(T).FullName} Min={Min} Max={Max}");
WriteLog($"Init {typeof(T).FullName} Max={Max}");
}
}
#endregion
@@ -99,26 +93,20 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
pi = _free.Pop();
_FreeCount--;
}
else if (_free2.Count > 0)
{
pi = _free2.Dequeue();
_FreeCount--;
}
else
{
var count = BusyCount;
if (Max > 0 && count >= Max)
if (Max > 0 && BusyCount >= Max)
{
var msg = $"申请失败,已有 {count:n0} 达到或超过最大值 {Max:n0}";
var msg = $"申请失败,已有 {BusyCount:n0} 达到或超过最大值 {Max:n0}";
WriteLog("Acquire Max " + msg);
throw new Exception(Name + " " + msg);
}
pi = OnCreate();
if (count == 0) Init();
if (BusyCount == 0) Init();
#if DEBUG
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, count + 1);
WriteLog("Acquire Create Free={0} Busy={1}", FreeCount, BusyCount + 1);
#endif
}
}
@@ -177,10 +165,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
}
lock (_syncRoot)
{
if (_FreeCount < Min)
_free.Push(value);
else
_free2.Enqueue(value);
_free.Push(value);
_FreeCount++;
}
@@ -214,12 +199,6 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
OnDispose(pi);
}
while (_free2.Count > 0)
{
var pi = _free2.Dequeue();
OnDispose(pi);
}
_FreeCount = 0;
foreach (var item in _busy)

View File

@@ -61,7 +61,8 @@ public class TextFileLog : Logger, IDisposable
MaxBytes = set.LogFileMaxBytes;
Backups = set.LogFileBackups;
_Timer = new TimerX(DoWriteAndClose, null, 0_000, 5_000) { Async = true };
_Timer = new TimerX(DoWriteAndClose, null, 0_000, 5_000, nameof(TextFileLog)) { Async = true };
_WriteTimer = new TimerX(DoWrite, null, 0_000, 1000, nameof(TextFileLog)) { Async = true };
}
private static readonly NonBlockingDictionary<String, TextFileLog> cache = new(StringComparer.OrdinalIgnoreCase);
@@ -96,6 +97,7 @@ public class TextFileLog : Logger, IDisposable
protected virtual void Dispose(Boolean disposing)
{
_Timer.TryDispose();
_WriteTimer.TryDispose();
// 销毁前把队列日志输出
if (Interlocked.CompareExchange(ref _writing, 1, 0) == 0) WriteAndClose(DateTime.MinValue);
@@ -176,6 +178,7 @@ public class TextFileLog : Logger, IDisposable
#region
private readonly TimerX? _Timer;
private readonly TimerX? _WriteTimer;
private readonly ConcurrentQueue<String> _Logs = new();
private volatile Int32 _logCount;
private Int32 _writing;
@@ -223,7 +226,10 @@ public class TextFileLog : Logger, IDisposable
// 连续5秒没日志就关闭
_NextClose = now.AddSeconds(5);
}
private void DoWrite(Object? state)
{
WriteLog();
}
/// <summary>关闭文件</summary>
private void DoWriteAndClose(Object? state)
{
@@ -323,7 +329,6 @@ public class TextFileLog : Logger, IDisposable
// 推入队列
Enqueue($"{e.GetAndReset()}");
WriteLog();
}
protected bool Check()
@@ -340,35 +345,17 @@ public class TextFileLog : Logger, IDisposable
}
protected void WriteLog()
{
// 异步写日志,实时。即使这里错误,定时器那边仍然会补上
// 写日志,实时。即使这里错误,定时器那边仍然会补上
if (Interlocked.CompareExchange(ref _writing, 1, 0) == 0)
{
// 调试级别 或 致命错误 同步写日志
if (Setting.Current.LogLevel <= LogLevel.Debug || Level >= LogLevel.Error)
try
{
try
{
WriteFile();
}
finally
{
_writing = 0;
}
if (!_Logs.IsEmpty) WriteFile();
}
else
finally
{
ThreadPool.UnsafeQueueUserWorkItem(s =>
{
try
{
WriteFile();
}
catch { }
finally
{
_writing = 0;
}
}, null);
_writing = 0;
}
}
}

View File

@@ -12,18 +12,13 @@ namespace PooledAwait
{
private static ObjectPoolLock<T> pool = new();
[ThreadStatic]
private static T? ts_local;
/// <summary>
/// Gets an instance from the pool if possible
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? TryGet()
{
var tmp = ts_local;
ts_local = null;
return tmp ?? pool.Get();
return pool.Get();
}
/// <summary>
@@ -34,11 +29,6 @@ namespace PooledAwait
{
if (value != null)
{
if (ts_local == null)
{
ts_local = value;
return;
}
pool.Return(value);
}
}

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.11.116</PluginVersion>
<ProPluginVersion>10.11.116</ProPluginVersion>
<DefaultVersion>10.11.116</DefaultVersion>
<PluginVersion>10.12.3</PluginVersion>
<ProPluginVersion>10.12.3</ProPluginVersion>
<DefaultVersion>10.12.3</DefaultVersion>
<AuthenticationVersion>10.11.6</AuthenticationVersion>
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
<NET8Version>8.0.21</NET8Version>

View File

@@ -8,6 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using PooledAwait;
using ThingsGateway.Foundation.Extension.String;
using TouchSocket.SerialPorts;
@@ -26,65 +28,33 @@ public static class ChannelOptionsExtensions
/// <param name="e">接收数据</param>
/// <param name="funcs">事件</param>
/// <returns></returns>
internal static ValueTask OnChannelReceivedEvent(
this IClientChannel clientChannel,
ReceivedDataEventArgs e,
ChannelReceivedEventHandler funcs)
internal static ValueTask OnChannelReceivedEvent(this IClientChannel clientChannel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
{
clientChannel.ThrowIfNull(nameof(IClientChannel));
e.ThrowIfNull(nameof(ReceivedDataEventArgs));
funcs.ThrowIfNull(nameof(ChannelReceivedEventHandler));
if (funcs.Count == 0) return EasyValueTask.CompletedTask;
return OnChannelReceivedEvent(clientChannel, e, funcs);
return InvokeHandlersSequentially(clientChannel, e, funcs);
}
private static ValueTask InvokeHandlersSequentially(
IClientChannel clientChannel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
{
var enumerator = new HandlerEnumerator(clientChannel, e, funcs);
return enumerator.MoveNextAsync();
}
private struct HandlerEnumerator
{
private readonly IClientChannel _channel;
private readonly ReceivedDataEventArgs _e;
private readonly ChannelReceivedEventHandler _funcs;
private int _index;
public HandlerEnumerator(IClientChannel channel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
static async PooledValueTask OnChannelReceivedEvent(IClientChannel clientChannel, ReceivedDataEventArgs e, ChannelReceivedEventHandler funcs)
{
_channel = channel;
_e = e;
_funcs = funcs;
_index = -1;
}
public ValueTask MoveNextAsync()
{
_index++;
if (_index >= _funcs.Count) return default;
var func = _funcs[_index];
if (func == null) return MoveNextAsync();
bool isLast = _index == _funcs.Count - 1;
var vt = func.Invoke(_channel, _e, isLast);
if (vt.IsCompletedSuccessfully)
if (funcs.Count > 0)
{
if (_e.Handled) return default;
return MoveNextAsync();
for (int i = 0; i < funcs.Count; i++)
{
var func = funcs[i];
if (func == null) continue;
var taskResult= func.Invoke(clientChannel, e, i == funcs.Count - 1);
if(!taskResult.IsCompletedSuccessfully)
{
await taskResult.ConfigureAwait(false);
}
if (e.Handled)
{
break;
}
}
}
return Awaited(vt);
}
private async ValueTask Awaited(ValueTask vt)
{
await vt.ConfigureAwait(false);
if (!_e.Handled)
await MoveNextAsync().ConfigureAwait(false);
}
}

View File

@@ -128,7 +128,6 @@ public class TextFileLogger : ThingsGateway.NewLife.Log.TextFileLog, TouchSocket
// 推入队列
Enqueue(stringBuilder.ToString());
WriteLog();
}
/// <inheritdoc/>

View File

@@ -43,6 +43,16 @@ public struct OperResult<T> : IOperResult<T>
Exception = operResult.Exception;
ErrorType = operResult.ErrorType;
}
/// <summary>
/// 从另一个操作对象中赋值信息
/// </summary>
public OperResult(OperResult operResult)
{
OperCode = operResult.OperCode;
ErrorMessage = operResult.ErrorMessage;
Exception = operResult.Exception;
ErrorType = operResult.ErrorType;
}
/// <summary>
/// 传入错误信息
@@ -162,7 +172,13 @@ public struct OperResult<T, T2> : IOperResult<T, T2>
Exception = operResult.Exception;
ErrorType = operResult.ErrorType;
}
public OperResult(OperResult operResult)
{
OperCode = operResult.OperCode;
ErrorMessage = operResult.ErrorMessage;
Exception = operResult.Exception;
ErrorType = operResult.ErrorType;
}
/// <summary>
/// 传入错误信息
/// </summary>
@@ -275,7 +291,13 @@ public struct OperResult<T, T2, T3> : IOperResult<T, T2, T3>
Exception = operResult.Exception;
ErrorType = operResult.ErrorType;
}
public OperResult(OperResult operResult)
{
OperCode = operResult.OperCode;
ErrorMessage = operResult.ErrorMessage;
Exception = operResult.Exception;
ErrorType = operResult.ErrorType;
}
/// <summary>
/// 传入错误信息
/// </summary>
@@ -389,7 +411,13 @@ public struct OperResult : IOperResult
Exception = operResult.Exception;
ErrorType = operResult.ErrorType;
}
public OperResult(OperResult operResult)
{
OperCode = operResult.OperCode;
ErrorMessage = operResult.ErrorMessage;
Exception = operResult.Exception;
ErrorType = operResult.ErrorType;
}
/// <summary>
/// 传入错误信息
/// </summary>

View File

@@ -380,7 +380,7 @@ public abstract partial class CollectBase : DriverBase
if (cancellationToken.IsCancellationRequested) return;
CancellationToken readToken = default;
var readerLockTask = @this.ReadWriteLock.ReaderLockAsync(cancellationToken);
if (!readerLockTask.IsCompleted)
if (!readerLockTask.IsCompletedSuccessfully)
{
readToken = await readerLockTask.ConfigureAwait(false);
}
@@ -403,7 +403,7 @@ public abstract partial class CollectBase : DriverBase
OperResult<ReadOnlyMemory<byte>> readResult = default;
var readTask = @this.ReadSourceAsync(variableSourceRead, allToken);
if (!readTask.IsCompleted)
if (!readTask.IsCompletedSuccessfully)
{
readResult = await readTask.ConfigureAwait(false);
}
@@ -435,7 +435,7 @@ 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 readTask1 = @this.ReadSourceAsync(variableSourceRead, allToken);
if (!readTask1.IsCompleted)
if (!readTask1.IsCompletedSuccessfully)
{
readResult = await readTask1.ConfigureAwait(false);
}
@@ -490,195 +490,6 @@ public abstract partial class CollectBase : DriverBase
}
}
// private ValueTask ReadVariableSource(object? state, CancellationToken cancellationToken)
// {
// var enumerator = new ReadVariableSourceEnumerator(this, state, cancellationToken);
// return enumerator.MoveNextAsync();
// }
// private struct ReadVariableSourceEnumerator
// {
// private readonly CollectBase _owner;
// private readonly object? _state;
// private readonly CancellationToken _cancellationToken;
// private VariableSourceRead _variableSourceRead;
// private CancellationToken _readToken;
// private CancellationToken _allToken;
// private OperResult<ReadOnlyMemory<byte>> _readResult;
// private int _readErrorCount;
// private ValueTask<CancellationToken> _readerLockTask;
// private ValueTask<OperResult<ReadOnlyMemory<byte>>> _readTask;
// private int _step;
// public ReadVariableSourceEnumerator(CollectBase owner, object? state, CancellationToken cancellationToken)
// {
// _owner = owner;
// _state = state;
// _cancellationToken = cancellationToken;
// _variableSourceRead = default!;
// _readToken = default;
// _allToken = default;
// _readResult = default;
// _readErrorCount = 0;
// _readerLockTask = default;
// _readTask = default;
// _step = 0;
// }
// public ValueTask MoveNextAsync()
// {
// switch (_step)
// {
// case 0:
// if (_state is not VariableSourceRead vsr) return default;
// _variableSourceRead = vsr;
// if (_owner.Pause) return default;
// if (_cancellationToken.IsCancellationRequested) return default;
//#pragma warning disable CA2012 // 正确使用 ValueTask
// _readerLockTask = _owner.ReadWriteLock.ReaderLockAsync(_cancellationToken);
//#pragma warning restore CA2012 // 正确使用 ValueTask
// if (!_readerLockTask.IsCompleted)
// {
// _step = 1;
// return AwaitReaderLock();
// }
// _readToken = _readerLockTask.Result;
// goto case 2;
// case 1:
// _readToken = _readerLockTask.Result;
// goto case 2;
// case 2:
// if (_readToken.IsCancellationRequested)
// {
// return _owner.ReadVariableSource(_state, _cancellationToken);
// }
// var allTokenSource = _owner._linkedCtsCache.GetLinkedTokenSource(_cancellationToken, _readToken);
// _allToken = allTokenSource.Token;
//#pragma warning disable CA2012 // 正确使用 ValueTask
// _readTask = _owner.ReadSourceAsync(_variableSourceRead, _allToken);
//#pragma warning restore CA2012 // 正确使用 ValueTask
// if (!_readTask.IsCompleted)
// {
// _step = 3;
// return AwaitRead();
// }
// _readResult = _readTask.Result;
// goto case 4;
// case 3:
// _readResult = _readTask.Result;
// goto case 4;
// case 4:
// while (!_readResult.IsSuccess && _readErrorCount < _owner.CollectProperties.RetryCount)
// {
// if (_owner.Pause) return default;
// if (_cancellationToken.IsCancellationRequested) return default;
// if (_readToken.IsCancellationRequested)
// {
// return _owner.ReadVariableSource(_state, _cancellationToken);
// }
// _readErrorCount++;
// if (_owner.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
// _owner.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] failed - {3}",
// _owner.DeviceName, _variableSourceRead?.RegisterAddress, _variableSourceRead?.Length, _readResult.ErrorMessage));
//#pragma warning disable CA2012 // 正确使用 ValueTask
// _readTask = _owner.ReadSourceAsync(_variableSourceRead, _allToken);
//#pragma warning restore CA2012 // 正确使用 ValueTask
// if (!_readTask.IsCompleted)
// {
// _step = 5;
// return AwaitReadRetry();
// }
// _readResult = _readTask.Result;
// }
// goto case 6;
// case 5:
// _readResult = _readTask.Result;
// _step = 4;
// return MoveNextAsync();
// case 6:
// if (_readResult.IsSuccess)
// {
// if (_owner.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
// _owner.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}",
// _owner.DeviceName, _variableSourceRead?.RegisterAddress, _variableSourceRead?.Length, _readResult.Content.Span.ToHexString(' ')));
// _owner.CurrentDevice.SetDeviceStatus(TimerX.Now, null);
// }
// else
// {
// if (_cancellationToken.IsCancellationRequested) return default;
// if (_readToken.IsCancellationRequested)
// {
// return _owner.ReadVariableSource(_state, _cancellationToken);
// }
// if (_variableSourceRead.LastErrorMessage != _readResult.ErrorMessage)
// {
// if (!_cancellationToken.IsCancellationRequested)
// _owner.LogMessage?.LogWarning(_readResult.Exception, string.Format(AppResource.CollectFail, _owner.DeviceName,
// _variableSourceRead?.RegisterAddress, _variableSourceRead?.Length, _readResult.ErrorMessage));
// }
// else
// {
// if (!_cancellationToken.IsCancellationRequested && _owner.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
// _owner.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data failed - {3}",
// _owner.DeviceName, _variableSourceRead?.RegisterAddress, _variableSourceRead?.Length, _readResult.ErrorMessage));
// }
// _variableSourceRead.LastErrorMessage = _readResult.ErrorMessage;
// _owner.CurrentDevice.SetDeviceStatus(TimerX.Now, null, _readResult.ErrorMessage);
// var time = DateTime.Now;
// foreach (var item in _variableSourceRead.VariableRuntimes)
// {
// item.SetValue(null, time, isOnline: false);
// }
// }
// break;
// }
// return default;
// }
// private async ValueTask AwaitReaderLock()
// {
// await _readerLockTask.ConfigureAwait(false);
// _step = 1;
// await MoveNextAsync().ConfigureAwait(false);
// }
// private async ValueTask AwaitRead()
// {
// await _readTask.ConfigureAwait(false);
// _step = 3;
// await MoveNextAsync().ConfigureAwait(false);
// }
// private async ValueTask AwaitReadRetry()
// {
// await _readTask.ConfigureAwait(false);
// _step = 5;
// await MoveNextAsync().ConfigureAwait(false);
// }
// }
#endregion

View File

@@ -172,7 +172,7 @@ public abstract class CollectFoundationBase : CollectBase
// 从协议读取数据
OperResult<ReadOnlyMemory<byte>> read = default;
var readTask = @this.FoundationDevice.ReadAsync(variableSourceRead.AddressObject, cancellationToken);
if (!readTask.IsCompleted)
if (!readTask.IsCompletedSuccessfully)
{
read = await readTask.ConfigureAwait(false);
}
@@ -200,116 +200,6 @@ public abstract class CollectFoundationBase : CollectBase
}
///// <summary>
///// 采集驱动读取,读取成功后直接赋值变量,失败不做处理,注意非通用设备需重写
///// </summary>
// protected override ValueTask<OperResult<ReadOnlyMemory<byte>>> ReadSourceAsync(VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
// {
// if (cancellationToken.IsCancellationRequested)
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>( new OperResult<ReadOnlyMemory<byte>>(new OperationCanceledException()));
// // 值类型状态机
// var stateMachine = new ReadSourceStateMachine(this, variableSourceRead, cancellationToken);
// return stateMachine.MoveNextAsync();
// }
// private struct ReadSourceStateMachine
// {
// private readonly VariableSourceRead _variableSourceRead;
// private readonly CancellationToken _cancellationToken;
// private readonly CollectFoundationBase _owner;
// private OperResult<ReadOnlyMemory<byte>> _result;
// private ValueTask<OperResult<ReadOnlyMemory<byte>>> _readTask;
// public ReadSourceStateMachine(CollectFoundationBase owner, VariableSourceRead variableSourceRead, CancellationToken cancellationToken)
// {
// _owner = owner;
// _variableSourceRead = variableSourceRead;
// _cancellationToken = cancellationToken;
// _result = default;
// State = 0;
// }
// public int State { get; private set; }
// public ValueTask<OperResult<ReadOnlyMemory<byte>>> MoveNextAsync()
// {
// try
// {
// switch (State)
// {
// case 0:
// // 异步读取
// if (_cancellationToken.IsCancellationRequested)
// {
// _result = new OperResult<ReadOnlyMemory<byte>>(new OperationCanceledException());
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(_result);
// }
//#pragma warning disable CA2012 // 正确使用 ValueTask
// _readTask = _owner.FoundationDevice.ReadAsync(_variableSourceRead.AddressObject, _cancellationToken);
//#pragma warning restore CA2012 // 正确使用 ValueTask
// // 检查是否任务已完成
// if (_readTask.IsCompleted)
// {
// _result = _readTask.Result;
// State = 1;
// return MoveNextAsync();
// }
// // 如果任务尚未完成,继续等待
// State = 2;
// return Awaited(_readTask);
// case 1:
// // 解析结构化内容
// if (_result.IsSuccess)
// {
// var parsedResult = _variableSourceRead.VariableRuntimes.PraseStructContent(_owner.FoundationDevice, _result.Content.Span, false);
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(new OperResult<ReadOnlyMemory<byte>>(parsedResult));
// }
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(_result);
// case 2:
// // 完成任务后,解析内容
// _result = _readTask.Result;
// if (_result.IsSuccess)
// {
// var parsedResult = _variableSourceRead.VariableRuntimes.PraseStructContent(_owner.FoundationDevice, _result.Content.Span, false);
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(new OperResult<ReadOnlyMemory<byte>>(parsedResult));
// }
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(_result);
// default:
// throw new InvalidOperationException("Unexpected state.");
// }
// }
// catch (Exception ex)
// {
// return new ValueTask<OperResult<ReadOnlyMemory<byte>>>(new OperResult<ReadOnlyMemory<byte>>(ex));
// }
// }
// private async ValueTask<OperResult<ReadOnlyMemory<byte>>> Awaited(ValueTask<OperResult<ReadOnlyMemory<byte>>> vt)
// {
// try
// {
// await vt.ConfigureAwait(false);
// return await MoveNextAsync().ConfigureAwait(false);
// }
// catch (Exception ex)
// {
// return new OperResult<ReadOnlyMemory<byte>>(ex);
// }
// }
// }
/// <summary>
/// 批量写入变量值,需返回变量名称/结果,注意非通用设备需重写
/// </summary>

View File

@@ -8,7 +8,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Riok.Mapperly" Version="4.3.0" ExcludeAssets="runtime" PrivateAssets="all">
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Rougamo.Fody" Version="5.0.2" />
<PackageReference Include="TouchSocket.Dmtp" Version="$(TSVersion)" />
<!--<PackageReference Include="TouchSocket.WebApi.Swagger" Version="$(TSVersion)" />-->

View File

@@ -1347,27 +1347,7 @@ EventCallback.Factory.Create<MouseEventArgs>(this, async e =>
private TreeViewItem<ChannelDeviceTreeItem> UnknownTreeViewItem;
SmartTriggerScheduler? scheduler;
private bool _initialized;
public override async Task SetParametersAsync(ParameterView parameters)
{
parameters.SetParameterProperties(this);
if (!_initialized)
{
_initialized = true;
OnInitialized();
await OnInitializedAsync();
OnParametersSet();
StateHasChanged();
await OnParametersSetAsync();
}
else
{
OnParametersSet();
StateHasChanged();
await OnParametersSetAsync();
}
}
protected override async Task OnInitializedAsync()
{

View File

@@ -10,7 +10,9 @@
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Blazor.Diagrams\ThingsGateway.Blazor.Diagrams.csproj" />
<ProjectReference Include="..\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj" />
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Riok.Mapperly" Version="4.3.0" ExcludeAssets="runtime" PrivateAssets="all">
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<ProjectReference Include="..\..\Admin\ThingsGateway.Admin.Razor\ThingsGateway.Admin.Razor.csproj" />
<ProjectReference Include="..\..\Foundation\ThingsGateway.Foundation.Razor\ThingsGateway.Foundation.Razor.csproj" />
</ItemGroup>

View File

@@ -7,15 +7,13 @@
<OutputType>WinExe</OutputType>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<TargetFrameworks>net8.0;$(OtherTargetFrameworks);</TargetFrameworks>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>true</GarbageCollectionAdaptationMode>
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
<CETCompat>false</CETCompat>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
</PropertyGroup>
<ItemGroup>

View File

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

View File

@@ -558,7 +558,7 @@ public class ModbusSlave : DeviceBase, IModbusAddress
}
}
private static bool TryParseRequest(object requestInfo, out ModbusRequest modbusRequest, out ReadOnlySequence<byte> sequences, out bool modbusRtu)
private static bool TryParseRequest(IRequestInfo requestInfo, out ModbusRequest modbusRequest, out ReadOnlySequence<byte> sequences, out bool modbusRtu)
{
modbusRequest = default;
sequences = default;

View File

@@ -15,7 +15,9 @@
<ProjectReference Include="..\..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj">
</ProjectReference>
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Riok.Mapperly" Version="4.3.0" ExcludeAssets="runtime" PrivateAssets="all">
<IncludeAssets>compile; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

View File

@@ -64,12 +64,10 @@
<ApplicationIcon>favicon.ico</ApplicationIcon>
<!--<PublishAot>true</PublishAot>-->
<CETCompat>false</CETCompat>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Project Sdk="Microsoft.NET.Sdk.Razor">
<Import Project="..\Version.props" />
@@ -37,17 +37,15 @@
<ApplicationIcon>favicon.ico</ApplicationIcon>
<!--<PublishAot>true</PublishAot>-->
<CETCompat>false</CETCompat>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<PublishAot>true</PublishAot>
<DebugType>none</DebugType>
<EmbedAllSources>false</EmbedAllSources>
<EmitDebugInformation>false</EmitDebugInformation>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
</PropertyGroup>

View File

@@ -32,26 +32,14 @@
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<CETCompat>false</CETCompat>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<CETCompat>false</CETCompat>
<!--<TieredCompilation>false</TieredCompilation>-->
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
<!--editbin /LARGEADDRESSAWARE:NO ThingsGateway.Server.exe-->
</PropertyGroup>

View File

@@ -10,7 +10,7 @@
},
"RemoteServerManagement": {
"Enable": true,
"Enable": false,
"Name": "ThingsGateway",
"ServerUri": "0.0.0.0:8399",
"VerifyToken": "ThingsGateway",

View File

@@ -111,7 +111,8 @@ public class Startup : AppStartup
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
options.MaxBufferedUnacknowledgedRenderBatches = 20;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10);
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
})
.AddHubOptions(options =>
{
@@ -131,7 +132,8 @@ public class Startup : AppStartup
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
options.MaxBufferedUnacknowledgedRenderBatches = 20;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10);
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
}).AddHubOptions(options =>
{
//单个传入集线器消息的最大大小。默认 32 KB

View File

@@ -51,26 +51,11 @@
<PublishReadyToRunComposite>true</PublishReadyToRunComposite>
<ApplicationIcon>favicon.ico</ApplicationIcon>
<CETCompat>false</CETCompat>
<ServerGarbageCollection>true</ServerGarbageCollection>
<!--动态适用GC-->
<GarbageCollectionAdaptationMode>1</GarbageCollectionAdaptationMode>
<CETCompat>false</CETCompat>
<!--<TieredCompilation>false</TieredCompilation>-->
<!--使用自托管线程池-->
<!--<UseWindowsThreadPool>false</UseWindowsThreadPool> -->
<!--使用工作站GC-->
<!--<ServerGarbageCollection>true</ServerGarbageCollection>-->
<!--<PlatformTarget>x86</PlatformTarget>-->
<!--editbin /LARGEADDRESSAWARE:NO ThingsGateway.Server.exe-->
</PropertyGroup>

View File

@@ -1,5 +1,9 @@
{
"configProperties": {
"System.Runtime.EnableWriteXorExecute": false
"System.Runtime.EnableWriteXorExecute": false,
"System.GC.HeapHardLimitPercent": 95, //堆限制百分比
"System.GC.HighMemoryPercent": 90, //高内存百分比
"System.GC.DynamicAdaptationMode": 1, //动态适应模式
//"System.GC.RegionRange": 549755813888 //8GB, 区域范围保留的虚拟内存如出现OOM可以调大一般是进程内存限制的2倍
}
}

302
wiki.json Normal file
View File

@@ -0,0 +1,302 @@
{
"repo_notes": [
{
"content": ""
}
],
"pages": [
{
"title": "Overview",
"purpose": "Introduce ThingsGateway, explaining what it is, its core purpose as an industrial IoT gateway, and the high-level architecture",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Key Features",
"purpose": "List and describe the main features including multi-protocol support, plugin architecture, data persistence options, and deployment models",
"parent": "Overview",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "System Requirements and Dependencies",
"purpose": "Document the .NET versions, target frameworks, required NuGet packages, and system requirements",
"parent": "Overview",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Getting Started",
"purpose": "Provide quick-start guide for installing and running ThingsGateway",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Installation and Deployment",
"purpose": "Explain deployment options including Docker (x64/ARM64), web server, desktop application, and configuration basics",
"parent": "Getting Started",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Initial Configuration",
"purpose": "Guide users through initial setup including appsettings.json, environment variables, logging configuration, and first channel/device setup",
"parent": "Getting Started",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Architecture Overview",
"purpose": "Explain the overall system architecture including layered design, separation of concerns, and component relationships",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Layered Architecture",
"purpose": "Describe the Foundation, Gateway Application, and UI/Admin layers and their responsibilities",
"parent": "Architecture Overview",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Plugin System Architecture",
"purpose": "Explain the plugin architecture including Foundation vs Plugin layer separation, dynamic loading with AssemblyLoadContext, and plugin types",
"parent": "Architecture Overview",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Build System and Conditional Compilation",
"purpose": "Document the MSBuild-based build system including Directory.Build.props, conditional plugin loading, multi-targeting, and NuGet package generation",
"parent": "Architecture Overview",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Foundation Layer",
"purpose": "Document the core foundation libraries that provide protocol implementations and communication primitives",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Device Communication",
"purpose": "Explain DeviceBase abstraction, IDevice interface, protocol implementations, read/write operations, and data parsing",
"parent": "Foundation Layer",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Channel Architecture",
"purpose": "Document IChannel interface, channel implementations (TCP/UDP/Serial), channel options, and connection lifecycle",
"parent": "Foundation Layer",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Data Handling and Transformation",
"purpose": "Describe DataHandlingAdapter, message parsing, ThingsGatewayBitConverter for endianness/format conversion, and WaitHandlePool for request-response correlation",
"parent": "Foundation Layer",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Core Utilities",
"purpose": "Document TimerX scheduling, ExpiringDictionary caching, WaitLock concurrency primitives, and Reflect utilities",
"parent": "Foundation Layer",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Gateway Application",
"purpose": "Explain the main gateway application layer that orchestrates device communication and data flow",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Runtime Management System",
"purpose": "Document the entity-to-runtime conversion, GlobalData static registry, RuntimeServiceHelper, and runtime synchronization",
"parent": "Gateway Application",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Device Lifecycle Management",
"purpose": "Explain DeviceThreadManage, driver initialization, StartAsync/StopAsync lifecycle, TaskSchedulerLoop, and thread management",
"parent": "Gateway Application",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Variable Management",
"purpose": "Document VariableRuntime, VariableSourceRead, variable packing optimization, and data collection workflow",
"parent": "Gateway Application",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Concurrency and Read/Write Coordination",
"purpose": "Explain AsyncReadWriteLock, reader/writer priority, duty cycle control, and concurrent operation management",
"parent": "Gateway Application",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Event System",
"purpose": "Document GlobalData event dispatchers, VariableValueChangeEvent, DeviceStatusChangeEvent, AlarmChangedEvent, and event flow",
"parent": "Gateway Application",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Redundancy and Failover",
"purpose": "Explain device redundancy system, master/slave configuration, failover triggers, and variable state transfer",
"parent": "Gateway Application",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Plugin Development",
"purpose": "Guide for developing custom plugins for data collection and business logic integration",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Collection Plugins (CollectBase)",
"purpose": "Document how to create data collection plugins including protocol implementation, variable loading, and scheduled tasks",
"parent": "Plugin Development",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Business Plugins (BusinessBase)",
"purpose": "Explain business plugin development for data persistence, distribution, and serving protocols",
"parent": "Plugin Development",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Plugin Service and Dynamic Loading",
"purpose": "Document PluginService, AssemblyLoadContext usage, plugin discovery, driver instantiation, and property configuration",
"parent": "Plugin Development",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Plugin Packaging and Distribution",
"purpose": "Explain how to package plugins as NuGet packages, versioning, and deployment via Directory.build.targets",
"parent": "Plugin Development",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Built-in Protocols",
"purpose": "Document the built-in protocol implementations including Modbus, Siemens S7, OPC UA, OPC DA, and Dlt645",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Modbus Protocol",
"purpose": "Document Modbus Master/Slave implementations, RTU/TCP/UDP support, function codes, and address parsing",
"parent": "Built-in Protocols",
"page_notes": [
{
"content": ""
}
]
},
{
"title": "Siemens S7 Protocol",
"purpose": "Explain S7 Master implementation, PLC types, area addressing, and data block access",
"parent": "Built-in Protocols",
"page_notes": [
{
"content": ""
}
]
}
]
}