You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
13 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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