001准备工作
-
准备工作安装Visual Studio 2022
-
新建一个基于 windows 的窗体应用,其余选项跟着下一步进行
-
获取S7.NET 的函数库文件
点击该选项
检索,获得,安装 s7netplus 到你的项目中
-
引用 s7netplus 到命名空间中去
using S7.Net;//西门子通讯驱动包
using S7.Net.Types;
别忘了一些常用的库也要引用到项目中
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;
至此,基本的文件已经备妥
002建立S71200的通讯
S7.Net与 S7 200 S7 300 S7 400 S7 1200 和 S7 1500 兼容。本文以S71200为例子
public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot)
CpuType {S7200 = 0,S7300 = 10,S7400 = 20,S71200 = 30,S71500 = 40}
Ip: 包含外部以太网卡CPU 的 IP 地址
Rack:它包含PLC 的机架,您可以在 Step7 的硬件配置中找到该机架,可默认0。
Slot : 这是CPU 的插槽,您可以在 Step7 的硬件配置中找到它,可默认1。
//创建实例化
Plc S71200 = new(CpuType.S71200, "192.168.0.33", 0, 1);
带地址修改的实例化,请在设计器中配置好textBox控件的文本内容
//创建可以自定义CPU类型和IP地址的实例化
S71200 = new Plc(CpuType.S71200, txtAddress.Text, Convert.ToInt16(txtRack.Text), Convert.ToInt16(txtSlot.Text));
Plc 实例话后,会生成如下方法
-
S71200.Open(); 启动Plc通讯连接
-
S71200.Close(); 停止Plc通讯连接
-
S71200.IsConnected 该bool值可用于检查Plc是否处于通信连接中
003数据交互
-
西门子PLC处建立对应的DB块
-
创建你想要交互的实体类
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;
using S7.Net;//西门子通讯驱动包
using S7.Net.Types;
namespace S7LinkTest
{
internal class PlcShowDate: INotifyPropertyChanged//建立PlcDate的class ,并未其赋予接口属性(属性改变通知)
{
private float actPos_PN1;
public float ActPos_PN1
{get{ return actPos_PN1; }set{if (value != actPos_PN1){ actPos_PN1 = value; Change(nameof(ActPos_PN1)); }}}
private float actPos_PN2;
public float ActPos_PN2
{get { return actPos_PN2; }set { if (value != actPos_PN2) { actPos_PN2 = value; Change(nameof(ActPos_PN2)); } }}
private float actPos_PN3;
public float ActPos_PN3
{get { return actPos_PN3; }set { if (value != actPos_PN3) { actPos_PN3 = value; Change(nameof(ActPos_PN3)); } }}
private float actPos_PN4;
public float ActPos_PN4
{get { return actPos_PN4; }set { if (value != actPos_PN4) { actPos_PN4 = value; Change(nameof(ActPos_PN4)); } }}
private float actPos_PTO1;
public float ActPos_PTO1
{ get { return actPos_PTO1; } set { if (value != actPos_PTO1) { actPos_PTO1 = value; Change(nameof(ActPos_PTO1)); } } }
private float actPos_PTO2;
public float ActPos_PTO2
{ get { return actPos_PTO2; } set { if (value != actPos_PTO2) { actPos_PTO2 = value; Change(nameof(ActPos_PTO2)); } } }
private float actPos_PTO3;
public float ActPos_PTO3
{ get { return actPos_PTO3; } set { if (value != actPos_PTO3) { actPos_PTO3 = value; Change(nameof(ActPos_PTO3)); } } }
private float actPos_PTO4;
public float ActPos_PTO4
{ get { return actPos_PTO4; } set { if (value != actPos_PTO4) { actPos_PTO4 = value; Change(nameof(ActPos_PTO4)); } } }
private float actPos_PTO5;
public float ActPos_PTO5
{ get { return actPos_PTO5; } set { if (value != actPos_PTO5) { actPos_PTO5 = value; Change(nameof(ActPos_PTO5)); } } }
private float actPos_PTO6;
public float ActPos_PTO6
{ get { return actPos_PTO6; } set { if (value != actPos_PTO6) { actPos_PTO6 = value; Change(nameof(ActPos_PTO6)); } } }
private float actPos_PTO7;
public float ActPos_PTO7
{ get { return actPos_PTO7; } set { if (value != actPos_PTO7) { actPos_PTO7 = value; Change(nameof(ActPos_PTO7)); } } }
private float actPos_PTO8;
public float ActPos_PTO8
{ get { return actPos_PTO8; } set { if (value != actPos_PTO8) { actPos_PTO8 = value; Change(nameof(ActPos_PTO8)); } } }
#region 方法:属性改变通知
/*
*internal class PlcDate: INotifyPropertyChanged //INotifyPropertyChanged 通知客户端属性值已更改
* 建立委托:表示将处理 PropertyChanged 事件的方法,该事件在更改组件上的属性时引发。
*/
public event PropertyChangedEventHandler? PropertyChanged;//建立委托:表示将处理 PropertyChanged 事件的方法,该事件在更改组件上的属性时引发。
public void Change(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
#endregion 方法:属性&值改变通知
}
}
-
开始数据交互
private void BtnS7OpenLink_Click(object sender, EventArgs e)
{
S71200 = new Plc(CpuType.S71200, txtAddress.Text, Convert.ToInt16(txtRack.Text), Convert.ToInt16(txtSlot.Text));
if (!S71200.IsConnected)
{
try { S71200.Open(); }
catch (Exception ex)
{ MessageBox.Show(ex.Message); }
}
if (S71200.IsConnected)//通讯建立,建立多线程交互数据
{
try
{
Task.Run(() =>
{
while (true)
{
/*以下属性值受 属性改变通知 影响,会改变主线程的UI。
* 多线程运行的程序是不能直接修改主线程UI的
*/
this.Invoke(new Action(() => // 调用 this.Invoke(new Action(() => {******})); 可以修改主线程UI
{
if (S71200.IsConnected)
{
#pragma warning disable CS8605 // 取消装箱可能为 null 的值。
plcDate.ActPos_PN1 = ((uint)S71200.Read("DB61.DBD0")).ConvertToFloat();
plcDate.ActPos_PN2 = ((uint)S71200.Read("DB61.DBD4")).ConvertToFloat();
plcDate.ActPos_PN3 = ((uint)S71200.Read("DB61.DBD8")).ConvertToFloat();
plcDate.ActPos_PN4 = ((uint)S71200.Read("DB61.DBD12")).ConvertToFloat();
S71200.ReadClass(weightDate, 21);
#pragma warning restore CS8605 // 取消装箱可能为 null 的值。
}
}));
Thread.Sleep(200);
}
});
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
MessageBox.Show("通讯成功" + S71200.IP);
}
else { MessageBox.Show("连接失败"); }
}
在本项目中,该类中数据是设备的轴实际位置数据,需要在窗口中设置对应 label 控件来显示实际位置
该处位置显示用上了数据绑定的方法
public LinkControl()//初始化程序段落
{
InitializeComponent();
#region 轴数据数据绑定
labelPosX1.DataBindings.Add("Text", plcDate, "ActPos_PN1");
labelPosY1.DataBindings.Add("Text", plcDate, "ActPos_PN3");
labelPosX2.DataBindings.Add("Text", plcDate, "ActPos_PN2");
labelPosY2.DataBindings.Add("Text", plcDate, "ActPos_PN4");
#endregion 轴数据数据绑定
}
在这个数据绑定中我们不难发现 labelPosX1 的 Text 属性值是string,而ActPos_PN1的数据属性是float ,这两者值属性不同是不能直接绑定的,因此调用了一个当属性发生改变时,强制将 float 属性转变成 string ,然后把值赋予 labelPosX1.Text ,该方法函数如下,使用方法上面代码中也有展示。
#region 方法:属性改变通知
/*
*internal class PlcDate: INotifyPropertyChanged //INotifyPropertyChanged 通知客户端属性值已更改
* 建立委托:表示将处理 PropertyChanged 事件的方法,该事件在更改组件上的属性时引发。
*/
public event PropertyChangedEventHandler? PropertyChanged;//建立委托:表示将处理 PropertyChanged 事件的方法,该事件在更改组件上的属性时引发。
public void Change(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
#endregion 方法:属性&值改变通知
这个 IF 的的判断可以帮助我门在变量值改变时才启动数据转化操作,如果没有这种判断会损耗性能,加大程序的工作量。