Compare commits

..

6 Commits

Author SHA1 Message Date
Diego
b78a76e60f 增加采集设备写入数据的控制台文本日志 2025-06-11 23:49:37 +08:00
Diego
28bd751d44 增加tab右键菜单 2025-06-11 22:57:00 +08:00
Diego
dcba7b9810 10.7.50 2025-06-11 21:50:46 +08:00
Diego
7f1a741ce6 优化sql实时表执行性能 2025-06-11 21:39:14 +08:00
Diego
ca2b17d433 10.7.47 2025-06-11 17:08:19 +08:00
Diego
af589eac10 更新依赖 2025-06-11 15:05:03 +08:00
44 changed files with 286 additions and 223 deletions

View File

@@ -27,9 +27,9 @@
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.5" />
<PackageReference Include="System.Formats.Asn1" Version="9.0.5" />
<PackageReference Include="System.Threading.RateLimiting" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(NET9Version)" />
<PackageReference Include="System.Formats.Asn1" Version="$(NET9Version)" />
<PackageReference Include="System.Threading.RateLimiting" Version="$(NET9Version)" />
</ItemGroup>
<ItemGroup>
<Content Remove="SeedData\Admin\*.json" />

View File

@@ -1,7 +0,0 @@
@namespace ThingsGateway.Admin.Razor
@using ThingsGateway.Admin.Application
<AvatarUpload @bind-Value="@Picture" DisplayText=@AdminLocalizer["Picture"] IsSingle Accept="image/*" OnChange="OnAvatarUpload" DefaultFileList=PreviewFileList></AvatarUpload>

View File

@@ -1,73 +0,0 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Components.Forms;
using ThingsGateway.Admin.Application;
namespace ThingsGateway.Admin.Razor;
public partial class SysUserAvatarEdit : IDisposable
{
private List<UploadFile> PreviewFileList;
[Parameter]
[NotNull]
public SysUser? Model { get; set; }
[FileValidation(Extensions = [".png", ".jpg", ".jpeg"], FileSize = 200 * 1024)]
public IBrowserFile? Picture { get; set; }
[Inject]
private IStringLocalizer<ThingsGateway.Admin.Razor._Imports> AdminLocalizer { get; set; }
private CancellationTokenSource? ReadAvatarToken { get; set; }
[Inject]
[NotNull]
private ToastService ToastService { get; set; }
public void Dispose()
{
ReadAvatarToken?.Cancel();
GC.SuppressFinalize(this);
}
protected override Task OnParametersSetAsync()
{
PreviewFileList = new(new[] { new UploadFile { PrevUrl = Model.Avatar } });
return base.OnParametersSetAsync();
}
private async Task OnAvatarUpload(UploadFile file)
{
if (file?.File != null)
{
var format = file.File.ContentType;
ReadAvatarToken ??= new CancellationTokenSource();
if (ReadAvatarToken.IsCancellationRequested)
{
ReadAvatarToken.Dispose();
ReadAvatarToken = new CancellationTokenSource();
}
await file.RequestBase64ImageFileAsync(format, 640, 480, 1024 * 200, ReadAvatarToken.Token);
if (file.Code != 0)
{
await ToastService.Error($"{file.Error} ");
}
else
{
Model.Avatar = file.PrevUrl;
}
}
}
}

View File

@@ -3,7 +3,7 @@
<div class="row g-2 mx-1 form-inline">
<div class="col-12 col-md-12">
<SysUserAvatarEdit Model="Model"></SysUserAvatarEdit>
<AvatarUpload @bind-Value="@Picture" DisplayText=@AdminLocalizer["Picture"] IsMultiple=false Accept="image/*" OnChange="OnAvatarUpload" DefaultFileList=PreviewFileList></AvatarUpload>
</div>
<div class="col-12 col-md-6">

View File

@@ -8,6 +8,8 @@
// QQ群605534569
//------------------------------------------------------------------------------
using Microsoft.AspNetCore.Components.Forms;
using ThingsGateway.Admin.Application;
using ThingsGateway.NewLife.Extension;
@@ -45,4 +47,55 @@ public partial class SysUserEdit
Model.OrgId = items.LastOrDefault()?.Parent?.Value?.ToLong() ?? 0;
return Task.CompletedTask;
}
[Inject]
ToastService ToastService { get; set; }
#region
private List<UploadFile> PreviewFileList;
[FileValidation(Extensions = [".png", ".jpg", ".jpeg"], FileSize = 200 * 1024)]
public IBrowserFile? Picture { get; set; }
private CancellationTokenSource? ReadAvatarToken { get; set; }
public void Dispose()
{
ReadAvatarToken?.Cancel();
GC.SuppressFinalize(this);
}
protected override void OnInitialized()
{
PreviewFileList = new(new[] { new UploadFile { PrevUrl = Model.Avatar } });
base.OnInitialized();
}
private async Task OnAvatarUpload(UploadFile file)
{
if (file?.File != null)
{
var format = file.File.ContentType;
ReadAvatarToken ??= new CancellationTokenSource();
if (ReadAvatarToken.IsCancellationRequested)
{
ReadAvatarToken.Dispose();
ReadAvatarToken = new CancellationTokenSource();
}
await file.RequestBase64ImageFileAsync(format, 640, 480, 1024 * 200, token: ReadAvatarToken.Token);
if (file.Code != 0)
{
await ToastService.Error($"{file.Error} ");
}
else
{
Model.Avatar = file.PrevUrl;
}
}
}
#endregion
}

View File

@@ -4,7 +4,7 @@
<ValidateForm class="p-4" Model="@Model" OnValidSubmit="OnSave">
<AvatarUpload @bind-Value="@Picture" DisplayText=@AdminLocalizer["Picture"] IsSingle Accept="image/*" OnChange="OnAvatarUpload" DefaultFileList=PreviewFileList></AvatarUpload>
<AvatarUpload @bind-Value="@Picture" DisplayText=@AdminLocalizer["Picture"] IsMultiple=false Accept="image/*" OnChange="OnAvatarUpload" DefaultFileList=PreviewFileList></AvatarUpload>
<BootstrapInput @bind-Value=@Model.Phone></BootstrapInput>
<BootstrapInput @bind-Value=@Model.Email></BootstrapInput>

View File

@@ -40,10 +40,10 @@ public partial class UserInfoEditComponent
GC.SuppressFinalize(this);
}
protected override Task OnParametersSetAsync()
protected override void OnInitialized()
{
PreviewFileList = new(new[] { new UploadFile { PrevUrl = Model.Avatar } });
return base.OnParametersSetAsync();
base.OnInitialized();
}
private async Task OnAvatarUpload(UploadFile file)
@@ -58,7 +58,7 @@ public partial class UserInfoEditComponent
ReadAvatarToken = new CancellationTokenSource();
}
await file.RequestBase64ImageFileAsync(format, 640, 480, 1024 * 200, ReadAvatarToken.Token);
await file.RequestBase64ImageFileAsync(format, 640, 480, 1024 * 200, token: ReadAvatarToken.Token);
if (file.Code != 0)
{

View File

@@ -9,10 +9,10 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net8.0'">
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET8Version)" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="$(NET9Version)" />
</ItemGroup>
<PropertyGroup>

View File

@@ -45,7 +45,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET9Version)" />
</ItemGroup>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
@@ -54,8 +54,8 @@
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="$(NET9Version)" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(NET9Version)" />
</ItemGroup>
<ItemGroup>

View File

@@ -43,18 +43,18 @@
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(NET8Version)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(NET8Version)" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.2" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="8.0.1" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="9.0.5" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="9.0.5" />
<PackageReference Include="System.Text.Json" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="$(NET9Version)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="$(NET9Version)" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="$(NET9Version)" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="$(NET9Version)" />
<PackageReference Include="System.Text.Json" Version="$(NET9Version)" />
</ItemGroup>

View File

@@ -13,10 +13,10 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="$(NET8Version)" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebView" Version="$(NET9Version)" />
</ItemGroup>
</Project>

View File

@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BootstrapBlazor.FontAwesome" Version="9.0.2" />
<PackageReference Include="BootstrapBlazor" Version="9.7.0" />
<PackageReference Include="BootstrapBlazor" Version="9.7.3" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
</ItemGroup>

View File

@@ -26,6 +26,10 @@
height: var(--line-chart-table-height);
}
.wb-control .wb-full {
display: none;
}
.dialog-table {
height: calc(100vh - 200px);
}

View File

@@ -939,7 +939,7 @@ namespace ThingsGateway.SqlSugar
for (int i = 0; i < st.FrameCount; i++)
{
var frame = st.GetFrame(i);
if (!frame.GetMethod().Module.Name.Equals("sqlsugar.dll", StringComparison.CurrentCultureIgnoreCase) && frame.GetMethod().Name.First() != '<')
if (!frame.GetMethod().Module.Name.Equals("thingsgateway.sqlsugar.dll", StringComparison.CurrentCultureIgnoreCase) && frame.GetMethod().Name.First() != '<')
{
info.MyStackTraceList.Add(new StackTraceInfoItem()
{

View File

@@ -142,7 +142,7 @@ namespace ThingsGateway.SqlSugar.TDengine
for (int i = 0; i < st.FrameCount; i++)
{
var frame = st.GetFrame(i);
if (!string.Equals(frame.GetMethod().Module.Name, "sqlsugar.dll", StringComparison.OrdinalIgnoreCase) && frame.GetMethod().Name.First() != '<')
if (!string.Equals(frame.GetMethod().Module.Name, "thingsgateway.sqlsugar.dll", StringComparison.OrdinalIgnoreCase) && frame.GetMethod().Name.First() != '<')
{
info.MyStackTraceList.Add(new StackTraceInfoItem()
{

View File

@@ -24,7 +24,7 @@
<ItemGroup>
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.0" />
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.605" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET9Version)" />
<PackageReference Include="MySqlConnector" Version="2.4.0" />
<PackageReference Include="Npgsql" Version="9.0.3" />
<PackageReference Include="CsvHelper" Version="33.1.0" />

View File

@@ -1,9 +1,11 @@
<Project>
<PropertyGroup>
<PluginVersion>10.7.45</PluginVersion>
<ProPluginVersion>10.7.45</ProPluginVersion>
<PluginVersion>10.7.52</PluginVersion>
<ProPluginVersion>10.7.52</ProPluginVersion>
<AuthenticationVersion>2.5.0</AuthenticationVersion>
<NET8Version>8.0.17</NET8Version>
<NET9Version>9.0.6</NET9Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -121,12 +121,12 @@ public static class TextFileReader
}
using var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, FileOptions.SequentialScan);
var result = ReadLogsInverse(fs, lineCount, fileInfo.Length);
var result = ReadLogsInverse(fs, lineCount, length);
_cache.Set(cacheKey, new LogDataCache
{
LogDatas = result,
Length = fileInfo.Length,
Length = length,
});
return new OperResult<List<LogData>>() { Content = result };
@@ -140,7 +140,6 @@ public static class TextFileReader
private static List<LogData> ReadLogsInverse(FileStream fs, int lineCount, long length)
{
length = fs.Length;
long ps = 0; // 保存起始位置
List<string> txt = new(); // 存储读取的文本内容

View File

@@ -9,7 +9,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="$(NET9Version)" />
<PackageReference Include="TouchSocket" Version="3.1.7" />
<PackageReference Include="TouchSocket.SerialPorts" Version="3.1.7" />
</ItemGroup>

View File

@@ -17,12 +17,12 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.Components" Version="8.0.16" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="$(NET8Version)" />
<PackageReference Include="Microsoft.AspNetCore.Components" Version="$(NET8Version)" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components" Version="9.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="$(NET9Version)" />
<PackageReference Include="Microsoft.AspNetCore.Components" Version="$(NET9Version)" />
</ItemGroup>
<ItemGroup>

View File

@@ -220,6 +220,23 @@ public class ControlController : ControllerBase
{
return GlobalData.VariableRuntimeService.InsertTestDataAsync(testVariableCount, testDeviceCount, slaveUrl, businessEnable, restart, default);
}
/// <summary>
/// 确认实时报警
/// </summary>
/// <returns></returns>
[HttpPost("checkRealAlarm")]
[RequestAudit]
[DisplayName("确认实时报警")]
public async Task CheckRealAlarm(long variableId)
{
if (GlobalData.ReadOnlyRealAlarmIdVariables.TryGetValue(variableId, out var variable))
{
await GlobalData.SysUserService.CheckApiDataScopeAsync(variable.CreateOrgId, variable.CreateUserId).ConfigureAwait(false);
GlobalData.AlarmHostedService.ConfirmAlarm(variableId);
}
}
}
public class ChannelInput
{

View File

@@ -34,7 +34,7 @@ public class RuntimeInfoController : ControllerBase
/// <returns></returns>
[HttpGet("channelList")]
[DisplayName("获取通道信息")]
public async Task<SqlSugarPagedList<ChannelRuntime>> GetChannelListAsync(ChannelPageInput input)
public async Task<SqlSugarPagedList<ChannelRuntime>> GetChannelListAsync([FromQuery] ChannelPageInput input)
{
var channelRuntimes = await GlobalData.GetCurrentUserChannels().ConfigureAwait(false);
@@ -54,7 +54,7 @@ public class RuntimeInfoController : ControllerBase
/// <returns></returns>
[HttpGet("deviceList")]
[DisplayName("获取设备信息")]
public async Task<SqlSugarPagedList<DeviceRuntime>> GetDeviceListAsync(DevicePageInput input)
public async Task<SqlSugarPagedList<DeviceRuntime>> GetDeviceListAsync([FromQuery] DevicePageInput input)
{
var deviceRuntimes = await GlobalData.GetCurrentUserDevices().ConfigureAwait(false);
var data = deviceRuntimes
@@ -72,7 +72,7 @@ public class RuntimeInfoController : ControllerBase
/// <returns></returns>
[HttpGet("realAlarmList")]
[DisplayName("获取实时报警变量信息")]
public async Task<SqlSugarPagedList<AlarmVariable>> GetRealAlarmList(AlarmVariablePageInput input)
public async Task<SqlSugarPagedList<AlarmVariable>> GetRealAlarmList([FromQuery] AlarmVariablePageInput input)
{
var realAlarmVariables = await GlobalData.GetCurrentUserRealAlarmVariables().ConfigureAwait(false);
@@ -84,29 +84,13 @@ public class RuntimeInfoController : ControllerBase
return data;
}
/// <summary>
/// 确认实时报警
/// </summary>
/// <returns></returns>
[HttpPost("checkRealAlarm")]
[RequestAudit]
[DisplayName("确认实时报警")]
public async Task CheckRealAlarm(long variableId)
{
if (GlobalData.ReadOnlyRealAlarmIdVariables.TryGetValue(variableId, out var variable))
{
await GlobalData.SysUserService.CheckApiDataScopeAsync(variable.CreateOrgId, variable.CreateUserId).ConfigureAwait(false);
GlobalData.AlarmHostedService.ConfirmAlarm(variableId);
}
}
/// <summary>
/// 获取变量信息
/// </summary>
/// <returns></returns>
[HttpGet("variableList")]
[DisplayName("获取变量信息")]
public async Task<SqlSugarPagedList<VariableRuntime>> GetVariableList(VariablePageInput input)
public async Task<SqlSugarPagedList<VariableRuntime>> GetVariableList([FromQuery] VariablePageInput input)
{
var variables = await GlobalData.GetCurrentUserIdVariables().ConfigureAwait(false);
var data = variables
@@ -144,7 +128,7 @@ public class RuntimeInfoController : ControllerBase
/// </summary>
[HttpGet("getPluginInfos")]
[DisplayName("获取插件")]
public SqlSugarPagedList<PluginInfo> GetPluginInfos(PluginInfoPageInput input)
public SqlSugarPagedList<PluginInfo> GetPluginInfos([FromQuery] PluginInfoPageInput input)
{
//指定关键词搜索为插件FullName
return GlobalData.PluginService.GetList().WhereIF(!input.Name.IsNullOrWhiteSpace(), a => a.Name == input.Name)

View File

@@ -328,7 +328,9 @@ public abstract class CollectBase : DriverBase, IRpcDriver
if (await TestOnline(cancellationToken).ConfigureAwait(false))
return true;
var readErrorCount = 0;
LogMessage?.Trace(string.Format("{0} - Executing method[{1}]", DeviceName, readVariableMethods.MethodInfo.Name));
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name));
var readResult = await InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false);
// 方法调用失败时重试一定次数
@@ -342,9 +344,10 @@ public abstract class CollectBase : DriverBase, IRpcDriver
return true;
readErrorCount++;
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Execute method[{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage));
LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage));
LogMessage?.Trace(string.Format("{0} - Executing method[{1}]", DeviceName, readVariableMethods.MethodInfo.Name));
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Executing method [{1}]", DeviceName, readVariableMethods.MethodInfo.Name));
readResult = await InvokeMethodAsync(readVariableMethods, cancellationToken: cancellationToken).ConfigureAwait(false);
}
@@ -352,7 +355,7 @@ public abstract class CollectBase : DriverBase, IRpcDriver
{
// 方法调用成功时记录日志并增加成功计数器
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Execute method[{1}] - Succeeded {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.Content?.ToSystemTextJsonString()));
LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - Succeeded {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.Content?.ToSystemTextJsonString()));
readResultCount.deviceMethodsVariableSuccessNum++;
CurrentDevice.SetDeviceStatus(TimerX.Now, false);
}
@@ -370,8 +373,10 @@ public abstract class CollectBase : DriverBase, IRpcDriver
else
{
if (!cancellationToken.IsCancellationRequested)
{
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Execute method[{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage));
LogMessage?.Trace(string.Format("{0} - Execute method [{1}] - failed - {2}", DeviceName, readVariableMethods.MethodInfo.Name, readResult.ErrorMessage));
}
}
readResultCount.deviceMethodsVariableFailedNum++;
@@ -405,7 +410,8 @@ public abstract class CollectBase : DriverBase, IRpcDriver
var readErrorCount = 0;
LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
var readResult = await ReadSourceAsync(variableSourceRead, cancellationToken).ConfigureAwait(false);
// 读取失败时重试一定次数
@@ -419,10 +425,11 @@ public abstract class CollectBase : DriverBase, IRpcDriver
return true;
readErrorCount++;
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Collection[{1} - {2}] failed - {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] failed - {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
readResult = await ReadSourceAsync(variableSourceRead, cancellationToken).ConfigureAwait(false);
}
@@ -430,7 +437,7 @@ public abstract class CollectBase : DriverBase, IRpcDriver
{
// 读取成功时记录日志并增加成功计数器
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Collection[{1} - {2}] data succeeded {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content?.ToHexString(' ')));
LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content?.ToHexString(' ')));
readResultCount.deviceSourceVariableSuccessNum++;
CurrentDevice.SetDeviceStatus(TimerX.Now, false);
}
@@ -449,8 +456,10 @@ public abstract class CollectBase : DriverBase, IRpcDriver
else
{
if (!cancellationToken.IsCancellationRequested)
{
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
LogMessage?.Trace(string.Format("{0} - Collection[{1} - {2}] data failed - {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data failed - {3}", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
}
}
readResultCount.deviceSourceVariableFailedNum++;

View File

@@ -183,9 +183,22 @@ public abstract class CollectFoundationBase : CollectBase
{
try
{
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug)
LogMessage?.Debug(string.Format("{0} - Writing [{1} - {2} - {3}]", DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType));
// 调用协议的写入方法,将写入信息中的数据写入到对应的寄存器地址,并获取操作结果
var result = await FoundationDevice.WriteAsync(writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, cancellationToken).ConfigureAwait(false);
if (result.IsSuccess)
{
if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Debug)
LogMessage?.Debug(string.Format("{0} - Write [{1} - {2} - {3}] data succeeded", DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType));
}
else
{
LogMessage?.Warning(string.Format("{0} - Write [{1} - {2} - {3}] data failed {4}", DeviceName, writeInfo.Key.RegisterAddress, writeInfo.Value, writeInfo.Key.DataType, result.ToString()));
}
// 将操作结果添加到结果字典中,使用变量名称作为键
operResults.TryAdd(writeInfo.Key.Name, result);
}

View File

@@ -54,7 +54,7 @@ public static class ChineseResource
public const string VariablePackError = "变量打包失败 {0} ";
public const string GetMethodError = "插件方法初始化失败 {0} ";
public const string MethodNotNull = "特殊方法变量 {0} 找不到执行方法 {1},请检查现有方法列表";
public const string MethodFail = "{0} - 执行方法[{1}] - 失败 {2}";
public const string MethodFail = "{0} - 执行方法 [{1}] - 失败 {2}";
public const string CollectFail = "{0} - 采集[{1} - {2}] 数据失败 {3}";
public const string CollectSuccess = "{0} - 采集[{1} - {2}] 数据成功 {3}";
public const string WriteExpressionsError = " {0} 转换写入表达式 {1} 失败 {2} ";

View File

@@ -104,9 +104,10 @@ public partial class PluginDebugPage
{
Title = pluginInfo.Name,
ContentTemplate = debugRender,
Width = "1200px",
Height = "800px",
Top = "100px",
Max = false,
Width = "1440px",
Height = "810px",
Top = "70px",
Left = "220px",
Background = "var(--bb-primary-color)",
};

View File

@@ -11,7 +11,7 @@
<h6 class="mt-4 " style="color:var(--bs-red);"> @Localizer["SavePlugin"] </h6>
</div>
<div class="col-12">
<ButtonUpload @ref=ButtonUpload @bind-Value="@Model.MainFile" Accept=".dll" IsSingle ShowLabelTooltip="true" />
<ButtonUpload @ref=ButtonUpload @bind-Value="@Model.MainFile" Accept=".dll" IsMultiple=false ShowLabelTooltip="true" />
</div>
<div class="col-12">
<ButtonUpload @bind-Value="@Model.OtherFiles" IsMultiple ShowLabelTooltip="true"/>

View File

@@ -70,7 +70,13 @@ public partial class QuestDBProducer : BusinessBaseWithCacheIntervalVariableMode
.Map(dest => dest.CollectTime, (src) => src.CollectTime < DateTime.MinValue ? utcTime : src.CollectTime!.Value.ToUniversalTime())//注意sqlsugar插入时无时区直接utc时间
.Map(dest => dest.CreateTime, (src) => DateTime.UtcNow)
;//注意sqlsugar插入时无时区直接utc时间
_config.ForType<VariableBasicData, QuestDBHistoryValue>()
//.Map(dest => dest.Id, src => CommonUtils.GetSingleId())
.Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
.Map(dest => dest.Value, src => src.Value == null ? string.Empty : src.Value.ToString() ?? string.Empty)
.Map(dest => dest.CollectTime, (src) => src.CollectTime < DateTime.MinValue ? utcTime : src.CollectTime.ToUniversalTime())//注意sqlsugar插入时无时区直接utc时间
.Map(dest => dest.CreateTime, (src) => DateTime.UtcNow)
;//注意sqlsugar插入时无时区直接utc时间
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
}

View File

@@ -158,12 +158,21 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
.Map(dest => dest.Value, src => src.Value == null ? string.Empty : src.Value.ToString() ?? string.Empty)
.Map(dest => dest.CreateTime, (src) => DateTime.Now);
_exRealTimerTick = new(_driverPropertys.RealTableBusinessInterval);
_config.ForType<VariableBasicData, SQLHistoryValue>()
//.Map(dest => dest.Id, (src) =>CommonUtils.GetSingleId())
.Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
.Map(dest => dest.Value, src => src.Value == null ? string.Empty : src.Value.ToString() ?? string.Empty)
.Map(dest => dest.CreateTime, (src) => DateTime.Now);
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
}
public override Task AfterVariablesChangedAsync(CancellationToken cancellationToken)
{
RealTimeVariables.Clear();
_initRealData = false;
return base.AfterVariablesChangedAsync(cancellationToken);
}
protected override async Task ProtectedStartAsync(CancellationToken cancellationToken)
{
@@ -209,42 +218,25 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
{
if (_driverPropertys.IsReadDB)
{
if (_exRealTimerTick.IsTickHappen())
try
{
try
var varList = RealTimeVariables.ToListWithDequeue();
if (varList.Count > 0)
{
var varList = IdVariableRuntimes.Select(a => a.Value);
if (_driverPropertys.GroupUpdate)
var result = await UpdateAsync(varList.Adapt<List<SQLRealValue>>(), cancellationToken).ConfigureAwait(false);
if (success != result.IsSuccess)
{
var groups = varList.GroupBy(a => a.BusinessGroup);
foreach (var item in groups)
{
var result = await UpdateAsync(item.Adapt<List<SQLRealValue>>(), cancellationToken).ConfigureAwait(false);
if (success != result.IsSuccess)
{
if (!result.IsSuccess)
LogMessage?.LogWarning(result.ToString());
success = result.IsSuccess;
}
}
}
else
{
var result = await UpdateAsync(varList.Adapt<List<SQLRealValue>>(), cancellationToken).ConfigureAwait(false);
if (success != result.IsSuccess)
{
if (!result.IsSuccess)
LogMessage?.LogWarning(result.ToString());
success = result.IsSuccess;
}
if (!result.IsSuccess)
LogMessage?.LogWarning(result.ToString());
success = result.IsSuccess;
}
}
catch (Exception ex)
{
if (success)
LogMessage?.LogWarning(ex);
success = false;
}
}
catch (Exception ex)
{
if (success)
LogMessage?.LogWarning(ex);
success = false;
}
}

View File

@@ -58,13 +58,6 @@ public class SqlDBProducerProperty : BusinessPropertyWithCacheInterval
[AutoGenerateColumn(ComponentType = typeof(Textarea), Rows = 1)]
public string BigTextConnectStr { get; set; } = "server=.;uid=sa;pwd=111111;database=test;";
/// <summary>
/// 实时表间隔上传时间
/// </summary>
[DynamicProperty]
public virtual string RealTableBusinessInterval { get; set; } = "3000";
/// <summary>
/// 实时表脚本
/// </summary>

View File

@@ -10,11 +10,11 @@
using Mapster;
using System.Collections.Concurrent;
using System.Diagnostics;
using ThingsGateway.Extension.Generic;
using ThingsGateway.Foundation;
using ThingsGateway.NewLife;
using ThingsGateway.Plugin.DB;
using TouchSocket.Core;
@@ -27,8 +27,8 @@ namespace ThingsGateway.Plugin.SqlDB;
public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<SQLHistoryValue>
{
private TypeAdapterConfig _config;
private TimeTick _exRealTimerTick;
private volatile bool _initRealData;
private ConcurrentDictionary<long, VariableBasicData> RealTimeVariables { get; } = new ConcurrentDictionary<long, VariableBasicData>();
protected override ValueTask<OperResult> UpdateVarModel(IEnumerable<CacheDBItem<SQLHistoryValue>> item, CancellationToken cancellationToken)
{
@@ -36,7 +36,10 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
}
protected override void VariableTimeInterval(IEnumerable<VariableRuntime> variableRuntimes, List<VariableBasicData> variables)
{
TimeIntervalUpdateVariable(variables);
if (_driverPropertys.IsHistoryDB)
{
TimeIntervalUpdateVariable(variables);
}
base.VariableTimeInterval(variableRuntimes, variables);
}
@@ -90,6 +93,11 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
AddQueueVarModel(new CacheDBItem<SQLHistoryValue>(variableRuntime.Adapt<SQLHistoryValue>(_config)));
}
}
if (_driverPropertys.IsReadDB)
{
RealTimeVariables.AddOrUpdate(variable.Id, variable, (key, oldValue) => variable);
}
}
@@ -163,28 +171,39 @@ public partial class SqlDBProducer : BusinessBaseWithCacheIntervalVariableModel<
}
else
{
if (!_initRealData)
{
if (datas?.Count > 0)
{
var result = await db.Storageable(datas).As(_driverPropertys.ReadDBTableName).PageSize(5000).ExecuteSqlBulkCopyAsync(cancellationToken).ConfigureAwait(false);
if (result > 0)
LogMessage?.Trace($"RealTable Data Count{result}");
Stopwatch stopwatch = new();
stopwatch.Start();
var ids = (await db.Queryable<SQLRealValue>().AS(_driverPropertys.ReadDBTableName).Select(a => a.Id).ToListAsync(cancellationToken).ConfigureAwait(false)).ToHashSet();
var InsertData = IdVariableRuntimes.Where(a => !ids.Contains(a.Key)).Select(a=>a.Value).Adapt<List<SQLRealValue>>();
var result = await db.Fastest<SQLRealValue>().AS(_driverPropertys.ReadDBTableName).PageSize(100000).BulkCopyAsync(InsertData).ConfigureAwait(false);
_initRealData = true;
return OperResult.Success;
}
return OperResult.Success;
stopwatch.Stop();
if (result > 0)
{
LogMessage?.Trace($"RealTable Insert Data Count{result}watchTime: {stopwatch.ElapsedMilliseconds} ms");
}
}
else
{
if (datas?.Count > 0)
{
Stopwatch stopwatch = new();
stopwatch.Start();
var result = await db.Fastest<SQLRealValue>().AS(_driverPropertys.ReadDBTableName).PageSize(100000).BulkUpdateAsync(datas).ConfigureAwait(false);
LogMessage?.Trace($"RealTable Data Count{result}");
stopwatch.Stop();
if (result > 0)
{
LogMessage?.Trace($"RealTable Data Count{result}watchTime: {stopwatch.ElapsedMilliseconds} ms");
}
return OperResult.Success;
}
return OperResult.Success;
}
}
}
catch (Exception ex)

View File

@@ -84,7 +84,11 @@ public partial class TDengineDBProducer : BusinessBaseWithCacheIntervalVariableM
//.Map(dest => dest.Id, src => CommonUtils.GetSingleId())
.Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
;//注意sqlsugar插入时无时区直接utc时间
_config.ForType<VariableBasicData, TDengineDBHistoryValue>()
.Map(dest => dest.Value, src => src.Value != null ? src.Value.GetType() == typeof(string) ? src.Value.ToString() : JToken.FromObject(src.Value).ToString() : string.Empty)
//.Map(dest => dest.Id, src => CommonUtils.GetSingleId())
.Map(dest => dest.Id, src => src.Id)//Id更改为变量Id
;//注意sqlsugar插入时无时区直接utc时间
await base.InitChannelAsync(channel, cancellationToken).ConfigureAwait(false);
}

View File

@@ -9,7 +9,7 @@
</ProjectReference>
<ProjectReference Include="..\..\Gateway\ThingsGateway.Gateway.Razor\ThingsGateway.Gateway.Razor.csproj">
</ProjectReference>
<PackageReference Include="Confluent.Kafka" Version="2.10.0" GeneratePathProperty="true">
<PackageReference Include="Confluent.Kafka" Version="2.10.1" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
</ItemGroup>

View File

@@ -22,7 +22,7 @@
<EditTemplate Context="caFile">
<div class="col-12 col-md-12">
<ButtonUpload @bind-Value=@(((MqttClientProperty)caFile).CAFile_BrowserFile) OnChange="OnCAFileChange" IsSingle ShowLabelTooltip="true" IsMultiple=false IsDisabled=@(mqttClientProperty.TLS == false) />
<ButtonUpload @bind-Value=@(((MqttClientProperty)caFile).CAFile_BrowserFile) OnChange="OnCAFileChange" ShowLabelTooltip="true" IsMultiple=false IsDisabled=@(mqttClientProperty.TLS == false) />
</div>
</EditTemplate>
</EditorItem>
@@ -31,7 +31,7 @@
<EditTemplate Context="caFile">
<div class="col-12 col-md-12">
<ButtonUpload @bind-Value=@(((MqttClientProperty)caFile).ClientCertificateFile_BrowserFile) OnChange="OnClientCertificateFileChange" IsSingle ShowLabelTooltip="true" IsMultiple=false IsDisabled=@(mqttClientProperty.TLS == false) />
<ButtonUpload @bind-Value=@(((MqttClientProperty)caFile).ClientCertificateFile_BrowserFile) OnChange="OnClientCertificateFileChange" ShowLabelTooltip="true" IsMultiple=false IsDisabled=@(mqttClientProperty.TLS == false) />
</div>
</EditTemplate>
</EditorItem>
@@ -40,7 +40,7 @@
<EditTemplate Context="caFile">
<div class="col-12 col-md-12">
<ButtonUpload @bind-Value=@(((MqttClientProperty)caFile).ClientKeyFile_BrowserFile) OnChange="OnClientKeyFileChange" IsSingle ShowLabelTooltip="true" IsMultiple=false IsDisabled=@(mqttClientProperty.TLS == false) />
<ButtonUpload @bind-Value=@(((MqttClientProperty)caFile).ClientKeyFile_BrowserFile) OnChange="OnClientKeyFileChange" ShowLabelTooltip="true" IsMultiple=false IsDisabled=@(mqttClientProperty.TLS == false) />
</div>
</EditTemplate>
</EditorItem>

View File

@@ -67,7 +67,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET9Version)" />
</ItemGroup>
<ItemGroup>

View File

@@ -86,12 +86,18 @@
</div>
</Side>
<Main>
<Tab @ref=_tab ClickTabToNavigation="true" ShowToolbar="true" ShowContextMenu="true" ShowContextMenuFullScreen="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
<Tab @ref=_tab ClickTabToNavigation="true" ShowToolbar="true" ShowContextMenu="true" ShowContextMenuFullScreen="true" ShowExtendButtons="false" ShowClose="true" AllowDrag=true
AdditionalAssemblies="@App.RazorAssemblies" Menus="@MenuService.AllOwnMenuItems"
DefaultUrl=@("/") Body=@(Body!) OnCloseTabItemAsync=@((a)=>
{
return Task.FromResult(!(a.Url=="/"||a.Url.IsNullOrEmpty()));
})>
<BeforeContextMenuTemplate>
<ContextMenuItem Icon="fa fa-window-restore" Text="@Localizer["WindowRestore"]" OnClick="WinboxRender"></ContextMenuItem>
<ContextMenuDivider></ContextMenuDivider>
</BeforeContextMenuTemplate>
</Tab>
</Main>
<NotAuthorized>

View File

@@ -124,6 +124,39 @@ public partial class MainLayout : IDisposable
#endregion
private async Task WinboxRender(ContextMenuItem item, object? context)
{
if (context is TabItem tabItem)
{
await WinboxRender(tabItem.ChildContent, tabItem.Text);
await _tab.RemoveTab(tabItem);
}
}
[Inject]
[NotNull]
private WinBoxService? WinBoxService { get; set; }
private async Task WinboxRender(RenderFragment item, string title)
{
if (item != null)
{
var option = new WinBoxOption()
{
Title = title,
ContentTemplate = item,
Max = false,
Width = "1440px",
Height = "810px",
Top = "70px",
Left = "220px",
Background = "var(--bb-primary-color)",
};
await WinBoxService.Show(option);
}
}
private string _versionString = string.Empty;
[Inject]
[NotNull]

View File

@@ -1,4 +1,10 @@
::deep .avatar {

.mainlayout ::deep .menu-icon {
width: 16px;
}
::deep .avatar {
border-radius: 1.5rem;
width: 28px;
height: 28px;

View File

@@ -46,6 +46,7 @@
"Welcome": "Welcome"
},
"ThingsGateway.Server.MainLayout": {
"WindowRestore": "FloatingWindow",
"变量管理": "Variable",
"采集设备": "CollectionDevices",
"菜单管理": "Menu",

View File

@@ -46,6 +46,7 @@
"Welcome": "欢迎使用"
},
"ThingsGateway.Server.MainLayout": {
"WindowRestore": "浮动小窗",
"变量管理": "变量管理",
"采集设备": "采集设备",
"菜单管理": "菜单管理",

View File

@@ -80,7 +80,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET9Version)" />
</ItemGroup>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
@@ -89,8 +89,8 @@
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="$(NET9Version)" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(NET9Version)" />
</ItemGroup>

View File

@@ -4,10 +4,10 @@
<ValidateForm Model="Model" OnValidSubmit="OnSave">
<div class="row g-2 mx-1 form-inline">
<div class="col-12">
<ButtonUpload @bind-Value="@Model.ZipFile" Accept=".zip" IsSingle ShowLabelTooltip="true" />
<ButtonUpload @bind-Value="@Model.ZipFile" Accept=".zip" IsMultiple=false ShowLabelTooltip="true" />
</div>
<div class="col-12">
<ButtonUpload @bind-Value="@Model.JsonFile" Accept=".json" IsSingle ShowLabelTooltip="true" />
<ButtonUpload @bind-Value="@Model.JsonFile" Accept=".json" IsMultiple=false ShowLabelTooltip="true" />
<Button Color="Color.Link" class="mx-2" Size="Size.ExtraSmall" Text="@Localizer["DownTemplate"]" OnClick="DownTemplate" />
</div>
</div>

View File

@@ -42,7 +42,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET9Version)" />
</ItemGroup>
<!--安装服务守护-->
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
@@ -50,8 +50,8 @@
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.1" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net9.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="$(NET9Version)" />
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="$(NET9Version)" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>10.7.45</Version>
<Version>10.7.52</Version>
</PropertyGroup>
<ItemGroup>