using System; using System.Collections.Generic; using System.Linq; using System.Text; using QMAPP.FJC.Entity.EnergyManage; using QMAPP.FJC.BLL.Common; namespace QMAPP.FJC.BLL.EnergyManage { public class MeterReader { #region CONST /// /// 电表响应报文头 /// const byte RESPONSE_HEAD = 0xCC; /// /// 电表查询报文头 /// const byte REQUEST_HEAD = 0xAA; /// /// 抄表命令 /// const byte CMD_READMETER = 0x1; /// /// 查询表信息 /// const byte CMD_GETMETERINFO = 0x09; /// /// 复位表 /// const byte CMD_RESETMETER = 0x0E; const Int16 CMD_LEN_READMETER = 10; const Int16 CMD_LEN_GETMETERINFO = 9; const Int16 CMD_LEN_RESETMETER = 9; /// /// 电表查询指令集 0x01抄表 0x09查询表信息 0x0E复位表 /// static byte[] cmdids = new byte[] { CMD_READMETER, CMD_GETMETERINFO, CMD_RESETMETER }; /// /// 电表查询指令长度 /// static Int16[] cmdlens = new Int16[] { CMD_LEN_READMETER, CMD_LEN_GETMETERINFO, CMD_LEN_RESETMETER }; /// /// 电表响应超时时间 /// const int RESPONSE_TIMEOUT = 2500; #endregion /// /// 抄全部表 /// public static void ReadAllMeter() { try { DAL.EnergyManage.MeterDAL meterdal=new DAL.EnergyManage.MeterDAL(); List Meters = meterdal.GetList(new Meter { STATE = "1" }); foreach (var mg in Meters.GroupBy(p => p.NET_ADDR)) { using (System.Net.Sockets.Socket comServer = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp)) { try { var connresult = comServer.BeginConnect(mg.First().IPAddr, mg.First().Port, null, null); if (!connresult.AsyncWaitHandle.WaitOne(2000))//连接超时 { var meter = mg.First(); meterdal.SaveReading(meter , -1 , false , string.Format("电表({0}-{1});网络({2});错误({3})" , meter.METER_CODE , meter.NET_ID , meter.NET_ADDR , ReadMeterError.SOCKET_CONNECT_FAILED)); continue; } comServer.EndConnect(connresult); foreach (var meter in mg.OrderBy(p=>p.METER_CODE)) { ReadMeterError error; var reading = ReadMeter(comServer, meter.SysNum, meter.MeterNum, meter.UserNum, out error); var result= error == ReadMeterError.SUCCESS; meterdal.SaveReading(meter , reading * meter.RATIO , result , result ? "" : string.Format("电表({0}-{1});网络({2});错误({3})" , meter.METER_CODE , meter.NET_ID , meter.NET_ADDR , error)); } } catch (System.Net.Sockets.SocketException ex)//串口服务器连接异常 { var meter = mg.First(); meterdal.SaveReading(meter , -1 , false , string.Format("电表({0}-{1});网络({2});错误({3})" , meter.METER_CODE , meter.NET_ID , meter.NET_ADDR , ReadMeterError.SOCKET_CONNECT_FAILED)); continue; } } } } catch (Exception ex) { throw ex; } } /// /// 抄表 /// /// 串口服务器SOCKET /// 电表系统号 /// 电表号 /// 用户号 /// 错误消息 /// 电表读数 private static double ReadMeter(System.Net.Sockets.Socket comServer, Int16 sysnum, Int16 meternum, byte usernum, out ReadMeterError error) { try { var requestdata = GetReadCommand(sysnum, meternum, usernum); bool sended = false; List responsedata = new List(); System.Net.Sockets.SocketError socketerr; while (true) { var buffer = new byte[1024]; var asyResult = comServer.BeginReceive(buffer, 0, 1024, System.Net.Sockets.SocketFlags.None, null, null); if (!sended) { comServer.Send(requestdata); sended = true; } if (asyResult.AsyncWaitHandle.WaitOne(RESPONSE_TIMEOUT))//电表响应超时 { var rlen = comServer.EndReceive(asyResult, out socketerr); responsedata.AddRange(buffer.Take(rlen)); if (CheckResopnse(responsedata, requestdata)) { error = ReadMeterError.SUCCESS; return BitConverter.ToInt32(responsedata.Skip(9).Take(4).Reverse().ToArray(), 0) * 0.1; } if (rlen == 0) { error = ReadMeterError.SOCKET_REMOTE_CLOSED; break; } } else { comServer.Disconnect(true); //var rlen = comServer.EndReceive(asyResult, out socketerr); var cr= comServer.BeginConnect(comServer.RemoteEndPoint, null, null); cr.AsyncWaitHandle.WaitOne(RESPONSE_TIMEOUT); error = ReadMeterError.METER_RESPONSE_TIMEOUT; break; } } return -1; } catch (System.Net.Sockets.SocketException se) { error = ReadMeterError.SOCKET_ERROR; return -1; } } /// /// 检查电表返回数据 /// /// 返回报文 /// 查询报文 /// private static bool CheckResopnse(List responsedata, byte[] requestdata) { var headIndex = responsedata.IndexOf(RESPONSE_HEAD); //检查返回数据是否包含响应报文头 if (headIndex != 0)//报文头不是第一个字节或不包含报文头 { //移出无用数据 responsedata.RemoveRange(0, headIndex > 0 ? headIndex : responsedata.Count); } if (responsedata.Count < 5)//返回数据长度不应小于5 报文头(1)+包长(2)+校验位(2) { return false; } //第1,2位数据为包长 高位在前 UInt16 ResponseLen = BitConverter.ToUInt16(new byte[] { responsedata[2], responsedata[1] }, 0); if (ResponseLen > (responsedata.Count - 1))//验证返回数据长度是否达到报文包长 { return false; } //返回数据的末尾两位为校验位 高位在前 var checksum = BitConverter.ToUInt16(responsedata.Skip(responsedata.Count - 2).Take(2).Reverse().ToArray(), 0); //对数据重新进行CRC校验 var crcCheck = CRC.GetKey(responsedata.Skip(1).Take(ResponseLen - 2).ToArray()); if (checksum != crcCheck) //比对校验位 { //移除无用的数据 responsedata.RemoveAt(0); return false; } if (responsedata[3] != CMD_READMETER)//验证指令ID是否为抄表指令 { //移除无用的数据 responsedata.RemoveAt(0); return false; } if (!responsedata.Skip(3).Take(6).SequenceEqual(requestdata.Skip(3).Take(6)))//验证返回的系统号表号用户号是否一致 { //移除无用的数据 responsedata.RemoveAt(0); return false; } if (ResponseLen < 29)//单表抄表返回数据长度为29个字节 { //移除无用的数据 responsedata.RemoveAt(0); return false; } return true; } /// /// 生成抄表命令 /// /// 电表系统号 /// 电表号 /// 用户号 /// 抄表命令报文 private static byte[] GetReadCommand(Int16 sysnum, Int16 meternum, byte usernum) { byte cmdid = cmdids[0]; Int16 len = cmdlens[0]; byte[] lenbytes = BitConverter.GetBytes(len).Reverse().ToArray();//报文包长 byte[] sysnumbytes = BitConverter.GetBytes(sysnum).Reverse().ToArray();//系统号 byte[] meternumbytes = BitConverter.GetBytes(meternum).Reverse().ToArray();//电表号 List cmd = new List(); cmd.AddRange(lenbytes); cmd.Add(cmdid); cmd.AddRange(sysnumbytes); cmd.AddRange(meternumbytes); if (cmdid == CMD_READMETER) { cmd.Add(usernum);//用户号 } UInt16 checksum = (UInt16)CRC.GetKey(cmd.ToArray()); //校验位 byte[] checksumbytes = BitConverter.GetBytes(checksum).Reverse().ToArray(); cmd.AddRange(checksumbytes); cmd.Insert(0, REQUEST_HEAD);//插入查询报文头 return cmd.ToArray(); } } /// /// 抄表错误代码 /// public enum ReadMeterError { /// /// 读取成功 /// SUCCESS, /// /// Socket错误连接被关闭 /// SOCKET_REMOTE_CLOSED, /// /// Socket错误 /// SOCKET_ERROR, /// /// Socket连接失败 /// SOCKET_CONNECT_FAILED, /// /// 电表响应超时 /// METER_RESPONSE_TIMEOUT, /// /// 电表响应数据错误 /// METER_DATA_ERROR } }