349 lines
14 KiB
C#
349 lines
14 KiB
C#
using JinYuan.Models;
|
|||
|
|
using System;
|
||
|
|
using System.Buffers;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.Net;
|
||
|
|
using System.Net.NetworkInformation;
|
||
|
|
using System.Net.Sockets;
|
||
|
|
|
||
|
|
namespace JinYuan.Helper
|
||
|
|
{
|
||
|
|
public class EnergyMeterHelper
|
||
|
|
{
|
||
|
|
public const int BufferSize = 1024;//6kb
|
||
|
|
|
||
|
|
private int _timeout = 2000;
|
||
|
|
private Ping _ping = null;
|
||
|
|
private IPEndPoint _endPoint = null;
|
||
|
|
private Socket EnergyMeterSocket = null;
|
||
|
|
private PingReply _pingReply = null;
|
||
|
|
private string _lastError = "";
|
||
|
|
|
||
|
|
public enum ModBusExceptionCode //错误代码
|
||
|
|
{
|
||
|
|
IllegalFunction = 01,
|
||
|
|
IllegalDataAddress,
|
||
|
|
IllegalDataValue,
|
||
|
|
SlaveDeviceFailure,
|
||
|
|
Acknowledge,
|
||
|
|
SlaveDeviceBusy,
|
||
|
|
GatewayPathUnavailable,
|
||
|
|
GatewayTargetDeviceFailed2Respond,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// 构造函数
|
||
|
|
/// </summary>
|
||
|
|
public EnergyMeterHelper()
|
||
|
|
{
|
||
|
|
// used to ping the PLC
|
||
|
|
//
|
||
|
|
this._ping = new Ping();
|
||
|
|
|
||
|
|
// EndPoint parametres
|
||
|
|
//
|
||
|
|
this._endPoint = new IPEndPoint(0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// set ip and port
|
||
|
|
/// </summary>
|
||
|
|
public void SetTCPParams(IPAddress ip, int port)
|
||
|
|
{
|
||
|
|
this._endPoint.Address = ip;
|
||
|
|
this._endPoint.Port = port;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// returns the connection status
|
||
|
|
/// </summary>
|
||
|
|
public bool Connected
|
||
|
|
{
|
||
|
|
get
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
return (EnergyMeterSocket == null) ? false : EnergyMeterSocket.Connected;
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
//PrintfLog.LogError(ex.Message);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// close the socket
|
||
|
|
/// </summary>
|
||
|
|
/// <returns></returns>
|
||
|
|
public void Close()
|
||
|
|
{
|
||
|
|
lock (this)
|
||
|
|
{
|
||
|
|
if (EnergyMeterSocket == null) return;
|
||
|
|
if (Connected)
|
||
|
|
{
|
||
|
|
EnergyMeterSocket.Disconnect(false);
|
||
|
|
EnergyMeterSocket.Close();
|
||
|
|
}
|
||
|
|
EnergyMeterSocket.Dispose();
|
||
|
|
EnergyMeterSocket = null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool Connect()
|
||
|
|
{
|
||
|
|
if (this.Connected == true)
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (this.TCPConnect())
|
||
|
|
{
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
Close();
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private bool TCPConnect()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (EnergyMeterSocket != null)
|
||
|
|
{
|
||
|
|
EnergyMeterSocket.Dispose();
|
||
|
|
}
|
||
|
|
EnergyMeterSocket = new Socket(_endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||
|
|
|
||
|
|
EnergyMeterSocket.SendTimeout = _timeout;
|
||
|
|
EnergyMeterSocket.ReceiveTimeout = _timeout;
|
||
|
|
EnergyMeterSocket.Connect(this._endPoint);
|
||
|
|
return this.Connected;
|
||
|
|
}
|
||
|
|
catch (System.Exception ex)
|
||
|
|
{
|
||
|
|
TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " 智能电表:网口连接失败");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool Ping()
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
if (this._endPoint.Address == null) return false;
|
||
|
|
|
||
|
|
this._pingReply = this._ping.Send(this._endPoint.Address, this._timeout);
|
||
|
|
|
||
|
|
return (this._pingReply.Status == IPStatus.Success) ? true : false;
|
||
|
|
|
||
|
|
}
|
||
|
|
catch (Exception)
|
||
|
|
{
|
||
|
|
//PrintfLog.LogError("ECFFU ping exception");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool SendAndGetRes(byte[] bytes, ref List<ElectricEnergy> listM)//, ref JetReturnResult jet
|
||
|
|
{
|
||
|
|
var arrayPool = ArrayPool<byte>.Shared;
|
||
|
|
byte[] buffer = arrayPool.Rent(BufferSize);//new byte[1024];
|
||
|
|
byte[] RecvData = new byte[88 * 4 + 9];//0x58
|
||
|
|
int size = 88 * 4 + 9;//0x58
|
||
|
|
|
||
|
|
if (!Ping())
|
||
|
|
{
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
try
|
||
|
|
{
|
||
|
|
// 发送
|
||
|
|
Send(bytes, bytes.Length);
|
||
|
|
// 接收
|
||
|
|
Receive(ref RecvData, size);
|
||
|
|
// 解析
|
||
|
|
if ((RecvData[0] == Convert.ToByte(01 >> 8) && RecvData[1] == Convert.ToByte(01)))//pTask.id
|
||
|
|
{
|
||
|
|
if (RecvData[7] == 0x04)//读线圈寄存器功能码
|
||
|
|
{
|
||
|
|
//数据赋值
|
||
|
|
Array.Copy(RecvData, 0, buffer, 0, RecvData.Length);
|
||
|
|
TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"电能表读取原始数据:{string.Join(" ", RecvData)}");
|
||
|
|
|
||
|
|
decimal PhaseVoltageA = Convert.ToDecimal(HextoFloat(buffer[9], buffer[10], buffer[11], buffer[12]));
|
||
|
|
decimal PhaseVoltageB = Convert.ToDecimal(HextoFloat(buffer[13], buffer[14], buffer[15], buffer[16]));
|
||
|
|
decimal PhaseVoltageC = Convert.ToDecimal(HextoFloat(buffer[17], buffer[18], buffer[19], buffer[20]));
|
||
|
|
decimal PhaseCurrentA = Convert.ToDecimal(HextoFloat(buffer[21], buffer[22], buffer[23], buffer[24]));
|
||
|
|
decimal PhaseCurrentB = Convert.ToDecimal(HextoFloat(buffer[25], buffer[26], buffer[27], buffer[28]));
|
||
|
|
decimal PhaseCurrentC = Convert.ToDecimal(HextoFloat(buffer[29], buffer[30], buffer[31], buffer[32]));
|
||
|
|
decimal PhasePowerA = Convert.ToDecimal(HextoFloat(buffer[33], buffer[34], buffer[35], buffer[36]));
|
||
|
|
decimal PhasePowerB = Convert.ToDecimal(HextoFloat(buffer[37], buffer[38], buffer[39], buffer[40]));
|
||
|
|
decimal PhasePowerC = Convert.ToDecimal(HextoFloat(buffer[41], buffer[42], buffer[43], buffer[44]));
|
||
|
|
decimal ActivePower = Convert.ToDecimal(HextoFloat(buffer[121], buffer[122], buffer[123], buffer[124]));
|
||
|
|
decimal PowerFactor = Convert.ToDecimal(HextoFloat(buffer[133], buffer[134], buffer[135], buffer[136]));
|
||
|
|
decimal pt = 1;
|
||
|
|
decimal ct = 150 / 5;
|
||
|
|
decimal AccumulatedElectricity = Convert.ToDecimal(HextoFloat(buffer[181], buffer[182], buffer[183], buffer[184]));
|
||
|
|
|
||
|
|
ElectricEnergy m = new ElectricEnergy();
|
||
|
|
m.PhaseVoltageA = PhaseVoltageA;
|
||
|
|
m.PhaseVoltageB = PhaseVoltageB;
|
||
|
|
m.PhaseVoltageC = PhaseVoltageC;
|
||
|
|
m.PhaseCurrentA = PhaseCurrentA * ct;
|
||
|
|
m.PhaseCurrentB = PhaseCurrentB * ct;
|
||
|
|
m.PhaseCurrentC = PhaseCurrentC * ct;
|
||
|
|
m.PhasePowerA = PhaseVoltageA * PhaseCurrentA * ct;
|
||
|
|
m.PhasePowerB = PhaseVoltageB * PhaseCurrentB * ct;
|
||
|
|
m.PhasePowerC = PhaseVoltageC * PhaseCurrentC * ct;
|
||
|
|
m.ActivePower = ActivePower * ct;
|
||
|
|
m.PowerFactor = PowerFactor * ct;
|
||
|
|
m.pt = pt;//Convert.ToDecimal(HextoFloat(buffer[0], buffer[0], buffer[0], buffer[0]));
|
||
|
|
m.ct = ct;//Convert.ToDecimal(HextoFloat(buffer[0], buffer[0], buffer[0], buffer[0]));
|
||
|
|
m.AccumulatedElectricity = AccumulatedElectricity;
|
||
|
|
listM.Add(m);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
switch (RecvData[8])
|
||
|
|
{
|
||
|
|
case (byte)ModBusExceptionCode.IllegalDataAddress:
|
||
|
|
_lastError = string.Format("SendAndGetRes():ReadInputRegister [IllegalDataAddress]");
|
||
|
|
break;
|
||
|
|
case (byte)ModBusExceptionCode.IllegalDataValue:
|
||
|
|
_lastError = string.Format("SendAndGetRes():ReadInputRegister [IllegalDataValue]");
|
||
|
|
break;
|
||
|
|
case (byte)ModBusExceptionCode.IllegalFunction:
|
||
|
|
_lastError = string.Format("SendAndGetRes():ReadInputRegister [IllegalFunction]");
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
_lastError = string.Format("SendAndGetRes():ReadInputRegister [FunctionException]");
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"电能表读取数据异常,原因:{_lastError}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"电能表读取原始数据:{string.Join(" ", RecvData)}");
|
||
|
|
TxtHelper.WriteTxt($@"D:\APILog\Logs\智能电表信息\{DateTime.Now.ToString("yyyy-MM-dd")}.txt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + $"电能表读取数据异常,原因:{ex}");
|
||
|
|
Close();
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
arrayPool.Return(buffer);
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// send a command to energy meter device
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="command"></param>
|
||
|
|
/// <param name="cmdLen"></param>
|
||
|
|
/// <returns></returns>
|
||
|
|
private int Send(Byte[] command, int cmdLen)
|
||
|
|
{
|
||
|
|
if (!Connected)
|
||
|
|
{
|
||
|
|
throw new Exception("EnergyMeter Socket is not connected.");
|
||
|
|
}
|
||
|
|
// sends the command
|
||
|
|
//
|
||
|
|
int bytesSent = EnergyMeterSocket.Send(command, cmdLen, SocketFlags.None);
|
||
|
|
|
||
|
|
// it checks the number of bytes sent
|
||
|
|
//
|
||
|
|
if (bytesSent != cmdLen)
|
||
|
|
{
|
||
|
|
string msg = string.Format("EnergyMeter Sending error. (Expected bytes: {0} Sent: {1})", cmdLen, bytesSent);
|
||
|
|
throw new Exception(msg);
|
||
|
|
}
|
||
|
|
return bytesSent;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// receives a response from the plc
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="response"></param>
|
||
|
|
/// <param name="respLen"></param>
|
||
|
|
/// <returns></returns>
|
||
|
|
private int Receive(ref Byte[] response, int respLen)
|
||
|
|
{
|
||
|
|
if (!this.Connected)
|
||
|
|
{
|
||
|
|
throw new Exception("EnergyMeter Socket is not connected.");
|
||
|
|
}
|
||
|
|
|
||
|
|
// receives the response, this is a synchronous method and can hang the process
|
||
|
|
int bytesRecv = EnergyMeterSocket.Receive(response, respLen, SocketFlags.None);
|
||
|
|
|
||
|
|
// check the number of bytes received
|
||
|
|
//
|
||
|
|
// if (bytesRecv != respLen)
|
||
|
|
// {
|
||
|
|
// string msg = string.Format("Receiving error. (Expected: {0} Received: {1})"
|
||
|
|
// , respLen, bytesRecv);
|
||
|
|
// throw new Exception(msg);
|
||
|
|
// }
|
||
|
|
return bytesRecv;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// HexToFloat
|
||
|
|
/// </summary>
|
||
|
|
/// <param name="H1">高字高字节</param>
|
||
|
|
/// <param name="H2">高字低字节</param>
|
||
|
|
/// <param name="D1">低字高字节</param>
|
||
|
|
/// <param name="D2">低字低字节</param>
|
||
|
|
/// <returns>float</returns>
|
||
|
|
public static float HextoFloat(byte H1, byte H2, byte D1, byte D2)
|
||
|
|
{
|
||
|
|
try
|
||
|
|
{
|
||
|
|
//byte-->short
|
||
|
|
int s1, s2;
|
||
|
|
s1 = Convert.ToInt32(H1 * 256) + Convert.ToInt32(H2);
|
||
|
|
s2 = Convert.ToInt32(D1 * 256) + Convert.ToInt32(D2);
|
||
|
|
|
||
|
|
//将输入数值short转化为无符号unsigned short
|
||
|
|
int us1 = s1, us2 = s2;
|
||
|
|
if (s1 < 0) us1 += 65536;
|
||
|
|
if (s2 < 0) us2 += 65536;
|
||
|
|
//sign: 符号位, exponent: 阶码, mantissa:尾数
|
||
|
|
int sign, exponent;
|
||
|
|
float mantissa;
|
||
|
|
//计算符号位
|
||
|
|
sign = us1 / 32768;
|
||
|
|
//去掉符号位
|
||
|
|
int emCode = us1 % 32768;
|
||
|
|
//计算阶码
|
||
|
|
exponent = emCode / 128;
|
||
|
|
//计算尾数
|
||
|
|
mantissa = (float)(emCode % 128 * 65536 + us2) / 8388608;
|
||
|
|
//代入公式 fValue = (-1) ^ S x 2 ^ (E - 127) x (1 + M)
|
||
|
|
return (float)Math.Pow(-1, sign) * (float)Math.Pow(2, exponent - 127) * (1 + mantissa);
|
||
|
|
}
|
||
|
|
catch (Exception ex)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|