天津投入产出系统后端
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

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
}
}