• 使用OPC与PLC通讯 一


    总结自己在opc与自控开发的经验。首先介绍OPC DA模式下的OPC各种操作。

    在使用opc时需要引用到 OPCDAAuto.dll 这个类库。

    在项目引用后需要注册这个类库,否则程序跑起来会报错,“未找到工厂类 。。。”

    将该dll文件放在任意目录下,建议在引用程序的的同级目录下。 

    在 cmd 控制台  输入regsvr32  Q:PLCDataIntegrationpackages1OPCDaAutoOPCDAAuto.dll 

    注册完成后电脑会提示注册成功,这时,就可以使用工具类中的方法啦。

    1.定义相关变量

            private OPCServer opcServer;
            private OPCGroups opcGroups;
            private OPCGroup opcGroup;
            private List<int> itemHandleClient = new List<int>();
            private List<int> itemHandleServer = new List<int>();
            private List<string> itemNames = new List<string>();
            private List<model> modelValues = new List<model>();
            private OPCItems opcItems;
            private OPCItem opcItem;
            private Dictionary<string, string> itemValues = new Dictionary<string, string>();
    

      

    2.使用opc从plc中读取数据。这个是使用OPC DAAuto中的Connect方法。Connect之前要先

    创建OPCServer 对象

    opcServer = new OPCServer(); 

    OPCServer.StartTime:服务器的启动时间

    OPCServer.CurrentTime:服务器的当前时间,各个客户端可以通过这个属性值完成一些同步的操作

    //strHostIP 主机IP,DA模式下通常为127.0.0.1;
    //strHostName opc服务名,通常为字符串,例如kepsserver 的opc名称为 Kepware.KepServerEX.V6
     private bool ConnectServer(string strHostIP, string strHostName)
            {
                try
                {
    opcServer = new OPCServer(); opcServer.Connect(strHostName, strHostIP); }
    catch (Exception ex) { SaveCommand("连接到OPC服务器失败!" + ex.Message); return false; } txtLog.Text += "连接到OPC服务器成功!" + " "; return true; }

    3.连接成功后就可以用opcServer这个对象了。

    根据自己开发OPCServer 和OPCClient的经验,这里的OPCGroups与OPCGroup可以用树来理解,OPCGroups下可以有很多OPCGroup,OPCGroup下添加很多测点item。

    OPC API 里AddItem 就是往OPCGroup下添加测点

    4.OPCGroups与OPCGroup

      opcGroups = opcServer.OPCGroups;
     opcGroup = opcGroups.Add("YoursGroupName");

    这里从OPCServer中获取OPCGroups信息,保持一致。

    而OPCGroups下的OPCGroup可自行添加,然后自己注册的测点属性都在这个组下面

    可以给OPCGroup设置如下属性,这将影响到这个组下面的测点数据采集频率等。

         private void SetGroupProperty(OPCGroup opcGroup, int updateRate)
            {
                opcGroup.IsActive = true;
                opcGroup.DeadBand = 0f;
                opcGroup.UpdateRate = updateRate;
                opcGroup.IsSubscribed = true;
            }
    UpdateRate是一个整型值,影响组中测点数据更新频率。相关属性如下
    OPCGroups.Count:下属组(Group)的数量
    DefaultGroupIsActive(新添加的OPC组的活动状态的默认值。默认初始值是活动状态)
    DefaultGroupUpdateRate(新添加的OPC组的默认数据更新周期,默认初始值是1000亳秒)、
    DefaultGrouPDeadband( 新添加的OPC组的默认不敏感区的默认值,即能引起数据变化的最小数值百分比,默认值是0%)
    DefaultGroupLocaleID(新添加的OPC组区域标识符的默认值)
    DefaultGroupTimeBias(新添加的OPC组的时间偏差的默认值)等。
    
    

    5.设置OPCGroup属性

      opcGroups = opcServer.OPCGroups;
      opcGroup = opcGroups.Add("MYOPCGROUP");
      SetGroupProperty(opcGroup, UpdateRateOPC);
      opcItems = opcGroup.OPCItems;

    6.注册opc监控测点。根据项目实际情况将监测点取出,以字符串数组传递。

            public void AddItems(string[] itemNamesAdded)
            {
                itemHandleServer.Clear();
    
                for (int i = 0; i < itemNamesAdded.Length; i++)
                {
                    itemNames.Add(itemNamesAdded[i]);
                    itemValues.Add(itemNamesAdded[i], "");
                }
                for (int j = 0; j < itemNamesAdded.Length; j++)
                {
                    try
                    {
                        itemHandleClient.Add((itemHandleClient.Count == 0) ? 1 : (itemHandleClient[itemHandleClient.Count - 1] + 1));
                        opcItem = opcItems.AddItem(itemNamesAdded[j], itemHandleClient[itemHandleClient.Count - 1]);
                        itemHandleServer.Add(opcItem.ServerHandle);
                    }
                    catch (Exception ex)
                    {
                        SaveCommand(itemNamesAdded[j] + ex.Message);
                    }
                }
            }

    7.注册读写或数据变化事件,opc的事件很多,常用的有以下几个。

    opcGroup.AsyncWriteComplete //测点写入完成后触发
    opcGroup.AsyncReadComplete //读取指令完成后触发
    opcGroup.DataChange  //opcc测点数值变化后触发,通常用于实时数据上报

     //注册事件,然后在自己的方法里实现相关操作
    opcGroup.AsyncWriteComplete += new DIOPCGroupEvent_AsyncWriteCompleteEventHandler(opcGroup_AsyncWriteComplete);
    opcGroup.DataChange
    += new DIOPCGroupEvent_DataChangeEventHandler(KepGroup_DataChange);
    opcGroup.AsyncReadComplete += new DIOPCGroupEvent_AsyncReadCompleteEventHandler(GroupAsyncReadComplete);

    //实现方法 KepGroup_DataChange

            /// <summary>
            /// 数据变动转储
            /// </summary>
            /// <param name="TransactionID"></param>
            /// <param name="NumItems"></param>
            /// <param name="ClientHandles"></param>
            /// <param name="ItemValues"></param>
            /// <param name="Qualities"></param>
            /// <param name="TimeStamps"></param>
            private void KepGroup_DataChange(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps)
            {
                string itemID = string.Empty;
    
                try
                {
                    string text = "hello rabbit";
                    int i = 0;
                    for (i = 1; i <= NumItems; i++)
                    {
                        //SetLogInfo($"{ItemValues.GetValue(i)}", Color.Green);
    
                        if (ItemValues.GetValue(i) == null) { continue; }
    
                        text = ItemValues.GetValue(i).ToString();
    
                        //SetLogInfo($"本次发布消息{text}", Color.Green);
    
                        itemID = opcItems.GetOPCItem(itemHandleServer[(int)ClientHandles.GetValue(i) - 1]).ItemID;
    
                        string message = itemID + ":" + text;
    
                         MessageBox.Show(mesage);
                    }
                    
                }
                catch (Exception ex)
                {                
                    SaveCommand(ex.Message + itemID);
                }
            }

    实现方法 GroupAsyncReadComplete

            private void GroupAsyncReadComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array ItemValues, ref Array Qualities, ref Array TimeStamps, ref Array Errors)
            {
    
                //SaveCommand("**********GroupAsyncReadComplete***********");
    
                string itemID = string.Empty;
                string empty = string.Empty;
    
                try
                {
    
                    int i = 0;
    
                    for (i = 1; i <= NumItems; i++)
                    {
                        //非空判断,防止程序异常中断
                        if (ItemValues.GetValue(i) != null)
                        {
                            empty = ItemValues.GetValue(i).ToString();
                        }
    
                        //获取监测点id
                        itemID = opcItems.GetOPCItem(itemHandleServer[(int)ClientHandles.GetValue(i) - 1]).ItemID;
    
                        string text = itemID + ":" + empty;
    
                        MessageBox.Show("读取完成,结果为"+text);
                    }
    
                    MessageBox.Show($"本次读取完成,共{i}个", Color.Blue, 2);
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"GroupAsyncReadComplete:{ex.Message},itemID:{ itemID }");
                }
            }

    7.使用监测数据,在成功读取到数据后就可以按自己的需求使用数据了,可以存到数据库,放到Redis,或者推到Rabbit等消息队列都没问题。

    8.通过opc写入测点值实现,远程控制plc

    其实远程控制,在代码层面来看并没有那么复杂,因为前面的工作都做好后,opc通道已经打开的情况下,控制只是一个写入操作。把指定的值写到指定的测点中,就是这么简单。实现控制难点不在控制本身,重点难点在于保证通信网络的稳定,和监测状态的

    实时返回。总而言之,控制本身不复杂,复杂在安全性和稳定型的保证上。使用opc多为IT与OT融合的情景之下,IT平台本身缺乏对底层设备的有效监管,需要借助于opc客户端充当代理中介角色。这时OPC客户端本身的稳定性,和时效型显得尤为重要。这点

    需要研发人员多多考虑。这点大家有好的方法可以评论分享下。

             //调用示例

              string[] array2 = {"LCU1.PLC_GATE1_OPEN"};//点号开启1号闸门
              string[] array3 = {"1"};

             AsyncWrite(array2, array3);      

    /// <summary> /// 异步写方法,支持批量操作也可以调用opc单点读写方法 /// </summary> /// <param name="writeItemNames"></param> /// <param name="writeItemValues"></param> public void AsyncWrite(string[] writeItemNames, string[] writeItemValues) { try { OPCItem[] array = new OPCItem[writeItemNames.Length]; for (int i = 0; i < writeItemNames.Length; i++) { for (int j = 0; j < itemNames.Count; j++) { if (itemNames[j] == writeItemNames[i]) { array[i] = opcItems.GetOPCItem(itemHandleServer[j]); break; } } } int[] array2 = new int[writeItemNames.Length + 1]; array2[0] = 0; for (int k = 1; k < writeItemNames.Length + 1; k++) { array2[k] = array[k - 1].ServerHandle; } Array ServerHandles = array2; object[] array3 = new object[writeItemNames.Length + 1]; array3[0] = ""; for (int l = 1; l < writeItemNames.Length + 1; l++) { array3[l] = writeItemValues[l - 1]; } Array Values = array3; opcGroup.AsyncWrite(writeItemNames.Length, ref ServerHandles, ref Values, out Array _, 2009, out int _); GC.Collect(); } catch (Exception ex) { MessageBox.Show(ex.Message); } }

    至此OPC基本操作都完成了,OPC提供的官方API很深奥,有很多可以学的东西。OPC现在已经是自动化领域,跨域提供数据的潮流了,尤其时OPC UA发布后,自动化(OT)与信息化(IT)日趋融合,opc在传统自动化企业转型中应该会担任比较重要的角色。

    所以OPC还是很值得一学的。

    分享一首好听的歌 错都错了 ~~~

  • 相关阅读:
    select2使用
    Jquery DataTables相关示例
    基于Cef内核的多店铺登录器(含源码)
    Navi.Soft31.产品.登录器(永久免费)
    基于JQuery EasyUI的WebMVC控件封装(含源码)
    EntityFrameWork实现部分字段获取和修改(含源码)
    基于Ado.Net的日志组件
    C#实现七牛云存储
    局域网内手机播放视频
    基于微软企业库的AOP组件(含源码)
  • 原文地址:https://www.cnblogs.com/motion/p/15035673.html
Copyright © 2020-2023  润新知