初識上位機(下):C#讀寫PLC數據塊數據

大家好,我是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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章