C#上位机实现西门子PLC标签通信
大家好!我是付工。
上位机与西门子PLC进行S7通信时,我们必须取消勾选优化的块访问,然后基于地址寻址来实现数据访问。
如果不想取消勾选,能不能实现标签通信呢?
答案是可以的,有两种方案,一种是OPCUA通信,另一种是S7CommPlus协议。
今天跟大家分享一下如何基于S7CommPlus实现标签通信。
界面设计
首先创建好Windows项目,框架采用.Net 8,界面设计如下所示:

程序运行后,输入PLC的IP地址或域名,点击建立连接,如果连接成功,文字会变成断开连接,连接成功后,点击变量配置,会弹出一个选择变量的界面,如下图所示:

选择要读取的变量后,关闭窗体,在主窗体中可以点击开始读取,界面上将显示读取变量的实时值,最终实现效果如下图所示:

实现过程
首先通过Nuget搜索S7CommPlus,选择下图勾选的通信库进行安装,关于该库的详细信息可以自行查阅。

1、首先创建通信库对象,建立连接编写代码如下:
private S7CommPlusConnection conn = new S7CommPlusConnection();
private bool isConnected = false;
public bool IsConnected
{
get { return isConnected; }
set
{
isConnected = value;
this.btn_Connect.Text = isConnected ? "断开连接" : "建立连接";
}
}
private void btn_Connect_Click(object sender, EventArgs e)
{
if (isConnected == false)
{
int res = conn.Connect(this.txt_IP.Text.Trim());
if (res == 0)
{
IsConnected = true;
}
else
{
MessageBox.Show("PLC连接失败", "PLC连接");
}
}
else
{
cts?.Cancel();
IsReading = false;
Thread.Sleep(100);
conn.Disconnect();
IsConnected = false;
}
}
2、连接成功后,点击变量管理,通过通信对象的Browse方法获取到所有的PLC变量,变量管理窗体的目的是为了选择部分需要读取的变量,测试环境下,也可以直接读取所有的变量,这样就不需要变量管理窗体了,代码如下所示:
private List<VarInfo> AllVarInfo = new List<VarInfo>();
private void btn_VarConfig_Click(object sender, EventArgs e)
{
if (IsConnected == false)
{
MessageBox.Show("请先连接PLC", "变量配置");
return;
}
// 获取标签列表
int res = conn.Browse(out AllVarInfo);
if (res == 0)
{
FrmVarConfig frmVarConfig = new FrmVarConfig(AllVarInfo.Select(c => c.Name).ToList());
DialogResult dialogResult = frmVarConfig.ShowDialog();
if (dialogResult == DialogResult.OK)
{
this.dgv_Data.Rows.Clear();
List<string> SelectList = frmVarConfig.SelList;
foreach (var item in SelectList)
{
VarInfo? info = AllVarInfo.Find(c => c.Name == item);
if (info != null)
{
int index = this.dgv_Data.Rows.Add();
this.dgv_Data.Rows[index].Cells[0].Value = info.Name;
this.dgv_Data.Rows[index].Cells[1].Value = info.AccessSequence;
this.dgv_Data.Rows[index].Cells[2].Value = Softdatatype.Types[info.Softdatatype];
}
}
}
}
else
{
MessageBox.Show("获取标签列表失败", "变量配置");
}
}
3、选择变量后,点击开始读取,开启一个多线程循环读取PLC数据,代码如下:
private CancellationTokenSource cts;
private void btn_Read_Click(object sender, EventArgs e)
{
if (IsConnected == false)
{
MessageBox.Show("请先连接PLC", "变量读取");
return;
}
if (this.btn_Read.Text == "开始读取")
{
List<ItemAddress> readlist = new List<ItemAddress>();
foreach (DataGridViewRow item in this.dgv_Data.Rows)
{
readlist.Add(new ItemAddress(item.Cells[1].Value.ToString()));
}
cts = new CancellationTokenSource();
Task.Run(new Action(() =>
{
while (cts.IsCancellationRequested == false)
{
lock (this.objlock)
{
List<object> values = new List<object>();
List<UInt64> errors = new List<UInt64>();
int res = conn.ReadValues(readlist, out values, out errors);
if (res == 0 && values.Count == readlist.Count)
{
for (int i = 0; i < readlist.Count; i++)
{
this.dgv_Data.Rows[i].Cells[3].Value = GetActualValue(values[i]);
}
}
else
{
Console.WriteLine("读取失败");
}
}
}
}));
this.btn_Read.Text = "停止读取";
}
else
{
cts?.Cancel();
this.btn_Read.Text = "开始读取";
}
}
4、上面涉及一个数据处理的方法,代码如下:
private object GetActualValue(object value)
{
if (value is ValueBool)
{
return ((ValueBool)value).GetValue();
}
else if (value is ValueUSInt)
{
return ((ValueUSInt)value).GetValue();
}
else if (value is ValueUSIntArray)
{
byte[] bytes = ((ValueUSIntArray)value).GetVa
return Encoding.GetEncoding("GBK").GetString(
}
else if (value is ValueByte)
{
return ((ValueByte)value).GetValue();
}
else if (value is ValueUInt)
{
return ((ValueUInt)value).GetValue();
}
else if (value is ValueUIntArray)
{
return ((ValueUIntArray)value).GetValue();
}
else if (value is ValueUDInt)
{
return ((ValueUDInt)value).GetValue();
}
else if (value is ValueULInt)
{
return ((ValueULInt)value).GetValue();
}
else if (value is ValueSInt)
{
return ((ValueSInt)value).GetValue();
}
else if (value is ValueInt)
{
return ((ValueInt)value).GetValue();
}
else if (value is ValueDInt)
{
return ((ValueDInt)value).GetValue();
}
else if (value is ValueLInt)
{
return ((ValueLInt)value).GetValue();
}
else if (value is ValueWord)
{
return ((ValueWord)value).GetValue();
}
else if (value is ValueDWord)
{
return ((ValueDWord)value).GetValue();
}
else if (value is ValueLWord)
{
return ((ValueLWord)value).GetValue();
}
else if (value is ValueReal)
{
return ((ValueReal)value).GetValue();
}
else if (value is ValueLReal)
{
return ((ValueLReal)value).GetValue();
}
else if (value is ValueTimestamp)
{
return ((ValueTimestamp)value).GetValue();
}
else if (value is ValueTimespan)
{
return ((ValueTimespan)value).GetValue();
}
else if (value is ValueRID)
{
return ((ValueRID)value).GetValue();
}
else if (value is ValueAID)
{
return ((ValueAID)value).GetValue();
}
else if (value is ValueBlob)
{
return ((ValueBlob)value).GetValue();
}
else if (value is ValueWString)
{
return ((ValueWString)value).GetValue();
}
else if (value is ValueStruct)
{
return ((ValueStruct)value).GetValue();
}
else
{
return string.Empty;
}
}
5、按照上述步骤编写程序,运行结果如下:
