2024-11-08 12:04:28 +08:00

1024 lines
46 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using JiShe.CollectBus.Common.Enums;
using JiShe.CollectBus.Common.Extensions;
using JiShe.CollectBus.Common.Models;
using JiShe.CollectBus.Protocol.Contracts.Interfaces;
using JiShe.CollectBus.Protocol.Contracts.Models;
using Microsoft.Extensions.Logging;
using System.Net.Sockets;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace JiShe.CollectBus.Protocol.Contracts.Abstracts
{
public abstract class BaseProtocolPlugin(ILogger<BaseProtocolPlugin> logger) : IProtocolPlugin
{
//起始字符
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;
public const string errorData = "EE";
public abstract Task<ProtocolInfo> GetAsync();
public abstract Task AnalyzeAsync(MessageReceivedEvent messageReceivedEvent, Action<byte[]>? sendAction = null);
/// <summary>
/// 登录帧解析
/// </summary>
/// <param name="messageReceivedEvent">报文</param>
/// <param name="sendAction">发送委托</param>
/// <returns></returns>
public virtual async Task LoginAsync(MessageReceivedLoginEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
{
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 = BuildSendCommandBytes(reqParam);
if (sendAction != null)
{
sendAction(bytes);
}
}
/// <summary>
/// 心跳帧解析
/// </summary>
/// <param name="messageReceivedEvent">报文</param>
/// <param name="sendAction">发送委托</param>
/// <returns></returns>
public virtual async Task HeartbeatAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
{
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 = BuildSendCommandBytes(reqParam);
if (sendAction != null)
{
sendAction(bytes);
}
}
}
/// <summary>
/// 解析确认或否认数据
/// </summary>
/// <param name=""></param>
/// <returns></returns>
public virtual async Task AnalyzeAnswerDataAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
{
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>
public virtual async Task AnalyzeAmmeterParameterReadingDataAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
{
var hexDatas = GetHexDatas(messageReceivedEvent.MessageHexString);
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>
public virtual async Task AnalyzeActivePowerIndicationReadingDataAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
{
var hexDatas = GetHexDatas(messageReceivedEvent.MessageHexString);
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>
public virtual async Task AnalyzeDailyFrozenReadingDataAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
{
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();
var datas = new List<decimal>();
//附录A.14 kWh 5字节
for (int i = 0; i < rateNumberHex; i++)
{
var skipCount = 9 + i * 5;
var dataHexs = hexDatas.Skip(skipCount).Take(5).ToList();
var data = AnalyzeDataAccordingToA14(dataHexs[0], dataHexs[1], dataHexs[2], dataHexs[3], dataHexs[4]);
datas.Add(data);
}
}
//接收<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 AnalyzeF25ReadingDataAsync(MessageReceivedHeartbeatEvent messageReceivedEvent, Action<byte[]>? sendAction = null)
{
var hexDatas = GetHexDatas(messageReceivedEvent.MessageHexString);
//A.15 分时日月年
var readingTimeHex = hexDatas.Take(5).ToList();
var readingTime = AnalyzeDataAccordingToA15(readingTimeHex[0], readingTimeHex[1], readingTimeHex[2], readingTimeHex[3], readingTimeHex[4]);
//A.9 kW
var crntTotalActivePowerHexs = hexDatas.Skip((int)F25DataItemEnum.CrntTotalActivePower).Take(3).ToList();
var crntTotalActivePower = AnalyzeDataAccordingToA09(crntTotalActivePowerHexs[0], crntTotalActivePowerHexs[1], crntTotalActivePowerHexs[2]);
var crntActivePowerOfAHexs = hexDatas.Skip((int)F25DataItemEnum.CrntActivePowerOfA).Take(3).ToList();
var crntActivePowerOfA = AnalyzeDataAccordingToA09(crntActivePowerOfAHexs[0], crntActivePowerOfAHexs[1], crntActivePowerOfAHexs[2]);
var crntActivePowerOfBHexs = hexDatas.Skip((int)F25DataItemEnum.CrntActivePowerOfB).Take(3).ToList();
var crntActivePowerOfB = AnalyzeDataAccordingToA09(crntActivePowerOfBHexs[0], crntActivePowerOfBHexs[1], crntActivePowerOfBHexs[2]);
var crntActivePowerOfCHexs = hexDatas.Skip((int)F25DataItemEnum.CrntActivePowerOfC).Take(3).ToList();
var crntActivePowerOfC = AnalyzeDataAccordingToA09(crntActivePowerOfCHexs[0], crntActivePowerOfCHexs[1], crntActivePowerOfCHexs[2]);
var crntTotalReactivePowerHexs = hexDatas.Skip((int)F25DataItemEnum.CrntTotalReactivePower).Take(3).ToList();
var crntTotalReactivePower = AnalyzeDataAccordingToA09(crntTotalReactivePowerHexs[0], crntTotalReactivePowerHexs[1], crntTotalReactivePowerHexs[2]);
var crntReactivePowerOfAHexs = hexDatas.Skip((int)F25DataItemEnum.CrntReactivePowerOfA).Take(3).ToList();
var crntReactivePowerOfA = AnalyzeDataAccordingToA09(crntReactivePowerOfAHexs[0], crntReactivePowerOfAHexs[1], crntReactivePowerOfAHexs[2]);
var crntReactivePowerOfBHexs = hexDatas.Skip((int)F25DataItemEnum.CrntReactivePowerOfB).Take(3).ToList();
var crntReactivePowerOfB = AnalyzeDataAccordingToA09(crntReactivePowerOfBHexs[0], crntReactivePowerOfBHexs[1], crntReactivePowerOfBHexs[2]);
var crntReactivePowerOfCHexs = hexDatas.Skip((int)F25DataItemEnum.CrntReactivePowerOfC).Take(2).ToList();
var crntReactivePowerOfC = AnalyzeDataAccordingToA09(crntReactivePowerOfCHexs[0], crntReactivePowerOfCHexs[1], crntReactivePowerOfCHexs[2]);
//A.5 %
var crntTotalPowerFactorHexs = hexDatas.Skip((int)F25DataItemEnum.CrntTotalPowerFactor).Take(2).ToList();
var crntTotalPowerFactor = AnalyzeDataAccordingToA05(crntTotalPowerFactorHexs[0], crntTotalPowerFactorHexs[1]);
var crntPowerFactorOfAHexs = hexDatas.Skip((int)F25DataItemEnum.CrntPowerFactorOfA).Take(2).ToList();
var crntPowerFactorOfA = AnalyzeDataAccordingToA05(crntPowerFactorOfAHexs[0], crntPowerFactorOfAHexs[1]);
var crntPowerFactorOfBHexs = hexDatas.Skip((int)F25DataItemEnum.CrntPowerFactorOfB).Take(2).ToList();
var crntPowerFactorOfB = AnalyzeDataAccordingToA05(crntPowerFactorOfBHexs[0], crntPowerFactorOfBHexs[1]);
var crntPowerFactorOfCHexs = hexDatas.Skip((int)F25DataItemEnum.CrntPowerFactorOfC).Take(2).ToList();
var crntPowerFactorOfC = AnalyzeDataAccordingToA05(crntPowerFactorOfCHexs[0], crntPowerFactorOfCHexs[1]);
//A.7 V
var crntVoltageOfAHexs = hexDatas.Skip((int)F25DataItemEnum.CrntVoltageOfA).Take(2).ToList();
var crntVoltageOfA = AnalyzeDataAccordingToA07(crntVoltageOfAHexs[0], crntVoltageOfAHexs[1]);
var crntVoltageOfBHexs = hexDatas.Skip((int)F25DataItemEnum.CrntVoltageOfB).Take(2).ToList();
var crntVoltageOfB = AnalyzeDataAccordingToA07(crntVoltageOfBHexs[0], crntVoltageOfBHexs[1]);
var crntVoltageOfCHexs = hexDatas.Skip((int)F25DataItemEnum.CrntVoltageOfC).Take(2).ToList();
var crntVoltageOfC = AnalyzeDataAccordingToA07(crntVoltageOfCHexs[0], crntVoltageOfCHexs[1]);
//A.25 A
var crntCurrentOfAHexs = hexDatas.Skip((int)F25DataItemEnum.CrntCurrentOfA).Take(3).ToList();
var crntCurrentOfA = AnalyzeDataAccordingToA25(crntCurrentOfAHexs[0], crntCurrentOfAHexs[1], crntCurrentOfAHexs[2]);
var crntCurrentOfBHexs = hexDatas.Skip((int)F25DataItemEnum.CrntCurrentOfB).Take(3).ToList();
var crntCurrentOfB = AnalyzeDataAccordingToA25(crntCurrentOfBHexs[0], crntCurrentOfBHexs[1], crntCurrentOfBHexs[2]);
var crntCurrentOfCHexs = hexDatas.Skip((int)F25DataItemEnum.CrntCurrentOfC).Take(3).ToList();
var crntCurrentOfC = AnalyzeDataAccordingToA25(crntCurrentOfCHexs[0], crntCurrentOfCHexs[1], crntCurrentOfCHexs[2]);
var crntZeroSequenceCurrentHexs = hexDatas.Skip((int)F25DataItemEnum.CrntZeroSequenceCurrent).Take(3).ToList();
var crntZeroSequenceCurrent = AnalyzeDataAccordingToA25(crntZeroSequenceCurrentHexs[0], crntZeroSequenceCurrentHexs[1], crntZeroSequenceCurrentHexs[2]);
//A.9 kVA
var crntTotalApparentPowerHexs = hexDatas.Skip((int)F25DataItemEnum.CrntTotalApparentPower).Take(3).ToList();
var crntTotalApparentPower = AnalyzeDataAccordingToA09(crntTotalApparentPowerHexs[0], crntTotalApparentPowerHexs[1], crntTotalApparentPowerHexs[2]);
var crntApparentPowerOfAHexs = hexDatas.Skip((int)F25DataItemEnum.CrntApparentPowerOfA).Take(3).ToList();
var crntApparentPowerOfA = AnalyzeDataAccordingToA09(crntApparentPowerOfAHexs[0], crntApparentPowerOfAHexs[1], crntApparentPowerOfAHexs[2]);
var crntApparentPowerOfBHexs = hexDatas.Skip((int)F25DataItemEnum.CrntApparentPowerOfB).Take(3).ToList();
var crntApparentPowerOfB = AnalyzeDataAccordingToA09(crntApparentPowerOfBHexs[0], crntApparentPowerOfBHexs[1], crntApparentPowerOfBHexs[2]);
var crntApparentPowerOfCHexs = hexDatas.Skip((int)F25DataItemEnum.CrntApparentPowerOfC).Take(3).ToList();
var crntApparentPowerOfC = AnalyzeDataAccordingToA09(crntApparentPowerOfCHexs[0], crntApparentPowerOfCHexs[1], crntApparentPowerOfCHexs[2]);
}
/// <summary>
/// 解析透明转发 应答结果
/// </summary>
/// <param name="messageReceivedEvent"></param>
/// <param name="sendAction"></param>
/// <returns></returns>
public virtual async Task AnalyzeTransparentForwardingAnswerResultAsync(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;
}
/// <summary>
/// 获取命令数据单元-数据体
/// </summary>
/// <param name="messageHexString"></param>
/// <returns></returns>
public static List<string> GetHexDatas(string messageHexString)
{
var hexStringList = messageHexString.StringToPairs();
var hexDatas = (List<string>)hexStringList.GetAnalyzeValue(CommandChunkEnum.Data);
return hexDatas;
}
/// <summary>
/// 解析时间标签
/// </summary>
/// <param name="hexDatas"></param>
public 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
}
#region
/// <summary>
/// 根据A05解析数据
/// </summary>
/// <param name="singleDigitNumberAndDeciles">个位和十分位</param>
/// <param name="hundredDigitNumbersAndTenDigitNumber">百位和十位</param>
/// <returns></returns>
public decimal AnalyzeDataAccordingToA05(string singleDigitNumberAndDeciles, string hundredDigitNumbersAndTenDigitNumber)
{
if (singleDigitNumberAndDeciles.IsErrorData() && hundredDigitNumbersAndTenDigitNumber.IsErrorData())
{
return 0;
}
var bin1 = hundredDigitNumbersAndTenDigitNumber.HexToBin().PadLeft(8, '0');
var hundredDigitNumbers = bin1.Substring(1, 3).BinToDec();//百位
var tenDigitNumber = bin1.Substring(4).BinToDec();//十位
var bin2 = singleDigitNumberAndDeciles.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;
}
/// <summary>
/// 数据格式07
/// </summary>
/// <param name="singleDigitNumberAndDeciles">个位、十分位</param>
/// <param name="hundredDigitNumbersAndTenDigitNumber">百位、十位</param>
/// <returns></returns>
public decimal AnalyzeDataAccordingToA07(string singleDigitNumberAndDeciles, string hundredDigitNumbersAndTenDigitNumber)
{
if (singleDigitNumberAndDeciles.IsErrorData() && hundredDigitNumbersAndTenDigitNumber.IsErrorData())
{
return 0;
}
var bin1 = hundredDigitNumbersAndTenDigitNumber.HexToBin().PadLeft(8, '0');
var hundredDigitNumbers = bin1.Substring(1, 3).BinToDec();//百位
var tenDigitNumber = bin1.Substring(4).BinToDec();//十位
var bin2 = singleDigitNumberAndDeciles.HexToBin().PadLeft(8, '0');
var singleDigitNumber = bin2.Substring(0, 4).BinToDec();//个位
var deciles = bin2.Substring(4).BinToDec();//十分位
var value = decimal.Parse($"{hundredDigitNumbers}{tenDigitNumber}{singleDigitNumber}.{deciles}");
return value;
}
/// <summary>
/// 数据格式09
/// </summary>
/// <param name="thousandthPercentileAndTenThousandPositions">千分位和万分位</param>
/// <param name="decilesAndPercentile">十分位和百分位</param>
/// <param name="tenAndSingleDigit">十位和个位</param>
/// <returns></returns>
public decimal AnalyzeDataAccordingToA09(string thousandthPercentileAndTenThousandPositions, string decilesAndPercentile, string tenAndSingleDigit)
{
if (thousandthPercentileAndTenThousandPositions.IsErrorData() && decilesAndPercentile.IsErrorData() && tenAndSingleDigit.IsErrorData())
{
return 0;
}
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}.{decilesAndPercentile}{thousandthPercentileAndTenThousandPositions}");
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
/// </summary>
/// <param name="tenDigitAndSingleDigitNumber">十位 个位</param>
/// <param name="thousandAndHundredsOfPosition">千位 百位</param>
/// <param name="hundredThousandAndTenThousandOfPosition">十万位 万位</param>
/// <param name="millionsAndMillionOfPosition">千万位 百万位</param>
/// <param name="hundredMillionAndBillionOfPosition">十亿位 亿位</param>
/// <param name="hundredBillionsAndBillionsOfPosition">千亿位 百亿位</param>
/// <returns></returns>
public string AnalyzeDataAccordingToA12(string tenDigitAndSingleDigitNumber, string thousandAndHundredsOfPosition, string hundredThousandAndTenThousandOfPosition,
string millionsAndMillionOfPosition, string hundredMillionAndBillionOfPosition, string hundredBillionsAndBillionsOfPosition)
{
var value = $"{hundredBillionsAndBillionsOfPosition}{hundredMillionAndBillionOfPosition}{millionsAndMillionOfPosition}" +
$"{hundredThousandAndTenThousandOfPosition}{thousandAndHundredsOfPosition}{tenDigitAndSingleDigitNumber}";
return value;
}
/// <summary>
/// 数据格式14
/// </summary>
/// <param name="tenThousandPositionsAndThousandthPercentile">千分,万分</param>
/// <param name="decilesAndPercentile">十分、百分</param>
/// <param name="tenDigitNumberAndSingleDigitNumber">十位、个位</param>
/// <param name="thousandAndHundredsOfPosition">千位、百位</param>
/// <param name="hundredThousandAndTenThousandOfPosition">十万位、万位</param>
/// <returns></returns>
public decimal AnalyzeDataAccordingToA14(string tenThousandPositionsAndThousandthPercentile, string decilesAndPercentile,
string tenDigitNumberAndSingleDigitNumber, string thousandAndHundredsOfPosition, string hundredThousandAndTenThousandOfPosition)
{
if (tenThousandPositionsAndThousandthPercentile.IsErrorData() && decilesAndPercentile.IsErrorData() && tenDigitNumberAndSingleDigitNumber.IsErrorData()
&& thousandAndHundredsOfPosition.IsErrorData() && hundredThousandAndTenThousandOfPosition.IsErrorData())
return 0;
var value = decimal.Parse($"{hundredThousandAndTenThousandOfPosition}{thousandAndHundredsOfPosition}" +
$"{tenDigitNumberAndSingleDigitNumber}.{decilesAndPercentile}{tenThousandPositionsAndThousandthPercentile}");
return value;
}
/// <summary>
/// 数据格式A.15
/// </summary>
/// <param name="minuteHex"></param>
/// <param name="hourHex"></param>
/// <param name="dayHex"></param>
/// <param name="monthHex"></param>
/// <param name="yearHex"></param>
/// <returns></returns>
public DateTime AnalyzeDataAccordingToA15(string minuteHex, string hourHex, string dayHex, string monthHex, string yearHex)
{
var centuryString = (DateTime.Now.Year / 100).ToString();
var time = DateTime.Parse($"{centuryString}{yearHex}-{monthHex}-{dayHex} {hourHex}:{minuteHex}: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();//个位
}
/// <summary>
/// 数据格式20
/// </summary>
/// <param name="dayHex"></param>
/// <param name="monthHex"></param>
/// <param name="yearHex"></param>
/// <returns></returns>
public DateTime AnalyzeDataAccordingToA20(string dayHex, string monthHex, string yearHex)
{
var centuryString = (DateTime.Now.Year / 100).ToString();
var time = DateTime.Parse($"{centuryString}{yearHex}-{monthHex}-{dayHex}");
return time;
}
/// <summary>
/// 数据格式25
/// </summary>
/// <param name="percentileAndThousandthPercentile">百分、千分位</param>
/// <param name="singleDigitNumberAndDeciles">个位、十分位</param>
/// <param name="hundredDigitNumbersAndTenDigitNumber">百位、十位</param>
/// <returns></returns>
public decimal AnalyzeDataAccordingToA25(string percentileAndThousandthPercentile, string singleDigitNumberAndDeciles, string hundredDigitNumbersAndTenDigitNumber)
{
if (percentileAndThousandthPercentile.IsErrorData() && singleDigitNumberAndDeciles.IsErrorData() && hundredDigitNumbersAndTenDigitNumber.IsErrorData())
return 0;
var bin1 = hundredDigitNumbersAndTenDigitNumber.HexToBin().PadLeft(8, '0');
var hundredDigitNumbers = bin1.Substring(1, 3).BinToDec();//百位
var tenDigitNumber = bin1.Substring(4).BinToDec();//十位
var bin2 = singleDigitNumberAndDeciles.HexToBin().PadLeft(8, '0');
var singleDigitNumber = bin2.Substring(0, 4).BinToDec();//个位
var deciles = bin1.Substring(4).BinToDec();//十分位
var value = decimal.Parse($"{hundredDigitNumbers}{tenDigitNumber}{singleDigitNumber}.{deciles}{percentileAndThousandthPercentile}");
return value;
}
#endregion
#region
/// <summary>
/// 构建电表参数设置-下发命令
/// </summary>
/// <param name="reqParameter"></param>
/// <param name="meterParameters"></param>
/// <returns></returns>
public byte[] BuildAmmeterParameterSetSendCmd(ReqParameter reqParameter, List<AmmeterParameter> meterParameters)
{
var dataUnit = BuildAmmeterParameterSendDataUnit(meterParameters);
var bytes = BuildSendCommandBytes(reqParameter, dataUnit);
return bytes;
}
/// <summary>
/// 构建电表参数读取-下发命令
/// </summary>
/// <param name="reqParameter"></param>
/// <param name="meterNumberList">对象序号</param>
public void BuildAmmeterParameterReadingSendCmd(ReqParameter reqParameter, List<int> meterNumberList)
{
var dataUnit = new List<string>();
var countHex = meterNumberList.Count().DecToHex().PadLeft(4, '0');
var countHexPairs = countHex.StringToPairs();
countHexPairs.Reverse();
dataUnit.AddRange(countHexPairs);
foreach (var number in meterNumberList)
{
var numberHex = number.DecToHex().PadLeft(4, '0');
var numberHexPairs = numberHex.StringToPairs();
numberHexPairs.Reverse();
dataUnit.AddRange(numberHexPairs);
}
var bytes = BuildSendCommandBytes(reqParameter, dataUnit);
}
/// <summary>
/// 构建电表参数设置-下发数据单元
/// </summary>
/// <param name="meterParameters"></param>
/// <returns></returns>
public List<string> BuildAmmeterParameterSendDataUnit(List<AmmeterParameter> meterParameters)
{
var hexDatas = new List<string>();
var countHex = meterParameters.Count().DecToHex().PadLeft(4, '0');
hexDatas.Add(countHex);
//TODO 优化代码:目标数据入参,返回类型为出参
for (int i = 0; i <= meterParameters.Count - 1; i++)
{
var meter = meterParameters[i];
var indexHex = (i + 1).DecToHex().PadLeft(4, '0');
hexDatas.Add(indexHex);
var pnHex = meter.Pn.DecToHex().PadLeft(4, '0');
hexDatas.Add(pnHex);
var baudRateBin = meter.BaudRate.DecToBin().PadLeft(3, '0');
var portBin = meter.Port.DecToBin().PadLeft(5, '0');
var baudRateAndPortHex = $"{baudRateBin}{portBin}".BinToHex().PadLeft(2, '0');
hexDatas.Add(baudRateAndPortHex);
var protocolTypeHex = ((int)meter.ProtocolType).DecToHex().PadLeft(2, '0');
hexDatas.Add(protocolTypeHex);
hexDatas.Add(meter.Address);
hexDatas.Add(meter.Password.PadLeft(12, '0'));
var rateNumberBin = $"0000{meter.RateNumber.DecToBin().PadLeft(4, '0')}";
var rateNumberHex = rateNumberBin.BinToHex().PadLeft(2, '0');
hexDatas.Add(rateNumberHex);
var intBitNumberBin = (meter.IntegerBitNumber - 4).DecToBin().PadLeft(2, '0');
var decBitNumberBin = (meter.DecimalBitNumber - 1).DecToBin().PadLeft(2, '0');
var intAndDecBitNumberBin = $"0000{intBitNumberBin}{decBitNumberBin}";
var intAndDecBitNumberHex = intAndDecBitNumberBin.BinToHex().PadLeft(2, '0');
hexDatas.Add(intAndDecBitNumberHex);
hexDatas.Add(meter.CollectorAddress.PadLeft(12, '0'));
var userCategoryNumberBin = meter.UserCategoryNumber.DecToBin().PadLeft(4, '0');
var userSubclassNumberBin = meter.UserSubclassNumber.DecToBin().PadLeft(4, '0');
var userNumberHex = $"{userCategoryNumberBin}{userSubclassNumberBin}".BinToHex().PadLeft(2, '0');
hexDatas.Add(userNumberHex);
}
//高位在前,低位在后
var datas = new List<string>();
foreach (var hexData in hexDatas)
{
if (hexData.Length == 2)
datas.Add(hexData);
else
{
var lst = hexData.StringToPairs();
lst.Reverse();
datas.AddRange(lst);
}
}
datas.AddRange(GetPW());
return datas;
}
/// <summary>
/// 构建透明转发-下发数据单元
/// </summary>
/// <param name="port">终端通信端口 1~31</param>
/// <param name="baudRate">0~7 对应300,600,1200,2400,4800,7200,9600,19200</param>
/// <param name="stopBit"></param>
/// <param name="parity"></param>
/// <param name="dataBit"></param>
/// <returns></returns>
public List<string> BuildTransparentForwardingSendDataUnit(int port, BaudRate baudRate, StopBit stopBit, Parity parity, DataBit dataBit,
int waitContentTimeout, int waitByteTimeout, List<string> datas)
{
var dataUnit = new List<string>();
var portHex = port.DecToHex().PadLeft(2, '0');
dataUnit.Add(portHex);
var baudRateBin = ((int)baudRate).DecToBin().PadLeft(3, '0');
var stopBitBin = ((int)stopBit).DecToBin();
var parityBin = parity != Parity.None ? $"1{((int)parity).DecToBin()}" : $"0{((int)parity).DecToBin()}";
var dataBitBin = ((int)dataBit).DecToBin().PadLeft(2, '0');
var controlHex = $"{baudRateBin}{stopBitBin}{parityBin}{dataBitBin}".BinToHex().PadLeft(2, '0'); ;
dataUnit.Add(controlHex);
var waitContentTimeoutBin = $"1{waitContentTimeout.DecToBin().PadLeft(7, '0')}";
var waitContentTimeoutHex = waitContentTimeoutBin.BinToHex().PadLeft(2, '0');
var waitByteTimeoutHex = waitByteTimeout.DecToHex().PadLeft(2, '0');
dataUnit.Add(waitContentTimeoutHex);
dataUnit.Add(waitByteTimeoutHex);
var countHex = datas.Count.DecToHex().PadLeft(4, '0');
var countHexPairs = countHex.StringToPairs();
countHexPairs.Reverse();
dataUnit.AddRange(countHexPairs);
dataUnit.AddRange(datas);
return dataUnit;
}
//AUX=消息认证码字段PW,16个字节
public List<string> GetPW()
{
var str = "00";
var pWList = Enumerable.Repeat(str, pWLen).ToList();
return pWList;
}
/// <summary>
/// 构建下发命令
/// </summary>
/// <param name="reqParameter"></param>
/// <param name="dataUnit"></param>
/// <returns></returns>
public byte[] BuildSendCommandBytes(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);
logger.LogInformation($"回复:{string.Join(" ", cmdStrList)}");
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
}
}