完善复杂类型增量源码生成器

This commit is contained in:
ChenYi 2025-05-07 16:37:26 +08:00
parent bca7202558
commit 6b012d9303
10 changed files with 401 additions and 379 deletions

View File

@ -15,13 +15,11 @@ namespace JiShe.CollectBus.IncrementalGenerator
public class ComplexTypeSourceAnalyzers : IIncrementalGenerator
{
private const string AttributeFullName = "JiShe.CollectBus.Analyzers.Shared.SourceAnalyzersAttribute";
private const string SourceEntityAccessorFactoryNamespace = "JiShe.CollectBus.Analyzers.Shared";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
//Debugger.Launch();
// 步骤1筛选带有 [SourceAnalyzers] 的类
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
@ -108,11 +106,6 @@ namespace JiShe.CollectBus.IncrementalGenerator
continue;
}
var code = BuildAccessorsForType(classSymbol, compilation, processedTypes);
context.AddSource($"{classSymbol.Name}Extension.g.cs", code);
var code3 = BuildAccessorsForSourceEntity(classSymbol, compilation, processedTypes);
context.AddSource($"{classSymbol.Name}Accessor.g.cs", code3);
}
@ -121,39 +114,6 @@ namespace JiShe.CollectBus.IncrementalGenerator
context.AddSource("SourceEntityAccessorFactory.g.cs", BuildFactoryCode());
}
/// <summary>
/// 构建类的属性访问器代码
/// </summary>
/// <param name="classSymbol"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
/// <returns></returns>
private static string BuildAccessorsForType(
INamedTypeSymbol classSymbol,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
{
var code = new StringBuilder();
code.AppendLine("#pragma warning disable CS0419 // 禁用警告");
code.AppendLine("// Generated code for " + classSymbol.Name);
code.AppendLine("// <auto-generated/>");
code.AppendLine("#nullable enable");
code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};");
code.AppendLine();
code.AppendLine($"public static class {classSymbol.Name}Extension{GetGenericParams(classSymbol)}");
code.AppendLine("{");
foreach (var prop in GetAllPropertiesInHierarchy(classSymbol))
{
if (prop.IsIndexer) continue;
GeneratePropertyAccessorsForType(prop, code, compilation, processedTypes);
}
code.AppendLine("}");
return code.ToString();
}
/// <summary>
/// 获取泛型参数
/// </summary>
@ -166,63 +126,6 @@ namespace JiShe.CollectBus.IncrementalGenerator
return $"<{string.Join(", ", parameters)}>";
}
/// <summary>
/// 生成属性访问器代码
/// </summary>
/// <param name="prop"></param>
/// <param name="code"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
private static void GeneratePropertyAccessorsForType(
IPropertySymbol prop,
StringBuilder code,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
{
// 安全类型转换
if (prop.Type is not ITypeSymbol propType) return;
code.AppendLine($" // Processing property: {prop.Name}");
// 处理元组类型
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
{
GenerateTupleAccessorsForType(prop, tupleType, code);
}
else if (propType is INamedTypeSymbol namedType)
{
GenerateStandardAccessorsForType(prop, namedType, code);
ProcessNestedType(namedType, compilation, processedTypes);
}
}
/// <summary>
/// 处理元组类型
/// </summary>
/// <param name="prop"></param>
/// <param name="tupleType"></param>
/// <param name="code"></param>
private static void GenerateTupleAccessorsForType(IPropertySymbol prop, INamedTypeSymbol tupleType, StringBuilder code)
{
var elements = tupleType.TupleElements;
var parentType = prop.ContainingType.ToDisplayString();
for (int i = 0; i < elements.Length; i++)
{
var element = elements[i];
if (element.Type is not ITypeSymbol elementType) continue;
var elementName = element.CorrespondingTupleField?.Name ?? $"Item{i + 1}";
code.AppendLine($" public static {elementType.ToDisplayString()} Get{prop.Name}_{elementName}({parentType} obj) => obj.{prop.Name}.{elementName};");
if (prop.SetMethod != null)
{
var assignments = elements.Select((e, idx) =>
idx == i ? "value" : $"obj.{prop.Name}.{e.Name}");
code.AppendLine($" public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType.ToDisplayString()} value) => obj.{prop.Name} = ({string.Join(", ", assignments)});");
}
}
}
/// <summary>
/// 生成标准属性的访问器
@ -230,7 +133,7 @@ namespace JiShe.CollectBus.IncrementalGenerator
/// <param name="prop"></param>
/// <param name="propType"></param>
/// <param name="code"></param>
private static void GenerateStandardAccessorsForType(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code)
private static void GenerateStandardAccessors(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code)
{
var parentType = prop.ContainingType.ToDisplayString();
code.AppendLine($" public static {propType.ToDisplayString()} Get{prop.Name}({parentType} obj) => obj.{prop.Name};");
@ -243,208 +146,315 @@ namespace JiShe.CollectBus.IncrementalGenerator
/// <summary>
/// 处理嵌套类型
/// 构建实体访问器代码(支持泛型)
/// </summary>
/// <param name="typeSymbol"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
private static void ProcessNestedType(ITypeSymbol typeSymbol, Compilation compilation, HashSet<ITypeSymbol> processedTypes)
{
if (typeSymbol is not INamedTypeSymbol namedType) return;
if (!ShouldProcessNestedType(namedType)) return;
if (!processedTypes.Add(namedType)) return;
var code = BuildAccessorsForType(namedType, compilation, processedTypes);
}
/// <summary>
/// 处理嵌套类型
/// </summary>
/// <param name="symbol"></param>
/// <returns></returns>
private static bool ShouldProcessNestedType(INamedTypeSymbol symbol)
{
return symbol.DeclaredAccessibility == Accessibility.Public &&
!symbol.IsTupleType &&
!symbol.IsAnonymousType &&
!symbol.IsImplicitlyDeclared &&
!symbol.Name.StartsWith("<");
}
/// <summary>
/// 生成扩展类工厂代码
/// </summary>
/// <returns></returns>
private static string BuildFactoryCode()
{
return """
using System;
using System.Collections.Concurrent;
namespace JiShe.CollectBus.Analyzers.Shared;
public static class SourceEntityAccessorFactory
{
private static readonly ConcurrentDictionary<Type, object> _accessors = new();
public static ISourceEntityAccessor<T> GetAccessor<T>()
{
if (!_accessors.TryGetValue(typeof(T), out var accessor))
{
accessor = Activator.CreateInstance(
Type.GetType($"{typeof(T).Namespace}.{typeof(T).Name}Accessor")!)!;
_accessors.TryAdd(typeof(T), accessor);
}
return (ISourceEntityAccessor<T>)accessor!;
}
}
""";
}
/// <summary>
/// 构建实体类的源属性访问器代码
/// </summary>
/// <param name="classSymbol"></param>
/// <param name="compilation"></param>
/// <param name="processedTypes"></param>
/// <returns></returns>
private static string BuildAccessorsForSourceEntity(
INamedTypeSymbol classSymbol,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
INamedTypeSymbol classSymbol,
Compilation compilation,
HashSet<ITypeSymbol> processedTypes)
{
var code = new StringBuilder();
code.AppendLine("#pragma warning disable CS0419 // 禁用警告");
code.AppendLine("// Generated code for " + classSymbol.Name);
code.AppendLine("// <auto-generated/>");
code.AppendLine("#nullable enable");
code.AppendLine("using System;");
code.AppendLine("using System.Collections.Generic;");
code.AppendLine("using JiShe.CollectBus.Analyzers.Shared;");
code.AppendLine($"namespace {classSymbol.ContainingNamespace.ToDisplayString()};");
code.AppendLine();
code.AppendLine($"public sealed class {classSymbol.Name}Accessor : ISourceEntityAccessor<{classSymbol.Name}>");
// 处理泛型类型参数
var genericParams = classSymbol.IsGenericType
? $"<{string.Join(", ", classSymbol.TypeParameters.Select(t => t.Name))}>"
: "";
code.AppendLine(
$"public sealed class {classSymbol.Name}Accessor{genericParams} " +
$": ISourceEntityAccessor<{classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>");
code.AppendLine("{");
GetGeneratePropertyValueForSourceEntity(GetAllPropertiesInHierarchy(classSymbol), code, compilation, classSymbol);
var propList = GetAllPropertiesInHierarchy(classSymbol);
foreach (var prop in propList)
{
// 安全类型转换
if (prop.Type is not ITypeSymbol propType) continue;
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
{
GenerateTupleAccessors(prop, tupleType, code, classSymbol);
}
else if (propType is INamedTypeSymbol namedType)
{
GenerateStandardAccessors(prop, namedType, code);
}
}
//生成当前类属性名称集合
GeneratePropertyListForSourceEntity(propList, code, compilation, classSymbol);
//生成当前类属性访问
GetGeneratePropertyValueForSourceEntity(
propList,
code,
compilation,
classSymbol);
//生成当前类属性设置
SetGeneratePropertyValueForSourceEntity(
propList,
code,
compilation,
classSymbol);
SetGeneratePropertyValueForSourceEntity(GetAllPropertiesInHierarchy(classSymbol), code, compilation, classSymbol);
code.AppendLine("}");
return code.ToString();
}
private static void GetGeneratePropertyValueForSourceEntity(IEnumerable<IPropertySymbol> propList,
/// <summary>
/// 生成泛型ValueTuple 元组访问器
/// </summary>
private static void GenerateTupleAccessors(
IPropertySymbol prop,
INamedTypeSymbol tupleType,
StringBuilder code,
INamedTypeSymbol classSymbol)
{
var parentType = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var tupleElements = tupleType.TupleElements;
for (int i = 0; i < tupleElements.Length; i++)
{
var element = tupleElements[i];
var elementType = element.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var elementName = element.Name;
// Getter保持不变
code.AppendLine(
$" public static {elementType} Get{prop.Name}_{elementName}" +
$"({parentType} obj) => obj.{prop.Name}.{elementName};");
// Setter修复使用元组字面量
if (prop.SetMethod != null)
{
var valueExpressions = tupleElements.Select((e, idx) =>
idx == i ? "value" : $"obj.{prop.Name}.{e.Name}"
).ToList();
// 关键修复:生成正确的元组字面量表达式
code.AppendLine(
$" public static void Set{prop.Name}_{elementName}" +
$"({parentType} obj, {elementType} value) => " +
$"obj.{prop.Name} = ({string.Join(", ", valueExpressions)});");
}
}
}
/// <summary>
/// 处理System.Tuple类型的访问器生成
/// </summary>
private static void GenerateSystemTupleAccessors(
IPropertySymbol prop,
INamedTypeSymbol tupleType,
StringBuilder code,
INamedTypeSymbol classSymbol)
{
var parentType = classSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var elementTypes = tupleType.TypeArguments;
var tupleTypeName = tupleType.ToDisplayString();
for (int i = 0; i < elementTypes.Length; i++)
{
var elementType = elementTypes[i].ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
var elementName = $"Item{i + 1}";
// Getter
code.AppendLine(
$" public static {elementType} Get{prop.Name}_{elementName}" +
$"({parentType} obj) => obj.{prop.Name}.{elementName};");
// Setter
if (prop.SetMethod != null)
{
var assignments = elementTypes.Select((_, idx) =>
idx == i ? "value" : $"obj.{prop.Name}.Item{idx + 1}"
).ToList();
code.AppendLine(
$" public static void Set{prop.Name}_{elementName}" +
$"({parentType} obj, {elementType} value) => " +
$"obj.{prop.Name} = new {tupleTypeName}({string.Join(", ", assignments)});");
}
}
}
/// <summary>
/// 增强的工厂类实现
/// </summary>
private static string BuildFactoryCode()
{
return """
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace JiShe.CollectBus.Analyzers.Shared;
public static class SourceEntityAccessorFactory
{
private static readonly ConcurrentDictionary<Type, object> _accessors = new();
public static ISourceEntityAccessor<T> GetAccessor<T>()
{
return (ISourceEntityAccessor<T>)_accessors.GetOrAdd(typeof(T), t =>
{
var typeName = $"{t.Namespace}.{t.Name}Accessor";
if (t.IsGenericType)
{
var genericArgs = t.GetGenericArguments();
var genericDef = t.GetGenericTypeDefinition();
typeName = $"{genericDef.Namespace}.{genericDef.Name}Accessor`{genericArgs.Length}";
}
var type = Type.GetType(typeName)
?? Assembly.GetAssembly(t)?.GetType(typeName)
?? throw new InvalidOperationException($"Accessor type {typeName} not found");
return t.IsGenericType
? Activator.CreateInstance(type.MakeGenericType(t.GetGenericArguments()))
: Activator.CreateInstance(type);
});
}
}
""";
}
/// <summary>
/// 属性访问生成逻辑
/// </summary>
/// <param name="propList">属性集合</param>
/// <param name="code"></param>
/// <param name="compilation"></param>
/// <param name="classSymbol"></param>
private static void GetGeneratePropertyValueForSourceEntity(
IEnumerable<IPropertySymbol> propList,
StringBuilder code,
Compilation compilation,
INamedTypeSymbol classSymbol)
{
code.AppendLine($" public object GetPropertyValue({classSymbol.Name} targetEntity, string propertyName)");
code.AppendLine($" public object GetPropertyValue({classSymbol} targetEntity, string propertyName)");
code.AppendLine(" {");
code.AppendLine(" return propertyName switch");
code.AppendLine(" {");
code.AppendLine(" return propertyName switch");
code.AppendLine(" {");
// 遍历所有属性
foreach (var property in propList)
foreach (var prop in propList)
{
// 安全类型转换
if (property.Type is not ITypeSymbol propType) continue;
// 处理元组类型
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType)
{
foreach (var element in tupleType.TupleElements)
{
code.AppendLine(
$" \"{prop.Name}.{element.Name}\" => " +
$"Get{prop.Name}_{element.Name}(targetEntity),");
}
}
else if (propType is INamedTypeSymbol namedType)
else
{
// 生成属性访问的
code.AppendLine($" \"{property.Name}\" => {classSymbol.Name}Extension.Get{property.Name}(targetEntity),");
code.AppendLine(
$" \"{prop.Name}\" => " +
$"Get{prop.Name}(targetEntity),");
}
}
code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")");
code.AppendLine(" };");
code.AppendLine(" }");
}
private static void SetGeneratePropertyValueForSourceEntity(IEnumerable<IPropertySymbol> propList,
StringBuilder code,
Compilation compilation,
INamedTypeSymbol classSymbol)
{
code.AppendLine($" public void SetPropertyValue({classSymbol.Name} targetEntity, string propertyName, object value)");
code.AppendLine(" {");
code.AppendLine(" return propertyName switch");
code.AppendLine(" {");
// 遍历所有属性
foreach (var property in propList)
{
// 安全类型转换
if (property.Type is not ITypeSymbol propType) continue;
// 处理元组类型
if (propType is INamedTypeSymbol { IsTupleType: true } tupleType)
{
}
else if (propType is INamedTypeSymbol namedType)
{
// 生成属性设置赋值
code.AppendLine($" \"{property.Name}\" => {classSymbol.Name}Extension.Set{property.Name}(targetEntity,({namedType.ToDisplayString()})value),");
}
}
code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")");
code.AppendLine(" };");
code.AppendLine(" }");
code.AppendLine(" _ => throw new ArgumentException($\"Unknown property: {propertyName}\")");
code.AppendLine(" };");
code.AppendLine(" }");
}
/// <summary>
/// 处理元组类型
/// 属性设置生成逻辑
/// </summary>
/// <param name="prop"></param>
/// <param name="tupleType"></param>
/// <param name="propList">属性集合</param>
/// <param name="code"></param>
private static void GenerateTupleAccessorsForSourceEntity(IPropertySymbol prop, INamedTypeSymbol tupleType, StringBuilder code)
/// <param name="compilation"></param>
/// <param name="classSymbol"></param>
private static void SetGeneratePropertyValueForSourceEntity(
IEnumerable<IPropertySymbol> propList,
StringBuilder code,
Compilation compilation,
INamedTypeSymbol classSymbol)
{
var elements = tupleType.TupleElements;
var parentType = prop.ContainingType.ToDisplayString();
code.AppendLine($" public void SetPropertyValue({classSymbol} targetEntity, string propertyName, object value)");
code.AppendLine(" {");
code.AppendLine(" switch (propertyName)");
code.AppendLine(" {");
for (int i = 0; i < elements.Length; i++)
foreach (var prop in propList)
{
var element = elements[i];
if (element.Type is not ITypeSymbol elementType) continue;
var elementName = element.CorrespondingTupleField?.Name ?? $"Item{i + 1}";
code.AppendLine($" public static {elementType.ToDisplayString()} Get{prop.Name}_{elementName}({parentType} obj) => obj.{prop.Name}.{elementName};");
if (prop.SetMethod != null)
if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType)
{
var assignments = elements.Select((e, idx) =>
idx == i ? "value" : $"obj.{prop.Name}.{e.Name}");
code.AppendLine($" public static void Set{prop.Name}_{elementName}({parentType} obj, {elementType.ToDisplayString()} value) => obj.{prop.Name} = ({string.Join(", ", assignments)});");
foreach (var element in tupleType.TupleElements)
{
var elementType = element.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
code.AppendLine($" case \"{prop.Name}.{element.Name}\":");
code.AppendLine($" Set{prop.Name}_{element.Name}(");
code.AppendLine($" targetEntity, ({elementType})value);");
code.AppendLine(" break;");
}
}
else if (prop.SetMethod != null)
{
var propType = prop.Type.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
code.AppendLine($" case \"{prop.Name}\":");
code.AppendLine($" Set{prop.Name}(");
code.AppendLine($" targetEntity, ({propType})value);");
code.AppendLine(" break;");
}
}
code.AppendLine(" default:");
code.AppendLine(" throw new ArgumentException($\"Unknown property: {propertyName}\");");
code.AppendLine(" }");
code.AppendLine(" }");
}
/// <summary>
/// 生成标准属性的访问器
/// 属性名称集合
/// </summary>
/// <param name="prop"></param>
/// <param name="propType"></param>
/// <param name="propList">属性集合</param>
/// <param name="code"></param>
private static void GenerateStandardAccessorsForSourceEntity(IPropertySymbol prop, INamedTypeSymbol propType, StringBuilder code)
/// <param name="compilation"></param>
/// <param name="classSymbol"></param>
private static void GeneratePropertyListForSourceEntity(
IEnumerable<IPropertySymbol> propList,
StringBuilder code,
Compilation compilation,
INamedTypeSymbol classSymbol)
{
var parentType = prop.ContainingType.ToDisplayString();
code.AppendLine($" public object {propType.ToDisplayString()} Get{prop.Name}({parentType} obj) => obj.{prop.Name};");
if (prop.SetMethod != null)
code.AppendLine(" public List<string> PropertyList {get;} = new List<string>()");
code.AppendLine(" {");
List<string> tempPropList = new List<string>();
foreach (var prop in propList)
{
code.AppendLine($" public static void Set{prop.Name}({parentType} obj, {propType.ToDisplayString()} value) => obj.{prop.Name} = value;");
if (prop.Type is INamedTypeSymbol { IsTupleType: true } tupleType)
{
foreach (var element in tupleType.TupleElements)
{
tempPropList.Add($"\"{prop.Name}.{element.Name}\"");
}
}
else
{
tempPropList.Add($"\"{prop.Name}\"");
}
}
code.Append(string.Join(",", tempPropList));
code.AppendLine(" };");
}
}
}

View File

@ -8,13 +8,13 @@ namespace JiShe.CollectBus.IoTDB.Model
/// Table模型单项数据实体
/// </summary>
[EntityType(EntityTypeEnum.TableModel)]
//[SourceAnalyzers]
[SourceAnalyzers]
public class TableModelSingleMeasuringEntity<T> : IoTEntity
{
/// <summary>
/// 单项数据键值对
/// </summary>
[SingleMeasuring(nameof(SingleColumn))]
public required Tuple<string, T> SingleColumn { get; set; }
public required ValueTuple<string, T> SingleColumn { get; set; }
}
}

View File

@ -8,13 +8,13 @@ namespace JiShe.CollectBus.IoTDB.Model
/// Tree模型单项数据实体
/// </summary>
[EntityType(EntityTypeEnum.TreeModel)]
//[SourceAnalyzers]
[SourceAnalyzers]
public class TreeModelSingleMeasuringEntity<T> : IoTEntity
{
/// <summary>
/// 单项数据键值对
/// </summary>
[SingleMeasuring(nameof(SingleMeasuring))]
public required Tuple<string, T> SingleMeasuring { get; set; }
public required ValueTuple<string, T> SingleMeasuring { get; set; }
}
}

View File

@ -20,6 +20,7 @@ using JiShe.CollectBus.IoTDB.Options;
using Microsoft.Extensions.Logging;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using JiShe.CollectBus.Analyzers.Shared;
namespace JiShe.CollectBus.IoTDB.Provider
{
@ -176,6 +177,8 @@ namespace JiShe.CollectBus.IoTDB.Provider
/// <returns></returns>
public async Task<DeviceMetadata> GetMetadata<T>() where T : IoTEntity
{
var accessor = SourceEntityAccessorFactory.GetAccessor<T>();
var columns = CollectColumnMetadata(typeof(T));
var metadata = BuildDeviceMetadata<T>(columns);
var metaData = MetadataCache.AddOrUpdate(
@ -260,6 +263,8 @@ namespace JiShe.CollectBus.IoTDB.Provider
List<string> tempColumnNames = new List<string>();
tempColumnNames.AddRange(metadata.ColumnNames);
var accessor = SourceEntityAccessorFactory.GetAccessor<T>();
var entityTypeAttribute = typeof(T).GetCustomAttribute<EntityTypeAttribute>();
if (entityTypeAttribute == null)
@ -295,69 +300,74 @@ namespace JiShe.CollectBus.IoTDB.Provider
foreach (var measurement in tempColumnNames)
{
PropertyInfo propertyInfo = typeof(T).GetProperty(measurement);
if (propertyInfo == null)
{
throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,没有找到{measurement}属性,属于异常情况,-101。");
}
var value = propertyInfo.GetValue(entity);
if (propertyInfo.IsDefined(typeof(SingleMeasuringAttribute), false) && metadata.IsSingleMeasuring == true)//表示当前对象是单测点模式
{
if (value != null)
{
Type tupleType = value.GetType();
Type[] tupleArgs = tupleType.GetGenericArguments();
Type item2Type = tupleArgs[1]; // T 的实际类型
var item1 = tupleType.GetProperty("Item1")!.GetValue(value);
var item2 = tupleType.GetProperty("Item2")!.GetValue(value);
if (item1 == null || item2 == null)
{
throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,单测点模式构建失败,没有获取测点名称或者测点值,-102。");
}
var indexOf = metadata.ColumnNames.IndexOf(measurement);
metadata.ColumnNames[indexOf] = (string)item1!;
rowValues.Add(item2);
}
else
{
rowValues.Add(null);
}
//同时如果是单测点模式且是table模型存储路径只能通过DevicePathBuilder.GetDeviceTableName(entity)获取
if (_runtimeContext.UseTableSessionPool)
{
tableNameOrTreePath = DevicePathBuilder.GetDeviceTableName(entity);
}
}
else
{
//需要根据value的类型进行相应的值映射转换例如datetime转换为long的时间戳值
if (value != null)
{
Type tupleType = value.GetType();
var tempValue = tupleType.Name.ToUpper() switch
{
"DATETIME" => Convert.ToDateTime(value).GetDateTimeOffset().ToUnixTimeNanoseconds(),
_ => value
};
rowValues.Add(tempValue);
}
else
{
rowValues.Add(value);
}
}
rowValues.Add(accessor.GetPropertyValue(entity,measurement));
}
values.Add(rowValues);
//foreach (var measurement in tempColumnNames)
//{
// PropertyInfo propertyInfo = typeof(T).GetProperty(measurement);
// if (propertyInfo == null)
// {
// throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,没有找到{measurement}属性,属于异常情况,-101。");
// }
// var value = propertyInfo.GetValue(entity);
// if (propertyInfo.IsDefined(typeof(SingleMeasuringAttribute), false) && metadata.IsSingleMeasuring == true)//表示当前对象是单测点模式
// {
// if (value != null)
// {
// Type tupleType = value.GetType();
// Type[] tupleArgs = tupleType.GetGenericArguments();
// Type item2Type = tupleArgs[1]; // T 的实际类型
// var item1 = tupleType.GetProperty("Item1")!.GetValue(value);
// var item2 = tupleType.GetProperty("Item2")!.GetValue(value);
// if (item1 == null || item2 == null)
// {
// throw new Exception($"{nameof(BuildTablet)} 构建表模型{typeof(T).Name}时,单测点模式构建失败,没有获取测点名称或者测点值,-102。");
// }
// var indexOf = metadata.ColumnNames.IndexOf(measurement);
// metadata.ColumnNames[indexOf] = (string)item1!;
// rowValues.Add(item2);
// }
// else
// {
// rowValues.Add(null);
// }
// //同时如果是单测点模式且是table模型存储路径只能通过DevicePathBuilder.GetDeviceTableName(entity)获取
// if (_runtimeContext.UseTableSessionPool)
// {
// tableNameOrTreePath = DevicePathBuilder.GetDeviceTableName(entity);
// }
// }
// else
// {
// //需要根据value的类型进行相应的值映射转换例如datetime转换为long的时间戳值
// if (value != null)
// {
// Type tupleType = value.GetType();
// var tempValue = tupleType.Name.ToUpper() switch
// {
// "DATETIME" => Convert.ToDateTime(value).GetDateTimeOffset().ToUnixTimeNanoseconds(),
// _ => value
// };
// rowValues.Add(tempValue);
// }
// else
// {
// rowValues.Add(value);
// }
// }
//}
values.Add(rowValues);
//如果指定了路径
if (!string.IsNullOrWhiteSpace(tableNameOrTreePath))

View File

@ -90,7 +90,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData
DeviceType = $"{data.MeterType}",
ProjectId = $"{data.ProjectId}",
Timestamps = data.TimeSpan!.Value.GetFormatTime(analysisBaseDto.DensityUnit, analysisBaseDto.TimeDensity).GetDateTimeOffset().ToUnixTimeMilliseconds(),
SingleMeasuring = new Tuple<string, T>(data.FiledName ?? string.Empty, data.DataValue ?? default)
SingleMeasuring = (data.FiledName ?? string.Empty, data.DataValue ?? default)
};
_runtimeContext.UseTableSessionPool = true; // 使用表模型池
var taskSendInfo = await _dbProvider.QueryAsync<MeterReadingTelemetryPacketInfo>(new IoTDBQueryOptions() { TableNameOrTreePath = DevicePathBuilder.GetTableName<MeterReadingTelemetryPacketInfo>(), Conditions = conditions, PageIndex = 0, PageSize = 1 });
@ -193,7 +193,7 @@ namespace JiShe.CollectBus.Protocol.T37612012.AnalysisData
DeviceType = $"{item.MeterType}",
ProjectId = $"{item.ProjectId}",
Timestamps = item.TimeSpan!.Value.GetFormatTime(analysisBaseDto.DensityUnit, analysisBaseDto.TimeDensity).GetDateTimeOffset().ToUnixTimeMilliseconds(), // TODO:这里暂时格式化15分钟数据需要进行调整
SingleMeasuring = new Tuple<string, T>(item.FiledName ?? string.Empty, item.DataValue ?? default)
SingleMeasuring = (item.FiledName ?? string.Empty, item.DataValue ?? default)
};
_runtimeContext.UseTableSessionPool = true; // 使用表模型池
var taskSendInfo = await _dbProvider.QueryAsync<MeterReadingTelemetryPacketInfo>(new IoTDBQueryOptions() { TableNameOrTreePath = DevicePathBuilder.GetTableName<MeterReadingTelemetryPacketInfo>(), Conditions = conditions, PageIndex = 0, PageSize = 1 });

View File

@ -73,7 +73,9 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
//ElectricityMeterTreeModelExtension.GetCurrent()
//SourceEntityAccessorFactory.SetCurrent(meter);
ElectricityMeterTreeModelAccessor
//ElectricityMeterTreeModelAccessor.
//TableModelSingleMeasuringEntityExtension
//TableModelSingleMeasuringEntityAccessor.GetSystemName(meter);
await _iotDBProvider.InsertAsync(meter);
}
@ -172,6 +174,8 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
[HttpGet]
public async Task TestTreeModelSingleMeasuringEntity(string measuring, string value, DateTime time)
{
time = DateTime.Now;
var meter = new TreeModelSingleMeasuringEntity<string>()
{
SystemName = "energy",
@ -179,7 +183,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
DeviceType = "1",
ProjectId = "10059",
Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(),
SingleMeasuring = new Tuple<string, string>(measuring, value)
SingleMeasuring = (measuring, value)
};
await _iotDBProvider.InsertAsync(meter);
}
@ -192,6 +196,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
[HttpGet]
public async Task TestTreeModelSingleMeasuringEntity2(string measuring, int value, DateTime time)
{
time = DateTime.Now;
var meter = new TreeModelSingleMeasuringEntity<int>()
{
SystemName = "energy",
@ -199,7 +204,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
DeviceType = "Ammeter",
ProjectId = "10059",
Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(),
SingleMeasuring = new Tuple<string, int>(measuring, value)
SingleMeasuring = (measuring, value)
};
await _iotDBProvider.InsertAsync(meter);
}
@ -212,6 +217,8 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
[HttpGet]
public async Task TestTableModelSingleMeasuringEntity(string measuring, string value, DateTime time)
{
time = DateTime.Now;
var meter = new TableModelSingleMeasuringEntity<string>()
{
SystemName = "energy",
@ -219,7 +226,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
DeviceType = "Ammeter",
ProjectId = "10059",
Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(),
SingleColumn = new Tuple<string, string>(measuring, value)
SingleColumn = (measuring, value)
};
_dbContext.UseTableSessionPool = true;
await _iotDBProvider.InsertAsync(meter);
@ -233,6 +240,8 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
[HttpGet]
public async Task TestTableModelSingleMeasuringEntity2(string measuring, int value, DateTime time)
{
time = DateTime.Now;
var meter = new TableModelSingleMeasuringEntity<int>()
{
SystemName = "energy",
@ -240,7 +249,7 @@ public class SampleAppService : CollectBusAppService, ISampleAppService, IKafkaS
DeviceType = "Ammeter",
ProjectId = "10059",
Timestamps = time.GetDateTimeOffset().ToUnixTimeMilliseconds(),
SingleColumn = new Tuple<string, int>(measuring, value)
SingleColumn = (measuring, value)
};
_dbContext.UseTableSessionPool = true;
await _iotDBProvider.InsertAsync(meter);

View File

@ -336,47 +336,47 @@ namespace JiShe.CollectBus.ScheduledMeterReading
//此处代码不要删除
#if DEBUG
var timeDensity = "15";
var serverTagName = "JiSheCollectBus2";
var redisCacheMeterInfoHashKeyTemp = $"{string.Format(RedisConst.CacheMeterInfoHashKey, SystemType, serverTagName, MeterTypeEnum.Ammeter, timeDensity)}";
var redisCacheMeterInfoSetIndexKeyTemp = $"{string.Format(RedisConst.CacheMeterInfoSetIndexKey, SystemType, serverTagName, MeterTypeEnum.Ammeter, timeDensity)}";
var redisCacheMeterInfoZSetScoresIndexKeyTemp = $"{string.Format(RedisConst.CacheMeterInfoZSetScoresIndexKey, SystemType, serverTagName, MeterTypeEnum.Ammeter, timeDensity)}";
//var timeDensity = "15";
//var serverTagName = "JiSheCollectBus2";
//var redisCacheMeterInfoHashKeyTemp = $"{string.Format(RedisConst.CacheMeterInfoHashKey, SystemType, serverTagName, MeterTypeEnum.Ammeter, timeDensity)}";
//var redisCacheMeterInfoSetIndexKeyTemp = $"{string.Format(RedisConst.CacheMeterInfoSetIndexKey, SystemType, serverTagName, MeterTypeEnum.Ammeter, timeDensity)}";
//var redisCacheMeterInfoZSetScoresIndexKeyTemp = $"{string.Format(RedisConst.CacheMeterInfoZSetScoresIndexKey, SystemType, serverTagName, MeterTypeEnum.Ammeter, timeDensity)}";
List<AmmeterInfo> meterInfos = new List<AmmeterInfo>();
List<string> focusAddressDataLista = new List<string>();
var timer1 = Stopwatch.StartNew();
//List<string> focusAddressDataLista = new List<string>();
//var timer1 = Stopwatch.StartNew();
var allIds = new HashSet<string>();
decimal? score = null;
string member = null;
//var allIds = new HashSet<string>();
//decimal? score = null;
//string member = null;
while (true)
{
var page = await _redisDataCacheService.GetAllPagedData<AmmeterInfo>(
redisCacheMeterInfoHashKeyTemp,
redisCacheMeterInfoZSetScoresIndexKeyTemp,
pageSize: 1000,
lastScore: score,
lastMember: member);
//while (true)
//{
// var page = await _redisDataCacheService.GetAllPagedData<AmmeterInfo>(
// redisCacheMeterInfoHashKeyTemp,
// redisCacheMeterInfoZSetScoresIndexKeyTemp,
// pageSize: 1000,
// lastScore: score,
// lastMember: member);
meterInfos.AddRange(page.Items);
focusAddressDataLista.AddRange(page.Items.Select(d => $"{d.MeterId}"));
foreach (var item in page.Items)
{
if (!allIds.Add(item.MemberId))
{
_logger.LogError($"{item.MemberId}Duplicate data found!");
}
}
if (!page.HasNext) break;
score = page.NextScore;
member = page.NextMember;
}
// meterInfos.AddRange(page.Items);
// focusAddressDataLista.AddRange(page.Items.Select(d => $"{d.MeterId}"));
// foreach (var item in page.Items)
// {
// if (!allIds.Add(item.MemberId))
// {
// _logger.LogError($"{item.MemberId}Duplicate data found!");
// }
// }
// if (!page.HasNext) break;
// score = page.NextScore;
// member = page.NextMember;
//}
timer1.Stop();
_logger.LogError($"电表初始化读取数据总共花费时间{timer1.ElapsedMilliseconds}毫秒");
DeviceGroupBalanceControl.InitializeCache(focusAddressDataLista, _kafkaOptions.NumPartitions);
//timer1.Stop();
//_logger.LogError($"电表初始化读取数据总共花费时间{timer1.ElapsedMilliseconds}毫秒");
//DeviceGroupBalanceControl.InitializeCache(focusAddressDataLista, _kafkaOptions.NumPartitions);
return;
#else
var meterInfos = await GetAmmeterInfoList(gatherCode);

View File

@ -6,7 +6,7 @@ using JiShe.CollectBus.IoTDB.Model;
namespace JiShe.CollectBus.Ammeters
{
[EntityType(EntityTypeEnum.TableModel)]
[SourceAnalyzers]
//[SourceAnalyzers]
public class ElectricityMeter : IoTEntity
{
[ATTRIBUTEColumn]

View File

@ -2,6 +2,7 @@
using JiShe.CollectBus.IoTDB.Attribute;
using JiShe.CollectBus.IoTDB.Enums;
using JiShe.CollectBus.IoTDB.Model;
using System;
namespace JiShe.CollectBus.Ammeters
{
@ -35,5 +36,7 @@ namespace JiShe.CollectBus.Ammeters
[FIELDColumn]
public double? Currentd { get; set; }
public Tuple<int,string> TupleData { get; set;}
}
}

View File

@ -22,19 +22,9 @@ namespace JiShe.CollectBus.Analyzers.Shared
/// <param name="value"></param>
void SetPropertyValue(T entity, string propertyName, object value);
///// <summary>
///// 判断是否是元组属性
///// </summary>
///// <param name="propertyName"></param>
///// <returns></returns>
//bool IsTupleProperty(string propertyName);
///// <summary>
///// 获取元组属性值
///// </summary>
///// <param name="entity"></param>
///// <param name="tuplePropertyName"></param>
///// <returns></returns>
//(object Item1, object Item2) GetTupleParts(T entity, string tuplePropertyName);
/// <summary>
/// 属性名称集合
/// </summary>
List<string> PropertyList { get; }
}
}