C#与西门子1500通讯案例——基于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,
    }

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

2.开启连接:Open()

源代码如下:

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

3.断开连接:Close()

源代码如下:

        /// <summary>
        /// Close connection to PLC
        /// </summary>
        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端数据

在这里插入图片描述

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";
            }
        }

在这里插入图片描述在这里插入图片描述
我配置的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 });
        }

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";
            }
        }

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

        }


在这里插入图片描述

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

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

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


总结

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

  • 24
    点赞
  • 161
    收藏
    觉得还不错? 一键收藏
  • 35
    评论
基于C#、ASP.NET和SQL Server的农产品电商平台设计与实现是一个综合性的项目,涉及到前后端开发以及数据库设计。下面是该平台的基本架构和实现方法的概述。 1. 前端设计: 前端使用ASP.NET来实现,采用响应式设计,确保平台在不同设备上的可用性和用户体验。通过HTML、CSS和JavaScript来构建页面,并使用ASP.NET MVC框架实现页面的渲染和数据绑定。可以使用Bootstrap等前端框架来加快开发速度和提高用户界面的质量。 2. 后端开发: 后端使用C#语言开发,基于ASP.NET的Web API来实现接口的设计和实现。通过这些接口,前端可以和后端进行数据交互。在后端,可以使用Entity Framework作为ORM(对象关系映射)工具,以简化与数据库的交互,并提高数据访问的效率。同时,还可以使用ASP.NET的身份验证和授权功能,确保只有合法用户可以访问相应的资源。 3. 数据库设计: 使用SQL Server作为数据库管理系统,并进行合理的数据库设计。首先,需要明确平台的功能需求,例如商品管理、订单管理、用户管理等。根据这些功能需求,设计相应的实体表,并定义表之间的关系。此外,还需要考虑数据库的性能优化,例如索引的建立、查询优化等。 4. 功能实现: 基于平台的需求,依次实现不同的功能模块。例如,实现用户注册和登录功能,实现商品的浏览和购买功能,实现订单的生成和支付功能等等。在实现这些功能时,需要注意安全性和稳定性,并进行充分的测试和调试。 总结起来,基于C#、ASP.NET和SQL Server的农产品电商平台设计与实现需要充分考虑前端界面的交互性、后端接口的设计和实现、数据库的设计和优化以及功能的实现等方面。通过合理的架构设计和细致的实现,可以实现一个高性能、稳定可靠的农产品电商平台。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值