添加项目文件。

master
wenjy 1 month ago
parent bceb68149e
commit f4f8c6e023

BIN
.DS_Store vendored

Binary file not shown.

@ -0,0 +1,13 @@
# Default ignored files
/shelf/
/workspace.xml
# Rider ignored files
/contentModel.xml
/projectSettingsUpdater.xml
/.idea.Sln.Iot.iml
/modules.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

@ -0,0 +1,76 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Business
* 8BCBBC2D-F2EB-45A3-9DBA-24217C772093
*
* WenJY
*
* 2025-05-07 13:44:30
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using Sln.Iot.Business.@base;
using Sln.Iot.Common;
using Sln.Iot.Config;
using Sln.Iot.Model.dto;
using Sln.Iot.Serilog;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Business
{
/// <summary>
/// 校时指令
/// </summary>
public class CheckTimeBusiness:BaseBusiness
{
public CheckTimeBusiness(SerilogHelper logger, AppConfig appConfig, StringChange stringChange) : base(logger, appConfig, stringChange)
{
}
public override FilterResult BufferAnalysis(ISocketClient client, BufferRequestInfo requestInfo, int bodyLength)
{
ResponsePack sendResponsePackInfo = new ResponsePack()
{
m_MessageType = 0x08,
m_PackLen = new byte[] {0x00, 0x06}
};
base.GetMessagePack(ref sendResponsePackInfo,requestInfo.buffer);
DateTime currentTime = DateTime.Now;
byte[] timeBuffer = new byte[]
{
base._stringChange.HexStrTorbytes(currentTime.ToString("ss"))[0],
base._stringChange.HexStrTorbytes(currentTime.ToString("mm"))[0],
base._stringChange.HexStrTorbytes(currentTime.ToString("HH"))[0],
base._stringChange.HexStrTorbytes(currentTime.ToString("dd"))[0],
base._stringChange.HexStrTorbytes(currentTime.ToString("MM"))[0],
base._stringChange.HexStrTorbytes(currentTime.ToString("yy"))[0],
};
base.SendMessageAsync(client, sendResponsePackInfo,timeBuffer);
return FilterResult.Success;
}
public override void ResponseHandle(ISocketClient client, byte[] buffer)
{
//校时指令通过业务数据返回
}
}
}

@ -0,0 +1,299 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Business
* F35152F4-9983-4881-AB1C-F9561EFAF534
*
* WenJY
*
* 2025-05-20 10:30:12
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Sln.Iot.Business.@base;
using Sln.Iot.Common;
using Sln.Iot.Config;
using Sln.Iot.Model.dao;
using Sln.Iot.Model.dto;
using Sln.Iot.Repository.service;
using Sln.Iot.Serilog;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Business
{
/// <summary>
/// 电能数据
/// </summary>
public class ElectricBusiness:BaseBusiness
{
private Dictionary<string ,DateTime> _lastCollectTimeDict = new Dictionary<string, DateTime>();
private readonly IRecordDnbInstantService? _service;
public ElectricBusiness(SerilogHelper logger, AppConfig appConfig, StringChange stringChange, IRecordDnbInstantService? service) : base(logger, appConfig, stringChange)
{
_service = service;
}
public override FilterResult BufferAnalysis(ISocketClient client, BufferRequestInfo requestInfo, int bodyLength)
{
ByteBlock byteBlock = new ByteBlock(requestInfo.Body);
if (byteBlock.CanReadLen < 1)
{
return FilterResult.Cache;
}
int pos = byteBlock.Pos;
try
{
List<RecordDnbInstant> result = new List<RecordDnbInstant>();
var amount = requestInfo.BufferLength / bodyLength;
_logger.Info($"收到{amount}个电表数据,开始循环解析......");
for (int i = 0; i < amount; i++)
{
RecordDnbInstant dnbInstant = new RecordDnbInstant();
#region 表号解析
byteBlock.Read(out byte[] b_MeterID, 2);
var decParts = b_MeterID.Select(b => $"{b:D2}").ToArray();
var equipId = $"{requestInfo.ColletEquipCode}_{decParts[0]}{decParts[1]}";
#endregion
dnbInstant.monitorId = equipId;
do
{
byteBlock.Read(out byte[] b_UA_flag, 2);
base._stringChange.ConvertBytesToUInt16(b_UA_flag, out uint flag);
switch (flag)
{
case CommParams.AU: //A项电压
byteBlock.Read(out byte[] b_UA, 4);
base._stringChange.SwapBytes(ref b_UA);
float f_UA = BitConverter.ToSingle(b_UA, 0);
ValueIsNan(ref f_UA);
dnbInstant.vA = (decimal) f_UA;
break;
case CommParams.BU: //B项电压
byteBlock.Read(out byte[] b_UB, 4);
base._stringChange.SwapBytes(ref b_UB);
float f_UB = BitConverter.ToSingle(b_UB, 0);
ValueIsNan(ref f_UB);
dnbInstant.vB = (decimal) f_UB;
break;
case CommParams.CU: //C项电压
byteBlock.Read(out byte[] b_UC, 4);
base._stringChange.SwapBytes(ref b_UC);
float f_UC = BitConverter.ToSingle(b_UC, 0);
ValueIsNan(ref f_UC);
dnbInstant.vC = (decimal) f_UC;
break;
case CommParams.AI: //A项电流
byteBlock.Read(out byte[] b_IA, 4);
base._stringChange.SwapBytes(ref b_IA);
float f_IA = BitConverter.ToSingle(b_IA, 0);
ValueIsNan(ref f_IA);
dnbInstant.iA = (decimal) f_IA;
break;
case CommParams.BI: //B项电流
byteBlock.Read(out byte[] b_IB, 4);
base._stringChange.SwapBytes(ref b_IB);
float f_IB = BitConverter.ToSingle(b_IB, 0);
ValueIsNan(ref f_IB);
dnbInstant.iB = (decimal) f_IB;
break;
case CommParams.CI: //C项电流
byteBlock.Read(out byte[] b_IC, 4);
base._stringChange.SwapBytes(ref b_IC);
float f_IC = BitConverter.ToSingle(b_IC, 0);
ValueIsNan(ref f_IC);
dnbInstant.iC = (decimal) f_IC;
break;
case CommParams.GLYS: //功率因数
byteBlock.Read(out byte[] b_GLYS, 4);
base._stringChange.SwapBytes(ref b_GLYS);
float f_GLYS = BitConverter.ToSingle(b_GLYS, 0);
ValueIsNan(ref f_GLYS);
dnbInstant.powerFactor = (decimal) f_GLYS;
break;
case CommParams.YGGL: //有功功率
byteBlock.Read(out byte[] b_YGGL, 4);
base._stringChange.SwapBytes(ref b_YGGL);
float f_YGGL = BitConverter.ToSingle(b_YGGL, 0);
ValueIsNan(ref f_YGGL);
dnbInstant.activePower = Convert.ToDecimal(f_YGGL / 1000);
break;
case CommParams.WGGL: //无功功率
byteBlock.Read(out byte[] b_WGGL, 4);
base._stringChange.SwapBytes(ref b_WGGL);
float f_WGGL = BitConverter.ToSingle(b_WGGL, 0);
ValueIsNan(ref f_WGGL);
dnbInstant.reactivePower = (decimal) f_WGGL;
break;
case CommParams.ZXYGZ: //正向有功
byteBlock.Read(out byte[] b_ZXYGZ, 4);
base._stringChange.SwapBytes(ref b_ZXYGZ);
float f_ZXYGZ = BitConverter.ToSingle(b_ZXYGZ, 0);
ValueIsNan(ref f_ZXYGZ);
dnbInstant.positiveActive = (decimal) f_ZXYGZ;
break;
case CommParams.CJSJ: //采集时间
byteBlock.Read(out byte[] b_CJSJ, 6);
string strDateTime = "20" + b_CJSJ[5].ToString("x2")
+ "-" + b_CJSJ[4].ToString("x2")
+ "-" + b_CJSJ[3].ToString("x2")
+ " " + b_CJSJ[2].ToString("x2")
+ ":" + b_CJSJ[1].ToString("x2")
+ ":" + b_CJSJ[0].ToString("x2");
dnbInstant.collectTime = Convert.ToDateTime(strDateTime);
break;
}
} while (byteBlock.Pos % bodyLength != 0);
dnbInstant.recordTime = DateTime.Now;
var serializeObject = JsonConvert.SerializeObject(dnbInstant);
_logger.Info($"第{i+1}个电表{dnbInstant.monitorId}解析完成:{serializeObject}");
DateTime? lastCollectTime = GetLastCollectTime(dnbInstant.monitorId);
if (lastCollectTime != null && DateTime.Now -lastCollectTime < TimeSpan.FromMinutes(_appConfig.electricTimeInterval))
{
//时间间隔小于采集间隔,不保存
continue;
}
// 如果字典中已有该键,则更新;否则新增
_lastCollectTimeDict[dnbInstant.monitorId] = DateTime.Now;
result.Add(dnbInstant);
}
if (result.Count > 0)
{
//是否开启 FF 异常值过滤
if (_appConfig.virtualFlag)
{
ParamVerification(ref result);
}
var inRes = _service.SplitInsert(result,out List<long> insertIds);
_logger.Info($"{result.Count}个电表数据解析处理完成,数据保存{(inRes ? "" : "")}");
}
else
{
_logger.Info($"{amount}个电表数据解析处理完成,没有需要保存的数据");
}
return FilterResult.Success;
}
catch (Exception e)
{
base._logger.Error($"电能数据解析异常:{e.Message}");
}
return FilterResult.Cache;
}
/// <summary>
/// FF FF参数过滤
/// </summary>
/// <param name="dnbInstants"></param>
/// <exception cref="ArgumentNullException"></exception>
private void ParamVerification(ref List<RecordDnbInstant> dnbInstants)
{
if (dnbInstants == null)
{
throw new ArgumentNullException($"过滤参数方法异常,传入参数为空");
}
for (int i = dnbInstants.Count - 1; i >= 0; i--)
{
var item = dnbInstants[i];
if (item.positiveActive == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},正向有功为 FF FF FF FF已启用过滤不保存该表数据");
dnbInstants.RemoveAt(i);
continue;
}
}
}
private DateTime? GetLastCollectTime(string key)
{
// 检查键是否存在
if (_lastCollectTimeDict.TryGetValue(key, out DateTime collectTime))
{
return collectTime;
}
else
{
return null;
}
}
/// <summary>
/// 应答处理
/// </summary>
/// <param name="client"></param>
/// <param name="buffer"></param>
public override void ResponseHandle(ISocketClient client, byte[] buffer)
{
ResponsePack sendResponsePackInfo = new ResponsePack()
{
m_MessageType = 0xB3
};
base.GetMessagePack(ref sendResponsePackInfo, buffer);
base.SendMessageAsync(client,sendResponsePackInfo);
}
}
}

@ -0,0 +1,276 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Business
* D5F7092D-7C5C-42B9-A373-1332CFD77169
*
* WenJY
*
* 2025-05-20 14:45:41
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Sln.Iot.Business.@base;
using Sln.Iot.Common;
using Sln.Iot.Config;
using Sln.Iot.Model.dao;
using Sln.Iot.Model.dto;
using Sln.Iot.Repository.service;
using Sln.Iot.Serilog;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Business
{
public class FluidBusiness:BaseBusiness
{
private Dictionary<string ,DateTime> _lastCollectTimeDict = new Dictionary<string, DateTime>();
private readonly IRecordFluidInstantService? _service;
public FluidBusiness(SerilogHelper logger, AppConfig appConfig, StringChange stringChange, IRecordFluidInstantService? service) : base(logger, appConfig, stringChange)
{
_service = service;
}
public override FilterResult BufferAnalysis(ISocketClient client, BufferRequestInfo requestInfo, int bodyLength)
{
ByteBlock byteBlock = new ByteBlock(requestInfo.Body);
if (byteBlock.CanReadLen < 1)
{
return FilterResult.Cache;
}
int pos = byteBlock.Pos;
try
{
List<RecordFluidInstant> result = new List<RecordFluidInstant>();
var amount = requestInfo.BufferLength / bodyLength;
_logger.Info($"收到{amount}个流体数据,开始循环解析......");
for (int i = 0; i < amount; i++)
{
RecordFluidInstant fluidInstant = new RecordFluidInstant();
#region 表号解析
byteBlock.Read(out byte[] b_MeterID, 2);
var decParts = b_MeterID.Select(b => $"{b:D2}").ToArray();
var equipId = $"{requestInfo.ColletEquipCode}_{decParts[0]}{decParts[1]}";
#endregion
fluidInstant.monitorId = equipId;
do
{
byteBlock.Read(out byte[] b_UA_flag, 2);
base._stringChange.ConvertBytesToUInt16(b_UA_flag, out uint flag);
switch (flag)
{
case CommParams.Press: //压力值
byteBlock.Read(out byte[] b_Press, 4);
base._stringChange.SwapBytes(ref b_Press);
float f_Press = BitConverter.ToSingle(b_Press, 0);
ValueIsNan(ref f_Press);
fluidInstant.press = (decimal)f_Press;
break;
case CommParams.STemperature: //温度值
byteBlock.Read(out byte[] b_Temperature, 4);
base._stringChange.SwapBytes(ref b_Temperature);
float f_Temperature = BitConverter.ToSingle(b_Temperature, 0);
ValueIsNan(ref f_Temperature);
fluidInstant.temperature = (decimal)f_Temperature;
break;
case CommParams.Frequency: //频率值
byteBlock.Read(out byte[] b_Frequency, 4);
base._stringChange.SwapBytes(ref b_Frequency);
float f_Frequency = BitConverter.ToSingle(b_Frequency, 0);
ValueIsNan(ref f_Frequency);
fluidInstant.frequency = (decimal)f_Frequency;
break;
case CommParams.Density: //密度值
byteBlock.Read(out byte[] b_Density, 4);
base._stringChange.SwapBytes(ref b_Density);
float f_Density = BitConverter.ToSingle(b_Density, 0);
ValueIsNan(ref f_Density);
fluidInstant.density = (decimal)f_Density;
break;
case CommParams.FluxInstantValue: //瞬时流值
byteBlock.Read(out byte[] b_FluxInstantValue, 4);
base._stringChange.SwapBytes(ref b_FluxInstantValue);
float f_FluxInstantValue = BitConverter.ToSingle(b_FluxInstantValue, 0);
ValueIsNan(ref f_FluxInstantValue);
fluidInstant.instantFlow = (decimal)f_FluxInstantValue;
break;
case CommParams.FluxEyeableTotalValue: //累计流量值
byteBlock.Read(out byte[] b_FluxEyeableTotalValue, 4);
base._stringChange.SwapBytes(ref b_FluxEyeableTotalValue);
float f_FluxEyeableTotalValue = BitConverter.ToSingle(b_FluxEyeableTotalValue, 0);
ValueIsNan(ref f_FluxEyeableTotalValue);
fluidInstant.totalFlow = (decimal)f_FluxEyeableTotalValue;
break;
case CommParams.HeatInstantValue: //瞬时热量
byteBlock.Read(out byte[] b_HeatInstantValue, 4);
base._stringChange.SwapBytes(ref b_HeatInstantValue);
float f_HeatInstantValue = BitConverter.ToSingle(b_HeatInstantValue, 0);
ValueIsNan(ref f_HeatInstantValue);
fluidInstant.instantHeat = (decimal)f_HeatInstantValue;
break;
case CommParams.HeatToftalValue: //累计热量值
byteBlock.Read(out byte[] b_HeatToftalValue, 4);
base._stringChange.SwapBytes(ref b_HeatToftalValue);
float f_HeatToftalValue = BitConverter.ToSingle(b_HeatToftalValue, 0);
ValueIsNan(ref f_HeatToftalValue);
fluidInstant.totalHeat = (decimal)f_HeatToftalValue;
break;
case CommParams.CJSJ: //采集时间
byteBlock.Read(out byte[] b_CJSJ, 6);
string strDateTime = "20" + b_CJSJ[5].ToString("x2")
+ "-" + b_CJSJ[4].ToString("x2")
+ "-" + b_CJSJ[3].ToString("x2")
+ " " + b_CJSJ[2].ToString("x2")
+ ":" + b_CJSJ[1].ToString("x2")
+ ":" + b_CJSJ[0].ToString("x2");
fluidInstant.collectTime = Convert.ToDateTime(strDateTime);
break;
}
} while (byteBlock.Pos % bodyLength != 0);
fluidInstant.recordTime = DateTime.Now;
var serializeObject = JsonConvert.SerializeObject(fluidInstant);
_logger.Info($"第{i+1}个流体仪表{fluidInstant.monitorId}解析完成:{serializeObject}");
DateTime? lastCollectTime = GetLastCollectTime(fluidInstant.monitorId);
if (lastCollectTime != null && DateTime.Now -lastCollectTime < TimeSpan.FromMinutes(_appConfig.fluidTimeInterval))
{
//时间间隔小于采集间隔,不保存
continue;
}
// 如果字典中已有该键,则更新;否则新增
_lastCollectTimeDict[fluidInstant.monitorId] = DateTime.Now;
result.Add(fluidInstant);
}
if (result.Count > 0)
{
//是否开启 FF 异常值过滤
if (_appConfig.virtualFlag)
{
ParamVerification(ref result);
}
var inRes = _service.SplitInsert(result,out List<long> insertIds);
_logger.Info($"{result.Count}个流体数据解析处理完成,数据保存{(inRes ? "" : "")}");
}
else
{
_logger.Info($"{amount}个流体数据解析处理完成,没有需要保存的数据");
}
return FilterResult.Success;
}
catch (Exception e)
{
base._logger.Error($"流体数据解析异常:{e.Message}");
}
return FilterResult.Cache;
}
/// <summary>
/// FF FF参数过滤
/// </summary>
/// <param name="dnbInstants"></param>
/// <exception cref="ArgumentNullException"></exception>
private void ParamVerification(ref List<RecordFluidInstant> fluidInstants)
{
if (fluidInstants == null)
{
throw new ArgumentNullException($"过滤参数方法异常,传入参数为空");
}
for (int i = fluidInstants.Count - 1; i >= 0; i--)
{
var item = fluidInstants[i];
if (item.totalFlow == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},累计流量为 FF FF FF FF已启用过滤不保存该表数据");
fluidInstants.RemoveAt(i);
continue;
}
}
}
private DateTime? GetLastCollectTime(string key)
{
// 检查键是否存在
if (_lastCollectTimeDict.TryGetValue(key, out DateTime collectTime))
{
return collectTime;
}
else
{
return null;
}
}
/// <summary>
/// 应答处理
/// </summary>
/// <param name="client"></param>
/// <param name="buffer"></param>
public override void ResponseHandle(ISocketClient client, byte[] buffer)
{
ResponsePack sendResponsePackInfo = new ResponsePack()
{
m_MessageType = 0xB4
};
base.GetMessagePack(ref sendResponsePackInfo, buffer);
base.SendMessageAsync(client, sendResponsePackInfo);
}
}
}

@ -0,0 +1,79 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Business
* AF7CD4B2-87AF-4F91-BECA-13779B05170C
*
* WenJY
*
* 2025-04-11 16:04:07
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using Sln.Iot.Business.@base;
using Sln.Iot.Common;
using Sln.Iot.Config;
using Sln.Iot.Model.dto;
using Sln.Iot.Serilog;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Business
{
/// <summary>
/// 心跳指令
/// </summary>
public class HeartBusiness:BaseBusiness
{
public HeartBusiness(SerilogHelper logger, AppConfig appConfig,StringChange stringChange) : base(logger, appConfig,stringChange)
{
}
public override FilterResult BufferAnalysis(ISocketClient client, BufferRequestInfo requestInfo, int bodyLength)
{
//心跳没有业务逻辑处理
return FilterResult.Success;
}
/// <summary>
/// 应答处理
/// </summary>
/// <param name="client"></param>
/// <param name="buffer"></param>
public override void ResponseHandle(ISocketClient client, byte[] buffer)
{
ResponsePack sendResponsePackInfo = new ResponsePack()
{
m_MessageType = 0xA4
};
base.GetMessagePack(ref sendResponsePackInfo,buffer);
//ByteBlock byteBlock = new ByteBlock(requestInfo.Body);
//byteBlock.Read(out byte[] b_MeterID, 2);
//var MeterID_1 = "00" + Convert.ToInt32(b_MeterID[0]).ToString();
//MeterID_1 = MeterID_1.Substring(MeterID_1.Length - 2, 2);
//var MeterID_2 = "00" + Convert.ToInt32(b_MeterID[1]).ToString();
//MeterID_2 = MeterID_2.Substring(MeterID_2.Length - 2, 2);
//var equipId = requestInfo.ColletEquipCOde + "_" + MeterID_1 + MeterID_2;
//Console.WriteLine($"心跳:::::{}");
base.SendMessageAsync(client, sendResponsePackInfo);
}
}
}

@ -0,0 +1,309 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Business
* 7C26094C-5352-4997-866A-FA618F2E5D27
*
* WenJY
*
* 2025-04-11 15:47:06
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Sln.Iot.Business.@base;
using Sln.Iot.Common;
using Sln.Iot.Config;
using Sln.Iot.Model.dao;
using Sln.Iot.Model.dto;
using Sln.Iot.Repository.service;
using Sln.Iot.Serilog;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Business
{
/// <summary>
/// 物联网环境:温度、湿度、照度、噪音、振动
/// </summary>
public class IotEnvBusiness:BaseBusiness
{
private readonly IRecordIotEnvInstantService? _service;
public IotEnvBusiness(SerilogHelper logger, AppConfig appConfig, StringChange stringChange, IRecordIotEnvInstantService? service) : base(logger, appConfig, stringChange)
{
_service = service;
}
public override FilterResult BufferAnalysis(ISocketClient client, BufferRequestInfo requestInfo, int bodyLength)
{
ByteBlock byteBlock = new ByteBlock(requestInfo.Body);
if (byteBlock.CanReadLen < 1)
{
return FilterResult.Cache;
}
int pos = byteBlock.Pos;
try
{
List<RecordIotEnvInstant> result = new List<RecordIotEnvInstant>();
var amount = requestInfo.BufferLength / bodyLength;
_logger.Info($"收到{amount}个物联网环境数据,开始循环解析......");
for (int i = 0; i < amount; i++)
{
RecordIotEnvInstant iotEnvInstant = new RecordIotEnvInstant();
#region 表号解析 Add By Wenjy 2024-04-18
// byteBlock.Read(out byte[] b_MeterID, 2);
// var MeterID_1 = "00" + Convert.ToInt32(b_MeterID[0]).ToString();
// MeterID_1 = MeterID_1.Substring(MeterID_1.Length - 2, 2);
// var MeterID_2 = "00" + Convert.ToInt32(b_MeterID[1]).ToString();
// MeterID_2 = MeterID_2.Substring(MeterID_2.Length - 2, 2);
// var equipId = requestInfo.ColletEquipCode + "_" + MeterID_1 + MeterID_2;
#endregion
#region 表号解析
byteBlock.Read(out byte[] b_MeterID, 2);
var decParts = b_MeterID.Select(b => $"{b:D2}").ToArray();
var equipId = $"{requestInfo.ColletEquipCode}_{decParts[0]}{decParts[1]}";
#endregion
iotEnvInstant.monitorId = equipId;
#region 物联网参数解析 Edit By Wenjy 2025-05-07 修改 Nan 值过滤
do
{
byteBlock.Read(out byte[] b_UA_flag, 2);
base._stringChange.ConvertBytesToUInt16(b_UA_flag, out uint flag);
switch (flag)
{
case CommParams.TTempreture: //温度
byteBlock.Read(out byte[] tempreture, 4);
base._stringChange.SwapBytes(ref tempreture);
float f_tempreture = BitConverter.ToSingle(tempreture, 0);
ValueIsNan(ref f_tempreture);
iotEnvInstant.temperature = (decimal) f_tempreture;
break;
case CommParams.Humidity: //湿度
byteBlock.Read(out byte[] humidity, 4);
base._stringChange.SwapBytes(ref humidity);
float f_humidity = BitConverter.ToSingle(humidity, 0);
ValueIsNan(ref f_humidity);
iotEnvInstant.humidity = (decimal) f_humidity;
break;
case CommParams.Noise: //噪音
byteBlock.Read(out byte[] noise, 4);
base._stringChange.SwapBytes(ref noise);
float f_noise = BitConverter.ToSingle(noise, 0);
ValueIsNan(ref f_noise);
iotEnvInstant.noise = (decimal) f_noise;
break;
case CommParams.VibrationSpeed: //振动-速度
byteBlock.Read(out byte[] vibrationSpeed, 4);
base._stringChange.SwapBytes(ref vibrationSpeed);
float f_vibrationSpeed = BitConverter.ToSingle(vibrationSpeed, 0);
ValueIsNan(ref f_vibrationSpeed);
iotEnvInstant.VibrationSpeed = (decimal) f_vibrationSpeed;
break;
case CommParams.VibrationDisplacement: //振动-位移
bodyLength = 58;
byteBlock.Read(out byte[] vibrationDisplacement, 4);
base._stringChange.SwapBytes(ref vibrationDisplacement);
float f_vibrationDisplacement = BitConverter.ToSingle(vibrationDisplacement, 0);
ValueIsNan(ref f_vibrationDisplacement);
iotEnvInstant.VibrationDisplacement = (decimal)f_vibrationDisplacement;
break;
case CommParams.VibrationAcceleration: //振动-加速度
byteBlock.Read(out byte[] vibrationAcceleration, 4);
base._stringChange.SwapBytes(ref vibrationAcceleration);
float f_vibrationAcceleration = BitConverter.ToSingle(vibrationAcceleration, 0);
ValueIsNan(ref f_vibrationAcceleration);
iotEnvInstant.VibrationAcceleration = (decimal)f_vibrationAcceleration;
break;
case CommParams.VibrationTemp: //振动-温度
byteBlock.Read(out byte[] vibrationTemp, 4);
base._stringChange.SwapBytes(ref vibrationTemp);
float f_vibrationTemp = BitConverter.ToSingle(vibrationTemp, 0);
ValueIsNan(ref f_vibrationTemp);
iotEnvInstant.VibrationTemp = (decimal)f_vibrationTemp;
break;
case CommParams.CJSJ:
byteBlock.Read(out byte[] b_CJSJ, 6);
string strDateTime = "20" + b_CJSJ[5].ToString("x2")
+ "-" + b_CJSJ[4].ToString("x2")
+ "-" + b_CJSJ[3].ToString("x2")
+ " " + b_CJSJ[2].ToString("x2")
+ ":" + b_CJSJ[1].ToString("x2")
+ ":" + b_CJSJ[0].ToString("x2");
iotEnvInstant.collectTime = Convert.ToDateTime(strDateTime);
break;
}
} while (byteBlock.Pos % bodyLength != 0);
#endregion
iotEnvInstant.recordTime = DateTime.Now;
var serializeObject = JsonConvert.SerializeObject(iotEnvInstant);
_logger.Info($"第{i+1}个物联网表{iotEnvInstant.monitorId}解析完成:{serializeObject}");
result.Add(iotEnvInstant);
}
if (result.Count > 0)
{
//是否开启 FF 异常值过滤
if (_appConfig.virtualFlag)
{
ParamVerification(ref result);
}
var inRes = _service.SplitInsert(result,out List<long> insertIds);
_logger.Info($"{amount}个物联网数据解析处理完成,保存{result.Count}个物联网数据,保存{(inRes ? "" : "")}");
}
else
{
_logger.Info($"{amount}个物联网数据解析处理完成,没有需要保存的数据");
}
return FilterResult.Success;
}
catch (Exception e)
{
base._logger.Error($"物联网数据解析异常:{e.Message}");
}
return FilterResult.Cache;
}
/// <summary>
/// 回复指令
/// </summary>
/// <param name="client"></param>
/// <param name="buffer"></param>
public override void ResponseHandle(ISocketClient client, byte[] buffer)
{
ResponsePack sendResponsePackInfo = new ResponsePack()
{
m_MessageType = 0xB5
};
base.GetMessagePack(ref sendResponsePackInfo, buffer);
base.SendMessageAsync(client,sendResponsePackInfo);
}
/// <summary>
/// FF FF参数过滤
/// </summary>
/// <param name="iotEnvInstants"></param>
/// <exception cref="ArgumentNullException"></exception>
private void ParamVerification(ref List<RecordIotEnvInstant> iotEnvInstants)
{
if (iotEnvInstants == null)
{
throw new ArgumentNullException($"过滤参数方法异常,传入参数为空");
}
for (int i = iotEnvInstants.Count - 1; i >= 0; i--)
{
var item = iotEnvInstants[i];
if (item.temperature == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},温度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.humidity == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},湿度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.noise == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},噪音值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.VibrationSpeed == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},振动速度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.VibrationDisplacement == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},振动位移值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.VibrationAcceleration == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},振动加速度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
if (item.VibrationTemp == _appConfig.virtualValue)
{
_logger.Info($"MonitorId:{item.monitorId},振动温度值为 FF FF FF FF已启用过滤不保存该表数据");
iotEnvInstants.RemoveAt(i);
continue;
}
}
}
}
}

@ -0,0 +1,98 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Business
* 71034873-2FF6-4081-AC87-DFCCCCCF51F2
*
* WenJY
*
* 2025-04-11 16:03:28
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using Microsoft.Extensions.Logging;
using Sln.Iot.Business.@base;
using Sln.Iot.Common;
using Sln.Iot.Config;
using Sln.Iot.Model.dto;
using Sln.Iot.Serilog;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Business
{
/// <summary>
/// 登录指令
/// </summary>
public class LoginBusiness:BaseBusiness
{
public LoginBusiness(SerilogHelper logger, AppConfig appConfig,StringChange stringChange) : base(logger, appConfig,stringChange)
{
}
/// <summary>
/// 指令解析
/// </summary>
/// <param name="client"></param>
/// <param name="requestInfo"></param>
/// <param name="bodyLength"></param>
/// <returns></returns>
public override FilterResult BufferAnalysis(ISocketClient client, BufferRequestInfo requestInfo, int bodyLength)
{
var flag = "";
byte[] bDeviceType = new byte[1];
byte[] bDeviceID = new byte[2];
Array.Copy(requestInfo.buffer, 1, bDeviceType, 0, 1);
Array.Copy(requestInfo.buffer, 2, bDeviceID, 0, 2);
flag = base._stringChange.ConverToString(bDeviceType);
flag += base._stringChange.ConverToString(bDeviceID);
string clientIdStr = flag.ToString();
if (clientIdStr.Contains("45"))
{
if (client.Id != clientIdStr)
{
client.ResetId(clientIdStr);
//更新客户端状态
}
}
return FilterResult.Success;
}
/// <summary>
/// 应答处理
/// </summary>
/// <param name="client"></param>
/// <param name="buffer"></param>
public override void ResponseHandle(ISocketClient client, byte[] buffer)
{
ResponsePack sendResponsePackInfo = new ResponsePack()
{
m_MessageType = 0xA1
};
base.GetMessagePack(ref sendResponsePackInfo,buffer);
base.SendMessageAsync(client, sendResponsePackInfo);
}
}
}

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Sln.Iot.Common\Sln.Iot.Common.csproj" />
<ProjectReference Include="..\Sln.Iot.Model\Sln.Iot.Model.csproj" />
<ProjectReference Include="..\Sln.Iot.Repository\Sln.Iot.Repository.csproj" />
<ProjectReference Include="..\Sln.Iot.Serilog\Sln.Iot.Serilog.csproj" />
<ProjectReference Include="..\Sln.Iot.Socket\Sln.Iot.Socket.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,151 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Business.base
* AA8CCDD9-6D6B-47B7-8309-5C9D41FFC822
*
* WenJY
*
* 2025-04-11 15:04:43
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using Sln.Iot.Common;
using Sln.Iot.Config;
using Sln.Iot.Model.dto;
using Sln.Iot.Serilog;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Business.@base
{
public abstract class BaseBusiness
{
public SerilogHelper _logger;
public AppConfig _appConfig;
public StringChange _stringChange;
public BaseBusiness(SerilogHelper logger,AppConfig appConfig,StringChange stringChange)
{
_logger = logger;
_appConfig = appConfig;
_stringChange = stringChange;
}
/// <summary>
/// 指令解析方法
/// </summary>
/// <param name="client"></param>
/// <param name="requestInfo"></param>
public abstract FilterResult BufferAnalysis(ISocketClient client,BufferRequestInfo requestInfo,int bodyLength);
/// <summary>
/// 应答响应
/// </summary>
/// <param name="client"></param>
/// <param name="messagePack"></param>
public abstract void ResponseHandle(ISocketClient client, byte[] buffer);
/// <summary>
/// 封装回复指令
/// </summary>
/// <param name="SendMessagePackInfo"></param>
/// <param name="buffer"></param>
public void GetMessagePack(ref ResponsePack SendMessagePackInfo,byte[] buffer)
{
SendMessagePackInfo.m_EnergyType = buffer[1];
Array.Copy(buffer, 2, SendMessagePackInfo.m_Meteraddr, 0, 2);
Array.Copy(buffer, 4, SendMessagePackInfo.m_Msta, 0, 2);
SendMessagePackInfo.m_StartFlag = buffer[6];
}
/// <summary>
/// 发送接收成功指令
/// </summary>
/// <param name="client"></param>
/// <param name="pMessagePack"></param>
/// <returns></returns>
public bool SendMessageAsync(ISocketClient client, ResponsePack pMessagePack,byte[] buffer = null)
{
ushort num = 0;
try
{
byte[] SendBuffer = new byte[12];
if (buffer != null)
{
SendBuffer = new byte[12 + buffer.Length];
}
SendBuffer[num] = pMessagePack.m_BeginChar;
num = (ushort)(num + 1);
SendBuffer[num] = pMessagePack.m_EnergyType;
num = (ushort)(num + 1);
Array.Copy(pMessagePack.m_Meteraddr, 0, SendBuffer, num, pMessagePack.m_Meteraddr.Length);
num = (ushort)(num + 2);
Array.Copy(pMessagePack.m_Msta, 0, SendBuffer, num, pMessagePack.m_Msta.Length);
num = (ushort)(num + 2);
SendBuffer[num] = pMessagePack.m_StartFlag;
num = (ushort)(num + 1);
SendBuffer[num] = pMessagePack.m_MessageType;
num = (ushort)(num + 1);
Array.Copy(pMessagePack.m_PackLen, 0, SendBuffer, num, pMessagePack.m_PackLen.Length);
num = (ushort)(num + 2);
if (buffer != null)
{
Array.Copy(buffer, 0, SendBuffer, num, buffer.Length);
num = (ushort)(num + buffer.Length);
}
pMessagePack.m_Verify = _stringChange.CalculateVerifyToArray(SendBuffer, SendBuffer.Length - 1)[0];
SendBuffer[num] = pMessagePack.m_Verify;
num = (ushort)(num + 1);
SendBuffer[num] = pMessagePack.m_EndChar;
_logger.Info($"向客户端:{client.Id};地址:{client.GetIPPort()};发送终端消息:{_stringChange.bytesToHexStr(SendBuffer, SendBuffer.Length)}");
client.SendAsync(SendBuffer);
return true;
}
catch (Exception ex)
{
_logger.Error($"SendMessageToClient异常{ex.Message}");
return false;
}
}
/// <summary>
/// Nan 值处理
/// </summary>
/// <param name="value"></param>
public void ValueIsNan(ref float value)
{
if (double.IsNaN(value))
{
if (_appConfig.virtualFlag)
{
value = _appConfig.virtualValue;
}
else
{
value = 0;
}
}
}
}
}

@ -0,0 +1,5 @@
namespace Sln.Iot.Common;
public class Class1
{
}

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
</ItemGroup>
</Project>

@ -0,0 +1,320 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Common
* 78FCCE90-A4C0-4DCC-AB23-7C21EC7ECE05
*
* WenJY
*
* 2025-04-11 15:06:46
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
namespace Sln.Iot.Common
{
public class StringChange
{
/// <summary>
/// 将字符串强制转换成int转换失败则返回0
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public int ParseToInt(string str)
{
int returnInt = 0;
if (str == null || str.Trim().Length < 1)
{
return returnInt;
}
if (int.TryParse(str, out returnInt))
{
return returnInt;
}
else
{
return 0;
}
}
/// <summary>
/// char数组转Array
/// </summary>
/// <param name="cha"></param>
/// <param name="len"></param>
/// <returns></returns>
public string CharArrayToString(char[] cha, int len)
{
string str = "";
for (int i = 0; i < len; i++)
{
str += string.Format("{0}", cha[i]);
}
return str;
}
public byte[] HexStrTorbytes(string strHex)//e.g. " 01 01" ---> { 0x01, 0x01}
{
strHex = strHex.Replace(" ", "");
if ((strHex.Length % 2) != 0)
strHex += " ";
byte[] returnBytes = new byte[strHex.Length / 2];
for (int i = 0; i < returnBytes.Length; i++)
returnBytes[i] = Convert.ToByte(strHex.Substring(i * 2, 2), 16);
return returnBytes;
}
public string StringToHexString(string s, Encoding encode)
{
byte[] b = encode.GetBytes(s); //按照指定编码将string编程字节数组
string result = string.Empty;
for (int i = 0; i < b.Length; i++) //逐字节变为16进制字符以%隔开
{
result += "%" + Convert.ToString(b[i], 16);
}
return result;
}
public string HexStringToString(string hs, Encoding encode)
{
//以%分割字符串,并去掉空字符
string[] chars = hs.Split(new char[] { '%' }, StringSplitOptions.RemoveEmptyEntries);
byte[] b = new byte[chars.Length];
//逐个字符变为16进制字节数据
for (int i = 0; i < chars.Length; i++)
{
b[i] = Convert.ToByte(chars[i], 16);
}
//按照指定编码将字节数组变为字符串
return encode.GetString(b);
}
public byte[] Swap16Bytes(byte[] OldU16)
{
byte[] ReturnBytes = new byte[2];
ReturnBytes[1] = OldU16[0];
ReturnBytes[0] = OldU16[1];
return ReturnBytes;
}
/// <param name="strbase64">64Base码</param>
/// <param name="path">保存路径</param>
/// <param name="filename">文件名称</param>
/// <returns></returns>
public bool Base64ToImage(string strbase64, string path, string filename)
{
bool Flag = false;
try
{
//base64编码的文本 转为 图片
//图片名称
byte[] arr = Convert.FromBase64String(strbase64);//将指定的字符串(它将二进制数据编码为 Base64 数字)转换为等效的 8 位无符号整数数组。
using (MemoryStream ms = new MemoryStream(arr))
{
Bitmap bmp = new Bitmap(ms);//加载图像
if (!Directory.Exists(path))//判断保存目录是否存在
{
Directory.CreateDirectory(path);
}
bmp.Save((path + "\\" + filename + ".png"), System.Drawing.Imaging.ImageFormat.Png);//将图片以JPEG格式保存在指定目录(可以选择其他图片格式)
ms.Close();//关闭流并释放
if (File.Exists(path + "\\" + filename + ".png"))//判断是否存在
{
Flag = true;
}
}
}
catch (Exception ex)
{
Console.WriteLine("图片保存失败:" + ex.Message);
}
return Flag;
}
/// <summary>
/// 获取时间戳
/// </summary>
/// <returns></returns>
public long GetTimeStamp()
{
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds);
}
public byte[] ConvertFloatToINt(byte[] floatBytes)
{
byte[] intBytes = new byte[floatBytes.Length / 2];
for (int i = 0; i < intBytes.Length; i++)
{
intBytes[i] = floatBytes[i * 2];
}
return intBytes;
}
//CRC异或校验
public byte CalculateVerify(byte[] pMessage, int iLength)
{
UInt16 i;
byte iVerify = 0;
iVerify = pMessage[0];
for (i = 1; i < iLength; i++)
{
iVerify = (byte)(iVerify ^ pMessage[i]);
}
return iVerify;
}
public int HexStringToNegative(string strNumber)
{
int iNegate = 0;
int iNumber = Convert.ToInt32(strNumber, 16);
if (iNumber > 127)
{
int iComplement = iNumber - 1;
string strNegate = string.Empty;
char[] binchar = Convert.ToString(iComplement, 2).PadLeft(8, '0').ToArray();
foreach (char ch in binchar)
{
if (Convert.ToInt32(ch) == 48)
{
strNegate += "1";
}
else
{
strNegate += "0";
}
}
iNegate = -Convert.ToInt32(strNegate, 2);
}
return iNegate;
}
/// <summary>
/// Byte[] 转 uint16
/// </summary>
/// <param name="buffer"></param>
/// <param name="falg"></param>
/// <exception cref="ArgumentException"></exception>
public void ConvertBytesToUInt16(byte[] buffer,out uint falg)
{
if (buffer == null || buffer.Length < 2)
{
throw new ArgumentException("Input array length must be at least 2.");
}
var input = buffer.Reverse().ToArray();
falg = (uint) ((input[1] << 8) | input[0]);
}
/// <summary>
/// Byte[] 移位转换
/// </summary>
/// <param name="input"></param>
/// <exception cref="ArgumentException"></exception>
public void SwapBytes(ref byte[] input)
{
if (input == null || input.Length % 2 != 0)
{
throw new ArgumentException("Input array length must be a multiple of 2.");
}
byte[] result = new byte[input.Length];
for (int j = 0; j < input.Length; j += 2)
{
ushort swapped = (ushort)((input[j + 1] << 8) | input[j]);
result[j] = (byte)(swapped >> 8);
result[j + 1] = (byte)swapped;
}
input = result;
}
/// <summary>
/// Byte[] 转string
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public string ConverToString(byte[] data)
{
string str;
StringBuilder stb = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
if ((int)data[i] > 15)
{
stb.Append(Convert.ToString(data[i], 16).ToUpper()); //添加字符串
}
else //如果是小于0F需要加个零
{
stb.Append("0" + Convert.ToString(data[i], 16).ToUpper());
}
}
str = stb.ToString();
return str;
}
/// <summary>
/// Byte[] 转 Hex
/// </summary>
/// <param name="bytes"></param>
/// <param name="iLen"></param>
/// <returns></returns>
public string bytesToHexStr(byte[] bytes, int iLen)
{
StringBuilder sb = new StringBuilder();
if (bytes != null)
{
for (int i = 0; i < iLen; i++)
{
sb.Append(bytes[i].ToString("X2"));
}
}
return sb.ToString();
}
/// <summary>
/// 校验计算
/// </summary>
/// <param name="pMessage"></param>
/// <param name="iLength"></param>
/// <returns></returns>
public byte[] CalculateVerifyToArray(byte[] pMessage, int iLength)
{
UInt16 i;
int iVerify = 0;
iVerify = pMessage[0];
for (i = 0; i < iLength - 1; i++)
{
iVerify = iVerify + pMessage[i + 1];
}
return BitConverter.GetBytes(Convert.ToUInt16(iVerify));
}
}
}

@ -0,0 +1,75 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Config
* 132376EA-435B-4340-B8F3-44D61AAE1E99
*
* WenJY
*
* 2025-04-11 10:54:31
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System.Collections.Generic;
using Microsoft.Extensions.Options;
namespace Sln.Iot.Config
{
public class AppConfig: IOptions<AppConfig>
{
/// <summary>
/// 日志文件路径
/// </summary>
public string logPath { get; set; }
/// <summary>
/// 监听端口
/// </summary>
public int listernPort { get; set; }
/// <summary>
/// 虚拟值用于过滤FFFFFF
/// </summary>
public long virtualValue { get; set; }
/// <summary>
/// 是否启用虚拟值
/// </summary>
public bool virtualFlag { get; set; }
/// <summary>
/// 电力数据采集间隔
/// </summary>
public int electricTimeInterval { get; set; }
/// <summary>
/// 流体数据采集间隔
/// </summary>
public int fluidTimeInterval { get; set; }
/// <summary>
/// Sql连接配置
/// </summary>
public List<SqlConfig> sqlConfig { get; set; }
/// <summary>
/// Redis配置
/// </summary>
public string redisConfig { get; set; }
public AppConfig Value => this;
}
}

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options" Version="9.0.0" />
</ItemGroup>
</Project>

@ -0,0 +1,50 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Config
* E59D09B8-4750-4486-BB59-5983CD7FB394
*
* WenJY
*
* 2025-04-11 13:40:21
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
namespace Sln.Iot.Config
{
public class SqlConfig
{
/// <summary>
/// Sql 配置ID实体通过该ID关联数据源
/// </summary>
public string configId { get; set; }
/// <summary>
/// 数据库类型MySql-0;SqlServer-1;Sqlite-2;Oracle-3
/// </summary>
public int dbType { get; set; }
/// <summary>
/// 是否启用true-是false-否
/// </summary>
public bool isFlag{get;set;}
/// <summary>
/// 连接字符串
/// </summary>
public string connStr { get; set; }
}
}

@ -0,0 +1,5 @@
namespace Sln.Iot.Model;
public class Class1
{
}

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SqlSugarCore" Version="5.1.4.193" />
</ItemGroup>
</Project>

@ -0,0 +1,209 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Model.dao
* 74702D18-55B3-4AA3-91EB-E02ADC9AEBC8
*
* WenJY
*
* 2025-04-11 13:23:39
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using SqlSugar;
namespace Sln.Iot.Model.dao
{
///<summary>
///计量设备信息
///</summary>
[SugarTable("ems_base_monitor_info"), TenantAttribute("tao_iot")]
public partial class BaseMonitorInfo
{
public BaseMonitorInfo(){
}
/// <summary>
/// Desc:自增标识
/// Default:
/// Nullable:False
/// </summary>
[SugarColumn(IsPrimaryKey=true,IsIdentity=true,ColumnName="obj_id")]
public int objid {get;set;}
/// <summary>
/// Desc:父级编号
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="parent_id")]
public int? parentId {get;set;}
/// <summary>
/// Desc:计量设备编号
/// Default:
/// Nullable:False
/// </summary>
[SugarColumn(IsPrimaryKey=true,ColumnName="monitor_code")]
public string monitorId {get;set;}
/// <summary>
/// Desc:计量设备名称
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="monitor_name")]
public string monitorName {get;set;}
/// <summary>
/// Desc:计量设备位置
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="monitor_addr")]
public string monitorAddr {get;set;}
/// <summary>
/// Desc:计量设备类型
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="monitor_type")]
public int? monitorType {get;set;}
/// <summary>
/// Desc:计量设备状态
/// Default:0
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="monitor_status")]
public int? monitorStatus {get;set;}
/// <summary>
/// Desc:采集设备编号
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="collect_device_id")]
public string collectDeviceId {get;set;}
/// <summary>
/// Desc:祖级列表
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="ancestors")]
public string ancestors {get;set;}
/// <summary>
/// Desc:等级
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="grade")]
public int? grade {get;set;}
/// <summary>
/// Desc:传感器仪表
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="meter_type_id")]
public string meterTypeId {get;set;}
/// <summary>
/// Desc:修正值
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="correct_value")]
public decimal? correctValue {get;set;}
/// <summary>
/// Desc:PT值
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="pt")]
public int? pt {get;set;}
/// <summary>
/// Desc:CT值
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="ct")]
public int? ct {get;set;}
/// <summary>
/// Desc:是否虚拟
/// Default:false
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="is_ammeter")]
public string isAmmeter {get;set;}
/// <summary>
/// Desc:通断复位
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="is_key_monitor")]
public int? isKeyMonitor {get;set;}
/// <summary>
/// Desc:是否断路
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="is_circuit")]
public int? isCircuit {get;set;}
/// <summary>
/// Desc:创建人
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="create_by")]
public string createBy {get;set;}
/// <summary>
/// Desc:创建时间
/// Default:CURRENT_TIMESTAMP
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="create_time")]
public DateTime? createTime {get;set;}
/// <summary>
/// Desc:更新人
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="update_by")]
public string updateBy {get;set;}
/// <summary>
/// Desc:更新时间
/// Default:
/// Nullable:True
/// </summary>
[SugarColumn(ColumnName="update_time")]
public DateTime? updateTime {get;set;}
}
}

@ -0,0 +1,132 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Model.dao
* B1423370-1BD2-4199-AEDC-80C06296A9BC
*
* WenJY
*
* 2025-05-20 10:57:48
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using SqlSugar;
namespace Sln.Iot.Model.dao
{
/// <summary>
/// 电实时数据
/// </summary>
[SplitTable(SplitType.Day)]
[SugarTable("record_dnb_instant_{year}{month}{day}"), TenantAttribute("tao_iot")]
public class RecordDnbInstant
{
/// <summary>
/// 编号
///</summary>
[SugarColumn(ColumnName="objid" ,IsPrimaryKey = true )]
public long objid { get; set; }
/// <summary>
/// 计量设备编号
///</summary>
[SugarColumn(ColumnName="monitor_id" )]
public string monitorId { get; set; }
/// <summary>
/// A项电压
///</summary>
[SugarColumn(ColumnName="va" )]
public decimal? vA { get; set; }
/// <summary>
/// B项电压
///</summary>
[SugarColumn(ColumnName="vb" )]
public decimal? vB { get; set; }
/// <summary>
/// C项电压
///</summary>
[SugarColumn(ColumnName="vc" )]
public decimal? vC { get; set; }
/// <summary>
/// A项电流
///</summary>
[SugarColumn(ColumnName="ia" )]
public decimal? iA { get; set; }
/// <summary>
/// B项电流
///</summary>
[SugarColumn(ColumnName="ib" )]
public decimal? iB { get; set; }
/// <summary>
/// C项电流
///</summary>
[SugarColumn(ColumnName="ic" )]
public decimal? iC { get; set; }
/// <summary>
/// 功率因数
///</summary>
[SugarColumn(ColumnName="glys" )]
public decimal? powerFactor { get; set; }
/// <summary>
/// 正向有功
///</summary>
[SugarColumn(ColumnName="zxyg" )]
public decimal? positiveActive { get; set; }
/// <summary>
/// 有功功率
///</summary>
[SugarColumn(ColumnName="active_power" )]
public decimal? activePower { get; set; }
/// <summary>
/// 无功功率
///</summary>
[SugarColumn(ColumnName="reactive_power" )]
public decimal? reactivePower { get; set; }
/// <summary>
/// 采集方式
/// 默认值: 0
///</summary>
[SugarColumn(ColumnName="collect_type" )]
public int collectType
{
get { return 1;}
}
/// <summary>
/// 采集时间
///</summary>
[SugarColumn(ColumnName="collect_time" )]
public DateTime? collectTime { get; set; }
/// <summary>
/// 记录时间
///</summary>
[SplitField]
[SugarColumn(ColumnName="record_time" )]
public DateTime? recordTime { get; set; }
}
}

@ -0,0 +1,121 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Model.dao
* CB52EBBD-0D7F-498D-B2D0-B9926F06F5AF
*
* WenJY
*
* 2025-05-20 14:40:03
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using SqlSugar;
namespace Sln.Iot.Model.dao
{
/// <summary>
/// 流体实时数据
/// </summary>
[SplitTable(SplitType.Day)]
[SugarTable("record_fluid_instant_{year}{month}{day}"), Tenant("tao_iot")]
public class RecordFluidInstant
{
/// <summary>
/// 自增标识
///</summary>
[SugarColumn(ColumnName="objid" ,IsPrimaryKey = true )]
public long objid { get; set; }
/// <summary>
/// 计量设备编号
///</summary>
[SugarColumn(ColumnName="monitor_id" )]
public string monitorId { get; set; }
/// <summary>
/// 温度值
/// </summary>
[SugarColumn(ColumnName="temperature" )]
public decimal? temperature{get;set;}
/// <summary>
/// 压力值
/// </summary>
[SugarColumn(ColumnName="press" )]
public decimal? press{get;set;}
/// <summary>
/// 频率值
/// </summary>
[SugarColumn(ColumnName="frequency" )]
public decimal? frequency{get;set;}
/// <summary>
/// 密度值
/// </summary>
[SugarColumn(ColumnName="density" )]
public decimal? density{get;set;}
/// <summary>
/// 瞬时热量
///</summary>
[SugarColumn(ColumnName="instant_heat" )]
public decimal? instantHeat { get; set; }
/// <summary>
/// 累计热量
///</summary>
[SugarColumn(ColumnName="total_heat" )]
public decimal? totalHeat { get; set; }
/// <summary>
/// 瞬时流量
///</summary>
[SugarColumn(ColumnName="instant_flow" )]
public decimal? instantFlow { get; set; }
/// <summary>
/// 累计流量
///</summary>
[SugarColumn(ColumnName="total_flow" )]
public decimal? totalFlow { get; set; }
/// <summary>
/// 采集方式
/// 默认值: 0
///</summary>
[SugarColumn(ColumnName="collect_type" )]
public int collectType
{
get { return 1;}
}
/// <summary>
/// 采集时间
///</summary>
[SugarColumn(ColumnName="collect_time" )]
public DateTime? collectTime { get; set; }
/// <summary>
/// 记录时间
///</summary>
[SplitField]
[SugarColumn(ColumnName="record_time" )]
public DateTime? recordTime { get; set; }
}
}

@ -0,0 +1,116 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Model.dao
* 2946BBED-E772-4BC4-953F-F4B9834C27F6
*
* WenJY
*
* 2025-04-11 15:20:56
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using SqlSugar;
namespace Sln.Iot.Model.dao
{
[SplitTable(SplitType.Day)]
[SugarTable("record_iotenv_instant_{year}{month}{day}"), TenantAttribute("tao_iot")]
public class RecordIotEnvInstant
{
/// <summary>
/// 编号 ,IsIdentity = true
///</summary>
[SugarColumn(ColumnName="objid" ,IsPrimaryKey = true )]
public long objid { get; set; }
/// <summary>
/// 计量设备编号
///</summary>
[SugarColumn(ColumnName="monitorId" )]
public string monitorId { get; set; }
/// <summary>
/// 温度
/// </summary>
[SugarColumn(ColumnName="temperature" )]
public decimal temperature { get; set; }
/// <summary>
/// 湿度
/// </summary>
[SugarColumn(ColumnName="humidity" )]
public decimal humidity { get; set; }
/// <summary>
/// 照度
/// </summary>
[SugarColumn(ColumnName="illuminance" )]
public decimal illuminance { get; set; }
/// <summary>
/// 噪音
/// </summary>
[SugarColumn(ColumnName="noise" )]
public decimal noise { get; set; }
/// <summary>
/// 气体浓度
/// </summary>
[SugarColumn(ColumnName="concentration" )]
public decimal concentration { get; set; }
/// <summary>
/// 振动-速度
/// </summary>
[SugarColumn(ColumnName = "vibration_speed")]
public decimal VibrationSpeed { get; set; }
/// <summary>
/// 振动-位移
/// </summary>
[SugarColumn(ColumnName = "vibration_displacement")]
public decimal VibrationDisplacement { get; set; }
/// <summary>
/// 振动-加速度
/// </summary>
[SugarColumn(ColumnName = "vibration_acceleration")]
public decimal VibrationAcceleration { get; set; }
/// <summary>
/// 振动-温度
/// </summary>
[SugarColumn(ColumnName = "vibration_temp")]
public decimal VibrationTemp { get; set; }
/// <summary>
/// 采集时间
///</summary>
[SugarColumn(ColumnName="collectTime" )]
public DateTime? collectTime { get; set; }
/// <summary>
/// 记录时间
///</summary>
[SplitField]
[SugarColumn(ColumnName="recodeTime" )]
public DateTime? recordTime { get; set; }
}
}

@ -0,0 +1,100 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Model.dto
* CA8D9816-589C-4E15-96FD-CA864799054D
*
* WenJY
*
* 2025-04-11 15:10:04
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
namespace Sln.Iot.Model.dto
{
public struct CommParams
{
/// <summary>
/// 温度
/// </summary>
public const uint TTempreture = 0x8E50;
/// <summary>
/// 湿度
/// </summary>
public const uint Humidity = 0x8E52;
/// <summary>
/// 噪音
/// </summary>
public const uint Noise = 0x8E53;
/// <summary>
/// 振动-速度
/// </summary>
public const uint VibrationSpeed = 0x8E54;
/// <summary>
/// 振动-位移
/// </summary>
public const uint VibrationDisplacement = 0x8E55;
/// <summary>
/// 振动-加速度
/// </summary>
public const uint VibrationAcceleration = 0x8E56;
/// <summary>
/// 振动-温度
/// </summary>
public const uint VibrationTemp = 0x8E57;
/// <summary>
/// 采集时间
/// </summary>
public const uint CJSJ = 0x8030;
#region 电能数据结构体
public const uint AI = 0x8E21;
public const uint BI = 0x8E22;
public const uint CI = 0x8E23;
public const uint AU = 0x8E11;
public const uint BU = 0x8E12;
public const uint CU = 0x8E13;
public const uint GLYS = 0xB650;
public const uint ZXYGZ = 0x9010;
public const uint ZXWG = 0x9030;
public const uint YGGL = 0xB651;
public const uint WGGL = 0xB652;
#endregion
#region 流体数据结构体
public const uint Press = 0x9B00;
public const uint STemperature = 0x9B01;
public const uint Frequency = 0x9B02;
public const uint FluxInstantValue = 0x9B03;
public const uint FluxEyeableTotalValue = 0x9B05;
public const uint HeatInstantValue = 0x9B06;
public const uint HeatToftalValue = 0x9B07;
public const uint Density = 0x9B0E;
#endregion
}
}

@ -0,0 +1,40 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Model.dto
* CE889E7E-08A9-4043-A157-32CB2773E568
*
* WenJY
*
* 2025-04-11 15:08:47
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
namespace Sln.Iot.Model.dto
{
public class ResponsePack
{
public byte m_BeginChar = 0x68; //帧头
public byte m_EnergyType; //能源类型
public byte[] m_Meteraddr = new byte[2];//地址
public byte[] m_Msta = new byte[2]; //命令序列号
public byte m_StartFlag; //起始符
public byte m_MessageType; //控制码
public byte[] m_PackLen = new byte[2]; //数据长度
public byte m_Verify;
public byte m_EndChar = 0x16; //尾盘
}
}

@ -0,0 +1,42 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Model.dto
* CF7EE865-8FD7-4B6E-BD69-B86E09886CB6
*
* WenJY
*
* 2025-04-11 15:10:36
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
namespace Sln.Iot.Model.dto
{
public class TagInfo
{
public byte[] PC = new byte[2];
public int Count { get; set; }
public int RSSI{ get; set; }
public int Antana{ get; set; }
public byte[] EPC{ get; set; }
public byte[] Data{ get; set; }
public string PCstring = (string) null;
public string EPCstring = (string) null;
public DateTime Time{ get; set; }
}
}

@ -0,0 +1,5 @@
namespace Sln.Iot.Repository;
public class Class1
{
}

@ -0,0 +1,44 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository
* 130152CF-25A2-4CF6-BF55-FF62811E139D
*
* WenJY
*
* 2025-04-11 13:24:59
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using SqlSugar;
namespace Sln.Iot.Repository
{
public class Repository<T> : SimpleClient<T> where T : class, new()
{
public ITenant itenant = null;//多租户事务、GetConnection、IsAnyConnection等功能
public Repository(ISqlSugarClient db)
{
itenant = db.AsTenant();//用来处理事务
base.Context = db.AsTenant().GetConnectionScopeWithAttr<T>();//获取子Db
//如果不想通过注入多个仓储
//用到ChangeRepository或者Db.GetMyRepository需要看标题4写法
}
}
}

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Content Include="service\.DS_Store" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sln.Iot.Config\Sln.Iot.Config.csproj" />
<ProjectReference Include="..\Sln.Iot.Model\Sln.Iot.Model.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,73 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository
* EBCC8183-D907-4049-B036-0C5BA2284E22
*
* WenJY
*
* 2025-04-11 13:39:23
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Sln.Iot.Config;
using SqlSugar;
namespace Sln.Iot.Repository
{
public static class SqlsugarSetup
{
/// <summary>
/// 注册SqlSugar
/// </summary>
/// <param name="services"></param>
public static void AddSqlSugarSetup(this IServiceCollection services)
{
services.AddSingleton<ISqlSugarClient>(x =>
{
var appConfig = x.GetService<AppConfig>();
var connectConfigList = new List<ConnectionConfig>();
if (appConfig.sqlConfig != null)
{
foreach (var item in appConfig.sqlConfig)
{
if (item.isFlag)
{
var config = new ConnectionConfig()
{
ConfigId = item.configId,
DbType = (DbType)item.dbType,
ConnectionString = item.connStr,
InitKeyType = InitKeyType.Attribute,
IsAutoCloseConnection = true,
};
connectConfigList.Add(config);
}
}
}
SqlSugarScope Db = new SqlSugarScope(connectConfigList, db =>
{
db.Aop.OnLogExecuting = (sql, pars) => { };
});
return Db;
});
}
}
}

Binary file not shown.

@ -0,0 +1,35 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service
* 9B119436-8BE4-4693-9D28-F971874E03CB
*
* WenJY
*
* 2025-04-11 13:27:35
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using Sln.Iot.Model.dao;
using Sln.Iot.Repository.service.@base;
namespace Sln.Iot.Repository.service
{
public interface IBaseMonitorInfoService : IBaseService<BaseMonitorInfo>
{
}
}

@ -0,0 +1,42 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service
* D397E4B7-48E1-4BF3-8906-B11F96EE8B42
*
* WenJY
*
* 2025-05-20 11:26:07
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System.Collections.Generic;
using Sln.Iot.Model.dao;
using Sln.Iot.Repository.service.@base;
namespace Sln.Iot.Repository.service
{
public interface IRecordDnbInstantService:IBaseService<RecordDnbInstant>
{
/// <summary>
/// 分表保存
/// </summary>
/// <param name="list"></param>
/// <param name="insertIds"></param>
/// <returns></returns>
bool SplitInsert(List<RecordDnbInstant> list,out List<long> insertIds);
}
}

@ -0,0 +1,42 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service
* 6403A242-FF9C-4942-B02B-8680BBD7ACBD
*
* WenJY
*
* 2025-05-20 14:46:25
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System.Collections.Generic;
using Sln.Iot.Model.dao;
using Sln.Iot.Repository.service.@base;
namespace Sln.Iot.Repository.service
{
public interface IRecordFluidInstantService:IBaseService<RecordFluidInstant>
{
/// <summary>
/// 分表保存
/// </summary>
/// <param name="list"></param>
/// <param name="insertIds"></param>
/// <returns></returns>
bool SplitInsert(List<RecordFluidInstant> list,out List<long> insertIds);
}
}

@ -0,0 +1,42 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service
* 44931288-195F-4560-B9D8-9EA8A511FE8A
*
* WenJY
*
* 2025-04-11 15:44:29
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System.Collections.Generic;
using Sln.Iot.Model.dao;
using Sln.Iot.Repository.service.@base;
namespace Sln.Iot.Repository.service
{
public interface IRecordIotEnvInstantService:IBaseService<RecordIotEnvInstant>
{
/// <summary>
/// 分表保存
/// </summary>
/// <param name="list"></param>
/// <param name="insertIds"></param>
/// <returns></returns>
bool SplitInsert(List<RecordIotEnvInstant> list,out List<long> insertIds);
}
}

@ -0,0 +1,37 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service.Impl
* 625A971B-5BA8-4E05-9AE1-DC6D8E3D6051
*
* WenJY
*
* 2025-04-11 13:28:02
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using Sln.Iot.Model.dao;
using Sln.Iot.Repository.service.@base;
namespace Sln.Iot.Repository.service.Impl
{
public class BaseMonitorInfoServiceImpl : BaseServiceImpl<BaseMonitorInfo>, IBaseMonitorInfoService
{
public BaseMonitorInfoServiceImpl(Repository<BaseMonitorInfo> repository):base(repository)
{
}
}
}

@ -0,0 +1,73 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service.Impl
* EF060FA7-6D52-478C-84FF-F26EEC206685
*
* WenJY
*
* 2025-05-20 11:26:53
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using Sln.Iot.Model.dao;
using Sln.Iot.Repository.service.@base;
namespace Sln.Iot.Repository.service.Impl
{
public class RecordDnbInstantServiceImpl: BaseServiceImpl<RecordDnbInstant>, IRecordDnbInstantService
{
public RecordDnbInstantServiceImpl(Repository<RecordDnbInstant> rep) : base(rep)
{
}
/// <summary>
/// 分表保存
/// </summary>
/// <param name="list"></param>
/// <param name="insertIds"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public bool SplitInsert(List<RecordDnbInstant> list,out List<long> insertIds)
{
if (list == null)
{
throw new ArgumentNullException($"参数为空");
}
try
{
// _rep.AsTenant().BeginTran();
var sqlSugarClient = _rep.Context;
//加载指定分表策略
//sqlSugarClient.CurrentConnectionConfig.ConfigureExternalServices.SplitTableService = new MonitorIdToSplitService();
insertIds = sqlSugarClient.Insertable(list).SplitTable().ExecuteReturnSnowflakeIdList();
// _rep.AsTenant().CommitTran();
return true;
}
catch (Exception ex)
{
// _rep.AsTenant().RollbackTran();
throw new InvalidOperationException($"电能数据分表保存异常:{ex.Message}");
}
}
}
}

@ -0,0 +1,73 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service.Impl
* 72B88660-5410-4016-9E12-B1E11841AB4B
*
* WenJY
*
* 2025-05-20 14:47:03
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using Sln.Iot.Model.dao;
using Sln.Iot.Repository.service.@base;
namespace Sln.Iot.Repository.service.Impl
{
public class RecordFluidInstantServiceImpl: BaseServiceImpl<RecordFluidInstant>, IRecordFluidInstantService
{
public RecordFluidInstantServiceImpl(Repository<RecordFluidInstant> rep) : base(rep)
{
}
/// <summary>
/// 分表保存
/// </summary>
/// <param name="list"></param>
/// <param name="insertIds"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public bool SplitInsert(List<RecordFluidInstant> list,out List<long> insertIds)
{
if (list == null)
{
throw new ArgumentNullException($"参数为空");
}
try
{
// _rep.AsTenant().BeginTran();
var sqlSugarClient = _rep.Context;
//加载指定分表策略
//sqlSugarClient.CurrentConnectionConfig.ConfigureExternalServices.SplitTableService = new MonitorIdToSplitService();
insertIds = sqlSugarClient.Insertable(list).SplitTable().ExecuteReturnSnowflakeIdList();
// _rep.AsTenant().CommitTran();
return true;
}
catch (Exception ex)
{
// _rep.AsTenant().RollbackTran();
throw new InvalidOperationException($"流体数据分表保存异常:{ex.Message}");
}
}
}
}

@ -0,0 +1,74 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service.Impl
* C914F6F6-C7CC-4DDC-B061-C89A15419EF0
*
* WenJY
*
* 2025-04-11 15:44:49
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using Sln.Iot.Model.dao;
using Sln.Iot.Repository.service.@base;
using Sln.Iot.Repository.service.split;
namespace Sln.Iot.Repository.service.Impl
{
public class RecordIotEnvInstantServiceImpl: BaseServiceImpl<RecordIotEnvInstant>, IRecordIotEnvInstantService
{
public RecordIotEnvInstantServiceImpl(Repository<RecordIotEnvInstant> rep) : base(rep)
{
}
/// <summary>
/// 分表保存
/// </summary>
/// <param name="list"></param>
/// <param name="insertIds"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public bool SplitInsert(List<RecordIotEnvInstant> list,out List<long> insertIds)
{
if (list == null)
{
throw new ArgumentNullException($"参数为空");
}
try
{
// _rep.AsTenant().BeginTran();
var sqlSugarClient = _rep.Context;
//加载指定分表策略
//sqlSugarClient.CurrentConnectionConfig.ConfigureExternalServices.SplitTableService = new MonitorIdToSplitService();
insertIds = sqlSugarClient.Insertable(list).SplitTable().ExecuteReturnSnowflakeIdList();
// _rep.AsTenant().CommitTran();
return true;
}
catch (Exception ex)
{
// _rep.AsTenant().RollbackTran();
throw new InvalidOperationException($"物联网数据分表保存异常:{ex.Message}");
}
}
}
}

@ -0,0 +1,359 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service.base
* AAD164C5-C115-422B-B57B-E9669385D083
*
* WenJY
*
* 2025-04-11 13:26:34
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using SqlSugar;
namespace Sln.Iot.Repository.service.@base
{
public class BaseServiceImpl<T> : IBaseService<T> where T : class, new()
{
public readonly Repository<T> _rep;
public BaseServiceImpl(Repository<T> rep)
{
_rep = rep;
}
/// <summary>
/// 添加实体信息
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public bool Insert(T model)
{
if (model == null)
{
throw new ArgumentNullException($"添加实体信息异常:实体参数为空");
}
try
{
return _rep.CopyNew().Insert(model);
}
catch (Exception ex)
{
throw new InvalidOperationException($"添加实体信息异常:{ex.Message}");
}
}
/// <summary>
/// 批量添加实体集合
/// </summary>
/// <param name="lisT"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public bool Insert(List<T> lisT)
{
if (lisT == null)
{
throw new ArgumentNullException($"批量添加实体集合异常:实体集合参数为空");
}
try
{
// _rep.AsTenant().BeginTran();
var info = _rep.CopyNew().InsertRange(lisT);
// _rep.AsTenant().CommitTran();
return true;
}
catch (Exception ex)
{
// _rep.AsTenant().RollbackTran();
throw new InvalidOperationException($"批量添加实体集合异常:{ex.Message}");
}
}
/// <summary>
/// 根据id 删除信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public bool DeleteById(object id)
{
if (id == null)
{
throw new ArgumentNullException($"根据id删除信息异常:Id参数为空");
}
try
{
return _rep.DeleteById(id);
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据id删除信息异常:{ex.Message}");
}
}
/// <summary>
/// 根据实体删除信息
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="InvalidOperationException"></exception>
public bool Delete(T model)
{
if (model == null)
{
throw new ArgumentNullException($"根据实体删除信息异常:实体参数为空");
}
try
{
return _rep.DeleteById(model);
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据实体删除信息异常:{ex.Message}");
}
}
/// <summary>
/// 根据实体集合批量删除信息
/// </summary>
/// <param name="entitys"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public bool Deletes(List<T> entitys)
{
if (entitys == null)
{
throw new ArgumentNullException($"根据实体集合批量删除信息异常:实体集合参数为空");
}
try
{
return _rep.Delete(entitys);
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据实体集合批量删除信息异常:{ex.Message}");
}
}
/// <summary>
/// 根据实体更新信息
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public bool Update(T model)
{
if (model == null)
{
throw new ArgumentNullException($"根据实体更新信息异常:实体参数为空");
}
try
{
return _rep.Update(model);
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据实体更新信息异常:{ex.Message}");
}
}
/// <summary>
/// 批量更新实体集合信息
/// </summary>
/// <param name="entitys"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public bool Update(List<T> entitys)
{
if (entitys == null)
{
throw new ArgumentNullException($"批量更新实体集合信息异常:实体集合参数为空");
}
try
{
return _rep.UpdateRange(entitys);
}
catch (Exception ex)
{
throw new InvalidOperationException($"批量更新实体集合信息异常:{ex.Message}");
}
}
/// <summary>
/// 根据Where条件更新实体信息
/// </summary>
/// <param name="entity"></param>
/// <param name="strWhere"></param>
/// <returns></returns>
public bool Update(T entity, string strWhere)
{
if (entity == null)
{
throw new ArgumentNullException($"根据Where条件更新实体信息异常:实体参数为空");
}
if (string.IsNullOrEmpty(strWhere))
{
throw new ArgumentNullException($"根据Where条件更新实体信息异常:Where参数为空");
}
try
{
return _rep.AsUpdateable(entity).Where(strWhere).ExecuteCommandHasChange();
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据Where条件更新实体信息异常:{ex.Message}");
}
}
/// <summary>
/// 根据实体更新指定列
/// </summary>
/// <param name="entity"></param>
/// <param name="lstColumns"></param>
/// <param name="lstIgnoreColumns"></param>
/// <param name="strWhere"></param>
/// <returns></returns>
public bool Update(T entity, List<string> lstColumns = null, List<string> lstIgnoreColumns = null, string strWhere = "")
{
try
{
IUpdateable<T> up = _rep.AsUpdateable(entity);
if (lstIgnoreColumns != null && lstIgnoreColumns.Count > 0)
{
up = up.IgnoreColumns(lstIgnoreColumns.ToArray());
}
if (lstColumns != null && lstColumns.Count > 0)
{
up = up.UpdateColumns(lstColumns.ToArray());
}
if (!string.IsNullOrEmpty(strWhere))
{
up = up.Where(strWhere);
}
return up.ExecuteCommandHasChange();
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据实体更新指定列异常:{ex.Message}");
}
}
/// <summary>
/// 查询所有信息
/// </summary>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public List<T> Query()
{
try
{
return _rep.GetList();
}
catch (Exception ex)
{
throw new InvalidOperationException($"查询所有信息异常:{ex.Message}");
}
}
/// <summary>
/// 根据Id查询实体
/// </summary>
/// <param name="objId"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public T Query(object objId)
{
if (objId == null)
{
throw new ArgumentNullException($"根据Id查询实体信息异常:Id参数为空");
}
try
{
return _rep.GetById(objId);
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据Id查询实体信息异常:{ex.Message}");
}
}
/// <summary>
/// 根据表达式查询
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public List<T> Query(Expression<Func<T, bool>> whereExpression)
{
if (whereExpression == null)
{
throw new ArgumentNullException($"根据表达式查询实体信息异常:表达式参数为空");
}
try
{
return _rep.GetList(whereExpression);
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据表达式查询实体信息异常:{ex.Message}");
}
}
/// <summary>
/// 根据表达式排序查询
/// </summary>
/// <param name="whereExpression"></param>
/// <param name="orderByExpression"></param>
/// <param name="isAsc"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public List<T> Query(Expression<Func<T, bool>> whereExpression, Expression<Func<T, object>> orderByExpression, bool isAsc = true)
{
if (whereExpression == null)
{
throw new ArgumentNullException($"根据表达式排序查询信息异常:条件表达式参数为空");
}
if (orderByExpression == null)
{
throw new ArgumentNullException($"根据表达式排序查询信息异常:排序表达式参数为空");
}
try
{
return _rep.AsQueryable().OrderByIF(orderByExpression != null, orderByExpression, isAsc ? OrderByType.Asc : OrderByType.Desc).WhereIF(whereExpression != null, whereExpression).ToList();
}
catch (Exception ex)
{
throw new InvalidOperationException($"根据表达式排序查询信息异常:{ex.Message}");
}
}
}
}

@ -0,0 +1,130 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service.base
* 4F2637EA-9206-45C6-92B8-E2CDBA5A1B22
*
* WenJY
*
* 2025-04-11 13:26:02
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace Sln.Iot.Repository.service.@base
{
public interface IBaseService<T> where T : class
{
/// <summary>
/// 添加实体信息
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
bool Insert(T model);
/// <summary>
/// 批量添加实体集合
/// </summary>
/// <param name="lisT"></param>
/// <returns></returns>
bool Insert(List<T> lisT);
/// <summary>
/// 根据id 删除信息
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
bool DeleteById(object id);
/// <summary>
/// 根据实体删除信息
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
bool Delete(T model);
/// <summary>
/// 根据实体集合批量删除信息
/// </summary>
/// <param name="ids"></param>
/// <returns></returns>
bool Deletes(List<T> entitys);
/// <summary>
/// 根据实体更新信息
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
bool Update(T model);
/// <summary>
/// 批量更新实体集合信息
/// </summary>
/// <param name="entitys"></param>
/// <returns></returns>
bool Update(List<T> entitys);
/// <summary>
/// 根据Where条件更新实体信息
/// </summary>
/// <param name="entity"></param>
/// <param name="strWhere"></param>
/// <returns></returns>
bool Update(T entity, string strWhere);
/// <summary>
/// 根据实体更新指定列
/// </summary>
/// <param name="entity"></param>
/// <param name="lstColumns"></param>
/// <param name="lstIgnoreColumns"></param>
/// <param name="strWhere"></param>
/// <returns></returns>
bool Update(T entity, List<string> lstColumns = null, List<string> lstIgnoreColumns = null, string strWhere = "");
/// <summary>
/// 查询所有信息
/// </summary>
/// <returns></returns>
List<T> Query();
/// <summary>
/// 根据Id查询实体
/// </summary>
/// <param name="objId"></param>
/// <returns></returns>
T Query(object objId);
/// <summary>
/// 根据表达式查询
/// </summary>
/// <param name="whereExpression"></param>
/// <returns></returns>
List<T> Query(Expression<Func<T, bool>> whereExpression);
/// <summary>
/// 根据表达式排序查询
/// </summary>
/// <param name="whereExpression">查询条件</param>
/// <param name="orderByExpression">排序条件</param>
/// <param name="isAsc">是否正序</param>
/// <returns></returns>
List<T> Query(Expression<Func<T, bool>> whereExpression, Expression<Func<T, object>> orderByExpression, bool isAsc = true);
}
}

@ -0,0 +1,97 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Repository.service.split
* 2D1DE17A-79B9-48D8-BB98-60E89BEC711A
*
* WenJY
*
* 2025-04-27 10:10:14
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using SqlSugar;
namespace Sln.Iot.Repository.service.split
{
/// <summary>
/// 自定义分表策略:根据传感器 ID 进行分表
/// </summary>
public class MonitorIdToSplitService:ISplitTableService
{
/// <summary>
/// 返回数据库中所有分表
/// </summary>
/// <param name="db"></param>
/// <param name="EntityInfo"></param>
/// <param name="tableInfos"></param>
/// <returns></returns>
public List<SplitTableInfo> GetAllTables(ISqlSugarClient db, EntityInfo EntityInfo, List<DbTableInfo> tableInfos)
{
List<SplitTableInfo> result = new List<SplitTableInfo>();
foreach (var item in tableInfos)
{
if (item.Name.Contains("record_iotenv_instant")) //区分标识如果不用正则符复杂一些,防止找错表
{
SplitTableInfo data = new SplitTableInfo()
{
TableName = item.Name //要用item.name不要写错了
};
result.Add(data);
}
}
return result.OrderBy(it=>it.TableName).ToList();//打断点看一下有没有查出所有分表
}
/// <summary>
/// 获取分表字段的值
/// </summary>
/// <param name="db"></param>
/// <param name="entityInfo"></param>
/// <param name="splitType"></param>
/// <param name="entityValue"></param>
/// <returns></returns>
public object GetFieldValue(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object entityValue)
{
var splitColumn = entityInfo.Columns.FirstOrDefault(it => it.PropertyInfo.GetCustomAttribute<SplitFieldAttribute>() != null);
var value = splitColumn.PropertyInfo.GetValue(entityValue, null);
return value;
}
/// <summary>
/// 默认表名
/// </summary>
/// <param name="db"></param>
/// <param name="EntityInfo"></param>
/// <returns></returns>
public string GetTableName(ISqlSugarClient db, EntityInfo entityInfo)
{
return entityInfo.DbTableName;
}
public string GetTableName(ISqlSugarClient db, EntityInfo entityInfo, SplitType type)
{
return entityInfo.DbTableName;//目前模式少不需要分类(自带的有 日、周、月、季、年等进行区分)
}
public string GetTableName(ISqlSugarClient db, EntityInfo entityInfo, SplitType splitType, object fieldValue)
{
return entityInfo.DbTableName + "_"+fieldValue; //根据值按首字母
}
}
}

@ -0,0 +1,5 @@
namespace Sln.Iot.Serilog;
public class Class1
{
}

@ -0,0 +1,65 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Serilog
* 77C04261-6831-4763-A6E1-C6B2B66D4DD9
*
* WenJY
*
* 2025-04-11 11:12:30
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Sln.Iot.Config;
namespace Sln.Iot.Serilog
{
public static class SerilogExtensions
{
public static void UseSerilogExtensions(this IServiceProvider service)
{
//启用Serilog中间件
#region 通过配置文件读取日志存放位置
var appConfig = service.GetService<AppConfig>();
var logPath = Path.Combine(appConfig.logPath, "Logs");
#endregion
Log.Logger = new LoggerConfiguration().MinimumLevel.Information().WriteTo.Console()
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Info"))
.WriteTo.File(Path.Combine($"{logPath}/Info/", "Info.log"), rollingInterval: RollingInterval.Day))
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Plc"))
.WriteTo.File(Path.Combine($"{logPath}/Plc/", "Plc.log"), rollingInterval: RollingInterval.Day))
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Camera"))
.WriteTo.File(Path.Combine($"{logPath}/Camera/", "Camera.log"), rollingInterval: RollingInterval.Day))
.WriteTo.Logger(lc => lc
.Filter.ByIncludingOnly(logEvent => logEvent.Properties.ContainsKey("Module") && logEvent.Properties["Module"].ToString().Contains("Error"))
.WriteTo.File(Path.Combine($"{logPath}/Error/", "Error.log"), rollingInterval: RollingInterval.Day))
.CreateLogger();
}
}
}

@ -0,0 +1,105 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Serilog
* 15731D3E-0D48-41B6-B77B-A4CC592B4939
*
* WenJY
*
* 2025-04-11 11:09:14
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using Serilog;
namespace Sln.Iot.Serilog
{
public class SerilogHelper
{
private readonly ILogger? Info_logger = Log.ForContext("Module", "Info");
private readonly ILogger? Plc_logger = Log.ForContext("Module", "Plc");
private readonly ILogger? Error_logger = Log.ForContext("Module", "Error");
private readonly ILogger? Camera_logger = Log.ForContext("Module", "Camera");
/// <summary>
/// Info日志
/// </summary>
/// <param name="msg"></param>
public void Info(string msg)
{
if (Info_logger != null)
{
this.Info_logger.Information(msg);
}
}
/// <summary>
/// Plc日志
/// </summary>
/// <param name="msg"></param>
public void Plc(string msg)
{
if (Plc_logger != null)
{
this.Plc_logger.Information(msg);
}
}
/// <summary>
/// 相机日志
/// </summary>
/// <param name="msg"></param>
public void Camera(string msg)
{
if (Camera_logger != null)
{
this.Camera_logger.Information(msg);
}
}
/// <summary>
/// Error日志
/// </summary>
/// <param name="msg"></param>
/// <param name="ex"></param>
public void Error(string msg, Exception ex = null)
{
if (!string.IsNullOrEmpty(msg) && ex == null)
{
this.Error_logger.Information("【附加信息】 : {0}<br>", new object[] { msg });
}
else if (!string.IsNullOrEmpty(msg) && ex != null)
{
string errorMsg = BeautyErrorMsg(ex);
this.Error_logger.Information("【附加信息】 : {0}<br>{1}", new object[] { msg, errorMsg });
}
else if (string.IsNullOrEmpty(msg) && ex != null)
{
string errorMsg = BeautyErrorMsg(ex);
this.Error_logger.Information(errorMsg);
}
}
private string BeautyErrorMsg(Exception ex)
{
string errorMsg = string.Format("【异常类型】:{0} <br>【异常信息】:{1} <br>【堆栈调用】:{2}", new object[] { ex.GetType().Name, ex.Message, ex.StackTrace });
errorMsg = errorMsg.Replace("\r\n", "<br>");
errorMsg = errorMsg.Replace("位置", "<strong style=\"color:red\">位置</strong>");
return errorMsg;
}
}
}

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sln.Iot.Config\Sln.Iot.Config.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,72 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Socket.Adapter
* 8A1D4D97-E419-4A49-B921-7FF76B190F45
*
* WenJY
*
* 2025-04-11 13:55:38
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using TouchSocket.Core;
namespace Sln.Iot.Socket.Adapter
{
public class BufferRequestInfo: IRequestInfo
{
/// <summary>
/// 帧头 0x68 1个字
/// </summary>
public byte[] header { get; internal set; }
/// <summary>
/// 采集设备编号,由采集器类型(1个字)、采集器地址(2个字)组成
/// </summary>
public string ColletEquipCode { get; internal set; }
/// <summary>
/// 自定义属性,DataType
/// </summary>
public byte DataType { get; internal set; }
/// <summary>
/// Buffer长度
/// </summary>
public int BufferLength { get; internal set; }
/// <summary>
/// 内容体
/// </summary>
public byte[] Body { get; internal set; }
/// <summary>
/// 校验位 1个字从帧头到数据累加CS校验
/// </summary>
public byte CheckBit { get; internal set; }
/// <summary>
/// 帧尾 0x16 1个字
/// </summary>
public byte[] Tail { get; internal set; }
/// <summary>
/// 原始Buffer
/// </summary>
public ByteBlock buffer { get; internal set; }
}
}

@ -0,0 +1,117 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Socket.Adapter
* 50003A25-42CE-44A7-9940-FFDE3BD0A52A
*
* WenJY
*
* 2025-04-11 13:56:24
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using System.Text;
using TouchSocket.Core;
namespace Sln.Iot.Socket.Adapter
{
public class CustomDataHandlingAdapter:CustomDataHandlingAdapter<BufferRequestInfo>
{
protected override FilterResult Filter(in ByteBlock byteBlock, bool beCached, ref BufferRequestInfo request, ref int tempCapacity)
{
CacheTimeoutEnable = true;
CacheTimeout = new TimeSpan(0, 0, 0, 0, 5000);
int pos = byteBlock.Pos;
try
{
if (byteBlock.CanReadLen < 5)
{
return FilterResult.Cache;
}
byteBlock.Read(out byte[] header, 1);
byteBlock.Read(out byte[] deviceType, 1);
byteBlock.Read(out byte[] deviceId, 2);
string DeviceType = Encoding.ASCII.GetString(deviceType);
string collectEquipCode = DeviceType + this.ConverToString(deviceId);
byteBlock.Pos += 3;
byteBlock.Read(out byte[] dataType, 1);
byteBlock.Read(out byte[] lengthByte, 2);
string hexString = BitConverter.ToString(lengthByte).Replace("-", "");
int bodyLength = Convert.ToInt32(hexString, 16);
if (bodyLength > byteBlock.CanReadLen)
{
byteBlock.Pos = pos; //body数据不足。回退游标
return FilterResult.Cache;
}
else
{
byteBlock.Read(out byte[] body, bodyLength);
byteBlock.Read(out byte[] check, 1);
byteBlock.Read(out byte[] tail, 1);
request = new BufferRequestInfo()
{
header = header,
ColletEquipCode = collectEquipCode,
DataType = dataType[0],
BufferLength = bodyLength,
Body = body,
CheckBit = check[0],
Tail = tail,
buffer = byteBlock
};
return FilterResult.Success;
}
}catch (Exception ex)
{
Logger.Error("FilterResult"+ex.Message);
byteBlock.Pos = pos; //body数据不足。回退游标
return FilterResult.Cache;
}
}
private string ConverToString(byte[] data)
{
string str;
StringBuilder stb = new StringBuilder();
for (int i = 0; i < data.Length; i++)
{
if ((int)data[i] > 15)
{
stb.Append(Convert.ToString(data[i], 16).ToUpper()); //添加字符串
}
else //如果是小于0F需要加个零
{
stb.Append("0" + Convert.ToString(data[i], 16).ToUpper());
}
}
str = stb.ToString();
return str;
}
}
}

@ -0,0 +1,5 @@
namespace Sln.Iot.Socket;
public class Class1
{
}

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="TouchSocket" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Sln.Iot.Serilog\Sln.Iot.Serilog.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,127 @@
#region << 版 本 注 释 >>
/*--------------------------------------------------------------------
* (c) 2025 WenJY
* CLR4.0.30319.42000
* Mr.Wen's MacBook Pro
* Sln.Iot.Socket
* 6D821766-EAFA-4C51-A757-8786E77645AC
*
* WenJY
*
* 2025-04-11 13:51:11
* V1.0.0
*
*
*--------------------------------------------------------------------
*
*
*
*
* V1.0.0
*--------------------------------------------------------------------*/
#endregion << 版 本 注 释 >>
using System;
using Sln.Iot.Serilog;
using Sln.Iot.Socket.Adapter;
using TouchSocket.Core;
using TouchSocket.Sockets;
namespace Sln.Iot.Socket
{
public class TcpServer
{
private readonly SerilogHelper _logger;
private readonly TcpService _service;
public TcpServer(SerilogHelper logger, TcpService service)
{
_logger = logger;
_service = service;
}
/// <summary>
/// 接收客户端指令委托
/// </summary>
public delegate void ReceivedClientBuffer(byte[] buffer);
public event ReceivedClientBuffer? ReceivedClientBufferEvent;
public delegate void RefreshClientInfo(TcpService tcpService);
public event RefreshClientInfo? RefreshClientInfoEvent;
[Obsolete("Obsolete")]
public delegate void ReceivedBufferRequestInfo(SocketClient client,BufferRequestInfo requestInfo);
public event ReceivedBufferRequestInfo ReceivedBufferRequestInfoEvent;
public void Init(int serverPort)
{
try
{
_service.Connecting = (client, e) => {
_logger.Info($"客户端{client.IP}正在接入服务");
return EasyTask.CompletedTask;
};
_service.Connected = (client, e) => {
_logger.Info($"客户端{client.IP}接入服务成功");
RefreshClientInfoEvent?.Invoke(_service);
return EasyTask.CompletedTask;
};
_service.Disconnected = (client, e) => {
_logger.Info($"客户端{client.IP}断开连接");
RefreshClientInfoEvent?.Invoke(_service);
return EasyTask.CompletedTask;
};
_service.Received = (client, e) =>
{
if (e.RequestInfo is BufferRequestInfo request)
{
string msg = $"收到客户端:{client.Id};指令====>>>>Header:{BitConverter.ToString(request.header).Replace("-", "")};DataType:{request.DataType.ToString("X2")};BufferLength:{request.BufferLength};Body:{BitConverter.ToString(request.Body).Replace("-", "")};CheckBit:{request.CheckBit.ToString("X2")};Tail:{BitConverter.ToString(request.Tail).Replace("-", "")};";
_logger.Info($"{msg}");
ReceivedBufferRequestInfoEvent?.Invoke(client,request);
}
return EasyTask.CompletedTask;
};
_service.Setup(new TouchSocketConfig()//载入配置
.SetListenIPHosts(new IPHost[] { new IPHost($"0.0.0.0:{serverPort}") })
.SetTcpDataHandlingAdapter(() => new CustomDataHandlingAdapter())
.ConfigureContainer(a =>//容器的配置顺序应该在最前面
{
a.AddConsoleLogger();
})
.ConfigurePlugins(a =>
{
//自定义插件
}));
_service.Start();
_logger.Info($"TcpServer启动成功监听端口{serverPort}");
}
catch (Exception ex)
{
//throw new InvalidOperationException($"TcpServer启动异常{ex.Message}");
_logger.Error($"TcpServer启动异常{ex.Message}");
}
}
/// <summary>
/// 向所有客户端发送心跳
/// </summary>
public void SendHeartBeat()
{
var clients = _service.SocketClients.GetClients();
foreach (var item in clients)
{
_service.Send(item.Id,"heartbeat");
}
}
}
}

@ -0,0 +1,67 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34714.143
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot", "Sln.Iot\Sln.Iot.csproj", "{2140AD68-D4CE-44EC-B9D3-20D18EB59F9D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Business", "Sln.Iot.Business\Sln.Iot.Business.csproj", "{F59EB67D-66FD-43B9-B6CC-46BF25202C21}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Config", "Sln.Iot.Config\Sln.Iot.Config.csproj", "{2E10DED8-5F53-4ED9-892F-B0007B85806E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Common", "Sln.Iot.Common\Sln.Iot.Common.csproj", "{899D8A81-D3E3-4599-8A8C-D60280A777F3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Model", "Sln.Iot.Model\Sln.Iot.Model.csproj", "{503E7EAE-6323-4CE2-AAE4-C6A7CBDFC4B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Repository", "Sln.Iot.Repository\Sln.Iot.Repository.csproj", "{DA193E49-8B4A-4C8D-B44E-844E22983DA8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Serilog", "Sln.Iot.Serilog\Sln.Iot.Serilog.csproj", "{A9CCC9F6-BE1C-4B73-AFBF-83D363D7F64F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sln.Iot.Socket", "Sln.Iot.Socket\Sln.Iot.Socket.csproj", "{5B7C6367-7B41-48A6-9A71-2F191CE14000}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2140AD68-D4CE-44EC-B9D3-20D18EB59F9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2140AD68-D4CE-44EC-B9D3-20D18EB59F9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2140AD68-D4CE-44EC-B9D3-20D18EB59F9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2140AD68-D4CE-44EC-B9D3-20D18EB59F9D}.Release|Any CPU.Build.0 = Release|Any CPU
{F59EB67D-66FD-43B9-B6CC-46BF25202C21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F59EB67D-66FD-43B9-B6CC-46BF25202C21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F59EB67D-66FD-43B9-B6CC-46BF25202C21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F59EB67D-66FD-43B9-B6CC-46BF25202C21}.Release|Any CPU.Build.0 = Release|Any CPU
{2E10DED8-5F53-4ED9-892F-B0007B85806E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E10DED8-5F53-4ED9-892F-B0007B85806E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E10DED8-5F53-4ED9-892F-B0007B85806E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E10DED8-5F53-4ED9-892F-B0007B85806E}.Release|Any CPU.Build.0 = Release|Any CPU
{899D8A81-D3E3-4599-8A8C-D60280A777F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{899D8A81-D3E3-4599-8A8C-D60280A777F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{899D8A81-D3E3-4599-8A8C-D60280A777F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{899D8A81-D3E3-4599-8A8C-D60280A777F3}.Release|Any CPU.Build.0 = Release|Any CPU
{503E7EAE-6323-4CE2-AAE4-C6A7CBDFC4B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{503E7EAE-6323-4CE2-AAE4-C6A7CBDFC4B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{503E7EAE-6323-4CE2-AAE4-C6A7CBDFC4B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{503E7EAE-6323-4CE2-AAE4-C6A7CBDFC4B2}.Release|Any CPU.Build.0 = Release|Any CPU
{DA193E49-8B4A-4C8D-B44E-844E22983DA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA193E49-8B4A-4C8D-B44E-844E22983DA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA193E49-8B4A-4C8D-B44E-844E22983DA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA193E49-8B4A-4C8D-B44E-844E22983DA8}.Release|Any CPU.Build.0 = Release|Any CPU
{A9CCC9F6-BE1C-4B73-AFBF-83D363D7F64F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A9CCC9F6-BE1C-4B73-AFBF-83D363D7F64F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9CCC9F6-BE1C-4B73-AFBF-83D363D7F64F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9CCC9F6-BE1C-4B73-AFBF-83D363D7F64F}.Release|Any CPU.Build.0 = Release|Any CPU
{5B7C6367-7B41-48A6-9A71-2F191CE14000}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5B7C6367-7B41-48A6-9A71-2F191CE14000}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5B7C6367-7B41-48A6-9A71-2F191CE14000}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5B7C6367-7B41-48A6-9A71-2F191CE14000}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {47F07B24-817F-4612-9BD3-D8AD5F4B31C3}
EndGlobalSection
EndGlobal

@ -0,0 +1,108 @@
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Sln.Iot.Business;
using Sln.Iot.Business.@base;
using Sln.Iot.Config;
using Sln.Iot.Repository;
using Sln.Iot.Serilog;
using Sln.Iot.Socket;
using TouchSocket.Sockets;
namespace Sln.Iot
{
internal class Program
{
public static IServiceProvider? ServiceProvider = null;
static async Task Main(string[] args)
{
var services = new ServiceCollection();
ConfigureServices(services);
ServiceProvider = services.BuildServiceProvider();
ServiceProvider.UseSerilogExtensions();
var appConfig = ServiceProvider.GetService<AppConfig>();
var log = ServiceProvider.GetService<SerilogHelper>();
log.Info($"系统启动成功,日志存放位置:{appConfig.logPath}");
var _server = ServiceProvider.GetService<TcpServer>();
_server.Init(appConfig.listernPort);
_server.ReceivedBufferRequestInfoEvent += (client, info) =>
{
bool isRet = false;
BaseBusiness _business = null;
int bodyLength = 0;
switch (info.DataType)
{
case 0x08: //校时指令
_business = ServiceProvider.GetService<CheckTimeBusiness>();
break;
case 0x21: //登录指令
_business = ServiceProvider.GetService<LoginBusiness>();
break;
case 0x24: //心跳指令
_business = ServiceProvider.GetService<HeartBusiness>();
break;
case 0x83: //电能指令
bodyLength = 70;
_business = ServiceProvider.GetService<ElectricBusiness>();
break;
case 0x84: //水
bodyLength = 58;
_business = ServiceProvider.GetService<FluidBusiness>();
break;
case 0x85: //物联网环境
bodyLength = info.BufferLength;
_business = ServiceProvider.GetService<IotEnvBusiness>();
break;
default:
break;
}
if (_business != null)
{
Parallel.Invoke(
() => _business.ResponseHandle(client, info.buffer),
() => _business.BufferAnalysis(client, info, bodyLength)
);
}
};
await Task.Delay(-1);
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<AppConfig>(provider =>
{
var configurationBuilder = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
IConfiguration configuration = configurationBuilder.Build();
var ap = configuration.GetSection("AppConfig").Get<AppConfig>();
return ap;
});
Assembly[] assemblies =
{
Assembly.LoadFrom("Sln.Iot.Repository.dll"),
Assembly.LoadFrom("Sln.Iot.Socket.dll"),
Assembly.LoadFrom("Sln.Iot.Common.dll"),
Assembly.LoadFrom("Sln.Iot.Business.dll"),
};
services.Scan(scan => scan.FromAssemblies(assemblies)
.AddClasses()
.AsImplementedInterfaces()
.AsSelf()
.WithTransientLifetime());
services.AddSingleton(typeof(SerilogHelper));
services.AddSingleton(typeof(TcpService));
services.AddSqlSugarSetup();
}
}
}

@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Sln.Iot.Business\Sln.Iot.Business.csproj" />
<ProjectReference Include="..\Sln.Iot.Common\Sln.Iot.Common.csproj" />
<ProjectReference Include="..\Sln.Iot.Config\Sln.Iot.Config.csproj" />
<ProjectReference Include="..\Sln.Iot.Model\Sln.Iot.Model.csproj" />
<ProjectReference Include="..\Sln.Iot.Repository\Sln.Iot.Repository.csproj" />
<ProjectReference Include="..\Sln.Iot.Serilog\Sln.Iot.Serilog.csproj" />
<ProjectReference Include="..\Sln.Iot.Socket\Sln.Iot.Socket.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.5" />
<PackageReference Include="Scrutor" Version="6.0.1" />
</ItemGroup>
<ItemGroup>
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

@ -0,0 +1,19 @@
{
"AppConfig": {
"logPath": "\\\\Mac\\Home\\Public\\WorkSpace\\Mesnac\\项目资料\\IOT物联网数据采集\\日志信息",
"listernPort": 7001,
"virtualFlag": true,
"virtualValue": 9999999,
"electricTimeInterval": 1, //
"fluidTimeInterval": 1, //
"SqlConfig": [
{
"configId": "tao_iot", //tao:
"dbType": 0, //tidb mysql
"isFlag": true,
//"connStr": "server=127.0.0.1;Port=4000;Database=tao_iot;Uid=root;" //Pwd=haiwei@123;
"connStr": "server=1.13.177.47;Port=3306;Database=tao_iot;Uid=root;Pwd=Haiwei123456;"
}
],
}
}

@ -0,0 +1,210 @@
**RKKC1604-N2型动力环境监控一体机.上行通讯协议**
**1.终端主动发送登录指令0x21/0xA1**
终端发起有效登录指令后,上位机软件才能对终端发送的数据进行解析和存库。
| 类别 | 数据内容 | 数据长度 | 备注 |
| --- | --- | --- | --- |
| 帧开始 | 0x68 | 1个字节 | |
| 采集器类型 | 0x45 | 1个字节 | 0x45-采集终端类型 |
| 采集器地址 | 0x0001~0x9999 | 2个字节 | 高位在前低位在后BCD码形式 |
| 命令序列号 | | 2个字节 | 高位在前低位在后,命令帧的序列号 |
| 起始符 | 0x68 | 1个字节 | |
| 控制码 | 0x21 | 1个字节 | 登录指令标识码 |
| 数据长度 | 0x00 0x03 | 2个字节 | 固定为0x00,0x03 |
| 数据域 | 0x12 0x34 0x56 | 3个字节 | 固定为 0x12 0x34 0x56 |
| 校验码 | CS | 1个字节 | 从帧开始到数据域最后一位的数据累加CS校验 |
| 结束符 | 0x16 | 1个字节 | 1个字节 |
举例:
终端发送指令:
68 45 01 02 00 BF 68 21 00 03 12 34 56 97 16
上位机返回指令:
68 45 01 02 00 BF 68 A1 00 00 78 16
**2.上位机对采集终端设备校时指令(0x08/0x88)**
为保证现场采集设备与服务器之间的时间同步,采集终端登录服务器成功后服务器需要对采集设备进行校时。在后续正常工作时,为纠正设备之间的时间偏差,建议每隔一段时间(比如8个小时)对现场终端进行一次校时。
| 类别 | 数据内容 | 数据长度 | 备注 |
| --- | --- | --- | --- |
| 帧开始 | 0x68 | 1个字节 | |
| 采集器类型 | 0x45 | 1个字节 | 0x45-采集终端类型 |
| 采集器地址 | 0x0001~0x9999 | 2个字节 | 高位在前低位在后BCD码形式 |
| 命令序列号 | | 2个字节 | 高位在前低位在后,命令帧的序列号 |
| 起始符 | 0x68 | 1个字节 | |
| 控制码 | 0x08 | 1个字节 | 设置终端参数标识码 |
| 数据长度 | 0x00 0x0D | 2个字节 | 固定为0x00,0x0D |
| 数据域 | 0x00 | 1个字节 | 固定为0x00 |
| | 0x00 | 1个字节 | 固定为0x00 |
| | 0x31 0x32 0x33 | 3个字节 | 密码固定为0x31 0x32 0x33 |
| | 0x80 0x30 | 2个字节 | 时间标识码固定为0x80 0x30 |
| | 秒-分-时-日-月-年 | 6个字节 | 具体时间:秒-分-时-日-月-年 |
| 校验码 | CS | 1个字节 | 从帧开始到数据长度数据累加CS校验 |
| 结束符 | 0x16 | 1个字节 | 1个字节 |
举例:
上位机发送指令:
68 45 01 02 40 00 68 08 00 0D 00 00 31 32 33 80 30 22 39 14 16 09 15 56 16
终端返回指令:
68 45 01 02 40 00 68 88 00 04 00 80 30 00 94 16
**3.终端主动发送心跳指令(0x24/0xA4)**
通过心跳指令判断采集设备与上位机直接的通讯连接状态,采集设备通过定时发送心跳指令,在有效时间内若得到上位机的响应,则判断出终端与上位机之间处于有效连接中,否则若在有效时间捏没有得到上位机的响应,则判断出终端没有连接上上位机,终端则发起登录上位机服务器的流程和指令。
| 类别 | 数据内容 | 数据长度 | 备注 |
| --- | --- | --- | --- |
| 帧开始 | 0x68 | 1个字节 | |
| 采集器类型 | 0x45 | 1个字节 | 0x45-采集终端类型 |
| 采集器地址 | 0x0001~0x9999 | 2个字节 | 高位在前低位在后BCD码形式 |
| 命令序列号 | | 2个字节 | 高位在前低位在后,命令帧的序列号 |
| 起始符 | 0x68 | 1个字节 | |
| 控制码 | 0x24 | 1个字节 | 心跳指令标识码 |
| 数据长度 | | 2个字节 | 固定为0x00,0x00 |
| 校验码 | CS | 1个字节 | 从帧开始到数据长度数据累加CS校验 |
| 结束符 | 0x16 | 1个字节 | 1个字节 |
举例:
终端发送指令:
68 45 00 01 00 01 68 24 00 00 3B 16
上位机返回指令:
68 45 00 01 00 01 68 A4 00 00 BB 16
**4.终端主动上传实时数据指令**
采集设备根据设定的时间间隔,定时对现场仪表进行数据采集,并将采集到的数据综合处理后主动上传到上位机平台。
| 类别 | 数据内容 | 数据长度 | 备注 |
| --- | --- | --- | --- |
| 帧开始 | 0x68 | 1个字节 | |
| 采集器类型 | 0x45 | 1个字节 | 0x45-采集终端类型 |
| 采集器地址 | 0x0001~0x9999 | 2个字节 | 高位在前低位在后BCD码形式 |
| 命令序列号 | | 2个字节 | 高位在前低位在后,命令帧的序列号 |
| 起始符 | 0x68 | 1个字节 | |
| 控制码 | 0x83/0x84/0x85/0x86/0x87 | 1个字节 | 0x83-电力0x84-压缩空气、水0x85-温湿度、照度0x86-开关量IO状态0x87-蒸汽 |
| 数据长度 | N个仪表*1个仪表的长度 | 2个字节 | 1条仪表数据数据长度L
0x83L=2(表序号)+8*6(8项数据项)+8采集时间 = 58个字节
0x84L=2(表序号)+8*6(8项数据项)+8采集时间 = 58个字节
0x85: L=2(表序号)+4*6(8项数据项)+8采集时间 = 34个字节
0x86: L=2(表序号)+16(16路IO输入)+8采集时间 = 26个字节N=1
0x87L=2(表序号)+8*6(8项数据项)+8采集时间 = 58个字节 |
| 数据域 | 0x00 | N*L个字节 | |
| 校验码 | CS | 1个字节 | 从帧开始到数据长度数据累加CS校验 |
| 结束符 | 0x16 | 1个字节 | 1个字节 |
控制码:
| 序号 | 终端主动发送数据控制码 | 上位机返回对应控制码 | 备注 |
| --- | --- | --- | --- |
| 1 | 0x83 | 0xB3 | |
| 2 | 0x84 | 0xB4 | |
| 3 | 0x85 | 0xB5 | |
| 3 | 0x85 | 0xB6 | |
电力采集数据域举例:
![](attachment:fecb539a-3dd8-4e4e-8761-046c06386784:image1.png)
举例:
终端发生命令帧:
68 45 00 01 01 FF 68 83 00 3A 01 00 8E 11 61 4E 4B 3C 8E 12 61 4E 4B 3C 8E 13 61 4E 4B 3C 8E 21 61 4E 4B 3C 8E 22 61 4E 4B 3C 8E 23 61 4E 4B 3C B6 50 3F 7C AC 08 90 10 61 4E 4B 3C 80 30 50 40 16 28 04 20 F5 16
上位机返回:
68 45 00 01 0A 3F 68 B3 00 00 12 16
**5.终端主动上传历史数据指令**
在采集设备与上位机有效连接的期间内,采集设备对存储在内部存储区内的历史数据进行批量上传工作。
| 类别 | 数据内容 | 数据长度 | 备注 |
| --- | --- | --- | --- |
| 帧开始 | 0x68 | 1个字节 | |
| 采集器类型 | 0x45 | 1个字节 | 0x45-采集终端类型 |
| 采集器地址 | 0x0001~0x9999 | 2个字节 | 高位在前低位在后BCD码形式 |
| 命令序列号 | | 2个字节 | 高位在前低位在后,命令帧的序列号 |
| 起始符 | 0x68 | 1个字节 | |
| 控制码 | 0x83/0x84/0x85/0x86/0x87 | 1个字节 | 0x83-电力0x84-压缩空气、水0x85-温湿度、照度0x86-开关量IO状态0x87-蒸汽 |
| 数据长度 | N个仪表*1个仪表的长度 | 2个字节 | 1条仪表数据数据长度L
0x93L=2(表序号)+8*6(8项数据项)+8采集时间 = 58个字节
0x94L=2(表序号)+8*6(8项数据项)+8采集时间 = 58个字节
0x95: L=2(表序号)+4*6(4项数据项)+8采集时间 = 34个字节
0x96: L=2(表序号)+16(16路IO输入)+8采集时间 = 26个字节N=1
0x97L=2(表序号)+8*6(8项数据项)+8采集时间 = 58个字节 |
| 数据域 | 0x00 | N*L个字节 | |
| 校验码 | CS | 1个字节 | 从帧开始到数据长度数据累加CS校验 |
| 结束符 | 0x16 | 1个字节 | 1个字节 |
控制码:
| 序号 | 终端主动发送数据控制码 | 上位机返回对应控制码 | 备注 |
| --- | --- | --- | --- |
| 1 | 0x93 | 0xC3 | |
| 2 | 0x94 | 0xC4 | |
| 3 | 0x95 | 0xC5 | |
| 4 | 0x96 | 0xC6 | |
| 5 | 0x97 | 0xC7 | |
电力采集数据域举例:
![](attachment:fecb539a-3dd8-4e4e-8761-046c06386784:image1.png)
举例:
终端发生命令帧:
68 45 00 01 01 FF 68 93 00 3A 01 00 8E 11 61 4E 4B 3C 8E 12 61 4E 4B 3C 8E 13 61 4E 4B 3C 8E 21 61 4E 4B 3C 8E 22 61 4E 4B 3C 8E 23 61 4E 4B 3C B6 50 3F 7C AC 08 90 10 61 4E 4B 3C 80 30 50 40 16 28 04 20 05 16
上位机返回:
68 45 00 01 0A 3F 68 C3 00 00 22 16
# 附录:上传数据标识码(部分)
| 标识符类型 | 说明 | 标识符类型 | 说明 |
| --- | --- | --- | --- |
| 0x8E11 | A相电压 | 0x9B00 | 仪表压力值 |
| 0x8E12 | B相电压 | 0x9B01 | 仪表温度值 |
| 0x8E13 | C相电压 | 0x9B02 | 仪表频率值 |
| 0x8E21 | A相电流 | 0x9B03 | 仪表瞬时流值 |
| 0x8E22 | B相电流 | 0x9B05 | 仪表累积流量值 |
| 0x8E23 | C相电流 | 0x9B06 | 仪表瞬时热量 |
| 0xB650 | 总功率因数 | 0x9B07 | 仪表累积热量值 |
| 0x9010 | 正向有功总电能 | 0x9B0E | 仪表密度值 |
| 0x8030 | 仪表数据采集时间 | | |
| 标识符类型 | 说明 | 标识符类型 | 说明 |
| --- | --- | --- | --- |
| 0x8E50 | 温湿度.温度 | 0x8E51 | 照度.流明 |
| 0x8E52 | 温湿度.湿度 | 0x8E53 | 噪声.分贝 |
| 0x8E54 | 振动-速度 | 0x8E55 | 振动-位移 |
| 0x8E56 | 振动-加速度 | 0x8E57 | 振动-温度 |
| 0x8030 | 仪表数据采集时间 | | |
指令样例:
`温度指令68 54 00 02 0E 7F 68 85 00 28 01 01 8E 50 70 A4 41 DD 8E 51 00 00 00 00 8E 52 00 00 00 00 8E 53 00 00 00 00 8E 54 00 00 00 00 80 30 06 47 04 03 09 24 25 16`
`湿度指令68 54 00 01 18 3F 68 85 00 28 01 01 8E 50 7A E1 41 FC 8E 51 00 00 00 00 8E 52 0A 3D 42 2E 8E 53 00 00 00 00 8E 54 00 00 00 00 80 30 34 47 04 03 09 24 39 16`
`噪音指令68 54 00 03 1B 3F 68 85 00 28 01 01 8E 50 00 00 00 00 8E 51 00 00 00 00 8E 52 00 00 00 00 8E 53 66 66 42 93 8E 54 00 00 00 00 80 30 36 53 09 14 03 25 AF 16`
`电能指令6845002104BF6883023011008E11199A43688E12333343678E13199A43688E21BA5E3FA98E2268733F718E23353F3FAEB651FF000000B65200000000B6504DD33F629010C6E14590803037520521052512008E11000043688E12199A43678E13199A43688E21000000008E22000000008E2300000000B651FF000000B65200000000B65000003F809010AF484605803043520521052513008E11E66643678E12199A43678E13199A43688E21353F3E5E8E2260423E658E239DB23E6FB651FF000000B65200000000B65016873F799010F33344BC803049520521052514008E11333343688E12199A43678E13199A43688E21000000008E22000000008E2300000000B651FF000000B65200000000B65000003F8090103266461C803054520521052515008E11000043688E12CCCD43668E13199A43688E21BA5E3F998E22AE143F978E2326E93FA1B651FF000000B65200000000B65081063F5590106F5C454B803000530521052516008E11000043688E12000043678E13199A43688E211EB83D858E2247AE3D618E23999A3D99B651FF000000B65200000000B650020C3F4B9010619A44F0803006530521052517008E11000043688E12E66643668E13333343688E21A1CB3E858E224FDF3E8D8E23FDF43E94B651FF000000B65200000000B65068733F5190101D1F44C7803011530521052518008E11199A43688E12CCCD43668E13000043688E21CED93EB78E223F7D3EB58E23CCCD3ECCB651FF000000B65200000000B6506C8B3F67901091EC44FD80301753052105257116`
`流体指令68450073197F688400E801009B00000000009B01000000009B02000000009B03000000009B0500003F809B06000000009B07000000009B0E00000000803019250521052502009B00000000009B01000000009B02000000009B03000000009B0500003F809B06000000009B07000000009B0E00000000803020250521052503009B00000000009B01000000009B02000000009B03000000009B05C00044219B06000000009B07000000009B0E00000000803021250521052504009B00000000009B01000000009B02000000009B03000000009B05000043BE9B06000000009B07000000009B0E0000000080302225052105254216`

@ -0,0 +1,144 @@
# Sql File 数据结构
record_iotenv_instant动力环境数据表
```sql
/*
Navicat Premium Data Transfer
Source Server : 127.0.0.1_4000
Source Server Type : MySQL
Source Server Version : 80011 (8.0.11-TiDB-v8.5.1)
Source Host : 127.0.0.1:4000
Source Schema : tao_iot
Target Server Type : MySQL
Target Server Version : 80011 (8.0.11-TiDB-v8.5.1)
File Encoding : 65001
Date: 20/05/2025 14:18:32
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for record_iotenv_instant
-- ----------------------------
DROP TABLE IF EXISTS `record_iotenv_instant`;
CREATE TABLE `record_iotenv_instant` (
`objid` bigint NOT NULL AUTO_INCREMENT COMMENT '主键标识',
`monitorId` varchar(50) DEFAULT NULL COMMENT '计量设备编号',
`temperature` decimal(18,2) DEFAULT NULL COMMENT '温度',
`humidity` decimal(18,2) DEFAULT NULL COMMENT '湿度',
`illuminance` decimal(18,2) DEFAULT NULL COMMENT '照度',
`noise` decimal(18,2) DEFAULT NULL COMMENT '噪声',
`concentration` decimal(18,2) DEFAULT NULL COMMENT '硫化氢浓度',
`vibration_speed` decimal(18,2) DEFAULT NULL COMMENT '振动-速度(mm/s)',
`vibration_displacement` decimal(18,2) DEFAULT NULL COMMENT '振动-位移(um)',
`vibration_acceleration` decimal(18,2) DEFAULT NULL COMMENT '振动-加速度(g)',
`vibration_temp` decimal(18,2) DEFAULT NULL COMMENT '振动-温度(℃)',
`collectTime` datetime DEFAULT NULL COMMENT '采集时间',
`recodeTime` datetime DEFAULT NULL COMMENT '记录时间',
PRIMARY KEY (`objid`) /*T![clustered_index] CLUSTERED */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin AUTO_INCREMENT=6687491 COMMENT='物联网数据';
SET FOREIGN_KEY_CHECKS = 1;
```
record_dnb_instant电能数据表
```sql
/*
Navicat Premium Data Transfer
Source Server : 127.0.0.1_4000
Source Server Type : MySQL
Source Server Version : 80011 (8.0.11-TiDB-v8.5.1)
Source Host : 127.0.0.1:4000
Source Schema : tao_iot
Target Server Type : MySQL
Target Server Version : 80011 (8.0.11-TiDB-v8.5.1)
File Encoding : 65001
Date: 20/05/2025 14:18:23
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for record_dnb_instant
-- ----------------------------
DROP TABLE IF EXISTS `record_dnb_instant`;
CREATE TABLE `record_dnb_instant` (
`objid` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`monitor_id` varchar(64) DEFAULT NULL COMMENT '计量设备编号',
`va` decimal(18,2) DEFAULT NULL COMMENT 'A项电压',
`vb` decimal(18,2) DEFAULT NULL COMMENT 'B项电压',
`vc` decimal(18,2) DEFAULT NULL COMMENT 'C项电压',
`ia` decimal(18,2) DEFAULT NULL COMMENT 'A项电流',
`ib` decimal(18,2) DEFAULT NULL COMMENT 'B项电流',
`ic` decimal(18,2) DEFAULT NULL COMMENT 'C项电流',
`glys` decimal(18,2) DEFAULT NULL COMMENT '功率因数',
`zxyg` decimal(18,2) DEFAULT NULL COMMENT '正向有功',
`active_power` decimal(18,2) DEFAULT NULL COMMENT '有功功率',
`reactive_power` decimal(18,2) DEFAULT NULL COMMENT '无功功率',
`collect_type` int DEFAULT '0' COMMENT '采集方式',
`collect_time` datetime DEFAULT NULL COMMENT '采集时间',
`record_time` datetime DEFAULT NULL COMMENT '记录时间',
PRIMARY KEY (`objid`) /*T![clustered_index] CLUSTERED */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='电实时数据';
SET FOREIGN_KEY_CHECKS = 1;
```
record_fluid_instant流体实时数据
```sql
/*
Navicat Premium Data Transfer
Source Server : 127.0.0.1_4000
Source Server Type : MySQL
Source Server Version : 80011 (8.0.11-TiDB-v8.5.1)
Source Host : 127.0.0.1:4000
Source Schema : tao_iot
Target Server Type : MySQL
Target Server Version : 80011 (8.0.11-TiDB-v8.5.1)
File Encoding : 65001
Date: 20/05/2025 14:31:46
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for record_fluid_instant
-- ----------------------------
DROP TABLE IF EXISTS `record_fluid_instant`;
CREATE TABLE `record_fluid_instant` (
`objid` bigint NOT NULL AUTO_INCREMENT COMMENT '自增标识',
`monitor_id` varchar(64) DEFAULT NULL COMMENT '计量设备编号',
`temperature` decimal(18,2) DEFAULT NULL COMMENT '温度值',
`press` decimal(18,2) DEFAULT NULL COMMENT '压力值',
`frequency` decimal(18,2) DEFAULT NULL COMMENT '频率值',
`density` decimal(18,2) DEFAULT NULL COMMENT '密度值',
`instant_heat` decimal(18,2) DEFAULT NULL COMMENT '瞬时热量',
`total_heat` decimal(18,2) DEFAULT NULL COMMENT '累计热量值',
`instant_flow` decimal(18,2) DEFAULT NULL COMMENT '瞬时流量',
`total_flow` decimal(18,2) DEFAULT NULL COMMENT '累计流量',
`collect_type` int DEFAULT '0' COMMENT '采集方式',
`collect_time` datetime DEFAULT NULL COMMENT '采集时间',
`record_time` datetime DEFAULT NULL COMMENT '记录时间',
PRIMARY KEY (`objid`) /*T![clustered_index] CLUSTERED */
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='流体实时数据';
SET FOREIGN_KEY_CHECKS = 1;
```
Loading…
Cancel
Save