C#与西门子PLC通信:S7NetPlus和HslCommunication使用指南
西门子S7协议是用来和PLC进行通讯的一个协议,默认端口是102,数据会保存在一个个DB块中,比较经典的用法是一个DB块专门用来读取,一个用来写入。
DB(数据块)+ {块号}.DBX/DBD/DBW+{字节地址}.{位偏移}
1、数据块是存放全局变量的内存区域,类似数据库的表.
2、三种类型的字节地址是共用的,所以要特别注意不同数据类型占用的长度是不同的,否则会出现异常数据的情形。
提到字节排列就得提到不同的通信设备的排列方式是有区别的,像是C#,三菱PLC就是小端排列类似于ABCD,4个字母表示4个字节;西门子PLC是大端排列,顺序是DCBA,modbus等协议是不规则排列,顺序有BADC,CDAB。
DBX(data block bit) 按位访问,用于访问数据块中的单个位即Bool类型,例如: DB1.DBX0.1 表示数据块1的第0字节的第0位。
每个字节有8位,所以偏移量的范围是0~7.
DBW(Data Block Word)数据块字,用于访问16位的数据,例如:Word,int类型。由于1个字 = 16位也就是2个字节,所以地址必须是偶数,无需使用位偏移。
例如:DB1.DBW0表示数据块1的第0个字节开始的16位数据
DBD(data block double word)数据块双字,用于访问32位的数据,例如:DWord,Dint,Real类型。由于1个双字 = 32位也就是4个字节,所以地址必须是4的整数,无需使用位偏移。
例如:DB1.DBD0表示数据块1的第0个字节开始的32位数据
使用C#实现西门子S7通讯可以通过S7NetPlus,HslCommunication类库来实现
1、使用S7NetPlus实现读写数据
// 第一个参数是CPU的型号
// 第三个参数是PLC的机架(Rank)
// 第四个参数是CPU的插槽(Slot)
using (var plc = new Plc(CpuType.S71200, "127.0.0.1", 0, 1))
{
try
{
plc.Open();
if (plc.IsConnected == false)
{
this.textBox1.Text = this.textBox1 + "连接失败" + Environment.NewLine;
}
#region 写入数据
// 写入一个bool类型
plc.Write("DB1.DBX0.0", true);
// 写入一个无符号整数
ushort ushortValue = 3600;
plc.Write("DB1.DBW2.0", ushortValue);
// 写入一个浮点数
float floatValue = 3.14f;
plc.Write("DB1.DBD4", floatValue);
// 写入一个有符号整数
int intValue = 2025;
plc.Write("DB1.DBD8", intValue);
#endregion
#region 读取数据
// 读取一个bool类型
var is_am = (bool)plc.Read("DB1.DBX0.0");
this.textBox1.Text = this.textBox1.Text + "is_am=" + is_am + Environment.NewLine;
// 读取一个无符号的整数类型 - word 对应c#的就是ushort类型
var second = (ushort)plc.Read("DB1.DBW2.0");
this.textBox1.Text = this.textBox1.Text + "second=" + second + Environment.NewLine;
// 读取一个浮点数
var pie = ((uint)plc.Read("DB1.DBD4")).ConvertToFloat();
this.textBox1.Text = this.textBox1.Text + "pie=" + pie + Environment.NewLine;
// 读取一个有符号的整数
var year = ((uint)plc.Read("DB1.DBD8")).ConvertToInt();
this.textBox1.Text = this.textBox1.Text + "year=" + year + Environment.NewLine;
#endregion
}
catch (Exception ex)
{
this.textBox1.Text = this.textBox1 + ex.Message + Environment.NewLine;
}
}
2、使用HslCommunication实现读写功能
写入:
plc = new SiemensS7Net(SiemensPLCS.S1200, "127.0.0.1");
Random random = new Random();
var flag = await plc.WriteAsync("DB1.DBX0", random.Next(0, 2) == 0 ? false : true);
var int_value = await plc.WriteAsync("DB1.DBW2", (short)random.Next(0, 1000));
var float_value = await plc.WriteAsync("DB1.DBD4", (float)random.Next(0, 10) / 10.0f);
// 中文不能使用默认的Ascii编码,需要使用UTF8编码
var arr = new string[3] { "中", "美", "日" };
var string_value = await plc.WriteAsync("DB1.DBD8", arr[random.Next(0, 3)], Encoding.UTF8);
await Task.Delay(1000, cts.Token);
if (int_value.IsSuccess == false)
{
this.Invoke(new Action(() => {
this.textBox1.Text = "通讯失败" + Environment.NewLine + this.textBox1.Text;
}));
continue;
}
读取:
plc = new SiemensS7Net(SiemensPLCS.S1200, "127.0.0.1");
var flag = await plc.ReadBoolAsync("DB1.DBX0");
var int_value = await plc.ReadInt16Async("DB1.DBW2");
var float_value = await plc.ReadFloatAsync("DB1.DBD4");
// 读取中文不能使用默认的Ascii编码,需要使用UTF8编码
var string_value = await plc.ReadStringAsync("DB1.DBD8", Encoding.UTF8);
await Task.Delay(1000, cts.Token);
if (int_value.IsSuccess == false)
{
this.Invoke(new Action(() => {
this.textBox1.Text = "通讯失败" + Environment.NewLine + this.textBox1.Text;
}));
continue;
}
this.Invoke(new Action(() =>
{
this.textBox1.Text = "flag:" + flag.Content.ToString() + Environment.NewLine + this.textBox1.Text;
this.textBox1.Text = "float_value:" + float_value.Content.ToString() + Environment.NewLine + this.textBox1.Text;
this.textBox1.Text = "int_value:" + int_value.Content.ToString() + Environment.NewLine + this.textBox1.Text;
this.textBox1.Text = "string_value:" + string_value.Content.ToString() + Environment.NewLine + this.textBox1.Text;
}));