This commit is contained in:
zenghongyao 2025-05-22 09:34:48 +08:00
commit 86da556e98
14 changed files with 381 additions and 71 deletions

View File

@ -348,44 +348,58 @@ namespace JiShe.CollectBus.IncrementalGenerator
private static string BuildFactoryCode()
{
return """
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace JiShe.CollectBus.Analyzers.Shared;
namespace JiShe.CollectBus.Analyzers.Shared;
public static class SourceEntityAccessorFactory
{
private static readonly ConcurrentDictionary<Type, object> _accessors = new();
public static ISourceEntityAccessor<T> GetAccessor<T>()
public static class SourceEntityAccessorFactory
{
return (ISourceEntityAccessor<T>)_accessors.GetOrAdd(typeof(T), t =>
private static readonly ConcurrentDictionary<Type, object> _accessors = new();
public static ISourceEntityAccessor<T> GetAccessor<T>()
{
// 获取泛型类型定义信息(如果是泛型类型)
var isGeneric = t.IsGenericType;
var genericTypeDef = isGeneric ? t.GetGenericTypeDefinition() : null;
var arity = isGeneric ? genericTypeDef!.GetGenericArguments().Length : 0;
// 构建访问器类名
var typeName = isGeneric
? $"{t.Namespace}.{genericTypeDef!.Name.Split('`')[0]}Accessor`{arity}"
: $"{t.Namespace}.{t.Name}Accessor";
// 尝试从当前程序集加载
var accessorType = Assembly.GetAssembly(t)!.GetType(typeName)
?? throw new InvalidOperationException($"Accessor type {typeName} not found");
// 处理泛型参数
if (isGeneric && accessorType.IsGenericTypeDefinition)
return (ISourceEntityAccessor<T>)_accessors.GetOrAdd(typeof(T), t =>
{
accessorType = accessorType.MakeGenericType(t.GetGenericArguments());
}
// 获取泛型类型定义信息(如果是泛型类型)
var isGeneric = t.IsGenericType;
var genericTypeDef = isGeneric ? t.GetGenericTypeDefinition() : null;
var arity = isGeneric ? genericTypeDef!.GetGenericArguments().Length : 0;
return Activator.CreateInstance(accessorType)!;
});
// 构建访问器类名
var typeName = isGeneric
? $"{t.Namespace}.{genericTypeDef!.Name.Split('`')[0]}Accessor`{arity}"
: $"{t.Namespace}.{t.Name}Accessor";
// 尝试从当前程序集加载
var accessorType = Assembly.GetAssembly(t)!.GetType(typeName)
?? throw new InvalidOperationException($"Accessor type {typeName} not found");
// 处理泛型参数
if (isGeneric && accessorType.IsGenericTypeDefinition)
{
accessorType = accessorType.MakeGenericType(t.GetGenericArguments());
}
return Activator.CreateInstance(accessorType)!;
});
}
public static object GetAccessor(Type type)
{
MethodInfo getAccessorMethod = typeof(SourceEntityAccessorFactory)
.GetMethod(
name: nameof(GetAccessor),
bindingAttr: BindingFlags.Public | BindingFlags.Static,
types: Type.EmptyTypes
);
MethodInfo genericMethod = getAccessorMethod.MakeGenericMethod(type);
return genericMethod.Invoke(null, null);
}
}
}
""";
}
@ -531,8 +545,8 @@ namespace JiShe.CollectBus.IncrementalGenerator
var entityType = prop.ContainingType.ToDisplayString();//entity 实体类型名称
var propType = prop.Type;//实体属性的类型
var propTypeName = propType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
// var declaredTypeName = propType.Name; // 直接获取类型名称(如 "Int32"
// 处理可空类型,获取底层具体类型名称
// var declaredTypeName = propType.Name; // 直接获取类型名称(如 "Int32"
// 处理可空类型,获取底层具体类型名称
var declaredTypeName = propType switch
{
INamedTypeSymbol { OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } nullableType =>
@ -583,19 +597,19 @@ namespace JiShe.CollectBus.IncrementalGenerator
$"GetValueTupleElementDeclaredTypeName(typeof({elementType})), " +//$"\"{elementDeclaredName}\", " +
$"(e) => Get{prop.Name}_{elementName}(({entityType})e), " +
$"(e, v) => Set{prop.Name}_{elementName}(({entityType})e, ({elementType})v))");
}
}
}
}
}
}
code.AppendLine(string.Join(",\n", initializerLines));
code.AppendLine(" };");
code.AppendLine(string.Join(",\n", initializerLines));
code.AppendLine(" };");
code.AppendLine(GetValueTupleElementName());
}
code.AppendLine(GetValueTupleElementName());
}
private static string GetValueTupleElementName()
{
return """
private static string GetValueTupleElementName()
{
return """
public static string GetValueTupleElementDeclaredTypeName(Type declaredType)
{
string typeName;
@ -613,7 +627,7 @@ namespace JiShe.CollectBus.IncrementalGenerator
return typeName;
}
""";
}
}
private static string GenerateAttributeInitializer(AttributeData attribute)

View File

@ -0,0 +1,14 @@
namespace JiShe.CollectBus.IoTDB.Attributes
{
/// <summary>
/// 需要忽略表模型初始化,有此特性无需初始化
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class IgnoreInitTableAttribute : System.Attribute
{
public IgnoreInitTableAttribute()
{
}
}
}

View File

@ -10,12 +10,11 @@ namespace JiShe.CollectBus.IoTDB.Interface
/// </summary>
public interface IIoTDbProvider
{
///// <summary>
///// 切换 SessionPool
///// </summary>
///// <param name="useTableSession">是否使用表模型</param>
//void SwitchSessionPool(bool useTableSession);
/// <summary>
/// 切换 SessionPool
/// </summary>
/// <param name="sessionpolType">是否使用表模型</param>
/// <returns></returns>
IIoTDbProvider GetSessionPool(bool sessionpolType);
/// <summary>
@ -66,5 +65,11 @@ namespace JiShe.CollectBus.IoTDB.Interface
/// <param name="options"></param>
/// <returns></returns>
Task<BusPagedResult<T>> QueryAsync<T>(IoTDBQueryOptions options) where T : IoTEntity, new();
/// <summary>
/// 初始化表模型
/// </summary>
/// <returns></returns>
Task InitTableSessionModelAsync();
}
}

View File

@ -32,5 +32,12 @@ namespace JiShe.CollectBus.IoTDB.Interface
/// <param name="sql"></param>
/// <returns></returns>
Task<SessionDataSet> ExecuteQueryStatementAsync(string sql);
/// <summary>
/// 执行无返回结果SQL
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
Task<int> ExecuteNonQueryStatementAsync(string sql);
}
}

View File

@ -50,7 +50,7 @@ namespace JiShe.CollectBus.IoTDB.Model
/// </summary>
private string _devicePath;
/// <summary>
/// 设备路径
/// 设备路径,树模型使用,表模型会在数据插入的时候直接获取继承类的名称作为表明
/// </summary>
public virtual string DevicePath
{

View File

@ -7,6 +7,7 @@ namespace JiShe.CollectBus.IoTDB.Model
/// Table模型单项数据实体
/// </summary>
[SourceAnalyzers(EntityTypeEnum.TableModel)]
[IgnoreInitTable]
public class TableModelSingleMeasuringEntity<T> : IoTEntity
{
/// <summary>

View File

@ -26,6 +26,8 @@ using System.Diagnostics.Metrics;
using Newtonsoft.Json.Linq;
using static System.Runtime.InteropServices.JavaScript.JSType;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Linq;
namespace JiShe.CollectBus.IoTDB.Provider
{
@ -366,6 +368,10 @@ namespace JiShe.CollectBus.IoTDB.Provider
{
tableNameOrTreePath = metadata.TableNameOrTreePath;
}
else if(metadata.IsSingleMeasuring && string.IsNullOrWhiteSpace(metadata.TableNameOrTreePath) == true)//单侧点时,且路径没有指定,取默认实体名称和第一个列名组合。
{
tableNameOrTreePath = $"{metadata.EntityName}_{metadata.ColumnNames.First()}";
}
else
{
tableNameOrTreePath = DevicePathBuilder.GetTableName<T>();
@ -630,10 +636,7 @@ namespace JiShe.CollectBus.IoTDB.Provider
{
// 过滤元组子项
if (member.NameOrPath.Contains(".Item")) continue;
// 类型名称处理
string declaredTypeName = member.DeclaredTypeName;
// 特性查询优化
var attributes = member.CustomAttributes ?? Enumerable.Empty<Attribute>();
var tagAttr = attributes.OfType<TAGColumnAttribute>().FirstOrDefault();
@ -842,6 +845,25 @@ namespace JiShe.CollectBus.IoTDB.Provider
}
}
/// <summary>
/// 处理不同列类型的逻辑
/// </summary>
/// <param name="groupedColumns"></param>
/// <param name="category"></param>
private string ProcessCategory(IReadOnlyDictionary<ColumnCategory, List<ColumnInfo>> groupedColumns, ColumnCategory category)
{
if (groupedColumns.TryGetValue(category, out var cols))
{
List<string> tempColumnInfos = new List<string>();
foreach (var item in cols)
{
tempColumnInfos.Add($" {item.Name} {item.DataType} {item.Category}");
}
return string.Join(",", tempColumnInfos);
}
return string.Empty;
}
/// <summary>
/// 数据列结构
/// </summary>
@ -976,6 +998,136 @@ namespace JiShe.CollectBus.IoTDB.Provider
return cache;
}
private static readonly Regex _asciiAlphanumericRegex = new Regex(@"^[a-zA-Z0-9]*$", RegexOptions.Compiled);
/// <summary>
/// 初始化表模型
/// </summary>
/// <returns></returns>
public async Task InitTableSessionModelAsync()
{
//获取JiShe.CollectBus.IoTDB程序集和JiShe.CollectBus.Domain程序集中的所有 [SourceAnalyzers(EntityTypeEnum.TableModel)] 的实体
var assemblyNames = new[] { "JiShe.CollectBus.IoTDB", "JiShe.CollectBus.Domain" };
var assemblies = CommonHelper.LoadAssemblies(assemblyNames);
var targetTypes = CollectTargetTypes(assemblies);
if (targetTypes == null || targetTypes.Count <= 0)
{
_logger.LogError($"{nameof(InitTableSessionModelAsync)} 初始化表模型时没有找到对应的实体类信息。");
return;
}
// @"CREATE TABLE table1(
// time TIMESTAMP TIME,
// region STRING TAG,
// plant_id STRING TAG,
// device_id STRING TAG,
// model_id STRING ATTRIBUTE,
// maintenance STRING ATTRIBUTE,
// temperature FLOAT FIELD,
// humidity FLOAT FIELD,
// status Boolean FIELD,
// arrival_time TIMESTAMP FIELD
//) COMMENT 'table1' WITH(TTL = 31536000000);";
foreach (var item in targetTypes)
{
var accessor = SourceEntityAccessorFactory.GetAccessor(item);
//通过 dynamic 简化操作
dynamic dynamicAccessor = accessor;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("CREATE TABLE IF NOT EXISTS ");
stringBuilder.Append(dynamicAccessor.EntityName);
stringBuilder.Append("( time TIMESTAMP TIME, ");
var columns = new List<ColumnInfo>();
foreach (var member in dynamicAccessor.MemberList)
{
// 过滤元组子项
if (member.NameOrPath.Contains(".Item")) continue;
// 特性查询优化
var attributes = (IEnumerable<Attribute>)(member.CustomAttributes ?? Enumerable.Empty<Attribute>());
var tagAttr = attributes.OfType<TAGColumnAttribute>().FirstOrDefault();
var attrColumn = attributes.OfType<ATTRIBUTEColumnAttribute>().FirstOrDefault();
var fieldColumn = attributes.OfType<FIELDColumnAttribute>().FirstOrDefault();
// 构建ColumnInfo
ColumnInfo? column = null;
if (tagAttr != null)
{
column = new ColumnInfo(member.NameOrPath, ColumnCategory.TAG, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName);
}
else if (attrColumn != null)
{
column = new ColumnInfo(member.NameOrPath, ColumnCategory.ATTRIBUTE, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName);
}
else if (fieldColumn != null)
{
column = new ColumnInfo(member.NameOrPath, ColumnCategory.FIELD, GetDataTypeFromTypeName(member.DeclaredTypeName), false, member.DeclaredTypeName);
}
if (!column.HasValue)
{
_logger.LogError($"{nameof(InitTableSessionModelAsync)} 初始化表模型时实体类{dynamicAccessor.EntityName}的{member.NameOrPath}列的ColumnInfo构建失败。");
continue;
}
columns.Add(column.Value);
}
//按业务逻辑顺序处理TAG -> ATTRIBUTE -> FIELD
var groupedColumns = columns
.GroupBy(c => c.Category)
.ToDictionary(g => g.Key, g => g.ToList());
List<string> tempColumInfos = new List<string>();
tempColumInfos.Add( ProcessCategory(groupedColumns, ColumnCategory.TAG));
tempColumInfos.Add(ProcessCategory(groupedColumns, ColumnCategory.ATTRIBUTE));
tempColumInfos.Add(ProcessCategory(groupedColumns, ColumnCategory.FIELD));
stringBuilder.Append(string.Join(",", tempColumInfos.Where(d => !string.IsNullOrWhiteSpace(d))));
stringBuilder.Append($" ) COMMENT '{item.Name}' ");
_logger.LogError($"{dynamicAccessor.EntityName} 初始化语句:{stringBuilder.ToString()}");
await CurrentSession.ExecuteNonQueryStatementAsync($"{stringBuilder.ToString()}");
}
}
/// <summary>
/// 获取程序集中的所有 [SourceAnalyzers(EntityTypeEnum.TableModel)] 的实体
/// </summary>
/// <param name="assemblies"></param>
/// <returns></returns>
private List<Type> CollectTargetTypes(List<Assembly> assemblies)
{
var targetTypes = new List<Type>();
foreach (var assembly in assemblies)
{
try
{
foreach (var type in assembly.GetExportedTypes())
{
//获取表模型特性的类
var sourceAnalyzersAttribute = type.GetCustomAttribute<SourceAnalyzersAttribute>();
//需要忽略表模型初始化,有此特性无需初始化
var ignoreInitTableAttribute = type.GetCustomAttribute<IgnoreInitTableAttribute>();
if (sourceAnalyzersAttribute?.EntityType == EntityTypeEnum.TableModel && ignoreInitTableAttribute == null)
{
if (type.GetConstructor(Type.EmptyTypes) != null)
targetTypes.Add(type);
}
}
}
catch (ReflectionTypeLoadException ex)
{
_logger.LogError($"加载 {assembly} 失败: {string.Join(", ", ex.LoaderExceptions.Select(e => e.Message))}");
}
}
return targetTypes;
}
}
}

View File

@ -71,8 +71,7 @@ namespace JiShe.CollectBus.IoTDB.Provider
if (result != 0)
{
throw new Exception($"{nameof(SessionPoolAdapter)} Tree模型数据入库没有成功返回结果为{result}请检查IoTEntity继承子类属性索引是否有变动。");
}
//await CloseAsync();
}
return result;
}
@ -83,9 +82,18 @@ namespace JiShe.CollectBus.IoTDB.Provider
/// <returns></returns>
public async Task<SessionDataSet> ExecuteQueryStatementAsync(string sql)
{
var result = await _sessionPool.ExecuteQueryStatementAsync(sql, _options.Timeout);
//await result.Close();
//await CloseAsync();
var result = await _sessionPool.ExecuteQueryStatementAsync(sql, _options.Timeout);
return result;
}
/// <summary>
/// 执行无返回结果SQL
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public async Task<int> ExecuteNonQueryStatementAsync(string sql)
{
var result = await _sessionPool.ExecuteNonQueryStatementAsync(sql);
return result;
}

View File

@ -70,8 +70,7 @@ namespace JiShe.CollectBus.IoTDB.Provider
{
throw new Exception($"{nameof(TableSessionPoolAdapter)} table模型数据入库没有成功返回结果为{result}请检查IoTEntity继承子类属性索引是否有变动。");
}
//await CloseAsync();
return result;
}
@ -82,9 +81,18 @@ namespace JiShe.CollectBus.IoTDB.Provider
/// <returns></returns>
public async Task<SessionDataSet> ExecuteQueryStatementAsync(string sql)
{
var result = await _sessionPool.ExecuteQueryStatementAsync(sql,_options.Timeout);
//await result.Close();
//await CloseAsync();
var result = await _sessionPool.ExecuteQueryStatementAsync(sql,_options.Timeout);
return result;
}
/// <summary>
/// 执行无返回结果SQL
/// </summary>
/// <param name="sql"></param>
/// <returns></returns>
public async Task<int> ExecuteNonQueryStatementAsync(string sql)
{
var result = await _sessionPool.ExecuteNonQueryStatementAsync(sql);
return result;
}

View File

@ -30,6 +30,7 @@ using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Sockets;
@ -173,7 +174,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
};
await _iotDBProvider.InsertAsync(meter2);
ElectricityMeter meter3 = new ElectricityMeter()
{
@ -207,7 +208,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
time = DateTime.Now;
//System.Reflection.PropertyInfo;
//System.Reflection.FieldInfo
//TreeModelSingleMeasuringEntityAccessor
//TreeModelSingleMeasuringEntityAccessor
var meter = new TreeModelSingleMeasuringEntity<decimal?>()
{
SystemName = "energy",
@ -585,5 +586,35 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
await Task.CompletedTask;
}
/// <summary>
/// IoTDB空表查询情况
/// </summary>
/// <returns></returns>
[HttpGet]
public async Task TestIoTDBEmptyTableQuery()
{
var meter = new MeterReadingTelemetryPacketInfo() { DevicePath = "MeterReadingTelemetryPacketInfo", DeviceId = "1111" };
QueryCondition conditions = new QueryCondition()
{
Field = "DeviceId",
Operator = "=",
Value = meter.DeviceId
};
var query = new IoTDBQueryOptions()
{
TableNameOrTreePath = meter.DevicePath,
PageIndex = 1,
PageSize = 1,
Conditions = new List<QueryCondition>() { conditions },
};
await _iotDBProvider.GetSessionPool(true).InitTableSessionModelAsync();
var pageResult = await _iotDBProvider.GetSessionPool(true).QueryAsync<MeterReadingTelemetryPacketInfo>(query);
await Task.CompletedTask;
}
}

View File

@ -329,6 +329,8 @@ namespace JiShe.CollectBus.ScheduledMeterReading
// 创建取消令牌源
//var cts = new CancellationTokenSource();
await _dbProvider.GetSessionPool(true).InitTableSessionModelAsync();
_ = _dataChannelManage.ScheduledMeterTaskReadingAsync(DataChannelManage.TaskDataChannel.Reader);
// //此处代码不要删除

View File

@ -13,7 +13,8 @@ namespace JiShe.CollectBus.IotSystems.Devices
/// 设备表型数据信息
/// </summary>
[SourceAnalyzers(EntityTypeEnum.TableModel)]
public class DeviceTreeModelDataInfo: IoTEntity
[IgnoreInitTable]
public class DeviceTableModelDataInfo : IoTEntity
{
[FIELDColumn]

View File

@ -12,8 +12,8 @@ namespace JiShe.CollectBus.IotSystems.Devices
/// <summary>
/// 设备树模型数据信息
/// </summary>
[SourceAnalyzers(EntityTypeEnum.TableModel)]
public class DeviceTableModelDataInfo : IoTEntity
[SourceAnalyzers(EntityTypeEnum.TreeModel)]
public class DeviceTreeModelDataInfo : IoTEntity
{
[FIELDColumn]

View File

@ -865,5 +865,72 @@ namespace JiShe.CollectBus.Common.Helpers
return Convert.ToInt64(scoresStr);
}
/// <summary>
/// 加载指定名称的程序集
/// </summary>
/// <param name="assemblyNames"></param>
/// <returns></returns>
public static List<Assembly> LoadAssemblies(string[] assemblyNames)
{
var assemblies = new List<Assembly>();
// 获取已加载的程序集
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
if (assemblyNames.Contains(asm.GetName().Name))
assemblies.Add(asm);
}
// 尝试加载未加载的程序集
foreach (var name in assemblyNames)
{
if (!assemblies.Any(a => a.GetName().Name == name))
{
try
{
var assembly = Assembly.Load(name);
assemblies.Add(assembly);
}
catch (FileNotFoundException)
{
// 若Load失败尝试从基目录加载
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{name}.dll");
if (File.Exists(path))
{
try
{
assemblies.Add(Assembly.LoadFrom(path));
}
catch { /* 记录错误 */ }
}
}
}
}
return assemblies;
}
/// <summary>
/// 创建类型实例
/// </summary>
/// <param name="types"></param>
/// <returns></returns>
public static List<object> CreateInstances(List<Type> types)
{
var instances = new List<object>();
foreach (var type in types)
{
try
{
instances.Add(Activator.CreateInstance(type));
}
catch (Exception)
{
throw;
}
}
return instances;
}
}
}