当前位置:   article > 正文

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

c# s7.net


前言

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


一、目的

通过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,
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

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

2.开启连接:Open()

源代码如下:

        /// <summary>
        /// Connects to the PLC and performs a COTP ConnectionRequest and S7 CommunicationSetup.
        /// </summary>
        public void Open()
  • 1
  • 2
  • 3
  • 4

3.断开连接:Close()

源代码如下:

        /// <summary>
        /// Close connection to PLC
        /// </summary>
        public void Close()
  • 1
  • 2
  • 3
  • 4

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

源代码如下:

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

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
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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#类型去自动分解成字符数组)
  • 1
  • 2
  • 3
  • 4
  • 5

三、通讯实例

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

1.先配置好plc端数据

在这里插入图片描述

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";
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述在这里插入图片描述
我配置的plc机架与机槽:
在这里插入图片描述

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 });
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

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";
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

在这里插入图片描述
此处需要说明为什么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";
            }

        }


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

在这里插入图片描述

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

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

测试代码与更改的s7.net源码下载


总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/290056
推荐阅读
相关标签
  

闽ICP备14008679号