Dienstag, 22. Juli 2008

OPC: DataChange-Event

Ein OPC-Client hat die Möglichkeit, vom Server Nachrichten über Aktualisierungen von Werten zu empfangen. Das ist natürlich sehr nützlich, wenn man gerne eine bestimmte Aktion ausführen möchte, wenn ein definierter Wert erreicht wird. Kommt ja auch nicht gerade selten vor, diese Anforderung, also hat die OPC Foundation da schon ganz schön schlau definiert.

Nicht ganz so schlau ist jedoch die Umsetzung innerhalb der OpcDAAuto.dll gelungen. Ich habe heute etwa einen halben Tag gekämpft, bis ich tatsächlich in der Lage war, den DataChange-Event, der vom Server bei Veränderungen von zuvor dafür angemeldeten Werten gesendet wird, zu empfangen. Ich habe Tutorials gewälzt, das Internet durchforstet und experimentiert - alles ohne Erfolg. Alle Voraussetzungen, die ich finden konnte, schienen mir erfüllt. Nach ewigem Herumprobieren gab es nur noch einen einzigen Unterschied zwischen den Tutorials - die, heruntergeladen und kompiliert im Übrigen einwandfrei funktionierten - und meiner Applikation. Dieser fand sich beim Anlegen von OPC-Items, also jenen Werten, die ich überwachen wollte. Mein Code - im Übrigen eigentlich absolut korrekt - sah folgendermaßen aus:

   1:  OPCGroupClass opcGroupTriggerItems;
   2:   
   3:  // Gruppe anlegen
   4:  opcGroupTriggerItems = ( OPCGroupClass ) opcServer.OPCGroups.Add( "TriggerItems" );
   5:   
   6:  // Items hinzufügen
   7:  opcGroupTriggerItems.OPCItems.AddItem( connection + "/" + alias + "/TriggerItem1", 0 ) );
   8:  opcGroupTriggerItems.OPCItems.AddItem( connection + "/" + alias + "/TriggerItem2", 1 ) );
   9:  opcGroupTriggerItems.OPCItems.AddItem( connection + "/" + alias + "/TriggerItem3", 2 ) );
  10:  opcGroupTriggerItems.OPCItems.AddItem( connection + "/" + alias + "/TriggerItem4", 3 ) );
  11:   
  12:  // Event Handler Methode registrieren
  13:  // WICHTIG: Die Event Handler Methode wird erst nach dem hinzufügen der Items zur Gruppe registriert,
  14:  //          da sonst beim Hinzufügen für jedes Item ein Event ausgelöst wird.
  15:  opcGroupTriggerItems.DataChange += new DIOPCGroupEvent_DataChangeEventHandler( opcGroupTriggerItems_DataChange );

Hier wird zuerst eine Gruppe auf dem OPC-Server angelegt, zu der dann anschließend die Items hinzugefügt werden, die die Werte darstellen, die überwacht werden sollen. Um zu vermeiden, dass für alle Items ein initialer Event ausgelöst wird, der über die Änderung von "nicht vorhanden" nach "vorhanden" und die damit verbundene Wertänderung benachrichtigt, wird die Event-Handler-Methode erst nach dem Anlegen der nötigen Items registriert. So wurden die Items korrekt angelegt, sie konnten ausgelesen werden, aber die Benachrichtigung über Wertänderungen funktionierte, wie bereits erwähnt, überhaupt nicht.

Der Code, der mir von den Tutorials aufgezeigt wurde und der jetzt, nachdem ich ihn so auch in meiner Applikation habe, zu dem gewünschten Ergebnis führt, sieht so aus:

   1:  OPCGroupClass opcGroupTriggerItems;
   2:  string[] itemIds = new string[5];
   3:  int[] clientHandles = new int[5];
   4:  int[] serverHandles;
   5:  int[] errors;
   6:   
   7:  // Gruppe anlegen
   8:  opcGroupTriggerItems = ( OPCGroupClass ) opcServer.OPCGroups.Add( "TriggerItems" );
   9:   
  10:  // Items erstellen
  11:  itemIds.SetValue( connection + "/" + alias + "/TriggerItem1", 1 );
  12:  itemIds.SetValue( connection + "/" + alias + "/TriggerItem2", 2 );
  13:  itemIds.SetValue( connection + "/" + alias + "/TriggerItem3", 3 );
  14:  itemIds.SetValue( connection + "/" + alias + "/TriggerItem4", 4 );
  15:   
  16:  // ClientHandles erstellen
  17:  clientHandles.SetValue( 1, 1 );
  18:  clientHandles.SetValue( 2, 2 );
  19:  clientHandles.SetValue( 3, 3 );
  20:  clientHandles.SetValue( 4, 4 );
  21:   
  22:  // Items der Gruppe hinzufügen
  23:  opcGroupTriggerItems.OPCItems.AddItems( 4, ref itemIds, ref clientHandles, out serverHandles, out errors, null, null );
  24:   
  25:  // Event Handler Methode registrieren
  26:  // WICHTIG: Die Event Handler Methode wird erst nach dem hinzufügen der Items zur Gruppe registriert,
  27:  //          da sonst beim Hinzufügen für jedes Item ein Event ausgelöst wird.
  28:  opcGroupTriggerItems.DataChange += new DIOPCGroupEvent_DataChangeEventHandler( opcGroupTriggerItems_DataChange );

Auch hier wird zunächst eine Gruppe angelegt. Es werden hier jedoch die Items nicht, wie zuvor, einzeln der Gruppe hinzugefügt. Statt dessen werden ein String-Array mit den Werten und ein Integer-Array mit den Client-Handles, unter denen die Items eindeutig zu identifizieren sind, angelegt. Hier ist die Besonderheit zu beachten, dass der erste Wert des Arrays jeweils nicht beachtet wird, das Array wird immer nur von 1 bis max durchlaufen. Das hängt, so weit ich informiert bin, damit zusammen, dass die von mir verwendete OpcDAAuto.dll in VB geschrieben ist. Jedenfalls werden anschließend die Items und ihre Client-Handles gesamt an eine Methode übergeben, die sie der Gruppe hinzufügen. Anschließend wieder die Event-Handler-Methode registriert. Test anwerfen - funzt.  Sehr erstaunlich und für mich nicht wirklich nachvollziehbar, aber im Endeffekt ist es jetzt auch egal, es läuft ja nun :)

Keine Kommentare: