大家好,我是Edison。
作爲一個工業自動化領域的程序員,不懂點PLC和上位機,貌似有點說不過去。這裏我用兩篇小文帶你快速進入上位機開發領域。後續,我會考慮再出一個系列文章一起玩工控上位機。
上一篇:搭建PLC模擬仿真環境
複習一下
在上一篇中,我們通過PLCSIM Advanced軟件創建了一個虛擬的西門子S7-1500 PLC如下所示:
然後,我們創建了一個博途的自動化項目,和我們的虛擬PLC進行了組態。在編譯完成後,我們創建的數據塊中的數據字段就得到了偏移量,如下圖所示,0,2,4, 260就是所謂的偏移量,會在後面用到。
創建Windows Form項目
這裏開始我們就開始使用C#創建一個Windows Form項目,然後通過S7NetPlus庫來連接PLC,並讀取和寫入數據塊中的數據,這是一個典型的上位機數據採集的場景。這裏我們創建一個.NET Framework 4.8的Windows Form項目,並拖控件完成一個如下圖所示的窗體應用界面:
這個窗體提供了連接和斷開PLC,以及讀取 和 寫入 文本框中的數據,接下來我們就來實現這幾個功能。
實現PLC的連接與斷開
要實現S7 PLC的連接和操作,目前已經有很多較爲成熟的組件了,我們這裏使用S7NetPlus組件,直接通過NuGet安裝最新版本即可。
然後編寫Connect按鈕的Click事件如下:
private static Plc s7Instance; public MainForm() { InitializeComponent(); } private void btnConnect_Click(object sender, System.EventArgs e) { if (btnConnect.Text == "Connect") { if (s7Instance == null) s7Instance = new Plc(CpuType.S71500, txtPlcIPAddress.Text.Trim(), 0, 1); s7Instance.Open(); btnConnect.Text = "Disconnect"; } else { s7Instance.Close(); btnConnect.Text = "Connect"; txtBool01.Clear(); txtInt01.Clear(); txtStr01.Clear(); txtStr02.Clear(); } }
實現PLC數據塊的讀取
由於我們在博途項目中設置的數據塊是DB01,且只有4個字段,所以這裏我們編寫ReadData按鈕的Click事件如下,它通過指定參數讀取到指定類型的數據並綁定到文本框的Text中。
private void btnReadData_Click(object sender, System.EventArgs e) { if (s7Instance == null || !s7Instance.IsConnected) { MessageBox.Show("Your PLC is not connected now!", "Error", MessageBoxButtons.OK); return; } // bool var boolData = (bool)s7Instance.Read(DataType.DataBlock, 1, 0, VarType.Bit, 1); txtBool01.Text = boolData ? "1" : "0"; // int var intData = (short)s7Instance.Read(DataType.DataBlock, 1, 2, VarType.Int, 1); txtInt01.Text = intData.ToString(); // string var count = (byte)s7Instance.Read(DataType.DataBlock, 1, 4 + 1, VarType.Byte, 1); // +1表示讀取偏移值的長度 var str01Data = Encoding.Default.GetString(s7Instance.ReadBytes(DataType.DataBlock, 1, 4 + 2, count)); // +2表示讀取偏移值(跳過)的字符 txtStr01.Text = str01Data; // wstring var str02Data = (string)s7Instance.Read(DataType.DataBlock, 1, 260, VarType.S7WString, 254); txtStr02.Text = str02Data; }
要點解讀:
(1)針對bool和int類型,我們可以直接通過Read方法快速讀取到,但需要告訴PLC準確的讀寫位置和數據類型,主要是偏移量一定要給正確。
Read方法的參數分別爲數據塊類型,數據塊,偏移量,讀取類型,讀取長度
(2)針對string和wstring類型,就稍微麻煩一些了:針對string,需要先獲取string值的所佔長度。再拿到具體byte值。轉換爲utf8格式的ascci碼,具體代碼中有體現。
+1 表示獲取到長度
+2 表示獲取到跳過偏移長度的字符
特別注意:string類型只能存儲ascci碼,需要注意,不能存儲中文!
針對wstring,稍微簡單點,但是需要注意的是獲取的字符需要爲254個,因爲符號佔用了4個字節。
實現PLC數據塊的寫入
和讀取一樣,通過Write方法即可輕鬆實現寫入,但針對string和wstring仍然是複雜一些,這裏我封裝了一個S7DataWriter的靜態類,提供了兩個方法來獲取要寫入的bytes,因爲它無法直接接收C#程序中的string類型。
public static class S7DataWriter { /// <summary> /// 獲取西門子PLC字符串數組--String類型 /// </summary> public static byte[] GetPlcStringByteArray(string str) { byte[] value = Encoding.Default.GetBytes(str); byte[] head = new byte[2]; head[0] = Convert.ToByte(254); head[1] = Convert.ToByte(str.Length); value = head.Concat(value).ToArray(); return value; } /// <summary> /// 獲取西門子PLC字符串數組--WString類型 /// </summary> public static byte[] GetPlcWStringByteArray(string str) { byte[] value = Encoding.BigEndianUnicode.GetBytes(str); byte[] head = BitConverter.GetBytes((short)508); byte[] length = BitConverter.GetBytes((short)str.Length); Array.Reverse(head); Array.Reverse(length); head = head.Concat(length).ToArray(); value = head.Concat(value).ToArray(); return value; } }
然後,我們就可以編寫Write Data按鈕的Click事件了:
private void btnWriteData_Click(object sender, System.EventArgs e) { if (s7Instance == null || !s7Instance.IsConnected) { MessageBox.Show("Your PLC is not connected now!", "Error", MessageBoxButtons.OK); return; } // bool var boolValue = txtBool01.Text.Trim() == "1" ? true : false; s7Instance.Write(DataType.DataBlock, 1, 0, boolValue); // int var intValue = Convert.ToInt16(txtInt01.Text); s7Instance.Write(DataType.DataBlock, 1, 2, intValue); // string s7Instance.Write(DataType.DataBlock, 1, 4, S7DataWriter.GetPlcStringByteArray(txtStr01.Text.Trim())); // wstring s7Instance.Write(DataType.DataBlock, 1, 260, S7DataWriter.GetPlcWStringByteArray(txtStr02.Text.Trim())); MessageBox.Show("Write data successfully!", "Info", MessageBoxButtons.OK); }
效果演示
和讀取一樣,通過Write方法即可輕鬆實現寫入,但針對string和wstring仍然是複雜一些,這裏我封裝了一個S7DataWriter的靜態類,提供了兩個方法來獲取要寫入的bytes,因爲它無法直接接收C#程序中的string類型。
(1)讀取數據
(2)寫入數據
小結
本文通過使用C#開發了一個簡單的WindowsForm窗體程序,實現了S7 PLC的連接、數據讀取和寫入。雖然只是一個簡單的Demo,但是從中可以看見上位機的基本思想,就是對PLC的數據採集和監控。當然,實現這個目的,不止S7協議一條路,我們還可以通過ModBus、OPC UA等協議,這些就留到後面的專題吧,如果你感興趣的話,就保持關注哦!
源碼
GitHub:https://github.com/Coder-EdisonZhou/PLC-Connectors