Compare commits

...

16 Commits

Author SHA1 Message Date
2248356998 qq.com
ce5fa0f37d fix: ixcom29s插件在超出512字节时 缓冲区会改变,导致数据写入错误,改为最大值4096 2025-10-31 12:27:15 +08:00
2248356998 qq.com
694437c7d5 更新readme 2025-10-30 18:06:54 +08:00
2248356998 qq.com
0e78cdefe7 feat: 支持net10 sdk下正确编译 net462 2025-10-30 17:57:17 +08:00
2248356998 qq.com
087dc9aaa3 更新logo 2025-10-30 17:20:12 +08:00
2248356998 qq.com
dacf255f1a fix: mqttCollect插件无法订阅两个以上的主题 2025-10-28 23:34:47 +08:00
2248356998 qq.com
e3960ce115 feat(DeviceBase): 发送前检查在线状态 2025-10-28 20:00:24 +08:00
2248356998 qq.com
0cf098be85 feat: bacnet添加debug日志 2025-10-28 19:36:30 +08:00
2248356998 qq.com
c93c80468c build: 10.12.16
fix: 系统总内存在特定情况下单位错误
feat: 网关定时任务性能优化
2025-10-27 17:12:13 +08:00
2248356998 qq.com
a464594885 feat: 网关定时任务性能优化 2025-10-27 14:37:36 +08:00
2248356998 qq.com
b42f8afa35 feat: 通道多设备日志分类显示 2025-10-24 17:46:24 +08:00
2248356998 qq.com
facf8bd401 修改ConnectionLimiterCircuitHandler.cs 2025-10-24 08:27:26 +08:00
2248356998 qq.com
feeb17eca3 启用ConnectionLimiterCircuitHandler 2025-10-24 01:31:15 +08:00
2248356998 qq.com
b3405cd674 修改CircuitHandler.cs 2025-10-24 01:16:35 +08:00
2248356998 qq.com
c35f9cef93 修改CircuitHandler.cs 2025-10-24 01:07:27 +08:00
2248356998 qq.com
3f382202db runtimeconfig.template.json 2025-10-24 01:00:53 +08:00
2248356998 qq.com
2a3493cc82 调整server option项 2025-10-24 00:51:56 +08:00
75 changed files with 507 additions and 424 deletions

View File

@@ -1,4 +1,7 @@
# ThingsGateway
<p align="center">
<img src="logo.svg" width = "400" height = "200" alt="The name of the image" align=center />
</p>
[![star](https://gitee.com/ThingsGateway/ThingsGateway/badge/star.svg?theme=gvp)](https://gitee.com/ThingsGateway/ThingsGateway/stargazers)
[![star](https://img.shields.io/github/stars/ThingsGateway/ThingsGateway?logo=github)](https://github.com/ThingsGateway/ThingsGateway)
@@ -11,31 +14,31 @@
</a>
## Introduction

A cross-platform, high-performance edge data collection gateway based on net8/10.

## Documentation

[Documentation](https://thingsgateway.cn/).

[NuGet](https://www.nuget.org/packages?q=Tags%3A%22ThingsGateway%22)

## Demo

[Demo](https://demo.thingsgateway.cn/)

Account: **SuperAdmin**

Password: **111111**

## Docker
@@ -50,7 +53,7 @@ docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
### Plugin List

#### Data Collection Plugins
@@ -80,28 +83,28 @@ docker pull registry.cn-shenzhen.aliyuncs.com/thingsgateway/thingsgateway_arm64
| TDengineDB | Time-series database storage |
| QuestDB | Time-series database storage |

## License

[License](https://thingsgateway.cn/docs/1)


## Sponsorship

[Sponsorship Approach](https://thingsgateway.cn/docs/1000)

## Community

QQ Group: 605534569 [Jump](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=NnBjPO-8kcNFzo_RzSbdICflb97u2O1i&authKey=V1MI3iJtpDMHc08myszP262kDykbx2Yev6ebE4Me0elTe0P0IFAmtU5l7Sy5w0jx&noverify=0&group_code=605534569)

## Pro Plugins

[Plugin List](https://thingsgateway.cn/docs/1001)

View File

@@ -1,4 +1,7 @@
# ThingsGateway
<p align="center">
<img src="logo.svg" width = "400" height = "200" alt="The name of the image" align=center />
</p>
[![star](https://gitee.com/ThingsGateway/ThingsGateway/badge/star.svg?theme=gvp)](https://gitee.com/ThingsGateway/ThingsGateway/stargazers)
[![star](https://img.shields.io/github/stars/ThingsGateway/ThingsGateway?logo=github)](https://github.com/ThingsGateway/ThingsGateway)

BIN
icon.ico

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 177 KiB

9
logo.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 236 KiB

View File

@@ -11,8 +11,6 @@
using System.ComponentModel;
using System.Runtime;
using ThingsGateway.NewLife;
namespace ThingsGateway.Admin.Application;
/// <inheritdoc/>
@@ -22,7 +20,7 @@ public class HardwareInfo
/// 当前磁盘信息
/// </summary>
public DriveInfo DriveInfo { get; set; }
/// <summary>
/// 主机环境
@@ -151,6 +149,6 @@ public class HardwareInfo
/// 更新时间
/// </summary>
public DateTime UpdateTime { get; set; }
public ulong AppRunTotalMinute { get; set; }
public ulong SystemRunTotalMinute { get; set; }
public ulong AppRunTotalMinute { get; set; }
public ulong SystemRunTotalMinute { get; set; }
}

View File

@@ -74,10 +74,10 @@ public class HardwareJob : IJob, IHardwareJob
{
try
{
var machine = MachineInfo.GetCurrent();
var machine = MachineInfo.GetCurrent();
if (HardwareInfo == null)
{
HardwareInfo=machine.AdaptHardwareInfo();
HardwareInfo = machine.AdaptHardwareInfo();
string currentPath = Directory.GetCurrentDirectory();
DriveInfo drive = new(Path.GetPathRoot(currentPath));
@@ -100,8 +100,8 @@ public class HardwareJob : IJob, IHardwareJob
var machine = MachineInfo.GetCurrent();
machine.Refresh();
machine.AdaptHardwareInfo(HardwareInfo);
HardwareInfo.AppRunTotalMinute = (ulong)Runtime.AppTickCount64 / 1000 /60;
HardwareInfo.SystemRunTotalMinute = (ulong)Runtime.TickCount64 / 1000 /60;
HardwareInfo.AppRunTotalMinute = (ulong)Runtime.AppTickCount64 / 1000 / 60;
HardwareInfo.SystemRunTotalMinute = (ulong)Runtime.TickCount64 / 1000 / 60;
HardwareInfo.UpdateTime = TimerX.Now;
error = false;
}

View File

@@ -8,8 +8,6 @@
// QQ群605534569
//------------------------------------------------------------------------------
using BootstrapBlazor.Components;
using Riok.Mapperly.Abstractions;
using ThingsGateway.NewLife;
@@ -20,7 +18,7 @@ namespace ThingsGateway.Admin.Application;
public static partial class AdminMapper
{
public static partial HardwareInfo AdaptHardwareInfo(this MachineInfo src);
public static partial void AdaptHardwareInfo(this MachineInfo src, HardwareInfo dto);
public static partial void AdaptHardwareInfo(this MachineInfo src, HardwareInfo dto);
public static partial LoginInput AdaptLoginInput(this OpenApiLoginInput src);
public static partial OpenApiLoginOutput AdaptOpenApiLoginOutput(this LoginOutput src);

View File

@@ -87,45 +87,45 @@ public class Startup : AppStartup
#if NET8_0_OR_GREATER
services
.AddRazorComponents(options => options.TemporaryRedirectionUrlValidityDuration = TimeSpan.FromMinutes(10))
.AddInteractiveServerComponents(options =>
{
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
options.MaxBufferedUnacknowledgedRenderBatches = 20;
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
})
.AddHubOptions(options =>
{
//单个传入集线器消息的最大大小。默认 32 KB
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
options.StreamBufferCapacity = 30;
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
.AddInteractiveServerComponents(options =>
{
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
options.MaxBufferedUnacknowledgedRenderBatches = 5;
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
})
.AddHubOptions(options =>
{
//单个传入集线器消息的最大大小。默认 32 KB
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
options.StreamBufferCapacity = 30;
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
});
#else
services.AddServerSideBlazor(options =>
{
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
options.MaxBufferedUnacknowledgedRenderBatches = 20;
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
}).AddHubOptions(options =>
{
//单个传入集线器消息的最大大小。默认 32 KB
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
options.StreamBufferCapacity = 30;
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
{
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
options.MaxBufferedUnacknowledgedRenderBatches = 5;
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
})
.AddHubOptions(options =>
{
//单个传入集线器消息的最大大小。默认 32 KB
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
options.StreamBufferCapacity = 30;
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
});
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -0,0 +1,134 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Common;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class EnumerableQueryPageOptionsExtensions
{
public static IEnumerable<T> GetData<T>(this IEnumerable<T> datas, QueryPageOptions option, out int totalCount, FilterKeyValueAction where = null)
{
totalCount = 0;
if (datas == null)
return new List<T>();
where ??= option.ToFilter();
if (where.HasFilters())
{
datas = datas.Where(where.GetFilterFunc<T>());//name asc模式
}
if (option.SortList.Count > 0)
{
datas = datas.Sort(option.SortList);//name asc模式
}
if (option.AdvancedSortList.Count > 0)
{
datas = datas.Sort(option.AdvancedSortList);//name asc模式
}
if (option.SortOrder != SortOrder.Unset && !option.SortName.IsNullOrWhiteSpace())
{
datas = datas.Sort(option.SortName, option.SortOrder);
}
totalCount = datas.Count();
if (option.IsPage)
{
datas = datas.Skip((option.PageIndex - 1) * option.PageItems).Take(option.PageItems);
}
else if (option.IsVirtualScroll)
{
datas = datas.Skip((option.StartIndex) * option.PageItems).Take(option.PageItems);
}
return datas;
}
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
{
if (queryFunc != null)
query = queryFunc(query);
where ??= option.ToFilter();
if (where.HasFilters())
{
query = query.Where(where.GetFilterFunc<T>());//name asc模式
}
if (option.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(option.SortName))
{
var invoker = Utility.GetSortFunc<T>();
query = invoker(query, option.SortName, option.SortOrder);
}
else if (option.SortList.Count > 0)
{
var invoker = Utility.GetSortListFunc<T>();
query = invoker(query, option.SortList);
}
else if (option.AdvancedSortList.Count > 0)
{
var invoker = Utility.GetSortListFunc<T>();
query = invoker(query, option.AdvancedSortList);
}
return query;
}
/// <summary>
/// 根据查询条件返回QueryData
/// </summary>
public static QueryData<T> GetQueryData<T>(this IEnumerable<T> datas, QueryPageOptions option, FilterKeyValueAction where = null)
{
var ret = new QueryData<T>()
{
IsSorted = option.SortOrder != SortOrder.Unset,
IsFiltered = option.Filters.Count > 0,
IsAdvanceSearch = option.AdvanceSearches.Count > 0 || option.CustomerSearches.Count > 0,
IsSearch = option.Searches.Count > 0
};
var items = datas.GetData(option, out var totalCount, where);
ret.TotalCount = totalCount;
if (totalCount > 0)
{
if (!items.Any() && option.PageIndex != 1)
{
option.PageIndex = 1;
items = datas.GetData(option, out totalCount, where);
}
}
ret.Items = items.ToList();
return ret;
}
/// <summary>
/// 根据查询条件返回QueryData
/// </summary>
public static QueryData<SelectedItem> GetQueryData<T>(this IEnumerable<T> datas, VirtualizeQueryOption option, Func<IEnumerable<T>, IEnumerable<SelectedItem>> func, FilterKeyValueAction where = null)
{
var ret = new QueryData<SelectedItem>()
{
IsSorted = false,
IsFiltered = false,
IsAdvanceSearch = false,
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
};
var items = datas.Skip((option.StartIndex)).Take(option.Count);
ret.TotalCount = datas.Count();
ret.Items = func(items).ToList();
return ret;
}
}

View File

@@ -19,7 +19,7 @@ using System.Reflection;
using ThingsGateway.Common.Extension;
namespace ThingsGateway.DB;
namespace ThingsGateway.Common;
/// <summary>
/// 导出excel扩展

View File

@@ -11,7 +11,7 @@
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Http;
namespace ThingsGateway.DB;
namespace ThingsGateway.Common;
/// <inheritdoc/>
[ThingsGateway.DependencyInjection.SuppressSniffer]

View File

@@ -14,8 +14,6 @@ using System.ComponentModel.DataAnnotations;
using ThingsGateway.Common.Extension;
using Swashbuckle.AspNetCore.SwaggerGen;
#if NET8_0_OR_GREATER
using System.Collections.Frozen;

View File

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

View File

@@ -10,7 +10,7 @@
using Yitter.IdGenerator;
namespace ThingsGateway.DB;
namespace ThingsGateway.Common;
/// <summary>
/// 公共功能

View File

@@ -16,74 +16,6 @@ namespace ThingsGateway.DB;
[ThingsGateway.DependencyInjection.SuppressSniffer]
public static class QueryPageOptionsExtensions
{
public static IEnumerable<T> GetData<T>(this IEnumerable<T> datas, QueryPageOptions option, out int totalCount, FilterKeyValueAction where = null)
{
totalCount = 0;
if (datas == null)
return new List<T>();
where ??= option.ToFilter();
if (where.HasFilters())
{
datas = datas.Where(where.GetFilterFunc<T>());//name asc模式
}
if (option.SortList.Count > 0)
{
datas = datas.Sort(option.SortList);//name asc模式
}
if (option.AdvancedSortList.Count > 0)
{
datas = datas.Sort(option.AdvancedSortList);//name asc模式
}
if (option.SortOrder != SortOrder.Unset && !option.SortName.IsNullOrWhiteSpace())
{
datas = datas.Sort(option.SortName, option.SortOrder);
}
totalCount = datas.Count();
if (option.IsPage)
{
datas = datas.Skip((option.PageIndex - 1) * option.PageItems).Take(option.PageItems);
}
else if (option.IsVirtualScroll)
{
datas = datas.Skip((option.StartIndex) * option.PageItems).Take(option.PageItems);
}
return datas;
}
public static IEnumerable<T> GetQuery<T>(this IEnumerable<T> query, QueryPageOptions option, Func<IEnumerable<T>, IEnumerable<T>>? queryFunc = null, FilterKeyValueAction where = null)
{
if (queryFunc != null)
query = queryFunc(query);
where ??= option.ToFilter();
if (where.HasFilters())
{
query = query.Where(where.GetFilterFunc<T>());//name asc模式
}
if (option.SortOrder != SortOrder.Unset && !string.IsNullOrEmpty(option.SortName))
{
var invoker = Utility.GetSortFunc<T>();
query = invoker(query, option.SortName, option.SortOrder);
}
else if (option.SortList.Count > 0)
{
var invoker = Utility.GetSortListFunc<T>();
query = invoker(query, option.SortList);
}
else if (option.AdvancedSortList.Count > 0)
{
var invoker = Utility.GetSortListFunc<T>();
query = invoker(query, option.AdvancedSortList);
}
return query;
}
/// <summary>
/// 根据查询条件返回sqlsugar ISugarQueryable
/// </summary>
@@ -111,50 +43,4 @@ public static class QueryPageOptionsExtensions
return query;
}
/// <summary>
/// 根据查询条件返回QueryData
/// </summary>
public static QueryData<T> GetQueryData<T>(this IEnumerable<T> datas, QueryPageOptions option, FilterKeyValueAction where = null)
{
var ret = new QueryData<T>()
{
IsSorted = option.SortOrder != SortOrder.Unset,
IsFiltered = option.Filters.Count > 0,
IsAdvanceSearch = option.AdvanceSearches.Count > 0 || option.CustomerSearches.Count > 0,
IsSearch = option.Searches.Count > 0
};
var items = datas.GetData(option, out var totalCount, where);
ret.TotalCount = totalCount;
if (totalCount > 0)
{
if (!items.Any() && option.PageIndex != 1)
{
option.PageIndex = 1;
items = datas.GetData(option, out totalCount, where);
}
}
ret.Items = items.ToList();
return ret;
}
/// <summary>
/// 根据查询条件返回QueryData
/// </summary>
public static QueryData<SelectedItem> GetQueryData<T>(this IEnumerable<T> datas, VirtualizeQueryOption option, Func<IEnumerable<T>, IEnumerable<SelectedItem>> func, FilterKeyValueAction where = null)
{
var ret = new QueryData<SelectedItem>()
{
IsSorted = false,
IsFiltered = false,
IsAdvanceSearch = false,
IsSearch = !option.SearchText.IsNullOrWhiteSpace()
};
var items = datas.Skip((option.StartIndex)).Take(option.Count);
ret.TotalCount = datas.Count();
ret.Items = func(items).ToList();
return ret;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -8,7 +8,7 @@ namespace ThingsGateway.NewLife.Collections;
/// 文档 https://newlifex.com/core/object_pool
/// </remarks>
/// <typeparam name="T"></typeparam>
public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
public class ObjectPoolLock<T> : DisposeBase where T : class
{
#region
/// <summary>名称</summary>
@@ -22,11 +22,6 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
/// <summary>繁忙个数</summary>
public Int32 BusyCount => _BusyCount;
/// <summary>最大个数。默认00表示无上限</summary>
public Int32 Max { get; set; } = 0;
private readonly object _syncRoot = new();
/// <summary>基础空闲集合。只保存最小个数,最热部分</summary>
private readonly Stack<T> _free = new();
@@ -73,7 +68,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
if (_inited) return;
_inited = true;
WriteLog($"Init {typeof(T).FullName} Max={Max}");
WriteLog($"Init {typeof(T).FullName}");
}
}
#endregion
@@ -86,7 +81,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
T? pi = null;
do
{
lock (_syncRoot)
lock (lockThis)
{
if (_free.Count > 0)
{
@@ -95,13 +90,6 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
}
else
{
if (Max > 0 && BusyCount >= Max)
{
var msg = $"申请失败,已有 {BusyCount:n0} 达到或超过最大值 {Max:n0}";
WriteLog("Acquire Max " + msg);
throw new Exception(Name + " " + msg);
}
pi = OnCreate();
if (BusyCount == 0) Init();
@@ -114,7 +102,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
// 如果拿到的对象不可用,则重新借
} while (pi == null || !OnGet(pi));
lock (_syncRoot)
lock (lockThis)
{
// 加入繁忙集合
_busy.Add(pi);
@@ -129,16 +117,12 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
/// <returns></returns>
protected virtual Boolean OnGet(T value) => true;
/// <summary>申请资源包装项Dispose时自动归还到池中</summary>
/// <returns></returns>
public PoolItem<T> GetItem() => new(this, Get());
/// <summary>归还</summary>
/// <param name="value"></param>
public virtual Boolean Return(T value)
{
if (value == null) return false;
lock (_syncRoot)
lock (lockThis)
{
// 从繁忙队列找到并移除缓存项
if (!_busy.Remove(value))
@@ -163,7 +147,7 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
{
return false;
}
lock (_syncRoot)
lock (lockThis)
{
_free.Push(value);
_FreeCount++;
@@ -180,18 +164,9 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
/// <summary>清空已有对象</summary>
public virtual Int32 Clear()
{
var count = _FreeCount + _BusyCount;
//_busy.Clear();
//_BusyCount = 0;
//_free.Clear();
//while (_free2.TryDequeue(out var rs)) ;
//_FreeCount = 0;
lock (_syncRoot)
lock (lockThis)
{
count = _FreeCount + _BusyCount;
var count = _FreeCount + _BusyCount;
while (_free.Count > 0)
{
@@ -207,9 +182,9 @@ public class ObjectPoolLock<T> : DisposeBase, IPool<T> where T : class
}
_busy.Clear();
_BusyCount = 0;
return count;
}
return count;
}
/// <summary>销毁</summary>

View File

@@ -1,7 +1,9 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
@@ -13,9 +15,6 @@ using ThingsGateway.NewLife.Reflection;
using ThingsGateway.NewLife.Serialization;
using ThingsGateway.NewLife.Windows;
using System.Diagnostics;
using System.Runtime;
#if NETFRAMEWORK
using System.Management;
@@ -413,7 +412,7 @@ public class MachineInfo
#if NETFRAMEWORK || WINDOWS
{
var ci = new Microsoft.VisualBasic.Devices.ComputerInfo();
Memory = (ulong)(ci.TotalPhysicalMemory / 1024.0);
Memory = (ulong)(ci.TotalPhysicalMemory / 1024.0 / 1024.0);
}
#endif
@@ -633,7 +632,7 @@ public class MachineInfo
//if (dic2.TryGetValue("Model Name", out str)) Product = str;
if (dic.TryGetValue("Model Identifier", out var str)) Product = str;
if (dic.TryGetValue("Processor Name", out str)) Processor = str;
if (dic.TryGetValue("Memory", out str)) Memory = (UInt64)str.TrimEnd("GB").Trim().ToLong() * 1024 * 1024;
if (dic.TryGetValue("Memory", out str)) Memory = (UInt64)str.TrimEnd("GB").Trim().ToLong() * 1024;
if (dic.TryGetValue("Serial Number (system)", out str)) Serial = str;
if (dic.TryGetValue("Hardware UUID", out str)) UUID = str;
if (dic.TryGetValue("Processor Name", out str)) Processor = str;
@@ -869,7 +868,7 @@ public class MachineInfo
if (dic != null)
{
if (dic.TryGetValue("MemTotal", out var str) && !str.IsNullOrEmpty())
Memory = (UInt64)str.TrimEnd(" kB").ToLong();
Memory = (UInt64)(str.TrimEnd(" kB").ToLong() / 1024.0);
ulong ma = 0;
if (dic.TryGetValue("MemAvailable", out str) && !str.IsNullOrEmpty())

View File

@@ -184,7 +184,7 @@ public static class Runtime
public static Int64 AppStartTick = TickCount64;
/// <summary>软件启动以来的毫秒数</summary>
public static Int64 AppTickCount64 => TickCount64-AppStartTick;
public static Int64 AppTickCount64 => TickCount64 - AppStartTick;
#if NETCOREAPP3_1_OR_GREATER
/// <summary>系统启动以来的毫秒数</summary>
@@ -256,7 +256,7 @@ public static class Runtime
return dic;
}
#endregion
#endregion
#region
private static Boolean? _createConfigOnMissing;

View File

@@ -1,29 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Foundation.props" />
<PropertyGroup>
<IncludeAsyncInterfaces>false</IncludeAsyncInterfaces>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework)=='net6.0'">
<DefineConstants>$(DefineConstants);PLAT_THREADPOOLWORKITEM;PLAT_MRVTSC</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework)=='net8.0'">
<DefineConstants>$(DefineConstants);PLAT_THREADPOOLWORKITEM;PLAT_MRVTSC</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework)=='netstandard2.0'">
<DefineConstants>$(DefineConstants);PLAT_MRVTSC</DefineConstants>
<IncludeAsyncInterfaces>true</IncludeAsyncInterfaces>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework)=='net462'">
<DefineConstants>$(DefineConstants);PLAT_MRVTSC</DefineConstants>
<IncludeAsyncInterfaces>true</IncludeAsyncInterfaces>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="1.1.0" Condition="$(IncludeAsyncInterfaces)=='true'" />
</ItemGroup>
</Project>

View File

@@ -1,5 +0,0 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Benchmark, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d136a87a0c11ded39332f7ead1f2ce53256a42a350ee847df1dc520bc781210a5f1fe73b3f91a4001fdcfa35340a212e70443de46e6928510f57a55f4ab9b95257f5067d4de4be8cdad460781866b20a6ca20f66302df61b52e55e16c080cad95aae8b94ffdd54718b9963d0240b0d0d5afe655b05c91771f7dc8a314387d3c3")]
[assembly: InternalsVisibleTo("PooledAwait.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100d136a87a0c11ded39332f7ead1f2ce53256a42a350ee847df1dc520bc781210a5f1fe73b3f91a4001fdcfa35340a212e70443de46e6928510f57a55f4ab9b95257f5067d4de4be8cdad460781866b20a6ca20f66302df61b52e55e16c080cad95aae8b94ffdd54718b9963d0240b0d0d5afe655b05c91771f7dc8a314387d3c3")]

View File

@@ -4,7 +4,7 @@
<Import Project="..\..\PackNuget.props" />
<PropertyGroup>
<TargetFrameworks>net47;netstandard2.0;net6.0;net6.0-windows;net8.0;$(OtherTargetFrameworks);net8.0-windows;</TargetFrameworks>
<TargetFrameworks>net462;netstandard2.0;net6.0;net6.0-windows;net8.0;$(OtherTargetFrameworks);net8.0-windows;</TargetFrameworks>
<AssemblyName>ThingsGateway.NewLife.X</AssemblyName>
<RootNamespace>ThingsGateway.NewLife</RootNamespace>
<AssemblyTitle>工具核心库</AssemblyTitle>
@@ -12,9 +12,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>newlife.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
@@ -24,7 +21,7 @@
<None Remove="..\..\..\README.zh-CN.md" Pack="false" PackagePath="\" />
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net47' or '$(TargetFramework)'=='net5.0-windows' or '$(TargetFramework)'=='net6.0-windows' or '$(TargetFramework)'=='net7.0-windows' or '$(TargetFramework)'=='net8.0-windows'">
<PropertyGroup Condition="'$(TargetFramework)'=='net462' or '$(TargetFramework)'=='net5.0-windows' or '$(TargetFramework)'=='net6.0-windows' or '$(TargetFramework)'=='net7.0-windows' or '$(TargetFramework)'=='net8.0-windows'">
<DefineConstants>__WIN__</DefineConstants>
</PropertyGroup>
@@ -35,11 +32,12 @@
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">
<PackageReference Include="System.Memory" Version="4.6.3" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net47'">
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
<PackageReference Include="System.Memory" Version="4.6.3" />
<PackageReference Include="System.ValueTuple" Version="4.6.1" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net47'">
<ItemGroup Condition="'$(TargetFramework)'=='net462'">
<Using Include="System.Net.Http" />
<Reference Include="Microsoft.VisualBasic" />
<Reference Include="System.Management" />
@@ -54,10 +52,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup>
<PropertyGroup Condition="$(TargetFramework)=='net6.0'">
<DefineConstants>$(DefineConstants);PLAT_THREADPOOLWORKITEM;PLAT_MRVTSC</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework)=='net8.0'">
<PropertyGroup Condition="$(TargetFramework)=='net6.0' OR $(TargetFramework)=='net6.0-windows' OR $(TargetFramework)=='net8.0' OR $(TargetFramework)=='net8.0-windows' OR $(TargetFramework)=='net10.0' OR $(TargetFramework)=='net10.0-windows'">
<DefineConstants>$(DefineConstants);PLAT_THREADPOOLWORKITEM;PLAT_MRVTSC</DefineConstants>
</PropertyGroup>
@@ -66,7 +61,7 @@
<IncludeAsyncInterfaces>true</IncludeAsyncInterfaces>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework)=='net47'">
<PropertyGroup Condition="$(TargetFramework)=='net462'">
<DefineConstants>$(DefineConstants);PLAT_MRVTSC</DefineConstants>
<IncludeAsyncInterfaces>true</IncludeAsyncInterfaces>
</PropertyGroup>
@@ -88,6 +83,6 @@
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
</ItemGroup>-->
</Project>

View File

@@ -7,7 +7,6 @@
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Photino.NET" Version="4.0.16" />
</ItemGroup>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -24,7 +24,7 @@
<ItemGroup>
<PackageReference Include="SqlSugarCore.Dm" Version="8.8.2" />
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.905" />
<PackageReference Include="SqlSugarCore.Kdbndp" Version="9.3.7.1031" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="7.0.20" />
<!--<PackageReference Include="Microsoft.Data.Sqlite" Version="$(NET10Version)" />-->
<PackageReference Include="MySqlConnector" Version="2.4.0" />

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.12.11</PluginVersion>
<ProPluginVersion>10.12.11</ProPluginVersion>
<DefaultVersion>10.12.11</DefaultVersion>
<PluginVersion>10.12.26</PluginVersion>
<ProPluginVersion>10.12.26</ProPluginVersion>
<DefaultVersion>10.12.26</DefaultVersion>
<AuthenticationVersion>10.11.7</AuthenticationVersion>
<SourceGeneratorVersion>10.11.7</SourceGeneratorVersion>
<NET8Version>8.0.21</NET8Version>
@@ -12,7 +12,7 @@
<IsTrimmable>false</IsTrimmable>
<ManagementProPluginVersion>10.11.87</ManagementProPluginVersion>
<ManagementPluginVersion>10.11.87</ManagementPluginVersion>
<TSVersion>4.0.0-rc.1</TSVersion>
<TSVersion>4.0.0-rc.10</TSVersion>
</PropertyGroup>
@@ -22,7 +22,9 @@
<OtherTargetFrameworks>net10.0</OtherTargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<RestoreEnablePackagePruning Condition="'$(TargetFramework)' == 'net462' "> false</RestoreEnablePackagePruning>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' != 'net8.0' ">
<PluginTargetFramework>net10.0</PluginTargetFramework>

View File

@@ -30,7 +30,6 @@ public interface IClientChannel : IChannel, ISender, IClient, IClientSender, IOn
WaitHandlePool<MessageBase> WaitHandlePool { get; }
WaitLock GetLock(string key);
void LogSeted(bool logSeted);
/// <summary>
/// 设置数据处理适配器

View File

@@ -80,13 +80,11 @@ public class OtherChannel : SetupConfigObject, IClientChannel
private bool logSet;
/// <inheritdoc/>
public void SetDataHandlingAdapterLogger(ILog log)
{
if (!logSet && ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
if (ReadOnlyDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{
logSet = true;
handleAdapter.Logger = log;
}
}
@@ -96,12 +94,8 @@ public class OtherChannel : SetupConfigObject, IClientChannel
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
SetAdapter(singleStreamDataHandlingAdapter);
logSet = false;
}
public void LogSeted(bool logSeted)
{
logSet = logSeted;
}
/// <summary>
/// 设置数据处理适配器。
/// </summary>

View File

@@ -52,13 +52,11 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
/// <inheritdoc/>
public DataHandlingAdapter ReadOnlyDataHandlingAdapter => ProtectedDataHandlingAdapter;
private bool logSet;
/// <inheritdoc/>
public void SetDataHandlingAdapterLogger(ILog log)
{
if (!logSet && ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
if (ProtectedDataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{
logSet = true;
handleAdapter.Logger = log;
}
}
@@ -68,13 +66,9 @@ public class SerialPortChannel : SerialPortClient, IClientChannel
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
SetAdapter(singleStreamDataHandlingAdapter);
logSet = false;
}
public void LogSeted(bool logSeted)
{
logSet = logSeted;
}
/// <inheritdoc/>

View File

@@ -34,12 +34,10 @@ public class TcpClientChannel : TcpClient, IClientChannel
WaitHandlePool = new WaitHandlePool<MessageBase>(minSign, maxSign);
pool?.CancelAll();
}
private bool logSet;
public void SetDataHandlingAdapterLogger(ILog log)
{
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
if (DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{
logSet = true;
handleAdapter.Logger = log;
}
}
@@ -49,12 +47,8 @@ public class TcpClientChannel : TcpClient, IClientChannel
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
SetAdapter(singleStreamDataHandlingAdapter);
logSet = false;
}
public void LogSeted(bool logSeted)
{
logSet = logSeted;
}
/// <inheritdoc/>
public ChannelReceivedEventHandler ChannelReceived { get; } = new();

View File

@@ -25,13 +25,11 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
public TcpSessionClientChannel()
{
}
private bool logSet;
/// <inheritdoc/>
public void SetDataHandlingAdapterLogger(ILog log)
{
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
if (DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{
logSet = true;
handleAdapter.Logger = log;
}
}
@@ -41,12 +39,8 @@ public class TcpSessionClientChannel : TcpSessionClient, IClientChannel
if (adapter is SingleStreamDataHandlingAdapter singleStreamDataHandlingAdapter)
SetAdapter(singleStreamDataHandlingAdapter);
logSet = false;
}
public void LogSeted(bool logSeted)
{
logSet = logSeted;
}
public void ResetSign(int minSign = 1, int maxSign = ushort.MaxValue - 1)
{
var pool = WaitHandlePool;

View File

@@ -30,27 +30,21 @@ public class UdpSessionChannel : UdpSession, IClientChannel
ResetSign();
}
public override TouchSocketConfig Config => base.Config ?? ChannelOptions.Config;
private bool logSet;
/// <inheritdoc/>
public void SetDataHandlingAdapterLogger(ILog log)
{
if (!logSet && DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
if (DataHandlingAdapter is IDeviceDataHandleAdapter handleAdapter)
{
logSet = true;
handleAdapter.Logger = log;
}
}
public void LogSeted(bool logSeted)
{
logSet = logSeted;
}
/// <inheritdoc/>
public void SetDataHandlingAdapter(DataHandlingAdapter adapter)
{
if (adapter is UdpDataHandlingAdapter udpDataHandlingAdapter)
SetAdapter(udpDataHandlingAdapter);
logSet = false;
}

View File

@@ -333,6 +333,10 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
/// <inheritdoc/>
private Task SendAsync(ISendMessage sendMessage, IClientChannel channel, CancellationToken token = default)
{
if(!channel.Online)
{
throw new InvalidOperationException("Channel is offline");
}
return SendAsync(this, sendMessage, channel, token);
static async PooledTask SendAsync(DeviceBase @this, ISendMessage sendMessage, IClientChannel channel, CancellationToken token)
@@ -1060,10 +1064,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
}
Channel.Collects.Remove(this);
if (Channel is IClientChannel clientChannel)
{
clientChannel.LogSeted(false);
}
}
}
@@ -1118,10 +1118,6 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
Channel.Collects.Remove(this);
if (Channel is IClientChannel clientChannel)
{
clientChannel.LogSeted(false);
}
}

View File

@@ -428,8 +428,9 @@ where TWriter : IByteBlockWriter
}
public static int WriteNormalString(this Span<byte> span, string value, Encoding encoding)
public static int WriteNormalString(this Span<byte> span, string value, Encoding encoding=null)
{
encoding ??= Encoding.UTF8;
var maxSize = encoding.GetMaxByteCount(value.Length);
var chars = value.AsSpan();
@@ -439,7 +440,7 @@ where TWriter : IByteBlockWriter
{
fixed (byte* p1 = &span[0])
{
var len = Encoding.UTF8.GetBytes(p, chars.Length, p1, maxSize);
var len = encoding.GetBytes(p, chars.Length, p1, maxSize);
return len;
}
}

View File

@@ -21,7 +21,7 @@
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Admin\ThingsGateway.NewLife.X\ThingsGateway.NewLife.X.csproj" />

View File

@@ -59,14 +59,15 @@ public class CronScheduledTask : DisposeBase, IScheduledTask
}
return false;
}
private static volatile int NextId = 0;
public void Start()
{
_timer?.Dispose();
if (Check()) return;
if (_taskAction != null)
_timer = new TimerX(TimerCallback, _state, _interval, nameof(CronScheduledTask)) { Async = true, Reentrant = false };
_timer = new TimerX(TimerCallback, _state, _interval, $"{nameof(CronScheduledTask)}{(Interlocked.Increment(ref NextId) / 100)}") { Async = true, Reentrant = false };
else if (_taskFunc != null || _valueTaskFunc != null)
_timer = new TimerX(TimerCallbackAsync, _state, _interval, nameof(CronScheduledTask)) { Async = true, Reentrant = false };
_timer = new TimerX(TimerCallbackAsync, _state, _interval, $"{nameof(CronScheduledTask)}{(Interlocked.Increment(ref NextId) / 100)}") { Async = true, Reentrant = false };
}
private ValueTask TimerCallbackAsync(object? state)

View File

@@ -46,11 +46,13 @@ public class ScheduledAsyncTask : DisposeBase, IScheduledTask, IScheduledIntInte
}
return false;
}
private static volatile int NextId = 0;
public void Start()
{
_timer?.Dispose();
if (!Check())
_timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, nameof(ScheduledAsyncTask)) { Async = true, Reentrant = false };
_timer = new TimerX(DoAsync, _state, IntervalMS, IntervalMS, $"{nameof(ScheduledAsyncTask)}{(Interlocked.Increment(ref NextId) / 100)}") { Async = true, Reentrant = false };
}
private ValueTask DoAsync(object? state)

View File

@@ -36,11 +36,12 @@ public class ScheduledSyncTask : DisposeBase, IScheduledTask, IScheduledIntInter
}
return false;
}
private static volatile int NextId = 0;
public void Start()
{
_timer?.Dispose();
if (!Check())
_timer = new TimerX(TimerCallback, _state, IntervalMS, IntervalMS, nameof(ScheduledSyncTask)) { Async = true, Reentrant = false };
_timer = new TimerX(TimerCallback, _state, IntervalMS, IntervalMS, $"{nameof(ScheduledSyncTask)}{(Interlocked.Increment(ref NextId) / 100)}") { Async = true, Reentrant = false };
}
private void TimerCallback(object? state)

View File

@@ -430,7 +430,7 @@ public abstract partial class CollectBase : DriverBase
readErrorCount++;
if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
@this.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] failed - {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
@this.LogMessage?.Trace(string.Format("{0} - Failed to collect data [{1} - {2}] - {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
//if (LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
// LogMessage?.Trace(string.Format("{0} - Collecting [{1} - {2}]", DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length));
@@ -450,7 +450,7 @@ public abstract partial class CollectBase : DriverBase
{
// 读取成功时记录日志并增加成功计数器
if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
@this.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data succeeded {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content.Span.ToHexString(' ')));
@this.LogMessage?.Trace(string.Format("{0} - Collected [{1} - {2}] data successfully {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.Content.Span.ToHexString(' ')));
@this.CurrentDevice.SetDeviceStatus(TimerX.Now, null);
}
else
@@ -475,7 +475,7 @@ public abstract partial class CollectBase : DriverBase
if (!cancellationToken.IsCancellationRequested)
{
if (@this.LogMessage?.LogLevel <= TouchSocket.Core.LogLevel.Trace)
@this.LogMessage?.Trace(string.Format("{0} - Collection [{1} - {2}] data failed - {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
@this.LogMessage?.Trace(string.Format("{0} - Failed to collect data [{1} - {2}] - {3}", @this.DeviceName, variableSourceRead?.RegisterAddress, variableSourceRead?.Length, readResult.ErrorMessage));
}
}
@@ -566,10 +566,24 @@ public abstract partial class CollectBase : DriverBase
{
foreach (var item in varRead)
{
if (!item.Value.Equals(writeInfoLists[item].ToObject(item.Value?.GetType())))
var cValue = writeInfoLists[item].ToObject(item.RawValue?.GetType());
if (!item.RawValue.Equals(cValue))
{
// 如果写入值与读取值不同,则更新操作结果为失败
operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}");
if (cValue is IComparable)
{
operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}");
}
else
{
if (cValue != null)
{
if (item.RawValue.ToSystemTextJsonString(false) != cValue.ToSystemTextJsonString(false))
operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}");
}
else
operResults[item.Name] = new OperResult($"The write value is inconsistent with the read value, Write value: {writeInfoLists[item].ToObject(item.Value?.GetType())}, read value: {item.Value}");
}
}
}
}

View File

@@ -509,34 +509,16 @@ internal sealed class AlarmTask : IDisposable
{
scheduledTask.Change(100, 100);
}
ParallelOptions.CancellationToken = cancellation;
// 遍历设备变量列表
if (!GlobalData.AlarmEnableIdVariables.IsEmpty)
{
// 使用 Parallel.ForEach 执行指定的操作
Parallel.ForEach(GlobalData.AlarmEnableIdVariables, ParallelOptions, (item, state, index) =>
{
// 如果取消请求已经被触发,则结束任务
if (cancellation.IsCancellationRequested)
return;
// 如果该变量的报警功能未启用,则跳过该变量
if (!item.Value.AlarmEnable)
return;
// 如果该变量离线,则跳过该变量
if (!item.Value.IsOnline)
return;
// 对该变量进行报警分析
AlarmAnalysis(item.Value);
});
Parallel.ForEach(GlobalData.AlarmEnableIdVariables, ParallelOptions, Analysis);
}
else
{
//if (scheduledTask.Period != 5000)
// scheduledTask.Change(0, 5000); // 如果没有启用报警的变量则设置下次执行时间为5秒后
scheduledTask.SetNext(5000); // 如果没有启用报警的变量则设置下次执行时间为5秒后
}
@@ -552,6 +534,21 @@ internal sealed class AlarmTask : IDisposable
}
}
private static void Analysis(KeyValuePair<long, VariableRuntime> item, ParallelLoopState state, long index)
{
// 如果取消请求已经被触发,则结束任务
if (state.ShouldExitCurrentIteration)
return;
// 如果该变量的报警功能未启用,则跳过该变量
if (!item.Value.AlarmEnable)
return;
// 如果该变量离线,则跳过该变量
if (!item.Value.IsOnline)
return;
// 对该变量进行报警分析
AlarmAnalysis(item.Value);
}
}

View File

@@ -26,7 +26,7 @@ internal static class RuntimeServiceHelper
public static async Task InitAsync(List<ChannelRuntime> newChannelRuntimes, List<DeviceRuntime> newDeviceRuntimes, ILogger logger)
{
//批量修改之后,需要重新加载通道
foreach (var newChannelRuntime in newChannelRuntimes)
await newChannelRuntimes.ParallelForEachAsync(async (newChannelRuntime, token) =>
{
try
{
@@ -44,7 +44,7 @@ internal static class RuntimeServiceHelper
{
logger.LogWarning(ex, "Init Channel");
}
}
}).ConfigureAwait(false);
GlobalData.ChannelDeviceRuntimeDispatchService.Dispatch(null);
GlobalData.VariableRuntimeDispatchService.Dispatch(null);
}
@@ -71,28 +71,28 @@ internal static class RuntimeServiceHelper
public static async Task InitAsync(List<DeviceRuntime> newDeviceRuntimes, ILogger logger)
{
foreach (var newDeviceRuntime in newDeviceRuntimes)
{
try
await newDeviceRuntimes.ParallelForEachAsync(async (newDeviceRuntime, token) =>
{
if (GlobalData.IdChannels.TryGetValue(newDeviceRuntime.ChannelId, out var newChannelRuntime))
try
{
newDeviceRuntime.Init(newChannelRuntime);
if (GlobalData.IdChannels.TryGetValue(newDeviceRuntime.ChannelId, out var newChannelRuntime))
{
newDeviceRuntime.Init(newChannelRuntime);
var newVariableRuntimes = (await GlobalData.VariableService.GetAllAsync(newDeviceRuntime.Id).ConfigureAwait(false)).AdaptEnumerableVariableRuntime();
var newVariableRuntimes = (await GlobalData.VariableService.GetAllAsync(newDeviceRuntime.Id).ConfigureAwait(false)).AdaptEnumerableVariableRuntime();
newVariableRuntimes.ParallelForEach(item => item.Init(newDeviceRuntime));
newVariableRuntimes.ParallelForEach(item => item.Init(newDeviceRuntime));
}
else
{
logger.LogWarning("Channel not found");
}
}
else
catch (Exception ex)
{
logger.LogWarning("Channel not found");
logger.LogWarning(ex, "Init Device");
}
}
catch (Exception ex)
{
logger.LogWarning(ex, "Init Device");
}
}
}).ConfigureAwait(false);
GlobalData.ChannelDeviceRuntimeDispatchService.Dispatch(null);
GlobalData.VariableRuntimeDispatchService.Dispatch(null);
@@ -234,16 +234,16 @@ internal static class RuntimeServiceHelper
public static async Task RestartDeviceAsync(List<DeviceRuntime> newDeviceRuntimes)
{
var groups = GlobalData.GetDeviceThreadManages(newDeviceRuntimes);
foreach (var group in groups)
await groups.ParallelForEachAsync(async (group, token) =>
{
if (group.Key != null)
await group.Key.RestartDeviceAsync(group.Value, false).ConfigureAwait(false);
}
foreach (var group in GlobalData.GetAllVariableBusinessDeviceRuntime().Where(a => !newDeviceRuntimes.Contains(a)).Where(a => a.Driver?.DeviceThreadManage != null).GroupBy(a => a.Driver.DeviceThreadManage))
}).ConfigureAwait(false);
await GlobalData.GetAllVariableBusinessDeviceRuntime().Where(a => !newDeviceRuntimes.Contains(a)).Where(a => a.Driver?.DeviceThreadManage != null).GroupBy(a => a.Driver.DeviceThreadManage).ParallelForEachAsync(async (group, token) =>
{
if (group.Key != null)
await group.Key.RestartDeviceAsync(group.ToArray(), false).ConfigureAwait(false);
}
}).ConfigureAwait(false);
}
public static async Task RemoveDeviceAsync(HashSet<long> newDeciceIds)
{
@@ -254,17 +254,17 @@ internal static class RuntimeServiceHelper
public static async Task RemoveDeviceAsync(IEnumerable<DeviceRuntime> deviceRuntimes)
{
var groups = GlobalData.GetDeviceThreadManages(deviceRuntimes);
foreach (var group in groups)
{
if (group.Key != null)
await group.Key.RemoveDeviceAsync(group.Value.Select(a => a.Id).ToArray()).ConfigureAwait(false);
}
await groups.ParallelForEachAsync(async (group, token) =>
{
if (group.Key != null)
await group.Key.RemoveDeviceAsync(group.Value.Select(a => a.Id).ToArray()).ConfigureAwait(false);
}).ConfigureAwait(false);
foreach (var group in GlobalData.GetAllVariableBusinessDeviceRuntime().Where(a => !deviceRuntimes.Contains(a)).Where(a => a.Driver?.DeviceThreadManage != null).GroupBy(a => a.Driver.DeviceThreadManage))
await GlobalData.GetAllVariableBusinessDeviceRuntime().Where(a => !deviceRuntimes.Contains(a)).Where(a => a.Driver?.DeviceThreadManage != null).GroupBy(a => a.Driver.DeviceThreadManage).ParallelForEachAsync(async (group, token) =>
{
if (group.Key != null)
await group.Key.RestartDeviceAsync(group.ToArray(), false).ConfigureAwait(false);
}
}).ConfigureAwait(false);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -41,7 +41,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
<PackageReference Include="BenchmarkDotNet" Version="0.15.5" />
<PackageReference Include="NModbus" Version="3.0.81" />
<PackageReference Include="S7netplus" Version="0.20.0" />
<!--<PackageReference Include="ThingsGateway.Foundation.Modbus" Version="$(DefaultVersion)" />

View File

@@ -10,6 +10,7 @@
using Riok.Mapperly.Abstractions;
using ThingsGateway.Common;
using ThingsGateway.DB;
namespace ThingsGateway.Plugin.DB;

View File

@@ -171,10 +171,10 @@ public partial class MqttCollect : CollectBase
mqttClientSubscribeOptionsBuilder = mqttClientSubscribeOptionsBuilder.WithTopicFilter(
f => f.WithTopic(item));
}
var mqttClientSubscribeOptions = mqttClientSubscribeOptionsBuilder.Build();
if (mqttClientSubscribeOptions.TopicFilters.Count > 0)
_mqttSubscribeOptions = mqttClientSubscribeOptions;
}
var mqttClientSubscribeOptions = mqttClientSubscribeOptionsBuilder.Build();
if (mqttClientSubscribeOptions.TopicFilters.Count > 0)
_mqttSubscribeOptions = mqttClientSubscribeOptions;
return Task.FromResult(dataResult);
}

View File

@@ -20,6 +20,8 @@ using System.Diagnostics.CodeAnalysis;
using ThingsGateway.Extension;
using ThingsGateway.Foundation.OpcDa;
using ThingsGateway.Foundation.OpcDa.Rcw;
using ThingsGateway.Common;
#if Plugin

View File

@@ -35,6 +35,8 @@ using ThingsGateway.Razor;
using TouchSocket.Core;
using ThingsGateway.Common;
namespace ThingsGateway.Debug;
/// <summary>
@@ -73,7 +75,7 @@ public partial class OpcUaImportVariable
{
Items = BuildTreeItemList(await PopulateBranchAsync(ObjectIds.ObjectsFolder), RenderTreeItem).ToList();
ShowSkeleton = false;
await InvokeAsync(StateHasChanged);
return InvokeAsync(StateHasChanged);
});
}
await base.OnAfterRenderAsync(firstRender);

View File

@@ -83,6 +83,9 @@
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor" Link="Layout\MainLayout.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\MainLayout.razor.cs" Link="Layout\MainLayout.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\MainLayout.razor.css" Link="Layout\MainLayout.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\Gitee2025opensource.razor" Link="Layout\Gitee2025opensource.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\Gitee2025opensource.razor.cs" Link="Layout\Gitee2025opensource.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Gitee2025opensource.razor.css" Link="Layout\Gitee2025opensource.razor.css" />
<Content Include="..\ThingsGateway.Server\Layout\AccessDenied.razor" Link="Layout\AccessDenied.razor" />
<Compile Include="..\ThingsGateway.Server\Layout\AccessDenied.razor.cs" Link="Layout\AccessDenied.razor.cs" />
<Content Include="..\ThingsGateway.Server\Layout\Login.razor" Link="Layout\Login.razor" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -0,0 +1,18 @@
@inherits ComponentBase
@namespace ThingsGateway.Server
<div class="popup-overlay">
<div class="popup-window text-align: center; ">
<p>
🎉 <strong>ThingsGateway</strong> 正在参加
<strong> Gitee 2025 最受欢迎的开源软件评选活动 </strong>
需要你的支持!
</p>
<a href="https://gitee.com/activity/2025opensource?ident=I4XWR9"
target="_blank"
rel="noopener noreferrer"
class="popup-link" onclick="@OnClick">
👉 前往投票支持
</a>
</div>
</div>

View File

@@ -0,0 +1,28 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
#pragma warning disable CA2007 // 考虑对等待的任务调用 ConfigureAwait
using Microsoft.AspNetCore.Components;
namespace ThingsGateway.Server;
public partial class Gitee2025opensource
{
[CascadingParameter]
private Func<Task>? OnCloseAsync { get; set; }
private async Task OnClick()
{
if (OnCloseAsync != null)
{
await OnCloseAsync();
}
}
}

View File

@@ -0,0 +1,43 @@
/* 弹窗遮罩 */
.popup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(10, 10, 10, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 9999;
}
/* 弹窗主体 */
.popup-window {
background: #fff;
border-radius: 12px;
padding: 30px 40px;
max-width: 420px;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.3);
text-align: center;
animation: popup-in 0.3s ease-out;
}
/* 按钮与链接 */
.popup-link {
display: inline-block;
margin-top: 10px;
margin-right: 20px; /* ✅ 按钮之间留空隙 */
margin-bottom: 20px; /* ✅ 按钮之间留空隙 */
background-color: #e4405f;
color: white;
padding: 8px 16px;
border-radius: 8px;
text-decoration: none;
font-weight: bold;
transition: background 0.2s;
}
.popup-link:hover {
background-color: #c8324f;
}

View File

@@ -112,3 +112,4 @@
</CascadingValue>
</CascadingValue>

View File

@@ -239,4 +239,36 @@ public partial class MainLayout : IDisposable
};
await DialogService.Show(op);
}
/// <summary>
/// 显示投票弹窗
/// </summary>
/// <returns></returns>
public async Task ShowGitee()
{
await DialogService.Show(new DialogOption()
{
IsScrolling = false,
ShowFooter = false,
Title = "Gitee 评选活动",
BodyTemplate = BootstrapDynamicComponent.CreateComponent<Gitee2025opensource>().Render(),
ShowCloseButton = false,
ShowHeaderCloseButton = false,
Size = Size.Small,
});
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
if (WebsiteOption.Value.Demo)
{
await ShowGitee();
}
}
await base.OnAfterRenderAsync(firstRender);
}
}

View File

@@ -41,7 +41,7 @@ public class Program
{
}
}
};

View File

@@ -109,8 +109,8 @@ public class Startup : AppStartup
.AddInteractiveServerComponents(options =>
{
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
options.MaxBufferedUnacknowledgedRenderBatches = 20;
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
options.MaxBufferedUnacknowledgedRenderBatches = 5;
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
})
@@ -120,30 +120,31 @@ public class Startup : AppStartup
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
options.StreamBufferCapacity = 30;
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
});
#else
services.AddServerSideBlazor(options =>
{
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromMinutes(2);
options.MaxBufferedUnacknowledgedRenderBatches = 20;
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
}).AddHubOptions(options =>
{
//单个传入集线器消息的最大大小。默认 32 KB
options.MaximumReceiveMessageSize =32 * 1024 * 1024;
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
options.StreamBufferCapacity = 30;
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.HandshakeTimeout = TimeSpan.FromSeconds(30);
});
{
options.RootComponents.MaxJSRootComponents = 500;
options.JSInteropDefaultCallTimeout = TimeSpan.FromSeconds(30);
options.MaxBufferedUnacknowledgedRenderBatches = 5;
options.DisconnectedCircuitMaxRetained = 1;
options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10);
})
.AddHubOptions(options =>
{
//单个传入集线器消息的最大大小。默认 32 KB
options.MaximumReceiveMessageSize = 32 * 1024 * 1024;
//可为客户端上载流缓冲的最大项数。 如果达到此限制,则会阻止处理调用,直到服务器处理流项。
options.StreamBufferCapacity = 30;
options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
});
#endif
@@ -207,10 +208,7 @@ public class Startup : AppStartup
var websiteOptions = App.GetConfig<WebsiteOptions>("Website");
if (websiteOptions.BlazorConnectionLimitEnable)
{
services.AddSingleton<CircuitHandler, ConnectionLimiterCircuitHandler>();
}
services.AddSingleton<CircuitHandler, ConnectionLimiterCircuitHandler>();
if (websiteOptions.Demo)
{
authenticationBuilder.AddOAuth<GiteeOAuthOptions, AdminOAuthHandler<GiteeOAuthOptions>>("Gitee", "Gitee", options =>

View File

@@ -10,6 +10,8 @@
using Microsoft.AspNetCore.Components.Server.Circuits;
using System.Runtime;
using ThingsGateway.Common;
namespace ThingsGateway.Server;
@@ -22,6 +24,12 @@ public class ConnectionLimiterCircuitHandler : CircuitHandler
public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken)
{
//主动触发垃圾回收,释放上个链路资源
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true);
GC.WaitForPendingFinalizers();
GC.Collect(1, GCCollectionMode.Optimized, blocking: false, compacting: false);
WebsiteOptions ??= App.GetOptions<WebsiteOptions>();
if (!WebsiteOptions.BlazorConnectionLimitEnable)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 177 KiB