协议实战 · 2026-03-06

C#上位机实现西门子PLC标签通信

大家好!我是付工。

上位机与西门子PLC进行S7通信时,我们必须取消勾选优化的块访问,然后基于地址寻址来实现数据访问。

如果不想取消勾选,能不能实现标签通信呢?

答案是可以的,有两种方案,一种是OPCUA通信,另一种是S7CommPlus协议。

今天跟大家分享一下如何基于S7CommPlus实现标签通信。

界面设计

首先创建好Windows项目,框架采用.Net 8,界面设计如下所示:

原文配图 1

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

原文配图 2

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

原文配图 3

实现过程

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

原文配图 4

‌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、按照上述步骤编写程序,运行结果如下:

原文配图 5
本文为原文文字快照,已收录 5 张技术配图;二维码与推广图未收录。
标签:#S7 #标签通信