PLC通讯实现-C#实现汇川PLC-AM401以太网通讯ModBus TCP
- 背景
- 抽象设计
- 汇川PLC-AM401以太网通讯实现Modbus TCP
- 测试效果
- 引用库下载
背景
本人近十年的工作都与工业软件相关、其中工控系统开发过程中有一个必要环节就是跟各大厂商的PLC进行通讯,而对于从互联网行业跨入工业互联网行业的从业人员来说要实现各型号PLC通讯还是需要一个过程的,随着PLC的需求和应用越来越广泛,本文就介绍一下上位机软件与汇川PLC-AM401的通讯实现。
抽象设计
首先我们要进行一下抽象设计,先设计一个抽象类(接口也可以,此处因为还有其他业务使用了抽象类)BaseEquip,对PLC的常规操作进行定义,即Open、Read、Write、Close,业务代码调用BaseEquip进行PLC的读写,然后在实现各型号的Equip类,对Open、Read、Write、Close进行实现,根据配置在业务代码中对BaseEquip进行实例化,这样后期更改PLC型号后,只需修改配置即可,不用修改业务代码。
汇川PLC-AM401以太网通讯实现Modbus TCP
实现语言C#
抽象基类BaseEquip
public class BaseEquip
{
/// <summary>
/// 打开设备
/// </summary>
/// <returns></returns>
public abstract bool Open();
/// <summary>
/// 读取信息
/// </summary>
/// <param name="block">数据块</param>
/// <param name="start">起始地址</param>
/// <param name="len">长度</param>
/// <param name="buff">读取返回信息</param>
/// <returns></returns>
public abstract bool Read(string block, int start, int len, out object[] buff);
/// <summary>
/// 读取信息
/// </summary>
/// <param name="block">数据块</param>
/// <param name="start">起始地址</param>
/// <param name="len">长度</param>
/// <param name="buff">读取返回信息</param>
/// <returns></returns>
public virtual bool Read(string block, int start, int len, out ushort[] buff)
{
buff = new ushort[len];
return false;
}
/// <summary>
/// 写入信息
/// </summary>
/// <param name="block">数据块</param>
/// <param name="start">起始地址</param>
/// <param name="buff">长度</param>
/// <returns></returns>
public abstract bool Write(int block, int start, object[] buff);
/// <summary>
/// 写入信息
/// </summary>
/// <param name="block">数据块</param>
/// <param name="start">起始地址</param>
/// <param name="buff">长度</param>
/// <returns></returns>
public virtual bool Write(int block, int start, ushort[] buff)
{
return false;
}
/// <summary>
/// 关闭设备
/// </summary>
public abstract void Close();
}
设备实现类Equip.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Wongoing.Equips;
using Wongoing.Plc.Communication.ModBus;
namespace Wongoing.Equip.Modbus.Modbus.Net
{
/// <summary>
/// Modbus以太网通讯通常指ModbusTCP
/// </summary>
public class Equip : BaseEquip
{
#region 字段定义
private ModbusTcpNet modBusTcpClient = null;
private bool _isOpen = false; //是否打开连接
#endregion
#region 属性定义
public string IP
{
get
{
string ip = "192.168.1.105"; //PLC的IP地址
return ip;
}
}
#endregion
#region 打开方法,连接PLC
/// <summary>
/// 打开方法,连接PLC
/// </summary>
/// <returns></returns>
public override bool Open()
{
lock (this)
{
try
{
if (this._isOpen == true || (modBusTcpClient != null))
{
return true;
}
this.State = false;
this.modBusTcpClient = new ModbusTcpNet(this.IP);
Wongoing.Plc.Communication.OperateResult result = this.modBusTcpClient.ConnectServer();
if (result.IsSuccess)
{
this.State = true;
Console.WriteLine("连接成功!");
return this.State;
}
else
{
Console.WriteLine("PLC【ModbusTCP】连接失败!");
this.State = false;
return this.State;
}
}
catch(Exception ex)
{
this.State = false;
this._isOpen = false;
Console.WriteLine(ex.Message);
return this.State;
}
}
}
#endregion
#region 读取方法
/// <summary>
/// PLC数据读取方法
/// </summary>
/// <param name="block">要读取的块号</param>
/// <param name="start">要读取的起始字</param>
/// <param name="len">要读取的长度,最大255,超过255则不读取</param>
/// <param name="buff"></param>
/// <returns></returns>
public override bool Read(string block, int start, int len, out object[] buff)
{
lock (this)
{
buff = new object[len];
try
{
if (len > 256)
{
for (int i = 0; i < len; i++)
{
buff[i] = 0;
}
base.State = false;
return false;
}
#region 因考虑响应时间问题限制一次最多100字节
int maxOneLen = 100; //单次允许读取的最大长度
int count = len / maxOneLen; //要读取的次数
int mod = len % maxOneLen; //剩余的长度
bool flag = true; //保存读取标志
for (int i = 0; i < count; i++)
{
object[] _buff = new object[maxOneLen];
flag = this.ReadByLen(block, start + i * maxOneLen, maxOneLen, out _buff);
if (flag == false)
{
base.State = flag;
return false;
}
for (int k = i * maxOneLen; k < (i + 1) * maxOneLen; k++)
{
buff[k] = _buff[k - i * maxOneLen];
}
}
if (mod > 0)
{
object[] _buff = new object[mod];
flag = this.ReadByLen(block, start + count * maxOneLen, mod, out _buff);
if (flag == false)
{
base.State = flag;
return false;
}
for (int k = count * maxOneLen; k < count * maxOneLen + mod; k++)
{
buff[k] = _buff[k - count * maxOneLen];
}
}
#endregion
base.State = flag;
return flag;
}
catch (Exception ex)
{
Console.WriteLine(String.Format("读取PLC【ModbusTCP】设备失败-({0})!", ex.Message));
base.State = false;
return false;
}
}
}
/// <summary>
/// PLC数据读取方法
/// </summary>
/// <param name="block">要读取的块号</param>
/// <param name="start">要读取的起始字</param>
/// <param name="len">要读取的长度,最大255,超过255则不读取</param>
/// <param name="buff"></param>
/// <returns></returns>
public override bool Read(string block, int start, int len, out ushort[] buff)
{
lock (this)
{
buff = new ushort[len];
try
{
for (int i = 0; i < len; i++)
{
buff[i] = 0;
}
int maxOneLen = 100; //单次允许读取的最大长度,限制为100个字
int count = len / maxOneLen; //要读取的次数
int mod = len % maxOneLen; //剩余的长度
bool flag = true; //保存读取标志
for (int i = 0; i < count; i++)
{
ushort[] _buff = new ushort[maxOneLen];
flag = this.ReadByLen(block, start + i * maxOneLen, maxOneLen, out _buff);
if (flag == false)
{
base.State = flag;
return false;
}
for (int k = i * maxOneLen; k < (i + 1) * maxOneLen; k++)
{
buff[k] = _buff[k - i * maxOneLen];
}
}
if (mod > 0)
{
ushort[] _buff = new ushort[mod];
flag = this.ReadByLen(block, start + count * maxOneLen, mod, out _buff);
if (flag == false)
{
base.State = flag;
return false;
}
for (int k = count * maxOneLen; k < count * maxOneLen + mod; k++)
{
buff[k] = _buff[k - count * maxOneLen];
}
}
base.State = flag;
return flag;
}
catch (Exception ex)
{
Console.WriteLine(String.Format("读取PLC【ModbusTCP】设备block={0}, start={1}, len={2} 失败-({3})!", block, start, len, ex.Message));
base.State = false;
return false;
}
}
}
/// <summary>
/// 读取方法
/// </summary>
/// <param name="block">块号</param>
/// <param name="start">起始字</param>
/// <param name="len">长度,最长不超过100</param>
/// <param name="buff">数据缓冲区,存放读取的数据</param>
/// <returns>读取成功返回true,读取失败返回false</returns>
private bool ReadByLen(string block, int start, int len, out object[] buff)
{
lock (this)
{
buff = new object[len];
//for (int i = 0; i < len; i++)
//{
// buff[i] = 0;
//}
//return true;
if (!this.Open())
{
return false;
}
int[] _buff = new int[len];
int iblock = Convert.ToInt32(block);
start = iblock + start;
Wongoing.Plc.Communication.OperateResult<ushort[]> result = this.modBusTcpClient.ReadUInt16(start.ToString(), (ushort)len);
if (!result.IsSuccess)
{
Console.WriteLine("PLC【ModbusRTU】读取失败:" + result.ToMessageShowString());
return false;
}
for (int i = 0; i < len; i++)
{
buff[i] = result.Content[i];
}
this.State = true;
return true;
}
}
/// <summary>
/// 单次读取最长100个字的方法
/// </summary>
/// <param name="block">块号</param>
/// <param name="start">起始字</param>
/// <param name="len">长度,最长不超过100</param>
/// <param name="buff">数据缓冲区,存放读取的数据</param>
/// <returns>读取成功返回true,读取失败返回false</returns>
public bool ReadByLen(string block, int start, int len, out ushort[] buff)
{
lock (this)
{
buff = new ushort[len];
if (!this.Open())
{
return false;
}
int iblock = Convert.ToInt32(block);
start = iblock + start;
Wongoing.Plc.Communication.OperateResult<ushort[]> result = this.modBusTcpClient.ReadUInt16(start.ToString(), (ushort)len);
if (!result.IsSuccess)
{
Console.WriteLine("PLC【ModbusRTU】读取失败:" + result.ToMessageShowString());
return false;
}
for (int i = 0; i < len; i++)
{
buff[i] = result.Content[i];
}
this.State = true;
return true;
}
}
#endregion
#region 写入方法
/// <summary>
/// PLC数据写入方法
/// </summary>
/// <param name="block">要写入的块号</param>
/// <param name="start">起始字</param>
/// <param name="buff">要写入PLC的数据</param>
/// <returns>写入成功返回true,失败返回false</returns>
public override bool Write(int block, int start, object[] buff)
{
lock (this)
{
try
{
int len = buff.Length;
int maxOneLen = 100; //单次允许读取的最大长度
int count = len / maxOneLen; //要读取的次数
int mod = len % maxOneLen; //剩余的长度
bool flag = true; //保存写入标志
for (int i = 0; i < count; i++)
{
object[] _buff = new object[maxOneLen];
for (int k = i * maxOneLen; k < (i + 1) * maxOneLen; k++)
{
_buff[k - i * maxOneLen] = buff[k];
}
flag = this.WriteMax100(block, start + i * maxOneLen, _buff);
if (flag == false)
{
return false;
}
}
if (mod > 0)
{
object[] _buff = new object[mod];
for (int k = count * maxOneLen; k < count * maxOneLen + mod; k++)
{
_buff[k - count * maxOneLen] = buff[k];
}
flag = this.WriteMax100(block, start + count * maxOneLen, _buff);
}
return flag;
}
catch (Exception ex)
{
Console.WriteLine(String.Format("写入PLC(ModbusTCP)设备失败-({0})!", ex.Message));
return false;
}
}
}
/// <summary>
/// PLC数据写入方法
/// </summary>
/// <param name="block">要写入的块号</param>
/// <param name="start">起始字</param>
/// <param name="buff">要写入PLC的数据</param>
/// <returns>写入成功返回true,失败返回false</returns>
public override bool Write(int block, int start, ushort[] buff)
{
lock (this)
{
try
{
int len = buff.Length;
int maxOneLen = 100; //单次允许读取的最大长度
int count = len / maxOneLen; //要读取的次数
int mod = len % maxOneLen; //剩余的长度
bool flag = true; //保存写入标志
for (int i = 0; i < count; i++)
{
ushort[] _buff = new ushort[maxOneLen];
for (int k = i * maxOneLen; k < (i + 1) * maxOneLen; k++)
{
_buff[k - i * maxOneLen] = buff[k];
}
flag = this.WriteMax100(block, start + i * maxOneLen, _buff);
if (flag == false)
{
return false;
}
}
if (mod > 0)
{
ushort[] _buff = new ushort[mod];
for (int k = count * maxOneLen; k < count * maxOneLen + mod; k++)
{
_buff[k - count * maxOneLen] = buff[k];
}
flag = this.WriteMax100(block, start + count * maxOneLen, _buff);
}
return flag;
}
catch (Exception ex)
{
Console.WriteLine(String.Format("写入PLC(AB5000)设备失败-({0})!", ex.Message));
return false;
}
}
}
/// <summary>
/// 单次写入最多100个字至PLC
/// </summary>
/// <param name="block">要写入的块号</param>
/// <param name="start">要写入的起始字</param>
/// <param name="buff">要写入的数据</param>
/// <returns>写入成功返回true,失败返回false</returns>
private bool WriteMax100(int block, int start, object[] buff)
{
lock (this)
{
try
{
if (buff.Length == 0)
{
return true;
}
if (!this.Open())
{
return false;
}
ushort[] _buff = new ushort[buff.Length];
for (int i = 0; i < buff.Length; i++)
{
_buff[i] = ToValue(buff[i], 0);
}
int iblock = Convert.ToInt32(block);
start = iblock + start;
Wongoing.Plc.Communication.OperateResult result = this.modBusTcpClient.Write(start.ToString(), _buff);
if (!result.IsSuccess)
{
Console.WriteLine(String.Format("写入ModbusTCP_PLC异常:block ={0},Start={1}", block, start));
return false;
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(String.Format("写入ModbusTCP_PLC异常:block ={0},Start={1}", block, start), ex);
return false;
}
}
}
/// <summary>
/// 单次写入最多100个字至PLC
/// </summary>
/// <param name="block">要写入的块号</param>
/// <param name="start">要写入的起始字</param>
/// <param name="buff">要写入的数据</param>
/// <returns>写入成功返回true,失败返回false</returns>
private bool WriteMax100(int block, int start, ushort[] buff)
{
lock (this)
{
try
{
if (buff.Length == 0)
{
return true;
}
// ICSharpCode.Core.LoggingService.Info("Open");
if (!this.Open())
{
return false;
}
int iblock = Convert.ToInt32(block);
start = iblock + start;
Wongoing.Plc.Communication.OperateResult result = this.modBusTcpClient.Write(start.ToString(), buff);
if (!result.IsSuccess)
{
Console.WriteLine(String.Format("写入ModbusTCP_PLC异常:block ={0},Start={1}", block, start));
return false;
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(String.Format("写入ModbusTCP_PLC异常:block ={0},Start={1}", block, start), ex);
return false;
}
}
}
#endregion
#region 关闭连接
/// <summary>
/// 关闭连接
/// </summary>
public override void Close()
{
lock (this)
{
try
{
Wongoing.Plc.Communication.OperateResult result = this.modBusTcpClient.ConnectClose();
if (result.IsSuccess)
{
this.modBusTcpClient = null;
Console.WriteLine("PLC【ModbusTPC】设备已断开连接!");
}
else
{
Console.WriteLine("PLC【ModbusTPC】设备已断开连接异常:" + result.ToMessageShowString());
}
}
catch
{
}
}
}
#endregion
#region 辅助方法
private ushort ToValue(object obj, ushort defaultValue)
{
ushort result = 0;
if (obj != null
&& obj != DBNull.Value
&& ushort.TryParse(obj.ToString(), out result))
{
return result;
}
return defaultValue;
}
#endregion
}
}
测试效果
引用库下载
Wongoing.Plc.Communication.dll下载