Compare commits

..

4 Commits

Author SHA1 Message Date
2248356998 qq.com
79406ad4a0 更新依赖 2025-09-27 23:59:38 +08:00
2248356998 qq.com
20c44f10ca 10.11.73 2025-09-27 20:18:56 +08:00
2248356998 qq.com
31d6b2a9e6 添加FastMapper 2025-09-26 15:24:37 +08:00
2248356998 qq.com
68e5a9c546 build: 10.11.71 2025-09-26 11:51:05 +08:00
39 changed files with 468 additions and 106 deletions

View File

@@ -14,7 +14,7 @@
<ItemGroup>
<PackageReference Include="BootstrapBlazor.TableExport" Version="9.2.6" />
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
<PackageReference Include="BootstrapBlazor" Version="9.10.2" />
<PackageReference Include="BootstrapBlazor" Version="9.10.3" />
</ItemGroup>
<ItemGroup>

View File

@@ -10,7 +10,6 @@
// ------------------------------------------------------------------------
using System.Security.Cryptography;
using System.Text;
namespace ThingsGateway.DataEncryption;
@@ -72,8 +71,8 @@ public static class PBKDF2Encryption
#if NET10_0_OR_GREATER
var computedHash = Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(text), saltBytes, iterationCount, HashAlgorithmName.SHA256, derivedKeyLength);
#else
using var pbkdf2 = new Rfc2898DeriveBytes(text, saltBytes, iterationCount, HashAlgorithmName.SHA256);
var computedHash = pbkdf2.GetBytes(derivedKeyLength);
using var pbkdf2 = new Rfc2898DeriveBytes(text, saltBytes, iterationCount, HashAlgorithmName.SHA256);
var computedHash = pbkdf2.GetBytes(derivedKeyLength);
#endif
return computedHash.SequenceEqual(storedHashBytes);

View File

@@ -0,0 +1,169 @@
using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
namespace ThingsGateway.NewLife;
public class FastMapperOption
{
public Dictionary<string, string> MapperProperties { get; set; } = new();
public HashSet<string> IgnoreProperties { get; set; } = new();
}
public static class FastMapper
{
// 泛型 + 非泛型共用缓存
private static readonly ConcurrentDictionary<(Type Source, Type Target), Delegate> _mapCache
= new ConcurrentDictionary<(Type, Type), Delegate>();
#region
public static TTarget Mapper<TSource, TTarget>(TSource source, FastMapperOption option = null)
where TTarget : class, new()
{
if (source == null) return null;
var key = (typeof(TSource), typeof(TTarget));
if (!_mapCache.TryGetValue(key, out var del))
{
del = CreateMapFunc<TSource, TTarget>();
_mapCache[key] = del;
}
var func = (Func<TSource, FastMapperOption, TTarget>)del;
return func(source, option);
}
#endregion
#region
public static object Mapper(object source, Type targetType, FastMapperOption option = null)
{
if (source == null) return null;
var sourceType = source.GetType();
var key = (sourceType, targetType);
if (!_mapCache.TryGetValue(key, out var del))
{
// 动态生成泛型委托并缓存
var method = typeof(FastMapper).GetMethod(nameof(CreateMapFunc), BindingFlags.NonPublic | BindingFlags.Static);
var genericMethod = method.MakeGenericMethod(sourceType, targetType);
del = genericMethod.Invoke(null, null) as Delegate;
_mapCache[key] = del;
}
return del.DynamicInvoke(source, option);
}
#endregion
#region Expression Tree
private static Func<TSource, FastMapperOption, TTarget> CreateMapFunc<TSource, TTarget>()
where TTarget : class, new()
{
var sourceType = typeof(TSource);
var targetType = typeof(TTarget);
var sourceParam = Expression.Parameter(sourceType, "src");
var optionParam = Expression.Parameter(typeof(FastMapperOption), "opt");
var targetVar = Expression.Variable(targetType, "dest");
var expressions = new List<Expression>
{
Expression.Assign(targetVar, Expression.New(targetType))
};
var sourceProperties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var targetProperties = targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var targetDict = targetProperties.Where(p => p.CanWrite).ToDictionary(p => p.Name);
foreach (var sp in sourceProperties)
{
if (!sp.CanRead) continue;
// FastMapperOption 重命名
var pNameExpr = Expression.Constant(sp.Name);
if (targetDict.TryGetValue(sp.Name, out PropertyInfo? tp))
{
// Ignore check
Expression ignoreCheck = Expression.Call(
Expression.Property(optionParam, nameof(FastMapperOption.IgnoreProperties)),
nameof(HashSet<string>.Contains),
null,
pNameExpr
);
Expression assign;
// 1⃣ 简单类型直接赋值
if (IsSimpleType(sp.PropertyType))
{
assign = Expression.Assign(
Expression.Property(targetVar, tp),
Expression.Convert(Expression.Property(sourceParam, sp), tp.PropertyType)
);
}
// 2⃣ 集合类型
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(sp.PropertyType) && sp.PropertyType != typeof(string))
{
var elementSourceType = sp.PropertyType.IsArray
? sp.PropertyType.GetElementType()
: sp.PropertyType.GetGenericArguments().FirstOrDefault();
var elementTargetType = tp.PropertyType.IsArray
? tp.PropertyType.GetElementType()
: tp.PropertyType.GetGenericArguments().FirstOrDefault();
if (elementSourceType != null && elementTargetType != null)
{
var mapListMethod = typeof(FastMapper).GetMethod(nameof(MapList), BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod(elementSourceType, elementTargetType);
assign = Expression.Assign(
Expression.Property(targetVar, tp),
Expression.Call(mapListMethod, Expression.Property(sourceParam, sp), optionParam)
);
}
else continue;
}
// 3⃣ 引用类型/嵌套对象
else
{
var mapMethod = typeof(FastMapper).GetMethod(nameof(Mapper), new Type[] { typeof(object), typeof(Type), typeof(FastMapperOption) });
var arg0 = Expression.Convert(Expression.Property(sourceParam, sp), typeof(object)); // ✅ fix Nullable/ValueType
assign = Expression.Assign(
Expression.Property(targetVar, tp),
Expression.Convert(
Expression.Call(mapMethod, arg0, Expression.Constant(tp.PropertyType), optionParam),
tp.PropertyType
)
);
}
expressions.Add(assign);
}
}
expressions.Add(targetVar);
var body = Expression.Block(new[] { targetVar }, expressions);
return Expression.Lambda<Func<TSource, FastMapperOption, TTarget>>(body, sourceParam, optionParam).Compile();
}
#endregion
#region
public static IEnumerable<TTarget> MapList<TSource, TTarget>(IEnumerable<TSource> list, FastMapperOption option = null)
where TTarget : class, new()
{
if (list == null) yield break;
foreach (var item in list)
yield return Mapper<TSource, TTarget>(item, option);
}
#endregion
private static bool IsSimpleType(Type type)
{
return type.IsPrimitive
|| type.IsEnum
|| type == typeof(string)
|| type == typeof(decimal)
|| type == typeof(DateTime)
|| (Nullable.GetUnderlyingType(type) != null && IsSimpleType(Nullable.GetUnderlyingType(type)));
}
}

View File

@@ -32,7 +32,7 @@
<PackageReference Include="CsvHelper" Version="33.1.0" />
<PackageReference Include="TDengine.Connector" Version="3.1.9" />
<PackageReference Include="Oracle.ManagedDataAccess.Core" Version="23.9.1" />
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.26" />
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.27" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.1" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.11.70</PluginVersion>
<ProPluginVersion>10.11.70</ProPluginVersion>
<DefaultVersion>10.11.70</DefaultVersion>
<PluginVersion>10.11.75</PluginVersion>
<ProPluginVersion>10.11.75</ProPluginVersion>
<DefaultVersion>10.11.75</DefaultVersion>
<AuthenticationVersion>10.11.6</AuthenticationVersion>
<SourceGeneratorVersion>10.11.6</SourceGeneratorVersion>
<NET8Version>8.0.20</NET8Version>
@@ -12,7 +12,7 @@
<IsTrimmable>false</IsTrimmable>
<ManagementProPluginVersion>10.11.70</ManagementProPluginVersion>
<ManagementPluginVersion>10.11.70</ManagementPluginVersion>
<TSVersion>4.0.0-beta.57</TSVersion>
<TSVersion>4.0.0-beta.70</TSVersion>
</PropertyGroup>

View File

@@ -36,7 +36,7 @@
<EditorItem @bind-Field="@context.StopBits" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.DtrEnable" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.RtsEnable" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.StreamAsync" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.Handshake" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.CacheTimeout" Ignore=@(context.ChannelType == ChannelTypeEnum.UdpSession || context.ChannelType == ChannelTypeEnum.Other) />

View File

@@ -42,7 +42,7 @@
"PortName": "COM Port",
"RemoteUrl": "Remote IP Address",
"RtsEnable": "Rts",
"StreamAsync": "StreamAsync",
"Handshake": "Handshake",
"SaveChannel": "Add/Modify Channel",
"StopBits": "Stop Bits"
},
@@ -102,7 +102,7 @@
"PortName": "PortName",
"RemoteUrl": "RemoteUrl",
"RtsEnable": "RtsEnable",
"StreamAsync": "StreamAsync",
"Handshake": "Handshake",
"StopBits": "StopBits"
}
}

View File

@@ -40,7 +40,7 @@
"PortName": "COM口",
"RemoteUrl": "远程url",
"RtsEnable": "Rts",
"StreamAsync": "串口流读写",
"Handshake": "Handshake",
"SaveChannel": "添加/修改通道",
"StopBits": "停止位"
},
@@ -100,7 +100,7 @@
"PortName": "COM口",
"RemoteUrl": "远程url",
"RtsEnable": "Rts",
"StreamAsync": "串口流读写",
"Handshake": "串口流读写",
"StopBits": "停止位"
}
}

View File

@@ -69,9 +69,9 @@ namespace ThingsGateway.Foundation
public virtual bool DtrEnable { get; set; } = true;
/// <summary>
/// StreamAsync
/// Handshake
/// </summary>
public virtual bool StreamAsync { get; set; } = false;
public virtual Handshake Handshake { get; set; }
/// <summary>
/// RtsEnable

View File

@@ -140,10 +140,19 @@ public static class ChannelOptionsExtensions
/// <returns></returns>
private static SerialPortChannel GetSerialPort(this TouchSocketConfig config, IChannelOptions channelOptions)
{
var serialPortOption = channelOptions.Map<SerialPortOption>();
serialPortOption.ThrowIfNull(nameof(SerialPortOption));
channelOptions.ThrowIfNull(nameof(SerialPortOption));
channelOptions.Config = config;
config.SetSerialPortOption(serialPortOption);
config.SetSerialPortOption(options =>
{
options.PortName = channelOptions.PortName;
options.BaudRate = channelOptions.BaudRate;
options.DataBits = channelOptions.DataBits;
options.Parity = channelOptions.Parity;
options.StopBits = channelOptions.StopBits;
options.DtrEnable = channelOptions.DtrEnable;
options.RtsEnable = channelOptions.RtsEnable;
options.Handshake = channelOptions.Handshake;
});
//载入配置
SerialPortChannel serialPortChannel = new SerialPortChannel(channelOptions);
return serialPortChannel;

View File

@@ -72,11 +72,7 @@ public interface IChannelOptions
/// </summary>
bool RtsEnable { get; set; }
/// <summary>
/// StreamAsync
/// </summary>
bool StreamAsync { get; set; }
Handshake Handshake { get; set; }
#endregion
/// <summary>
/// 最大并发数量

View File

@@ -179,7 +179,12 @@ public class DeviceSingleStreamDataHandleAdapter<TRequest> : CustomDataHandlingA
{
throw new Exception($"Unable to convert {nameof(requestInfo)} to {nameof(ISendMessage)}");
}
var span = writer.GetSpan(sendMessage.MaxLength);
Span<byte> span = default;
if (Logger?.LogLevel <= LogLevel.Trace)
{
span = writer.GetSpan(sendMessage.MaxLength);
}
sendMessage.Build(ref writer);
if (Logger?.LogLevel <= LogLevel.Trace)
{

View File

@@ -18,8 +18,6 @@ using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Collections;
using ThingsGateway.NewLife.Extension;
using TouchSocket.SerialPorts;
namespace ThingsGateway.Foundation;
/// <summary>

View File

@@ -22,7 +22,7 @@
"PortName": "PortName",
"RemoteUrl": "RemoteUrl",
"RtsEnable": "RtsEnable",
"StreamAsync": "StreamAsync",
"Handshake": "Handshake",
"StopBits": "StopBits"
},
"ThingsGateway.Foundation.ConverterConfig": {

View File

@@ -22,7 +22,7 @@
"PortName": "COM口",
"RemoteUrl": "远程url",
"RtsEnable": "Rts",
"StreamAsync": "串口流读写",
"Handshake": "Handshake",
"StopBits": "停止位"
},
"ThingsGateway.Foundation.ConverterConfig": {

View File

@@ -15,6 +15,7 @@ using System.Text;
using ThingsGateway.Foundation.Extension.Generic;
using ThingsGateway.Foundation.Extension.String;
using ThingsGateway.NewLife;
using TouchSocket.Resources;
@@ -187,8 +188,7 @@ public partial class ThingsGatewayBitConverter : IThingsGatewayBitConverter
// 更新设备地址为去除附加信息后的地址
registerAddress = sb.ToString();
var converter = (IThingsGatewayBitConverter)this!.Map(type);
var converter = (IThingsGatewayBitConverter)FastMapper.Mapper(this, type);
// 如果没有解析出任何附加信息,则直接返回默认的数据转换器
if (bcdFormat == null && stringlength == null && encoding == null && dataFormat == null && wstring == null)
{

View File

@@ -16,8 +16,6 @@ using ThingsGateway.NewLife.Json.Extension;
using TouchSocket.Core;
using static System.Net.Mime.MediaTypeNames;
namespace ThingsGateway.Gateway.Application;
/// <summary>

View File

@@ -138,11 +138,11 @@ public class Channel : ChannelOptionsBase, IPrimaryIdEntity, IBaseDataEntity, IB
public override bool RtsEnable { get; set; }
/// <summary>
/// StreamAsync
/// Handshake
/// </summary>
[SugarColumn(ColumnDescription = "StreamAsync", IsNullable = true)]
[SugarColumn(ColumnDescription = "Handshake", IsNullable = true)]
[AutoGenerateColumn(Visible = false, Filterable = true, Sortable = true)]
public override bool StreamAsync { get; set; } = false;
public override Handshake Handshake { get; set; }
/// <summary>
/// 缓存超时

View File

@@ -121,7 +121,7 @@ public class Device : BaseDataEntity, IValidatableObject
/// </summary>
[SugarColumn(ColumnDescription = "冗余扫描间隔")]
[AutoGenerateColumn(Visible = true, Filterable = true, Sortable = true)]
[MinValue(30000)]
[MinValue(10000)]
public virtual int RedundantScanIntervalTime { get; set; } = 30000;
/// <summary>

View File

@@ -307,7 +307,7 @@
"PortName": "PortName",
"RemoteUrl": "RemoteUrl",
"RtsEnable": "RtsEnable",
"StreamAsync": "StreamAsync",
"Handshake": "Handshake",
"SaveChannel": "Add/Modify Channel",
"SortCode": "SortCode",
"StopBits": "StopBits",

View File

@@ -306,7 +306,7 @@
"PortName": "COM口",
"RemoteUrl": "远程url",
"RtsEnable": "Rts",
"StreamAsync": "串口流读写",
"Handshake": "Handshake",
"SaveChannel": "添加/修改通道",
"SortCode": "排序",
"StopBits": "停止位",

View File

@@ -110,7 +110,11 @@ internal sealed class ChannelService : BaseService<Channel>, IChannelService
var device = devices.Keys.ToList();
ManageHelper.CheckDeviceCount(device.Count);
foreach (var item in device)
{
item.RedundantEnable = false;
item.RedundantDeviceId = null;
}
await db.Insertable(device).ExecuteCommandAsync().ConfigureAwait(false);
var variable = devices.SelectMany(a => a.Value).ToList();

View File

@@ -53,7 +53,11 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
{
var device = devices.Keys.ToList();
ManageHelper.CheckDeviceCount(device.Count);
foreach (var item in device)
{
item.RedundantEnable = false;
item.RedundantDeviceId = null;
}
await db.Insertable(device).ExecuteCommandAsync().ConfigureAwait(false);
var variable = devices.SelectMany(a => a.Value).ToList();
@@ -263,6 +267,9 @@ internal sealed class DeviceService : BaseService<Device>, IDeviceService
else
ManageHelper.CheckDeviceCount(1);
if (input.RedundantEnable && GlobalData.IsRedundant(input.RedundantDeviceId ?? 0))
throw Oops.Bah($"Redundancy configuration error, backup device has been planned into another redundancy group");
if (await base.SaveAsync(input, type).ConfigureAwait(false))
{

View File

@@ -330,6 +330,12 @@ internal sealed class DeviceThreadManage : IAsyncDisposable, IDeviceThreadManage
driver.IsInitSuccess = false;
LogMessage?.LogWarning(ex, string.Format(AppResource.InitFail, CurrentChannel.PluginName, driver?.DeviceName));
}
if (driver == null)
{
LogMessage?.LogWarning(string.Format(AppResource.InitFail, CurrentChannel.PluginName, driver?.DeviceName));
return;
}
if (driver?.DeviceId > 0)
{
if (CancellationTokenSources.TryGetValue(driver.DeviceId, out var oldCts))

View File

@@ -114,7 +114,7 @@
<EditorItem @bind-Field="@context.StopBits" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.DtrEnable" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.RtsEnable" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.StreamAsync" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.Handshake" Ignore=@(context.ChannelType != ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.CacheTimeout" Ignore=@(context.ChannelType == ChannelTypeEnum.UdpSession || context.ChannelType == ChannelTypeEnum.Other) />

View File

@@ -82,7 +82,7 @@
<EditorItem @bind-Field="@context.StopBits" Ignore=@(context.ChannelType!=ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.DtrEnable" Ignore=@(context.ChannelType!=ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.RtsEnable" Ignore=@(context.ChannelType!=ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.StreamAsync" Ignore=@(context.ChannelType!=ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.Handshake" Ignore=@(context.ChannelType!=ChannelTypeEnum.SerialPort) />
<EditorItem @bind-Field="@context.CacheTimeout" Ignore=@(context.ChannelType==ChannelTypeEnum.UdpSession||context.ChannelType==ChannelTypeEnum.Other) />

View File

@@ -6,12 +6,13 @@
<div class="rule-t text-end row g-0 pb-2">
<ButtonUpload class="col-auto ms-auto me-2" Size="Size.Small" ShowUploadFileList="false" BrowserButtonText=@(Localizer["Load"]) IsMultiple="false" OnChange="@Load" TValue="string" Accept=".json" />
<ButtonUpload class="col-auto ms-auto me-2" Size="Size.Small" ShowUploadFileList="false" BrowserButtonText=@(Localizer["Load"]) IsMultiple="true" OnChange="@Load" TValue="string" Accept=".json" />
<Button class="col-auto me-2" Size="Size.Small" Text=@(Localizer["Download"]) OnClick="Download" />
<Button class="col-auto me-2" Size="Size.Small" Text=@(Localizer["Save"]) OnClick="OnSave" />
<Button class="col-auto me-2" Size="Size.Small" Text=@(Localizer["Cancel"]) OnClick="OnCancel" />
</div>
<div class="row g-0 rule-b rulesengine">

View File

@@ -84,7 +84,8 @@ public partial class DragAndDrop
{
var data = await upload.GetBytesAsync(1024 * 1024);
var str = Encoding.UTF8.GetString(data);
await Load(str.FromJsonNetString<RulesJson>());
Load(str.FromJsonNetString<RulesJson>());
Load(Value);
}
catch (Exception ex)
{
@@ -92,21 +93,16 @@ public partial class DragAndDrop
}
}
protected override async Task OnParametersSetAsync()
protected override void OnParametersSet()
{
await Load(Value);
Load(Value);
}
public async Task Load(RulesJson value)
private void Load(RulesJson value)
{
try
{
Value = value;
RuleHelpers.Load(_blazorDiagram, Value);
}
catch (Exception ex)
{
await ToastService.Warn(ex);
}
Value = value;
RuleHelpers.Load(_blazorDiagram, Value);
}
[Inject]

View File

@@ -35,7 +35,8 @@ public partial class RulesPage : ThingsGatewayModuleComponentBase
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (firstRender) {
if (firstRender)
{
StateHasChanged();
}
}

View File

@@ -0,0 +1,163 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://kimdiego2098.github.io/
// QQ群605534569
//------------------------------------------------------------------------------
using BenchmarkDotNet.Diagnosers;
namespace ThingsGateway.Foundation;
using BenchmarkDotNet.Attributes;
using System;
using System.Collections.Generic;
using ThingsGateway.NewLife;
using TouchSocket.Core;
public class Foo
{
public string Name { get; set; }
public int Age { get; set; }
public int Age1 { get; set; }
public int Age2 { get; set; }
public int Age3 { get; set; }
public int Age11 { get; set; }
public int Age21 { get; set; }
public int Age31 { get; set; }
}
public class Bar
{
public string Name { get; set; }
public int Age { get; set; }
public Bar Child { get; set; }
public int Age1 { get; set; }
public int Age2 { get; set; }
public int Age3 { get; set; }
public int Age31 { get; set; }
public int Age32 { get; set; }
public int Age33 { get; set; }
}
[RankColumn]
[MemoryDiagnoser]
public class MapperBench
{
private List<Foo> foos;
[GlobalSetup]
public void Setup()
{
const int N = 100_000;
foos = new List<Foo>(N);
for (int i = 0; i < N; i++)
{
foos.Add(new Foo
{
Name = $"Name{i}",
Age = i,
});
}
}
[Benchmark(Baseline = true)]
public List<Bar> ManualConstructionMap100_000()
{
var bars = new List<Bar>(foos.Count);
foreach (var f in foos)
{
bars.Add(new Bar
{
Name = f.Name,
Age = f.Age,
});
}
return bars;
}
[Benchmark]
public List<Bar> ReflectionMap100_000()
{
var bars = new List<Bar>(foos.Count);
foreach (var f in foos)
{
bars.Add(OriginalMapper(f));
}
return bars;
}
[Benchmark]
public List<Bar> FastMapper100_000()
{
var bars = new List<Bar>(foos.Count);
foreach (var f in foos)
{
bars.Add(FastMapper.Mapper<Foo, Bar>(f));
}
return bars;
}
[Benchmark]
public List<Bar> ReflectionCachedMap100_000()
{
var bars = new List<Bar>(foos.Count);
foreach (var f in foos)
{
bars.Add(f.Map<Bar>());
}
return bars;
}
private static Bar OriginalMapper(Foo source)
{
if (source == null) return null;
var target = new Bar();
var st = typeof(Foo).GetProperties();
var tt = typeof(Bar).GetProperties();
foreach (var sp in st)
{
var tp = Array.Find(tt, x => x.Name == sp.Name);
if (tp == null || !tp.CanWrite) continue;
var value = sp.GetValue(source);
if (value == null)
{
tp.SetValue(target, null);
}
else if (IsSimpleType(sp.PropertyType))
{
// 基础类型直接赋值
tp.SetValue(target, value);
}
else
{
// 嵌套对象递归映射
tp.SetValue(target, value);
}
}
return target;
}
private static bool IsSimpleType(Type type)
{
return type.IsPrimitive || type.IsEnum || type == typeof(string) || type == typeof(decimal) || type == typeof(DateTime);
}
}

View File

@@ -32,7 +32,7 @@ using ModbusMaster = ThingsGateway.Foundation.Modbus.ModbusMaster;
namespace ThingsGateway.Foundation;
[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net10_0)]
//[SimpleJob(RuntimeMoniker.Net10_0)]
[MemoryDiagnoser]
public class ModbusBenchmark : IDisposable
{
@@ -57,10 +57,10 @@ public class ModbusBenchmark : IDisposable
ModbusType = ModbusTypeEnum.ModbusTcp,
};
thingsgatewaymodbus.InitChannel(clientChannel);
await clientChannel.SetupAsync(clientChannel.Config);
await clientChannel.SetupAsync(clientChannel.Config);
clientChannel.Logger.LogLevel = LogLevel.Warning;
await thingsgatewaymodbus.ConnectAsync(CancellationToken.None);
await thingsgatewaymodbus.ReadAsync("40001", 100);
await thingsgatewaymodbus.ConnectAsync(CancellationToken.None);
await thingsgatewaymodbus.ReadAsync("40001", 100);
thingsgatewaymodbuss.Add(thingsgatewaymodbus);
}
@@ -70,7 +70,7 @@ public class ModbusBenchmark : IDisposable
var factory = new NModbus.ModbusFactory();
var nmodbus = factory.CreateMaster(new TcpClient("127.0.0.1", 502));
await nmodbus.ReadHoldingRegistersAsync(1, 0, 100);
await nmodbus.ReadHoldingRegistersAsync(1, 0, 100);
nmodbuss.Add(nmodbus);
}
//for (int i = 0; i < Program.ClientCount; i++)
@@ -86,10 +86,10 @@ public class ModbusBenchmark : IDisposable
for (int i = 0; i < Program.ClientCount; i++)
{
var client = new ModbusTcpMaster();
await client.SetupAsync(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:502"));
await client.ConnectAsync(CancellationToken.None);
await client.ReadHoldingRegistersAsync(0, 100);
await client.SetupAsync(new TouchSocketConfig()
.SetRemoteIPHost("127.0.0.1:502"));
await client.ConnectAsync(CancellationToken.None);
await client.ReadHoldingRegistersAsync(0, 100);
modbusTcpMasters.Add(client);
}
@@ -104,8 +104,8 @@ public class ModbusBenchmark : IDisposable
for (int i = 0; i < Program.ClientCount; i++)
{
var client = factory.GetOrCreateTcpMaster();
await client.ConnectAsync("127.0.0.1", 502);
await client.ReadHoldingRegistersAsync(0x01, 0x00, 10);
await client.ConnectAsync("127.0.0.1", 502);
await client.ReadHoldingRegistersAsync(0x01, 0x00, 10);
_lgbModbusClients.Add(client);
}

View File

@@ -24,11 +24,9 @@ using TouchSocket.Core;
namespace ThingsGateway.Foundation;
//[SimpleJob(RuntimeMoniker.Net80)]
[SimpleJob(RuntimeMoniker.Net10_0)]
[SimpleJob(RuntimeMoniker.Net80)]
//[SimpleJob(RuntimeMoniker.Net10_0)]
[MemoryDiagnoser]
[BaselineColumn]
[RankColumn]
public class S7Benchmark : IDisposable
{
private List<SiemensS7Master> siemensS7s = new();
@@ -67,7 +65,7 @@ public class S7Benchmark : IDisposable
}
for (int i = 0; i < Program.ClientCount; i++)
{
var plc = new Plc(CpuType.S7300, "127.0.0.1", 102, 0, 0);
var plc = new Plc(CpuType.S71500, "127.0.0.1", 102, 0, 0);
await plc.OpenAsync();//打开plc连接
await plc.ReadAsync(DataType.Memory, 1, 0, VarType.Byte, 100);
plcs.Add(plc);
@@ -95,30 +93,29 @@ public class S7Benchmark : IDisposable
await Task.WhenAll(tasks);
}
//并发失败
//[Benchmark]
//public async Task HslCommunication()
//{
// List<Task> tasks = new List<Task>();
// foreach (var siemensS7Net in siemensS7Nets)
// {
// for (int i = 0; i < Program.TaskNumberOfItems; i++)
// {
// tasks.Add(Task.Run(async () =>
// {
// for (int i = 0; i < Program.NumberOfItems; i++)
// {
// var result = await siemensS7Net.ReadAsync("M0", 100);
// if (!result.IsSuccess)
// {
// throw new Exception(result.Message);
// }
// }
// }));
// }
// }
// await Task.WhenAll(tasks);
//}
[Benchmark]
public async Task HslCommunication()
{
List<Task> tasks = new List<Task>();
foreach (var siemensS7Net in siemensS7Nets)
{
for (int i = 0; i < Program.TaskNumberOfItems; i++)
{
tasks.Add(Task.Run(async () =>
{
for (int i = 0; i < Program.NumberOfItems; i++)
{
var result = await siemensS7Net.ReadAsync("M0", 100);
if (!result.IsSuccess)
{
throw new Exception(result.Message);
}
}
}));
}
}
await Task.WhenAll(tasks);
}

View File

@@ -45,10 +45,16 @@ namespace BenchmarkConsoleApp
//ManualConfig.Create(DefaultConfig.Instance)
//.WithOptions(ConfigOptions.DisableOptimizationsValidator)
//);
BenchmarkRunner.Run<ModbusBenchmark>(
ManualConfig.Create(DefaultConfig.Instance)
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
);
BenchmarkRunner.Run<MapperBench>(
ManualConfig.Create(DefaultConfig.Instance)
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
);
// BenchmarkRunner.Run<ModbusBenchmark>(
// ManualConfig.Create(DefaultConfig.Instance)
// .WithOptions(ConfigOptions.DisableOptimizationsValidator)
//);
// BenchmarkRunner.Run<S7Benchmark>(
//ManualConfig.Create(DefaultConfig.Instance)
//.WithOptions(ConfigOptions.DisableOptimizationsValidator)

View File

@@ -43,7 +43,7 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.15.4" />
<PackageReference Include="HslCommunication" Version="12.5.1" />
<PackageReference Include="Longbow.Modbus" Version="9.1.0" />
<PackageReference Include="Longbow.Modbus" Version="9.1.1" />
<PackageReference Include="NModbus" Version="3.0.81" />
<PackageReference Include="NModbus.Serial" Version="3.0.81" />
<PackageReference Include="S7netplus" Version="0.20.0" />

View File

@@ -14,7 +14,6 @@ using ThingsGateway.Common;
using ThingsGateway.DB;
using ThingsGateway.Debug;
using ThingsGateway.Foundation;
using ThingsGateway.Gateway.Application;
using ThingsGateway.NewLife;
using ThingsGateway.NewLife.Extension;
using ThingsGateway.NewLife.Threading;

View File

@@ -11,7 +11,6 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet.AspNetCore;

View File

@@ -12,7 +12,6 @@ using CSScripting;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MQTTnet;
using MQTTnet.Internal;

View File

@@ -12,6 +12,7 @@
<PkgOPCFoundation_NetStandard_Opc_Ua_ClientPackageFiles Include="$(PkgOPCFoundation_NetStandard_Opc_Ua_Client)\lib\net8.0\*.*" />
<PkgOPCFoundation_NetStandard_Opc_Ua_Client_ComplexTypesPackageFiles Include="$(PkgOPCFoundation_NetStandard_Opc_Ua_Client_ComplexTypes)\lib\net8.0\*.*" />
<PkgSystem_Formats_Asn1PackageFiles Include="$(PkgSystem_Formats_Asn1)\lib\net8.0\*.*" />
<PkgBitFaster_CachingPackageFiles Include="$(PkgBitFaster_Caching)\lib\net6.0\*.*" />
</ItemGroup>
<PropertyGroup>
@@ -23,6 +24,7 @@
<Copy SourceFiles="@(PkgOPCFoundation_NetStandard_Opc_Ua_ServerPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgOPCFoundation_NetStandard_Opc_Ua_Security_CertificatesPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgSystem_Formats_Asn1PackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgBitFaster_CachingPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" />
<!--<Copy SourceFiles="@(PkgThingsGateway_Foundation_OpcUaPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" />-->
<Copy SourceFiles="@(PkgOPCFoundation_NetStandard_Opc_Ua_ClientPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" />
<Copy SourceFiles="@(PkgOPCFoundation_NetStandard_Opc_Ua_Client_ComplexTypesPackageFiles)" DestinationFolder="$(ApplicationFolder)%(RecursiveDir)" />
@@ -36,6 +38,10 @@
<Pack>true</Pack>
<PackagePath>Content</PackagePath>
</Content>
<Content Include="$(ProjectDir)$(OutputPath)\$(TargetFramework)\**\*BitFaster*.dll">
<Pack>true</Pack>
<PackagePath>Content</PackagePath>
</Content>
</ItemGroup>
</Target>

View File

@@ -48,6 +48,10 @@
<PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.Security.Certificates" Version="1.5.377.21" GeneratePathProperty="true">
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<PackageReference Include="BitFaster.Caching" Version="2.5.4" GeneratePathProperty="true">
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
<!--<PackageReference Include="System.Formats.Asn1" Version="8.0.2" GeneratePathProperty="true">
<PrivateAssets>contentFiles;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>