2024-12-19 16:07:07 +08:00

1641 lines
64 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 System.ComponentModel.DataAnnotations;
using JiShe.CollectBus.Common.Enums;
using JiShe.CollectBus.Common.Models;
using System.Reflection;
namespace JiShe.CollectBus.Common.Extensions
{
public static class HexStringExtensions
{
//起始字符
private const string startStr = "68";
//结束字符
private const string endStr = "16";
//头部字节长度
private const int hearderLen = 6;
//消息认证码字段长度
private const int pWLen = 16;
private const int tPLen = 6;
private const int FixedLength = 18;
static object locker = new object();
static List<int> MSA = new List<int>();
static Dictionary<string, List<int>> usingMSA = new Dictionary<string, List<int>>();
static HexStringExtensions()
{
for (int i = 1; i <= 127; i++)
{
MSA.Add(i);
}
}
/// <summary>
/// Gets the msa.
/// </summary>
/// <param name="mark">集中器地址</param>
/// <returns></returns>
public static int GetMSA(string mark)
{
lock (locker)
{
if (!usingMSA.Keys.Contains(mark))
usingMSA.Add(mark, new List<int>());
int msa = MSA.Except(usingMSA[mark]).FirstOrDefault();
//if (msa == 1) msa = 2;//msa=1为自定义指令保留
usingMSA[mark].Add(msa);
if (msa == 127)
usingMSA[mark].RemoveAll(m => true);
return msa;
}
}
public static object GetAnalyzeValue(this List<string> hexStringList, CommandChunkEnum chunk)
{
if (hexStringList.Count < hearderLen || hexStringList[0] != startStr || hexStringList[5] != startStr || hexStringList.Count < FixedLength)
{
return null;
}
switch (chunk)
{
case CommandChunkEnum.AFN:
var aFn = hexStringList[(int)CommandChunkEnum.AFN].HexToDec();//1字节
return aFn;
case CommandChunkEnum.FN:
//(DT2*8)+DT1=fn
var dt1Bin = hexStringList[(int)CommandChunkEnum.FN - 1].HexToBin();
var dt1 = dt1Bin != "0" ? dt1Bin.Length : 0;
var dt2 = hexStringList[(int)CommandChunkEnum.FN].HexToDec();
var fn = dt2 * 8 + dt1;
return fn;
case CommandChunkEnum.A:
var aHexList = hexStringList.Skip((int)CommandChunkEnum.A).Take(5).ToList();
var a1 = aHexList[1] + aHexList[0];
var a2 = aHexList[3] + aHexList[2];
var a2Dec = a2.HexToDec();
var a3 = aHexList[4];
var a = $"{a1}{a2Dec.ToString().PadLeft(5, '0')}";
var a3Bin = aHexList[4].HexToBin().PadLeft(8, '0');
var msa = a3Bin.Substring(0, 7).BinToDec();
return new Tuple<string,int>(a, msa);
case CommandChunkEnum.SEQ:
var seq = hexStringList[(int)CommandChunkEnum.SEQ].HexToBin().PadLeft(8, '0');
var tpV = (TpV)Convert.ToInt32(seq.Substring(0, 1));
var firfin = (FIRFIN)Convert.ToInt32(seq.Substring(1, 2));
var con = (CON)Convert.ToInt32(seq.Substring(3, 1));
var prseqBin = seq.Substring(4, 4);
return new Seq
{
CON = con,
FIRFIN = firfin,
PRSEQ = prseqBin.BinToDec(),
TpV = tpV
};
case CommandChunkEnum.Data:
var lenIndex = (int)CommandChunkEnum.Len;
var lenBin = (hexStringList[lenIndex + 1]+hexStringList[lenIndex]).HexToBin();
var len = lenBin.Remove(lenBin.Length - 2).BinToDec();
//验证长度 2=(帧校验和+结束字符)
if (hexStringList.Count - 2 != hearderLen + len)
return null;
var dataHexList = hexStringList.Skip(FixedLength).Take(len + hearderLen - FixedLength).ToList();
return dataHexList;
default:
throw new ArgumentOutOfRangeException(nameof(chunk), chunk, null);
}
}
public static bool IsStartStr(this string str)
{
return str == startStr ? true : false;
}
/// <summary>
/// 字节加33
/// </summary>
/// <param name="hexStringList"></param>
/// <returns></returns>
public static List<string> AddHex33(this List<string> hexStringList)
{
for (int i = 0; i < hexStringList.Count; i++)
{
hexStringList[i] = (Convert.ToInt32(hexStringList[i], 16) + Convert.ToInt32("33", 16)).ToString("X2");
if (hexStringList[i].Length > 2)
{
hexStringList[i] = hexStringList[i].Substring(hexStringList[i].Length - 2);
}
}
return hexStringList;
}
#region 376.1
/// <summary>
/// 构建电表参数设置-下发命令
/// </summary>
/// <param name="address">集中器地址</param>
/// <param name="meterParameters"></param>
/// <returns></returns>
public static byte[] BuildAmmeterParameterSetSendCmd(string address, List<AmmeterParameter> meterParameters)
{
var dataUnit = BuildAmmeterParameterSendDataUnit(meterParameters);
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.1,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 10,
},
MSA = GetMSA(address),
Pn = 0,
Fn = 10
};
var bytes = BuildSendCommandBytes(reqParameter, dataUnit);
return bytes;
}
/// <summary>
/// 构建电表参数读取-下发命令
/// </summary>
/// <param name="address">集中器地址</param>
/// <param name="meterNumberList">对象序号</param>
public static void BuildAmmeterParameterReadingSendCmd(string address, 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 reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.2,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 0,
},
MSA = GetMSA(address),
Pn = 0,
Fn = 10
};
var bytes = BuildSendCommandBytes(reqParameter, dataUnit);
}
/// <summary>
/// 构建电表抄读一类数据-下发命令
/// </summary>
/// <param name="address"></param>
/// <param name="pn"></param>
/// <param name="aTypeOfDataItems">一类数据项</param>
public static void BuildAmmeterReadRealTimeDataSendCmd(string address, int pn, ATypeOfDataItems aTypeOfDataItems)
{
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.2,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 2,
},
MSA = GetMSA(address),
Pn = pn,
Fn = (int)aTypeOfDataItems
};
var bytes = HexStringExtensions.BuildSendCommandBytes(reqParameter);
}
/// <summary>
/// 构建电表抄读日冻结正向有功电能示值-下发命令
/// </summary>
/// <param name="address"></param>
/// <param name="pn"></param>
/// <param name="time"></param>
public static void BuildAmmeterReadDailyFreezingDataSendCmd(string address, int pn, DateTime time)
{
var dataUnit = time.ToString("yy-MM-dd").Split('-').ToList();
dataUnit.Reverse();
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.2,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 2,
},
MSA = GetMSA(address),
Pn = pn,
Fn = 161
};
var bytes = HexStringExtensions.BuildSendCommandBytes(reqParameter, dataUnit);
}
/// <summary>
/// 构建电表抄读实时电流-下发命令
/// </summary>
/// <param name="address"></param>
/// <param name="pn"></param>
public static void BuildAmmeterRealTimeCurrentDataSendCmd(string address, int pn)
{
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.2,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 2,
},
MSA = GetMSA(address),
Pn = pn,
Fn = 25
};
var bytes = HexStringExtensions.BuildSendCommandBytes(reqParameter);
}
/// <summary>
/// 构建电表读取二类数据-下发命令
/// </summary>
/// <param name="address"></param>
/// <param name="pn"></param>
/// <param name="iIDataTypeItems">fn</param>
/// <param name="density">冻结密度</param>
/// <param name="points">冻结点数</param>
public static void BuildAmmeterReadingIIdataTypeItemsSendCmd(string address, int pn, IIdataTypeItems iIDataTypeItems, FreezeDensity density,int points)
{
var queryDate = DateTime.Today.AddDays(-1);
var dataUnit = queryDate.ToString("yyMMddHHmm").StringToPairs();
dataUnit.Reverse();
//冻结密度
dataUnit.Add(((int)density).DecToHex());
dataUnit.Add(points.DecToHex());
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.2,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 2,
},
MSA = GetMSA(address),
Pn = pn,
Fn = (int)iIDataTypeItems
};
var bytes = HexStringExtensions.BuildSendCommandBytes(reqParameter, dataUnit);
}
/// <summary>
/// 构建电表读取二类数据-下发命令
/// </summary>
/// <param name="address"></param>
/// <param name="pn"></param>
/// <param name="iIDataTypeItems">fn</param>
/// <param name="tdType">数据时标</param>
public static void BuildAmmeterReadingBaseDataSendCmd(string address, int pn, IIdataTypeItems iIDataTypeItems,TdType tdType)
{
var queryDate = DateTime.Today.AddDays(-1);
List<string>? dataUnit = null;
switch (tdType)
{
case TdType.Td_d:
dataUnit = queryDate.ToString("yyMMdd").StringToPairs();
break;
case TdType.Td_m:
dataUnit = queryDate.ToString("yyMM").StringToPairs();
break;
}
if(dataUnit == null) return;
dataUnit.Reverse();
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.2,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 2,
},
MSA = GetMSA(address),
Pn = pn,
Fn = (int)iIDataTypeItems
};
var bytes = HexStringExtensions.BuildSendCommandBytes(reqParameter, dataUnit);
}
/// <summary>
/// 组装有/无功电量
/// </summary>
/// <param name="address"></param>
/// <param name="pn"></param>
/// <param name="cmdType"></param>
public static void BuildAmmeterReadingElectricityDataSendCmd(string address, int pn,int cmdType)
{
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.2,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 2,
},
MSA = GetMSA(address),
Pn = pn,
Fn = cmdType
};
var bytes = HexStringExtensions.BuildSendCommandBytes(reqParameter);
}
/// <summary>
/// 构建超功率设置-下发命令
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="modelCode"></param>
/// <param name="port"></param>
/// <param name="baudRate"></param>
/// <param name="power"></param>
public static void BuildAmmeterSuperpowerSettingSendCmd(string address, string password,string modelCode, int port, BaudRate baudRate, decimal power)
{
if (power > 0)
{
WsOnSupperPower(address, password, modelCode, true, port, baudRate);
//TODO:连续发几条嘛?
string dataMark;
//如果是多回路电表,地址只取前面部分 例如:564880000001_01 取564880000001
if (address.IndexOf('_') > 0)
{
var addressSplit = address.Split('_');
address = addressSplit[0];
var loop = Convert.ToInt32(addressSplit[1]);
if (loop == 1) { dataMark = "0E300101"; }
else if (loop == 2) { dataMark = "0E300201"; }
else
return;
}
else
{
dataMark = "0E300101";
}
var turnData = AssembleWsSupperPower(address, password, dataMark, power);
if (turnData != null)
{
var bytes = BuildTransparentForwardingSendCmd(address, port, baudRate, turnData);
}
}
else
{
WsOnSupperPower(address, password, modelCode, false, port, baudRate);
}
}
/// <summary>
/// 构建恶性负载绝对功率设置-下发命令
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="port"></param>
/// <param name="baudRate"></param>
/// <param name="power"></param>
public static void BuildAmmeterAbsolutePowerSettingSendCmd(string address, string password, int port, BaudRate baudRate, decimal power)
{
if (power <= 0)
{
power = 99;//等于小0 代表关闭
}
string dataMark;
//如果是多回路电表,地址只取前面部分 例如:564880000001_01 取564880000001
if (address.IndexOf('_') > 0)
{
var addressSplit = address.Split('_');
address = addressSplit[0];
var loop = Convert.ToInt32(addressSplit[1]);
if (loop == 1) { dataMark = "0E300103"; }
else if (loop == 2) { dataMark = "0E300203"; }
else
return;
}
else
{
dataMark = "0E300103";
}
var turnData = AssembleWsAbsolutePowerSetting(address, password, dataMark, power);
if (turnData != null)
{
var bytes = BuildTransparentForwardingSendCmd(address, port, baudRate, turnData);
}
}
/// <summary>
/// 构建恶性负载功率因数设置-下发命令
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="port"></param>
/// <param name="baudRate"></param>
/// <param name="power"></param>
public static void BuildAmmeterPowerFactorSettingSendCmd(string address, string password, int port, BaudRate baudRate, decimal power)
{
string dataMark;
//如果是多回路电表,地址只取前面部分 例如:564880000001_01 取564880000001
if (address.IndexOf('_') > 0)
{
var addressSplit = address.Split('_');
address = addressSplit[0];
var loop = Convert.ToInt32(addressSplit[1]);
if (loop == 1) { dataMark = "0E300104"; }
else if (loop == 2) { dataMark = "0E300204"; }
else
return;
}
else
{
dataMark = "0E300104";
}
var turnData = AssembleWsPowerFactor(address, password, dataMark, power);
if (turnData != null)
{
var bytes = BuildTransparentForwardingSendCmd(address, port, baudRate, turnData);
}
}
/// <summary>
/// 超功率开关
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="modelCode"></param>
/// <param name="port"></param>
/// <param name="baudRate"></param>
public static void WsOnSupperPower(string address,string password,string modelCode,bool isOn,int port,BaudRate baudRate)
{
List<string> modelCodes = new List<string>() { "DDS3102-S2", "DDS71", "DDZY71", "DTZY71", "DSZY71" };
//这些型号需进入软编程
if (modelCodes.Contains(modelCode))
{
var dataUnit1 = SoftProgram(address, modelCode, password);
BuildTransparentForwardingSendCmd(address, port, baudRate, dataUnit1);
}
var dataUnit2 = AssembleWsOnSupperPower(address, password, isOn);
BuildTransparentForwardingSendCmd(address, port, baudRate, dataUnit2);
}
/// <summary>
/// 构建水表参数设置-下发命令
/// </summary>
/// <param name="address">集中器地址</param>
/// <param name="meterParameters"></param>
/// <returns></returns>
public static byte[] BuildWaterMeterParameterSetSendCmd(string address, List<WaterMeterParameter> meterParameters)
{
var dataUnit = BuildWaterMeterParameterSendDataUnit(meterParameters);
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.1,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 10,
},
MSA = GetMSA(address),
Pn = 0,
Fn = 10
};
var bytes = BuildSendCommandBytes(reqParameter, dataUnit);
return bytes;
}
/// <summary>
/// 构建透明转发-下发命令
/// </summary>
/// <param name="address">集中器地址</param>
/// <param name="port">终端通信端口 1~31</param>
/// <param name="baudRate">0~7 对应300,600,1200,2400,4800,7200,9600,19200</param>
/// <param name="datas">转发内容</param>
/// <param name="stopBit">停止位</param>
/// <param name="parity">校验方式</param>
/// <param name="dataBit">数据位</param>
/// <param name="waitContentTimeout">等待报文超时时间/s</param>
/// <param name="waitByteTimeout">等待字节超时时间/ms</param>
public static byte[] BuildTransparentForwardingSendCmd(string address, int port, BaudRate baudRate, List<string> datas, StopBit stopBit = StopBit.Stop1, Parity parity = Parity.Even, DataBit dataBit = DataBit.D8,
int waitContentTimeout = 100, int waitByteTimeout = 100)
{
var dataUnit = BuildTransparentForwardingSendDataUnit(port, baudRate, datas, stopBit, parity, dataBit, waitContentTimeout, waitByteTimeout);
dataUnit.AddRange(GetPW());
var reqParameter = new ReqParameter2()
{
AFN = AFN.,
FunCode = (int)CMasterStationFunCode.2,
A = address,
Seq = new Seq()
{
TpV = TpV.,
FIRFIN = FIRFIN.,
CON = CON.,
PRSEQ = 0
},
MSA = GetMSA(address),
Pn = 0,
Fn = 1
};
var bytes = BuildSendCommandBytes(reqParameter, dataUnit);
return bytes;
}
[Obsolete]
public static List<string> AmmeterValveControl(string address, string specialnocode, string password, bool state, string modelCode = "")
{
address = address.Trim().TrimStart('0');
if (address.Length < 12) address = address.PadLeft(12, '0');
string Code = string.Empty;
if (state)
{
if (string.IsNullOrEmpty(specialnocode))
Code = "1B";
else
Code = specialnocode == "1B" || specialnocode == "1C" ? specialnocode : "1C";
}
else
Code = "1A";//跳闸
if (specialnocode == "1W")
{
if (state)
Code = "1A";
else
Code = "1C";
}
var pwdLevel = "02";
if (modelCode == "HL_DTSU2625" || modelCode == "DDZY9866")
pwdLevel = "04";
else if (modelCode == "DDS2705")
pwdLevel = "03";
if (!string.IsNullOrWhiteSpace(password) && password.Contains("|"))
{
var sp = password.Split('|');
pwdLevel = sp[1];
password = sp[0];
}
string strDate = DateTime.Now.AddYears(3).ToString("000012ddMMyy").StrAddSpan();
if (specialnocode == "1D" || modelCode == "SZBD_DDZY1225")
strDate = "FF FF FF FF FF FF";
string strP = password.StrAddSpan().StrReverseOrder();
string strSJY = " " + pwdLevel + " " + strP + " 01 00 00 00 " + Code + " 00 " + strDate;
string strLen = (strSJY.Replace(" ", "").Length / 2).ToString("X2");
string strReturn = "68 " + address.StrAddSpan().StrReverseOrder() + " 68 1C " + strLen + " " + strSJY.StrAddHex33() + " ";
string strSum = strReturn.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries).Select(i => Convert.ToInt32(i, 16)).Sum().ToString("X");
strReturn += strSum.Substring(strSum.Length - 2) + " 16";
return strReturn.Split(' ').ToList();
}
/// <summary>
/// 构建透明转发-下发数据单元
/// </summary>
/// <param name="port">终端通信端口 1~31</param>
/// <param name="baudRate">0~7 对应300,600,1200,2400,4800,7200,9600,19200</param>
/// <param name="datas">转发内容</param>
/// <param name="stopBit">停止位</param>
/// <param name="parity">校验方式</param>
/// <param name="dataBit">数据位</param>
/// <param name="waitContentTimeout">等待报文超时时间/s</param>
/// <param name="waitByteTimeout">等待字节超时时间/ms</param>
/// <returns></returns>
private static List<string> BuildTransparentForwardingSendDataUnit(int port, BaudRate baudRate, List<string> datas, StopBit stopBit = StopBit.Stop1, Parity parity = Parity.Even, DataBit dataBit = DataBit.D8,
int waitContentTimeout = 100, int waitByteTimeout = 100)
{
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()}" : "00";
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;
}
/// <summary>
/// 构建下发命令
/// </summary>
/// <param name="reqParameter"></param>
/// <param name="dataUnit"></param>
/// <returns></returns>
public static byte[] BuildSendCommandBytes(ReqParameter reqParameter, List<string>? dataUnit = null)
{
var cmdStrList = new List<string>();
var userDatas = BuildUserData(reqParameter, dataUnit);
var hearders = BuildHeaders(userDatas.Count);
var cs = GetCS(userDatas);
cmdStrList.AddRange(hearders);
cmdStrList.AddRange(userDatas);
cmdStrList.Add(cs);
cmdStrList.Add(endStr);
Console.WriteLine(string.Join(" ", cmdStrList));
var bytes = cmdStrList.Select(x => Convert.ToByte(x, 16)).ToArray();
return bytes;
}
/// <summary>
/// 构建电表参数设置-下发数据单元
/// </summary>
/// <param name="meterParameters"></param>
/// <returns></returns>
private static 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="meterParameters"></param>
/// <returns></returns>
private static List<string> BuildWaterMeterParameterSendDataUnit(List<WaterMeterParameter> meterParameters)
{
var hexDatas = new List<string>();
var countHex = meterParameters.Count().DecToHex().PadLeft(4, '0');
hexDatas.Add(countHex);
for (int i = 0; i <= meterParameters.Count - 1; i++)
{
var meter = meterParameters[i];
var protocolType = (int)meter.ProtocolType;
var currentProtocolType = protocolType;
if (protocolType == 43)
currentProtocolType = 1;
else if (protocolType > 32) currentProtocolType = 32;
//TODO:无线水表 小数位
if (currentProtocolType == 32 && meter.Address.Substring(0, 2) != "00")
{
var ejz = meter.Address.Substring(0, 2).HexToBin().PadLeft(8, '0');
var xs = ejz.Substring(6, 2).BinToDec();
var zs = ejz.Substring(0, 6).BinToDec();
//userData.Add(new QGDW3671UserData() { Name = "整数位无线水表", Value = zs });
//userData.Add(new QGDW3671UserData() { Name = "小数位无线水表", Value = xs });
}
var userCategoryNumber = meter.UserCategoryNumber;
var userSubclassNumber = meter.UserSubclassNumber;
if (protocolType == 32)
userCategoryNumber = 1;
else if (protocolType == 45)
{
userCategoryNumber = 1;
userSubclassNumber = 9;
}
else if (protocolType == 1)
userSubclassNumber = 1;
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 = currentProtocolType.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.DecToBin().PadLeft(2, '0');
var decBitNumberBin = meter.DecimalBitNumber.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 = userCategoryNumber.DecToBin().PadLeft(4, '0');
var userSubclassNumberBin = 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;
}
//AUX=消息认证码字段PW,16个字节
private static List<string> GetPW()
{
var str = "00";
var pWList = Enumerable.Repeat(str, pWLen).ToList();
return pWList;
}
/// <summary>
/// 帧校验和
/// </summary>
/// <param name="userData">用户数据区</param>
/// <returns></returns>
private static 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>
private static List<string> BuildUserData(ReqParameter reqParameter, List<string>? dataUnit)
{
var c = BuildC(reqParameter.FunCode, reqParameter.PRM);
var a = BuildAList(reqParameter.A, reqParameter.MSA);
var linkUserData = BuildLinkUserData(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>
private static List<string> BuildHeaders(int length)
{
var headers = new List<string>();
headers.Add(startStr);
var l = BuildLength(length);
headers.AddRange(l);
headers.AddRange(l);
headers.Add(startStr);
return headers;
}
/// <summary>
/// 长度 2字节 [用户数据区长度]
/// </summary>
/// <returns></returns>
private static List<string> BuildLength(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>
private static string BuildC(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>
private static List<string> BuildAList(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;
}
private static List<string> BuildLinkUserData(AFN aFN, Seq seq, int pn, int fn, List<string>? dataUnit)
{
var aFNValue = ((int)aFN).DecToHex().PadLeft(2, '0');
var sEQ = BuildSEQ(seq.TpV, seq.FIRFIN, seq.CON, seq.PRSEQ);
var dA = BuildDA(pn);
var dT = BuildDT(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(BuildTp("00"));
return list;
}
/// <summary>
/// 帧序列域
/// </summary>
/// <param name="tpV"></param>
/// <param name="fIRFIN"></param>
/// <param name="cON"></param>
/// <returns></returns>
private static string BuildSEQ(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>
private static List<string> BuildDA(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>
private static List<string> BuildDT(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>
private static List<string> BuildTp(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
#region 645
/// <summary>
/// 构建电表阀控下发数据单元
/// </summary>
/// <param name="address">电表地址</param>
/// <param name="specialControlCode">特殊控制码</param>
/// <param name="password">密码</param>
/// <param name="state">是否为开阀</param>
/// <param name="modelCode">型号码</param>
/// <returns></returns>
public static List<string> BuildAmmeterValveControlSendDataUnit(string address, string specialControlCode, string password, bool state, string modelCode = "")
{
var code = string.Empty;
if (state)
{
if (string.IsNullOrEmpty(specialControlCode))
code = "1B";
else
code = specialControlCode == "1B" || specialControlCode == "1C" ? specialControlCode : "1C";
}
else
code = "1A";//跳闸
if (specialControlCode == "1W")
{
if (state)
code = "1A";
else
code = "1C";
}
var pwdLevel = "02";
if (modelCode == "HL_DTSU2625" || modelCode == "DDZY9866")
pwdLevel = "04";
else if (modelCode == "DDS2705")
pwdLevel = "03"; //不带安全认证密级
if (!string.IsNullOrWhiteSpace(password) && password.Contains("|"))
{
var sp = password.Split('|');
pwdLevel = sp[1];
password = sp[0];
}
var strDate = DateTime.Now.AddYears(3).ToString("000012ddMMyy").StrAddSpan();//命令有效截止时间
if (specialControlCode == "1D" || modelCode == "SZBD_DDZY1225")
strDate = "FF FF FF FF FF FF";
var strP = password.StrAddSpan().StrReverseOrder();
var strSJY = " " + pwdLevel + " " + strP + " 01 00 00 00 " + code + " 00 " + strDate;
var dataUnit = strSJY.Replace(" ", "").StringToPairs();
var dataList = Build645SendCommand(address, "1C", dataUnit);
return dataList;
//string strLen = (strSJY.Replace(" ", "").Length / 2).ToString("X2");
//string strReturn = "68 " + address.StrAddSpan().StrReverseOrder() + " 68 1C " + strLen + " " + strSJY.StrAddHex33() + " ";
//string strSum = strReturn.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries).Select(i => Convert.ToInt32(i, 16)).Sum().ToString("X");
//strReturn += strSum.Substring(strSum.Length - 2) + " 16";
//return strReturn.Split(' ').ToList();
}
/// <summary>
/// 构建电表保电下发数据单元
/// </summary>
/// <param name="address">电表地址</param>
/// <param name="password"></param>
/// <param name="state">true 保电 false 保电解除</param>
/// <param name="modelCode">型号码</param>
/// <returns></returns>
public static List<string> BuildAmmeterLockSendDataUnit(string address, string password, bool state, string modelCode = "")
{
var code = state ? "3A" : "3B";
var strDate = (code + DateTime.Now.AddDays(1).ToString("00000012ddMMyy")).StrAddSpan();
if (modelCode == "SZBD_DDZY1225")
strDate = $"{code} 00 FF FF FF FF FF FF";
var strP = password.StrAddSpan().StrReverseOrder();
var strSJY = " 02 " + strP + " 01 00 00 00 " + strDate;
var dataUnit = strSJY.Replace(" ", "").StringToPairs();
var dataList = Build645SendCommand(address, "1C", dataUnit);
return dataList;
}
/// <summary>
/// 构建电表认证下发数据单元
/// </summary>
/// <param name="address">电表地址</param>
/// <param name="ramDon"></param>
/// <param name="maco"></param>
/// <returns></returns>
public static List<string> BuildAmmeterIdentitySendDataUnit(string address, string ramDon, string maco)
{
var codeList = "070004FF".StringToPairs();
codeList.Reverse();
var ramDonList = ramDon.StringToPairs();
ramDonList.Reverse();
var macoList = maco.StringToPairs();
macoList.Reverse();
var dataUnit = new List<string>();
dataUnit.AddRange(codeList);
dataUnit.AddRange(ramDonList);
dataUnit.AddRange(macoList);
var dataList = Build645SendCommand(address, "03", dataUnit);
return dataList;
}
/// <summary>
/// 构建电表清零下发数据单元
/// </summary>
/// <param name="address">电表地址</param>
/// <param name="password"></param>
/// <returns></returns>
public static List<string> BuildAmmterClearSendDataUnit(string address, string password)
{
var strP = password.StrAddSpan().StrReverseOrder();
var strSJY = " 02 " + strP + " 01 00 00 00 ";
var dataUnit = strSJY.Replace(" ", "").StringToPairs();
var dataList = Build645SendCommand(address, "1A", dataUnit);
return dataList;
}
/// <summary>
/// 构建645协议下发命令
/// </summary>
/// <param name="ammeterAddress">电表地址</param>
/// <param name="controlCode">控制码</param>
/// <param name="dataUnit">数据域 发送方按字节进行加33处理接收方按字节减33</param>
/// <returns></returns>
public static List<string> Build645SendCommand(string ammeterAddress, string controlCode, List<string>? dataUnit)
{
var cmdStrList = new List<string>();
cmdStrList.Add(startStr);
ammeterAddress = ammeterAddress.PadLeft(12, '0');
var addressList = ammeterAddress.StringToPairs();
addressList.Reverse();
cmdStrList.AddRange(addressList);
cmdStrList.Add(startStr);
cmdStrList.Add(controlCode);
var len = dataUnit != null ? dataUnit.Count.DecToHex().PadLeft(2, '0') : "00";
cmdStrList.Add(len);
if (dataUnit != null)
{
cmdStrList.AddRange(dataUnit.AddHex33());
}
var strSum = cmdStrList.Select(i => Convert.ToInt32(i, 16)).Sum().ToString("X");
strSum = strSum.Substring(strSum.Length - 2);
cmdStrList.Add(strSum);
cmdStrList.Add(endStr);
return cmdStrList;
}
/// <summary>
/// 威胜表的超功率(第一路恶性负载超功率判断阀值)
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="dataMark"></param>
/// <param name="power"></param>
/// <returns></returns>
public static List<string>? AssembleWsSupperPower(string address, string password, string dataMark, decimal power)
{
power = Math.Round(power, 4);
if (power > (decimal)99.9999) return null;
var strPower = power < 10 ? $"0{power}" : power.ToString();
var pStr = strPower.Replace(".", "").PadRight(6, '0');
var data = new string[] { pStr[4] + "" + pStr[5], pStr[2] + "" + pStr[3], pStr[0] + "" + pStr[1] };
return AssembleWrite(address, password, dataMark, data);
}
/// <summary>
/// 威胜表的超功率(超功率开关)
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="isOn"></param>
/// <returns></returns>
public static List<string> AssembleWsOnSupperPower(string address, string password, bool isOn = true)
{
var data = new string[] { (isOn ? "01" : "00") };
return AssembleWrite(address, password, "0E400001", data);
}
/// <summary>
/// 威胜表的超功率(第一路恶性负载绝对功率判断阈值)
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="dataMark"></param>
/// <param name="power"></param>
/// <returns></returns>
public static List<string>? AssembleWsAbsolutePowerSetting(string address, string password, string dataMark, decimal power)
{
power = Math.Round(power, 4);
if (power > (decimal)99.9999) return null;
var strPower = power < 10 ? $"0{power}" : power.ToString();
var pStr = strPower.Replace(".", "").PadRight(6, '0');
var data = new string[] { pStr[4] + "" + pStr[5], pStr[2] + "" + pStr[3], pStr[0] + "" + pStr[1] };
return AssembleWrite(address, password, dataMark, data);
}
/// <summary>
/// 威胜表的超功率(第一路恶性负载功率因数判断阀值)
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="dataMark"></param>
/// <param name="factor"></param>
/// <returns></returns>
public static List<string>? AssembleWsPowerFactor(string address, string password, string dataMark, decimal factor)
{
factor = Math.Round(factor, 3);
if (factor >= (decimal)1.0 || factor <= 0) return null;
var strFactor = factor.ToString();
var pStr = strFactor.Replace(".", "").PadRight(4, '0');
var data = new string[] { pStr[2] + "" + pStr[3], pStr[0] + "" + pStr[1] };
return AssembleWrite(address, password, dataMark, data);
}
/// <summary>
/// 构建写数据
/// </summary>
/// <param name="address"></param>
/// <param name="password"></param>
/// <param name="di0_3"></param>
/// <param name="data"></param>
/// <returns></returns>
public static List<string> AssembleWrite(string address, string password, string di0_3, string[] data)
{
var returnList = new List<string>();
returnList.Add(startStr);
var addressList = address.StringToPairs();
addressList.Reverse();
returnList.AddRange(addressList);
returnList.Add(startStr);
returnList.Add("14");
var len = (12 + data.Length).ToString("X2");
returnList.Add(len);
var di03List = di0_3.StringToPairs();
di03List.Reverse();
di03List.Add("02");
var pwdList = password.StringToPairs();
pwdList.Reverse();
di03List.AddRange(pwdList);
var mList = new List<string>() { "00", "11", "22", "33" };
di03List.AddRange(mList);
di03List.AddRange(data);
var listAdd33 = di03List.AddHex33();
returnList.AddRange(listAdd33);
var strSum = returnList.Select(i => Convert.ToInt32(i, 16)).Sum().ToString("X");
var cs = strSum.Substring(strSum.Length - 2);
returnList.Add(cs);
returnList.Add(endStr);
return returnList;
}
/// <summary>
/// 生成软编程指令
/// <param name="modelCode">型号编码</param>
/// </summary>
/// <returns></returns>
private static List<string> SoftProgram(string address,string modelCode,string password)
{
string bccmd = string.Empty;
if (modelCode == "DDS71")
{
bccmd = "68 AA AA AA AA AA AA 68 04 07 4E F4 35 BB BB BB 32";
}
else
{
bccmd = "68 AA AA AA AA AA AA 68 14 10 41 35 33 41 37 33 33 33 34 33 33 33 77 66 55 44";
}
//替换密码
var passwordList = password.StringToPairs();
passwordList.Reverse();
passwordList = passwordList.AddHex33();
var passwordStr = string.Join(" ", passwordList);
bccmd = bccmd.Replace("BB BB BB", passwordStr);
//替换表地址
var addressList = address.StringToPairs();
addressList.Reverse();
var addressStr = string.Join(" ", addressList);
bccmd = bccmd.Replace("AA AA AA AA AA AA", addressStr);
var fm = bccmd.Split(' ').ToList();
//生成CRC
var cRC = GetCRC(fm);
fm.Add(cRC);
fm.Add("16");
return fm;
}
#endregion
#region 188
/// <summary>
/// 标准 188协议阀控
/// </summary>
/// <param name="waterMeterAddress"></param>
/// <param name="mtype">表计类型</param>
/// <returns></returns>
public static List<string> BuildConfirm188WaterValve(string waterMeterAddress, bool state, string mtype = "10")
{
if (string.IsNullOrWhiteSpace(waterMeterAddress)) return null;
var dataUnit = new List<string>() { "A0", "17", "00", state ? "55" : "99" };
var dataList = Build188SendCommand(waterMeterAddress, "04", dataUnit);
return dataList;
}
/// <summary>
/// 构建188水表抄读下发数据单元
/// </summary>
/// <param name="waterMeterAddress"></param>
/// <returns></returns>
public static List<string> Build188WaterMeterReadingSendDataUnit(string waterMeterAddress)
{
var dataUnit = new List<string>() { "1F", "90", "00" };
var dataList = Build188SendCommand(waterMeterAddress, "01", dataUnit);
return dataList;
}
/// <summary>
/// 构建188协议下发命令
/// </summary>
/// <param name="waterMeterAddress">水表地址</param>
/// <param name="controlCode">控制码</param>
/// <param name="dataUnit">数据域</param>
/// <param name="meterType">表类型</param>
/// <returns></returns>
public static List<string> Build188SendCommand(string waterMeterAddress, string controlCode, List<string>? dataUnit = null, string meterType = "10")
{
//address.Substring(address.Length - 12, 12)
var cmdStrList = new List<string>();
cmdStrList.Add(startStr);
cmdStrList.Add(meterType);
waterMeterAddress = waterMeterAddress.PadLeft(14, '0');
var addressList = waterMeterAddress.StringToPairs();
addressList.Reverse();
cmdStrList.AddRange(addressList);
//控制码
cmdStrList.Add(controlCode);
var len = dataUnit != null ? dataUnit.Count.DecToHex().PadLeft(2, '0') : "00";
cmdStrList.Add(len);
if (dataUnit != null)
{
cmdStrList.AddRange(dataUnit);
}
var strSum = cmdStrList.Select(i => Convert.ToInt32(i, 16)).Sum().ToString("X");
strSum = strSum.Substring(strSum.Length - 2);
cmdStrList.Add(strSum);
cmdStrList.Add(endStr);
return cmdStrList;
}
/// <summary>
/// 组装水表阀控
/// </summary>
/// <param name="waterMeterAddress">水表地址</param>
/// <param name="password">The password.</param>
/// <param name="protocol">The poprotocol.</param>
/// <param name="state">if set to <c>true</c> [isopenvalve].</param>
/// <returns></returns>
public static List<string>? WaterMeterValveControl(string waterMeterAddress, string password, int protocol, bool state)
{
List<string>? turnData = null;
if (protocol == 1)
turnData = GetControlCode97(waterMeterAddress.Substring(waterMeterAddress.Length - 12, 12), password, state ? "3355" : "9966");//开阀/关阀
else if (protocol == 30)
turnData = GetControlCode07(waterMeterAddress.Substring(waterMeterAddress.Length - 12, 12), password, state ? "3355" : "9966");//开阀/关阀
return turnData;
}
private static List<string> GetControlCode97(string waterMeterAddress, string password, string valueCode)
{
var dataList = new List<string>();
var passwordList = password.StringToPairs();
passwordList.Reverse();
var valueCodeList = valueCode.StringToPairs();
valueCodeList.Reverse();
dataList.AddRange(valueCodeList);
dataList.AddRange(passwordList);
dataList.AddRange(valueCodeList);
return GetCode(waterMeterAddress, "04", dataList);
}
private static List<string> GetControlCode07(string waterMeterAddress, string password, string valueCode)
{
var dataList = new List<string>();
dataList.Add("02");
var passwordList = password.StringToPairs();
passwordList.Reverse();
dataList.AddRange(passwordList);
dataList.AddRange("01000000".StringToPairs());
dataList.AddRange(valueCode.StringToPairs());
dataList.Add("00");
var time = DateTime.Now.AddDays(1).ToString("yyMMddHHmmss");
var timeList = time.StringToPairs();
timeList.Reverse();
dataList.AddRange(timeList);
return GetCode(waterMeterAddress, "1C", dataList);
}
public static List<string> GetCode(string waterMeterAddress, string controlCode, List<string> childDataList)
{
var dataList = new List<string> { startStr };
waterMeterAddress = waterMeterAddress.PadLeft(12, '0');
var addressList = waterMeterAddress.StringToPairs();
addressList.Reverse();
dataList.AddRange(addressList);
dataList.Add(startStr);
dataList.Add(controlCode);
var num = childDataList.Count;
dataList.Add(num.ToString("X2"));
dataList.AddRange(childDataList.AddHex33());
var cs = dataList.Select(it => Convert.ToInt32(it, 16)).Sum().ToString("X2");
cs = cs.Substring(cs.Length - 2, 2);
dataList.Add(cs);
dataList.Add(endStr);
return dataList;
}
public static string GetCRC(List<string> inputFrm, int startIndex = 0)
{
int sum = 0;
for (int i = startIndex; i < inputFrm.Count; i++)
{
sum += (Convert.ToInt32(inputFrm[i], 16));
}
var sum16 = Convert.ToString(sum, 16);
return sum16.Substring(sum16.Length - 2, 2).ToUpper();
}
public static object GetAnalyzeValue(this List<string> hexStringList, CommandChunkEnum188 chunk)
{
if (hexStringList.Count < 11)
{
return null;
}
switch (chunk)
{
case CommandChunkEnum188.A:
var aHexList = hexStringList[(int)CommandChunkEnum188.A].Take(7).ToList();
aHexList.Reverse();
return string.Join("", aHexList.Skip(1).Take(6).ToList());
case CommandChunkEnum188.C:
var cHex = hexStringList[(int)CommandChunkEnum188.C];
return cHex;
case CommandChunkEnum188.Data:
var lenIndex = (int)CommandChunkEnum188.L;
var len = hexStringList[lenIndex].HexToDec();
//验证长度 2=(帧校验和+结束字符)
if (hexStringList.Count - 2 != 11 + len)
return null;
var dataHexList = hexStringList.Skip(11).Take(len).ToList();
return dataHexList;
default:
throw new ArgumentOutOfRangeException(nameof(chunk), chunk, null);
}
}
//public double AnalyzeCurrentTotalRate(List<string> hexList)
//{
// var str = string.Join("", hexList);
// var number = Convert.ToInt32(str) * 0.01;
// return number;
//}
#endregion
}
}