You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
12 KiB
294 lines
12 KiB
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
|
|
/// <summary>
|
|
/// 电表响应报文头
|
|
/// </summary>
|
|
const byte RESPONSE_HEAD = 0xCC;
|
|
/// <summary>
|
|
/// 电表查询报文头
|
|
/// </summary>
|
|
const byte REQUEST_HEAD = 0xAA;
|
|
/// <summary>
|
|
/// 抄表命令
|
|
/// </summary>
|
|
const byte CMD_READMETER = 0x1;
|
|
/// <summary>
|
|
/// 查询表信息
|
|
/// </summary>
|
|
const byte CMD_GETMETERINFO = 0x09;
|
|
/// <summary>
|
|
/// 复位表
|
|
/// </summary>
|
|
const byte CMD_RESETMETER = 0x0E;
|
|
const Int16 CMD_LEN_READMETER = 10;
|
|
const Int16 CMD_LEN_GETMETERINFO = 9;
|
|
const Int16 CMD_LEN_RESETMETER = 9;
|
|
|
|
|
|
/// <summary>
|
|
/// 电表查询指令集 0x01抄表 0x09查询表信息 0x0E复位表
|
|
/// </summary>
|
|
static byte[] cmdids = new byte[] { CMD_READMETER, CMD_GETMETERINFO, CMD_RESETMETER };
|
|
/// <summary>
|
|
/// 电表查询指令长度
|
|
/// </summary>
|
|
static Int16[] cmdlens = new Int16[] { CMD_LEN_READMETER, CMD_LEN_GETMETERINFO, CMD_LEN_RESETMETER };
|
|
/// <summary>
|
|
/// 电表响应超时时间
|
|
/// </summary>
|
|
const int RESPONSE_TIMEOUT = 2500;
|
|
#endregion
|
|
|
|
/// <summary>
|
|
/// 抄全部表
|
|
/// </summary>
|
|
public static void ReadAllMeter()
|
|
{
|
|
try
|
|
{
|
|
DAL.EnergyManage.MeterDAL meterdal=new DAL.EnergyManage.MeterDAL();
|
|
List<Meter> 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;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 抄表
|
|
/// </summary>
|
|
/// <param name="comServer">串口服务器SOCKET</param>
|
|
/// <param name="sysnum">电表系统号</param>
|
|
/// <param name="meternum">电表号</param>
|
|
/// <param name="usernum">用户号</param>
|
|
/// <param name="error">错误消息</param>
|
|
/// <returns>电表读数</returns>
|
|
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<byte> responsedata = new List<byte>();
|
|
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;
|
|
}
|
|
|
|
}
|
|
/// <summary>
|
|
/// 检查电表返回数据
|
|
/// </summary>
|
|
/// <param name="responsedata">返回报文</param>
|
|
/// <param name="requestdata">查询报文</param>
|
|
/// <returns></returns>
|
|
private static bool CheckResopnse(List<byte> 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;
|
|
|
|
}
|
|
/// <summary>
|
|
/// 生成抄表命令
|
|
/// </summary>
|
|
/// <param name="sysnum">电表系统号</param>
|
|
/// <param name="meternum">电表号</param>
|
|
/// <param name="usernum">用户号</param>
|
|
/// <returns>抄表命令报文</returns>
|
|
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<byte> cmd = new List<byte>();
|
|
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();
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// 抄表错误代码
|
|
/// </summary>
|
|
public enum ReadMeterError
|
|
{
|
|
/// <summary>
|
|
/// 读取成功
|
|
/// </summary>
|
|
SUCCESS,
|
|
/// <summary>
|
|
/// Socket错误连接被关闭
|
|
/// </summary>
|
|
SOCKET_REMOTE_CLOSED,
|
|
/// <summary>
|
|
/// Socket错误
|
|
/// </summary>
|
|
SOCKET_ERROR,
|
|
/// <summary>
|
|
/// Socket连接失败
|
|
/// </summary>
|
|
SOCKET_CONNECT_FAILED,
|
|
/// <summary>
|
|
/// 电表响应超时
|
|
/// </summary>
|
|
METER_RESPONSE_TIMEOUT,
|
|
/// <summary>
|
|
/// 电表响应数据错误
|
|
/// </summary>
|
|
METER_DATA_ERROR
|
|
}
|
|
}
|
|
|