C#與倍福PLC通信的方法與實例


前言

公司使用CPP代碼與PLC通信, 通信方式為ADS(倍福開放的通信方式).
偶然間發現倍福的ADS通信還支持其他好多種語言, 恰好最近在用C#寫一些設備調試的小程序, 就嘗試了下用C#寫了一個demo程序, 可以做到控制一個小電機.
下面會對接口文件做一些說明, 並貼出實現代碼.

准備

首先需要在工程中引入倍福官方提供的動態庫, 位置在:C:\TwinCAT\AdsApi.NET\v4.0.30319, 當然如果你安裝的倍福版本和我不一致, 最后的版本號可能會有變化.
引入方式是在工程中右擊引用, 點擊添加, 然后去對應的文件夾下找到庫文件, 最后在代碼開頭using一下, 如下圖所示:

編碼

1. 相關接口簡介

這里用的類是TwinCAT.Ads.TcAdsClient, 主要是充當客戶端角色, 而PLC充當服務器.
工程中聲明這個類的實例后, 可以跳轉到接口文檔, 其中:
public void Connect(string netID, int srvPort); 與PLC建立連接, 參數分別是AmsNetId和AmsPort, 另外還有5個重載方法, 沒有本質區別.
public bool Disconnect(); 斷開與PLC的連接.
public int CreateVariableHandle(string variableName); 根據變量名稱建立通信句柄, 根據該句柄可以對指定變量進行讀寫.
public int Read(int variableHandle, AdsStream dataStream); 讀句柄指向的PLC變量, 內容存到AdsStream類型變量中, 該類型看起來是個stream, 從父類里看, 應該可以轉化成字符數組.
public int Read(long indexGroup, long indexOffset, AdsStream dataStream); 倍福把不同種類的變量用indexGroup區分, indexOffset則需要去倍福工程內尋找, 用起來挺麻煩的, 試過, 很快就刪了...
public object ReadAny(int variableHandle, Type type); 從句柄內讀取指定類型的數據, 返回結果也是用戶自己決定是什么, 比較方便地接口.
public void Write(int variableHandle, AdsStream dataStream); 與Read類似, 也有根據變量表索引去寫的, 不再贅述.
public void WriteAny(int variableHandle, object value); 與ReadAny類似
public int AddDeviceNotification(...); 添加綁定變量, 根據句柄或索引表綁定到PLC, 一般用於監控PLC變量的變化.
public int AddDeviceNotificationEx(...); 上面的拓展方法, 區別是下面這個沒有AdsStream類型參數.
public event AdsNotificationEventHandler AdsNotification; 委托列表, 可以加回調函數, 它會在上面被綁定的變量出現變化時觸發.
public event AdsNotificationEventHandler AdsNotificationEx; 類似.

2. PLC部分

本文主要講上位機的實現, 這部分簡單帶一下.
主函數調用子程序:TestAxisCtrl();, 該子程序里調用一個寫好的功能塊:AxisFb(), 所以接口名就是: "TestAxisCtrl.AxisFb.".

3. 上位機界面


中間的控制區是GroupBox, 根據連接狀態決定是否可用.
下面有一個狀態欄, 如果出現運行時錯誤會在這里打印.

4. 連接與斷開

首先是全局變量和Form_Load:

private TcAdsClient PLC_Client;
private string AmsNetId;
private readonly string PLC_IO_NAME = "TestAxisCtrl.AxisFb.";
private int VarHandle;
private int Enabled_NotifyHandle;
private int Done_NotifyHandle;
private int ActPos_NotifyHandle;
private int ActVel_NotifyHandle;
private bool AxisEnabled;
private bool AxisDone;
private double AxisActPos;
private double AxisActVel;
private void Form1_Load(object sender, EventArgs e)
{
     CheckForIllegalCrossThreadCalls = false;
     //create object
     PLC_Client = new TcAdsClient();
     AmsNetId = ConfigurationManager.AppSettings["AmsNetId"];
}

這里主要把實例和一些共用的屬性放到全局, 在界面加載時初始化實例, 並讀取配置文件中記載的AmsNetId.
然后就是連接和關閉按鈕對應的觸發函數:

private void ConnectBtn_Click(object sender, EventArgs e)
{
    try
    {
        PLC_Client.Connect(AmsNetId, 851);
        if (PLC_Client.IsConnected) { AxisCtrlBox.Enabled = true; }
    }
    catch(Exception Err)
    {
        ResultBox.Text += Err.Message + '\n';
    }
}
private void CloseBtn_Click(object sender, EventArgs e)
{
    try
    {
        PLC_Client.AdsNotificationEx -= ProcessOutput;
        PLC_Client.Disconnect();
        if (!PLC_Client.IsConnected) { AxisCtrlBox.Enabled = false; }
    }
    catch (Exception Err)
    {
        ResultBox.Text += Err.Message + '\n';
    }
}

這里比較簡單, 沒啥好解釋的.

5. 變量寫入(使能和動作)

這里有點區別, 因為PLC變量有些是電平保持的, 有些是邊沿觸發的. 對於邊沿的, 綁定click事件就行, 每次click都是上次結果的取反. 而邊沿信號就要用Down和Up事件, 比如定位操作, 鼠標按下發置true, 松開就置false.保證發送給PLC的是一個脈沖(這部分也要看PLC的接口實現方式, 也可以做成自復位的, 因為公司里都這么用, 就習慣了).
代碼如下:

private void WriteToPLC(string VarName, object Obj)
{
    VarHandle = PLC_Client.CreateVariableHandle(PLC_IO_NAME + VarName);
    PLC_Client.WriteAny(VarHandle, Obj);
    PLC_Client.DeleteVariableHandle(VarHandle);
}

這樣, 每個按鈕綁定的事件調用該函數就行, 省去了每次都寫一堆的麻煩:)

6. 變量反饋

倍福是支持事件觸發回調的, 實現方式是先加Notify, 綁定到指定的PLC變量, PLC變量發生改變時觸發(需要設置觸發模式等參數).
實現代碼如下:

Enabled_NotifyHandle = PLC_Client.AddDeviceNotificationEx(PLC_IO_NAME + "Enabled", AdsTransMode.OnChange, 100, 100, AxisEnabled, typeof(bool));
Done_NotifyHandle = PLC_Client.AddDeviceNotificationEx(PLC_IO_NAME + "Done", AdsTransMode.OnChange, 100, 100, AxisDone, typeof(bool));
ActPos_NotifyHandle = PLC_Client.AddDeviceNotificationEx(PLC_IO_NAME + "ActPos", AdsTransMode.OnChange, 100, 100, AxisActPos, typeof(double));
ActVel_NotifyHandle = PLC_Client.AddDeviceNotificationEx(PLC_IO_NAME + "ActVel", AdsTransMode.OnChange, 100, 100, AxisActVel, typeof(double));
PLC_Client.AdsNotificationEx += ProcessOutput;

上面4條是綁定了我想監控的PLC變量, 第5條綁定了自定義觸發函數ProcessOutput(). 每次這些變量發生改變就會觸發該函數, 該函數具體如下:

private void ProcessOutput(object sender, AdsNotificationExEventArgs e)
{
    //enabled?
    if (e.NotificationHandle == Enabled_NotifyHandle)
    {
        if ((bool)e.Value)
        {
            EnableBtn.Text = "Disable";
            EnabledBox.Checked = true;
        }
        else
        {
            EnableBtn.Text = "Enable";
            EnabledBox.Checked = false;
        }
    }
    //Done?
    else if (e.NotificationHandle == Done_NotifyHandle)
    {
        if ((bool)e.Value)
        {
            DoneBox.Checked = true;
        }
        else
        {
            DoneBox.Checked = false;
        }
    }
    //ActPos update
    else if (e.NotificationHandle == ActPos_NotifyHandle)
    { ActPosBox.Text = ((double)e.Value).ToString(); }
    //ActVel update
    else if (e.NotificationHandle == ActVel_NotifyHandle)
    { ActVelBox.Text = ((double)e.Value).ToString(); }
}

根據NotificationHandle確定是誰觸發了該函數, 然后跳到指定分支執行邏輯.

7. 效果

最后附上運行效果圖:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM