PLC通讯实现-C#实现三菱PLC通讯MXComponent(三)
背景
本人近十年的工作都与工业软件相关、其中工控系统开发过程中有一个必要环节就是跟各大厂商的PLC进行通讯,而对于从互联网行业跨入工业互联网行业的从业人员来说要实现各型号PLC通讯还是需要一个过程的,本人在此对主流型号PLC通讯实现进行总结以便大家参考。
抽象设计
首先我们要进行一下抽象设计,先设计一个抽象类(接口也可以,此处因为还有其他业务使用了抽象类)BaseEquip,对PLC的常规操作进行定义,即Open、Read、Write、Close,业务代码调用BaseEquip进行PLC的读写,然后在实现各型号的Equip类,对Open、Read、Write、Close进行实现,根据配置在业务代码中对BaseEquip进行实例化,这样后期更改PLC型号后,只需修改配置即可,不用修改业务代码。
三菱PLC通讯实现MXComponent
实现语言C#
安装MxComponent通讯组件,并在MxComponent通讯组件中根据向导创建逻辑站。在C#通讯项目中增加对ACTMULTLib的引用,如果没有安装MxComponent则不能引用。
抽象基类BaseEquip
public class BaseEquip
{
/// <summary>
/// 打开设备
/// </summary>
/// <returns>成功返回true,失败返回false</returns>
public abstract bool Open();
/// <summary>
/// 读取信息
/// </summary>
/// <param name="block">数据块</param>
/// <param name="start">起始地址</param>
/// <param name="len">长度</param>
/// <param name="buff">读取返回信息</param>
/// <returns>成功返回true,失败返回false</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="buff">要写入的数据</param>
/// <returns>成功返回true,失败返回false</returns>
public abstract bool Write(int block, int start, object[] buff);
/// <summary>
/// 关闭设备
/// </summary>
public abstract void Close();
}
设备实现类Equip实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mesnac.Equips;
namespace Mesnac.Equip.Mitsubishi.MXComponent.Default
{
/// <summary>
/// 三菱通讯封装设备类
/// </summary>
public class Equip : BaseEquip
{
private int ActLogicalStationNumber = 0; //MX Component控件管理中定义的逻辑站号
private ACTMULTILib.ActEasyIFClass com_ReferencesEasyIF = null; //定义三菱PLC通讯控件对象
private bool _isOpen = false; //是否打开连接
public override bool Open()
{
lock (this)
{
try
{
if (this._isOpen == true)
{
return true;
}
this.State = false;
if (this.com_ReferencesEasyIF == null)
{
this.com_ReferencesEasyIF = new ACTMULTILib.ActEasyIFClass();
this.com_ReferencesEasyIF.ActLogicalStationNumber = this.ActLogicalStationNumber;
}
int Result = this.com_ReferencesEasyIF.Open(); //打开
if (Result != 0)
{
Console.WriteLine("PLC连接失败:" + this.GetErrInfo((uint)Result));
this.State = false;
return this.State;
}
else
{
this.State = true;
this._isOpen = true;
Console.WriteLine("连接成功!");
return this.State;
}
}
catch (Exception ex)
{
this.State = false;
this._isOpen = false;
Console.WriteLine(ex.Message);
return this.State;
}
}
}
/// <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;
}
int maxOneLen = 100; //单次允许读取的最大长度,AB500限制为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];
}
}
base.State = flag;
return flag;
}
catch (Exception ex)
{
//ICSharpCode.Core.LoggingService.Error(String.Format("读取PLC(AB500)设备失败-({0})!", ex.Message));
Console.WriteLine(ex.Message);
base.State = false;
return false;
}
}
}
/// <summary>
/// 单次读取最长100个字的方法
/// </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];
if (!this.Open())
{
return false;
}
int state = len;
int[] _buff = new int[len];
int iblock = Convert.ToInt32(block);
iblock = iblock + start;
//int iResult = this.com_ReferencesEasyIF.ReadDeviceBlock("D" + iblock, _buff.Length, out _buff[0]);
int iResult = this.com_ReferencesEasyIF.ReadDeviceBlock("L" + iblock, _buff.Length, out _buff[0]);
if (iResult != 0)
{
Console.WriteLine("PLC读取失败:" + this.GetErrInfo((uint)iResult));
this.State = false;
return false;
}
else
{
this.State = true;
}
int iReadLen = len;
if (iReadLen > state)
{
iReadLen = state;
}
for (int i = 0; i < iReadLen; i++)
{
int value = _buff[i];
if (value > ushort.MaxValue)
{
value = ushort.MaxValue - value;
}
buff[i] = value;
}
return true;
}
}
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;
}
/// <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
{
if (buff.Length > 256)
{
return false;
}
int len = buff.Length;
int maxOneLen = 100; //单次允许读取的最大长度,AB500限制为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)
{
//ICSharpCode.Core.LoggingService.Error(String.Format("写入PLC(AB500)设备失败-({0})!", ex.Message));
Console.WriteLine(ex.Message);
return false;
}
}
}
/// <summary>
/// 单次写入最多100个字至PLC
//