C#与西门子1500通讯案例——基于S7.net+

发布时间:2023-02-24 16:00

C#与西门子1500通讯案例——基于S7.net+

  • 前言
  • 一、目的
  • 二、S7.net+部分函数简介
    • 1.实例化:Plc(CpuType cpu, string ip, Int16 rack, Int16 slot)
    • 2.开启连接:Open()
    • 3.断开连接:Close()
    • 4.读确定地址(地址能反映数据大小):Read(string variable)
    • 5.读地址(自己输入相关参数):Read(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0)
    • 6.写地址:Write(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
  • 三、通讯实例
    • 1.先配置好plc端数据
    • 2.连接PLC通讯
    • 3.添加地址等数据,方便一次读取写入
    • 4.读取PLC数据
    • 5.写入PLC数据
  • 总结


前言

测试做完很久了,一直拖到现在才更新,给我自己一个大嘴巴。


一、目的

通过C#读写西门子对应存储区的数据。
支持PLC中大部分的常用类型:Bit, Byte, Word,DWord, Int,DInt,Real, LReal,String, WString:
要是有时间更新了,我再更新到这里。

二、S7.net+部分函数简介

源码下载地址:GitHub地址有兴趣的小伙伴可以详细研究一下,在此只对用到的函数做简要介绍。

1.实例化:Plc(CpuType cpu, string ip, Int16 rack, Int16 slot)

源代码如下:

    public Plc(CpuType cpu, string ip, Int16 rack, Int16 slot): this(cpu, ip, DefaultPort, rack, slot)
	//cpu:plc的类型,看下面代码
	//ip:指示连接plc的网口地址
	//rack:指示plc机架号
	//slot:指示plc机槽号
    public enum CpuType
    {
        S7200 = 0,
        Logo0BA8 = 1,
        S7200Smart = 2,
        S7300 = 10,
        S7400 = 20,
        S71200 = 30,
        S71500 = 40,
    }

关于rack(机架)与slot(机槽)的设定要和plc设定一致

2.开启连接:Open()

源代码如下:

        /// 
        /// Connects to the PLC and performs a COTP ConnectionRequest and S7 CommunicationSetup.
        /// 
        public void Open()

3.断开连接:Close()

源代码如下:

        /// 
        /// Close connection to PLC
        /// 
        public void Close()

4.读确定地址(地址能反映数据大小):Read(string variable)

源代码如下:

        public object? Read(string variable)
        //variable:plc中的数据地址表示,如:"DB1.DBX0.0", "DB20.DBD200", "MB20", "T45"等

5.读地址(自己输入相关参数):Read(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0)

源代码如下:

	public object? Read(DataType dataType, int db, int startByteAdr, VarType varType, int varCount, byte bitAdr = 0)
	//dtatType:plc内部对应的存储区域类型
	//db:指示地址号,如"DB1.DBX0.0"此处就为1
	//startByteAdr: 开始读取的位置。如:”DB10.DBW8“此处为8
	//varCount:连续读取当前类型的个数(string类型特例)
    public enum DataType
    {
        Input = 129,
        Output = 130,
        Memory = 131,
        DataBlock = 132,
        Timer = 29,
        Counter = 28
    }

6.写地址:Write(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)

源代码如下:

        public void Write(DataType dataType, int db, int startByteAdr, object value, int bitAdr = -1)
        //dtatType:plc内部对应的存储区域类型
        //db:指示地址号,如"DB1.DBX0.0"此处就为1
		//startByteAdr: 开始读取的位置。如:”DB10.DBW8“此处为8
		//value:写入plc的数据(内部会根据它原来的c#类型去自动分解成字符数组)

三、通讯实例

没有实际plc的可以通过仿真测试,具体配置可以转到我的另外两篇文章:
西门子——博图V16与PLCSIM Advanced仿真通讯配置(1500系列)
西门子——好用的通讯仿真通讯工具NetToPLCsim

1.先配置好plc端数据

C#与西门子1500通讯案例——基于S7.net+_第1张图片

2.连接PLC通讯

代码如下:

        private void btn_Connect_Click(object sender, EventArgs e)
        {
            if (plc == null)
            {
                plc = new Plc((CpuType)Enum.Parse(typeof(CpuType), cmbbx_plcType.Text),
                               tb_IP.Text,
                               Convert.ToInt16(tb_rack.Text),
                               Convert.ToInt16(tb_slot.Text));
            }
            if (!plc.IsConnected)
            {
                plc.Open();
                btn_Connect.Text = "断开";
                textBox2.Text = textBox2.Text + "打开成功!\r\n";
            }
            else
            {
                plc.Close();
                btn_Connect.Text = "链接";
                textBox2.Text = textBox2.Text + "连接关闭!\r\n";
            }
        }

C#与西门子1500通讯案例——基于S7.net+_第2张图片C#与西门子1500通讯案例——基于S7.net+_第3张图片
我配置的plc机架与机槽:
C#与西门子1500通讯案例——基于S7.net+_第4张图片

3.添加地址等数据,方便一次读取写入

代码如下:

        struct plcInfo 
        {
            public string DataPath;
            public VarType DataType;
            public int ByteCount;
        }
		//绑定数据的字典
        Dictionary<String, plcInfo> dic = new Dictionary<string, plcInfo>();

		private void Form1_Load(object sender, EventArgs e)
        {
            string[] cpuTypeArr = Enum.GetNames(typeof(CpuType));
            foreach (var item in cpuTypeArr)
            {
                cmbbx_plcType.Items.Add(item);
            }
            cmbbx_plcType.SelectedItem = CpuType.S71500.ToString();
            string[] dataTypeArr = Enum.GetNames(typeof(VarType));
            foreach (var item in dataTypeArr)
            {
                cmbbx_dataType.Items.Add(item);
            }
            cmbbx_dataType.SelectedItem = VarType.Int.ToString();

            dic.Add("bit", new plcInfo() { DataPath ="DB10.DBX0.0",DataType = VarType.Bit } );
            dic.Add("byte", new plcInfo() { DataPath = "DB10.DBB1", DataType = VarType.Byte });
            dic.Add("word", new plcInfo() { DataPath = "DB10.DBW2", DataType = VarType.Word } );
            dic.Add("dword", new plcInfo() { DataPath = "DB10.DBD4", DataType = VarType.DWord });
            dic.Add("int", new plcInfo() { DataPath = "DB10.DBW8", DataType = VarType.Int });
            dic.Add("dint", new plcInfo() { DataPath = "DB10.DBD10", DataType = VarType.DInt });
            dic.Add("real", new plcInfo() { DataPath = "DB10.DBD14", DataType = VarType.Real });
            dic.Add("lreal", new plcInfo() { DataPath = "DB10.DBX18.0", DataType = VarType.LReal } );
            dic.Add("string", new plcInfo() { DataPath = "DB10.DBX26.0", DataType = VarType.String } );
            dic.Add("s7wstring", new plcInfo() { DataPath = "DB10.DBX282.0", DataType = VarType.S7WString });
            //dic.Add("s5time", new plcInfo() { DataPath = "DB10.DBW794", DataType = VarType.S5Time });
            dic.Add("s5time", new plcInfo() { DataPath = "DB10.DBD794", DataType = VarType.S5Time });
        }

4.读取PLC数据

        private object GetValue(plcInfo plcInfo)
        {
            if (plc != null && plc.IsConnected)
            {
                var plcData = new PLCAddress(plcInfo.DataPath);
                VarType useType =  plcInfo.DataType;
                switch (useType)
                {
                    case VarType.Bit:
                        return plc.Read(plcInfo.DataPath);
                        break;
                    case VarType.Byte:
                        return plc.Read(plcInfo.DataPath);
                        break;
                    case VarType.Word:
                        return plc.Read(plcInfo.DataPath);
                        break;
                    case VarType.DWord:
                        return plc.Read(plcInfo.DataPath);
                        break;
                    case VarType.Int:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.Int, 1);
                        break;
                    case VarType.DInt:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.DInt, 1);
                        break;
                    case VarType.Real:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.Real, 1); 
                        break;
                    case VarType.LReal:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.LReal, 1);
                        break;
                    case VarType.String:
                        byte count = (byte)plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte + 1, VarType.Byte, 1);
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte + 2, VarType.String, count);
                        break;
                    case VarType.S7String:
                        byte S7StringCount = (byte)plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte + 1, VarType.Byte, 1);
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.S7String, S7StringCount);
                        break;
                    case VarType.S7WString:
                        short S7WStringCount = ((short)plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte + 2, VarType.Int, 1));
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.S7WString, S7WStringCount);
                        break;
                    case VarType.S5Time:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.S5Time, 1);
                        break;
                    case VarType.Counter:
                        break;
                    case VarType.DateTime:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.DateTime, 1);
                        break;
                    case VarType.DateTimeLong:
                        return plc.Read(plcData.DataType, plcData.DbNumber, plcData.StartByte, VarType.DateTimeLong, 1);
                        break;
                    default:
                        throw new Exception("无输入");
                        break;
                }
                return null;
            }
            else
            {
                return null;
            }
        }

        private void btn_Read_Click(object sender, EventArgs e)
        {
            foreach (var item in dic)
            {
                string showStr;
                showStr = GetValue(item.Value).ToString();
                textBox2.Text = textBox2.Text + item.Value.DataPath + "内容:" + showStr + "\r\n";
            }
        }

C#与西门子1500通讯案例——基于S7.net+_第5张图片
此处需要说明为什么Bit,Byte,Word与Dword可以直接用地址读取不会出错呢?
(可以尝试看一下int等其他数据用Read(plcInfo.DataPath)会出现什么情况)
1)首先地址样式必须与类型对应如DBX对应bit、DBB对应byte、DBW对应Word、DBD对应Dword;
2)这四个类型存储方式都特别简单,而且数据没有正负之分。

还有为何读取String与Wstring操作比较复杂(先看我另一篇文章了解西门子中两种数据的结构)
1)数据结构的特殊;
2)字符串类型实际是字符数组,所以在Read(dataType, db,startByteAdr, varType, varCount)中,varCount要特别注意是字符串字符的个数(Wstring是字符个数 * 2);

最后是关于开源代码作者的S7String类型,读取的数据其实和string类型一样,只是内部自动给数据做了处理

5.写入PLC数据

        private void SetValue(plcInfo plcInfo,object value) 
        {
            if (plc != null && plc.IsConnected)
            {
                var plcData = new PLCAddress(plcInfo.DataPath);
                VarType useType = plcInfo.DataType;

                switch (useType)
                {
                    case VarType.Bit:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte,Convert.ToBoolean(value));
                        break;
                    case VarType.Byte:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToByte( value));
                        break;
                    case VarType.Word:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToUInt16( value));
                        break;
                    case VarType.DWord:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToUInt32( value));
                        break;
                    case VarType.Int:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToInt16( value));
                        break;
                    case VarType.DInt:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToInt32( value));
                        break;
                    case VarType.Real:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, Convert.ToSingle( value));
                        break;
                    case VarType.LReal:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte,Convert.ToDouble( value));
                        break;
                    case VarType.String:
                        byte Count = Convert.ToByte(plcInfo.ByteCount == 0 ? 255 : plcInfo.ByteCount);
                        byte UsingCount = Convert.ToByte( value.ToString().Length);
                        byte[] stringArr = new byte[value.ToString().Length + 2];
                        stringArr[0] = Count;
                        stringArr[1] = UsingCount;
                        byte[] OstringArr = Encoding.ASCII.GetBytes(value.ToString());
                        for (int i = 0; i < OstringArr.Length; i++)
                        {
                            stringArr[2 + i] = OstringArr[i];
                        }
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, stringArr);
                        break;
                    case VarType.S7String:
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, value.ToString());
                        break;
                    case VarType.S7WString:
                        short wsCount = Convert.ToInt16(plcInfo.ByteCount == 0 ? 255 : plcInfo.ByteCount);
                        short wsUsingCount = Convert.ToInt16(value.ToString().Length);
                        byte[] wsstringArr = new byte[value.ToString().Length * 2 + 4];
                        wsstringArr[0] = Convert.ToByte(wsCount >> 8);
                        wsstringArr[1] = Convert.ToByte(wsCount & 0x00FF);
                        wsstringArr[2] = Convert.ToByte(wsUsingCount >> 8);
                        wsstringArr[3] = Convert.ToByte(wsUsingCount & 0x00FF);
                        byte[] wsOstringArr = Encoding.BigEndianUnicode.GetBytes(value.ToString());
                        for (int i = 0; i < wsOstringArr.Length; i++)
                        {
                            wsstringArr[4 + i] = wsOstringArr[i];
                        }
                        plc.Write(plcData.DataType, plcData.DbNumber, plcData.StartByte, wsstringArr);
                        break;
                    default:
                        break;
                }
            }
        }

        private void btn_Write_Click(object sender, EventArgs e)
        {
            object obj = new object();
            foreach (var item in dic)
            {

                switch (item.Value.DataType)
                {
                    case VarType.Bit:
                        obj = false;
                        SetValue(item.Value,obj );
                        break;
                    case VarType.Byte:
                        obj = 12;
                        SetValue(item.Value, obj);
                        break;
                    case VarType.Word:
                        obj = 100;
                        SetValue(item.Value, obj);
                        break;
                    case VarType.DWord:
                        obj = 100;
                        SetValue(item.Value, obj);
                        break;
                    case VarType.Int:
                        obj = 100;
                        SetValue(item.Value, obj);
                        break;
                    case VarType.DInt:
                        obj = 100;
                        SetValue(item.Value, obj);
                        break;
                    case VarType.Real:
                        obj = 10.22f;
                        SetValue(item.Value, obj);
                        break;
                    case VarType.LReal:
                        obj = 20.22;
                        SetValue(item.Value, obj);
                        break;
                    case VarType.String:
                        obj = "qwer";
                        SetValue(item.Value, obj);
                        break;
                    //case VarType.S7String:
                    //    break;
                    case VarType.S7WString:
                        obj = "嗡嗡嗡";
                        SetValue(item.Value, obj);
                        break;
                    case VarType.S5Time:
                        obj = 10;
                        SetValue(item.Value, obj);
                        break;
                    default:
                        break;
                }

                textBox2.Text = textBox2.Text + item.Value.DataPath + "写入:" + obj.ToString() + "\r\n";
            }

        }


C#与西门子1500通讯案例——基于S7.net+_第6张图片

此处需要注意的依旧的string与wstring这两个类型同样的先看(西门子——不同数据的存储方式),传输字符数组需要自己组合

再一个西门子PLC是大端模式(可看Unicode字符编码),需要注意传输Wstring时高低位的排序


总结

到此与西门子PLC的通讯测试就结束了,我只测试了西门子1500系列,若大家在其他系列测试有问题,可以留言我后台联系我。本文只研究了一些常用类型,基本都够用了,还有一些批量读取Struct(整个db块)的,我觉得没必要而且里面含有字符串(string和wstring)时也是个不小的麻烦。希望本文可以帮助到大伙。

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号