mirror of
				https://gitee.com/ThingsGateway/ThingsGateway.git
				synced 2025-10-31 23:53:58 +08:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 96b4287f3a | ||
|   | 7d406de29f | 
| @@ -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' "> | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
|   | ||||
| @@ -8,8 +8,6 @@ | ||||
| //  QQ群:605534569 | ||||
| //------------------------------------------------------------------------------ | ||||
|  | ||||
| using ThingsGateway.NewLife.Collections; | ||||
|  | ||||
| namespace ThingsGateway.NewLife; | ||||
|  | ||||
| /// <summary> | ||||
|   | ||||
| @@ -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 | ||||
|                             { | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|   | ||||
| @@ -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 构造 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|                     } | ||||
|   | ||||
| @@ -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/> | ||||
|   | ||||
| @@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.AspNetCore.Mvc; | ||||
|  | ||||
| using System.ComponentModel; | ||||
| using ThingsGateway.NewLife.DictionaryExtensions; | ||||
|  | ||||
| using ThingsGateway.FriendlyException; | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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")); | ||||
|   | ||||
| @@ -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)" />--> | ||||
|   | ||||
| @@ -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() | ||||
|     { | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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> | ||||
|   | ||||
| @@ -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( | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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> | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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倍 | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user