Compare commits

...

2 Commits

Author SHA1 Message Date
2248356998 qq.com
96b4287f3a fix: operResult隐式转换递归错误 2025-10-22 13:43:39 +08:00
2248356998 qq.com
7d406de29f 调整 runtimeconfig.template.json 文件 2025-10-22 00:47:33 +08:00
28 changed files with 254 additions and 262 deletions

View File

@@ -19,7 +19,9 @@
</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' ">

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

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using ThingsGateway.NewLife.Collections;
namespace ThingsGateway.NewLife;
/// <summary>

View File

@@ -61,7 +61,7 @@ public class TextFileLog : Logger, IDisposable
MaxBytes = set.LogFileMaxBytes;
Backups = set.LogFileBackups;
_Timer = new TimerX(DoWriteAndClose, null, 0_000, 5_000, nameof(TextFileLog)) { Async = true };
_Timer = new TimerX(DoWriteAndClose, null, 0_000, 60_000, nameof(TextFileLog)) { Async = true };
_WriteTimer = new TimerX(DoWrite, null, 0_000, 1000, nameof(TextFileLog)) { Async = true };
}
@@ -149,31 +149,67 @@ public class TextFileLog : Logger, IDisposable
/// <summary>获取日志文件路径</summary>
/// <returns></returns>
private String? GetLogFile()
private string? GetLogFile()
{
// 单日志文件
if (_isFile) return LogPath.GetBasePath();
// 目录多日志文件
var logfile = LogPath.CombinePath(String.Format(FileFormat, TimerX.Now.AddHours(Setting.Current.UtcIntervalHours), Level)).GetBasePath();
var baseFile = LogPath.CombinePath(
string.Format(FileFormat, TimerX.Now.AddHours(Setting.Current.UtcIntervalHours), Level)
).GetBasePath();
// 是否限制文件大小
if (MaxBytes == 0) return logfile;
// 不限制大小
if (MaxBytes == 0) return baseFile;
// 找到今天第一个未达到最大上限的文件
var max = MaxBytes * 1024L * 1024L;
var ext = Path.GetExtension(logfile);
var name = logfile.TrimEnd(ext);
var ext = Path.GetExtension(FileFormat);
string? latestFile = null;
DateTime latestTime = DateTime.MinValue;
foreach (var path in Directory.EnumerateFiles(LogPath, $"*{ext}", SearchOption.TopDirectoryOnly))
{
try
{
var lastWrite = File.GetLastWriteTimeUtc(path);
if (lastWrite > latestTime)
{
latestTime = lastWrite;
latestFile = path;
}
}
catch { }
}
if (latestFile != null)
{
try
{
var len = new FileInfo(latestFile).Length;
if (len < max)
return latestFile;
}
catch { }
}
var fileNameWithoutExt = Path.Combine(
Path.GetDirectoryName(baseFile)!,
Path.GetFileNameWithoutExtension(baseFile)
);
// 依序找下一个可用文件
for (var i = 1; i < 1024; i++)
{
if (i > 1) logfile = $"{name}_{i}{ext}";
var nextFile = i == 1 ? $"{fileNameWithoutExt}{ext}" : $"{fileNameWithoutExt}_{i}{ext}";
if (!File.Exists(nextFile))
return nextFile;
var fi = logfile.AsFile();
if (!fi.Exists || fi.Length < max) return logfile;
}
return null;
}
#endregion
#region
@@ -189,9 +225,9 @@ public class TextFileLog : Logger, IDisposable
{
var writer = LogWriter;
var now = TimerX.Now.AddHours(Setting.Current.UtcIntervalHours);
var logFile = GetLogFile();
if (logFile.IsNullOrEmpty()) return;
var now = TimerX.Now.AddHours(Setting.Current.UtcIntervalHours);
if (!_isFile && logFile != CurrentLogFile)
{
@@ -243,43 +279,36 @@ public class TextFileLog : Logger, IDisposable
DirectoryInfo? di = new DirectoryInfo(LogPath);
if (di.Exists)
{
// 删除*.del
// 删除 *.del
try
{
var dels = di.GetFiles("*.del");
if (dels?.Length > 0)
foreach (var item in di.EnumerateFiles("*.del"))
{
foreach (var item in dels)
{
item.Delete();
}
item.Delete();
}
}
catch { }
var ext = Path.GetExtension(FileFormat);
var fis = di.GetFiles("*" + ext);
if (fis != null && fis.Length > Backups)
var fis = di.EnumerateFiles($"*{ext}")
.OrderByDescending(e => e.LastWriteTimeUtc)
.Skip(Backups);
foreach (var item in fis)
{
// 删除最旧的文件
var retain = fis.Length - Backups;
fis = fis.OrderBy(e => e.LastWriteTimeUtc).Take(retain).ToArray();
foreach (var item in fis)
OnWrite(LogLevel.Info, "The log file has reached the maximum limit of {0}, delete {1}, size {2: n0} Byte", Backups, item.Name, item.Length);
try
{
item.Delete();
}
catch
{
OnWrite(LogLevel.Info, "The log file has reached the maximum limit of {0}, delete {1}, size {2: n0} Byte", Backups, item.Name, item.Length);
try
{
item.Delete();
item.MoveTo(item.FullName + ".del");
}
catch
{
try
{
item.MoveTo(item.FullName + ".del");
}
catch
{
}
}
}
}

View File

@@ -80,7 +80,14 @@ public static class XTrace
Log.Error("{0}", ex);
}
public static void WriteException(Exception ex, string message)
{
if (!InitLog()) return;
WriteVersion();
Log.Error("{0}, {1}", message, ex);
}
#endregion
#region

View File

@@ -65,8 +65,21 @@ public class TimerScheduler : IDisposable, ILogFeature
public static TimeProvider GlobalTimeProvider { get; set; } = TimeProvider.System;
#endregion
#region
private TimerScheduler(String name) => Name = name;
private TimerScheduler(String name)
{
Name = name;
_processCallback = state =>
{
try
{
Execute(state);
}
catch (Exception ex)
{
XTrace.WriteException(ex, "Timer执行错误");
}
};
}
/// <summary>销毁</summary>
public void Dispose()
{
@@ -258,17 +271,7 @@ public class TimerScheduler : IDisposable, ILogFeature
else
//Task.Factory.StartNew(() => ProcessItem(timer));
// 不需要上下文流动,捕获所有异常
ThreadPool.UnsafeQueueUserWorkItem(s =>
{
try
{
Execute(s);
}
catch (Exception ex)
{
XTrace.WriteException(ex);
}
}, timer);
ThreadPool.UnsafeQueueUserWorkItem(_processCallback, timer);
}
}
}
@@ -283,7 +286,7 @@ public class TimerScheduler : IDisposable, ILogFeature
WriteLog("调度线程已退出:{0}", Name);
}
private readonly WaitCallback _processCallback;
/// <summary>检查定时器是否到期</summary>
/// <param name="timer"></param>
/// <param name="now"></param>
@@ -325,9 +328,9 @@ public class TimerScheduler : IDisposable, ILogFeature
timer.hasSetNext = false;
string tracerName = timer.TracerName ?? "timer:ExecuteAsync";
string timerArg = timer.Timers.ToString();
using var span = timer.Tracer?.NewSpan(tracerName, timerArg);
//string tracerName = timer.TracerName ?? "timer:ExecuteAsync";
//string timerArg = timer.Timers.ToString();
//using var span = timer.Tracer?.NewSpan(tracerName, timerArg);
var sw = ValueStopwatch.StartNew();
try
{
@@ -351,7 +354,7 @@ public class TimerScheduler : IDisposable, ILogFeature
// 如果用户代码没有拦截错误,则这里拦截,避免出错了都不知道怎么回事
catch (Exception ex)
{
span?.SetError(ex, null);
//span?.SetError(ex, null);
XTrace.WriteException(ex);
}
finally
@@ -377,9 +380,9 @@ public class TimerScheduler : IDisposable, ILogFeature
timer.hasSetNext = false;
string tracerName = timer.TracerName ?? "timer:ExecuteAsync";
string timerArg = timer.Timers.ToString();
using var span = timer.Tracer?.NewSpan(tracerName, timerArg);
//string tracerName = timer.TracerName ?? "timer:ExecuteAsync";
//string timerArg = timer.Timers.ToString();
//using var span = timer.Tracer?.NewSpan(tracerName, timerArg);
var sw = ValueStopwatch.StartNew();
try
{
@@ -427,7 +430,7 @@ public class TimerScheduler : IDisposable, ILogFeature
// 如果用户代码没有拦截错误,则这里拦截,避免出错了都不知道怎么回事
catch (Exception ex)
{
span?.SetError(ex, null);
//span?.SetError(ex, null);
XTrace.WriteException(ex);
}
finally

View File

@@ -37,7 +37,7 @@
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.2" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="System.Formats.Asn1" Version="8.0.2" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.10" />
</ItemGroup>
</Project>

View File

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

View File

@@ -10,53 +10,53 @@
<div class="w-100" style=@($"height:{HeightString}")>
<Card HeaderText=@HeaderText class=@("w-100 h-100")>
<HeaderTemplate>
<div class="flex-fill">
</div>
<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">
@if (LogLevelChanged.HasDelegate)
{
<Select Value="@LogLevel" ValueChanged="LogLevelChanged" IsPopover></Select>
}
<Tooltip class=" col-auto" Title=@(Pause? PlayText:PauseText) Placement="Placement.Bottom">
<Button Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@(Pause?"fa fa-play":"fa fa-pause") OnClick="OnPause" />
<Button Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@(Pause ? "fa fa-play" : "fa fa-pause") OnClick="OnPause" />
</Tooltip>
</Tooltip>
<Tooltip class=" col-auto" Title="@RazorLocalizer["Export"]" Placement="Placement.Bottom">
<Tooltip class=" col-auto" Title="@ExportText" Placement="Placement.Bottom">
<Button IsAsync Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@("fa fa-sign-out") OnClick="HandleOnExportClick" />
<Button IsAsync Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@("fa fa-sign-out") OnClick="HandleOnExportClick" />
</Tooltip>
</Tooltip>
<Tooltip class=" col-auto" Title="@RazorLocalizer["Delete"]" Placement="Placement.Bottom">
<Tooltip class=" col-auto" Title="@DeleteText" Placement="Placement.Bottom">
<Button IsAsync Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@("far fa-trash-alt") OnClick="Delete" />
<Button IsAsync Color="Color.None" style="color: var(--bs-card-title-color);" Icon=@("far fa-trash-alt") OnClick="Delete" />
</Tooltip>
</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))>
</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))
@itemMessage.Message.Substring(0, Math.Min(itemMessage.Message.Length, 150))
</div>
@* </Tooltip> *@
</ItemContent>
</Virtualize>
</div>
</div>
@* </Tooltip> *@
</ItemContent>
</Virtualize>
</div>
</BodyTemplate>
</Card>
</BodyTemplate>
</Card>
</div>

View File

@@ -10,12 +10,11 @@
using Microsoft.AspNetCore.Components.Web;
using System.Diagnostics;
using System.Text.RegularExpressions;
using ThingsGateway.Extension;
using ThingsGateway.Foundation;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Threading;
using TouchSocket.Core;
@@ -23,6 +22,24 @@ namespace ThingsGateway.Debug;
public partial class LogConsole : IDisposable
{
private string PlayText { get; set; }
private string PauseText { get; set; }
private string ExportText { get; set; }
private string DeleteText { get; set; }
protected override void OnInitialized()
{
PlayText = RazorLocalizer["Play"];
PauseText = RazorLocalizer["Pause"];
ExportText = RazorLocalizer["Export"];
DeleteText = RazorLocalizer["Delete"];
_Timer = new TimerX(RunTimerAsync, null, 1_000, 1_000, nameof(LogConsole)) { Async = true };
base.OnInitialized();
}
private TimerX _Timer;
private bool Pause;
public bool Disposed { get; set; }
@@ -69,7 +86,7 @@ public partial class LogConsole : IDisposable
{
logPath = LogPath;
Messages = new List<LogMessage>();
await ExecuteAsync();
_Timer?.SetNext(0);
}
await base.OnParametersSetAsync();
@@ -82,63 +99,38 @@ public partial class LogConsole : IDisposable
public void Dispose()
{
Disposed = true;
_Timer?.SafeDispose();
GC.SuppressFinalize(this);
}
private WaitLock WaitLock = new(nameof(LogConsole));
protected async Task ExecuteAsync()
protected async ValueTask ExecuteAsync()
{
if (WaitLock.Waited) return;
try
{
await WaitLock.WaitAsync();
await Task.Delay(1000);
if (LogPath != null)
if (LogPath != null)
{
var files = await TextFileReadService.GetLogFilesAsync(LogPath);
if (!files.IsSuccess)
{
var files = await TextFileReadService.GetLogFilesAsync(LogPath);
if (!files.IsSuccess)
Messages = new List<LogMessage>();
await Task.Delay(1000);
}
else
{
var result = await TextFileReadService.LastLogDataAsync(files.Content.FirstOrDefault());
if (result.IsSuccess)
{
Messages = new List<LogMessage>();
await Task.Delay(1000);
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}")}")).ToArray();
}
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);
}
});
Messages = Array.Empty<LogMessage>();
}
}
}
catch (Exception ex)
{
NewLife.Log.XTrace.WriteException(ex);
}
finally
{
WaitLock.Release();
}
}
protected override void OnInitialized()
{
_ = RunTimerAsync();
base.OnInitialized();
}
private async Task Delete()
{
@@ -185,19 +177,9 @@ public partial class LogConsole : IDisposable
return Task.CompletedTask;
}
private async Task RunTimerAsync()
private async Task RunTimerAsync(object? state)
{
while (!Disposed)
{
try
{
await ExecuteAsync();
await InvokeAsync(StateHasChanged);
}
catch (Exception ex)
{
NewLife.Log.XTrace.WriteException(ex);
}
}
await ExecuteAsync();
await InvokeAsync(StateHasChanged);
}
}

View File

@@ -28,7 +28,7 @@ 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));
@@ -44,8 +44,8 @@ public static class ChannelOptionsExtensions
{
var func = funcs[i];
if (func == null) continue;
var taskResult= func.Invoke(clientChannel, e, i == funcs.Count - 1);
if(!taskResult.IsCompletedSuccessfully)
var taskResult = func.Invoke(clientChannel, e, i == funcs.Count - 1);
if (!taskResult.IsCompletedSuccessfully)
{
await taskResult.ConfigureAwait(false);
}

View File

@@ -139,8 +139,15 @@ public struct OperResult<T> : IOperResult<T>
/// <param name="operResult"></param>
public static implicit operator OperResult(OperResult<T> operResult)
{
return new OperResult(operResult);
return new OperResult
{
OperCode = operResult.OperCode,
ErrorMessage = operResult.ErrorMessage,
Exception = operResult.Exception,
ErrorType = operResult.ErrorType
};
}
}
/// <inheritdoc/>

View File

@@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.ComponentModel;
using ThingsGateway.NewLife.DictionaryExtensions;
using ThingsGateway.FriendlyException;

View File

@@ -327,9 +327,12 @@ public static class GlobalData
{
if (deviceRuntime.RedundantEnable && deviceRuntime.RedundantDeviceId != null)
return true;
else if (GlobalData.IdDevices.Any(a => a.Value.RedundantDeviceId == deviceRuntime.Id))
var id = deviceRuntime.Id;
foreach (var kv in GlobalData.IdDevices)
{
return true;
if (kv.Value.RedundantDeviceId == id)
return true;
}
}
return false;

View File

@@ -424,7 +424,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
if (differences?.Count > 0)
{
var data = models.ToList();
await GlobalData.CheckByDeviceIds(data.Select(a => a.DeviceId)).ConfigureAwait(false);
await GlobalData.CheckByDeviceIds(data.Select(a => a.DeviceId)).ConfigureAwait(false);
using var db = GetDB();
var result = (await db.Updateable(data).UpdateColumns(differences.Select(a => a.Key).ToArray()).ExecuteCommandAsync().ConfigureAwait(false)) > 0;
@@ -493,10 +493,10 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
private async Task<Func<ISugarQueryable<Variable>, ISugarQueryable<Variable>>> GetWhereQueryFunc(GatewayExportFilter exportFilter)
{
var dataScope = await GlobalData.SysUserService.GetCurrentUserDataScopeAsync().ConfigureAwait(false);
List<long>? filterDeviceIds= null;
if(dataScope!=null)
List<long>? filterDeviceIds = null;
if (dataScope != null)
{
filterDeviceIds= GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
filterDeviceIds = GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
}
HashSet<long>? deviceId = null;
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
@@ -513,7 +513,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Collect, a => a.DeviceId == exportFilter.DeviceId)
.WhereIF(deviceId != null, a => deviceId.Contains(a.DeviceId))
.WhereIF(filterDeviceIds != null , u => filterDeviceIds.Contains(u.DeviceId))//在指定机构列表查询
.WhereIF(filterDeviceIds != null, u => filterDeviceIds.Contains(u.DeviceId))//在指定机构列表查询
.WhereIF(exportFilter.PluginType == PluginTypeEnum.Business, u => SqlFunc.JsonLike(u.VariablePropertys, exportFilter.DeviceId.ToString()));
return whereQuery;
@@ -527,8 +527,8 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
{
filterDeviceIds = GlobalData.GetCurrentUserDeviceIds(dataScope).ToList();
}
HashSet<long>? deviceId = null;
if (!exportFilter.PluginName.IsNullOrWhiteSpace())
{
@@ -850,7 +850,7 @@ internal sealed class VariableService : BaseService<Variable>, IVariableService
{
variable.IsUp = false;
}
if (device.IsUp && (filterDeviceIds?.Contains(variable.DeviceId) != false))
{
importPreviewOutput.Results.Add(new(Interlocked.Increment(ref row), false, "Operation not permitted"));

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

@@ -599,25 +599,29 @@ public class ModbusSlave : DeviceBase, IModbusAddress
return WriteError(modbusRtu, client, sequences, e);
}
return Write(this, client, e, modbusRequest, sequences, modbusRtu, data);
static async PooledTask Write(ModbusSlave @this, IClientChannel client, ReceivedDataEventArgs e, ModbusRequest modbusRequest, ReadOnlySequence<byte> sequences, bool modbusRtu, OperResult<ReadOnlyMemory<byte>> data)
ValueByteBlock byteBlock = new(1024);
try
{
WriteReadResponse(modbusRequest, sequences, data.Content, ref byteBlock, modbusRtu);
return SendAndDisposeAsync(this, client, e, modbusRtu, byteBlock);
}
catch
{
byteBlock.SafeDispose();
return WriteError(modbusRtu, client, sequences, e);
}
static async PooledTask SendAndDisposeAsync(ModbusSlave @this, IClientChannel client, ReceivedDataEventArgs e, bool modbusRtu, ValueByteBlock byteBlock)
{
ValueByteBlock byteBlock = new(1024);
try
{
WriteReadResponse(modbusRequest, sequences, data.Content, ref byteBlock, modbusRtu);
await @this.ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
}
catch
{
await @this.WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false);
}
finally
{
byteBlock.SafeDispose();
byteBlock.SafeDispose(); //即使 ValueByteBlock 是 struct内部仍持有同一块 Memory<byte> 引用,可以安全释放
}
}
}
private Task HandleWriteRequestAsync(

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

@@ -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, 区域范围保留的虚拟内存如DOCKER内出现OOM可以调大一般是进程内存限制的2倍
}
}