diff --git a/JiShe.CollectBus.sln b/JiShe.CollectBus.sln
index 674eca4..0a7a6e7 100644
--- a/JiShe.CollectBus.sln
+++ b/JiShe.CollectBus.sln
@@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JiShe.CollectBus.FreeSql",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.FreeRedisProvider", "src\JiShe.CollectBus.FreeRedisProvider\JiShe.CollectBus.FreeRedisProvider.csproj", "{C06C4082-638F-2996-5FED-7784475766C1}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JiShe.CollectBus.IoTDBProvider", "src\JiShe.CollectBus.IoTDBProvider\JiShe.CollectBus.IoTDBProvider.csproj", "{A3F3C092-0A25-450B-BF6A-5983163CBEF5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -89,6 +91,10 @@ Global
{C06C4082-638F-2996-5FED-7784475766C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C06C4082-638F-2996-5FED-7784475766C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C06C4082-638F-2996-5FED-7784475766C1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -107,6 +113,7 @@ Global
{8BA01C3D-297D-42DF-BD63-EF07202A0A67} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
{FE0457D9-4038-4A17-8808-DCAD06CFC0A0} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
{C06C4082-638F-2996-5FED-7784475766C1} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
+ {A3F3C092-0A25-450B-BF6A-5983163CBEF5} = {649A3FFA-182F-4E56-9717-E6A9A2BEC545}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD}
diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs
new file mode 100644
index 0000000..42820bc
--- /dev/null
+++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/ATTRIBUTEColumnAttribute.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiShe.CollectBus.IoTDBProvider
+{
+ ///
+ /// Column分类标记特性(TAG字段)
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class TAGColumnAttribute : Attribute
+ {
+ }
+}
diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs
new file mode 100644
index 0000000..2c48a65
--- /dev/null
+++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/FIELDColumnAttribute.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiShe.CollectBus.IoTDBProvider
+{
+ ///
+ /// Column分类标记特性(FIELD字段)
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class FIELDColumnAttribute : Attribute
+ {
+ }
+}
diff --git a/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs b/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs
new file mode 100644
index 0000000..28f6a46
--- /dev/null
+++ b/src/JiShe.CollectBus.IoTDBProvider/Attribute/TAGColumnAttribute.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiShe.CollectBus.IoTDBProvider
+{
+ ///
+ /// Column分类标记特性(ATTRIBUTE字段)
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public class ATTRIBUTEColumnAttribute : Attribute
+ {
+ }
+}
diff --git a/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs b/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs
new file mode 100644
index 0000000..5a8902c
--- /dev/null
+++ b/src/JiShe.CollectBus.IoTDBProvider/CollectBusIoTDBModule.cs
@@ -0,0 +1,19 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Volo.Abp.Modularity;
+
+namespace JiShe.CollectBus.IoTDBProvider
+{
+ public class CollectBusIoTDBModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.Configure(context.Services.GetConfiguration().GetSection(nameof(IoTDBOptions)));
+ context.Services.AddSingleton();
+ }
+ }
+}
diff --git a/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs b/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs
new file mode 100644
index 0000000..dea65fb
--- /dev/null
+++ b/src/JiShe.CollectBus.IoTDBProvider/DeviceMetadata.cs
@@ -0,0 +1,24 @@
+using Apache.IoTDB;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiShe.CollectBus.IoTDBProvider
+{
+ ///
+ /// 设备元数据
+ ///
+ public class DeviceMetadata
+ {
+ public List Measurements { get; } = new();
+ public List Tags { get; } = new();
+
+ public List GetDataTypes()
+ {
+ // 根据实际类型映射TSDataType
+ return Measurements.Select(_ => TSDataType.TEXT).ToList();
+ }
+ }
+}
diff --git a/src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs b/src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs
new file mode 100644
index 0000000..ef6d7e4
--- /dev/null
+++ b/src/JiShe.CollectBus.IoTDBProvider/DevicePathBuilder.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiShe.CollectBus.IoTDBProvider
+{
+ ///
+ /// 设备路径构建器
+ ///
+ public static class DevicePathBuilder
+ {
+ ///
+ /// 构建存储组路径
+ ///
+ ///
+ ///
+ public static string BuildStorageGroupPath() where T : IoTEntity
+ {
+ var type = typeof(T);
+ return $"root.{type.GetProperty("SystemName")?.Name}.{type.GetProperty("ProjectCode")?.Name}";
+ }
+
+ ///
+ /// 构建设备路径
+ ///
+ ///
+ ///
+ ///
+ public static string BuildDevicePath(T entity) where T : IoTEntity
+ {
+ return $"root.{entity.SystemName}.{entity.ProjectCode}.{entity.DeviceId}";
+ }
+ }
+
+}
diff --git a/src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs
new file mode 100644
index 0000000..026a95a
--- /dev/null
+++ b/src/JiShe.CollectBus.IoTDBProvider/IIoTDBProvider.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiShe.CollectBus.IoTDBProvider
+{
+ ///
+ /// IoTDB数据源
+ ///
+ public interface IIoTDBProvider
+ {
+ ///
+ /// 插入数据
+ ///
+ ///
+ ///
+ ///
+ Task InsertAsync(T entity) where T : IoTEntity;
+
+ ///
+ /// 批量插入数据
+ ///
+ ///
+ ///
+ ///
+ Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity;
+
+ ///
+ /// 查询数据
+ ///
+ ///
+ ///
+ ///
+ Task> QueryAsync(QueryOptions options) where T : IoTEntity, new();
+ }
+}
diff --git a/src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs b/src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs
new file mode 100644
index 0000000..f2a5546
--- /dev/null
+++ b/src/JiShe.CollectBus.IoTDBProvider/IoTDBProvider.cs
@@ -0,0 +1,262 @@
+using Apache.IoTDB;
+using Apache.IoTDB.DataStructure;
+using Microsoft.Extensions.Options;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace JiShe.CollectBus.IoTDBProvider
+{
+ ///
+ /// IoTDB数据源
+ ///
+ public class IoTDBProvider : IIoTDBProvider, IDisposable
+ {
+ private readonly IoTDBOptions _options;
+ private readonly SessionPool _sessionPool;
+ private static readonly ConcurrentDictionary _metadataCache = new();
+
+ public IoTDBProvider(IOptions options)
+ {
+ _options = options.Value;
+ _sessionPool = new SessionPool(
+ _options.ClusterList,
+ _options.UserName,
+ _options.Password,
+ _options.PoolSize);
+ _sessionPool.Open(false).Wait();
+ }
+
+ ///
+ /// 获取设备元数据
+ ///
+ ///
+ ///
+ private DeviceMetadata GetMetadata() where T : IoTEntity
+ {
+ return _metadataCache.GetOrAdd(typeof(T), type =>
+ {
+ var metadata = new DeviceMetadata();
+ foreach (var prop in type.GetProperties())
+ {
+ var attr = prop.GetCustomAttribute();
+ if (attr != null)
+ {
+ metadata.Tags.Add(prop.Name);
+ }
+ else if (prop.Name != nameof(IoTEntity.Timestamp))
+ {
+ metadata.Measurements.Add(prop.Name);
+ }
+ }
+ return metadata;
+ });
+ }
+
+ ///
+ /// 插入数据
+ ///
+ ///
+ ///
+ ///
+ public async Task InsertAsync(T entity) where T : IoTEntity
+ {
+ var metadata = GetMetadata();
+ var storageGroup = DevicePathBuilder.BuildStorageGroupPath();
+ await EnsureStorageGroupCreated(storageGroup);
+
+ var tablet = BuildTablet(new[] { entity }, metadata);
+ await _sessionPool.InsertAlignedTabletAsync(tablet);
+ }
+
+ ///
+ /// 批量插入数据
+ ///
+ ///
+ ///
+ ///
+ public async Task BatchInsertAsync(IEnumerable entities) where T : IoTEntity
+ {
+ var metadata = GetMetadata();
+ var storageGroup = DevicePathBuilder.BuildStorageGroupPath();
+ await EnsureStorageGroupCreated(storageGroup);
+
+ var batchSize = 1000;
+ var batches = entities.Chunk(batchSize);
+
+ foreach (var batch in batches)
+ {
+ var tablet = BuildTablet(batch, metadata);
+ await _sessionPool.InsertAlignedTabletAsync(tablet);
+ }
+ }
+
+ ///
+ /// 构建表模型
+ ///
+ ///
+ ///
+ ///
+ ///
+ private Tablet BuildTablet(IEnumerable entities, DeviceMetadata metadata) where T : IoTEntity
+ {
+ var devicePath = DevicePathBuilder.BuildDevicePath(entities.First());
+ var timestamps = new List();
+ var values = new List>();
+
+ foreach (var entity in entities)
+ {
+ timestamps.Add(entity.Timestamp);
+ var rowValues = new List