Event-driven reading

If values from a PLC or NC are to be displayed continuously on a user interface, then it is very inefficient to use AdsSyncReadReq(), since this function must be called cyclically. By defining so-called notifications, a TwinCAT server can be caused to transfer values via ADS to another ADS device. A distinction is drawn between whether the TwinCAT server is to transmit the values cyclically, or only when the values change.

A notification is created with the AdsSyncAddDeviceNotificationReq() function. After this, the callback function is automatically invoked by TwinCAT. AdsSyncDelDeviceNotificationReq() is used to delete the notification again. Since the number of notifications is limited, you should ensure the notifications no longer required by your program are deleted. You will find further information under the description of the AdsNotificationAttrib structure.

The following program starts a notification on the handle of a variable. Each time the PLC variable changes, the callback function is invoked. As parameter the callback function contains among others a variable of type AdsNotificationHeader. This structure contains all the necessary information (value, timestamp, ...).

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

Notice

Time-intensive actions or ADS commands

No time-intensive actions or ADS commands may be executed in the callback.

#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();
}