【问题标题】:How to have a good communication with OPC?如何与 OPC 进行良好的通信?
【发布时间】:2014-10-01 08:10:45
【问题描述】:

我有一个使用 OPC 的线程应用程序。此线程与 OPC 工厂服务器建立连接并接收自动化通知。

OPC : v 3.40.2808.0

语言:Pascal,IDE:Delphi XE 2

与服务器的连接良好,通知即将到来。但有时,服务并不完美,我的应用程序没有收到所有通知。

我不确定问题出在我的代码中,但请稍微解释一下:

GestOPC = class(TThread)
    contructor Create(suspendu:boolean);
    destructor Destroy;override;
    procedure C******; // Called by connexion
    private
        procedure Execute;override;
        procedure CallGestOPCMainLabelAtt(const s : string);// IU Calling 
        procedure Call****;
        procedure Call****;
        procedure Call****;
    public
        sCallIHM :string;
        ***: boolean;
        ***: LongInt;
        ThreadListNotif: TThreadList,
        ServeurIF : OPCServer;
        OPCDataCallBack:IOPCDataCallBack;
        Buffer_notif : ^TNotif;
        procedure connexion;
    end;

TOPCDataCallback = class ( TInterfacedObject, IOPCDataCallback)
public 
    function OnDataChange(.....)HResult;stdcall;
    function OnReadComplete(.....)HResult;stdcall;
    function OnWriteComplete(.....)HResult;stdcall;
    function OnCancelComplete(.....)HResult;stdcall;
end;

OPC's Thread的OnDataChange:接收通知的事件

function TOPCDataCallback.OnDataChange ( dwTransid : DWORD; hgroup:OPCHANDLE; hrMasterquality: HResult;
hrMastererror : HResult;dwCount : DWORD;phClientItems:POPCHANDLEARRAY;pvValues: POleVariantArray;
pwQualities:PWordArray; pftTimeStamps : PFileTimeArray; pErrors :PResultList ): HResult;

var 
ClientItems :POPCHANDLEARRAY;
Values: POleVariantArray;
Qualities : PWordArray;
i,iCountBadItem:integer;

Begin
    if not bClosing then ///It's to not do anything if the app is closing.
    begin
        //Initialisation 
        iCountBadItem := 0; //this is for count during debbugging all notification unreadable
        Result := s_OK;
        ClientItems := POPCHANDLEARRAY (phClientItems);  
        Values := POleVariantArray(pvValues); 
        for i := 0 to dwCount -1 do 
        begin 
            if Qualities[i] = OPC_QUALITY_GOOD then 
            begin
                new(ClientOPC.Buffer_notif); 
                ClientOPC.Buffer_notif^.groupe_handle.indice_type1 := hGroup;
                ClientOPC.Buffer_notif^.item_client := ClientItems[i];
                ClientOPC.Buffer_notif^.valeur_item := Value[i];
                ClientOPC.ThreadListNotif.Add(ClientOPC.Buffer_Notif);
                      //This list is used in another Thread with LockList for use the notification.
            end
            else 
                iCountBadItem := iCountBadItem+1;
         end;
      end;

我的 OPC 线程执行程序是空的,以确保我有一个良好的接收:

procedure GestOPC.Execute; 
begin
    NameThreadForDebugging('GestOPC');
    while (not Terminated) do 
    begin 
        sleep(100);
    end;
end;

我的线程是由我的主窗体的 FormCreate 事件创建的:

     ClientOPC := GestOPC.Create(false);
     ClientOPC.FreeOnTerminate := false;

连接是由 ButtonClick' 事件启动的:

     ClientOPC.Connexion;

函数连接从创建接收器TOPCDataCallBack开始,与Ofs建立连接,对数据库进行SQL调用,最后动态修改我的接口(OPC在线程上的原因是为了不冻结我的连接期间的接口)。

对于我的 IU Calling,我使用这个结构:

sCallIHM := "Ceci est un exemple de mon travail ;) ";
Queue( 
    procedure 
        begin 
        CallGestOPCMainLabelAtt( sCallIHM );
        end ) ;

我如何升级它才能接收我的所有消息?

感谢阅读 =)

【问题讨论】:

    标签: multithreading delphi opc


    【解决方案1】:

    这段代码完全误解了如何使用TThread。你的Execute 方法什么都不做,所以就应用程序设计而言没有线程。您的所有GestOPC 函数都只是在调用它们的任何线程上执行。由于您使用的是Queuedocumentation explicitly states 警告:不要从主线程中调用队列。,这是一个明显的错误。

    当然,在这里调用Queue 可能不是错误。您还没有告诉我们有关您的 OPC 库的任何信息,当然一些 OPC 库在工作线程上引发了事件。如果是这种情况,那么您已经幸运地避免了一个错误 - 如果不是这种情况,那么您就设计了一个错误。只有你可以阅读库文档来确定是哪种情况。

    这个应用程序的设计都是错误的。建议你暂时忘掉TThread,把你的TThread变成一个普通的类,去掉无用的Execute方法,去掉对Queue(和Synchronize,如果你有的话)的调用),看看会发生什么。然后花点时间了解TThread 是如何工作的以及如何编写正确的多线程代码——尤其是如果你不知道自己在做什么,那么首先让你的代码在单线程上工作是很重要的,然后在一个线程模型。这有助于区分由线程问题引起的错误和由代码本身引起的错误。

    此外,来自 OPC 服务器的数据丢失也可能来自时间混叠。 OPC 服务器以某种轮询速率对您的现场设备进行采样,该速率可能因系统和标签而异。该轮询速率可以在 10-100 毫秒或更多的量级上。如果现场设备中的值变​​化速度超过此速率,则 OPC 服务器可能会错过更改,因此许多基于事件的客户端不会发出更改通知。因此,如果您必须确保观察到快速变化的硬件状态,那么确保以某种方式锁定或排队是至关重要的。调试时,您应该确定这不是错过更改通知的来源。

    【讨论】:

    • 对不起,我是初学者^^'
    • @Guillaume 不要抱歉。开始时最重要的教训之一是不要一次尝试做太多事情 - 否则你最终会淹没在你不理解的问题中。从一个简单的解决方案开始,先让它发挥作用,然后再增加复杂性。
    • 对不起,我是初学者 ^^' 但是“不要从主线程调用队列”我没有在主线程上使用它,不是吗?起初,这个应用程序是一个多线程应用程序,我必须升级才能通过 MQTT 通信。我没有同步 IU 修改时没有冻结我的线程。所以如果我理解得很好,我会把我的连接和我的 OPCDataCallBack 放在我的主线程中吗?如果我这样做,我的问题是 SQL 调用,我的 UI 冻结时间太长而且非常长。创建此线程后,它确实改变了冻结,非常好。对不起我的英语..(这个网站与编辑开玩笑^^)
    • @Guillaume 不,如果您在按钮单击处理程序中调用ClientOPC.Connexion;,那么它肯定是在主线程上执行的。在工作线程上执行的TThread 中的唯一 代码是Execute 方法,并且当您调用MyThread.Create(false)(不创建挂起)或调用@987654337 时开始@(如果你 Create(true) - 创建暂停)。否则,TThread 只是一个普通类,它的方法在调用它们的任何线程上执行。如果一个方法没有在Execute 方法中被调用,它就不会在线程上运行。
    • @Guillaume 因此,如果您有现在更好的冻结问题,那是因为您的代码中发生了其他一些变化,而不是因为您在后台线程中调用这些方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    • 2020-04-02
    • 1970-01-01
    • 2011-03-26
    相关资源
    最近更新 更多