Compare commits

...

9 Commits

Author SHA1 Message Date
2248356998 qq.com
1bad65378f build: 10.11.101 2025-10-14 11:46:26 +08:00
2248356998 qq.com
db3affc67e build: 10.11.100
feat(VariablePage): 优化UI性能
2025-10-14 10:18:09 +08:00
2248356998 qq.com
5ee8b50a92 build: 10.11.99 2025-10-13 22:11:05 +08:00
2248356998 qq.com
301beda2a2 build: 10.11.98 2025-10-13 21:10:39 +08:00
2248356998 qq.com
628b51a353 build: 10.11.97 2025-10-12 15:15:42 +08:00
2248356998 qq.com
f03445bc83 更新依赖 2025-10-12 00:25:57 +08:00
2248356998 qq.com
55a2ff5487 build: 10.11.96 2025-10-11 13:38:28 +08:00
2248356998 qq.com
0fef7dcf3b 更新依赖 2025-10-10 21:41:24 +08:00
2248356998 qq.com
19d9702606 fix: sqlite 批量更新 2025-10-10 14:19:43 +08:00
41 changed files with 971 additions and 409 deletions

View File

@@ -20,7 +20,7 @@
<ItemGroup>
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
<PackageReference Include="Rougamo.Fody" Version="5.0.2" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />

View File

@@ -5,7 +5,7 @@
<div class="tg-table h-100">
<Table TItem="TItem" IsBordered="true" IsStriped="true" TableSize="TableSize.Compact" SelectedRows=SelectedRows SelectedRowsChanged=privateSelectedRowsChanged IsMultipleSelect="IsMultipleSelect" @ref="Instance" SearchTemplate="SearchTemplate"
DataService="DataService" CreateItemCallback="CreateItemCallback!"
DataService="DataService" CreateItemCallback="CreateItemCallback!" RenderMode=RenderMode
IsPagination="IsPagination" PageItemsSource="PageItemsSource" IsFixedHeader="IsFixedHeader" IndentSize=24 RowHeight=RowHeight ShowSearchText="ShowSearchText" ShowSearchButton="ShowSearchButton" DisableEditButtonCallback="DisableEditButtonCallback" DisableDeleteButtonCallback="DisableDeleteButtonCallback" BeforeShowEditDialogCallback=" BeforeShowEditDialogCallback!"
IsTree="IsTree" OnTreeExpand="OnTreeExpand!" TreeNodeConverter="TreeNodeConverter!" TreeIcon="fa-solid fa-circle-chevron-right" TreeExpandIcon="fa-solid fa-circle-chevron-right fa-rotate-90" IsAutoQueryFirstRender=IsAutoQueryFirstRender
ShowDefaultButtons="ShowDefaultButtons" ShowAdvancedSearch="ShowAdvancedSearch" ShowResetButton=ShowResetButton
@@ -41,6 +41,7 @@
DoubleClickToEdit="DoubleClickToEdit"
OnDoubleClickCellCallback="OnDoubleClickCellCallback"
OnDoubleClickRowCallback="OnDoubleClickRowCallback"
RowContentTemplate="RowContentTemplate"
OnClickRowCallback="OnClickRowCallback">
</Table>
</div>

View File

@@ -13,6 +13,14 @@ namespace ThingsGateway.Admin.Razor;
[CascadingTypeParameter(nameof(TItem))]
public partial class AdminTable<TItem> where TItem : class, new()
{
/// <inheritdoc cref="Table{TItem}.RenderMode"/>
[Parameter]
public TableRenderMode RenderMode { get; set; }
public List<ITableColumn> Columns => Instance?.Columns;
/// <inheritdoc cref="Table{TItem}.SelectedRowsChanged"/>
[Parameter]
public EventCallback<List<TItem>> SelectedRowsChanged { get; set; }
@@ -40,6 +48,10 @@ public partial class AdminTable<TItem> where TItem : class, new()
/// <inheritdoc cref="Table{TItem}.OnDoubleClickRowCallback"/>
[Parameter]
public Func<TItem, Task>? OnDoubleClickRowCallback { get; set; }
/// <inheritdoc cref="Table{TItem}.RowContentTemplate"/>
[Parameter]
public RenderFragment<TableRowContext<TItem>>? RowContentTemplate { get; set; }
/// <inheritdoc cref="Table{TItem}.OnClickRowCallback"/>
[Parameter]
public Func<TItem, Task>? OnClickRowCallback { get; set; }

View File

@@ -5,7 +5,7 @@
<ItemGroup>
<ProjectReference Include="..\ThingsGateway.Admin.Application\ThingsGateway.Admin.Application.csproj" />
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.1" />
<PackageReference Include="BootstrapBlazor.Chart" Version="9.0.3" />
<PackageReference Include="BootstrapBlazor.UniverSheet" Version="9.0.5" />
<PackageReference Include="BootstrapBlazor.WinBox" Version="9.0.7" />
<PackageReference Include="BootstrapBlazor.CodeEditor" Version="9.0.3" />

View File

@@ -5,30 +5,101 @@ namespace ThingsGateway.NewLife;
public class ExpiringDictionary<TKey, TValue> : IDisposable
{
private ConcurrentDictionary<TKey, TValue> _dict = new();
private readonly TimerX _cleanupTimer;
public ExpiringDictionary(int cleanupInterval = 60000)
/// <summary>缓存项</summary>
public class CacheItem
{
_cleanupTimer = new TimerX(Clear, null, cleanupInterval, cleanupInterval) { Async = true };
private TValue? _value;
/// <summary>数值</summary>
public TValue? Value { get => _value; }
/// <summary>过期时间。系统启动以来的毫秒数</summary>
public Int64 ExpiredTime { get; set; }
/// <summary>是否过期</summary>
public Boolean Expired => ExpiredTime <= Runtime.TickCount64;
/// <summary>访问时间</summary>
public Int64 VisitTime { get; private set; }
/// <summary>构造缓存项</summary>
/// <param name="value"></param>
/// <param name="expire"></param>
public CacheItem(TValue? value, Int32 expire) => Set(value, expire);
/// <summary>设置数值和过期时间</summary>
/// <param name="value"></param>
/// <param name="expire">过期时间,秒</param>
public void Set(TValue value, Int32 expire)
{
_value = value;
var now = VisitTime = Runtime.TickCount64;
if (expire <= 0)
ExpiredTime = Int64.MaxValue;
else
ExpiredTime = now + expire * 1000L;
}
/// <summary>更新访问时间并返回数值</summary>
/// <returns></returns>
public TValue? Visit()
{
VisitTime = Runtime.TickCount64;
var rs = _value;
if (rs == null) return default;
return rs;
}
}
public void TryAdd(TKey key, TValue value)
private ConcurrentDictionary<TKey, CacheItem> _dict = new();
private readonly TimerX _cleanupTimer;
private int defaultExpire = 60;
public ExpiringDictionary(int expire = 60)
{
_dict.TryAdd(key, value);
defaultExpire = expire;
_cleanupTimer = new TimerX(TimerClear, null, 10000, 10000) { Async = true };
}
public bool TryAdd(TKey key, TValue value)
{
if (_dict.TryGetValue(key, out var item))
{
if (!item.Expired) return false;
item.Set(value, defaultExpire);
return true;
}
return _dict.TryAdd(key, new CacheItem(value, defaultExpire));
}
public bool TryGetValue(TKey key, out TValue value)
{
return _dict.TryGetValue(key, out value);
value = default;
// 没有值,直接结束
if (!_dict.TryGetValue(key, out var item) || item == null) return false;
// 得到已有值
value = item.Visit();
// 是否未过期的有效值
return !item.Expired;
}
public TValue GetOrAdd(TKey key, Func<TKey, TValue> func)
{
return _dict.GetOrAdd(key, func);
}
public TValue GetOrAdd(TKey key, TValue value)
{
return _dict.GetOrAdd(key, value);
CacheItem? item = null;
do
{
if (_dict.TryGetValue(key, out item) && item != null) return item.Visit();
item ??= new CacheItem(func(key), defaultExpire);
}
while (!_dict.TryAdd(key, item));
return item.Visit();
}
public bool TryRemove(TKey key) => _dict.TryRemove(key, out _);
@@ -41,7 +112,31 @@ public class ExpiringDictionary<TKey, TValue> : IDisposable
_dict = new();
data.Clear();
}
private void TimerClear(object? state)
{
var dic = _dict;
if (dic.IsEmpty) return;
// 60分钟之内过期的数据进入LRU淘汰
var now = Runtime.TickCount64;
// 这里先计算,性能很重要
var toDels = new List<TKey>();
foreach (var item in dic)
{
// 已过期,准备删除
var ci = item.Value;
if (ci.ExpiredTime <= now)
toDels.Add(item.Key);
}
// 确认删除
foreach (var item in toDels)
{
_dict.Remove(item);
}
}
public void Dispose()
{
_dict.Clear();

View File

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

View File

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

View File

@@ -463,7 +463,7 @@ public static class ProcessHelper
StandardOutputEncoding = outputEncoding,
StandardErrorEncoding = outputEncoding,
};
var process = Process.Start(psi);
using var process = Process.Start(psi);
if (process == null) return null;
if (msWait > 0 && !process.WaitForExit(msWait))

View File

@@ -561,7 +561,7 @@ public static class Reflect
/// <param name="method"></param>
/// <param name="target"></param>
/// <returns></returns>
public static TFunc? As<TFunc>(this MethodInfo method, object? target = null)
public static TFunc? As<TFunc>(this MethodInfo method, object? target = null) where TFunc : class
{
if (method == null) return default;
@@ -569,10 +569,14 @@ public static class Reflect
var func = DelegateCache<TFunc>.Cache.GetOrAdd(
key,
_ => (TFunc)(object)(
_ =>
{
return (
target == null
? Delegate.CreateDelegate(typeof(TFunc), method, true)
: Delegate.CreateDelegate(typeof(TFunc), target, method, true)));
? Delegate.CreateDelegate(typeof(TFunc), method, true) as TFunc
: Delegate.CreateDelegate(typeof(TFunc), target, method, true) as TFunc
);
});
return func;
}

View File

@@ -1,7 +1,6 @@
using System.Reflection;
using ThingsGateway.NewLife.Log;
using ThingsGateway.NewLife.Reflection;
namespace ThingsGateway.NewLife.Threading;
@@ -391,11 +390,7 @@ public class TimerX : ITimer, ITimerx, IDisposable
// 释放非托管资源
Scheduler?.Remove(this, disposing ? "Dispose" : "GC");
DelegateCache<TimerCallback>.Cache.Clear();
#if NET6_0_OR_GREATER
DelegateCache<Func<Object?, ValueTask>>.Cache.Clear();
#endif
DelegateCache<Func<Object?, Task>>.Cache.Clear();
}

View File

@@ -13,7 +13,7 @@ namespace ThingsGateway.SqlSugar
/// <summary>
/// 用于同步操作的锁对象
/// </summary>
internal static object LockObject = new object();
internal static readonly object LockObject = new object();
/// <summary>
/// SqlSugar提供者实例
/// </summary>

View File

@@ -31,61 +31,6 @@ namespace ThingsGateway.SqlSugar
/// 数据记录器
/// </summary>
private IDataRecord DataRecord;
/// <summary>
/// IsDBNull方法
/// </summary>
private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod(nameof(IDataRecord.IsDBNull), new Type[] { typeof(int) });
/// <summary>
/// GetBoolean方法
/// </summary>
private static readonly MethodInfo getBoolean = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetBoolean), new Type[] { typeof(int) });
/// <summary>
/// GetByte方法
/// </summary>
private static readonly MethodInfo getByte = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetByte), new Type[] { typeof(int) });
/// <summary>
/// GetDateTime方法
/// </summary>
private static readonly MethodInfo getDateTime = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDateTime), new Type[] { typeof(int) });
private static readonly MethodInfo getDecimal = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDecimal), new[] { typeof(int) });
private static readonly MethodInfo getDouble = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDouble), new[] { typeof(int) });
private static readonly MethodInfo getFloat = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetFloat), new[] { typeof(int) });
private static readonly MethodInfo getGuid = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetGuid), new[] { typeof(int) });
private static readonly MethodInfo getInt16 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt16), new[] { typeof(int) });
private static readonly MethodInfo getInt32 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt32), new[] { typeof(int) });
private static readonly MethodInfo getInt64 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt64), new[] { typeof(int) });
private static readonly MethodInfo getString = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetString), new[] { typeof(int) });
private static readonly MethodInfo getdatetimeoffset = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetDateTimeOffset));
private static readonly MethodInfo getdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetDateTimeOffsetDate));
private static readonly MethodInfo getStringGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetStringGuid));
private static readonly MethodInfo getXelement = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetXelement));
private static readonly MethodInfo getConvertStringGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertStringGuid));
private static readonly MethodInfo getEnum = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetEnum));
private static readonly MethodInfo getConvertString = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertString));
private static readonly MethodInfo getConvertFloat = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertFloat));
private static readonly MethodInfo getConvertBoolean = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertBoolean));
private static readonly MethodInfo getConvertByte = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertByte));
private static readonly MethodInfo getConvertChar = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertChar));
private static readonly MethodInfo getConvertDateTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDateTime));
private static readonly MethodInfo getConvertTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertTime));
private static readonly MethodInfo getTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetTime));
private static readonly MethodInfo getConvertDecimal = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDecimal));
private static readonly MethodInfo getConvertDouble = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDouble));
private static readonly MethodInfo getConvertDoubleToFloat = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDoubleToFloat));
private static readonly MethodInfo getConvertGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertGuid));
private static readonly MethodInfo getConvertInt16 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt16));
private static readonly MethodInfo getConvertInt32 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt32));
private static readonly MethodInfo getConvertInt64 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt64));
private static readonly MethodInfo getConvertEnum_Null = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertEnum_Null));
private static readonly MethodInfo getConvertdatetimeoffset = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertdatetimeoffset));
private static readonly MethodInfo getConvertdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertdatetimeoffsetDate));
private static readonly MethodInfo getOtherNull = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetOtherNull));
private static readonly MethodInfo getOther = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetOther));
private static readonly MethodInfo getJson = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetJson));
private static readonly MethodInfo getArray = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetArray));
private static readonly MethodInfo getEntity = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetEntity), new[] { typeof(SqlSugarProvider) });
private static readonly MethodInfo getMyIntNull = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetMyIntNull));
private static readonly MethodInfo getMyInt = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetMyInt));
/// <summary>
/// 加载委托
@@ -219,7 +164,7 @@ namespace ThingsGateway.SqlSugar
Label endIfLabel = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
var method = (columnInfo.SqlParameterDbType as Type).GetMethod(nameof(ISugarDataConverter.QueryConverter));
@@ -253,12 +198,12 @@ namespace ThingsGateway.SqlSugar
if (columnInfo.IsJson)
{
MethodInfo jsonMethod = getJson.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType);
MethodInfo jsonMethod = IDataReaderEntityBuilderHelpers.getJson.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType);
int i = DataRecord.GetOrdinal(fieldName);
Label endIfLabel = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
@@ -278,12 +223,12 @@ namespace ThingsGateway.SqlSugar
}
if (columnInfo.IsArray)
{
MethodInfo arrayMehtod = getArray.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType);
MethodInfo arrayMehtod = IDataReaderEntityBuilderHelpers.getArray.MakeGenericMethod(columnInfo.PropertyInfo.PropertyType);
int i = DataRecord.GetOrdinal(fieldName);
Label endIfLabel = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
@@ -298,7 +243,7 @@ namespace ThingsGateway.SqlSugar
Label endIfLabel = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
@@ -332,7 +277,7 @@ namespace ThingsGateway.SqlSugar
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Callvirt, IDataReaderEntityBuilderHelpers.isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
@@ -378,7 +323,7 @@ namespace ThingsGateway.SqlSugar
{
if (provider.IsNoSql)
{
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
if (method.IsVirtual)
generator.Emit(OpCodes.Callvirt, method);
else
@@ -393,39 +338,39 @@ namespace ThingsGateway.SqlSugar
{
if (bindPropertyType.IsEnum())
{
method = isNullableType ? getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : getEnum.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getEnum.MakeGenericMethod(bindPropertyType);
}
else if (bindPropertyType == UtilConstants.IntType)
{
method = isNullableType ? getConvertInt32 : getInt32;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt32 : IDataReaderEntityBuilderHelpers.getInt32;
}
else if (bindPropertyType == UtilConstants.DateTimeOffsetType && SugarCompatible.IsFramework)
{
method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertdatetimeoffset : IDataReaderEntityBuilderHelpers.getdatetimeoffset;
}
else if (bindPropertyType == UtilConstants.ByteType)
{
method = isNullableType ? getConvertByte : getByte;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertByte : IDataReaderEntityBuilderHelpers.getByte;
}
else if (bindPropertyType == UtilConstants.StringType && dbTypeName.EqualCase("timestamp"))
{
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
}
else if (dbTypeName.EqualCase("STRING"))
{
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
}
else if (bindPropertyType == UtilConstants.StringType && validPropertyName == "int")
{
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
}
else if (bindPropertyType == UtilConstants.StringType)
{
method = getString;
method = IDataReaderEntityBuilderHelpers.getString;
}
else
{
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
}
if (method.IsVirtual)
generator.Emit(OpCodes.Callvirt, method);
@@ -445,13 +390,13 @@ namespace ThingsGateway.SqlSugar
case CSharpDataType.@int:
CheckType(bind.IntThrow, bindPropertyTypeName, validPropertyName, propertyName);
if (bindPropertyTypeName.IsContainsIn("int", "int32"))
method = isNullableType ? getConvertInt32 : getInt32;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt32 : IDataReaderEntityBuilderHelpers.getInt32;
if (bindPropertyTypeName.IsContainsIn("int64"))
method = null;
if (bindPropertyTypeName.IsContainsIn("byte"))
method = isNullableType ? getConvertByte : getByte;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertByte : IDataReaderEntityBuilderHelpers.getByte;
if (bindPropertyTypeName.IsContainsIn("int16"))
method = isNullableType ? getConvertInt16 : getInt16;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt16 : IDataReaderEntityBuilderHelpers.getInt16;
if (bindPropertyTypeName == "uint32" && this.Context.CurrentConnectionConfig.DbType.IsIn(DbType.MySql, DbType.MySqlConnector))
method = null;
if (bindPropertyTypeName == "int16")
@@ -459,21 +404,21 @@ namespace ThingsGateway.SqlSugar
break;
case CSharpDataType.@bool:
if (bindPropertyTypeName == "bool" || bindPropertyTypeName == "boolean")
method = isNullableType ? getConvertBoolean : getBoolean;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertBoolean : IDataReaderEntityBuilderHelpers.getBoolean;
break;
case CSharpDataType.@string:
if (this.Context.CurrentConnectionConfig.DbType != DbType.Oracle)
{
CheckType(bind.StringThrow, bindPropertyTypeName, validPropertyName, propertyName);
}
method = getString;
method = IDataReaderEntityBuilderHelpers.getString;
if (bindPropertyTypeName == "guid")
{
method = isNullableType ? getConvertStringGuid : getStringGuid;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertStringGuid : IDataReaderEntityBuilderHelpers.getStringGuid;
}
else if (bindPropertyTypeName == "xelement")
{
method = getXelement;
method = IDataReaderEntityBuilderHelpers.getXelement;
}
else if (dbTypeName == "CHAR" && DataRecord.GetDataTypeName(ordinal) == "CHAR(36)")
{
@@ -487,35 +432,35 @@ namespace ThingsGateway.SqlSugar
case CSharpDataType.DateTime:
CheckType(bind.DateThrow, bindPropertyTypeName, validPropertyName, propertyName);
if (bindPropertyTypeName == "datetime")
method = isNullableType ? getConvertDateTime : getDateTime;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertDateTime : IDataReaderEntityBuilderHelpers.getDateTime;
if (bindPropertyTypeName == "datetime" && dbTypeName.Equals("time", StringComparison.CurrentCultureIgnoreCase))
method = isNullableType ? getConvertTime : getTime;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertTime : IDataReaderEntityBuilderHelpers.getTime;
if (bindPropertyTypeName == "datetimeoffset")
method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertdatetimeoffset : IDataReaderEntityBuilderHelpers.getdatetimeoffset;
break;
case CSharpDataType.@decimal:
CheckType(bind.DecimalThrow, bindPropertyTypeName, validPropertyName, propertyName);
if (bindPropertyTypeName == "decimal")
method = isNullableType ? getConvertDecimal : getDecimal;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertDecimal : IDataReaderEntityBuilderHelpers.getDecimal;
break;
case CSharpDataType.@float:
case CSharpDataType.@double:
CheckType(bind.DoubleThrow, bindPropertyTypeName, validPropertyName, propertyName);
if (bindPropertyTypeName.IsIn("double", "single") && dbTypeName != "real")
method = isNullableType ? getConvertDouble : getDouble;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertDouble : IDataReaderEntityBuilderHelpers.getDouble;
else
method = isNullableType ? getConvertFloat : getFloat;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertFloat : IDataReaderEntityBuilderHelpers.getFloat;
if (dbTypeName.Equals("float", StringComparison.CurrentCultureIgnoreCase) && isNullableType && bindPropertyTypeName.Equals("single", StringComparison.CurrentCultureIgnoreCase))
{
method = getConvertDoubleToFloat;
method = IDataReaderEntityBuilderHelpers.getConvertDoubleToFloat;
}
if (bindPropertyType == UtilConstants.DecType)
{
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
}
if (bindPropertyType == UtilConstants.IntType)
{
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
}
if (bindPropertyTypeName == "string")
{
@@ -525,45 +470,45 @@ namespace ThingsGateway.SqlSugar
case CSharpDataType.Guid:
CheckType(bind.GuidThrow, bindPropertyTypeName, validPropertyName, propertyName);
if (bindPropertyTypeName == "guid")
method = isNullableType ? getConvertGuid : getGuid;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertGuid : IDataReaderEntityBuilderHelpers.getGuid;
break;
case CSharpDataType.@byte:
if (bindPropertyTypeName == "byte")
method = isNullableType ? getConvertByte : getByte;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertByte : IDataReaderEntityBuilderHelpers.getByte;
break;
case CSharpDataType.@enum:
method = isNullableType ? getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : getEnum.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertEnum_Null.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getEnum.MakeGenericMethod(bindPropertyType);
break;
case CSharpDataType.@short:
CheckType(bind.ShortThrow, bindPropertyTypeName, validPropertyName, propertyName);
if (bindPropertyTypeName == "int16" || bindPropertyTypeName == "short")
method = isNullableType ? getConvertInt16 : getInt16;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt16 : IDataReaderEntityBuilderHelpers.getInt16;
break;
case CSharpDataType.@long:
if (bindPropertyTypeName == "int64" || bindPropertyTypeName == "long")
method = isNullableType ? getConvertInt64 : getInt64;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertInt64 : IDataReaderEntityBuilderHelpers.getInt64;
break;
case CSharpDataType.DateTimeOffset:
method = isNullableType ? getConvertdatetimeoffset : getdatetimeoffset;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertdatetimeoffset : IDataReaderEntityBuilderHelpers.getdatetimeoffset;
if (bindPropertyTypeName == "datetime")
method = isNullableType ? getConvertdatetimeoffsetDate : getdatetimeoffsetDate;
method = isNullableType ? IDataReaderEntityBuilderHelpers.getConvertdatetimeoffsetDate : IDataReaderEntityBuilderHelpers.getdatetimeoffsetDate;
break;
case CSharpDataType.Single:
break;
default:
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
break;
}
if (method == null && bindPropertyType == UtilConstants.StringType)
{
method = getConvertString;
method = IDataReaderEntityBuilderHelpers.getConvertString;
}
if (bindPropertyType == UtilConstants.ObjType)
{
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
}
if (method == null)
method = isNullableType ? getOtherNull.MakeGenericMethod(bindPropertyType) : getOther.MakeGenericMethod(bindPropertyType);
method = isNullableType ? IDataReaderEntityBuilderHelpers.getOtherNull.MakeGenericMethod(bindPropertyType) : IDataReaderEntityBuilderHelpers.getOther.MakeGenericMethod(bindPropertyType);
if (method.IsVirtual)
generator.Emit(OpCodes.Callvirt, method);

View File

@@ -0,0 +1,65 @@
using System.Data;
using System.Reflection;
namespace ThingsGateway.SqlSugar
{
internal static class IDataReaderEntityBuilderHelpers
{
/// <summary>
/// IsDBNull方法
/// </summary>
internal static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod(nameof(IDataRecord.IsDBNull), new Type[] { typeof(int) });
/// <summary>
/// GetBoolean方法
/// </summary>
internal static readonly MethodInfo getBoolean = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetBoolean), new Type[] { typeof(int) });
/// <summary>
/// GetByte方法
/// </summary>
internal static readonly MethodInfo getByte = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetByte), new Type[] { typeof(int) });
/// <summary>
/// GetDateTime方法
/// </summary>
internal static readonly MethodInfo getDateTime = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDateTime), new Type[] { typeof(int) });
internal static readonly MethodInfo getDecimal = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDecimal), new[] { typeof(int) });
internal static readonly MethodInfo getDouble = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetDouble), new[] { typeof(int) });
internal static readonly MethodInfo getFloat = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetFloat), new[] { typeof(int) });
internal static readonly MethodInfo getGuid = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetGuid), new[] { typeof(int) });
internal static readonly MethodInfo getInt16 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt16), new[] { typeof(int) });
internal static readonly MethodInfo getInt32 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt32), new[] { typeof(int) });
internal static readonly MethodInfo getInt64 = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetInt64), new[] { typeof(int) });
internal static readonly MethodInfo getString = typeof(IDataRecord).GetMethod(nameof(IDataRecord.GetString), new[] { typeof(int) });
internal static readonly MethodInfo getdatetimeoffset = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetDateTimeOffset));
internal static readonly MethodInfo getdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetDateTimeOffsetDate));
internal static readonly MethodInfo getStringGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetStringGuid));
internal static readonly MethodInfo getXelement = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetXelement));
internal static readonly MethodInfo getConvertStringGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertStringGuid));
internal static readonly MethodInfo getEnum = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetEnum));
internal static readonly MethodInfo getConvertString = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertString));
internal static readonly MethodInfo getConvertFloat = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertFloat));
internal static readonly MethodInfo getConvertBoolean = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertBoolean));
internal static readonly MethodInfo getConvertByte = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertByte));
internal static readonly MethodInfo getConvertChar = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertChar));
internal static readonly MethodInfo getConvertDateTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDateTime));
internal static readonly MethodInfo getConvertTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertTime));
internal static readonly MethodInfo getTime = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetTime));
internal static readonly MethodInfo getConvertDecimal = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDecimal));
internal static readonly MethodInfo getConvertDouble = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDouble));
internal static readonly MethodInfo getConvertDoubleToFloat = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertDoubleToFloat));
internal static readonly MethodInfo getConvertGuid = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertGuid));
internal static readonly MethodInfo getConvertInt16 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt16));
internal static readonly MethodInfo getConvertInt32 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt32));
internal static readonly MethodInfo getConvertInt64 = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertInt64));
internal static readonly MethodInfo getConvertEnum_Null = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertEnum_Null));
internal static readonly MethodInfo getConvertdatetimeoffset = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertdatetimeoffset));
internal static readonly MethodInfo getConvertdatetimeoffsetDate = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetConvertdatetimeoffsetDate));
internal static readonly MethodInfo getOtherNull = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetOtherNull));
internal static readonly MethodInfo getOther = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetOther));
internal static readonly MethodInfo getJson = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetJson));
internal static readonly MethodInfo getArray = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetArray));
internal static readonly MethodInfo getEntity = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetEntity), new[] { typeof(SqlSugarProvider) });
internal static readonly MethodInfo getMyIntNull = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetMyIntNull));
internal static readonly MethodInfo getMyInt = typeof(IDataRecordExtensions).GetMethod(nameof(IDataRecordExtensions.GetMyInt));
}
}

View File

@@ -11,7 +11,7 @@ namespace ThingsGateway.SqlSugar
/// <summary>
/// 类模板
/// </summary>
public static string ClassTemplate = "{using}\r\n" +
public const string ClassTemplate = "{using}\r\n" +
"namespace {Namespace}\r\n" +
"{\r\n" +
"{ClassDescription}{SugarTable}\r\n" +
@@ -26,7 +26,7 @@ namespace ThingsGateway.SqlSugar
/// <summary>
/// 类描述模板
/// </summary>
public static string ClassDescriptionTemplate =
public const string ClassDescriptionTemplate =
ClassSpace + "///<summary>\r\n" +
ClassSpace + "///{ClassDescription}" +
ClassSpace + "///</summary>";
@@ -34,13 +34,13 @@ namespace ThingsGateway.SqlSugar
/// <summary>
/// 属性模板
/// </summary>
public static string PropertyTemplate = PropertySpace + "{SugarColumn}\r\n" +
public const string PropertyTemplate = PropertySpace + "{SugarColumn}\r\n" +
PropertySpace + "public {PropertyType} {PropertyName} {get;set;}\r\n";
/// <summary>
/// 属性描述模板
/// </summary>
public static string PropertyDescriptionTemplate =
public const string PropertyDescriptionTemplate =
PropertySpace + "/// <summary>\r\n" +
PropertySpace + "/// Desc:{PropertyDescription}\r\n" +
PropertySpace + "/// Default:{DefaultValue}\r\n" +
@@ -50,12 +50,12 @@ namespace ThingsGateway.SqlSugar
/// <summary>
/// 构造函数模板
/// </summary>
public static string ConstructorTemplate = PropertySpace + " this.{PropertyName} ={DefaultValue};\r\n";
public const string ConstructorTemplate = PropertySpace + " this.{PropertyName} ={DefaultValue};\r\n";
/// <summary>
/// 命名空间模板
/// </summary>
public static string UsingTemplate = "using System;\r\n" +
public const string UsingTemplate = "using System;\r\n" +
"using System.Linq;\r\n" +
"using System.Text;" + "\r\n";
#endregion

View File

@@ -19,7 +19,7 @@ namespace ThingsGateway.SqlSugar
/// <summary>
/// 默认Razor类模板
/// </summary>
public static string DefaultRazorClassTemplate =
public const string DefaultRazorClassTemplate =
@"using System;
using System.Linq;
using System.Text;

View File

@@ -8,7 +8,7 @@ namespace ThingsGateway.SqlSugar
SqlSugarProvider Context { get; set; }
internal ISqlBuilder Builder;
List<SugarParameter> Parameters;
IEnumerable<StorageableInfo<T>> allDatas;
StorageableInfo<T>[] allDatas;
List<T> dbDataList = new List<T>();
List<KeyValuePair<StorageType, Func<StorageableInfo<T>, bool>, string>> whereFuncs = new List<KeyValuePair<StorageType, Func<StorageableInfo<T>, bool>, string>>();
Expression<Func<T, object>> whereExpression;
@@ -24,7 +24,7 @@ namespace ThingsGateway.SqlSugar
this.allDatas = datas.Select(it => new StorageableInfo<T>()
{
Item = it
});
}).ToArray();
}
Expression<Func<T, bool>> queryableWhereExp;
@@ -209,7 +209,7 @@ namespace ThingsGateway.SqlSugar
{
return this.Saveable().ToStorage();
}
if (!this.allDatas.Any())
if (this.allDatas.Length == 0)
return new StorageableResult<T>()
{
AsDeleteable = this.Context.Deleteable<T>().AS(asname).Where(it => false),
@@ -298,7 +298,7 @@ namespace ThingsGateway.SqlSugar
{
return this.Saveable().GetStorageableResult();
}
if (!this.allDatas.Any())
if (this.allDatas.Length == 0)
return new StorageableResult<T>()
{
//AsDeleteable = this.Context.Deleteable<T>().AS(asname).Where(it => false),
@@ -380,7 +380,7 @@ namespace ThingsGateway.SqlSugar
{
return await Saveable().ToStorageAsync().ConfigureAwait(false);
}
if (!this.allDatas.Any())
if (this.allDatas.Length == 0)
return new StorageableResult<T>()
{
AsDeleteable = this.Context.Deleteable<T>().AS(asname).Where(it => false),

View File

@@ -6,7 +6,7 @@ namespace ThingsGateway.SqlSugar
{
internal SqlSugarProvider Context { get; set; }
internal MethodInfo MethodInfo { get; set; }
internal object objectValue { get; set; }
internal object ObjValue { get; set; }
public int ExecuteCommand()
{
if (Context == null) return 0;
@@ -20,16 +20,14 @@ namespace ThingsGateway.SqlSugar
{
get
{
var type = "AsInsertable";
return GetAs(type);
return GetAs(nameof(AsInsertable));
}
}
public StorageableAsMethodInfo AsUpdateable
{
get
{
var type = "AsUpdateable";
return GetAs(type);
return GetAs(nameof(AsUpdateable));
}
}
@@ -48,9 +46,9 @@ namespace ThingsGateway.SqlSugar
private MethodInfo GetSaveMethod(ref object callValue)
{
if (objectValue == null)
if (ObjValue == null)
return null;
callValue = MethodInfo.Invoke(Context, new object[] { objectValue });
callValue = MethodInfo.Invoke(Context, new object[] { ObjValue });
return callValue.GetType().GetMyMethod(nameof(ExecuteCommand), 0);
}

View File

@@ -1113,7 +1113,7 @@ namespace ThingsGateway.SqlSugar
{
Context = this.Context,
MethodInfo = method,
objectValue = newList
ObjValue = newList
};
return result;
}
@@ -1126,7 +1126,7 @@ namespace ThingsGateway.SqlSugar
{
Context = this.Context,
MethodInfo = method,
objectValue = singleEntityObjectOrList
ObjValue = singleEntityObjectOrList
};
return result;
}

View File

@@ -2,7 +2,7 @@
{
public sealed class SnowFlakeSingle
{
private static object LockObject = new object();
private static readonly object LockObject = new object();
private static DistributedSystem.Snowflake.IdWorker worker;
public static int WorkId = 1;
public static int DatacenterId = 1;

View File

@@ -99,10 +99,7 @@ namespace ThingsGateway.SqlSugar
public void RemoveAllCache()
{
foreach (var key in GetAllKey())
{
this.Remove(key);
}
this.InstanceCache.Clear();
}
public IEnumerable<string> GetAllKey()

View File

@@ -28,6 +28,11 @@ namespace ThingsGateway.SqlSugar
this.Context.Close();
}
}
public Task<string> CreateTempAsync<T>(Dictionary<string, (Type, List<DataInfos>)> list) where T : class, new()
{
IsUpdate = true;
return Task.FromResult(string.Empty);
}
public Task CreateTempAsync<T>(DataTable dt) where T : class, new()
{
@@ -171,7 +176,7 @@ namespace ThingsGateway.SqlSugar
else
{
var count = list.FirstOrDefault().Value.Item2?.Count;
if(count==1)
if (count == 1)
{
var row = list.GetRows(0).ToDictionary(a => a.ColumnName, a => a.Value);
cmd.CommandText = this.Context.InsertableT(row).AS(tableName).ToSqlString().Replace(";SELECT LAST_INSERT_ROWID();", "");

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.27" />
<PackageReference Include="Oscar.Data.SqlClient" Version="4.2.28" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.2" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />

View File

@@ -1,9 +1,9 @@
<Project>
<PropertyGroup>
<PluginVersion>10.11.92</PluginVersion>
<ProPluginVersion>10.11.92</ProPluginVersion>
<DefaultVersion>10.11.92</DefaultVersion>
<PluginVersion>10.11.101</PluginVersion>
<ProPluginVersion>10.11.101</ProPluginVersion>
<DefaultVersion>10.11.101</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.87</ManagementProPluginVersion>
<ManagementPluginVersion>10.11.87</ManagementPluginVersion>
<TSVersion>4.0.0-beta.96</TSVersion>
<TSVersion>4.0.0-beta.120</TSVersion>
</PropertyGroup>

View File

@@ -73,7 +73,7 @@ public interface IChannelOptions
bool RtsEnable { get; set; }
bool StreamAsync { get; set; }
Handshake Handshake { get; set; }
#endregion
/// <summary>

View File

@@ -553,9 +553,9 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
return waitData.CompletedData;
var reusableTimeout = _reusableTimeouts.Get();
var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, Channel.ClosedToken);
try
{
var cts = reusableTimeout.GetTokenSource(timeout, cancellationToken, Channel.ClosedToken);
await waitData.WaitAsync(cts.Token).ConfigureAwait(false);
@@ -583,7 +583,9 @@ public abstract class DeviceBase : AsyncAndSyncDisposableObject, IDevice
else
{
var operResult = waitData.Check(reusableTimeout.TimeoutStatus);
return new MessageBase(operResult) { ErrorMessage = $"{operResult.ErrorMessage}, sign: {sign}" };
waitData.CompletedData.ErrorMessage = $"{operResult.ErrorMessage}, sign: {sign}";
return waitData.CompletedData;
//return new MessageBase(operResult) { ErrorMessage = $"{operResult.ErrorMessage}, sign: {sign}" };
}
}
catch (Exception ex)

View File

@@ -100,7 +100,7 @@ public static class LoggerExtensions
/// </summary>
public static string GetDeviceLogBasePath()
{
return PathExtensions.CombinePathWithOs("Logs","DeviceLog");
return PathExtensions.CombinePathWithOs("Logs", "DeviceLog");
}
/// <summary>

View File

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

View File

@@ -113,7 +113,7 @@ public partial class ManagementTask : AsyncDisposableObject
.ConfigurePlugins(a =>
{
a.UseTcpSessionCheckClear();
a.UseDmtpRpc().ConfigureDefaultSerializationSelector(b =>
a.UseDmtpRpc(a => a.ConfigureDefaultSerializationSelector(b =>
{
b.UseSystemTextJson(json =>
{
@@ -123,7 +123,7 @@ public partial class ManagementTask : AsyncDisposableObject
json.Converters.Add(new JObjectSystemTextJsonConverter());
json.Converters.Add(new JArraySystemTextJsonConverter());
});
});
}));
a.UseDmtpFileTransfer();//必须添加文件传输插件
@@ -176,7 +176,7 @@ public partial class ManagementTask : AsyncDisposableObject
.ConfigurePlugins(a =>
{
a.UseTcpSessionCheckClear();
a.UseDmtpRpc().ConfigureDefaultSerializationSelector(b =>
a.UseDmtpRpc(a => a.ConfigureDefaultSerializationSelector(b =>
{
b.UseSystemTextJson(json =>
{
@@ -186,7 +186,7 @@ public partial class ManagementTask : AsyncDisposableObject
json.Converters.Add(new JObjectSystemTextJsonConverter());
json.Converters.Add(new JArraySystemTextJsonConverter());
});
});
}));
a.UseDmtpFileTransfer();//必须添加文件传输插件

View File

@@ -346,7 +346,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
{
a.UseTcpSessionCheckClear();
a.UseDmtpRpc().ConfigureDefaultSerializationSelector(b =>
a.UseDmtpRpc(a => a.ConfigureDefaultSerializationSelector(b =>
{
b.UseSystemTextJson(json =>
{
@@ -356,7 +356,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
json.Converters.Add(new JObjectSystemTextJsonConverter());
json.Converters.Add(new JArraySystemTextJsonConverter());
});
});
}));
a.UseDmtpHeartbeat()//使用Dmtp心跳
.SetTick(TimeSpan.FromMilliseconds(redundancy.HeartbeatInterval))
.SetMaxFailCount(redundancy.MaxErrorCount);
@@ -390,7 +390,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
.ConfigurePlugins(a =>
{
a.UseTcpSessionCheckClear();
a.UseDmtpRpc().ConfigureDefaultSerializationSelector(b =>
a.UseDmtpRpc(a => a.ConfigureDefaultSerializationSelector(b =>
{
b.UseSystemTextJson(json =>
{
@@ -400,7 +400,7 @@ internal sealed class RedundancyTask : IRpcDriver, IAsyncDisposable
json.Converters.Add(new JObjectSystemTextJsonConverter());
json.Converters.Add(new JArraySystemTextJsonConverter());
});
});
}));
a.UseDmtpHeartbeat()//使用Dmtp心跳
.SetTick(TimeSpan.FromMilliseconds(redundancy.HeartbeatInterval))
.SetMaxFailCount(redundancy.MaxErrorCount);

View File

@@ -711,7 +711,7 @@ internal sealed class PluginService : IPluginService
{
using var fs = new FileStream(item, FileMode.Open);
//var cachePath = Path.GetDirectoryName(item).CombinePathWithOs($"Cache{cacheId}").CombinePath(Path.GetFileName(item));
if (item == path)
if (Path.GetFullPath(item) == Path.GetFullPath(path))
{
assembly = assemblyLoadContext.LoadFromStream(fs); //加载主程序集,并获取

View File

@@ -104,7 +104,14 @@ public static class RuleHelpers
{
if (propertyInfos.TryGetValue(item.Key, out var propertyInfo))
{
propertyInfo.SetValue(nodeModel, item.Value?.ToObject(propertyInfo.PropertyType));
try
{
propertyInfo.SetValue(nodeModel, item.Value?.ToObject(propertyInfo.PropertyType));
}
catch (Exception)
{
propertyInfo.SetValue(nodeModel, null);
}
}
}
}

View File

@@ -9,7 +9,7 @@
<ItemGroup>
<PackageReference Include="Portable.BouncyCastle" Version="1.9.0" />
<PackageReference Include="Riok.Mapperly" Version="4.2.1" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Rougamo.Fody" Version="5.0.1" />
<PackageReference Include="Rougamo.Fody" Version="5.0.2" />
<PackageReference Include="TouchSocket.Dmtp" Version="$(TSVersion)" />
<!--<PackageReference Include="TouchSocket.WebApi.Swagger" Version="$(TSVersion)" />-->
<PackageReference Include="TouchSocket.WebApi" Version="$(TSVersion)" />

View File

@@ -0,0 +1,19 @@
@namespace ThingsGateway.Gateway.Razor
@using System.Text.Json.Nodes
@using Microsoft.Extensions.Hosting
@using ThingsGateway.Admin.Application
@using ThingsGateway.Admin.Razor
@using ThingsGateway.Gateway.Application
@inherits ComponentDefault
@foreach (var col in RowContent.Columns)
{
<td class="@GetFixedCellClassString(col)" style="@GetFixedCellStyleString(col)">
<DynamicElement TagName="div" TriggerClick="@false"
StopPropagation="false"
class="@GetCellClassString(col, false, false)">
@GetValue(col, RowContent.Row)
</DynamicElement>
</td>
}

View File

@@ -0,0 +1,279 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
using System.Collections.Concurrent;
using ThingsGateway.NewLife.Threading;
using TouchSocket.Core;
namespace ThingsGateway.Gateway.Razor;
public partial class VariableRow : IDisposable
{
[Parameter]
public TableRowContext<VariableRuntime>? RowContent { get; set; }
private bool Disposed;
public void Dispose()
{
Disposed = true;
timer?.SafeDispose();
GC.SuppressFinalize(this);
}
TimerX? timer;
protected override void OnAfterRender(bool firstRender)
{
if (firstRender)
{
timer = new TimerX(Refresh, null, 1000, 1000, "VariableRow");
}
base.OnAfterRender(firstRender);
}
private Task Refresh(object? state)
{
if (!Disposed)
return InvokeAsync(StateHasChanged);
else
return Task.CompletedTask;
}
protected override void OnParametersSet()
{
FixedCellClassStringCache?.Clear();
CellClassStringCache?.Clear();
base.OnParametersSet();
}
/// <summary>
/// 获得 指定单元格数据方法
/// </summary>
/// <param name="col"></param>
/// <param name="item"></param>
/// <returns></returns>
protected RenderFragment GetValue(ITableColumn col, VariableRuntime item) => builder =>
{
if (col.Template != null)
{
builder.AddContent(0, col.Template(item));
}
else if (col.ComponentType == typeof(ColorPicker))
{
// 自动化处理 ColorPicker 组件
builder.AddContent(10, col.RenderColor(item));
}
else
{
builder.AddContent(20, col.RenderValue(item));
}
};
// internal static string? GetDoubleClickCellClassString(bool trigger) => CssBuilder.Default()
//.AddClass("is-dbcell", trigger)
//.Build();
/// <summary>
/// 获得指定列头固定列样式
/// </summary>
/// <param name="col"></param>
/// <param name="margin"></param>
/// <returns></returns>
protected string? GetFixedCellStyleString(ITableColumn col, int margin = 0)
{
string? ret = null;
if (col.Fixed)
{
ret = IsTail(col) ? GetRightStyle(col, margin) : GetLeftStyle(col);
}
return ret;
}
private string? GetLeftStyle(ITableColumn col)
{
var columns = RowContent.Columns.ToList();
var defaultWidth = 200;
var width = 0;
var start = 0;
var index = columns.IndexOf(col);
//if (GetFixedDetailRowHeaderColumn)
//{
// width += DetailColumnWidth;
//}
//if (GetFixedMultipleSelectColumn)
//{
// width += MultiColumnWidth;
//}
if (GetFixedLineNoColumn)
{
width += LineNoColumnWidth;
}
while (index > start)
{
var column = columns[start++];
width += column.Width ?? defaultWidth;
}
return $"left: {width}px;";
}
private bool GetFixedLineNoColumn = false;
private string? GetRightStyle(ITableColumn col, int margin)
{
var columns = RowContent.Columns.ToList();
var defaultWidth = 200;
var width = 0;
var index = columns.IndexOf(col);
// after
while (index + 1 < columns.Count)
{
var column = columns[index++];
width += column.Width ?? defaultWidth;
}
//if (ShowExtendButtons && FixedExtendButtonsColumn)
{
width += ExtendButtonColumnWidth;
}
// 如果是固定表头时增加滚动条位置
if (IsFixedHeader && (index + 1) == columns.Count)
{
width += margin;
}
return $"right: {width}px;";
}
private bool IsFixedHeader = true;
public int LineNoColumnWidth { get; set; } = 60;
public int ExtendButtonColumnWidth { get; set; } = 220;
private bool IsTail(ITableColumn col)
{
var middle = Math.Floor(RowContent.Columns.Count() * 1.0 / 2);
var index = Columns.IndexOf(col);
return middle < index;
}
private ConcurrentDictionary<ITableColumn, string> CellClassStringCache { get; } = new(ReferenceEqualityComparer.Instance);
/// <summary>
/// 获得 Cell 文字样式
/// </summary>
/// <param name="col"></param>
/// <param name="hasChildren"></param>
/// <param name="inCell"></param>
/// <returns></returns>
protected string? GetCellClassString(ITableColumn col, bool hasChildren, bool inCell)
{
if (CellClassStringCache.TryGetValue(col, out var cached))
{
return cached;
}
else
{
bool trigger = false;
return CellClassStringCache.GetOrAdd(col, col => CssBuilder.Default("table-cell")
.AddClass(col.GetAlign().ToDescriptionString(), col.Align == Alignment.Center || col.Align == Alignment.Right)
.AddClass("is-wrap", col.GetTextWrap())
.AddClass("is-ellips", col.GetTextEllipsis())
.AddClass("is-tips", col.GetShowTips())
.AddClass("is-resizable", AllowResizing)
.AddClass("is-tree", IsTree && hasChildren)
.AddClass("is-incell", inCell)
.AddClass("is-dbcell", trigger)
.AddClass(col.CssClass)
.Build());
}
}
private bool AllowResizing = true;
private bool IsTree = false;
private ConcurrentDictionary<ITableColumn, string> FixedCellClassStringCache { get; } = new(ReferenceEqualityComparer.Instance);
/// <summary>
/// 获得指定列头固定列样式
/// </summary>
/// <param name="col"></param>
/// <returns></returns>
protected string? GetFixedCellClassString(ITableColumn col)
{
if (FixedCellClassStringCache.TryGetValue(col, out var cached))
{
return cached;
}
else
{
return FixedCellClassStringCache.GetOrAdd(col, col => CssBuilder.Default()
.AddClass("fixed", col.Fixed)
.AddClass("fixed-right", col.Fixed && IsTail(col))
.AddClass("fr", IsLastColumn(col))
.AddClass("fl", IsFirstColumn(col))
.Build());
}
}
[Parameter]
public Func<List<ITableColumn>> ColumnsFunc { get; set; }
public List<ITableColumn> Columns => ColumnsFunc();
private ConcurrentDictionary<ITableColumn, bool> LastFixedColumnCache { get; } = new(ReferenceEqualityComparer.Instance);
private bool IsLastColumn(ITableColumn col)
{
if (LastFixedColumnCache.TryGetValue(col, out var cached))
{
return cached;
}
else
{
return LastFixedColumnCache.GetOrAdd(col, col =>
{
var ret = false;
if (col.Fixed && !IsTail(col))
{
var index = Columns.IndexOf(col) + 1;
ret = index < Columns.Count && Columns[index].Fixed == false;
}
return ret;
});
}
}
private ConcurrentDictionary<ITableColumn, bool> FirstFixedColumnCache { get; } = new(ReferenceEqualityComparer.Instance);
private bool IsFirstColumn(ITableColumn col)
{
if (FirstFixedColumnCache.TryGetValue(col, out var cached))
{
return cached;
}
else
{
return FirstFixedColumnCache.GetOrAdd(col, col =>
{
var ret = false;
if (col.Fixed && IsTail(col))
{
// 查找前一列是否固定
var index = Columns.IndexOf(col) - 1;
if (index > 0)
{
ret = !Columns[index].Fixed;
}
}
return ret;
});
}
}
}

View File

@@ -0,0 +1,60 @@
//------------------------------------------------------------------------------
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
// 此代码版权除特别声明外的代码归作者本人Diego所有
// 源代码使用协议遵循本仓库的开源协议及附加协议
// Gitee源代码仓库https://gitee.com/diego2098/ThingsGateway
// Github源代码仓库https://github.com/kimdiego2098/ThingsGateway
// 使用文档https://thingsgateway.cn/
// QQ群605534569
//------------------------------------------------------------------------------
namespace ThingsGateway.Gateway.Razor;
internal static class VariableRowHelpers
{
internal static Alignment GetAlign(this ITableColumn col) => col.Align ?? Alignment.None;
internal static bool GetTextWrap(this ITableColumn col) => col.TextWrap ?? false;
internal static bool GetShowTips(this ITableColumn col) => col.ShowTips ?? false;
internal static RenderFragment RenderColor<TItem>(this ITableColumn col, TItem item) => builder =>
{
var val = GetItemValue(col, item);
var v = val?.ToString() ?? "#000";
var style = $"background-color: {v};";
builder.OpenElement(0, "div");
builder.AddAttribute(1, "class", "is-color");
builder.AddAttribute(2, "style", style);
builder.CloseElement();
};
internal static object? GetItemValue<TItem>(this ITableColumn col, TItem item)
{
var fieldName = col.GetFieldName();
object? ret;
if (item is IDynamicObject dynamicObject)
{
ret = dynamicObject.GetValue(fieldName);
}
else
{
ret = Utility.GetPropertyValue<TItem, object?>(item, fieldName);
if (ret != null)
{
var t = ret.GetType();
if (t.IsEnum)
{
// 如果是枚举这里返回 枚举的描述信息
var itemName = ret.ToString();
if (!string.IsNullOrEmpty(itemName))
{
ret = Utility.GetDisplayName(t, itemName);
}
}
}
}
return ret;
}
internal static bool GetTextEllipsis(this ITableColumn col) => col.TextEllipsis ?? false;
}

View File

@@ -15,13 +15,13 @@
AllowResizing="true"
OnAdd="OnAdd"
IsFixedHeader=true
ShowCardView=false
IsMultipleSelect=true
SearchMode=SearchMode.Top
ShowExtendButtons=true
ShowToolbar="true"
ShowExportButton
IsAutoRefresh
AutoRefreshInterval="2000"
RenderMode="TableRenderMode.Table"
ShowDefaultButtons=true
ShowSearch=false
ExtendButtonColumnWidth=220
@@ -85,6 +85,11 @@
<VariableEditComponent Model=@(context) AutoRestartThread="AutoRestartThread"></VariableEditComponent>
</EditTemplate>
<RowContentTemplate Context="context">
<VariableRow RowContent="@context" ColumnsFunc="ColumnsFunc"></VariableRow>
</RowContentTemplate>
<ExportButtonDropdownTemplate Context="ExportContext">
@if ((AuthorizeButton("导出")))

View File

@@ -15,13 +15,17 @@ using ThingsGateway.Admin.Application;
using ThingsGateway.Admin.Razor;
using ThingsGateway.DB;
using ThingsGateway.Extension.Generic;
using ThingsGateway.NewLife.Extension;
using ThingsGateway.NewLife.Json.Extension;
namespace ThingsGateway.Gateway.Razor;
public partial class VariableRuntimeInfo : IDisposable
{
public List<ITableColumn> ColumnsFunc()
{
return table?.Columns;
}
#if !Management
[Parameter]
public ChannelDeviceTreeItem SelectModel { get; set; }
@@ -55,11 +59,11 @@ public partial class VariableRuntimeInfo : IDisposable
scheduler = new SmartTriggerScheduler(Notify, TimeSpan.FromMilliseconds(1000));
#if !Management
_ = RunTimerAsync();
//timer = new TimerX(RunTimerAsync, null, 1000, 1000) { Async = true };
#endif
base.OnInitialized();
}
//private TimerX timer;
/// <summary>
/// IntFormatter
/// </summary>
@@ -84,6 +88,12 @@ public partial class VariableRuntimeInfo : IDisposable
return Task.CompletedTask;
}
protected override void OnParametersSet()
{
base.OnParametersSet();
scheduler?.Trigger();
}
private async Task Notify(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested) return;
@@ -92,27 +102,23 @@ public partial class VariableRuntimeInfo : IDisposable
await InvokeAsync(table.QueryAsync);
}
private async Task RunTimerAsync()
{
while (!Disposed)
{
try
{
//if (table != null)
// await table.QueryAsync();
//private async Task RunTimerAsync(object? state)
//{
// try
// {
// //if (table != null)
// // await InvokeAsync(() => table.RowElementRefresh());
await InvokeAsync(StateHasChanged);
}
catch (Exception ex)
{
NewLife.Log.XTrace.WriteException(ex);
}
finally
{
await Task.Delay(1000);
}
}
}
// await InvokeAsync(StateHasChanged);
// }
// catch (Exception ex)
// {
// NewLife.Log.XTrace.WriteException(ex);
// }
// finally
// {
// }
//}
#region
@@ -126,7 +132,7 @@ public partial class VariableRuntimeInfo : IDisposable
return data;
#else
var data = Items
.WhereIf(!options.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(options.SearchText))
.WhereIf(!string.IsNullOrWhiteSpace(options.SearchText), a => a.Name.Contains(options.SearchText))
.GetQueryData(options);
_option = options;
return Task.FromResult(data);
@@ -354,7 +360,7 @@ public partial class VariableRuntimeInfo : IDisposable
#if !Management
var models = Items
.WhereIf(!_option.SearchText.IsNullOrWhiteSpace(), a => a.Name.Contains(_option.SearchText)).GetData(_option, out var total).Cast<Variable>().ToList();
.WhereIf(!string.IsNullOrWhiteSpace(_option.SearchText), a => a.Name.Contains(_option.SearchText)).GetData(_option, out var total).Cast<Variable>().ToList();
#else

View File

@@ -516,185 +516,169 @@ public class ModbusSlave : DeviceBase, IModbusAddress
return new OperResult(ex);
}
}
/// <inheritdoc/>
protected override async Task ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last)
protected override Task ChannelReceived(IClientChannel client, ReceivedDataEventArgs e, bool last)
{
var requestInfo = e.RequestInfo;
bool modbusRtu = false;
ModbusRequest modbusRequest = default;
ReadOnlySequence<byte> readOnlySequences = default;
//接收外部报文
if (requestInfo is ModbusRtuSlaveMessage modbusRtuSlaveMessage)
return HandleChannelReceivedAsync(client, e, last);
}
private async Task HandleChannelReceivedAsync(IClientChannel client, ReceivedDataEventArgs e, bool last)
{
if (!TryParseRequest(e.RequestInfo, out var modbusRequest, out var sequences, out var modbusRtu))
return;
if (!MulStation && modbusRequest.Station != Station)
return;
var function = NormalizeFunctionCode(modbusRequest.FunctionCode);
if (function <= 4)
await HandleReadRequestAsync(client, e, modbusRequest, sequences, modbusRtu).ConfigureAwait(false);
else
await HandleWriteRequestAsync(client, e, modbusRequest, sequences, modbusRtu, function).ConfigureAwait(false);
}
private static bool TryParseRequest(object requestInfo, out ModbusRequest modbusRequest, out ReadOnlySequence<byte> sequences, out bool modbusRtu)
{
modbusRequest = default;
sequences = default;
modbusRtu = false;
switch (requestInfo)
{
if (!modbusRtuSlaveMessage.IsSuccess)
{
return;
}
modbusRequest = modbusRtuSlaveMessage.Request;
readOnlySequences = modbusRtuSlaveMessage.Sequences;
modbusRtu = true;
case ModbusRtuSlaveMessage rtuMsg when rtuMsg.IsSuccess:
modbusRequest = rtuMsg.Request;
sequences = rtuMsg.Sequences;
modbusRtu = true;
return true;
case ModbusTcpSlaveMessage tcpMsg when tcpMsg.IsSuccess:
modbusRequest = tcpMsg.Request;
sequences = tcpMsg.Sequences;
modbusRtu = false;
return true;
default:
return false;
}
else if (requestInfo is ModbusTcpSlaveMessage modbusTcpSlaveMessage)
}
private static byte NormalizeFunctionCode(byte funcCode)
=> funcCode > 0x30 ? (byte)(funcCode - 0x30) : funcCode;
private async Task HandleReadRequestAsync(
IClientChannel client,
ReceivedDataEventArgs e,
ModbusRequest modbusRequest,
ReadOnlySequence<byte> sequences,
bool modbusRtu)
{
var data = ModbusRequest(modbusRequest, true);
if (!data.IsSuccess)
{
if (!modbusTcpSlaveMessage.IsSuccess)
{
await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false);
return;
}
ValueByteBlock byteBlock = new(1024);
try
{
WriteReadResponse(modbusRequest, sequences, data.Content, ref byteBlock, modbusRtu);
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
}
catch
{
await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false);
}
finally
{
byteBlock.SafeDispose();
}
}
private async Task HandleWriteRequestAsync(
IClientChannel client,
ReceivedDataEventArgs e,
ModbusRequest modbusRequest,
ReadOnlySequence<byte> sequences,
bool modbusRtu,
byte f)
{
var modbusAddress = new ModbusAddress(modbusRequest);
bool isSuccess;
switch (f)
{
case 5:
case 15:
modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode;
modbusAddress.FunctionCode = 1;
isSuccess = await HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false);
break;
case 6:
case 16:
modbusAddress.WriteFunctionCode = modbusRequest.FunctionCode;
modbusAddress.FunctionCode = 3;
isSuccess = await HandleWriteCoreAsync(modbusAddress, client, modbusRequest).ConfigureAwait(false);
break;
default:
return;
}
modbusRequest = modbusTcpSlaveMessage.Request;
readOnlySequences = modbusTcpSlaveMessage.Sequences;
modbusRtu = false;
}
if (isSuccess)
await WriteSuccess(modbusRtu, client, sequences, e).ConfigureAwait(false);
else
await WriteError(modbusRtu, client, sequences, e).ConfigureAwait(false);
}
private async Task<bool> HandleWriteCoreAsync(ModbusAddress address, IClientChannel client, ModbusRequest modbusRequest)
{
if (WriteData != null)
{
var result = await WriteData(address, ThingsGatewayBitConverter, client).ConfigureAwait(false);
if (!result.IsSuccess) return false;
}
if (IsWriteMemory)
{
var memResult = ModbusRequest(modbusRequest, false);
return memResult.IsSuccess;
}
return true;
}
private static void WriteReadResponse(
ModbusRequest modbusRequest,
ReadOnlySequence<byte> sequences,
ReadOnlyMemory<byte> content,
ref ValueByteBlock byteBlock,
bool modbusRtu)
{
if (modbusRtu)
ByteBlockExtension.Write(ref byteBlock, sequences.Slice(0, 2));
else
ByteBlockExtension.Write(ref byteBlock, sequences.Slice(0, 8));
if (modbusRequest.IsBitFunction)
{
var bitdata = content.Span.ByteToBool().AsSpan().BoolArrayToByte();
var len = (int)Math.Ceiling(modbusRequest.Length / 8.0);
var bitWriteData = bitdata.AsMemory().Slice(0, len);
WriterExtension.WriteValue(ref byteBlock, (byte)bitWriteData.Length);
byteBlock.Write(bitWriteData.Span);
}
else
{
return;
WriterExtension.WriteValue(ref byteBlock, (byte)content.Length);
byteBlock.Write(content.Span);
}
//忽略不同设备地址的报文
if (!MulStation && modbusRequest.Station != Station)
{
return;
}
var f = modbusRequest.FunctionCode > 0x30 ? modbusRequest.FunctionCode - 0x30 : modbusRequest.FunctionCode;
if (f <= 4)
{
var data = ModbusRequest(modbusRequest, true);
if (data.IsSuccess)
{
ValueByteBlock byteBlock = new(1024);
try
{
if (modbusRtu)
{
ByteBlockExtension.Write(ref byteBlock, readOnlySequences.Slice(0, 2));
if (modbusRequest.IsBitFunction)
{
var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte();
ReadOnlyMemory<byte> bitwritedata = bitdata.Length == (int)Math.Ceiling(modbusRequest.Length / 8.0) ? bitdata.AsMemory() : bitdata.AsMemory().Slice(0, (int)Math.Ceiling(modbusRequest.Length / 8.0));
WriterExtension.WriteValue(ref byteBlock, (byte)bitwritedata.Length);
byteBlock.Write(bitwritedata.Span);
}
else
{
WriterExtension.WriteValue(ref byteBlock, (byte)data.Content.Length);
byteBlock.Write(data.Content.Span);
}
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Memory.Span));
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
}
else
{
ByteBlockExtension.Write(ref byteBlock, readOnlySequences.Slice(0, 8));
if (modbusRequest.IsBitFunction)
{
var bitdata = data.Content.Span.ByteToBool().AsSpan().BoolArrayToByte();
ReadOnlyMemory<byte> bitwritedata = bitdata.Length == (int)Math.Ceiling(modbusRequest.Length / 8.0) ? bitdata.AsMemory() : bitdata.AsMemory().Slice(0, (int)Math.Ceiling(modbusRequest.Length / 8.0));
WriterExtension.WriteValue(ref byteBlock, (byte)bitwritedata.Length);
byteBlock.Write(bitwritedata.Span);
}
else
{
WriterExtension.WriteValue(ref byteBlock, (byte)data.Content.Length);
byteBlock.Write(data.Content.Span);
}
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
await ReturnData(client, byteBlock.Memory, e).ConfigureAwait(false);
}
}
catch
{
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
finally
{
byteBlock.SafeDispose();
}
}
else
{
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);//返回错误码
}
}
else//写入
{
if (f == 5 || f == 15)
{
//写入继电器
if (WriteData != null)
{
var modbusAddress = new ModbusAddress(modbusRequest) { WriteFunctionCode = modbusRequest.FunctionCode, FunctionCode = 1 };
// 接收外部写入时,传出变量地址/写入字节组/转换规则/客户端
if ((await WriteData(modbusAddress, ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
{
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
if (IsWriteMemory)
{
var result = ModbusRequest(modbusRequest, false);
if (result.IsSuccess)
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
else
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
else
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
else
{
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
}
else
{
//写入内存区
var result = ModbusRequest(modbusRequest, false);
if (result.IsSuccess)
{
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
else
{
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
}
}
else if (f == 6 || f == 16)
{
//写入寄存器
if (WriteData != null)
{
var modbusAddress = new ModbusAddress(modbusRequest) { WriteFunctionCode = modbusRequest.FunctionCode, FunctionCode = 3 };
if ((await WriteData(modbusAddress, ThingsGatewayBitConverter, client).ConfigureAwait(false)).IsSuccess)
{
if (IsWriteMemory)
{
var result = ModbusRequest(modbusRequest, false);
if (result.IsSuccess)
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
else
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
else
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
else
{
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
}
else
{
var result = ModbusRequest(modbusRequest, false);
if (result.IsSuccess)
{
await WriteSuccess(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
else
{
await WriteError(modbusRtu, client, readOnlySequences, e).ConfigureAwait(false);
}
}
}
}
if (modbusRtu)
byteBlock.Write(CRC16Utils.Crc16Only(byteBlock.Memory.Span));
else
ByteBlockExtension.WriteBackValue(ref byteBlock, (byte)(byteBlock.Length - 6), EndianType.Big, 5);
}
private async Task ReturnData(IClientChannel client, ReadOnlyMemory<byte> sendData, ReceivedDataEventArgs e)

View File

@@ -17,13 +17,13 @@
<AssemblyName>ThingsGateway.Plugin.Kafka</AssemblyName>
<PackageId>$(MSBuildProjectName)</PackageId>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\PluginPro\ThingsGateway.Management.Razor\ThingsGateway.Management.Razor.csproj" >
<ProjectReference Include="..\..\PluginPro\ThingsGateway.Management.Razor\ThingsGateway.Management.Razor.csproj">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</ProjectReference>
<PackageReference Include="Confluent.Kafka" Version="2.11.1" GeneratePathProperty="true">
<PackageReference Include="Confluent.Kafka" Version="2.12.0" GeneratePathProperty="true">
<PrivateAssets>contentFiles;compile;build;buildMultitargeting;buildTransitive;analyzers;</PrivateAssets>
</PackageReference>
</ItemGroup>
@@ -34,6 +34,6 @@
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -11,26 +11,7 @@ public class TestKafkaDynamicModel1 : DynamicModelBase
public TestKafkaDynamicModel1()
{
var name = "测试MqttServer";
if (GlobalData.ReadOnlyDevices.TryGetValue(name, out var kafka1))
{
id = kafka1.Id;
foreach (var item in kafka1.Driver?.IdVariableRuntimes)
{
//变量备注1作为Key(AE报警SourceId)
var data1 = item.Value.GetPropertyValue(id, nameof(BusinessVariableProperty.Data1));
if (!data1.IsNullOrEmpty())
{
variableRuntimes.Add(data1, item.Value);
}
}
}
else
{
throw new Exception($"找不到设备 {name}");
}
}
@@ -38,6 +19,48 @@ public class TestKafkaDynamicModel1 : DynamicModelBase
public override IEnumerable<dynamic> GetList(IEnumerable<object> datas)
{
if (id == 0)
{
var name = "kafka_DA"; Logger?.LogInformation("进来了10000");
if (GlobalData.ReadOnlyDevices.TryGetValue(name, out var kafka1))
{
id = kafka1.Id;
if (kafka1.Driver != null)
{
foreach (var item in kafka1.Driver?.IdVariableRuntimes)
{
//变量备注1作为Key(AE报警SourceId)
var data1 = item.Value.GetPropertyValue(id, nameof(BusinessVariableProperty.Data1));
if (!data1.IsNullOrEmpty())
{
variableRuntimes.Add(data1, item.Value);
}
}
}
else
{
foreach (var item in kafka1.ReadOnlyVariableRuntimes)
{
//变量备注1作为Key(AE报警SourceId)
var data1 = item.Value.GetPropertyValue(id, nameof(BusinessVariableProperty.Data1));
if (!data1.IsNullOrEmpty())
{
variableRuntimes.Add(data1, item.Value);
}
}
}
}
else
{
throw new Exception($"找不到设备 {name}");
}
}
if (datas == null) return null;
var pluginEventDatas = datas.Cast<PluginEventData>();
var opcDatas = pluginEventDatas.Select(
@@ -67,13 +90,14 @@ public class TestKafkaDynamicModel1 : DynamicModelBase
//重连时触发的事件,可以跳过不处理
//if(opcAeEventData.Refresh)
// continue;
Logger?.LogInformation("进来了");
var sourceName = opcAeEventData.SourceID;
if (variableRuntimes.TryGetValue(sourceName, out var variableRuntime))
{
var ack = opcAeEventData.EventType != Opc.Ae.EventType.Condition ? false : ((Opc.Ae.ConditionState)opcAeEventData.NewState).HasFlag(Opc.Ae.ConditionState.Acknowledged);
bool isRecover = opcAeEventData.EventType != Opc.Ae.EventType.Condition ? false : !((Opc.Ae.ConditionState)opcAeEventData.NewState).HasFlag(Opc.Ae.ConditionState.Active);
if (opcAeEventData.EventType != Opc.Ae.EventType.Condition)
{
bool alarm = (opcAeEventData.Message).Contains("raised");
@@ -92,6 +116,71 @@ public class TestKafkaDynamicModel1 : DynamicModelBase
isRecover = !alarm;
}
//判断报警类型
string _alarmInfo = "";
Logger.LogDebug($"opcAeEventData.ConditionName:{opcAeEventData.ConditionName}");
Logger.LogDebug($"opcAeEventData.NewState:{opcAeEventData.NewState}");
// 处理特定条件名称
if (opcAeEventData.ConditionName == "CiAdvancedAlarmState" ||
opcAeEventData.ConditionName == "CiDigitalAlarmState")
{
string _data8 = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data8));
string[] data8Parts = _data8?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
_alarmInfo = opcAeEventData.NewState switch
{
3 when data8Parts.Length > 0 => data8Parts[0],
1 when data8Parts.Length > 1 => data8Parts[1],
7 => "确认",
_ => ""
};
Logger.LogDebug($"_alarmInfo:{_alarmInfo}");
}
else
{
// 处理确认状态
if (opcAeEventData.NewState == 7)
{
_alarmInfo = "确认";
}
else
{
// 处理其他状态
_alarmInfo = string.IsNullOrEmpty(opcAeEventData.SubConditionName)
? "恢复"
: opcAeEventData.SubConditionName switch
{
"CiAnalogAlarmHighHigh" => "高高限报警",
"CiAnalogAlarmHigh" => "高限报警",
"CiAnalogAlarmLow" => "低限报警",
"CiAnalogAlarmLowLow" => "低低限报警",
"CiDigitalAlarmOn" => "合闸",
"CiAdvancedAlarmOn" => "通讯中断",
_ => "恢复" // 默认值
};
}
}
//处理报警等级
string _alarmLevel = opcAeEventData.Severity switch
{
997 => "1",
993 => "2",
989 => "3",
985 => "4",
_ => "0"
};
//处理报警内容
string content = "";
string _msg = opcAeEventData.Message;
string[] parts = _msg.Split('-', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length >= 2)
{
content = parts[1];
}
//构建告警实体
KafkaAlarmEntity alarmEntity = new KafkaAlarmEntity
{
@@ -100,14 +189,15 @@ public class TestKafkaDynamicModel1 : DynamicModelBase
resourceName = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data4)), //资源名称
metricCode = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data5)), //指标编码
metricName = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data6)), //指标名称
content = $"{variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data4))}{variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data6))}{opcAeEventData.Message}", //告警内容,设备名称+告警内容包含阈值信息可能opcae里没有带阈值信息那么就需要录入固定值可选Data10
content = $"{variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data4))}{variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data6))}{content}_{_alarmInfo}", //告警内容,设备名称+告警内容包含阈值信息可能opcae里没有带阈值信息那么就需要录入固定值可选Data10
alarmType = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data7)), // opcAeEventData.Severity 告警类型,子系统产生告警的类型,可能需要固定备注值
confirmedTime = ack ? opcAeEventData.Time.DateTimeToUnixTimestamp() : null, //告警确认时间
fixTime = isRecover ? opcAeEventData.Time : null, //解除告警时间
lastTime = opcAeEventData.AlarmTime, //产生告警时间
status = isRecover ? "FIXED" : "UNFIXED", //告警状态
alarmLevel = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data8)), //opcAeEventData.Severity.ToString(), //告警等级,可能需要固定备注值
alarmLevel = _alarmLevel,
// alarmLevel = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data8)), //opcAeEventData.Severity.ToString(), //告警等级,可能需要固定备注值
subSystemCode = variableRuntime.GetPropertyValue(id, nameof(BusinessVariableProperty.Data9)), //子系统编码
type = "SUB_SYSTEM_ALARM", //默认填写字段
confirmAccount = opcAeEventData.ActorID, //告警确认人

View File

@@ -15,9 +15,6 @@ using BootstrapBlazor.Components;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Localization;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using ThingsGateway.Gateway.Application;
namespace ThingsGateway.Server;
@@ -28,8 +25,6 @@ public partial class GatewayIndexComponent : IDisposable
[Parameter]
[EditorRequired]
[NotNull]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IStringLocalizer Localizer { get; set; }
private Chart AlarmPie { get; set; }
@@ -44,7 +39,6 @@ public partial class GatewayIndexComponent : IDisposable
public void Dispose()
{
Disposed = true;
GC.SuppressFinalize(this);
}
protected override void OnInitialized()
@@ -68,12 +62,15 @@ public partial class GatewayIndexComponent : IDisposable
if (AlarmPieInit)
await AlarmPie.Update(ChartAction.Update);
await InvokeAsync(StateHasChanged);
await Task.Delay(5000);
}
catch (Exception ex)
{
NewLife.Log.XTrace.WriteException(ex);
}
finally
{
await Task.Delay(5000);
}
}
}