1641 lines
64 KiB
C#
1641 lines
64 KiB
C#
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
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
}
|