2025-04-23 16:17:29 +08:00
using JiShe.CollectBus.Application.Contracts ;
using JiShe.CollectBus.Common.BuildSendDatas ;
2025-03-12 14:57:42 +08:00
using JiShe.CollectBus.Common.Consts ;
2025-04-14 23:42:18 +08:00
using JiShe.CollectBus.Common.DeviceBalanceControl ;
2025-04-23 16:17:29 +08:00
using JiShe.CollectBus.Common.Encrypt ;
using JiShe.CollectBus.Common.Enums ;
using JiShe.CollectBus.Common.Extensions ;
2025-04-10 14:12:14 +08:00
using JiShe.CollectBus.Common.Helpers ;
2025-03-12 14:57:42 +08:00
using JiShe.CollectBus.FreeSql ;
2025-03-14 14:24:38 +08:00
using JiShe.CollectBus.GatherItem ;
2025-04-21 22:57:49 +08:00
using JiShe.CollectBus.IoTDB.Context ;
2025-04-17 20:28:50 +08:00
using JiShe.CollectBus.IoTDB.Interface ;
2025-04-23 16:17:29 +08:00
using JiShe.CollectBus.IotSystems.Ammeters ;
2025-03-18 22:43:24 +08:00
using JiShe.CollectBus.IotSystems.MeterReadingRecords ;
2025-03-14 14:38:08 +08:00
using JiShe.CollectBus.IotSystems.Watermeter ;
2025-04-19 00:30:58 +08:00
using JiShe.CollectBus.Kafka.Internal ;
2025-04-15 15:49:51 +08:00
using JiShe.CollectBus.Kafka.Producer ;
2025-04-23 16:17:29 +08:00
using JiShe.CollectBus.Protocol.Contracts.Interfaces ;
2025-04-24 23:39:39 +08:00
using JiShe.CollectBus.Protocol.Contracts.Models ;
2025-04-24 17:48:20 +08:00
using JiShe.CollectBus.Protocol.Contracts.SendData ;
2025-03-12 14:57:42 +08:00
using Microsoft.AspNetCore.Authorization ;
2025-04-23 16:17:29 +08:00
using Microsoft.Extensions.DependencyInjection ;
2025-03-13 10:51:16 +08:00
using Microsoft.Extensions.Logging ;
2025-04-17 13:54:18 +08:00
using Microsoft.Extensions.Options ;
2025-04-23 16:17:29 +08:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Threading.Tasks ;
2025-03-12 14:57:42 +08:00
2025-03-14 14:38:08 +08:00
namespace JiShe.CollectBus.ScheduledMeterReading
2025-03-12 14:57:42 +08:00
{
/// <summary>
/// 能耗系统定时采集服务
/// </summary>
[AllowAnonymous]
//[Route($"/energy/app/scheduled")]
public class EnergySystemScheduledMeterReadingService : BasicScheduledMeterReadingService
{
2025-04-09 17:29:30 +08:00
string serverTagName = string . Empty ;
2025-04-23 16:17:29 +08:00
private readonly ILogger < BasicScheduledMeterReadingService > _logger ;
private readonly IIoTDbProvider _dbProvider ;
2025-04-24 17:48:20 +08:00
private readonly IProtocolService _protocolService ;
2025-04-21 22:57:49 +08:00
2025-04-16 17:36:46 +08:00
public EnergySystemScheduledMeterReadingService (
ILogger < EnergySystemScheduledMeterReadingService > logger ,
2025-04-21 10:17:40 +08:00
IIoTDbProvider dbProvider ,
2025-04-21 22:57:49 +08:00
IOptions < KafkaOptionConfig > kafkaOptions ,
IoTDBRuntimeContext runtimeContext ,
2025-04-23 16:17:29 +08:00
IProducerService producerService ,
2025-04-24 17:48:20 +08:00
IProtocolService protocolService ,
2025-04-23 16:17:29 +08:00
IRedisDataCacheService redisDataCacheService )
2025-04-16 17:36:46 +08:00
: base ( logger ,
producerService ,
2025-04-23 16:17:29 +08:00
redisDataCacheService ,
2025-04-17 13:54:18 +08:00
dbProvider ,
2025-04-21 22:57:49 +08:00
runtimeContext ,
2025-04-24 17:48:20 +08:00
protocolService ,
2025-04-17 13:54:18 +08:00
kafkaOptions )
2025-03-12 16:29:38 +08:00
{
2025-04-17 13:54:18 +08:00
serverTagName = kafkaOptions . Value . ServerTagName ;
2025-04-23 16:17:29 +08:00
_dbProvider = dbProvider ;
_logger = logger ;
2025-04-24 17:48:20 +08:00
_protocolService = protocolService ;
2025-03-12 16:29:38 +08:00
}
2025-03-12 14:57:42 +08:00
public sealed override string SystemType = > SystemTypeConst . Energy ;
2025-04-09 17:29:30 +08:00
public sealed override string ServerTagName = > serverTagName ;
2025-03-14 14:24:38 +08:00
/// <summary>
/// 获取采集项列表
/// </summary>
/// <returns></returns>
public override async Task < List < GatherItemInfo > > GetGatherItemByDataTypes ( )
{
try
{
string sql = $"SELECT DataType,ItemCode FROM TB_GatherItem(NOLOCK) WHERE [State]=0" ;
return await SqlProvider . Instance . Change ( DbEnum . EnergyDB )
. Ado
. QueryAsync < GatherItemInfo > ( sql , null ) ;
}
catch
{
return null ;
}
}
2025-03-12 14:57:42 +08:00
/// <summary>
/// 获取电表信息
/// </summary>
/// <param name="gatherCode">采集端Code</param>
/// <returns></returns>
//[HttpGet]
//[Route($"ammeter/list")]
public override async Task < List < AmmeterInfo > > GetAmmeterInfoList ( string gatherCode = "V4-Gather-8890" )
{
2025-04-14 16:41:41 +08:00
//List<AmmeterInfo> ammeterInfos = new List<AmmeterInfo>();
//ammeterInfos.Add(new AmmeterInfo()
//{
// Baudrate = 2400,
// FocusAddress = "402440506",
// Name = "张家祠工务(三相电表)",
2025-04-15 16:48:35 +08:00
// FocusId = 95780,
2025-04-14 16:41:41 +08:00
// DatabaseBusiID = 1,
// MeteringCode = 1,
// AmmerterAddress = "402410040506",
2025-04-15 16:48:35 +08:00
// MeterId = 127035,
2025-04-14 16:41:41 +08:00
// TypeName = 3,
// DataTypes = "449,503,581,582,583,584,585,586,587,588,589,590,591,592,593,594,597,598,599,600,601,602,603,604,605,606,607,608,661,663,677,679",
// TimeDensity = 15,
2025-04-23 09:42:09 +08:00
// BrandType = "",
2025-04-14 16:41:41 +08:00
//});
//ammeterInfos.Add(new AmmeterInfo()
//{
// Baudrate = 2400,
// FocusAddress = "542400504",
// Name = "五号配(长芦二所四排)(单相电表)",
2025-04-15 16:48:35 +08:00
// FocusId = 69280,
2025-04-14 16:41:41 +08:00
// DatabaseBusiID = 1,
// MeteringCode = 2,
// AmmerterAddress = "542410000504",
2025-04-15 16:48:35 +08:00
// MeterId = 95594,
2025-04-14 16:41:41 +08:00
// TypeName = 1,
// DataTypes = "581,589,592,597,601",
// TimeDensity = 15,
2025-04-23 09:42:09 +08:00
// BrandType = "",
2025-04-14 16:41:41 +08:00
//});
//return ammeterInfos;
2025-03-24 20:54:31 +08:00
2025-04-15 16:48:35 +08:00
string sql = $ @ "SELECT C.ID as MeterId,C.Name,C.FocusID as FocusId,C.SingleRate,C.MeteringCode,C.Code AS BrandType,C.Baudrate,C.Password,C.MeteringPort,C.[Address] AS AmmerterAddress,C.TypeName,C.Protocol,C.TripState,C.[State],B.[Address],B.AreaCode,B.AutomaticReport,D.DataTypes,B.TimeDensity,A.GatherCode,C.Special,C.[ProjectID],B.AbnormalState,B.LastTime,CONCAT(B.AreaCode, B.[Address]) AS FocusAddress,(select top 1 DatabaseBusiID from TB_Project where ID = B.ProjectID) AS DatabaseBusiID
2025-03-27 08:38:19 +08:00
FROM TB_GatherInfo ( NOLOCK ) AS A
INNER JOIN TB_FocusInfo ( NOLOCK ) AS B ON A . ID = B . GatherInfoID AND B . RemoveState > = 0 AND B . State > = 0
INNER JOIN TB_AmmeterInfo ( NOLOCK ) AS C ON B . ID = C . FocusID AND C . State > = 0 AND C . State < 100
INNER JOIN TB_AmmeterGatherItem ( NOLOCK ) AS D ON C . ID = D . AmmeterID AND D . State > = 0
WHERE 1 = 1 and C . Special = 0 ";
//TODO 记得移除特殊表过滤
2025-03-24 20:54:31 +08:00
2025-04-23 09:42:09 +08:00
if ( ! string . IsNullOrWhiteSpace ( gatherCode ) )
{
sql = $@"{sql} AND A.GatherCode = '{gatherCode}'" ;
}
2025-03-27 08:38:19 +08:00
return await SqlProvider . Instance . Change ( DbEnum . EnergyDB )
. Ado
. QueryAsync < AmmeterInfo > ( sql ) ;
2025-03-12 14:57:42 +08:00
}
2025-04-23 16:17:29 +08:00
/// <summary>
/// 获取电表阀控配置
/// </summary>
/// <param name="currentTime">阀控的时间</param>
/// <returns></returns>
public override async Task < List < AmmeterAutoValveControlSetting > > GetAmmeterAutoValveControlSetting ( string currentTime )
{
string sql = $ @ "SELECT A.*,B.AmmeterID as MeterId,E.GatherCode,D.AreaCode,D.[Address],CONCAT(D.AreaCode, D.[Address]) AS FocusAddress,D.ID as FocusId
FROM TB_AutoTripTask ( nolock ) AS A
INNER JOIN TB_AutoTripAmmeter ( nolock ) AS B ON A . ID = B . TripTaskID
INNER JOIN TB_AmmeterInfo ( nolock ) AS C ON B . AmmeterID = C . ID
INNER JOIN TB_FocusInfo ( nolock ) AS D ON D . ID = C . FocusID
INNER JOIN TB_GatherInfo ( NOLOCK ) AS E ON E . ID = D . GatherInfoID
WHERE A . IsForbid = 0 and A . State < > - 1 and E . GatherCode LIKE ' % V4 % ' and A . TripTime ";
if ( ! string . IsNullOrWhiteSpace ( currentTime ) )
{
sql = $@"{sql} AND A.TripTime = '{currentTime}'" ;
}
return await SqlProvider . Instance . Change ( DbEnum . EnergyDB )
. Ado
. QueryAsync < AmmeterAutoValveControlSetting > ( sql ) ;
}
/// <summary>
/// 电表自动阀控
/// </summary>
/// <returns></returns>
public override async Task AmmeterScheduledAutoValveControl ( )
2025-04-24 17:48:20 +08:00
{
2025-04-23 16:17:29 +08:00
var currentTime = DateTime . Now ;
string currentTimeStr = $"{currentTime:HH:mm}" ;
try
{
2025-04-24 17:48:20 +08:00
//获取电表阀控配置
2025-04-23 16:17:29 +08:00
var settingInfos = await GetAmmeterAutoValveControlSetting ( currentTimeStr ) ;
if ( settingInfos = = null | | settingInfos . Count < = 0 )
{
_logger . LogError ( $"{nameof(AmmeterScheduledAutoValveControl)} 电表自动阀控时,阀控数据为空, -101" ) ;
return ;
}
//批量获取对应的缓存电表信息
var ammeterInfos = new List < AmmeterInfo > ( ) ;
List < MeterReadingTelemetryPacketInfo > taskList = new List < MeterReadingTelemetryPacketInfo > ( ) ;
var metadata = await _dbProvider . GetMetadata < MeterReadingTelemetryPacketInfo > ( ) ;
foreach ( var settingInfo in settingInfos )
{
bool isGenerate = false ;
switch ( settingInfo . LoopType )
{
case "Once" :
isGenerate = CommonHelper . JudgeIsGenerate_Once ( settingInfo . OnceWithDate . Value , currentTime ) ;
break ;
case "EachDay" :
isGenerate = CommonHelper . JudgeIsGenerate_Day ( settingInfo . EachDayWithout , currentTime ) ;
break ;
case "EachWeek" :
isGenerate = CommonHelper . JudgeIsGenerate_Week ( settingInfo . EachWeekWith , currentTime ) ;
break ;
case "EachMonth" :
isGenerate = CommonHelper . JudgeIsGenerate_Month ( settingInfo . EachMonthWith , currentTime ) ;
break ;
}
if ( ! isGenerate ) //不生成,跳入下一循环
{
continue ;
}
//获取对应的缓存电表信息
var ammeterInfo = ammeterInfos . First ( ) ;
bool tripStateResult = false ;
2025-04-24 23:39:39 +08:00
string subItemCode = string . Empty ;
2025-04-23 16:17:29 +08:00
if ( settingInfo . TripType . Equals ( "on" ) )
{
ammeterInfo . TripState = 0 ;
tripStateResult = true ;
2025-04-24 23:39:39 +08:00
subItemCode = T645PacketItemCodeConst . C1C01C ;
if ( ammeterInfo . TypeName ! = 1 )
{
subItemCode = T645PacketItemCodeConst . C1C01B ;
}
2025-04-23 16:17:29 +08:00
}
else if ( settingInfo . TripType . Equals ( "off" ) )
{
ammeterInfo . TripState = 1 ;
tripStateResult = false ;
2025-04-24 23:39:39 +08:00
subItemCode = T645PacketItemCodeConst . C1C01A ;
2025-04-23 16:17:29 +08:00
}
else
{
_logger . LogError ( $"集中器[{settingInfo.FocusAddress}],[{settingInfo.MeterId}]阀控命令错误:{settingInfo.TripType},-102" ) ;
continue ;
}
2025-04-24 17:48:20 +08:00
var temCode = "10_01_" ;
//根据电表型号获取协议插件
var protocolPlugin = await _protocolService . GetProtocolServiceAsync ( ammeterInfo . BrandType ) ;
2025-04-23 16:17:29 +08:00
if ( protocolPlugin = = null )
{
2025-04-24 17:48:20 +08:00
_logger . LogError ( $"{nameof(AmmeterScheduledAutoValveControl)} 定时阀控运行时间{currentTime}没有找到对应的协议组件,-105" ) ;
return ;
2025-04-23 16:17:29 +08:00
}
2025-04-24 23:39:39 +08:00
ProtocolBuildResponse builderResponse = await protocolPlugin . BuildAsync ( new ProtocolBuildRequest ( )
2025-04-23 16:17:29 +08:00
{
2025-04-24 17:48:20 +08:00
FocusAddress = ammeterInfo . FocusAddress ,
Pn = ammeterInfo . MeteringCode ,
ItemCode = temCode ,
2025-04-24 23:39:39 +08:00
SubProtocolRequest = new SubProtocolBuildRequest ( )
{
MeterAddress = ammeterInfo . AmmerterAddress ,
Password = ammeterInfo . Password ,
ItemCode = subItemCode ,
}
2025-04-24 17:48:20 +08:00
} ) ;
string taskMark = CommonHelper . GetTaskMark ( builderResponse . AFn , builderResponse . Fn , ammeterInfo . MeteringCode , builderResponse . MSA , builderResponse . Seq ) ;
2025-04-23 16:17:29 +08:00
var meterReadingRecords = new MeterReadingTelemetryPacketInfo ( )
{
SystemName = SystemType ,
ProjectId = $"{ammeterInfo.ProjectID}" ,
DeviceType = $"{MeterTypeEnum.Ammeter}" ,
DeviceId = $"{ammeterInfo.MeterId}" ,
Timestamps = currentTime . GetDateTimeOffset ( ) . ToUnixTimeNanoseconds ( ) ,
DatabaseBusiID = ammeterInfo . DatabaseBusiID ,
PendingCopyReadTime = currentTime ,
CreationTime = currentTime ,
MeterAddress = ammeterInfo . AmmerterAddress ,
2025-04-24 17:48:20 +08:00
AFN = builderResponse . AFn ,
Fn = builderResponse . Fn ,
Seq = builderResponse . Seq ,
2025-04-23 16:17:29 +08:00
MSA = builderResponse . MSA ,
ItemCode = temCode ,
TaskMark = taskMark ,
IsSend = false ,
ManualOrNot = false ,
Pn = ammeterInfo . MeteringCode ,
IssuedMessageId = GuidGenerator . Create ( ) . ToString ( ) ,
IssuedMessageHexString = Convert . ToHexString ( builderResponse . Data ) ,
IsReceived = false ,
ScoreValue = $"{ammeterInfo.FocusAddress}.{taskMark}" . Md5Fun ( ) ,
} ;
taskList . Add ( meterReadingRecords ) ;
}
if ( taskList = = null | | taskList . Count < = 0 )
{
2025-04-24 17:48:20 +08:00
_logger . LogError ( $"{nameof(AmmeterScheduledAutoValveControl)} 定时阀控运行时间{currentTime}没有自动阀控任务生成,-106" ) ;
2025-04-23 16:17:29 +08:00
return ;
}
//任务记录入库
_ = _dbProvider . BatchInsertAsync ( metadata , taskList ) ;
//任务信息推送Kafka
_ = DeviceGroupBalanceControl . ProcessWithThrottleAsync < MeterReadingTelemetryPacketInfo > (
items : taskList ,
deviceIdSelector : data = > data . DeviceId ,
processor : ( data , groupIndex ) = >
{
_ = KafkaProducerIssuedMessageAction ( ProtocolConst . AmmeterSubscriberWorkerAutoValveControlIssuedEventName , data , groupIndex ) ;
}
) ;
//todo 阀控记录入库,推送到新的服务
}
catch ( Exception )
{
throw ;
}
}
2025-03-12 14:57:42 +08:00
/// <summary>
/// 获取水表信息
/// </summary>
/// <param name="gatherCode">采集端Code</param>
/// <returns></returns>
//[HttpGet]
//[Route($"ammeter/list")]
public override async Task < List < WatermeterInfo > > GetWatermeterInfoList ( string gatherCode = "V4-Gather-8890" )
{
string sql = $ @ "SELECT
2025-04-15 16:48:35 +08:00
A . ID as MeterId ,
2025-03-12 14:57:42 +08:00
A . Name ,
2025-04-15 16:48:35 +08:00
A . FocusID as FocusId ,
2025-03-12 14:57:42 +08:00
A . MeteringCode ,
A . Baudrate ,
A . MeteringPort ,
A . [ Address ] AS MeterAddress ,
A . [ Password ] ,
A . TypeName ,
A . Protocol ,
A . Code ,
A . LinkType ,
A . HaveValve ,
A . MeterType AS MeterTypeName ,
A . MeterBrand ,
A . TimesRate ,
A . TimeDensity ,
A . TripState ,
B . [ Address ] ,
B . AreaCode ,
B . AutomaticReport ,
A . [ State ] ,
C . GatherCode ,
A . [ ProjectID ] ,
B . AbnormalState ,
2025-03-21 11:48:31 +08:00
B . LastTime ,
CONCAT ( B . AreaCode , B . [ Address ] ) AS FocusAddress ,
( select top 1 DatabaseBusiID from TB_Project where ID = b . ProjectID ) AS DatabaseBusiID
2025-03-12 14:57:42 +08:00
FROM [ dbo ] . [ TB_WatermeterInfo ] ( NOLOCK ) AS A
INNER JOIN [ dbo ] . [ TB_FocusInfo ] ( NOLOCK ) AS B ON A . FocusID = B . ID AND B . RemoveState > = 0 AND B . State > = 0
INNER JOIN [ dbo ] . [ TB_GatherInfo ] ( NOLOCK ) AS C ON B . GatherInfoID = C . ID
WHERE A . State > = 0 AND A . State < 100 ";
if ( ! string . IsNullOrWhiteSpace ( gatherCode ) )
{
sql = $@"{sql} AND C.GatherCode= '{gatherCode}'" ;
}
return await SqlProvider . Instance . Change ( DbEnum . EnergyDB )
. Ado
. QueryAsync < WatermeterInfo > ( sql ) ;
2025-04-23 16:17:29 +08:00
}
2025-03-12 14:57:42 +08:00
}
}