天天看點

西門子PLC與C#程式的S7.net通信

1、導入S7.net通信檔案:打開工具->NuGet包管理器->管了解決方案得NuGet程式包,入下圖搜尋S7,安裝S7netplus。

西門子PLC與C#程式的S7.net通信
西門子PLC與C#程式的S7.net通信

在程式中引用S7.Net檔案

西門子PLC與C#程式的S7.net通信

建立西門子PLC類

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using S7.Net;

namespace 相機打開
{
    class SiemensPLC
    {
        //構造函數
        public SiemensPLC()
        {
            _plc = new Plc((CpuType)_plcType, _ipAddress, _crack, _slot);
            //_plc = new Plc(CpuType.S71500, _ipAddress, _crack, _slot);
        }
        /// <summary>
        /// PLC類型:S7200=0;Logo0BA8=1;S7200Smart=2;S7300=10,S7400=20;S71200=30;S71500=40;
        /// </summary>
        public int PLCType { get => _plcType; set => _plcType = value; }
        
        /// <summary>
        /// IP位址
        /// </summary>
        public string IpAdress { get => _ipAddress; set => IpAdress = value; }

        /// <summary>
        /// 機架
        /// </summary>
        public short Crack { get => _crack; set => _crack = value; }

        /// <summary>
        /// 槽
        /// </summary>
        public short Slot { get => _slot; set => _slot = value; }

        int _plcType=40;
        string _ipAddress="192.168.1.12";
        short _crack = 0;
        short _slot = 1;
        Plc _plc;

        /// <summary>
        /// PLC連接配接狀态
        /// </summary>
        /// <returns></returns>
        public bool ConnectState { get => _plc.IsConnected; }

        /// <summary>
        /// PLC連接配接
        /// </summary>
        public void Connect()
        {
            try
            {
                _plc.Open();
            }
            catch(Exception ex)
            {
                MessageBox.Show("PLC連接配接失敗:"+ex.Message.ToString());
            }
        }

        /// <summary>
        /// 連接配接
        /// </summary>
        public void Disconnect()
        {
            try
            {
                _plc.Close();
            }
            catch (Exception ex)
            {
                MessageBox.Show("PLC斷開失敗:" + ex.Message.ToString());
            }
        }

        /// <summary>
        /// 讀取
        /// </summary>
        /// <param name="varName"></param>
        /// <returns></returns>
        public object Read(string varName,string varType)
        {
            try
            {
                switch(varType)
                {
                    case "布爾":
                    case "整數":
                        return _plc.Read(varName);
                    case "實數":
                        return ((uint)_plc.Read(varName)).ConvertToFloat();
                    case "字元":
                        int dblength = varName.IndexOf(':') - 2;//DB号的長度
                        int db = Convert.ToInt32(varName.Substring(2, dblength));//DB号
                        int adrlength = varName.Length - varName.IndexOf(':') - 1;//startByteAdr的長度
                        int startByteAddress = Convert.ToInt32(varName.Substring(varName.IndexOf(':') + 1, adrlength));//startByteAdr

                        var count = (byte)_plc.Read(DataType.DataBlock, db, startByteAddress, VarType.Byte, 1);
                        return (string)_plc.Read(DataType.DataBlock, db, startByteAddress, VarType.S7String, count);
                    default:
                        return null;
                }                             
            }
            catch(Exception ex)
            {
                MessageBox.Show("讀取失敗:" + ex.Message.ToString());
                return null;
            }
        }

        /// <summary>
        /// 寫入
        /// </summary>
        /// <param name="varName"></param>
        /// <param name="varValue"></param>
        /// <param name="type"></param>位、位元組、單字、雙字和字元
        public void Write(string varName,string varValue,string type)
        {
            try
            {
                switch(type)
                {
                    case "位":
                        _plc.Write(varName, (varValue == "1") ? true : false);
                        break;
                    case "位元組":
                        _plc.Write(varName, Convert.ToByte(varValue));
                        break;
                    case "單字":
                        _plc.Write(varName, Convert.ToInt16(varValue));
                        break;
                    case "雙字":
                        _plc.Write(varName, Convert.ToInt32(varValue));
                        break;
                    case "實數":
                        _plc.Write(varName, Convert.ToSingle(varValue));
                        break;
                    case "字元":
                        
                        int dblength = varName.IndexOf(':') - 2;//DB号的長度
                        int db = Convert.ToInt32(varName.Substring(2,dblength));//DB号
                        int adrlength = varName.Length - varName.IndexOf(':')-1;//startByteAdr的長度
                        int startByteAddress = Convert.ToInt32(varName.Substring(varName.IndexOf(':')+1, adrlength));//startByteAdr

                        //var temp = System.Text.Encoding.ASCII.GetBytes(varValue);//編碼
                        var temp = System.Text.Encoding.Unicode.GetBytes(varValue);//編碼
                        var bytes = S7.Net.Types.S7String.ToByteArray(varValue, temp.Length);//位元組數組

                        _plc.WriteBytes(DataType.DataBlock, db, startByteAddress, bytes);
                        break;
                    default:
                        break;
                }
            }
            catch(Exception ex)
            {
                MessageBox.Show("寫入失敗:" + ex.Message.ToString());
            }
 
        }

    }
}

           

參考教程:https://www.cnblogs.com/xhubobo/p/14573092.html

https://www.cnblogs.com/minily/p/13324120.html

建立winform程式

西門子PLC與C#程式的S7.net通信
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using S7.Net;
using HalconDotNet;
using System.Diagnostics;


namespace 相機打開
{
    public partial class Form1 : Form
    {
        bool isMThread = false;//是否位多線程模式
        bool isStart = false;//線程模式啟動

        Task collectTask;//識别子線程

        // Local iconic variables 

        HObject ho_Image = null, ho_GrayImage = null;


        // Local control variables 

        HTuple hv_AcqHandle = null;//相機視窗句柄
        HTuple hv_WindowHandle = null;//視窗句柄
        HTuple hv_Width = new HTuple(), hv_Height = new HTuple();


        //PLC變量
        SiemensPLC PLC = new SiemensPLC();
        bool connectState = false;

        //線程變量
        Thread monitorThread;

        public Form1()
        {
            
            InitializeComponent();

            // Default settings used in HDevelop 
            HOperatorSet.SetSystem("width", 512);
            HOperatorSet.SetSystem("height", 512);
            if (HalconAPI.isWindows)
                HOperatorSet.SetSystem("use_window_thread", "true");
            action();


        }
        
        /// <summary>
        /// 視覺視窗初始化
        /// </summary>
        private void action()
        {
            // Initialize local and output iconic variables 
            HOperatorSet.GenEmptyObj(out ho_Image);
            HOperatorSet.GenEmptyObj(out ho_GrayImage);
            //Image Acquisition 01: Code generated by Image Acquisition 01

            //判斷視窗是否打開
            if(HDevWindowStack.IsOpen())
            {
                HOperatorSet.CloseWindow(HDevWindowStack.Pop());
            }
            HOperatorSet.SetWindowAttr("background_color", "black");
            hv_WindowHandle = this.hWindowControl1.HalconID;
            HOperatorSet.OpenWindow(0, 0, this.hWindowControl1.Width, this.hWindowControl1.Height, this.hWindowControl1.HalconWindow, "visible", "", out hv_WindowHandle);
            HDevWindowStack.Push(hv_WindowHandle);
        }

        /// <summary>
        /// 采用定時器方式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
            if(hv_AcqHandle==null)
            {
                return;
            }
            CollectImage();//圖像采集
        }

        /// <summary>
        /// 圖像采集
        /// </summary>
        private void CollectImage()
        {
            ho_Image.Dispose();
            if (hv_AcqHandle==null)
            {
                return;
            }
            HOperatorSet.GrabImageAsync(out ho_Image, hv_AcqHandle, -1);
            if (HDevWindowStack.IsOpen())
            {
                HOperatorSet.DispObj(ho_Image, HDevWindowStack.GetActive());
            }
            //HOperatorSet.WaitSeconds(0.1);
            ho_GrayImage.Dispose();
            HOperatorSet.Rgb1ToGray(ho_Image, out ho_GrayImage);
            HOperatorSet.DispObj(ho_GrayImage, HDevWindowStack.GetActive());
            HOperatorSet.GetImageSize(ho_GrayImage, out hv_Width, out hv_Height);
            HOperatorSet.SetPart(HDevWindowStack.GetActive(), 0, 0, hv_Height, hv_Width);
            //HOperatorSet.WaitSeconds(0.1);
        }

        /// <summary>
        /// 相機打開
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_OpenCamera_Click(object sender, EventArgs e)
        {
            //打開相機
            HOperatorSet.OpenFramegrabber("DirectShow", 1, 1, 0, 0, 0, 0, "default", 8, "rgb",
             -1, "false", "default", "[0] Integrated Camera", 0, -1, out hv_AcqHandle);
            HOperatorSet.GrabImageStart(hv_AcqHandle, -1);
        }

        /// <summary>
        /// 啟動停止按鈕
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_StartCollect_Click(object sender, EventArgs e)
        {
            if(this.rdb_Timer.Checked==true)
            {
                if (this.timer1.Enabled == false)
                {
                    this.timer1.Enabled = true;
                    this.btn_StartCollect.Text = "停止采集";
                    this.rdb_MThread.Enabled = false;
                    this.rdb_Timer.Enabled = false;
                }
                else
                {
                    this.timer1.Enabled = false;
                    this.btn_StartCollect.Text = "開始采集";
                    this.rdb_MThread.Enabled = true;
                    this.rdb_Timer.Enabled = true;
                }
            }
            else if(this.rdb_MThread.Checked==true)
            {
                if(this.btn_StartCollect.Text=="開始采集")
                {
                    isStart = true;
                    btn_StartCollect.Text = "停止采集";
                    this.rdb_MThread.Enabled = false;
                    this.rdb_Timer.Enabled = false;
                    //執行個體化子線程
                    collectTask = new Task(() =>
                    {
                        while (isStart)
                        {
                            CollectImage();
                        }
                    });
                    collectTask.Start();
                }
                else
                {
                    this.btn_StartCollect.Text = "開始采集";
                    this.rdb_MThread.Enabled = true;
                    this.rdb_Timer.Enabled = true;
                    isStart = false;
                }
                
            }
            else
            {
                MessageBox.Show("請先選擇模式!");
            }
           
        }

        /// <summary>
        /// 關閉窗體
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            Process.GetCurrentProcess().Kill();
        }

        private void btn_PLCConnect_Click(object sender, EventArgs e)
        {
            //連接配接線程
            ThreadPool.QueueUserWorkItem(t => PLC.Connect());


        }

        /// <summary>
        /// 退出按鈕
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Exit_Click(object sender, EventArgs e)
        {
            Process.GetCurrentProcess().Kill();
        }

        private void btn_Disconnect_Click(object sender, EventArgs e)
        {
            //斷開線程
            ThreadPool.QueueUserWorkItem(t => PLC.Disconnect());
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //監控線程
            monitorThread = new Thread(monitorFunc);
            monitorThread.Start();
        }

        private void btn_SingleRead_Click(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(t=>SingleRead());
        }

        /// <summary>
        /// 關閉相機
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_CloseCamera_Click(object sender, EventArgs e)
        {
            if (collectTask.Status == TaskStatus.Running || this.timer1.Enabled == true)
            {
                MessageBox.Show("請先停止采集!");
                return;
            }
            //關閉相機
            if (hv_AcqHandle != null)
            {
                HOperatorSet.CloseFramegrabber(hv_AcqHandle);
            }
            ho_Image.Dispose();
            ho_GrayImage.Dispose();
        }

        private void btn_Write_Click(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(t=>SingleWrite());
        }

        public void monitorFunc()
        {
            while(true)
            {
                connectState = PLC.ConnectState;
                this.Invoke((MethodInvoker)delegate
                {
                    lbl_ConnectState.Text = connectState ? "Connected" : "Disconnected";
                    btn_PLCConnect.Enabled = connectState ? false : true;
                    btn_Disconnect.Enabled = connectState ? true : false;
                    lbl_ConState.BackColor = connectState ? Color.Lime : Color.Red;
                });
                Thread.Sleep(100);
            }
        }

        public void SingleRead()
        {           
            connectState = PLC.ConnectState;
            this.Invoke((MethodInvoker)delegate
            {
                var value = PLC.Read(tbx_RSignleName.Text.Trim(), cbx_RTypeSelect.Text.Trim());
                tbx_RSingleValue.Text = Convert.ToString(value);
            });
        }

        public void SingleWrite()
        {
            this.Invoke((MethodInvoker)delegate
            {
                PLC.Write(tbx_WSignleName.Text.Trim(), tbx_WSingleValue.Text.Trim(), cbx_WTypeSelect.Text.Trim());
            });
           
        }
    }
}

           

裡面還包含一部分Halcon程式,最終的目的是實作PLC程式通過Halcon程式控制相機。

建立S71500PLC仿真

采用S7-PLCSIM Advanced 3.0

建立西門子博圖項目

西門子PLC與C#程式的S7.net通信
西門子PLC與C#程式的S7.net通信

提前設定電腦的PG端口

西門子PLC與C#程式的S7.net通信

在博圖中建立一個DB塊

西門子PLC與C#程式的S7.net通信

打開西門子仿真軟體

西門子PLC與C#程式的S7.net通信

這個仿真軟體用一段時間後就需要重新開機,否則博圖下載下傳程式時找不到端口,有時候還需要重新啟動博圖軟體才能正常下載下傳程式

西門子PLC與C#程式的S7.net通信

首先連接配接PLC,然後給DB1.DBX0.0指派1,觀察博圖的監控畫面

西門子PLC與C#程式的S7.net通信
西門子PLC與C#程式的S7.net通信

寫入成功

西門子PLC與C#程式的S7.net通信

讀取成功

分别嘗試位元組、單字、雙字寫入讀取都順利

實數讀寫

西門子PLC與C#程式的S7.net通信

比較麻煩的是字元串的讀取和寫入

西門子PLC有兩種字元串,一種是String,另一種是WString,前者是每一個字元由兩個位元組構成,大小為256個位元組;後者是每一個字元由四個位元組構成,大小為512個位元組。

西門子PLC與C#程式的S7.net通信

是以英文字元采用String,中文字元采用WString,雖然WString在博圖中無法檢視具體字元串,隻能看到編碼,但是不影響正常的寫入和讀取。

繼續閱讀