C#-DL/T 645—1997协议
DL/T 645—1997 常用的一种电表协议,虽然已经有了2007版的,但是依然有厂家在用呀!
这里主要写了下实时数据的获取,关于设置之类的可以自行研究下,协议文档可以留言。
1. 帧格式
注意事项:
(1)小端数据传输,即先小后大,低字节在前,高字节在后,地址域(电表的地址)也是一样的,先低位后高位;
(2)CS是前面所有字节模256的和;
(3)主站请求数据前需要发送FE FE FE FE来唤醒从站,即唤醒电表,看有人说有的是3个字节,暂时没遇到,我这里把这几个前导放到了每包的请求报文中,即FE FE FE FE .......;
(4)传输的数据(数据项标识和数据域)都是经过+0x33来处理的,可能是为了BCD码吧...
数据项标识:
源 +0x33 十进制 0x11B6 0x44E9 17641 // ABC电压 0x12B6 0x45E9 17897 0x13B6 0x46E9 18153 0x21B6 0x54E9 21737 // ABC电流 0x22B6 0x55E9 21993 0x23B6 0x56E9 22249 0x30B6 0x63E9 25577 // 有功功率 无功功率 总功率因数 0x40B6 0x73E9 29673 0x50B6 0x83E9 33769 0x31B6 0x64E9 25833 // ABC有功功率 0x32B6 0x65E9 26089 0x33B6 0x66E9 26345 0x41B6 0x74E9 29929 // ABC无功功率 0x42B6 0x75E9 30185 0x43B6 0x76E9 30441 0x51B6 0x84E9 34025 // ABC功率因数 0x52B6 0x85E9 34281 0x53B6 0x86E9 34537 0x1090 0x43C3 17347 // 正向有功总电能 反向有功总电能 0x2090 0x53C3 21443
按照这个表格进行请求实时数据即可,看个人需要,我们是只需要简单的这些数据,做了呈现和统计处理;
报文示例,这里是单点请求,其实可以按照块来请求,例如请求ABC相的电流,在07版协议中会有示例:
发送请求B相电流: FE FE FE FE 68 12 34 56 78 90 12 68 01 02 55 E9 C7 16
发送请求正向有功总电能: FE FE FE FE 68 12 34 56 78 90 12 68 01 02 43 C3 8F 16
接收正向有功总电能: 68 12 34 56 78 90 12 68 81 06 43 C3 33 44 55 66 45 16
注意:接收数据实测是没有前导FE的,不要多处理!校验位也是不计算前导FE的,这个据说只是用来唤醒对方;
+0x33,55 E9这个是经过+0x33发送,接收的数据43 C3 33 44 55 66这些是需要-0x33进行处理的;
简单请求实时数据来说,这协议就是这么简单的,解析如下图所示,看接收的数据0x43C3减去偏移的0x33,为0x1090,小端传输,即正向有功电能,数据也就是0x33445566,减去偏移0x33再反转(大小端)得到33221100(十进制),再乘于表格中的小数点,得到332211.00。
比如A相电流,若获取到的数据是0x3344,那么真实数据就是1100*0.01也就是11.00A。
实时数据这儿还需要注意下变比,即电压互感器变比(PT)和电流互感器变比(CT),需要的时候也是需要乘入到实时数据的值中的;即11.00A*CT*PT;
接下来上代码......
介绍下环境,C#,电表通过485线接入到串口服务器上,这个串口服务器是一个串口转网口的装置,把串口的数据转换后经过网口进行传输,从网口传输的数据可以转换成串口再下发到设备中,其中是透传,不做任何数据包中的转换;
既然用到了网口,那就不需要再考虑半双工了,咱们可以收发同时进行,即收发用两个线程来处理,注意下请求数据的时间间隔,不然可能把设备发挂了...
用到了数据库的操作,把这些实时数据的点表配置到数据库中,在程序启动的时候初始化即可,这样子迁移或者修改配置数据的时候可以只修改数据库的点表就行了,相对简单。
数据库表,用到了两个,一个是请求包protocol_DLT645package,一个是接收到数据解析用的protocol_DLT645_97signal,
1 DROP TABLE IF EXISTS `protocol_DLT645_97package`; 2 CREATE TABLE `protocol_DLT645_97package` ( 3 `id` int(11) NOT NULL AUTO_INCREMENT, 4 `devicetype` int(11) NOT NULL, -- 设备类型 5 `blockaddr` int(11) NOT NULL, -- 块地址 6 `blockname` varchar(50) NOT NULL, -- 块名称 7 `blockcontent` varchar(50) NOT NULL, -- 块内容 8 `visible` int(11) NOT NULL, -- 是否请求;0不请求;1请求; 9 PRIMARY KEY (`id`) 10 ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 11 12 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '17641', 'A电压', '17641', '1'); 13 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '17897', 'B电压', '17897', '1'); 14 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '18153', 'C电压', '18153', '1'); 15 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '21737', 'A电流', '21737', '1'); 16 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '21993', 'B电流', '21993', '1'); 17 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '22249', 'C电流', '22249', '1'); 18 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '25577', '有功功率', '25577', '1'); 19 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '29673', '无功功率', '29673', '1'); 20 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '33769', '总功率因数', '33769', '1'); 21 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '25833', 'A有功功率', '25833', '1'); 22 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '26089', 'B有功功率', '26089', '1'); 23 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '26345', 'C有功功率', '26345', '1'); 24 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '29929', 'A无功功率', '29929', '1'); 25 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '30185', 'B无功功率', '30185', '1'); 26 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '30441', 'C无功功率', '30441', '1'); 27 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '34025', 'A功率因数', '34025', '1'); 28 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '34281', 'B功率因数', '34281', '1'); 29 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '34537', 'C功率因数', '34537', '1'); 30 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '17347', '正向有功总电能', '17347', '1'); 31 INSERT INTO `protocol_DLT645_97package` VALUES (null, -1, '21443', '反向有功总电能', '21443', '1'); 32 33 34 35 DROP TABLE IF EXISTS `protocol_DLT645_97signal`; 36 CREATE TABLE `protocol_DLT645_97signal` ( 37 `id` int(11) NOT NULL AUTO_INCREMENT, 38 `devicetype` int(11) NOT NULL, -- 设备类型 39 `signaladdr` int(11) NOT NULL, -- 信号地址 40 `signalname` varchar(50) NOT NULL, -- 信号名称 41 `signalid` int(11) NOT NULL, -- 信号ID 42 `datalength` int(11) NOT NULL, -- 数据长度,所占字节数 43 `ratio` float NOT NULL, -- 变比 44 `ismultiplifct` int(11) NOT NULL, -- 是否乘于互感器变比,变比在协议中配置 45 `visible` int(11) NOT NULL, -- 是否需要解析;0不解析;1解析; 46 PRIMARY KEY (`id`) 47 ) ENGINE=MyISAM DEFAULT CHARSET=utf8; 48 49 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '17641', 'A电压', '-1', '2', '1', '1', '1' ); 50 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '17897', 'B电压', '-1', '2', '1', '1', '1' ); 51 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '18153', 'C电压', '-1', '2', '1', '1', '1' ); 52 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '21737', 'A电流', '-1', '2', '0.01', '1', '1' ); 53 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '21993', 'B电流', '-1', '2', '0.01', '1', '1' ); 54 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '22249', 'C电流', '-1', '2', '0.01', '1', '1' ); 55 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '25577', '有功功率', '-1', '2', '0.01', '1', '1' ); 56 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '29673', '无功功率', '-1', '2', '0.01', '1', '1' ); 57 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '33769', '总功率因数', '-1', '2', '0.001', '0', '1' ); 58 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '25833', 'A有功功率', '-1', '2', '0.01', '1', '1' ); 59 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '26089', 'B有功功率', '-1', '2', '0.01', '1', '1' ); 60 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '26345', 'C有功功率', '-1', '2', '0.01', '1', '1' ); 61 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '29929', 'A无功功率', '-1', '2', '0.01', '1', '1' ); 62 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '30185', 'B无功功率', '-1', '2', '0.01', '1', '1' ); 63 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '30441', 'C无功功率', '-1', '2', '0.01', '1', '1' ); 64 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '34025', 'A功率因数', '-1', '2', '0.001', '0', '1' ); 65 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '34281', 'B功率因数', '-1', '2', '0.001', '0', '1' ); 66 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '34537', 'C功率因数', '-1', '2', '0.001', '0', '1' ); 67 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '17347', '正向有功总电能', '-1', '4', '0.01', '1', '1' ); 68 INSERT INTO `protocol_DLT645_97signal` VALUES (null, -1, '21443', '反向有功总电能', '-1', '4', '0.01', '1', '1' );
数据库表也没什么介绍的,只是把请求的数据项标识配置成了动态的,比如增加的时候只在数据库中增加就行了,其他字段可有可无呀,个人需求;
初始化配置项:
/// <summary> /// 请求实时数据,块 /// </summary> public Dictionary<ushort, ushort[]> RequestRealDataDic { set; get; } /// <summary> /// 信号ID MAP /// </summary>
/// <summary> /// 电表物理地址 /// </summary> public byte[] MeterPhysicalAddr { set; get; } MeterPhysicalAddr = new byte[6]; var addr = ConfigurationManager.AppSettings["MeterPhysicalAddr"]; if (addr != null) { string[] strList = addr.Split('|'); for (int i = 0; i < 6; i++) { MeterPhysicalAddr[i] = BCD_to_HEX(byte.Parse(strList[5 - i])); } }
public Dictionary<int, Signal> SignalIdMap { set; get; }
初始化请求数据包
/// <summary> /// 初始化请求实时数据,块 /// </summary> private void InitRequestRealDataDic() { RequestRealDataDic = new Dictionary<ushort, ushort[]>(); List<protocol_dlt645_97package> pcakageS = DbHelp.GetList<protocol_dlt645_97package>(). Where(s => s.visible == (int)EnumYesOrNo.Yes). Where(s => s.devicetype == DeviceType).ToList(); if(pcakageS == null || pcakageS.Any() == false) { LogEvent.LogInfo.Fatal("pcakageS == null || pcakageS.Any() == false"); return; } foreach(protocol_dlt645_97package package in pcakageS) { string[] siganlAddrS = package.blockcontent.Split(' ', ',', ',').Where(s => !string.IsNullOrEmpty(s)).ToArray(); ushort[] signalAddrList = Array.ConvertAll(siganlAddrS, ushort.Parse); if (signalAddrList != null && signalAddrList.Count() != 0) { RequestRealDataDic.Add((ushort)package.blockaddr, signalAddrList); } } }
初始化解析数据包:
/// <summary> /// 初始化信号ID MAP /// </summary> private void InitSignalIdMap() { SignalIdMap = new Dictionary<int, Signal>(); List<protocol_dlt645_97signal> signalList = DbHelp.GetList<protocol_dlt645_97signal>().Where(x => x.devicetype == this.DeviceType).ToList(); if (signalList == null || signalList.Any() == false) { LogEvent.LogInfo.Fatal("signalList == null || signalList.Any() == false"); return; } foreach(protocol_dlt645_97signal dlt645signal in signalList) { Signal signal = new Signal { SignalID = dlt645signal.signalid, SignalName = dlt645signal.signalname, DataLength = dlt645signal.datalength, Visible = dlt645signal.visible, Ratio = dlt645signal.ratio, }; // 电流互感器 if(dlt645signal.ismultiplifct == (int)EnumYesOrNo.Yes) { signal.Ratio *= ConstValue.FCT; } // 电压互感器 if(dlt645signal.ismultiplifpt == (int)EnumYesOrNo.Yes) { signal.Ratio *= ConstValue.FPT; } SignalIdMap.Add(dlt645signal.signaladdr, signal); } }
两个线程一发一收,我们这里用到了redis当做消息队列处理,收发都是经过redis进行处理的,可以理解为当前的发送不是直接到设备的,而是经过一层统一处理放到redis的消息队列当中,相当于收发都取redis的,这个不重要,和直连是一样的。
发送实时数据请求线程,
/// <summary> /// 按块请求实时数据 /// </summary> private void RequestBlockThread() { while (true) { Thread.Sleep(RealDataInterval); try { foreach (ushort dataFlag in RequestRealDataDic.Keys) { Thread.Sleep(RealDataInterval); // 发送实时数据请求 SendDataRequest(dataFlag); // 有功功率请求频繁 //Thread.Sleep(RealDataInterval); //SendDataRequest(ConstValue.ActivePowerFlag); } } catch (Exception ex) { LogEvent.Loger.Fatal(ex.ToString()); } } } /// <summary> /// 发送实时数据请求 /// </summary> /// <param name="dataFlag"></param> private void SendDataRequest(ushort dataFlag) { MessageInfo sendMessage = new MessageInfo { MessageHead = ConstValue.MessageHead, Addr = MeterPhysicalAddr, // 改为自动获取或 StartHead = ConstValue.MessageHead, ControlCode = ConstValue.ControlCode, DataLength = ConstValue.DataFlagLength, DataFlag = dataFlag, EndFrame = ConstValue.MessageEndFrame }; SaveSendMessage(sendMessage); } /// <summary> /// 发送数据 /// </summary> /// <param name="message"></param> private void SaveSendMessage(MessageInfo message) { try { byte[] SendData = message.ObjectToByte(); LogEvent.LogInfo.FatalFormat("发送:\r\n{0}", DataCopy.ToHexString(SendData)); RedisHelper redisclient = new RedisHelper((int)EnumUserRedisNum.Protocol); string sessionId = redisclient.HashGetString(ConstValue.DeviceCodeSessionKey, string.Format("{0}|{1}", StationID, CanID)); SendMessage sendMessage = new SendMessage { SessionID = sessionId, SendData = SendData }; redisclient.ListRightPush(string.Format("{0}|发送", ProtocolMeterHelp.ProtocolID), sendMessage); } catch (Exception ex) { LogEvent.Loger.Fatal(ex.ToString()); } }
接收实时数据应答线程,
/// <summary> /// 获取消息线程 /// </summary> private void GetMessageTh() { RedisHelper redisclient = new RedisHelper((int)EnumUserRedisNum.Protocol); string key = String.Format("{0}|接收", ProtocolID); ProtocolMeter ProtocolMeter = ProtocolMeter.GetInstance(); while (true) { Thread.Sleep(RequestInterval * 100); try { if (Work == false) { continue; } ReviceMessage message = redisclient.ListLeftPop<ReviceMessage>(key); if (message == null) { continue; } ProtocolMeter = ProtocolMeter.GetInstance(); ProtocolMeter.ParseProtocol(message); } catch(Exception ex) { LogEvent.Loger.Fatal(ex.ToString()); } } } /// <summary> /// 解析数据包 /// </summary> /// <param name="rMsg"></param> public void ParseProtocol(ReviceMessage rMsg) { try { SaveClientStatus(rMsg); if (rMsg.Key == EnumSocektKeyType.连接.ToString()) { LogEvent.LogInfo.Debug("**************************客户端连接**************************"); // 保存设备、局站、协议等、 StationLogin(rMsg.SessionID); return; } // LogEvent.LogInfo.DebugFormat("接收:\r\n{0}", DataCopy.ToHexString(rMsg.ReviceData)); MessageInfo messageInfo = new MessageInfo(rMsg); if (!messageInfo.Valid) { MessageRecord.SaveInvalidMessage(rMsg.ReviceData); //记录非法数据包 return; } // 数据解析 SaveRealData(messageInfo.DataFlag, messageInfo.Data); } catch (Exception ex) { LogEvent.Loger.Fatal(ex.ToString()); } } /// <summary> /// 保存实时数据 /// </summary> /// <param name="dataFlag"></param> /// <param name="data"></param> private void SaveRealData(ushort dataFlag, byte[] data) { if (!RequestRealDataDic.ContainsKey(dataFlag)) { LogEvent.Loger.Fatal("!RequestRealDataDic.ContainsKey(dataFlag), return."); return; } List<RealDataInfo> realList = new List<RealDataInfo>(); Dictionary<int, byte[]> dataByteDic = new Dictionary<int, byte[]>(); int index = 0; ushort[] dataFlagS = RequestRealDataDic[dataFlag]; foreach(ushort flag in dataFlagS) { int dstOffset = 0; byte[] bS = new byte[SignalIdMap[flag].DataLength]; DataCopy.ByteCopy(data, index, bS, ref dstOffset, data.Length, SignalIdMap[flag].DataLength); index += SignalIdMap[flag].DataLength; dataByteDic.Add(flag, bS); } LogEvent.LogInfo.DebugFormat("dataByteDic---{0}", dataByteDic.Keys.Count); foreach (int flag in dataByteDic.Keys) { if(SignalIdMap[flag].Visible == (int)EnumYesOrNo.No) { continue; } int count = dataByteDic[flag].Length; double value = 0; value += HEX_to_BCD((byte)(dataByteDic[flag][count - 1] - ConstValue.DataFlagDeviation)) * Math.Pow(10, (count - 1) * 2); for (int i = 0; i < count - 1; i++) { value += HEX_to_BCD((byte)(dataByteDic[flag][i] - ConstValue.DataFlagDeviation)) * Math.Pow(10, i * 2); } realList.Add(new RealDataInfo { DeviceCode = string.Format("{0}|{1}", StationID, CanID), SignalID = SignalIdMap[flag].SignalID, Value = Math.Round(value * SignalIdMap[flag].Ratio, 3), StringValue = "", DataStatus = 0, UpdateTime = DateTime.Now }); LogEvent.LogInfo.DebugFormat($"{Convert.ToString(flag, 16)}|{DataCopy.ToHexString(dataByteDic[flag])}|{value * SignalIdMap[flag].Ratio}"); } //保存实时数据 RedisHelper redis = new RedisHelper((int)EnumUserRedisNum.RealData); redis.ListRightPush(ConstValue.SetRealData, realList); }
帧格式包如下,
using Common; using System; using LogManager; namespace Protocol_Meter_DLT645_97 { public class MessageInfo { #region 属性 /// <summary> /// 帧起始符,68H /// </summary> public byte MessageHead { set; get; } /// <summary> /// 地址域,6字节 /// </summary> public byte[] Addr { set; get; } /// <summary> /// 帧起始符,68H /// </summary> public byte StartHead { set; get; } /// <summary> /// 控制码 /// </summary> public byte ControlCode { set; get; } /// <summary> /// 数据域长度 /// </summary> public byte DataLength { set; get; } /// <summary> /// 数据标识 /// </summary> public ushort DataFlag { set; get; } /// <summary> /// 数据域 /// </summary> public byte[] Data { set; get; } /// <summary> /// 校验域 /// </summary> public byte CheckSum { set; get; } /// <summary> /// 结束帧,16H /// </summary> public byte EndFrame { set; get; } /// <summary> /// 发送数据的SOCKET /// </summary> public string SessionID { set; get; } /// <summary> /// 原始包 /// </summary> public byte[] MessageData { set; get; } /// <summary> /// 消息包是否有效 /// </summary> public bool Valid { set; get; } #endregion #region 构造 /// <summary> /// 默认构造 /// </summary> public MessageInfo() { } /// <summary> /// 通过接收的数据构造函数 /// </summary> /// <param name="message"></param> public MessageInfo(ReviceMessage message) { try { // 长度有误,固有长度+接收带有的FE的长度+发送的FE的长度 if(message.ReviceData.Length < ConstValue.MessageLengthWithoutData+ConstValue.FE_Length-4) { Valid = false; return; } MessageData = new byte[message.ReviceData.Length]; Buffer.BlockCopy(message.ReviceData, 0, MessageData, 0, message.ReviceData.Length); int index = 0; int dstOffset = 0; // FE FE FE FE 68 22 52 88 00 00 00 68 91 07 33 33 37 35 33 33 33 CF 16 index += ConstValue.FE_Length; // 帧起始符 MessageHead = DataCopy.GetByte(message.ReviceData, ref index); // 地址域,6字节 Addr = new byte[6]; DataCopy.ByteCopy(message.ReviceData, index, Addr, ref dstOffset, message.ReviceData.Length, 6); index += 6; // 帧起始符 StartHead = DataCopy.GetByte(message.ReviceData, ref index); // 控制码 ControlCode = DataCopy.GetByte(message.ReviceData, ref index); // 数据域长度 DataLength = DataCopy.GetByte(message.ReviceData, ref index); // 数据标识 DataFlag = DataCopy.GetUShortByHostOrder(message.ReviceData, ref index); // 数据域 int dataLength = message.ReviceData.Length - ConstValue.MessageLengthWithoutData - ConstValue.FE_Length + 4; dstOffset = 0; if (dataLength > 0) { Data = new byte[dataLength]; DataCopy.ByteCopy(message.ReviceData, index, Data, ref dstOffset, message.ReviceData.Length, dataLength); index += dataLength; } else { Valid = false; return; } // 校验码 CheckSum = DataCopy.GetByte(message.ReviceData, ref index); // 结束符 EndFrame = DataCopy.GetByte(message.ReviceData, ref index); SessionID = message.SessionID; Valid = true; } catch (Exception ex) { LogEvent.Loger.Fatal(ex.ToString()); Valid = false; } } #endregion #region 方法 /// <summary> /// 将对象转化为二进制 /// </summary> /// <returns></returns> public byte[] ObjectToByte() { int messageLength = ConstValue.MessageLengthWithoutData; if (Data != null) messageLength += Data.Length; int index = 0; byte[] result = new byte[messageLength]; try { DataCopy.ByteCopy(ConstValue.FE_Head, 0, result, ref index, ConstValue.FE_Head.Length, messageLength); // 包头 DataCopy.ByteCopy(MessageHead, result, ref index); // 地址域 if (Addr != null) DataCopy.ByteCopy(Addr, 0, result, ref index, Addr.Length, messageLength); // 帧起始符 DataCopy.ByteCopy(StartHead, result, ref index); // 控制码 DataCopy.ByteCopy(ControlCode, result, ref index); // 数据域长度 DataCopy.ByteCopy(DataLength, result, ref index); // 数据标识 DataCopy.ByteCopyNetworkOrder(DataFlag, result, ref index); // 消息包 if (Data != null) DataCopy.ByteCopy(Data, 0, result, ref index, Data.Length, messageLength); // 校验码 CheckSum = CheckSumHelp.AddCheckSum(result, 4, index); DataCopy.ByteCopy(CheckSum, result, ref index); // 数据标识 DataCopy.ByteCopy(EndFrame, result, ref index); } catch (Exception ex) { LogEvent.Loger.Fatal(ex.ToString()); return null; } return result; } #endregion } }
BCD码和十六进制的转换,
/// <summary> /// BCD转16进制 /// </summary> /// <param name="data"></param> /// <returns></returns> private byte HEX_to_BCD(byte data) { return (byte)((data >> 4) * 10 + (data & 0x0f)); } /// <summary> /// 16进制转BCD /// </summary> /// <param name="data"></param> /// <returns></returns> private byte BCD_to_HEX(byte data) { return (byte)(((data / 10) << 4) + (data % 10)); }
解析完实时数据之后就可以做业务处理了,这个协议关于实时数据这儿也就完了。
其他设置数据之类的可以自己研究一下,其他实时数据配置到数据库中即可,代码可留言...