2024-10-30 17:49:05 +08:00
|
|
|
|
using JiShe.CollectBus.Common.Enums;
|
2024-10-29 16:28:14 +08:00
|
|
|
|
using JiShe.CollectBus.Common.Extensions;
|
|
|
|
|
|
using JiShe.CollectBus.Common.Models;
|
2024-09-30 17:10:43 +08:00
|
|
|
|
using JiShe.CollectBus.Protocol.Contracts.Interfaces;
|
2024-10-08 14:41:41 +08:00
|
|
|
|
using JiShe.CollectBus.Protocol.Contracts.Models;
|
2024-10-29 16:28:14 +08:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2024-11-06 16:48:44 +08:00
|
|
|
|
using System.Net.Sockets;
|
2024-09-30 17:10:43 +08:00
|
|
|
|
|
|
|
|
|
|
namespace JiShe.CollectBus.Protocol.Contracts.Abstracts
|
|
|
|
|
|
{
|
2024-10-30 17:49:05 +08:00
|
|
|
|
public abstract class BaseProtocolPlugin(ILogger<BaseProtocolPlugin> logger) : IProtocolPlugin
|
2024-09-30 17:10:43 +08:00
|
|
|
|
{
|
2024-10-29 16:28:14 +08:00
|
|
|
|
//起始字符
|
|
|
|
|
|
public const string stx = "68";
|
|
|
|
|
|
//结束字符
|
|
|
|
|
|
public const string end = "16";
|
|
|
|
|
|
//头部字节长度
|
|
|
|
|
|
public const int hearderLen = 6;
|
|
|
|
|
|
//消息认证码字段长度
|
|
|
|
|
|
public const int pWLen = 16;
|
|
|
|
|
|
|
|
|
|
|
|
public const int tPLen = 6;
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-25 19:11:43 +08:00
|
|
|
|
public abstract Task<ProtocolInfo> GetAsync();
|
2024-09-30 17:10:43 +08:00
|
|
|
|
|
2024-10-29 16:28:14 +08:00
|
|
|
|
public abstract Task AnalyzeAsync(MessageReceivedEvent messageReceivedEvent, Action<byte[]>? sendAction = null);
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 登录帧解析
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="messageReceivedEvent">报文</param>
|
|
|
|
|
|
/// <param name="sendAction">发送委托</param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-10-30 17:49:05 +08:00
|
|
|
|
public virtual async Task LoginAsync(MessageReceivedLoginEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
|
2024-10-29 16:28:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
var hexStringList = messageReceivedEvent.MessageHexString.StringToPairs();
|
|
|
|
|
|
var aTuple = (Tuple<string, int>)hexStringList.GetAnalyzeValue(CommandChunkEnum.A);
|
|
|
|
|
|
var seq = (Seq)hexStringList.GetAnalyzeValue(CommandChunkEnum.SEQ);
|
|
|
|
|
|
var reqParam = new ReqParameter2
|
|
|
|
|
|
{
|
|
|
|
|
|
AFN = AFN.确认或否认,
|
|
|
|
|
|
FunCode = (int)CFromStationFunCode.链路数据,
|
|
|
|
|
|
PRM = PRM.从动站报文,
|
|
|
|
|
|
A = aTuple.Item1,
|
|
|
|
|
|
Seq = new Seq()
|
|
|
|
|
|
{
|
|
|
|
|
|
TpV = TpV.附加信息域中无时间标签,
|
|
|
|
|
|
FIRFIN = FIRFIN.单帧,
|
|
|
|
|
|
CON = CON.需要对该帧进行确认,
|
|
|
|
|
|
PRSEQ = seq.PRSEQ
|
|
|
|
|
|
},
|
|
|
|
|
|
MSA = aTuple.Item2,
|
|
|
|
|
|
Pn = 0,
|
|
|
|
|
|
Fn = 1
|
|
|
|
|
|
};
|
|
|
|
|
|
var bytes = GetCommandBytes(reqParam);
|
|
|
|
|
|
if (sendAction != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
sendAction(bytes);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 心跳帧解析
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="messageReceivedEvent">报文</param>
|
|
|
|
|
|
/// <param name="sendAction">发送委托</param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-10-30 17:49:05 +08:00
|
|
|
|
public virtual async Task HeartbeatAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
|
2024-10-29 16:28:14 +08:00
|
|
|
|
{
|
|
|
|
|
|
var hexStringList = messageReceivedEvent.MessageHexString.StringToPairs();
|
|
|
|
|
|
var aTuple = (Tuple<string,int>)hexStringList.GetAnalyzeValue(CommandChunkEnum.A);
|
|
|
|
|
|
var seq = (Seq)hexStringList.GetAnalyzeValue(CommandChunkEnum.SEQ);
|
|
|
|
|
|
if (seq.TpV == TpV.附加信息域中带时间标签)
|
|
|
|
|
|
{
|
|
|
|
|
|
//解析
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
if (seq.CON == CON.需要对该帧进行确认)
|
|
|
|
|
|
{
|
|
|
|
|
|
var reqParam = new ReqParameter2()
|
|
|
|
|
|
{
|
|
|
|
|
|
AFN = AFN.确认或否认,
|
|
|
|
|
|
FunCode = (int)CFromStationFunCode.链路数据,
|
|
|
|
|
|
PRM = PRM.从动站报文,
|
|
|
|
|
|
A = aTuple.Item1,
|
|
|
|
|
|
Seq = new Seq()
|
|
|
|
|
|
{
|
|
|
|
|
|
TpV = TpV.附加信息域中无时间标签,
|
|
|
|
|
|
FIRFIN = FIRFIN.单帧,
|
|
|
|
|
|
CON = CON.不需要对该帧进行确认,
|
|
|
|
|
|
PRSEQ = seq.PRSEQ,
|
|
|
|
|
|
},
|
|
|
|
|
|
MSA = aTuple.Item2,
|
|
|
|
|
|
Pn = 0,
|
|
|
|
|
|
Fn = 1
|
|
|
|
|
|
};
|
|
|
|
|
|
var bytes = GetCommandBytes(reqParam);
|
|
|
|
|
|
if (sendAction != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
sendAction(bytes);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-06 16:48:44 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 确认或否认
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name=""></param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-11-07 19:39:05 +08:00
|
|
|
|
public virtual async Task GetAnswerAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
|
2024-11-06 16:48:44 +08:00
|
|
|
|
{
|
|
|
|
|
|
var hexStringList = messageReceivedEvent.MessageHexString.StringToPairs();
|
|
|
|
|
|
var fn = hexStringList.GetAnalyzeValue(CommandChunkEnum.FN);
|
|
|
|
|
|
//1:全部确认
|
|
|
|
|
|
//2:全部否认
|
|
|
|
|
|
//3:按数据单元表示确认和否认
|
|
|
|
|
|
//4 硬件安全认证错误应答
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 电表档案解析
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="messageReceivedEvent"></param>
|
|
|
|
|
|
/// <param name="sendAction"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-11-07 19:39:05 +08:00
|
|
|
|
public virtual async Task GetAmmeterParameterAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
|
2024-11-06 16:48:44 +08:00
|
|
|
|
{
|
2024-11-07 19:39:05 +08:00
|
|
|
|
var hexDatas = GetHexDatas(messageReceivedEvent.MessageHexString);
|
2024-10-29 16:28:14 +08:00
|
|
|
|
|
2024-11-06 16:48:44 +08:00
|
|
|
|
var meterList = new List<AmmeterParameter>();
|
|
|
|
|
|
var count = (hexDatas[1] + hexDatas[0]).HexToDec();
|
|
|
|
|
|
//if (2 + count * 27 != hexDatas.Count - pWLen - tPLen - 2)
|
|
|
|
|
|
// return;
|
|
|
|
|
|
var index = 2;//数量
|
|
|
|
|
|
for (int i = 1; i <= count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var meterNumber = $"{hexDatas[index + 1]}{hexDatas[index]}".HexToDec();
|
|
|
|
|
|
index += 2;
|
|
|
|
|
|
|
|
|
|
|
|
var pn = $"{hexDatas[index + 1]}{hexDatas[index]}".HexToDec();
|
|
|
|
|
|
index += 2;
|
|
|
|
|
|
|
|
|
|
|
|
var baudRateAndPortBin = hexDatas[index].HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var baudRate = baudRateAndPortBin.Substring(0, 3).BinToDec();
|
|
|
|
|
|
var port = baudRateAndPortBin.Substring(3, 5).BinToDec();
|
|
|
|
|
|
index += 1;
|
|
|
|
|
|
|
|
|
|
|
|
var protocolType = (CommunicationProtocolType)hexDatas[index].HexToDec();
|
|
|
|
|
|
index += 1;
|
|
|
|
|
|
|
|
|
|
|
|
var addressHexList = hexDatas.Skip(index).Take(6).ToList();
|
|
|
|
|
|
addressHexList.Reverse();
|
|
|
|
|
|
var address = string.Join("", addressHexList);
|
|
|
|
|
|
index += 6;
|
|
|
|
|
|
|
|
|
|
|
|
var pwdHexList = hexDatas.Skip(index).Take(6).ToList();
|
|
|
|
|
|
pwdHexList.Reverse();
|
|
|
|
|
|
var password = string.Join("", pwdHexList.Take(3).ToList());
|
|
|
|
|
|
index += 6;
|
|
|
|
|
|
|
|
|
|
|
|
var rateNumberBin = hexDatas[index].HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var rateNumber = rateNumberBin.Substring(4).BinToDec();
|
|
|
|
|
|
index += 1;
|
|
|
|
|
|
|
|
|
|
|
|
var intBitAndDecBitNumberBin = hexDatas[index].HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var intBitNumber = intBitAndDecBitNumberBin.Substring(4, 2).BinToDec() + 4;
|
|
|
|
|
|
var decBitNumber = intBitAndDecBitNumberBin.Substring(6, 2).BinToDec() + 1;
|
|
|
|
|
|
index += 1;
|
|
|
|
|
|
|
|
|
|
|
|
// hexDatas.GetRange()
|
|
|
|
|
|
var collectorAddressHexList = hexDatas.Skip(index).Take(6).ToList();
|
|
|
|
|
|
collectorAddressHexList.Reverse();
|
|
|
|
|
|
var collectorAddress = string.Join("", collectorAddressHexList);
|
|
|
|
|
|
index += 6;
|
|
|
|
|
|
|
|
|
|
|
|
var userClassNumberBin = hexDatas[index].HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var userClass = userClassNumberBin.Substring(0, 4).BinToDec();
|
|
|
|
|
|
var userSubClass = userClassNumberBin.Substring(4, 4).BinToDec();
|
|
|
|
|
|
index += 1;
|
|
|
|
|
|
|
|
|
|
|
|
meterList.Add(new AmmeterParameter()
|
|
|
|
|
|
{
|
|
|
|
|
|
Pn = pn,
|
|
|
|
|
|
BaudRate = baudRate,
|
|
|
|
|
|
Port = port,
|
|
|
|
|
|
ProtocolType = protocolType,
|
|
|
|
|
|
Address = address,
|
|
|
|
|
|
Password = password,
|
|
|
|
|
|
RateNumber = rateNumber,
|
|
|
|
|
|
IntegerBitNumber = intBitNumber,
|
|
|
|
|
|
DecimalBitNumber = decBitNumber,
|
|
|
|
|
|
CollectorAddress = collectorAddress,
|
|
|
|
|
|
UserCategoryNumber = userClass,
|
|
|
|
|
|
UserSubclassNumber = userSubClass,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 当前正向有功电能示值
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="messageReceivedEvent">报文</param>
|
|
|
|
|
|
/// <param name="sendAction">发送委托</param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-11-07 19:39:05 +08:00
|
|
|
|
public virtual async Task GetActivePowerIndicationAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
|
2024-11-06 16:48:44 +08:00
|
|
|
|
{
|
2024-11-07 19:39:05 +08:00
|
|
|
|
var hexDatas = GetHexDatas(messageReceivedEvent.MessageHexString);
|
2024-11-06 16:48:44 +08:00
|
|
|
|
|
|
|
|
|
|
var minutes = Convert.ToInt32(hexDatas[0]); // 获取当前分钟数
|
|
|
|
|
|
var hours = Convert.ToInt32(hexDatas[1]); // 获取当前小时数
|
|
|
|
|
|
var day = Convert.ToInt32(hexDatas[2]); // 获取当前日期的日数
|
|
|
|
|
|
var month = Convert.ToInt32(hexDatas[3]); // 获取当前月份
|
|
|
|
|
|
var year = Convert.ToInt32(hexDatas[4]); // 获取当前日期的年份
|
|
|
|
|
|
|
|
|
|
|
|
var rateNumber = Convert.ToInt32(hexDatas[5]);
|
|
|
|
|
|
var kwhTotal = hexDatas.Skip(5).Take(5).ToList();
|
|
|
|
|
|
var kwhList = new List<decimal>();
|
|
|
|
|
|
var index = 11;
|
|
|
|
|
|
for (int i = 0; i < rateNumber; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var kwhHexList = hexDatas.Skip(11).Take(5).ToList();
|
|
|
|
|
|
kwhHexList.Reverse();
|
|
|
|
|
|
var integerStr = $"{kwhHexList.Take(0)}{kwhHexList.Take(1)}{kwhHexList.Take(2)}";
|
|
|
|
|
|
var decimalValStr = $"{kwhHexList[3]}{kwhHexList[4]}";
|
|
|
|
|
|
var val = decimal.Parse($"{integerStr}{decimalValStr}");
|
|
|
|
|
|
kwhList.Add(val);
|
|
|
|
|
|
index += 5;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 日冻结正向有功电能示值
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="messageReceivedEvent"></param>
|
|
|
|
|
|
/// <param name="sendAction"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2024-11-07 19:39:05 +08:00
|
|
|
|
public virtual async Task GetDailyFrozenAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
|
2024-11-06 16:48:44 +08:00
|
|
|
|
{
|
2024-11-07 19:39:05 +08:00
|
|
|
|
var hexDatas = GetHexDatas(messageReceivedEvent.MessageHexString);
|
|
|
|
|
|
//附录A.20 日月年
|
|
|
|
|
|
var td_dHex= hexDatas.Take(3).ToList();
|
|
|
|
|
|
//附录A.15 分时日月年
|
|
|
|
|
|
var readingTimeHex = hexDatas.Skip(3).Take(5).ToList();
|
|
|
|
|
|
var rateNumberHex = hexDatas.Skip(8).Take(1).FirstOrDefault().HexToDec();
|
|
|
|
|
|
|
|
|
|
|
|
//附录A.14 kWh 5字节
|
|
|
|
|
|
for (int i = 0; i < rateNumberHex; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//接收<2024/11/7 17:34:42>: 68 3E 01 3E 01 68
|
|
|
|
|
|
//控制域 88
|
|
|
|
|
|
//地址域 20 32 90 26 1A
|
|
|
|
|
|
//AFN 0C
|
|
|
|
|
|
//Seq 62
|
|
|
|
|
|
//pn 01 01
|
|
|
|
|
|
//fn 01 03
|
|
|
|
|
|
|
|
|
|
|
|
//F0 16
|
|
|
|
|
|
|
|
|
|
|
|
//报文解析:计量点1数据
|
|
|
|
|
|
//++++++++ F25:当前三相及总有/无功功率功率因数 三相电压电流 零序电流 ++++++++
|
|
|
|
|
|
//终端抄表时间:2024年11月07日17时34分 34 17 07 11 24
|
|
|
|
|
|
//当前总有功功率:-0.0028 kW 28 00 80
|
|
|
|
|
|
//当前A相有功功率:0 kW 00 00 80
|
|
|
|
|
|
//当前B相有功功率:0 kW 00 00 80
|
|
|
|
|
|
//当前C相有功功率:-0.0027 kW 27 00 80
|
|
|
|
|
|
//当前总无功功率:-0.0047 kW 47 00 80
|
|
|
|
|
|
//当前A相无功功率:0 kW 00 00 00
|
|
|
|
|
|
//当前B相无功功率:0 kW 00 00 00
|
|
|
|
|
|
//当前C相无功功率:-0.0047 kW 47 00 80
|
|
|
|
|
|
//当前总功率因数:-51.8 % 18 85
|
|
|
|
|
|
//当前A相功率因数:0 % 00 80
|
|
|
|
|
|
//当前B相功率因数:0 % 00 80
|
|
|
|
|
|
//当前C相功率因数:-50 % 00 85
|
|
|
|
|
|
//当前A相电压:0.1 V 01 00
|
|
|
|
|
|
//当前B相电压:0.2 V 02 00
|
|
|
|
|
|
//当前C相电压:239 V 90 23
|
|
|
|
|
|
//当前A相电流:0 A 00 00 80
|
|
|
|
|
|
//当前B相电流:0 A 00 00 80
|
|
|
|
|
|
//当前C相电流:-0.024 A 24 00 80
|
|
|
|
|
|
//当前零序电流:Error: 数据不符合BCD码格式 A EE EE EE
|
|
|
|
|
|
//当前总视在功率:Error: 数据不符合BCD码格式 kVA EE EE EE
|
|
|
|
|
|
//当前A相视在功率:Error: 数据不符合BCD码格式 kVA EE EE EE
|
|
|
|
|
|
//当前B相视在功率:Error: 数据不符合BCD码格式 kVA EE EE EE
|
|
|
|
|
|
//当前C相视在功率:Error: 数据不符合BCD码格式 kVA EE EE EE
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 当前三相及总有/无功功率、功率因数、三相电压、电流、零序电流、视在功率
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="messageReceivedEvent"></param>
|
|
|
|
|
|
/// <param name="sendAction"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public virtual async Task GetF25Async(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hexDatas = GetHexDatas(messageReceivedEvent.MessageHexString);
|
|
|
|
|
|
//A.15 分时日月年
|
|
|
|
|
|
var readingTimeHex = hexDatas.Take(5).ToList();
|
|
|
|
|
|
//A.9 kW
|
|
|
|
|
|
var crntTotalActivePowerHex = hexDatas.Skip((int)F25DataItemEnum.CrntTotalActivePower).Take(3).ToList();
|
|
|
|
|
|
var crntActivePowerOfA = hexDatas.Skip((int)F25DataItemEnum.CrntActivePowerOfA).Take(3).ToList();
|
|
|
|
|
|
var crntActivePowerOfB = hexDatas.Skip((int)F25DataItemEnum.CrntActivePowerOfB).Take(3).ToList();
|
|
|
|
|
|
var crntActivePowerOfC = hexDatas.Skip((int)F25DataItemEnum.CrntActivePowerOfC).Take(3).ToList();
|
|
|
|
|
|
var crntTotalReactivePower = hexDatas.Skip((int)F25DataItemEnum.CrntTotalReactivePower).Take(3).ToList();
|
|
|
|
|
|
var crntReactivePowerOfA = hexDatas.Skip((int)F25DataItemEnum.CrntReactivePowerOfA).Take(3).ToList();
|
|
|
|
|
|
var crntReactivePowerOfB = hexDatas.Skip((int)F25DataItemEnum.CrntReactivePowerOfB).Take(3).ToList();
|
|
|
|
|
|
var crntReactivePowerOfC = hexDatas.Skip((int)F25DataItemEnum.CrntReactivePowerOfC).Take(2).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
//A.5 %
|
|
|
|
|
|
var crntTotalPowerFactor = hexDatas.Skip((int)F25DataItemEnum.CrntTotalPowerFactor).Take(2).ToList();
|
|
|
|
|
|
var crntPowerFactorOfA = hexDatas.Skip((int)F25DataItemEnum.CrntPowerFactorOfA).Take(2).ToList();
|
|
|
|
|
|
var crntPowerFactorOfB = hexDatas.Skip((int)F25DataItemEnum.CrntPowerFactorOfB).Take(2).ToList();
|
|
|
|
|
|
var CrntPowerFactorOfC = hexDatas.Skip((int)F25DataItemEnum.CrntPowerFactorOfC).Take(2).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
//A.7 V
|
|
|
|
|
|
var crntVoltageOfA = hexDatas.Skip((int)F25DataItemEnum.CrntVoltageOfA).Take(2).ToList();
|
|
|
|
|
|
var crntVoltageOfB = hexDatas.Skip((int)F25DataItemEnum.CrntVoltageOfB).Take(2).ToList();
|
|
|
|
|
|
var crntVoltageOfC = hexDatas.Skip((int)F25DataItemEnum.CrntVoltageOfC).Take(2).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
//A.25 A
|
|
|
|
|
|
var crntCurrentOfA = hexDatas.Skip((int)F25DataItemEnum.CrntCurrentOfA).Take(3).ToList();
|
|
|
|
|
|
var crntCurrentOfB = hexDatas.Skip((int)F25DataItemEnum.CrntCurrentOfB).Take(3).ToList();
|
|
|
|
|
|
var crntCurrentOfC = hexDatas.Skip((int)F25DataItemEnum.CrntCurrentOfC).Take(3).ToList();
|
|
|
|
|
|
var crntZeroSequenceCurrent = hexDatas.Skip((int)F25DataItemEnum.CrntZeroSequenceCurrent).Take(3).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
//A.9 kVA
|
|
|
|
|
|
var crntTotalApparentPower = hexDatas.Skip((int)F25DataItemEnum.CrntTotalApparentPower).Take(3).ToList();
|
|
|
|
|
|
var crntApparentPowerOfA = hexDatas.Skip((int)F25DataItemEnum.CrntApparentPowerOfA).Take(3).ToList();
|
|
|
|
|
|
var crntApparentPowerOfB = hexDatas.Skip((int)F25DataItemEnum.CrntApparentPowerOfB).Take(3).ToList();
|
|
|
|
|
|
var crntApparentPowerOfC = hexDatas.Skip((int)F25DataItemEnum.CrntApparentPowerOfC).Take(3).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 透明转发 命令 应答
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="messageReceivedEvent"></param>
|
|
|
|
|
|
/// <param name="sendAction"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public virtual async Task GetTransparentForwardingResultAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hexDatas = GetHexDatas(messageReceivedEvent.MessageHexString);
|
|
|
|
|
|
|
|
|
|
|
|
var port = hexDatas[0].HexToDec();
|
|
|
|
|
|
|
|
|
|
|
|
//A.12
|
|
|
|
|
|
var a = hexDatas.Skip(1).Take(6).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
var result = hexDatas.Skip(7).Take(1).FirstOrDefault().HexToDec();
|
|
|
|
|
|
var transparentForwardingFlag = (TransparentForwardingFlagEnum)result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static List<string> GetHexDatas(string messageHexString)
|
|
|
|
|
|
{
|
|
|
|
|
|
var hexStringList = messageHexString.StringToPairs();
|
2024-11-06 16:48:44 +08:00
|
|
|
|
var hexDatas = (List<string>)hexStringList.GetAnalyzeValue(CommandChunkEnum.Data);
|
2024-11-07 19:39:05 +08:00
|
|
|
|
return hexDatas;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 解析时间标签
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hexDatas"></param>
|
|
|
|
|
|
private void AnalysisTp(List<string> hexDatas)
|
|
|
|
|
|
{
|
|
|
|
|
|
var pFC = hexDatas[0].HexToDec();//启动帧帧序号计数器
|
|
|
|
|
|
var seconds = Convert.ToInt32(hexDatas[1]); // 获取当前秒数
|
|
|
|
|
|
var minutes = Convert.ToInt32(hexDatas[2]); // 获取当前分钟数
|
|
|
|
|
|
var hours = Convert.ToInt32(hexDatas[3]); // 获取当前小时数
|
|
|
|
|
|
var day = Convert.ToInt32(hexDatas[4]); // 获取当前日期的日数
|
|
|
|
|
|
var delayTime = hexDatas[5].HexToDec();//延迟时间 min
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据格式05
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hex1"></param>
|
|
|
|
|
|
/// <param name="hex2"></param>
|
|
|
|
|
|
private decimal AnalysisA05(string hex1,string hex2)
|
|
|
|
|
|
{
|
|
|
|
|
|
var bin1 = hex1.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var hundredDigitNumbers = bin1.Substring(1, 3).BinToDec();//百位
|
|
|
|
|
|
var tenDigitNumber = bin1.Substring(4).BinToDec();//十位
|
|
|
|
|
|
|
|
|
|
|
|
var bin2 = hex2.HexToBin().PadLeft(8,'0');
|
|
|
|
|
|
var singleDigitNumber = bin1.Substring(0, 4).BinToDec();//个位
|
|
|
|
|
|
var deciles = bin1.Substring(4).BinToDec();//十分位
|
|
|
|
|
|
|
|
|
|
|
|
var value = decimal.Parse($"{hundredDigitNumbers}{tenDigitNumber}{singleDigitNumber}.{deciles}");
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
2024-11-06 16:48:44 +08:00
|
|
|
|
|
2024-11-07 19:39:05 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据格式07
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hex1"></param>
|
|
|
|
|
|
/// <param name="hex2"></param>
|
|
|
|
|
|
private decimal AnalysisA07(string hex1, string hex2)
|
|
|
|
|
|
{
|
|
|
|
|
|
var bin1 = hex1.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var tenDigitNumber = bin1.Substring(1, 3).BinToDec();//十位
|
|
|
|
|
|
var singleDigitNumber = bin1.Substring(4).BinToDec();//个位
|
2024-11-06 16:48:44 +08:00
|
|
|
|
|
2024-11-07 19:39:05 +08:00
|
|
|
|
var bin2 = hex2.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var deciles = bin1.Substring(0, 4).BinToDec();//十分位
|
|
|
|
|
|
var percentile = bin1.Substring(4).BinToDec();//百分位
|
|
|
|
|
|
|
|
|
|
|
|
var value = decimal.Parse($"{tenDigitNumber}{singleDigitNumber}.{deciles}{percentile}");
|
|
|
|
|
|
return value;
|
2024-11-06 16:48:44 +08:00
|
|
|
|
}
|
2024-11-07 19:39:05 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据格式09
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hex1"></param>
|
|
|
|
|
|
/// <param name="hex2"></param>
|
|
|
|
|
|
/// <param name="hex3"></param>
|
|
|
|
|
|
private decimal AnalysisA09(string hex1, string hex2, string tenAndSingleDigit)
|
|
|
|
|
|
{
|
|
|
|
|
|
var bin3 = tenAndSingleDigit.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var tenDigitNumber = bin3.Substring(1, 3).BinToDec();//十位
|
|
|
|
|
|
var singleDigitNumber = bin3.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
var value = decimal.Parse($"{tenDigitNumber}{singleDigitNumber}.{hex2}{hex1}");
|
|
|
|
|
|
return value;
|
|
|
|
|
|
|
|
|
|
|
|
//var bin3 = hex3.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
//var thousandthPercentile = bin3.Substring(0, 4).BinToDec();//千分位
|
|
|
|
|
|
//var tenThousandPositions = bin3.Substring(4).BinToDec();//万分位
|
|
|
|
|
|
|
|
|
|
|
|
//var bin2 = hex2.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
//var deciles = bin2.Substring(0, 4).BinToDec();//十分位
|
|
|
|
|
|
//var percentile = bin2.Substring(4).BinToDec();//百分位
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据格式12 TODO:待优化
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hex1"></param>
|
|
|
|
|
|
/// <param name="hex2"></param>
|
|
|
|
|
|
/// <param name="hex3"></param>
|
|
|
|
|
|
/// <param name="hex4"></param>
|
|
|
|
|
|
/// <param name="hex5"></param>
|
|
|
|
|
|
/// <param name="hex6"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private string AnalysisA12(string hex1, string hex2, string hex3, string hex4, string hex5, string hex6)
|
|
|
|
|
|
{
|
|
|
|
|
|
var bin1 = hex1.HexToBin().PadLeft(8, '0'); ;
|
|
|
|
|
|
var hundredBillionsOfPosition = bin1.Substring(0, 4).BinToDec();//千亿位
|
|
|
|
|
|
var billionsOfPosition = bin1.Substring(4).BinToDec();//百亿位
|
|
|
|
|
|
|
|
|
|
|
|
var bin2 = hex2.HexToBin().PadLeft(8, '0'); ;
|
|
|
|
|
|
var billionOfPosition = bin2.Substring(0, 4).BinToDec();//十亿位
|
|
|
|
|
|
var hundredMillionOfPosition = bin2.Substring(4).BinToDec();//亿位
|
|
|
|
|
|
|
|
|
|
|
|
var bin3 = hex3.HexToBin().PadLeft(8, '0'); ;
|
|
|
|
|
|
var millionsOfPosition = bin3.Substring(0, 4).BinToDec();//千万位
|
|
|
|
|
|
var millionOfPosition = bin3.Substring(4).BinToDec();//百万位
|
|
|
|
|
|
|
|
|
|
|
|
var bin4 = hex4.HexToBin().PadLeft(8, '0'); ;
|
|
|
|
|
|
var hundredThousandOfPosition = bin4.Substring(0, 4).BinToDec();//十万位
|
|
|
|
|
|
var tenThousandOfPosition = bin4.Substring(4).BinToDec();//万位
|
|
|
|
|
|
|
|
|
|
|
|
var bin5 = hex5.HexToBin().PadLeft(8, '0'); ;
|
|
|
|
|
|
var thousandOfPosition = bin5.Substring(0, 4).BinToDec();//千位
|
|
|
|
|
|
var hundredsOfPosition = bin5.Substring(4).BinToDec();//百位
|
|
|
|
|
|
|
|
|
|
|
|
var bin6 = hex6.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var tenDigitNumber = bin6.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
var singleDigitNumber = bin6.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
var value = $"{hundredBillionsOfPosition}{billionsOfPosition}{billionOfPosition}{hundredMillionOfPosition}{millionsOfPosition}" +
|
|
|
|
|
|
$"{hundredThousandOfPosition}{tenThousandOfPosition}{thousandOfPosition}{hundredsOfPosition}{tenDigitNumber}{singleDigitNumber}";
|
|
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据格式14
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hex1"></param>
|
|
|
|
|
|
/// <param name="hex2"></param>
|
|
|
|
|
|
/// <param name="hex3"></param>
|
|
|
|
|
|
/// <param name="hex4"></param>
|
|
|
|
|
|
/// <param name="hex5"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private decimal AnalysisA14(string hex1, string hex2, string hex3, string hex4, string hex5)
|
|
|
|
|
|
{
|
|
|
|
|
|
var bin1 = hex1.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var hundredThousandOfPosition = bin1.Substring(0, 4).BinToDec();//十万位
|
|
|
|
|
|
var tenThousandOfPosition = bin1.Substring(4).BinToDec();//万位
|
|
|
|
|
|
|
|
|
|
|
|
var bin2 = hex2.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var thousandOfPosition = bin2.Substring(0, 4).BinToDec();//千位
|
|
|
|
|
|
var hundredsOfPosition = bin2.Substring(4).BinToDec();//百位
|
|
|
|
|
|
|
|
|
|
|
|
var bin3 = hex3.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var tenDigitNumber = bin3.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
var singleDigitNumber = bin3.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
var bin4 = hex4.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var deciles = bin4.Substring(0, 4).BinToDec();//十分位
|
|
|
|
|
|
var percentile = bin4.Substring(4).BinToDec();//百分位
|
|
|
|
|
|
|
|
|
|
|
|
var bin5 = hex5.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var thousandthPercentile = bin5.Substring(0, 4).BinToDec();//千分位
|
|
|
|
|
|
var tenThousandPositions = bin5.Substring(4).BinToDec();//万分位
|
|
|
|
|
|
|
|
|
|
|
|
var value = decimal.Parse($"{hundredThousandOfPosition}{tenThousandOfPosition}{thousandOfPosition}{hundredsOfPosition}" +
|
|
|
|
|
|
$"{tenDigitNumber}{singleDigitNumber}.{deciles}{percentile}{thousandthPercentile}{tenThousandPositions}");
|
|
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据格式A.15
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hex1"></param>
|
|
|
|
|
|
/// <param name="hex2"></param>
|
|
|
|
|
|
/// <param name="hex3"></param>
|
|
|
|
|
|
/// <param name="hex4"></param>
|
|
|
|
|
|
/// <param name="hex5"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
private DateTime AnalysisA15(string hex1, string hex2, string hex3, string hex4, string hex5)
|
|
|
|
|
|
{
|
|
|
|
|
|
var centuryString = (DateTime.Now.Year / 100).ToString();
|
|
|
|
|
|
var time = DateTime.Parse($"{centuryString}{hex5}-{hex4}-{hex3} {hex2}:{hex1}:00");
|
|
|
|
|
|
return time;
|
|
|
|
|
|
//正常转换方式
|
|
|
|
|
|
//var bin1 = hex1.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
//var tenDigitNumberOfMinute = bin1.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
//var singleDigitNumberOfMinute = bin1.Substring(4).BinToDec();//个位
|
|
|
|
|
|
//var minute = Convert.ToInt32($"{tenDigitNumberOfMinute}{singleDigitNumberOfMinute}");
|
|
|
|
|
|
|
|
|
|
|
|
//var bin2 = hex2.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
//var tenDigitNumberOfHour = bin2.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
//var singleDigitNumberOfHour = bin2.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
//var bin3 = hex3.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
//var tenDigitNumberOfDay = bin3.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
//var singleDigitNumberOfDay = bin3.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
//var bin4 = hex4.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
//var tenDigitNumberOfMonth = bin4.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
//var singleDigitNumberOfMonth = bin4.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
//var bin5 = hex5.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
//var tenDigitNumberOfYear = bin5.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
//var singleDigitNumberOfYear = bin5.Substring(4).BinToDec();//个位
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void AnalysisA20(string hex1, string hex2, string hex3)
|
|
|
|
|
|
{
|
|
|
|
|
|
var bin1 = hex1.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var tenDigitNumberOfYear = bin1.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
var singleDigitNumberOfYear = bin1.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
var bin2 = hex2.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var tenDigitNumberOfMonth = bin2.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
var singleDigitNumberOfMonth = bin2.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
var bin3 = hex3.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var tenDigitNumberOfDay = bin3.Substring(0, 4).BinToDec();//十位
|
|
|
|
|
|
var singleDigitNumberOfDay = bin3.Substring(4).BinToDec();//个位
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据格式25
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="hex1"></param>
|
|
|
|
|
|
/// <param name="hex2"></param>
|
|
|
|
|
|
/// <param name="hex3"></param>
|
|
|
|
|
|
private decimal AnalysisA25(string hex1, string hex2, string hex3)
|
|
|
|
|
|
{
|
|
|
|
|
|
var bin1 = hex1.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var hundredDigitNumbers = bin1.Substring(1, 3).BinToDec();//百位
|
|
|
|
|
|
var tenDigitNumber = bin1.Substring(4).BinToDec();//十位
|
|
|
|
|
|
|
|
|
|
|
|
var bin2 = hex2.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var singleDigitNumber = bin1.Substring(0, 4).BinToDec();//个位
|
|
|
|
|
|
var deciles = bin1.Substring(4).BinToDec();//十分位
|
|
|
|
|
|
|
|
|
|
|
|
var bin3 = hex3.HexToBin().PadLeft(8, '0');
|
|
|
|
|
|
var percentile = bin3.Substring(0,4).BinToDec();//百分位
|
|
|
|
|
|
var thousandthPercentile = bin3.Substring(4).BinToDec();//千分位
|
|
|
|
|
|
|
|
|
|
|
|
var value = decimal.Parse($"{hundredDigitNumbers}{tenDigitNumber}{singleDigitNumber}.{deciles}{percentile}{thousandthPercentile}");
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-10-29 16:28:14 +08:00
|
|
|
|
#region 通用解析
|
|
|
|
|
|
|
|
|
|
|
|
public byte[] GetCommandBytes(ReqParameter reqParameter, List<string>? dataUnit = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var cmdStrList = new List<string>();
|
|
|
|
|
|
var userDatas = GetUserData(reqParameter, dataUnit);
|
|
|
|
|
|
var hearders = GetHeaders(userDatas.Count);
|
|
|
|
|
|
var cs = GetCS(userDatas);
|
|
|
|
|
|
cmdStrList.AddRange(hearders);
|
|
|
|
|
|
cmdStrList.AddRange(userDatas);
|
|
|
|
|
|
cmdStrList.Add(cs);
|
|
|
|
|
|
cmdStrList.Add(end);
|
2024-10-30 17:49:05 +08:00
|
|
|
|
logger.LogInformation($"回复:{string.Join(" ", cmdStrList)}");
|
2024-10-29 16:28:14 +08:00
|
|
|
|
var bytes = cmdStrList.Select(x => Convert.ToByte(x, 16)).ToArray();
|
|
|
|
|
|
return bytes;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 帧校验和
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="userData">用户数据区</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public string GetCS(List<string> userData)
|
|
|
|
|
|
{
|
|
|
|
|
|
byte sum = 0;
|
|
|
|
|
|
foreach (var d in userData)
|
|
|
|
|
|
{
|
|
|
|
|
|
var b = Convert.ToByte(d, 16);
|
|
|
|
|
|
sum += b;
|
|
|
|
|
|
}
|
|
|
|
|
|
return sum.ToString("X2");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 用户数据区
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="reqParameter"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<string> GetUserData(ReqParameter reqParameter, List<string>? dataUnit)
|
|
|
|
|
|
{
|
|
|
|
|
|
var c = GetC(reqParameter.FunCode, reqParameter.PRM);
|
|
|
|
|
|
var a = GetAList(reqParameter.A, reqParameter.MSA);
|
|
|
|
|
|
|
|
|
|
|
|
var linkUserData = GetLinkUserData(reqParameter.AFN, reqParameter.Seq,
|
|
|
|
|
|
((ReqParameter2)reqParameter).Pn, ((ReqParameter2)reqParameter).Fn, dataUnit);
|
|
|
|
|
|
|
|
|
|
|
|
var list = new List<string>() { c };
|
|
|
|
|
|
list.AddRange(a);
|
|
|
|
|
|
list.AddRange(linkUserData);
|
|
|
|
|
|
return list;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 固定长度的报文头 起始字符+长度+长度+起始字符
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="length"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<string> GetHeaders(int length)
|
|
|
|
|
|
{
|
|
|
|
|
|
var headers = new List<string>();
|
|
|
|
|
|
headers.Add(stx);
|
|
|
|
|
|
var l = GetLength(length);
|
|
|
|
|
|
headers.AddRange(l);
|
|
|
|
|
|
headers.AddRange(l);
|
|
|
|
|
|
headers.Add(stx);
|
|
|
|
|
|
return headers;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 长度 2字节 [用户数据区长度]
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<string> GetLength(int length1)
|
|
|
|
|
|
{
|
|
|
|
|
|
var binaryLen = length1.DecToBin();
|
|
|
|
|
|
var protocolIdentification = Enum.Format(typeof(ProtocolIdentification),
|
|
|
|
|
|
ProtocolIdentification.本规约使用, "d").PadLeft(2, '0');
|
|
|
|
|
|
var lenStr = $"{binaryLen}{protocolIdentification}";
|
|
|
|
|
|
var hexLen = lenStr.BinToHex();
|
|
|
|
|
|
hexLen = hexLen.PadLeft(4, '0');
|
|
|
|
|
|
var list = hexLen.StringToPairs();
|
|
|
|
|
|
list.Reverse();
|
|
|
|
|
|
return list;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 控制域
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="funCode">功能码</param>
|
|
|
|
|
|
/// <param name="fcb"></param>
|
|
|
|
|
|
/// <param name="fcv"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public string GetC(int funCode, PRM pRM, int fcb = 0, FCV fcv = FCV.FCB位无效)
|
|
|
|
|
|
{
|
|
|
|
|
|
var cMasterStationFunCodeHex = funCode.DecToBin();
|
|
|
|
|
|
cMasterStationFunCodeHex = cMasterStationFunCodeHex.ToString().PadLeft(4, '0');
|
|
|
|
|
|
var strC = $"{(int)DIR.主站下行报文}{(int)pRM}{fcb}{(int)fcv}{cMasterStationFunCodeHex}";
|
|
|
|
|
|
var hexC = strC.BinToHex().PadLeft(2, '0');
|
|
|
|
|
|
return hexC;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 地址域 3220 09872
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="a1">行政区划码 BCD码 3220=2032</param>
|
|
|
|
|
|
/// <param name="a2">逻辑地址 BIN 09872=2690=>9026</param>
|
|
|
|
|
|
/// <param name="a3">主站地址 BIN 0~127</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<string> GetAList(string a, int mSA)
|
|
|
|
|
|
{
|
|
|
|
|
|
var list = new List<string>();
|
|
|
|
|
|
|
|
|
|
|
|
var a1 = a.Substring(0, 4);
|
|
|
|
|
|
var a1Pairs = a1.StringToPairs();
|
|
|
|
|
|
a1Pairs.Reverse();
|
|
|
|
|
|
list.AddRange(a1Pairs);
|
|
|
|
|
|
|
|
|
|
|
|
var a2 = Convert.ToInt32(a.Substring(4));
|
|
|
|
|
|
var decA2 = a2.DecToHex();
|
|
|
|
|
|
var a2Pairs = decA2.PadLeft(4, '0').StringToPairs();
|
|
|
|
|
|
a2Pairs.Reverse();
|
|
|
|
|
|
list.AddRange(a2Pairs);
|
|
|
|
|
|
|
|
|
|
|
|
//TODO:主站地址和组地址标志
|
|
|
|
|
|
var a3Bin = $"{mSA.DecToBin().PadLeft(7, '0')}0";
|
|
|
|
|
|
list.Add(a3Bin.BinToHex().PadLeft(2, '0'));
|
|
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public List<string> GetLinkUserData(AFN aFN, Seq seq, int pn, int fn, List<string>? dataUnit)
|
|
|
|
|
|
{
|
|
|
|
|
|
var aFNValue = ((int)aFN).DecToHex().PadLeft(2, '0');
|
|
|
|
|
|
var sEQ = GetSEQ(seq.TpV, seq.FIRFIN, seq.CON, seq.PRSEQ);
|
|
|
|
|
|
var dA = GetDA(pn);
|
|
|
|
|
|
var dT = GetDT(fn);
|
|
|
|
|
|
var list = new List<string>() { aFNValue, sEQ };
|
|
|
|
|
|
list.AddRange(dA);
|
|
|
|
|
|
list.AddRange(dT);
|
|
|
|
|
|
|
|
|
|
|
|
if (dataUnit != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
list.AddRange(dataUnit);
|
|
|
|
|
|
}
|
|
|
|
|
|
//list.AddRange(GetDataUnit(aFN,seq));
|
|
|
|
|
|
|
|
|
|
|
|
if (seq.TpV == TpV.附加信息域中带时间标签)
|
|
|
|
|
|
list.AddRange(GetTp("00"));
|
|
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 帧序列域
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="tpV"></param>
|
|
|
|
|
|
/// <param name="fIRFIN"></param>
|
|
|
|
|
|
/// <param name="cON"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public string GetSEQ(TpV tpV, FIRFIN fIRFIN, CON cON, int pRSEQ)
|
|
|
|
|
|
{
|
|
|
|
|
|
var tpVValue = Enum.Format(typeof(TpV),
|
|
|
|
|
|
tpV, "d");
|
|
|
|
|
|
var fIRFINValue = Enum.Format(typeof(FIRFIN),
|
|
|
|
|
|
fIRFIN, "d");
|
|
|
|
|
|
var cONValue = (int)cON;
|
|
|
|
|
|
var sEQBin = $"{tpVValue}{fIRFINValue}{cONValue}{pRSEQ.DecToBin().PadLeft(4, '0')}";
|
|
|
|
|
|
var hexSEQ = sEQBin.BinToHex().PadLeft(2, '0');
|
|
|
|
|
|
return hexSEQ;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 信息点标识
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="pn">计量点</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<string> GetDA(int pn)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (pn == 0)
|
|
|
|
|
|
return new List<string>() { "00", "00" };
|
|
|
|
|
|
var dA2 = (pn - 1) / 8 + 1;//信息点组从1开始 第几组
|
|
|
|
|
|
var dA1 = pn - (dA2 - 1) * 8;//pn % 8
|
|
|
|
|
|
var dA1Hex = "1".PadRight(dA1, '0').BinToHex();//对位信息 第几位 二进制有效位
|
|
|
|
|
|
var dA2Hex = dA2.DecToHex();
|
|
|
|
|
|
return new List<string>() { dA1Hex.PadLeft(2, '0'), dA2Hex.PadLeft(2, '0') };
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 数据单元标识
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="fn"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<string> GetDT(int fn)
|
|
|
|
|
|
{
|
|
|
|
|
|
var dT2 = (fn - 1) / 8;//从零开始 第几组
|
|
|
|
|
|
var dT1 = fn - dT2 * 8;
|
|
|
|
|
|
var dT1Hex = "1".PadRight(dT1, '0').BinToHex();//对位信息 第几位 二进制有效位
|
|
|
|
|
|
var dT2Hex = dT2.DecToHex();
|
|
|
|
|
|
return new List<string>() { dT1Hex.PadLeft(2, '0'), dT2Hex.PadLeft(2, '0') };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 时间标签
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="pFC">启动帧帧序号计数器PFC 1字节</param>
|
|
|
|
|
|
/// <param name="delayTime">允许发送传输延时时间 min 1字节</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<string> GetTp(string pFC = "00", int delayTime = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
var now = DateTime.Now; // 获取当前时间
|
|
|
|
|
|
var seconds = now.Second.ToString().PadLeft(2, '0'); // 获取当前秒数
|
|
|
|
|
|
var minutes = now.Minute.ToString().PadLeft(2, '0'); // 获取当前分钟数
|
|
|
|
|
|
var hours = now.Hour.ToString().PadLeft(2, '0'); // 获取当前小时数
|
|
|
|
|
|
var day = now.Day.ToString().PadLeft(2, '0'); // 获取当前日期的日数
|
|
|
|
|
|
return new List<string>() { pFC, seconds, minutes, hours, day, delayTime.ToString().PadLeft(2, '0') };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2024-09-30 17:10:43 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|