mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-11-05 10:03:58 +08:00
增加时序库存储插件;
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
global using ThingsGateway.Foundation.Core;
|
||||
global using ThingsGateway.Gateway.Application;
|
||||
global using ThingsGateway.Gateway.Core;
|
||||
325
framework/Plugin/ThingsGateway.Plugin.QuestDB/QuestDB.cs
Normal file
325
framework/Plugin/ThingsGateway.Plugin.QuestDB/QuestDB.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using Furion;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
|
||||
namespace ThingsGateway.Plugin.QuestDB;
|
||||
public class QuestDB : UpLoadBase
|
||||
{
|
||||
private readonly ConcurrentQueue<QuestDBHistoryValue> DeviceVariableRunTimes = new();
|
||||
private readonly QuestDBProperty driverPropertys = new();
|
||||
private readonly QuestDBVariableProperty variablePropertys = new();
|
||||
private TypeAdapterConfig _config;
|
||||
private GlobalDeviceData _globalDeviceData;
|
||||
|
||||
private List<DeviceVariableRunTime> _uploadVariables = new();
|
||||
private TimerTick exTimerTick;
|
||||
public QuestDB()
|
||||
{
|
||||
_config = new TypeAdapterConfig();
|
||||
_config.ForType<DeviceVariableRunTime, HistoryValue>()
|
||||
.Map(dest => dest.Value, (src) => ValueReturn(src))
|
||||
.Map(dest => dest.CollectTime, (src) => src.CollectTime.ToUniversalTime())//注意sqlsugar插入时无时区,直接utc时间
|
||||
.Map(dest => dest.CreateTime, (src) => DateTime.UtcNow);//注意sqlsugar插入时无时区,直接utc时间
|
||||
}
|
||||
private static object ValueReturn(DeviceVariableRunTime src)
|
||||
{
|
||||
if (src.Value?.ToString()?.IsBoolValue() == true)
|
||||
{
|
||||
if (src.Value.ToBoolean())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return src.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public override Type DriverDebugUIType => null;
|
||||
public override UpDriverPropertyBase DriverPropertys => driverPropertys;
|
||||
public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables;
|
||||
|
||||
public override VariablePropertyBase VariablePropertys => variablePropertys;
|
||||
|
||||
public override Task AfterStopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task BeforStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
SqlSugarClient db = GetHisDbAsync();
|
||||
db.DbMaintenance.CreateDatabase();
|
||||
db.CodeFirst.InitTables(typeof(QuestDBHistoryValue));
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var db = GetHisDbAsync();
|
||||
{
|
||||
if (!driverPropertys.IsInterval)
|
||||
{
|
||||
try
|
||||
{
|
||||
////变化推送
|
||||
var varList = DeviceVariableRunTimes.ToListWithDequeue();
|
||||
if (varList?.Count != 0)
|
||||
{
|
||||
await InserableAsync(db, varList, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, ToString());
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exTimerTick.IsTickHappen())
|
||||
{
|
||||
try
|
||||
{
|
||||
var varList = _uploadVariables.ToList().Adapt<List<QuestDBHistoryValue>>(_config);
|
||||
if (varList?.Count != 0)
|
||||
{
|
||||
await InserableAsync(db, varList, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override bool IsConnected() => _uploadVariables?.Count > 0;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
_globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
|
||||
_uploadVariables = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage.LogError(ex, ToString());
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected override void Init(UploadDeviceRunTime device)
|
||||
{
|
||||
|
||||
_globalDeviceData = App.GetService<GlobalDeviceData>();
|
||||
|
||||
var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id))
|
||||
.Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean())
|
||||
.ToList();
|
||||
|
||||
_uploadVariables = tags;
|
||||
|
||||
if (!driverPropertys.IsInterval)
|
||||
{
|
||||
_uploadVariables.ForEach(a =>
|
||||
{
|
||||
a.VariableValueChange += VariableValueChange;
|
||||
});
|
||||
}
|
||||
|
||||
if (_uploadVariables.Count == 0)
|
||||
{
|
||||
LogMessage.LogWarning("插件变量数量为0");
|
||||
}
|
||||
if (driverPropertys.IntervalTime < 1)
|
||||
driverPropertys.IntervalTime = 10;
|
||||
exTimerTick = new(driverPropertys.IntervalTime * 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aop设置
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
private static void AopSetting(SqlSugarClient db)
|
||||
{
|
||||
var config = db.CurrentConnectionConfig;
|
||||
|
||||
// 设置超时时间
|
||||
db.Ado.CommandTimeOut = 30;
|
||||
|
||||
// 打印SQL语句
|
||||
db.Aop.OnLogExecuting = (sql, pars) =>
|
||||
{
|
||||
//如果不是开发环境就打印sql
|
||||
if (App.HostEnvironment.IsDevelopment())
|
||||
{
|
||||
if (sql.StartsWith("SELECT"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
}
|
||||
if (sql.StartsWith("UPDATE"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
}
|
||||
if (sql.StartsWith("INSERT"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
}
|
||||
if (sql.StartsWith("DELETE"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
}
|
||||
WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars));
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine();
|
||||
}
|
||||
};
|
||||
//异常
|
||||
db.Aop.OnError = (ex) =>
|
||||
{
|
||||
//如果不是开发环境就打印日志
|
||||
if (App.WebHostEnvironment.IsDevelopment())
|
||||
{
|
||||
if (ex.Parametres == null) return;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
|
||||
WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres));
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private static void WriteSqlLog(string msg)
|
||||
{
|
||||
Console.WriteLine("【Sql执行时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
|
||||
Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine);
|
||||
}
|
||||
|
||||
private static void WriteSqlLogError(string msg)
|
||||
{
|
||||
Console.WriteLine("【Sql执行错误时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
|
||||
Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库链接
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private SqlSugarClient GetHisDbAsync()
|
||||
{
|
||||
var configureExternalServices = new ConfigureExternalServices
|
||||
{
|
||||
EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required
|
||||
{
|
||||
if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
|| (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null))
|
||||
column.IsNullable = true;
|
||||
},
|
||||
};
|
||||
var sqlSugarClient = new SqlSugarClient(new ConnectionConfig()
|
||||
{
|
||||
ConnectionString = driverPropertys.ConnectStr,//连接字符串
|
||||
DbType = DbType.QuestDB,//数据库类型
|
||||
IsAutoCloseConnection = true, //不设成true要手动close
|
||||
ConfigureExternalServices = configureExternalServices,
|
||||
}
|
||||
);
|
||||
AopSetting(sqlSugarClient);//aop配置
|
||||
return sqlSugarClient;
|
||||
}
|
||||
|
||||
private async Task InserableAsync(SqlSugarClient db, List<QuestDBHistoryValue> dbInserts, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await db.Insertable(dbInserts).ExecuteCommandAsync(cancellationToken);
|
||||
if (result > 0)
|
||||
LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString());
|
||||
//连接成功时补发缓存数据
|
||||
var cacheData = await CacheDb.GetCacheData();
|
||||
foreach (var item in cacheData)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = item.CacheStr.FromJsonString<List<QuestDBHistoryValue>>();
|
||||
var cacheresult = await db.Insertable(data).ExecuteCommandAsync(cancellationToken);
|
||||
if (cacheresult > 0)
|
||||
{
|
||||
await CacheDb.DeleteCacheData(item.Id);
|
||||
LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage.LogWarning(ex, ToString());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage.LogWarning(ex, ToString());
|
||||
await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime)
|
||||
{
|
||||
if (!driverPropertys.IsInterval)
|
||||
DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<QuestDBHistoryValue>(_config));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Gateway.Core;
|
||||
/// <summary>
|
||||
/// 历史数据表
|
||||
/// </summary>
|
||||
[SugarTable("historyValue", TableDescription = "历史数据表")]
|
||||
public class QuestDBHistoryValue
|
||||
{
|
||||
/// <summary>
|
||||
/// 采集时间
|
||||
/// </summary>
|
||||
[TimeDbSplitField(DateType.Month)]
|
||||
[Description("采集时间")]
|
||||
public DateTime CollectTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// </summary>
|
||||
[Description("上传时间")]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "symbol")]
|
||||
[Description("设备名称")]
|
||||
public string DeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "symbol")]
|
||||
[Description("变量名称")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
[Description("是否在线")]
|
||||
public bool IsOnline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量值
|
||||
/// </summary>
|
||||
[Description("变量值")]
|
||||
public double Value { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Plugin.QuestDB;
|
||||
|
||||
public class QuestDBProperty : UpDriverPropertyBase
|
||||
{
|
||||
[DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "host=localhost;port=8812;username=admin;password=quest;database=qdb;ServerCompatibilityMode=NoTypeLoading;";
|
||||
[DeviceProperty("是否间隔插入", "False时将每次变化写入")] public bool IsInterval { get; set; } = true;
|
||||
[DeviceProperty("间隔时间", "秒,实时表时代表更新间隔,历史表时代表插入间隔")] public int IntervalTime { get; set; } = 10;
|
||||
[DeviceProperty("缓存最大条数", "默认2千条")] public int CacheMaxCount { get; set; } = 2000;
|
||||
|
||||
/// <summary>
|
||||
/// 线程循环间隔
|
||||
/// </summary>
|
||||
[DeviceProperty("线程循环间隔", "最小10ms")]
|
||||
public int CycleInterval { get; set; } = 1000;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Plugin.QuestDB;
|
||||
|
||||
public class QuestDBVariableProperty : VariablePropertyBase
|
||||
{
|
||||
[VariableProperty("启用", "")]
|
||||
public bool Enable { get; set; } = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
||||
<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25 md %25dir%25 
copy "$(TargetDir)*QuestDB*.dll" %25dir%25



" />
|
||||
|
||||
</Target>
|
||||
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,15 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
global using ThingsGateway.Foundation.Core;
|
||||
global using ThingsGateway.Gateway.Application;
|
||||
global using ThingsGateway.Gateway.Core;
|
||||
@@ -0,0 +1,62 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
using SqlSugar;
|
||||
using SqlSugar.TDengine;
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ThingsGateway.Gateway.Core;
|
||||
/// <summary>
|
||||
/// 历史数据表
|
||||
/// </summary>
|
||||
[SugarTable("historyValue")]
|
||||
public class TDHistoryValue : STable
|
||||
{
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// </summary>
|
||||
[SugarColumn(InsertServerTime = true)]
|
||||
[Description("上传时间")]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 采集时间
|
||||
/// </summary>
|
||||
[Description("采集时间")]
|
||||
public DateTime CollectTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[Description("设备名称")]
|
||||
public string DeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量名称
|
||||
/// </summary>
|
||||
[Description("变量名称")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
[Description("是否在线")]
|
||||
public bool IsOnline { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量值
|
||||
/// </summary>
|
||||
[Description("变量值")]
|
||||
[SugarColumn(Length = 18, DecimalDigits = 2)]
|
||||
public double Value { get; set; }
|
||||
}
|
||||
325
framework/Plugin/ThingsGateway.Plugin.TDengineDB/TDengineDB.cs
Normal file
325
framework/Plugin/ThingsGateway.Plugin.TDengineDB/TDengineDB.cs
Normal file
@@ -0,0 +1,325 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
|
||||
using Furion;
|
||||
|
||||
using Mapster;
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
|
||||
using ThingsGateway.Foundation.Extension;
|
||||
using ThingsGateway.Foundation.Extension.ConcurrentQueue;
|
||||
using ThingsGateway.Foundation.Extension.String;
|
||||
|
||||
namespace ThingsGateway.Plugin.SQLDB;
|
||||
public class TDengineDB : UpLoadBase
|
||||
{
|
||||
private readonly ConcurrentQueue<TDHistoryValue> DeviceVariableRunTimes = new();
|
||||
private readonly TDengineDBProperty driverPropertys = new();
|
||||
private readonly TDengineDBVariableProperty variablePropertys = new();
|
||||
private GlobalDeviceData _globalDeviceData;
|
||||
private TypeAdapterConfig _config;
|
||||
|
||||
private List<DeviceVariableRunTime> _uploadVariables = new();
|
||||
private TimerTick exTimerTick;
|
||||
public TDengineDB()
|
||||
{
|
||||
_config = new TypeAdapterConfig();
|
||||
_config.ForType<DeviceVariableRunTime, HistoryValue>()
|
||||
.Map(dest => dest.Value, (src) => ValueReturn(src));
|
||||
}
|
||||
private static object ValueReturn(DeviceVariableRunTime src)
|
||||
{
|
||||
if (src.Value?.ToString()?.IsBoolValue() == true)
|
||||
{
|
||||
if (src.Value.ToBoolean())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return src.Value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override Type DriverDebugUIType => null;
|
||||
public override UpDriverPropertyBase DriverPropertys => driverPropertys;
|
||||
public override List<DeviceVariableRunTime> UploadVariables => _uploadVariables;
|
||||
|
||||
public override VariablePropertyBase VariablePropertys => variablePropertys;
|
||||
|
||||
public override Task AfterStopAsync()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task BeforStartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
SqlSugarClient db = GetHisDbAsync();
|
||||
db.DbMaintenance.CreateDatabase();
|
||||
db.CodeFirst.InitTables(typeof(TDHistoryValue));
|
||||
await Task.CompletedTask;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var db = GetHisDbAsync();
|
||||
{
|
||||
if (!driverPropertys.IsInterval)
|
||||
{
|
||||
try
|
||||
{
|
||||
////变化推送
|
||||
var varList = DeviceVariableRunTimes.ToListWithDequeue();
|
||||
if (varList?.Count != 0)
|
||||
{
|
||||
await InserableAsync(db, varList, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, ToString());
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (exTimerTick.IsTickHappen())
|
||||
{
|
||||
try
|
||||
{
|
||||
var varList = _uploadVariables.ToList().Adapt<List<TDHistoryValue>>(_config);
|
||||
if (varList?.Count != 0)
|
||||
{
|
||||
await InserableAsync(db, varList, cancellationToken);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage?.LogWarning(ex, ToString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (driverPropertys.CycleInterval > UploadDeviceThread.CycleInterval + 50)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(driverPropertys.CycleInterval - UploadDeviceThread.CycleInterval, cancellationToken);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override bool IsConnected() => _uploadVariables?.Count > 0;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
try
|
||||
{
|
||||
_globalDeviceData?.AllVariables?.ForEach(a => a.VariableValueChange -= VariableValueChange);
|
||||
_uploadVariables = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage.LogError(ex, ToString());
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
protected override void Init(UploadDeviceRunTime device)
|
||||
{
|
||||
|
||||
_globalDeviceData = App.GetService<GlobalDeviceData>();
|
||||
|
||||
var tags = _globalDeviceData.AllVariables.Where(a => a.VariablePropertys.ContainsKey(device.Id))
|
||||
.Where(b => GetPropertyValue(b, nameof(variablePropertys.Enable)).ToBoolean())
|
||||
.ToList();
|
||||
|
||||
_uploadVariables = tags;
|
||||
|
||||
if (!driverPropertys.IsInterval)
|
||||
{
|
||||
_uploadVariables.ForEach(a =>
|
||||
{
|
||||
a.VariableValueChange += VariableValueChange;
|
||||
});
|
||||
}
|
||||
|
||||
if (_uploadVariables.Count == 0)
|
||||
{
|
||||
LogMessage.LogWarning("插件变量数量为0");
|
||||
}
|
||||
if (driverPropertys.IntervalTime < 1)
|
||||
driverPropertys.IntervalTime = 10;
|
||||
exTimerTick = new(driverPropertys.IntervalTime * 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aop设置
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
private static void AopSetting(SqlSugarClient db)
|
||||
{
|
||||
var config = db.CurrentConnectionConfig;
|
||||
|
||||
// 设置超时时间
|
||||
db.Ado.CommandTimeOut = 30;
|
||||
|
||||
// 打印SQL语句
|
||||
db.Aop.OnLogExecuting = (sql, pars) =>
|
||||
{
|
||||
//如果不是开发环境就打印sql
|
||||
if (App.HostEnvironment.IsDevelopment())
|
||||
{
|
||||
if (sql.StartsWith("SELECT"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
}
|
||||
if (sql.StartsWith("UPDATE"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
}
|
||||
if (sql.StartsWith("INSERT"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
}
|
||||
if (sql.StartsWith("DELETE"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
}
|
||||
WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars));
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine();
|
||||
}
|
||||
};
|
||||
//异常
|
||||
db.Aop.OnError = (ex) =>
|
||||
{
|
||||
//如果不是开发环境就打印日志
|
||||
if (App.WebHostEnvironment.IsDevelopment())
|
||||
{
|
||||
if (ex.Parametres == null) return;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
|
||||
WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres));
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private static void WriteSqlLog(string msg)
|
||||
{
|
||||
Console.WriteLine("【Sql执行时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
|
||||
Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine);
|
||||
}
|
||||
|
||||
private static void WriteSqlLogError(string msg)
|
||||
{
|
||||
Console.WriteLine("【Sql执行错误时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
|
||||
Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取数据库链接
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private SqlSugarClient GetHisDbAsync()
|
||||
{
|
||||
var configureExternalServices = new ConfigureExternalServices
|
||||
{
|
||||
EntityService = (type, column) => // 修改列可空-1、带?问号 2、String类型若没有Required
|
||||
{
|
||||
if ((type.PropertyType.IsGenericType && type.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
|| (type.PropertyType == typeof(string) && type.GetCustomAttribute<RequiredAttribute>() == null))
|
||||
column.IsNullable = true;
|
||||
},
|
||||
};
|
||||
var sqlSugarClient = new SqlSugarClient(new ConnectionConfig()
|
||||
{
|
||||
ConnectionString = driverPropertys.ConnectStr,//连接字符串
|
||||
DbType = driverPropertys.DbType,//数据库类型
|
||||
IsAutoCloseConnection = true, //不设成true要手动close
|
||||
ConfigureExternalServices = configureExternalServices,
|
||||
}
|
||||
);
|
||||
AopSetting(sqlSugarClient);//aop配置
|
||||
return sqlSugarClient;
|
||||
}
|
||||
|
||||
private async Task InserableAsync(SqlSugarClient db, List<TDHistoryValue> dbInserts, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await db.Insertable(dbInserts).ExecuteCommandAsync(cancellationToken);
|
||||
if (result > 0)
|
||||
LogMessage.Trace(FoundationConst.LogMessageHeader + dbInserts.ToJsonString());
|
||||
//连接成功时补发缓存数据
|
||||
var cacheData = await CacheDb.GetCacheData();
|
||||
foreach (var item in cacheData)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = item.CacheStr.FromJsonString<List<TDHistoryValue>>();
|
||||
var cacheresult = await db.Insertable(data).ExecuteCommandAsync(cancellationToken);
|
||||
if (cacheresult > 0)
|
||||
{
|
||||
await CacheDb.DeleteCacheData(item.Id);
|
||||
LogMessage.Trace(FoundationConst.LogMessageHeader + $"主题:{item.Topic}{Environment.NewLine}负载:{item.CacheStr}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage.LogWarning(ex, ToString());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogMessage.LogWarning(ex, ToString());
|
||||
await CacheDb.AddCacheData("", dbInserts.ToJsonString(), driverPropertys.CacheMaxCount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void VariableValueChange(DeviceVariableRunTime collectVariableRunTime)
|
||||
{
|
||||
if (!driverPropertys.IsInterval)
|
||||
DeviceVariableRunTimes.Enqueue(collectVariableRunTime.Adapt<TDHistoryValue>(_config));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
|
||||
using SqlSugar;
|
||||
|
||||
namespace ThingsGateway.Plugin.SQLDB;
|
||||
|
||||
public class TDengineDBProperty : UpDriverPropertyBase
|
||||
{
|
||||
[DeviceProperty("数据库类型", "QuestDB/TDengine,主要TD需要手动建库,并指定超级表")] public DbType DbType { get; set; } = DbType.TDengine;
|
||||
[DeviceProperty("链接字符串", "")] public string ConnectStr { get; set; } = "Host=localhost;Port=6030;Username=root;Password=taosdata;Database=test";
|
||||
[DeviceProperty("是否间隔插入", "False时将每次变化写入")] public bool IsInterval { get; set; } = true;
|
||||
[DeviceProperty("间隔时间", "秒,实时表时代表更新间隔,历史表时代表插入间隔")] public int IntervalTime { get; set; } = 10;
|
||||
[DeviceProperty("缓存最大条数", "默认2千条")] public int CacheMaxCount { get; set; } = 2000;
|
||||
|
||||
/// <summary>
|
||||
/// 线程循环间隔
|
||||
/// </summary>
|
||||
[DeviceProperty("线程循环间隔", "最小10ms")]
|
||||
public int CycleInterval { get; set; } = 1000;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#region copyright
|
||||
//------------------------------------------------------------------------------
|
||||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||||
// QQ群:605534569
|
||||
//------------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
namespace ThingsGateway.Plugin.SQLDB;
|
||||
|
||||
public class TDengineDBVariableProperty : VariablePropertyBase
|
||||
{
|
||||
[VariableProperty("启用", "")]
|
||||
public bool Enable { get; set; } = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
||||
<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25 md %25dir%25 
copy "$(TargetDir)*TDengineDB*.dll" %25dir%25



" />
|
||||
|
||||
</Target>
|
||||
|
||||
<PropertyGroup>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SqlSugar.TDengineCore" Version="2.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
|
||||
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
|
||||
<Exec Command=" set dir="$(SolutionDir)Web\ThingsGateway.Web.Entry\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)"
 if not exist %25dir%25 md %25dir%25 
copy "$(TargetDir)*ThingsGateway.Plugin.TDengineDB*.dll" %25dir%25



" />
|
||||
|
||||
</Target>
|
||||
|
||||
|
||||
|
||||
</Project>
|
||||
@@ -52,10 +52,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Web.Core", "W
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Core", "Web\ThingsGateway.Core\ThingsGateway.Core.csproj", "{51313113-7BB8-494E-9C24-6787BECE39BB}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Admin", "Admin", "{79E7042F-F9E3-4D87-BFA9-4B7DD9736735}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gateway", "Gateway", "{000C3C62-345E-451C-8CEE-6F2C6A087116}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.ApiController", "Web\ThingsGateway.Gateway.ApiController\ThingsGateway.Gateway.ApiController.csproj", "{5D7BE567-2345-46C8-9F54-DDC1DA96D198}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Application", "Web\ThingsGateway.Gateway.Application\ThingsGateway.Gateway.Application.csproj", "{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F}"
|
||||
@@ -64,8 +60,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Blazo
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Gateway.Core", "Web\ThingsGateway.Gateway.Core\ThingsGateway.Gateway.Core.csproj", "{5CD79F91-7182-4A9D-9BEF-4DF410C782D2}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugin", "Plugin", "{CC8D0880-B73E-4DFC-9052-86504728708E}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
Plugin\Directory.Build.props = Plugin\Directory.Build.props
|
||||
@@ -105,6 +99,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.De
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Demo.Photino", "Demo\ThingsGateway.Foundation.Demo.Photino\ThingsGateway.Foundation.Demo.Photino.csproj", "{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.SQLDB", "Plugin\ThingsGateway.Plugin.SQLDB\ThingsGateway.Plugin.SQLDB.csproj", "{7EBD5500-0DA0-415A-831D-5DC350917501}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.QuestDB", "Plugin\ThingsGateway.Plugin.QuestDB\ThingsGateway.Plugin.QuestDB.csproj", "{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Plugin.TDengineDB", "Plugin\ThingsGateway.Plugin.TDengineDB\ThingsGateway.Plugin.TDengineDB.csproj", "{2C827B2C-75DF-413B-9AB2-2D1B438AC082}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -231,6 +231,18 @@ Global
|
||||
{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C5519C51-0A0C-4317-A43D-FFBB6B344ACB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -242,21 +254,18 @@ Global
|
||||
{566783A4-222B-46F5-AA12-0753997B3254} = {0874CBC5-C583-4FAD-BA93-94571D446898}
|
||||
{BEFBC44A-E140-4E3E-AFBE-2DD8B98EB9BF} = {0874CBC5-C583-4FAD-BA93-94571D446898}
|
||||
{9695B353-D773-40DD-B65E-7B10EB0C16EC} = {0874CBC5-C583-4FAD-BA93-94571D446898}
|
||||
{799C49A4-8E23-475A-A82D-080854718BEE} = {BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}
|
||||
{616CA361-B667-42C8-B4DC-097C7CD39830} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735}
|
||||
{16C62A28-BACE-4391-91F8-C2D78D063A1E} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735}
|
||||
{8FA03089-322F-44CB-8E4B-F2637388E944} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735}
|
||||
{14FF7150-6DB7-455B-AD00-6AB4DE37855B} = {79E7042F-F9E3-4D87-BFA9-4B7DD9736735}
|
||||
{799C49A4-8E23-475A-A82D-080854718BEE} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{616CA361-B667-42C8-B4DC-097C7CD39830} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{16C62A28-BACE-4391-91F8-C2D78D063A1E} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{8FA03089-322F-44CB-8E4B-F2637388E944} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{14FF7150-6DB7-455B-AD00-6AB4DE37855B} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{2861AA39-AAAE-47ED-9ACC-4C165DDF3EF1} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{9FF2A8A6-48D0-4D8A-9EAD-1905174291CC} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{51313113-7BB8-494E-9C24-6787BECE39BB} = {BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15}
|
||||
{79E7042F-F9E3-4D87-BFA9-4B7DD9736735} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{000C3C62-345E-451C-8CEE-6F2C6A087116} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{5D7BE567-2345-46C8-9F54-DDC1DA96D198} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
|
||||
{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
|
||||
{CD0F211A-F65B-4026-9750-68AC3C70D012} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
|
||||
{5CD79F91-7182-4A9D-9BEF-4DF410C782D2} = {000C3C62-345E-451C-8CEE-6F2C6A087116}
|
||||
{BB9C2A85-7A8A-4CF9-BF44-34DE9848EC15} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{51313113-7BB8-494E-9C24-6787BECE39BB} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{5D7BE567-2345-46C8-9F54-DDC1DA96D198} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{5CF1B3EC-84E2-484A-8DFC-2ECD2EE18E2F} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{CD0F211A-F65B-4026-9750-68AC3C70D012} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{5CD79F91-7182-4A9D-9BEF-4DF410C782D2} = {9EB46BB6-D4EA-4B06-95EE-6C971E653030}
|
||||
{2057E5BE-FACA-4D44-A2BA-E1F864A8DAFF} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
{A723D4D7-B796-4D97-BA68-95E5696C9559} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
{D9944D52-81B4-4DBC-8C3B-2A334CCBD4F6} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
@@ -269,6 +278,9 @@ Global
|
||||
{681F774F-7B0B-450A-917C-1385E1847CA6} = {237C7BC5-7B07-40B5-AF42-CE2F8E0893C3}
|
||||
{637A662B-7B70-4CE8-8F5F-0A095B9D77EC} = {95008B83-0324-4A7C-80DE-2BBDDD1A9099}
|
||||
{C5519C51-0A0C-4317-A43D-FFBB6B344ACB} = {95008B83-0324-4A7C-80DE-2BBDDD1A9099}
|
||||
{7EBD5500-0DA0-415A-831D-5DC350917501} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
{A99787D7-A93B-4357-A8B5-B5F1FD2930AB} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
{2C827B2C-75DF-413B-9AB2-2D1B438AC082} = {CC8D0880-B73E-4DFC-9052-86504728708E}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C49B2D3E-6818-4E28-91B7-6E4E7E264BBB}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.8.8.48" />
|
||||
<PackageReference Include="Furion.Pure" Version="4.8.8.48" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.110" />
|
||||
<PackageReference Include="SqlSugar.TDengineCore" Version="2.8.0" />
|
||||
<PackageReference Include="UAParser" Version="3.1.47" />
|
||||
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -38,6 +38,9 @@ public static class Startup
|
||||
{ nameof(PEnqueuedSnackbars.Position), SnackPosition.TopCenter }
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
{ nameof(MErrorHandler), new Dictionary<string, object>() { { nameof(MErrorHandler.ShowDetail), true } } },
|
||||
|
||||
{ nameof(MIcon), new Dictionary<string, object>() { { nameof(MIcon.Dense), true } } },
|
||||
@@ -49,7 +52,7 @@ public static class Startup
|
||||
{ nameof(MDescriptions), new Dictionary<string, object>() { { nameof(MDescriptions.Dense), true } } },
|
||||
{ nameof(MRow), new Dictionary<string, object>() { { nameof(MRow.Dense), true } } },
|
||||
{ "MAutocomplete", new Dictionary<string, object>() { { "Dense", true } } },
|
||||
{ "MCascader", new Dictionary<string, object>() { { "Dense", true } } },
|
||||
{ "MCascader", new Dictionary<string, object>() { { "Dense", true },{ "Outlined", true } } },
|
||||
{ "MCheckbox", new Dictionary<string, object>() { { "Dense", true } } },
|
||||
{ "MFileInput", new Dictionary<string, object>() { { "Dense", true } } },
|
||||
{ "MRadioGroup", new Dictionary<string, object>() { { "Dense", true } } },
|
||||
|
||||
@@ -352,7 +352,54 @@
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
|
||||
{
|
||||
"Id": 447087330930950,
|
||||
"FileName": "ThingsGateway.Plugin.SQLDB",
|
||||
"AssembleName": "SQLDB",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.Plugin.SQLDB/ThingsGateway.Plugin.SQLDB.dll",
|
||||
"CreateTime": "2023/8/6 18:23:02",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087330930951,
|
||||
"FileName": "ThingsGateway.Plugin.QuestDB",
|
||||
"AssembleName": "QuestDB",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.Plugin.QuestDB/ThingsGateway.Plugin.QuestDB.dll",
|
||||
"CreateTime": "2023/8/6 18:23:02",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 447087330930952,
|
||||
"FileName": "ThingsGateway.Plugin.TDengineDB",
|
||||
"AssembleName": "TDengineDB",
|
||||
"DriverTypeEnum": "Upload",
|
||||
"FilePath": "Plugins/ThingsGateway.Plugin.TDengineDB/ThingsGateway.Plugin.TDengineDB.dll",
|
||||
"CreateTime": "2023/8/6 18:23:02",
|
||||
"CreateUser": "superAdmin",
|
||||
"CreateUserId": 212725263002001,
|
||||
"IsDelete": 0,
|
||||
"UpdateTime": null,
|
||||
"UpdateUser": null,
|
||||
"UpdateUserId": null,
|
||||
"SortCode": 0,
|
||||
"ExtJson": null
|
||||
},
|
||||
{
|
||||
"Id": 442505,
|
||||
"FileName": "ThingsGateway.Plugin.DLT645",
|
||||
|
||||
@@ -99,9 +99,80 @@ public class HistoryValueWorker : BackgroundService
|
||||
IsAutoCloseConnection = true, //不设成true要手动close
|
||||
ConfigureExternalServices = configureExternalServices,
|
||||
});
|
||||
AopSetting(sqlSugarClient);
|
||||
return OperResult.CreateSuccessResult(sqlSugarClient);
|
||||
}
|
||||
|
||||
|
||||
#region db
|
||||
|
||||
/// <summary>
|
||||
/// Aop设置
|
||||
/// </summary>
|
||||
/// <param name="db"></param>
|
||||
private static void AopSetting(SqlSugarClient db)
|
||||
{
|
||||
var config = db.CurrentConnectionConfig;
|
||||
|
||||
// 设置超时时间
|
||||
db.Ado.CommandTimeOut = 30;
|
||||
|
||||
// 打印SQL语句
|
||||
db.Aop.OnLogExecuting = (sql, pars) =>
|
||||
{
|
||||
//如果不是开发环境就打印sql
|
||||
if (App.HostEnvironment.IsDevelopment())
|
||||
{
|
||||
if (sql.StartsWith("SELECT"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
}
|
||||
if (sql.StartsWith("UPDATE"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
}
|
||||
if (sql.StartsWith("INSERT"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Blue;
|
||||
}
|
||||
if (sql.StartsWith("DELETE"))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
}
|
||||
WriteSqlLog(UtilMethods.GetSqlString(config.DbType, sql, pars));
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.WriteLine();
|
||||
}
|
||||
};
|
||||
//异常
|
||||
db.Aop.OnError = (ex) =>
|
||||
{
|
||||
//如果不是开发环境就打印日志
|
||||
if (App.WebHostEnvironment.IsDevelopment())
|
||||
{
|
||||
if (ex.Parametres == null) return;
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
var pars = db.Utilities.SerializeObject(((SugarParameter[])ex.Parametres).ToDictionary(it => it.ParameterName, it => it.Value));
|
||||
WriteSqlLogError(UtilMethods.GetSqlString(config.DbType, ex.Sql, (SugarParameter[])ex.Parametres));
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
private static void WriteSqlLog(string msg)
|
||||
{
|
||||
Console.WriteLine("【Sql执行时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
|
||||
Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine);
|
||||
}
|
||||
private static void WriteSqlLogError(string msg)
|
||||
{
|
||||
Console.WriteLine("【Sql执行错误时间】:" + DateTimeExtensions.CurrentDateTime.ToDefaultDateTimeFormat());
|
||||
Console.WriteLine("【Sql语句】:" + msg + Environment.NewLine);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region worker服务
|
||||
private EasyLock easyLock = new();
|
||||
|
||||
@@ -327,6 +398,7 @@ public class HistoryValueWorker : BackgroundService
|
||||
try
|
||||
{
|
||||
_logger.LogWarning("连接历史数据表失败,尝试初始化表");
|
||||
sqlSugarClient.DbMaintenance.CreateDatabase();
|
||||
sqlSugarClient.CodeFirst.InitTables(typeof(HistoryValue));
|
||||
LastIsSuccess = true;
|
||||
StatuString = OperResult.CreateSuccessResult();
|
||||
@@ -467,7 +539,8 @@ public class HistoryValueMapper : IRegister
|
||||
{
|
||||
config.ForType<DeviceVariableRunTime, HistoryValue>()
|
||||
.Map(dest => dest.Value, (src) => ValueReturn(src))
|
||||
.Map(dest => dest.CollectTime, (src) => src.CollectTime.ToUniversalTime());//注意sqlsugar插入时无时区,直接utc时间
|
||||
.Map(dest => dest.CollectTime, (src) => src.CollectTime.ToUniversalTime())//注意sqlsugar插入时无时区,直接utc时间
|
||||
.Map(dest => dest.CreateTime, (src) => DateTime.UtcNow);//注意sqlsugar插入时无时区,直接utc时间
|
||||
}
|
||||
|
||||
private static object ValueReturn(DeviceVariableRunTime src)
|
||||
|
||||
@@ -27,13 +27,28 @@ public class HistoryValue : PrimaryIdEntity
|
||||
public override long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// 采集时间
|
||||
/// </summary>
|
||||
[TimeDbSplitField(DateType.Month)]
|
||||
[Description("上传时间")]
|
||||
[Description("采集时间")]
|
||||
[DataTable(Order = 1, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")]
|
||||
public DateTime CollectTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// </summary>
|
||||
[Description("上传时间")]
|
||||
[DataTable(Order = 1, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")]
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设备名称
|
||||
/// </summary>
|
||||
[SugarColumn(ColumnDataType = "symbol")]
|
||||
[Description("设备名称")]
|
||||
[DataTable(Order = 2, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")]
|
||||
public string DeviceName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 变量名称
|
||||
/// </summary>
|
||||
@@ -41,6 +56,7 @@ public class HistoryValue : PrimaryIdEntity
|
||||
[Description("变量名称")]
|
||||
[DataTable(Order = 2, IsShow = true, Sortable = true, CellClass = " table-text-truncate ")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否在线
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user