Detecting state change in TwinCAT router and the PLC

When an application is actually running it is often important to interrogate the state of TwinCAT and/or of its components; e.g., whether the PLC is in the RUN state. So that this does not have to be queried constantly, a change in state can be detected in a very effective way with the aid of callback functions.
The following sample program monitors the state of the PLC (runtime system 1) and that of the TwinCAT router.
By calling the function AdsAmsRegisterRouterNotification() the specified callback function is called whenever the state of the TwinCAT router changes. By means of the parameter that is passed, the current state can be queried.
With the help of the function AdsSyncAddDeviceNotificationReq() the state of the PLC is monitored. The data that is passed to the callback function represents the current state of the PLC.

Download: sample11-c-ads-dll-detectstatuschanges.zip

#include <iostream.h>
#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"

void __stdcall Callback(AmsAddr*, AdsNotificationHeader*, ULONG);
void __stdcall RouterCall(LONG);

void main()
{
  LONG            nErr, nPort;
  ULONG           hNotification, hUser = 0;
  AmsAddr         Addr;
  PAmsAddr        pAddr = &Addr;
  AdsNotificationAttrib   adsNotificationAttrib;
  
  // Open communication port on the ADS router
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';
  
  // Select Port: TwinCAT2 PLC1
  pAddr->port = 801;
 
  nErr = AdsAmsRegisterRouterNotification(&RouterCall);
  if (nErr) cerr << "Error: AdsAmsRegisterRouterNotification: " << nErr << '\n';

  // Invoke notification
  adsNotificationAttrib.cbLength       = sizeof(short);
  adsNotificationAttrib.nTransMode     = ADSTRANS_SERVERONCHA;
  adsNotificationAttrib.nMaxDelay      = 0; // report each change directly
  adsNotificationAttrib.dwChangeFilter = 0;
  nErr = AdsSyncAddDeviceNotificationReq(pAddr, ADSIGRP_DEVICE_DATA, ADSIOFFS_DEVDATA_ADSSTATE, &adsNotificationAttrib, Callback, hUser, &hNotification);
  if (nErr) cerr << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << "\n";
  getch();
 
  // The following calls return errors if TwinCAT is halted
  nErr = AdsSyncDelDeviceNotificationReq(pAddr, hNotification);
  if (nErr) cerr << "Error: AdsSyncDelDeviceNotificationReq: " << nErr << '\n';

  nErr = AdsAmsUnRegisterRouterNotification();
  if (nErr) cerr << "Error: AdsAmsUnRegisterRouterNotification: " << nErr << '\n';

  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';

  return;
}

// ADS state callback function
void __stdcall Callback(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser)
{
  INT       nIndex;
  nIndex = *(short *)pNotification->data;
  switch (nIndex)
  {
  case ADSSTATE_RUN:
    cout << "PLC run\n";
    break;
  case ADSSTATE_STOP: 
    cout << "PLC stop\n";
    break;
  default :
    cout << "PLC ADS-State" << nIndex << "\n";
    break;
  }
  cout.flush ();
}

// TwinCAT router callback function
void __stdcall RouterCall (long nReason)
{
  switch (nReason)
  {
  case AMSEVENT_ROUTERSTOP:
    cout << "TwinCAT-Router stop\n";
    break;
  case AMSEVENT_ROUTERSTART:
    cout << "TwinCAT-Router start\n";
    break;
  case AMSEVENT_ROUTERREMOVED: 
    cout << "TwinCAT-Router removed\n";
    break;
  default:
    cout << "TwinCAT-Router AMS-Event " << nReason << "\n";
    break;
  }
  cout.flush ();
}