目录
一、西门子PLC仿真环境搭建
【1.1】创建PLC项目
【1.2】编写PLC程序
二、C#代码编写
【2.1】窗口制作
【2.2】效果演示
【2.3】读取源码
【2.4】FrmSiemensSet源码
【2.5】Variable源码
一、西门子PLC仿真环境搭建
【1.1】创建PLC项目
-
搭建PLCSIM-Advacend模拟仿真
-
设置PLC的IP地址和PLCSIM一致
-
勾选GET/PUT(如果是S7协议必须勾选)选项
-
勾选块编译时仿真
-
创建变量用于测试,相关的DB块需要设置为去除优化访问
【1.2】编写PLC程序
【PLC作为服务器】
【Modbus Poll测试】
二、C#代码编写
【2.1】窗口制作
【2.2】效果演示
【2.3】读取源码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using Approach.DataConvertLib;
using Approach.ModbusTCPLib;
namespace Approach.ModbusTCP
{
public partial class FrmSiemens : Form
{
/// <summary>
/// 【字段】ModbusTCP连接对象
/// </summary>
private ModbusTcp _modbusTcp = new ModbusTcp();
/// <summary>
/// 【字段】ModbusTCP连接成功标识
/// </summary>
private bool _isConnected = false;
/// <summary>
/// 【字段】多线程取消对象
/// </summary>
private CancellationTokenSource _cts = null;
public FrmSiemens()
{
InitializeComponent();
}
/// <summary>
/// 【控件事件】建立连接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_Connect_Click(object sender, EventArgs e)
{
// 获取UI信息
string plcIp = txt_Ip.Text.Trim();
int plcPort = int.Parse(txt_Port.Text.Trim());
_isConnected = _modbusTcp.Connect(plcIp, plcPort);
if (_isConnected == true)
{
MessageBox.Show("与西门子PLC连接成功!", "连接提示");
// 开启线程(创建取消线程对象)
_cts = new CancellationTokenSource();
// 开启线程(创建线程对象)
Task.Run(new Action(() =>
{
PlcCommunicatin();
}), _cts.Token);
}
else
{
MessageBox.Show("与西门子PLC连接失败!", "连接提示");
}
}
/// <summary>
/// 【方法】与PLC进行通信的线程方法
/// </summary>
private void PlcCommunicatin()
{
// 线程循环执行
while(!_cts.IsCancellationRequested)
{
// 通过ModbusTCP读取寄存器信息(PLC发送182字节->读取91个字)
byte[] readData = _modbusTcp.ReadOutputRegisters(0, 91);
// 读取对应的字节个数
if(readData != null && readData.Length == 182) // 读取成功
{
// 解析报文,跟新UI数据
Invoke(new Action(() => {
UpdateUI(readData, gb_Read);
UpdateUI(readData, gb_Write);
}));
}
}
}
/// <summary>
/// 【方法】
/// </summary>
/// <param name="data"> 被处理的数据字节数组 </param>
/// <param name="conrtol"> 控件对象 </param>
private void UpdateUI(byte[] data, Control control)
{
if (!control.HasChildren)
return;
// 程序走到这里说明:Control控件中包含一个或者多个子控件
foreach (var item in control.Controls.OfType<Label>())
{
// 排除掉一些标签控件
if (item.Tag == null || item.Tag.ToString().Length == 0)
continue;
// 程序走到这里说明:获取的控件是需要进行操作的控件
Variable variable = GetVariableByTag(item.Tag.ToString());
if (variable != null)
{
switch (variable.ValueType)
{
case DataType.Bool:
{
item.BackColor = BitLib.GetBitFromByteArray(data, variable.Start, variable.OffsetOrLength) == true ? Color.LimeGreen : Color.Red;
break;
}
case DataType.Byte:
{
item.Text = ByteLib.GetByteFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.Short:
{
item.Text = ShortLib.GetShortFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.UShort:
{
item.Text = UShortLib.GetUShortFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.Int:
{
item.Text = IntLib.GetIntFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.UInt:
{
item.Text = UIntLib.GetUIntFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.Float:
{
item.Text = FloatLib.GetFloatFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.Double:
{
item.Text = DoubleLib.GetDoubleFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.Long:
{
item.Text = LongLib.GetLongFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.ULong:
{
item.Text = ULongLib.GetULongFromByteArray(data, variable.Start).ToString();
break;
}
case DataType.String:
{
item.Text = StringLib.GetSiemensStringFromByteArray(data, variable.Start, variable.OffsetOrLength);
break;
}
}
}
}
}
/// <summary>
/// 【方法】解析Lable->Tag的内容,将其转换为Variable这个类
/// </summary>
/// <param name="tagText"></param>
/// <returns></returns>
private Variable GetVariableByTag(string tagText)
{
if (!tagText.Contains(";"))
return null;
// 程序走到这里说明该控件Tag内容是可以解析的
string[] values = tagText.Split(';'); // 分割字符串
if (values.Length != 3)
return null;
try
{
return new Variable()
{
ValueType = (DataType)Convert.ToInt32(values[0]),
Start = Convert.ToInt32(values[1]),
OffsetOrLength = Convert.ToInt32(values[2])
};
}
catch (Exception)
{
return null;
}
}
/// <summary>
/// 【控件事件】断开连接
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btn_DisConnect_Click(object sender, EventArgs e)
{
if (_isConnected == true)
{
_modbusTcp.DisConnect();
}
}
/// <summary>
/// 【控件事件】双击Label控件弹出数据修改对话框
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CommonModify_DoubleClick(object sender, EventArgs e)
{
if(sender is Label label)
{
if (label.Tag == null || label.Tag.ToString().Length <= 0)
return;
// 程序走到这里说明:Label的Tag的值可以解析
// 获取源值
string srcValue = label.Text;
// 获取Variable对象
Variable variable = GetVariableByTag(label.Tag.ToString());
FrmSiemensSet frmSiemensSet = new FrmSiemensSet(srcValue, variable, _modbusTcp);
frmSiemensSet.ShowDialog();
}
}
}
}
【2.4】FrmSiemensSet源码
using Approach.DataConvertLib;
using Approach.ModbusTCPLib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Approach.ModbusTCP
{
public partial class FrmSiemensSet : Form
{
private Variable _variable = null;
private ModbusTcp _modbusTcp = null;
public FrmSiemensSet(string srcValue, Variable variable, ModbusTcp modbusTcp)
{
InitializeComponent();
lbl_SrcValue.Text = srcValue;
lbl_ValueType.Text = variable.ValueType.ToString();
_variable = variable;
_modbusTcp = modbusTcp;
}
private void btn_Ok_Click(object sender, EventArgs e)
{
bool bResult = false;
try
{
switch (_variable.ValueType)
{
case DataType.Short:
{
bResult = _modbusTcp.PreSetSingleRegister((ushort)(_variable.Start / 2), Convert.ToInt16(txt_SetValue.Text.Trim()));
break;
}
case DataType.UShort:
{
bResult = _modbusTcp.PreSetSingleRegister((ushort)(_variable.Start / 2), Convert.ToInt16(txt_SetValue.Text.Trim()));
break;
}
case DataType.Int:
{
bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromInt(Convert.ToInt32(txt_SetValue.Text.Trim())));
break;
}
case DataType.UInt:
{
bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromUInt(Convert.ToUInt32(txt_SetValue.Text.Trim())));
break;
}
case DataType.Float:
{
bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromFloat(Convert.ToSingle(txt_SetValue.Text.Trim())));
break;
}
case DataType.Double:
{
bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromDouble(Convert.ToDouble(txt_SetValue.Text.Trim())));
break;
}
case DataType.Long:
{
bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromLong(Convert.ToInt64(txt_SetValue.Text.Trim())));
break;
}
case DataType.ULong:
{
bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromULong(Convert.ToUInt64(txt_SetValue.Text.Trim())));
break;
}
case DataType.String:
{
bResult = _modbusTcp.PreSetMultiRegisters((ushort)(_variable.Start / 2), ByteArrayLib.GetByteArrayFromSiemensString(txt_SetValue.Text.Trim()));
break;
}
default:
{
MessageBox.Show("不支持该数据类型写入", "参数设置");
return;
}
}
}
catch(Exception ex)
{
MessageBox.Show("请检查参数数据类型是否正确:" + ex.Message, "参数设置");
return;
}
if (bResult == true)
{
this.DialogResult = DialogResult.OK;
}
}
private void btn_Cancel_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
private void FrmSiemensSet_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
btn_Ok_Click(null, null);
}
}
}
【2.5】Variable源码
using Approach.DataConvertLib;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Approach.ModbusTCP
{
/// <summary>
/// 变量实体类封装
/// </summary>
public class Variable
{
/// <summary>
/// 标签名称
/// </summary>
public string ValueName { get; set; }
/// <summary>
/// 起始字节
/// </summary>
public int Start { get; set; }
/// <summary>
/// 数据类型
/// </summary>
public DataType ValueType { get; set; }
/// <summary>
/// (位偏移)或(字符串)长度
/// </summary>
public int OffsetOrLength { get; set; }
/// <summary>
/// 变量实际值
/// </summary>
public object Value { get; set; }
}
}