Ereignisgesteuertes Lesen

Sollen in einer Bedieneroberfläche kontinuierlich Werte aus der SPS oder NC angezeigt werden, so ist das Benutzen von AdsSyncReadReq() sehr aufwendig, da diese Funktion zyklisch aufgerufen werden muss. Durch das Definieren sogenannter Notifications (Meldungen) kann ein TwinCAT Server dazu veranlasst werden, Werte über ADS an ein anderes ADS-Gerät zu übertragen. Hierbei wird unterschieden, ob der TwinCAT Server die Werte zyklisch oder nur bei Veränderung übertragen soll.

Mit der Funktion AdsSyncAddDeviceNotificationReq() wird eine Notification erzeugt. Die Callback-Funktion wird anschließend selbständig von TwinCAT aufgerufen. Mit AdsSyncDelDeviceNotificationReq() wird die Notification wieder gelöscht. Da die Anzahl der Notifications begrenzt ist, sollten Sie in Ihrem Programm dafür sorgen, das nicht mehr benötigte Notifications gelöscht werden. Weitere Informationen finden Sie bei der Beschreibung zur Struktur AdsNotificationAttrib.

Das folgende Programm startet eine Notification auf dem Handle einer Variablen. Bei jeder Änderung der SPS-Variablen, wird die Callback-Funktion aufgerufen. Als Parameter enthält die Callback-Funktion unter anderem eine Variable vom Typ AdsNotificationHeader. In dieser Struktur sind alle notwendigen Informationen (Wert, Zeitstempel, ...) enthalten.

Download: sample08-c-ads-dll-eventdrivenreading.zip

Hinweis

Zeitintensive Aktionen oder ADS-Befehle

Im Callback dürfen keine zeitintensiven Aktionen oder ADS-Befehle ausgeführt werden.

#include <iostream>
#include <conio.h>
#include <windows.h>
#include <winbase.h>

// ADS headers
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsDef.h"
#include "C:\TwinCAT\AdsApi\TcAdsDll\Include\TcAdsApi.h"

using namespace std;

void _stdcall Callback(AmsAddr*, AdsNotificationHeader*, unsigned long);

void main()

  long           nErr, nPort; 
  AmsAddr        Addr; 
  PAmsAddr           pAddr = &Addr; 
  ULONG          hNotification, hUser; 
  AdsNotificationAttrib  adsNotificationAttrib;
  char      szVar []={"MAIN.PlcVar"};

  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';
  pAddr->port = 801;

  // Set the attributes of the notification
  adsNotificationAttrib.cbLength = 4;
  adsNotificationAttrib.nTransMode = ADSTRANS_SERVERONCHA;
  adsNotificationAttrib.nMaxDelay = 0;
  adsNotificationAttrib.nCycleTime = 10000000; // Unit = 100ns, 1sec = 10000000

  // Get variable handle
  nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(hUser), &hUser, sizeof(szVar), szVar);
  if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';

  // Initiate the transmission of the PLC-variable 
  nErr = AdsSyncAddDeviceNotificationReq(       pAddr,
                    ADSIGRP_SYM_VALBYHND, 
                    hUser, 
                    &adsNotificationAttrib, 
                    Callback, 
                    hUser, 
                    &hNotification);

  if (nErr) cerr << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << '\n';
  cout.flush();

  // Wait for user intraction (keystroke)
  _getch();

  // Finish the transmission of the PLC-variable 
  nErr = AdsSyncDelDeviceNotificationReq(pAddr, hNotification);
  if (nErr) cerr << "Error: AdsSyncDelDeviceNotificationReq: " << nErr << '\n';

  // Release  the variable handle
  nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(hUser), &hUser); 
  if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

  // Close the communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}

// Callback-function
void __stdcall Callback(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser)
{
  int             nIndex; 
  static ULONG        nCount = 0; 
  SYSTEMTIME          SystemTime, LocalTime; 
  FILETIME        FileTime; 
  LARGE_INTEGER       LargeInteger; 
  TIME_ZONE_INFORMATION   TimeZoneInformation; 

  cout << ++nCount << ". Notification:\n"; 

  // print (to screen)) the value of the variable 
  cout << "Value: " << *(ULONG *)pNotification->data << '\n'; 
  cout << "Notification: " << pNotification->hNotification << '\n';

  // Convert the timestamp into SYSTEMTIME
  LargeInteger.QuadPart = pNotification->nTimeStamp;
  FileTime.dwLowDateTime = (DWORD)LargeInteger.LowPart;
  FileTime.dwHighDateTime = (DWORD)LargeInteger.HighPart;
  FileTimeToSystemTime(&FileTime, &SystemTime);

  // Convert the time value Zeit to local time
  GetTimeZoneInformation(&TimeZoneInformation);
  SystemTimeToTzSpecificLocalTime(&TimeZoneInformation, &SystemTime, &LocalTime);

  // Print out the timestamp
  cout << LocalTime.wHour << ":" << LocalTime.wMinute << ":" << LocalTime.wSecond << '.' << LocalTime.wMilliseconds;
 
  // Print out the ADS-address of the sender
  cout << "\nServerNetId: ";
  for (nIndex = 0; nIndex < 6; nIndex++)
    cout << (int)pAddr->netId.b[nIndex] << ".";
  cout << " Port: " << pAddr->port << "\n\n";
  cout.flush();
}