我有一个使用OPC的线程的应用程序。该线程与OPC Factory Server建立连接并接收自动化通知。
OPC:v 3.40.2808.0
Langage: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的线程的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 ) ;
如何升级才能收到我的所有信息?
感谢您阅读=)
答案 0 :(得分:4)
此代码完全误解了如何使用TThread
。您的Execute
方法什么都不做,因此就应用程序设计而言,没有线程。所有GestOPC
函数都只是在调用它们的任何线程上执行。由于您使用的是Queue
和documentation explicitly states 警告:请勿在主线程中调用队列。 ,这是一个明显的错误。
当然,在这里调用Queue
可能不是错误。您还没有告诉我们关于您的OPC库的任何信息,当然某些 OPC库会在工作线程上引发事件。如果是这种情况,你可以通过运气避免错误 - 如果不是这种情况那么你就会有设计错误。只有您可以阅读库文档,确定是哪种情况。
这个应用程序的设计都是错误的。建议您暂时忘记TThread
,将TThread
变成普通班级,摆脱无用的Execute
方法,摆脱对Queue
的调用(和Synchronize
,如果你有的话),看看会发生什么。然后花一些时间来了解TThread
如何工作以及如何编写正确的多线程代码 - 特别是如果你不知道自己在做什么,首先让你的代码单线程运行,然后工作是必不可少的在线程模型上。这有助于分离由线程问题和代码本身引起的错误引起的错误。
此外,OPC服务器中丢失的数据也可能来自时间混叠。 OPC服务器以某种轮询速率对现场设备进行采样,轮询速率因系统和标签而异。该轮询速率可以是10-100ms或更多。如果现场设备中的值变化快于此速率,则OPC服务器可能会错过更改,因此许多基于事件的客户端不会引发更改通知。因此,如果必须确保观察它们,确保以某种方式锁定或排队快速更改的硬件状态至关重要。调试时,您应该确定这不是错过更改通知的来源。