|
|
using System;
|
|
|
using System.Collections.Generic;
|
|
|
using System.Diagnostics;
|
|
|
using System.Linq;
|
|
|
using System.Net.Sockets;
|
|
|
using System.Net;
|
|
|
using System.Text;
|
|
|
using System.Threading.Tasks;
|
|
|
using System.Web.Configuration;
|
|
|
|
|
|
namespace Rs.Framework
|
|
|
{
|
|
|
public class ModbusTcp
|
|
|
{
|
|
|
Socket m_socket;
|
|
|
string m_ip;
|
|
|
int m_port;
|
|
|
bool isConnected = false;
|
|
|
int timeout = 2000;
|
|
|
Stopwatch stopWatch = new Stopwatch();
|
|
|
|
|
|
public ModbusTcp(string ip, int port)
|
|
|
{
|
|
|
m_ip = ip;
|
|
|
m_port = port;
|
|
|
m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
|
|
}
|
|
|
|
|
|
public string GetIp()
|
|
|
{
|
|
|
return m_ip;
|
|
|
}
|
|
|
|
|
|
public bool Connect()
|
|
|
{
|
|
|
try
|
|
|
{
|
|
|
IPEndPoint ipaddr = new IPEndPoint(IPAddress.Parse(m_ip), m_port);
|
|
|
m_socket.Connect(ipaddr);
|
|
|
isConnected = true;
|
|
|
return true;
|
|
|
}
|
|
|
catch (Exception)
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 获取读取报文
|
|
|
/// </summary>
|
|
|
/// <param name="deviceID"></param>
|
|
|
/// <param name="funCode"></param>
|
|
|
/// <param name="startAddr"></param>
|
|
|
/// <param name="readLen"></param>
|
|
|
/// <returns></returns>
|
|
|
public byte[] GetReadCommand(int deviceID, FunCode funCode, int startAddr, int readLen)
|
|
|
{
|
|
|
List<byte> readData = new List<byte>();
|
|
|
//添加通信标识
|
|
|
readData.Add(0x00);
|
|
|
readData.Add(0x00);
|
|
|
//添加协议标识
|
|
|
readData.Add(0x00);
|
|
|
readData.Add(0x00);
|
|
|
//报文长度(读取报文的长度是固定的6个字节)
|
|
|
readData.Add(0x00);
|
|
|
readData.Add(0x06);
|
|
|
//添加设备编号
|
|
|
readData.Add((byte)deviceID);
|
|
|
//添加功能码
|
|
|
readData.Add((byte)funCode);
|
|
|
//添加读取地址
|
|
|
readData.Add((byte)(startAddr >> 8));
|
|
|
readData.Add((byte)startAddr);
|
|
|
//添加读取长度
|
|
|
readData.Add((byte)(readLen >> 8));
|
|
|
readData.Add((byte)readLen);
|
|
|
return readData.ToArray();
|
|
|
}
|
|
|
|
|
|
private byte[] GetWriteCommand(int deviceID, FunCode funCode, int startAddr, byte[] writeData, int datalen = 0)
|
|
|
{
|
|
|
List<byte> readData = new List<byte>();
|
|
|
//添加通信标识
|
|
|
readData.Add(0x00);
|
|
|
readData.Add(0x00);
|
|
|
//添加协议标识
|
|
|
readData.Add(0x00);
|
|
|
readData.Add(0x00);
|
|
|
//计算后面报文的长度
|
|
|
if (funCode == FunCode.WriteSingleCoil || funCode == FunCode.WriteSingleRegister)
|
|
|
{
|
|
|
readData.Add(0x00);
|
|
|
readData.Add(0x06);
|
|
|
}
|
|
|
else if (funCode == FunCode.WriteMultiCoil || funCode == FunCode.WriteMultiRegister)
|
|
|
{
|
|
|
int len = 6 + 1 + writeData.Length;
|
|
|
readData.Add((byte)(len >> 8));
|
|
|
readData.Add((byte)len);
|
|
|
}
|
|
|
|
|
|
//添加设备编号
|
|
|
readData.Add((byte)deviceID);
|
|
|
//添加功能码
|
|
|
readData.Add((byte)funCode);
|
|
|
|
|
|
//添加写入地址
|
|
|
readData.Add((byte)(startAddr >> 8));
|
|
|
readData.Add((byte)startAddr);
|
|
|
//添加要写入的线圈的数量
|
|
|
|
|
|
if (funCode == FunCode.WriteMultiCoil || funCode == FunCode.WriteMultiRegister)
|
|
|
{
|
|
|
readData.Add((byte)(datalen >> 8));
|
|
|
readData.Add((byte)datalen);
|
|
|
//添加要写入数据的字节数
|
|
|
readData.Add((byte)writeData.Length);
|
|
|
}
|
|
|
//添加真实要写入的数据
|
|
|
readData.AddRange(writeData);
|
|
|
return readData.ToArray();
|
|
|
}
|
|
|
|
|
|
public ModbusErrorCode ReadUshortArr(int deviceID, FunCode funCode, int startAddr, int readLen, out int[] result)
|
|
|
{
|
|
|
result = new int[readLen];
|
|
|
if (isConnected)
|
|
|
{
|
|
|
List<byte> receiveBuffer = new List<byte>();
|
|
|
byte[] readDatas = GetReadCommand(deviceID, funCode, startAddr, readLen);
|
|
|
try
|
|
|
{
|
|
|
int len = m_socket.Send(readDatas);
|
|
|
if (len > 0)
|
|
|
{
|
|
|
//开始接收数据
|
|
|
stopWatch.Restart();
|
|
|
while (true && stopWatch.ElapsedMilliseconds <= timeout)
|
|
|
{
|
|
|
int receiveDataLen = m_socket.Available;
|
|
|
if (receiveDataLen > 0)
|
|
|
{
|
|
|
byte[] buffer = new byte[receiveDataLen];
|
|
|
len = m_socket.Receive(buffer, receiveDataLen, SocketFlags.None);
|
|
|
if (len > 0)
|
|
|
{
|
|
|
receiveBuffer.AddRange(buffer);
|
|
|
//判断数据是否接收完毕,
|
|
|
if (receiveBuffer.Count >= 6)
|
|
|
{
|
|
|
//读取报文长度
|
|
|
int packetLen = receiveBuffer[4] << 8 | receiveBuffer[5];
|
|
|
if (receiveBuffer.Count == packetLen + 6)
|
|
|
{
|
|
|
int dataLen = receiveBuffer[8];
|
|
|
//代表数据已经接受完毕,开始解析数据
|
|
|
for (int i = 0; i < dataLen / 2; i++)
|
|
|
{
|
|
|
result[i] = receiveBuffer[(i * 2) + 9] << 8 | receiveBuffer[(i * 2) + 10];
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return ModbusErrorCode.OK;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (stopWatch.ElapsedMilliseconds >= timeout)
|
|
|
{
|
|
|
return ModbusErrorCode.TIMEOUT;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return ModbusErrorCode.SENDFAIL;
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
return ModbusErrorCode.EXCEPTION;
|
|
|
}
|
|
|
return ModbusErrorCode.OK;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
//未连接
|
|
|
return ModbusErrorCode.NOCONNECT;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public ModbusErrorCode WriteSingleCoil(int deviceID, int startAddr, bool flag)
|
|
|
{
|
|
|
byte[] writeData = new byte[2] { 0x00, 0x00 };
|
|
|
List<byte> receiveBuffer = new List<byte>();
|
|
|
if (flag)
|
|
|
{
|
|
|
writeData[0] = 0xFF;
|
|
|
writeData[1] = 0x00;
|
|
|
}
|
|
|
return Write(deviceID, FunCode.WriteSingleCoil, startAddr, writeData);
|
|
|
}
|
|
|
|
|
|
public ModbusErrorCode WriteCoils(int deviceID, int startAddr, bool[] flags)
|
|
|
{
|
|
|
byte[] writeData = new byte[((flags.Length - 1) / 8) + 1];
|
|
|
int val = 0;
|
|
|
for (int i = 0; i < flags.Length; i++)
|
|
|
{
|
|
|
if (flags[i])
|
|
|
val = val | 1 << i;
|
|
|
else
|
|
|
val = val | 0 << i;
|
|
|
}
|
|
|
for (int i = 0; i < writeData.Length; i++)
|
|
|
{
|
|
|
writeData[i] = (byte)(val >> (i * 8));
|
|
|
}
|
|
|
return Write(deviceID, FunCode.WriteMultiCoil, startAddr, writeData, flags.Length);
|
|
|
}
|
|
|
|
|
|
public ModbusErrorCode WriteSingleRegister(int deviceID, int startAddr, ushort value)
|
|
|
{
|
|
|
byte[] writeData = new byte[2] { 0x00, 0x00 };
|
|
|
List<byte> receiveBuffer = new List<byte>();
|
|
|
writeData[0] = (byte)(value >> 8);
|
|
|
writeData[1] = (byte)value;
|
|
|
return Write(deviceID, FunCode.WriteSingleRegister, startAddr, writeData);
|
|
|
}
|
|
|
|
|
|
public ModbusErrorCode WriteMultiHoldRegister(int deviceID, int startAddr, ushort[] values)
|
|
|
{
|
|
|
byte[] writeData = new byte[values.Length * 2];
|
|
|
for (int i = 0; i < values.Length; i++)
|
|
|
{
|
|
|
writeData[(i * 2)] = (byte)(values[i] >> 8);
|
|
|
writeData[(i * 2) + 1] = (byte)(values[i]);
|
|
|
}
|
|
|
return Write(deviceID, FunCode.WriteMultiRegister, startAddr, writeData, values.Length);
|
|
|
}
|
|
|
|
|
|
private ModbusErrorCode Write(int deviceID, FunCode funCode, int startAddr, byte[] writeData, int writeLen = 0)
|
|
|
{
|
|
|
if (isConnected)
|
|
|
{
|
|
|
lock (this)
|
|
|
{
|
|
|
byte[] readDatas = GetWriteCommand(deviceID, funCode, startAddr, writeData, writeLen);
|
|
|
try
|
|
|
{
|
|
|
List<byte> receiveBuffer = new List<byte>();
|
|
|
int len = m_socket.Send(readDatas);
|
|
|
if (len > 0)
|
|
|
{
|
|
|
//开始接收数据
|
|
|
stopWatch.Restart();
|
|
|
while (true && stopWatch.ElapsedMilliseconds <= timeout)
|
|
|
{
|
|
|
int receiveDataLen = m_socket.Available;
|
|
|
if (receiveDataLen > 0)
|
|
|
{
|
|
|
byte[] buffer = new byte[receiveDataLen];
|
|
|
len = m_socket.Receive(buffer, receiveDataLen, SocketFlags.None);
|
|
|
if (len > 0)
|
|
|
{
|
|
|
|
|
|
receiveBuffer.AddRange(buffer);
|
|
|
if (receiveBuffer.Count >= 6)
|
|
|
{
|
|
|
////判断数据是否接收完毕
|
|
|
int framelen = receiveBuffer[4] << 8 | receiveBuffer[5];
|
|
|
if (framelen + 6 == receiveBuffer.Count)
|
|
|
{
|
|
|
return ModbusErrorCode.OK;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (stopWatch.ElapsedMilliseconds >= timeout)
|
|
|
{
|
|
|
return ModbusErrorCode.TIMEOUT;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
return ModbusErrorCode.SENDFAIL;
|
|
|
}
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
return ModbusErrorCode.EXCEPTION;
|
|
|
}
|
|
|
return ModbusErrorCode.OK;
|
|
|
}
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
//未连接
|
|
|
return ModbusErrorCode.NOCONNECT;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
public enum FunCode
|
|
|
{
|
|
|
/// <summary>
|
|
|
/// 读取 多个线圈(位)
|
|
|
/// </summary>
|
|
|
ReadMultiCoil = 01,
|
|
|
/// <summary>
|
|
|
/// 读取 多个离散量输入量(位)
|
|
|
/// </summary>
|
|
|
ReadMultiInput = 02,
|
|
|
/// <summary>
|
|
|
/// 读取 多个保持寄存器(16进制整型)
|
|
|
/// </summary>
|
|
|
ReadMultiHoldRegister = 03,
|
|
|
/// <summary>
|
|
|
/// 读取 多个输入寄存器(16进制整型)
|
|
|
/// </summary>
|
|
|
ReadMultiInputRegister = 04,
|
|
|
/// <summary>
|
|
|
/// 写入 单个线圈(位)
|
|
|
/// </summary>
|
|
|
WriteSingleCoil = 05,
|
|
|
/// <summary>
|
|
|
/// 写入 单个寄存器(16进制整型)
|
|
|
/// </summary>
|
|
|
WriteSingleRegister = 06,
|
|
|
/// <summary>
|
|
|
/// 写入 多个线圈(位)
|
|
|
/// </summary>
|
|
|
WriteMultiCoil = 15,
|
|
|
/// <summary>
|
|
|
/// 写入 多个寄存器(16进制整型)
|
|
|
/// </summary>
|
|
|
WriteMultiRegister = 16
|
|
|
|
|
|
}
|
|
|
|
|
|
public enum ModbusErrorCode
|
|
|
{
|
|
|
OK,
|
|
|
NOCONNECT,
|
|
|
SENDFAIL,
|
|
|
EXCEPTION,
|
|
|
TIMEOUT
|
|
|
}
|
|
|
}
|