C#与倍福PLC通信的方法与实例

503 阅读5分钟

前言

公司使用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、效果

最后附上运行效果图:

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:mrzono

出处:cnblogs.com/mrzono/p/15857885.html

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!