Hinweise zur Dokumentation

Diese Beschreibung wendet sich ausschließlich an ausgebildetes Fachpersonal der Steuerungs- und Automatisierungstechnik, das mit den geltenden nationalen Normen vertraut ist.
Zur Installation und Inbetriebnahme der Komponenten ist die Beachtung der Dokumentation und der nachfolgenden Hinweise und Erklärungen unbedingt notwendig.
Das Fachpersonal ist verpflichtet, für jede Installation und Inbetriebnahme die zu dem Zeitpunkt veröffentlichte Dokumentation zu verwenden.

Das Fachpersonal hat sicherzustellen, dass die Anwendung bzw. der Einsatz der beschriebenen Produkte alle Sicherheitsanforderungen, einschließlich sämtlicher anwendbaren Gesetze, Vorschriften, Bestimmungen und Normen erfüllt.

Disclaimer

Diese Dokumentation wurde sorgfältig erstellt. Die beschriebenen Produkte werden jedoch ständig weiter entwickelt.
Wir behalten uns das Recht vor, die Dokumentation jederzeit und ohne Ankündigung zu überarbeiten und zu ändern.
Aus den Angaben, Abbildungen und Beschreibungen in dieser Dokumentation können keine Ansprüche auf Änderung bereits gelieferter Produkte geltend gemacht werden.

Marken

Beckhoff®, TwinCAT®, EtherCAT®, EtherCAT G®, EtherCAT G10®, EtherCAT P®, Safety over EtherCAT®, TwinSAFE®, XFC®, XTS® und XPlanar® sind eingetragene und lizenzierte Marken der Beckhoff Automation GmbH.
Die Verwendung anderer in dieser Dokumentation enthaltenen Marken oder Kennzeichen durch Dritte kann zu einer Verletzung von Rechten der Inhaber der entsprechenden Bezeichnungen führen.

Patente

Die EtherCAT Technologie ist patentrechtlich geschützt, insbesondere durch folgende Anmeldungen und Patente:
EP1590927, EP1789857, EP1456722, EP2137893, DE102015105702
mit den entsprechenden Anmeldungen und Eintragungen in verschiedenen anderen Ländern.

EtherCAT® ist eine eingetragene Marke und patentierte Technologie lizenziert durch die Beckhoff Automation GmbH, Deutschland.

Copyright

© Beckhoff Automation GmbH & Co. KG, Deutschland.
Weitergabe sowie Vervielfältigung dieses Dokuments, Verwertung und Mitteilung seines Inhalts sind verboten, soweit nicht ausdrücklich gestattet.
Zuwiderhandlungen verpflichten zu Schadenersatz. Alle Rechte für den Fall der Patent-, Gebrauchsmuster- oder Geschmacksmustereintragung vorbehalten.


Übersicht

Beschreibung | Quelltexte
Beispiel 1: DLL-Version auslesen | Download
Beispiel 2: Synchron in die SPS schreiben | Download
Beispiel 3: Synchron aus der SPS lesen | Download
Beispiel 4: ADS-Status auslesen | Download
Beispiel 5: ADS-Information auslesen | Download
Beispiel 6: SPS starten/stoppen | Download
Beispiel 7: Zugriff auf ein Array in der SPS | Download
Beispiel 8: Ereignissgesteuertes Lesen | Download
Beispiel 9: Zugriff per Variablenname | Download
Beispiel 10: SPS-Variablendeklaration auslesen | Download
Beispiel 11: Statusänderung vom TwinCAT-Router und der SPS erkennen | Download
Beispiel 12: Änderungen an der Symboltabelle ereignisgesteuert erkennen | Download
Beispiel 13: Reserviert |
Beispiel 14: Upload der Symbolinformation für eine einzelne Variable | Download
Beispiel 15: Auslesen der SPS via Multi-Threading | Download
Beispiel 16: SPS-Variablendeklaration auslesen | Download
Beispiel 17: ADS-Summenkommando: Lesen und Schreiben | Download
Beispiel 18: ADS-Summenkommando: Holen und freigeben | Download
Beispiel 19: Reserviert |
Beispiel 20: Übertragen von Strukturen an die SPS | Download
Beispiel 21: Lesen und Schreiben von DATE/TIME-Variablen | Download


Einbinden in Visual C++

TwinCAT ADS API

Die ADS-Bibliotheken werden mit jeder TwinCAT-Installation ausgeliefert und befinden sich im Verzeichnis "..\TwinCAT\AdsApi". Wenn sie lokal keine Laufzeit oder IO benötigen, können sie die kostenlose Variante TwinCAT CP verwenden, um einen ADS Client zu entwickeln.

Header Dateien einbinden

Um die Funktionalität der TcAdsDll.dll in einem Projekt nutzen zu können, müssen die Headerdateien TcAdsApi.h und TcAdsDef.h in das Projekt eingefügt werden.

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

Bibliothek zum Projekt hinzufügen

Um die Funktionalität der TcAdsDll nutzen zu können, muss die TcAdsDll.lib eingebunden werden.
In Visual Studio muss der Menüpunkt "Project Properties" angewählt werden. Im Dialog "Project Properties" ist der Bereich "Configuration Properties\Linker\Input" anzuwählen. Um die Bibliothek einzubinden, muss der Pfad der TcAdsDll.lib in der Textbox "Additional Dependencies" eingetragen werden.


Übersicht

Beschreibung | Download
Beispiel 1: DLL-Version auslesen |
Beispiel 2: Synchron in die SPS schreiben |
Beispiel 3: Synchron aus der SPS lesen |
Beispiel 4: ADS-Status auslesen |
Beispiel 5: ADS-Information auslesen |
Beispiel 6: SPS starten/stoppen |
Beispiel 7: Zugriff auf ein Array in der SPS |
Beispiel 8: Ereignissgesteuertes Lesen |
Beispiel 9: Zugriff per Variablenname |
Beispiel 10: SPS-Variablendeklaration auslesen |
Beispiel 11: Statusänderung vom TwinCAT-Router und der SPS erkennen |
Beispiel 12: Änderungen an der Symboltabelle ereignisgesteuert erkennen |
Beispiel 13: Reserviert |
Beispiel 14: Upload der Symbolinformation für eine einzelne Variable |
Beispiel 15: Auslesen der SPS via Multi-Threading |
Beispiel 16: SPS-Variablendeklaration auslesen |
Beispiel 17: ADS-Summenkommando: Lesen und Schreiben |
Beispiel 18: ADS-Summenkommando: Holen und freigeben |
Beispiel 19: Reserviert |
Beispiel 20: Übertragen von Strukturen an die SPS | Download
Beispiel 21: Lesen und Schreiben von DATE/TIME-Variablen | Download


DLL-Version auslesen

Download

Beschreibung

Diese Programm ermittelt die Version der DLL-Datei.

Quellcode

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

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

void main()
{
  long        nTemp;
  AdsVersion* pDLLVersion;

  nTemp = AdsGetDllVersion();
  pDLLVersion = (AdsVersion *)&nTemp;
  cout << "Version: " << (int)pDLLVersion->version << '\n';
  cout << "Revision: " << (int)pDLLVersion->revision << '\n';
  cout << "Build: " << pDLLVersion->build << '\n';
  cout.flush();
  getch();
}


Merker synchron in die SPS schreiben

TwinCAT ADS-DLL: Beispiele

Synchron in die SPS schreiben

Download

Beschreibung

Bei diesem Beispielprogramm wird der eingegebene Wert in das Merkerdoppelwort 0 geschrieben.

Quellcode

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

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

void main()
{
  long     nErr, nPort;
  AmsAddr  Addr;
  PAmsAddr pAddr = &Addr;
  DWORD    dwData;

  // Kommunikationsport auf dem ADS Router öffnen
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 RTS1 Port = 801
  pAddr->port = 801;

  // Wert vom Benutzer einlesen, welcher zur SPS geschrieben werden sollen
  cout << "Value: ";
  cin >> dwData;

  // Wert in MD0 schreiben
  nErr = AdsSyncWriteReq( pAddr, 0x4020, 0x0, 0x4, &dwData );
  if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

  _getch();
  // Kommunikationsport schließen
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


Merker synchron aus der SPS lesen

TwinCAT ADS-DLL: Beispiele

Synchron aus der SPS lesen

Download

Beschreibung

Bei diesem Beispielprogramm wird der Wert aus dem Merkerdoppelwort 0 der SPS ausgelesen und am Bildschirm angezeigt.

Quellcode

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

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

void main()
{
  long      nErr, nPort;
  AmsAddr   Addr;
  PAmsAddr  pAddr = &Addr;
  DWORD     dwData;

  // Kommunikationsport auf dem ADS Router öffnen
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 RTS1 Port = 801
  pAddr->port = 801;

  // Werte aus MD0 auslesen und anzeigen
  do
  {
    nErr = AdsSyncReadReq(pAddr, 0x4020, 0x0, 0x4, &dwData);
    if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
    else cout << dwData << '\n';
    cout.flush();
  }
  while (getch() == '\r');    // mit Carriage return nächsten Wert auslesen, sonst Ende

  // Kommunikationsport schließen
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


ADS-Status auslesen

Beschreibung

Dieses Programm ließ den Status der SPS aus. Die Variable vom Typ ADSSTATE enthält Informationen, ob sich z.B. die SPS im RUN oder STOP Zustand befindet.

Quellcode

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

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

void main()
{
  ADSSTATE   nAdsState;
  USHORT	nDeviceState;
  long	nErr, nPort;
  AmsAddr	Addr;
  PAmsAddr   pAddr = &Addr;

  // Kommunikationsport auf dem ADS Router öffnen
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) nErr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 RTS1 Port = 801
  pAddr->port = 801;

  do
  {
    nErr = AdsSyncReadStateReq(pAddr, &nAdsState, &nDeviceState);
    if (nErr)
      cerr << "Error: AdsSyncReadStateReq: " << nErr << '\n';
    else
    {
      cout << "AdsState: " << nAdsState << '\n';
      cout << "DeviceState: " << nDeviceState << '\n';
    }
    cout.flush();
  }
  while ( getch() == '\r'); // Carriage return weiter, sonst Ende

  // Kommunikationsport schließen
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


ADS-Informationen auslesen

TwinCAT ADS-DLL: Beispiele

ADS-Status auslesen

Download

Beschreibung

Jedes ADS-Gerät enthält eine Versionsnummer und eine Bezeichnung. Das Beispielprogramm liest diese Informationen der SPS aus und zeigt sie am Bildschirm an.

Quellcode

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

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

void main()
{
  LONG           nErr, nPort;
  AdsVersion     Version;
  AdsVersion     *pVersion = &Version;
  char           pDevName[50];
  AmsAddr        Addr;
  PAmsAddr       pAddr = &Addr;

  // Kommunikationsport auf dem ADS Router öffnen
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 RTS1 Port = 801
  pAddr->port = 801;

  nErr = AdsSyncReadDeviceInfoReq(pAddr, pDevName, pVersion);
  if (nErr)
    cerr << "Error: AdsSyncReadDeviceInfoReq: " << nErr << '\n';
  else
  {
    cout << "Name: " << pDevName << '\n';
    cout << "Version: " << (int)pVersion->version << '\n';
    cout << "Revision: " << (int)pVersion->revision << '\n';
    cout << "Build: " << pVersion->build << '\n';
  }
  cout.flush();
  _getch();

  // Kommunikationsport schließen
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


SPS starten/stoppen

Beschreibung

Mit dem folgenden Programm wird das Laufzeitsystem 1 der SPS gestartet, bzw. gestoppt.

Quellcode

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

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

void main()
{
  USHORT      nAdsState;
  USHORT      nDeviceState = 0;
  long        nErr, nPort;
  int         ch;
  void        *pData = NULL;
  AmsAddr     Addr;
  PAmsAddr    pAddr = &Addr;

  // Kommunikationsport auf dem ADS Router öffnen
  nPort = AdsPortOpen();
  nErr = AdsGetLocalAddress(pAddr);
  if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

  // TwinCAT2 RTS1
  pAddr->port = 801;

  cout << "(R) -> PLC Run\n";
  cout << "(S) -> PLC Stop\n";
  cout.flush();
  ch = getch();
  ch = toupper(ch);
  while ( (ch == 'R') || (ch == 'S') )
  {
    switch (ch)
    {
      case 'R':
        nAdsState = ADSSTATE_RUN;
        break;
      case 'S':
        nAdsState = ADSSTATE_STOP;
        break;
    }
    nErr = AdsSyncWriteControlReq (pAddr, nAdsState, nDeviceState, 0, pData);
    if (nErr) cerr << "Error: AdsSyncWriteControlReq: " << nErr << '\n';
    ch = _getch();
    ch = toupper(ch);
  }

  // Kommunikationsport schließen
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}


Zugriff auf ein Array in der SPS

Beschreibung

Ein Array, welches sich in der SPS befindet, soll ausgelesen werden. Die Variable wird hierbei per Handle angesprochen. Die Funktion AdsSyncReadReq() wird die Länge des gesamten Arrays übergeben. Als Variable wird die Adresse des ersten Array-Elements verwendet.

Quellcode

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

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

void main()
{
  long          nErr, nPort;
  AmsAddr       Addr;
  PAmsAddr      pAddr = &Addr;
  unsigned long lHdlVar;
  int           nIndex;
  short         Data[10];
  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 = AMSPORT_R0_PLC_RTS1;

  // Fetch handle for the PLC variable
   nErr = AdsSyncReadWriteReq(	pAddr,
                ADSIGRP_SYM_HNDBYNAME,
                0x0,
                sizeof(lHdlVar),
                lHdlVar,
                sizeof(szVar),
                szVar);

  if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
  // Read values of the PLC variables (by handle)
  nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(Data), &Data[0]);
  if (nErr)
    cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
  else
  {
    for (nIndex = 0; nIndex < 10; nIndex++)
      cout << "Data[" << nIndex << "]: " << Data[nIndex] << '\n';
  }
  cout.flush();
  _getch();
  // Close communication port
  nErr = AdsPortClose();
  if (nErr) cerr << "Error: AdsPortClose: " << nErr << '\n';
}

SPS Programm

PROGRAM MAIN
VAR
    PLCVar 			: ARRAY[0..9] OF WORD;
    TP_1			: ARRAY[0..9] OF TP;
    TOGGEL			: BOOL;
    nIndex			: INT;
END_VAR

TOGGEL := NOT TOGGEL;

FOR nIndex := 0 TO 9 DO
    TP_1[nIndex]( IN := TOGGEL, PT := t#1s);
    IF (TP_1[nIndex].Q = 0) THEN
        PLCVar[nIndex] := PLCVar[nIndex] + 1 + nIndex;
    END_IF
END_FOR


Ereignissgesteuertes Lesen

TwinCAT ADS-DLL: Beispiele

Ereignisgesteuertes Lesen

Download

Beschreibung

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. Weiter Informationen finden Sie bei der Beschreibung zur Struktur AdsNotificationAttrib.

Das folgende Programm startet eine Notification auf den 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.

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

Quellcode

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


Zugriff per Variablenname

Beschreibung

Alle Daten, die ADS-Geräte nach Außen zur Verfügung stellen, sind in IndexGroups und IndexOffset organisiert. Eine IndexGroup kann man sich als Tabelle vorstellen, wobei jeder Eintrag über den IndexOffset angesprochen wird. Die TwinCAT-SPS verfügt z.B. über IndexGroups in denen die Variablen abgelegt sind, die zum Ein-/Ausgangs- oder Merkerbereich gehören. Außerdem stehen in der TwinCAT-SPS IndexGroups zur Verfügung, mit denen Systemfunktionen angesprochen werden können.

Für das Beispielprogramm sind die IndexGroups ADSIGRP_SYM_HNDBYNAME und ADSIGRP_ SYM_VALBYHND von Bedeutung. Über die IndexGroup ADSIGRP_SYM_HNDBYNAME wird von einer SPS-Variablen, die namentlich genannt wird, ein Handle angefordert. Mit Hilfe dieses Handles und der IndexGroup ADSIGRP_SYM_VALBYHND kann auf die Variable zugegriffen werden. Als IndexOffset wird der Handle der Variablen übergeben.

Quellcode

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

// ADS headers
#include "c:\twincat\adsapi\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\adsapi\tcadsdll\include\tcadsapi.h"

using namespace std;

void main()
{
  long      nErr, nPort;
  AmsAddr   Addr;
  PAmsAddr  pAddr = &Addr;
  ULONG     lHdlVar, nData;
  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 = AMSPORT_R0_PLC_RTS1;

  // Get the handle of the PLC-variable
  nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
  if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
  do
  {
    // Read the value of a PLC-variable, via handle
    nErr = AdsSyncReadReq( pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData );
    if (nErr)
      cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
    else
      cout << "Value: " << nData << '\n';
    cout.flush();
    if (nData > 10)
    {
      // Write the value of the PLC variable to 0
      nData = 0;
      nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData);
      if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';
    }
  }
  while ( _getch() == '\r');  // Get next value with ENTER-button

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

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


SPS-Variablendeklaration (statisch) auslesen

TwinCAT ADS-DLL: Beispiele

Upload SPS-Variablendeklaration (static)

Download

Beschreibung

Dieses Beispiel beschreibt das Auslesen der statischen SPS-Symbole.

Bei den Zugriff auf die Variablendeklaration werden folgende Informationen übertragen:

  • Variablenname
  • Datentyp
  • Länge
  • Adresse (IndexGroup / IndexOffset)
  • Kommentar

Sämtliche oben aufgelisteten Information werden in einen Datenstream übertragen. Bevor dieser ausgelesen werden kann, wird mit dem ersten AdsSyncReadReq() die Länge ermittelt. Bei dem zweiten AdsSyncReadReq() werden die eigentlichen Daten übertragen. Die Variable pchSymbols ist ein Pointer, der auf diesen Bereich zeigt. In der FOR-Schleife wird für jede einzelne SPS-Variable der entsprechende Datenbereich in die Struktur pAdsSymbolEntry kopiert. In dieser Struktur sind die einzelnen Informationen der SPS-Variablen abgelegt. Die Makros PADSSYMBOLNAME, PADSSYMBOLTYPE und PADSSYMBOLCOMMENT vereinfachen das Auswerten dieser Daten.

Quellcode

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

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

void main()
{
  long                  nErr, nPort;
  char                  *pchSymbols = NULL;
  UINT                  uiIndex;
  AmsAddr               Addr;
  PAmsAddr              pAddr = &Addr;
  AdsSymbolUploadInfo   tAdsSymbolUploadInfo;
  PAdsSymbolEntry       pAdsSymbolEntry;

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

  // Read the length of the variable declaration
  nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_UPLOADINFO, 0x0, sizeof(tAdsSymbolUploadInfo), &tAdsSymbolUploadInfo);
  if (!nErr)
  {
      pchSymbols = new char[tAdsSymbolUploadInfo.nSymSize];
      assert(pchSymbols);

      // Read information of the PLC-variable
      nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_UPLOAD, 0, tAdsSymbolUploadInfo.nSymSize, pchSymbols);
      if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';

      // Print out the information of the PLC-variable
      pAdsSymbolEntry = (PAdsSymbolEntry)pchSymbols;
      for (uiIndex = 0; uiIndex < tAdsSymbolUploadInfo.nSymbols; uiIndex++)
      {
        cout << PADSSYMBOLNAME(pAdsSymbolEntry) << "\t\t"
             << pAdsSymbolEntry->iGroup << '\t'
             << pAdsSymbolEntry->iOffs << '\t'
             << pAdsSymbolEntry->size << '\t'
             << PADSSYMBOLTYPE(pAdsSymbolEntry) << '\t'
             << PADSSYMBOLCOMMENT(pAdsSymbolEntry) << '\n';
        pAdsSymbolEntry = PADSNEXTSYMBOLENTRY(pAdsSymbolEntry); cout.flush();
      }
  }
  else
  {
      cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
  }
  getch();

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

  // Release the allocated memory
  if (pchSymbols) delete(pchSymbols);
}


Statusänderung vom TwinCAT-Router und der SPS erkennen

Beschreibung

Zur Laufzeit einer Applikation ist es oft von Bedeutung, den Status von TwinCAT und/oder dessen Komponenten abzufragen; z.B. ob die SPS im RUN-Status ist. Damit dieses nicht ständig abgefragt werden muß, kann mit Hilfe von Callback-Funktionen eine Statusänderung auf sehr effektive Weise erkannt werden.
Mit dem folgenden Beispielprogramm wird der Status der SPS (Laufzeitsystem 1) und der des TwinCAT-Routers überwacht.
Durch den Aufruf der Funktion AdsAmsRegisterRouterNotification() wird bei jeder Statusänderung des TwinCAT-Routers die angegebende Callback-Funktion aufgerufen. Anhand des Parameters, der übergeben wird, kann der aktuelle Status abgefragt werden.
Mit Hilfe der Funktion AdsSyncAddDeviceNotificationReq() wird der Status der SPS überwacht. Die Daten, die an die Callback-Funktion übergeben werden, stellen der aktuellen Status der SPS da.

Quellcode

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


Änderungen an der Symboltabelle ereignisgesteuert erkennen

Beschreibung

ADS-Geräte, die Symbolnamen unterstützen (SPS, NC, ...), legen die Symbolnamen in eine interne Tabelle ab. Dabei wird jedem Symbol ein Handle zugeordnet. Das Symbolhandle wird benötigt, um auf die Variablen zugreifen zu können (siehe auch Beispiel 9). Ändert sich die Symboltabelle, z.B. weil ein neues SPS-Programm in die Steuerung geschrieben wird, so müssen auch die Handles neu ermittelt werden. Wie Änderungen an der Symboltabelle erkannt werden können, zeigt das folgende Beispiel.

Quellcode

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

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

void __stdcall SymbolChanged(AmsAddr*, AdsNotificationHeader*, unsigned long);

void main()
{
  long                   nErr;
  AmsAddr                Addr;
  PAmsAddr               pAddr = &Addr;
  ULONG                  hNotification;
  AdsNotificationAttrib  adsNotificationAttrib;

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

  // Select Port: TwinCAT2 PLC1
  pAddr->port = 801;

  // Specify attributes of the notification
  adsNotificationAttrib.cbLength = 1;
  adsNotificationAttrib.nTransMode = ADSTRANS_SERVERONCHA;
  adsNotificationAttrib.nMaxDelay = 5000000; // 500ms
  adsNotificationAttrib.nCycleTime = 5000000; // 500ms

  // Start notification for changes to the symbol table
  nErr = AdsSyncAddDeviceNotificationReq(pAddr, ADSIGRP_SYM_VERSION, 0, &adsNotificationAttrib, SymbolChanged, NULL, &hNotification);
  if (nErr) cerr << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << '\n';

  // Wait for a key-press from the user
  _getch();

  // Stop notification
  nErr = AdsSyncDelDeviceNotificationReq(pAddr, hNotification);
  if (nErr) cerr << "Error: AdsSyncDelDeviceNotificationReq: " << nErr << '\n';

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

// Callback function
void __stdcall SymbolChanged(AmsAddr* pAddr, AdsNotificationHeader* pNotification, ULONG hUser)
{
  cout << "Symboltabelle hat sich geändert!\n";
  cout.flush();
}


Auslesen der SPS-Variablendeklaration einer einzelnen Variablen

Beschreibung

Bei den Zugriff auf die Variablendeklaration werden folgende Informationen übertragen:

  • Variablenname
  • Datentyp
  • Länge
  • Adresse (IndexGroup / IndexOffset)
  • Kommentar

Mit dem AdsSyncReadWriteReq() Aufruf wird die Information der Variablen ausgelesen. Der Name der Variable wird der Funktion im Parameter pWriteData übergeben. Nach dem Aufruf steht die angeforderte Information in der Variable pAdsSymbolEntry . In dieser Struktur sind die einzelnen Informationen der SPS-Variablen abgelegt. Die Makros PADSSYMBOLNAME, PADSSYMBOLTYPE und PADSSYMBOLCOMMENT vereinfachen das Auswerten dieser Daten. Als nächstes wird der Datentyp der Variable an Hand von pAdsSymbolEntry->dataType ausgewertet.
Falls der Datentyp dem Typ UDINT oder ARRAY OF UDINT wird zusätzlich der Wert dieser Variable ausgelsen.

Quellcode

#include <windows.h>
#include <conio.h>
#include <assert.h>
#include <string.h>
#include <iostream.h>

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

typedef enum AdsDataTypeId
{
    ADST_VOID = VT_EMPTY,
    ADST_INT8 = VT_I1,
    ADST_UINT8 = VT_UI1,
    ADST_INT16 = VT_I2,
    ADST_UINT16 = VT_UI2,
    ADST_INT32 = VT_I4,
    ADST_UINT32 = VT_UI4,
    ADST_INT64 = VT_I8,
    ADST_UINT64 = VT_UI8,
    ADST_REAL32 = VT_R4,
    ADST_REAL64 = VT_R8,
    ADST_STRING = VT_LPSTR,
    ADST_WSTRING = VT_LPWSTR,
    ADST_REAL80 = VT_LPWSTR+1,
    ADST_BIT = VT_LPWSTR+2,
    ADST_BIGTYPE = VT_BLOB,
    ADST_MAXTYPES = VT_STORAGE,
} ADS_DATATYPE;

typedef struct _ValueString
{
    DWORD dwValue;
    char* szLabel;
} ValueString;

ValueString AdsDatatypeString[] =
{
    { VT_EMPTY, "ADST_VOID", },
    { VT_I1, "ADST_INT8", },
    { VT_UI1, "ADST_UINT8", },
    { VT_I2, "ADST_INT16", },
    { VT_UI2, "ADST_UINT16", },
    { VT_I4, "ADST_INT32", },
    { VT_UI4, "ADST_UINT32", },
    { VT_I8, "ADST_INT64", },
    { VT_UI8, "ADST_UINT64", },
    { VT_R4, "ADST_REAL32", },
    { VT_R8, "ADST_REAL64", },
    { VT_LPSTR, "ADST_STRING", },
    { VT_LPWSTR, "ADST_WSTRING", },
    { VT_LPWSTR+2, "ADST_BIT", },
    { VT_BLOB, "ADST_BIGTYPE", },
    { VT_STORAGE, "ADST_MAXTYPES", },
};

void main()
{
    long nErr, nPort;
    AmsAddr Addr;
    PAmsAddr pAddr = &Addr;
    char szVariable[255];
    BYTE buffer[0xFFFF];
    PAdsSymbolEntry pAdsSymbolEntry;

    // 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;

    for(;;)
    {
        cout << "Enter variable Name: ";
        cin >> szVariable;

        nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_INFOBYNAMEEX, 0, sizeof(buffer), buffer, strlen(szVariable)+1, szVariable);
        if (nErr)
        {
            cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
        }
        else
        {
            pAdsSymbolEntry = (PAdsSymbolEntry)buffer;
            cout << "Name: " << PADSSYMBOLNAME(pAdsSymbolEntry) << "\n"
            <<"Index Group: "<< pAdsSymbolEntry->iGroup << '\n'
            <<"Index Offset: "<< pAdsSymbolEntry->iOffs << '\n'
            <<"Size: "<< pAdsSymbolEntry->size << '\n'
            <<"Type: "<< (char*)PADSSYMBOLTYPE(pAdsSymbolEntry) << '\n'
            <<"Comment: "<< (char*)PADSSYMBOLCOMMENT(pAdsSymbolEntry) << '\n';

            switch(pAdsSymbolEntry->dataType)
            {
            case ADST_UINT32:
                {
                    int nElements = pAdsSymbolEntry->size/sizeof(unsigned long);
                    unsigned long *pVal = new unsigned long[nElements];
                    cout << "Datatype: ADST_UINT32" <<'\n';
                    AdsSyncReadReq(pAddr, pAdsSymbolEntry->iGroup, pAdsSymbolEntry->iOffs, pAdsSymbolEntry->size, pVal);
                    if( nErr )
                    {
                        cerr << "Error: AdsSyncReadReq: Unable to read Value" << nErr << '\n';
                    }
                    else
                    {
                        cout << "Value: ";
                        for( int i=0; i<nElements; i++ )
                        {
                            cout << pVal[i] << '\t';
                        }
                        cout << '\n';
                    }
                }
                break;
            default:
                {
                    int nType = sizeof(AdsDatatypeString)/sizeof(ValueString);
                    for(int i=0; i< nType; i++)
                    {
                        if( AdsDatatypeString[i].dwValue == pAdsSymbolEntry->dataType )
                        {
                            cout << "Datatype:" << AdsDatatypeString[i].szLabel <<'\n';
                            break;
                        }
                    }
                    if( i == nType )
                        cout << "Datatype:" << "Unknown datatype:" << pAdsSymbolEntry->dataType <<'\n';
                }
                break;
            }
        }
        cout << "Exit(y/n)" << '\n';
        cout.flush();

        if(_getch() == 'y')
            break;
    }
    // Close communication port
    nErr = AdsPortClose();
    if (nErr) cerr << "Fehler: AdsPortClose: " << nErr << '\n';
}

SPS Programm

VAR_GLOBAL
    PLCVarBYTE AT %MB0: BYTE; (* PLCVarBYTE *)
    PLCVarWORD AT %MB4: WORD; (* PLCVarWORD *)
    PLCVarDWORD AT %MB8: DWORD; (* PLCVarDWORD *)
    PLCVarBOOL AT %MB12: BOOL; (* PLCVarBOOL *)
    PLCVarINT AT %MB16: INT; (* PLCVarINT *)
    PLCVarSINT AT %MB20: SINT; (* PLCVarSINT *)
    PLCVarUSINT AT %MB24: USINT; (* PLCVarUSINT *)
    PLCVarUINT AT %MB28: UINT; (* PLCVarUINT *)
    PLCVarDINT AT %MB32: DINT; (* PLCVarDINT *)
    PLCVarUDINT AT %MB34: UDINT := 1234; (* PLCVarUDINT *)
    PLCVarREAL AT %MB38: REAL; (* PLCVarREAL *)
    PLCVarLREAL AT %MB42: LREAL; (* PLCVarLREAL *)

    PLCVarUDINTArray :ARRAY[0..5] OF UDINT; (* PLCVarINTArray*)
END_VAR


Auslesen der SPS-Variablendeklaration einer einzelnen Variablen

TwinCAT ADS-DLL: Examples

Lese SPS Variable via Multithreading

Download

Beschreibung

Das Beispiel erläutert das Erzeugen zweier Threads, die in unterschiedlichen Zeitintervalen auf eine SPS zugreifen.

Hinweis: Verwenden Sie die erweiterten ADS Methoden , wenn Sie eine Multithreading Anwendung mit ADS Funktionalitäten entwickeln wollen.

Sourcecode

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

// ADS headers
#include "c:\twincat\adsapi\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\adsapi\tcadsdll\include\tcadsapi.h"

using namespace std;

DWORD WINAPI ThreadFunction1(LPVOID parameter)
{
    long		nErr, nPort;
    AmsAddr		Addr;
    PAmsAddr	pAddr = &Addr;
    ULONG		lHdlVar, nData;
    ULONG		cbReturn = 0;
    bool		bRun = true;
    char		szVar []={"MAIN.PLCVarRTS1"};

    // Open communication port on local ADS router
    // Use the ADS-Ex-Methods if you implement multi threading

    nPort = AdsPortOpenEx();
    nErr = AdsGetLocalAddressEx(nPort, pAddr);
    if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

    // TwinCAT2 RTS1 Port
    pAddr->port = 801;

    // Get the handle of the PLC-variable
    nErr = AdsSyncReadWriteReqEx2(nPort, pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar, &cbReturn);
    if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';

    while(bRun)
    {

        DWORD wait = 0;

        // Wait 100ms for stopEvent
        wait = WaitForSingleObject(parameter, 100);

        // If timeout elapse read plc data
        if(WAIT_TIMEOUT == wait)
        {
            // Read the value of a PLC-variable, via handle
            nErr = AdsSyncReadReqEx2(nPort, pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData, &cbReturn);
            if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
            else cout << "Actual thread --> Thread1 Value: " << nData << '\n';
            cout.flush();
        }

        else
        {
            bRun = false;
        }
    }

    // Release the handle of the PLC-variable
    nErr = AdsSyncWriteReqEx(nPort, pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
    if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

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

    return 1;
}

DWORD WINAPI ThreadFunction2(LPVOID parameter)
{
    long		nErr, nPort;
    AmsAddr		Addr;
    PAmsAddr	pAddr = &Addr;
    ULONG		lHdlVar, nData;
    ULONG		cbReturn = 0;
    bool		bRun = true;
    char		szVar []={"MAIN.PLCVarRTS2"};

    // Open communication port on the local ADS router
    // Use the ADS-Ex-Methods if you implement multi threading and open an additional port

    nPort = AdsPortOpenEx();
    nErr = AdsGetLocalAddressEx(nPort, pAddr);
    if (nErr) cerr << "Error: AdsGetLocalAddress: " << nErr << '\n';

    // TwinCAT2 RTS1 Port
    pAddr->port = 801;

    // Get the handle of the PLC-variable
    nErr = AdsSyncReadWriteReqEx2(nPort, pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar, &cbReturn);
    if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';

    while(bRun)
    {
        DWORD wait = 0;

        // Wait 500ms for stopEvent
        wait = WaitForSingleObject(parameter, 500);

        // If timeout elapse read plc data
        if(WAIT_TIMEOUT == wait)
        {
            // Read the value of a PLC-variable, via handle
            nErr = AdsSyncReadReqEx2(nPort, pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData, &cbReturn);
            if (nErr)
                cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
            else
                cout << "Actual thread --> Thread2 Value: " << nData << '\n';
            cout.flush();
        }

        else
        {
            bRun = false;
        }
    }

    // Release the handle of the PLC-variable
    nErr = AdsSyncWriteReqEx(nPort, pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
    if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr << '\n';

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

    return 1;
}

void main()
{
    // StopEvents are used to end the threads
    HANDLE hStopThread1 = CreateEvent(0, FALSE, FALSE, "StopEvent1");
    HANDLE hStopThread2 = CreateEvent(0, FALSE, FALSE, "StopEvent2");

    DWORD threadId1 = 1;
    DWORD threadId2 = 2;

    // Thread creation
    HANDLE hThread1 = CreateThread(0,			// no security
                0,			// default stack size
                ThreadFunction1,
                (void*)hStopThread1, 	// parameter
                0,			// creation flags
                &threadId1   		// threadId
                );

    HANDLE hThread2 = CreateThread(0,			// no security
                0,			// default stack size
                ThreadFunction2,
                (void*)hStopThread2, 	// parameter
                0,			// creation flags
                &threadId2   		// threadId
                );

    // Loop only for demonstration
    for(int i = 0; i < 10; ++i)
    {
        cout << "Actual thread --> Main Thread" << '\n';
        Sleep(1000);
    }

    // Stop threads
    SetEvent(hStopThread1);
    SetEvent(hStopThread2);
    cout << "Press key to exit: ";
    _getch();
}


Upload PLC-variabledeclaration (dynamic)

TwinCAT ADS-DLL: Sample

Upload PLC variable declaration (dynamic)

Download

Description

This sample describes how to upload the PLC symbol information in a more efficient dynamic way.

The PLC symbol information conatin the following parts :

  • Variablename
  • datentype
  • length
  • address (IndexGroup / IndexOffset)
  • comment

NOTE :
We highly recomment to NOT work with this IndexGroup/IndexOffset for ADS communication but instead use handles of symbols for ADS communication.
After uploading the information "name", "datatype" and "length" it makes sense to request a handle for this symbol.

Read the major information via ADS into internal class "CAdsParseSymbols" :

// Read major symbol informations via ADS from device

AdsSymbolUploadInfo2 info;
nResult = AdsSyncReadReq(&m_amsAddr, ADSIGRP_SYM_UPLOADINFO2, 0, sizeof(info), &info);
if ( nResult == ADSERR_NOERR )
{
    // size of symbol information
    PBYTE pSym = new BYTE[info.nSymSize];
    if ( pSym )
    {
        // upload symbols (instances)
        nResult = AdsSyncReadReq(&m_amsAddr, ADSIGRP_SYM_UPLOAD, 0, info.nSymSize, pSym);
        if ( nResult == ADSERR_NOERR )
        {
            // get size of datatype description
            PBYTE pDT = new BYTE[info.nDatatypeSize];
            if ( pDT )
            {
                // upload datatye-descriptions
                nResult = AdsSyncReadReq(&m_amsAddr, ADSIGRP_SYM_DT_UPLOAD, 0, info.nDatatypeSize, pDT);
                if ( nResult == ADSERR_NOERR )
                {
                    // create class-object for each datatype-description
                    m_pDynSymbols = new CAdsParseSymbols(pSym, info.nSymSize, pDT, info.nDatatypeSize);
                    if ( m_pDynSymbols == NULL )
                        nResult = ADSERR_DEVICE_NOMEMORY;
                    }
                    delete pDT;
                }
            }
            delete pSym;
        }
    }
}

Get Parent :
The routine Get Parent will jump NOT jump to the direct parent of a child. Instead this command will jump to the next entry of the direct parent.

Get Sibling:
Selecting this option will return the next symbol within the current hierachy level. Symbols containing child information will be displayed, but the child elements will not be displayed.

Get Child:
If the current symbol contains child-symbol information (so the current symbol is an instance of a datatype description), this command will enter the next hierachy and return the information about first child object.

Get Next:
Clicking this button the next symbol will be extracted from internal class "CAdsParseSymbols" and be displayed.
Selecting always just this option allows to navigate from first ADS-symbol through the hierachy symbol tree to the end of list.


ADS Sum Command : Read

TwinCAT ADS-DLL: Sample

ADS-sum command : Read or Write a list of variabled with one single ADS-command

Download

This sample describes how to read multiple single variables with one single ADS API call

Note that ADS is just a transport layer, but there could be important side effects : So read this requirements and take care on limitations.

Background :

ADS offers powerful and fast communication to exchange any kind of information. It's possible to read single variables or complete arrays and structures with each one single ADS-API call.
This new ADS command offers to read with one singale ADS call multiple variables which are not structured within a linear memory.

As a result the ADS caller application (like scada Systems etc.) can extremly speed up cyclic polling :
Sample :

  • Until now : Polling 4000 single variables which are not in a linear area (like array / structure / fixed PLC address ) would cause 4000 single Ads-ReadReq with each 1-2 ms protocol time.
    As a result the scanning of these variables take 4000ms-8000ms.
  • New Ads-Command allows to read multiple variables with one single ADS-ReadReq : 4000 single variables are handled with e.g. 8 single Ads-ReadReq (each call requesting 500 variables) with each 1-2 ms protocol time.
    As a result the scanning of these variables take just few 10ms.

REQUIREMENTS AND IMPORTANT LIMITATIONS :

Note that ADS is just a transport layer, but there could be important side effects. So read this requirements and take care on limitations :

  • Version of target ADS Device:
    ADS itself is just the transport layer, but the requested ADS device has to support the ADS-Command.

TwinCAT 2.11 >= Build 1550 required

  • Bytes length of requested data:
    Requesting a large list of values from variables is fine, but the requested data of the Ads-response (the data-byte-length) have to pass the AMS-router (size by default a 2048kb)
    So the caller has to limit the requested variables based on calculation of requested data-byte-length.
  • Number of Sub-ADS calls : Highly recommended to max. 500 !
    If the PLC is processing one ADS request, it will completely work on this single ADS request BEFORE starting neyt PLC cycle.
    As a result one single ADS request with 200.000 sub-Ads-requests would cause that PLC would collect and copy 200.000 variables into one single ADS response, before starting next PLC.
    So this large number of ads-sub-commands will jitter the PLC execution !
    We highly recommend to not request more than 500 Ads-Sub commands

// This code snippet using ADSIGRP_SUMUP_READ with IndexGroup 0xF080 and IndexOffset as number of ADS-sub-commands
// Demonstrates how to read a list of variables, see full demo-code

// Use ADS-ReadWrite request : "Write" the requested data down to ADS device and "Read" the received answer

 nErr = AdsSyncReadWriteReq( 	pAddr,
                0xF080, 		// Sum-Command, response will contain ADS-error code for each ADS-Sub-command
                reqNum,		// number of ADS-Sub-Commands
                4*reqNum+reqSize, // number requested bytes in the sample two variables each 4 bytes. NOTE : we request additional "error"-flag(long) for each ADS-sub commands
                (void*)(mAdsSumBufferRes), // provide buffer for response
                12*reqNum,	// send 12 bytes for each variable (each ads-Sub command consist of 3 * ULONG : IG, IO, Len)
                &parReq);

// This code snippet using ADSIGRP_SUMUP_WRITE with IndexGroup 0xF081 and IndexOffset as number of ADS-sub-commands
// Demonstrates how to write a list of variables, see full demo-code

// Use ADS-ReadWrite request : "Write" the send a list of data to list of variables down to ADS device and "Read" the received return codes

 nErr = AdsSyncReadWriteReq( 	pAddr,
                0xF081,		// ADS list-write command
                reqNum,		// number of ADS-Sub commands
                4*reqNum,		// we expect an ADS-error-return-code (long) for each ADS-Sub command
                (void*)(mAdsSumBufferRes), // provide space for the response containing the return codes
                16*reqNum,	// cbyteLen : in THIS sample we send two variables each 4 byte
                        // --> send 16 bytes (IG1, IO1, Len1, IG2, IO2, Len2, Data1, Data2)
                &parReq); 	// buffer with data


ADS-Summenkommando: Holen und Freigeben von mehreren Handles

Anforderung: TwinCAT 2.11 >= Build 1550

Beschreibung

Dieses Beispiel zeigt, wie man unter Zuhilfenahme des ADS-Summenkommandos, viele Handles holen und wieder freigeben kann. Aufgebaut als AdsSyncReadWriteRequest, dient es als Behälter, in dem die Unterkommandos transportiert werden.

1. Handles holen

Als erstes, werden die benötigten Header eingebunden.
#include <iostream.h>
#include <windows.h>
#include <conio.h>

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

Es folgt das Definieren einer Struktur, Deklarieren von Variablen und Reservieren von Speicher.

    // Structure declaration for values
    typedef struct dataReq
    {
        unsigned long		indexGroup;	// index group in ADS server interface
        unsigned long		indexOffset;	// index offset in ADS server interface
        unsigned long		rlength;		// count of bytes to read
        unsigned long		wlength;		// count of bytes to write
    }TDataReq, *PTDataReq;

    // Variables declaration
    AmsAddr		Addr;
    LONG		nErr, nPort;
    PAmsAddr		pAddr = &Addr;

    char		szVar1[] = {".bVar01"};
    char		szVar2[] = {".bVar02"};

    // Allocate memory
    ULONG	cbReq	= ( sizeof(TDataReq)*2 ) + sizeof(szVar1) + sizeof(szVar2);
    BYTE*	pBuffReq	= new BYTE[cbReq];

    BYTE*	pBuffRes	= new BYTE[24];

    // Put structure over memory
    PTDataReq	 	pDataReq = (PTDataReq)pBuffReq;
    ULONG*	 	pDataRes = (ULONG*)pBuffRes;

Die zu übertragenden Werte werden hinter die letzte Struktur geschrieben.

    // pDataReq-> structure 1
    pDataReq->indexGroup	= ADSIGRP_SYM_HNDBYNAME;
    pDataReq->indexOffset	= 0x0;
    pDataReq->rlength		= sizeof(ULONG);
    pDataReq->wlength		= sizeof(szVar1);

    // Skip to next structure
    pDataReq = pDataReq+1;

    // pDataReq-> structure 2
    pDataReq->indexGroup	= ADSIGRP_SYM_HNDBYNAME;
    pDataReq->indexOffset	= 0x0;
    pDataReq->rlength		= sizeof(ULONG);
    pDataReq->wlength		= sizeof(szVar2);

    // Skip to write data 1
    char*	szVarName = ( (char*)pDataReq ) + sizeof(TDataReq);
    strncpy( szVarName, szVar1, sizeof(szVar1) );

    // Skip to write data 2
    szVarName = szVarName + sizeof(szVar1);
    strncpy( szVarName, szVar2, sizeof(szVar2) );

Für die Kommunikation wird ein Port geöffnet und die lokale Adresse übergeben. Kommt es zur Übertragung wird vorher der Port vom Laufzeitsystem 1 der Adresse zugewiesen.

Die Parameter für das Summenkommando bestehen aus IndexGroup (0xf082) - Aufruf des Summenkommandos, IndexOffset (0x2) - Anzahl der Unterkommandos, ReadLength (0x18) - Größenangabe der zu lesenden Daten, ReadData (pBuffRes) - Speicher, der gelesene Daten entgegen nimmt, WriteLength (cbReq) - Größenangabe der zu sendenen Daten und WriteLength (pBuffReq) - Speicher, der zu sendende Daten enthält.
    // Kommunikationsport auf dem ADS Router öffnen
    nPort = AdsPortOpen();
    nErr  = AdsGetLocalAddress(pAddr);

    cout << "open port:  ";
        if (nErr == 0)
        {
            cout << "OK" << '\n';

            // Get handles
            pAddr->port = AMSPORT_R0_PLC_RTS1;
            nErr = AdsSyncReadWriteReq(
                pAddr,
                0xf082,		// ADS list-read-write command
                0x2,		// number of ADS-sub commands
                0x18,		// we expect an ADS-error-return-code for each ADS-sub command
                pBuffRes,		// provide space for the response containing the return codes
                cbReq,		// cbReq : send 48 bytes (IG1, IO1, RLen1, WLen1,
                        // IG2, IO2, RLen2, WLen2, Data1, Data2)
                pBuffReq );	// buffer with data
        }
        else {cout << "ERROR [" << nErr << "]" << '\n';};

    cout << "connect:    ";
        if (nErr == 0)
        {
            cout << "OK" << '\n';

            // Skip to handle 1 and examine the value
            ULONG nVarHandle = *( (ULONG*)pBuffRes );
            if (nVarHandle != 0)
            {
                cout << " > handle1: ";
                cout << "ERROR [" << nVarHandle << "]" << '\n';
            }

            // Skip to handle 2 and examine the value
            nVarHandle = *( (ULONG*)pBuffRes + 2 );
            if (nVarHandle != 0)
            {
                cout << " > handle2: ";
                cout << "ERROR [" << nVarHandle << "]" << '\n';
            }
        }
        else {cout << "ERROR [" << nErr << "]" << '\n';};

Nach dem Senden des Request, erwarten wir einen ADS Fehlercode und Länge für jeden Handle den wir versuchen zu bekommen.

2. Handles freigeben

Es wird ein weiteres Mal eine Struktur definiert, Variablen deklariert und Speicher reserviert.
    // Structure declaration for values
    typedef struct dataRel
    {
        unsigned long		indexGroup;	// index group in ADS server interface
        unsigned long		indexOffset;	// index offset in ADS server interface
        unsigned long		length;		// count of bytes to write
    }TDataRel, *PTDataRel;

    // Variables declaration
    ULONG*	nVar1	= (ULONG*)pBuffRes+4;
    ULONG*	nVar2	= (ULONG*)pBuffRes+5;

    // Allocate memory
    ULONG	cbRel	   = sizeof(TDataRel)*2 + sizeof(ULONG)*2;
    BYTE*	pBuffRel    = new BYTE[cbRel];

    ULONG	cbRelRes    = sizeof(ULONG)*2;
    BYTE*	pBuffRelRes = new BYTE[cbRelRes];

    // Put structure over memory
    PTDataRel  pDataRel    = (PTDataRel)pBuffRel;
    ULONG*	  pDataRelRes = (ULONG*)pBuffRelRes;

Die zu empfangenen Werte werden wieder hinter die letzte Struktur geschrieben.

    // pDataRel-> structure 1
    pDataRel->indexGroup	= ADSIGRP_SYM_RELEASEHND;
    pDataRel->indexOffset	= 0x0;
    pDataRel->length		= sizeof(ULONG);

    // Skip to next structure
    pDataRel++;

    // pDataReq-> structure 2
    pDataRel->indexGroup	= ADSIGRP_SYM_RELEASEHND;
    pDataRel->indexOffset	= 0x0;
    pDataRel->length		= sizeof(ULONG);

    // Skip to next structure
    pDataRel++;

    // Write handles into structure
    memcpy(	       pDataRel,   nVar1, sizeof(ULONG) );
    memcpy( (ULONG*)pDataRel+1, nVar2, sizeof(ULONG) );

Für die Freigabe der Handles wird die bestehende Verbindung benutzt.

Die Parameter für das Summenkommando bestehen aus IndexGroup (0xf081) - Aufruf des Summenkommandos, IndexOffset (0x2) - Anzahl der Unterkommandos, ReadLength (cbRelRes) - Größenangabe der zu lesenden Daten, ReadData (pBuffRelRes) - Speicher, der gelesene Daten entgegen nimmt, WriteLength (cbRel) - Größenangabe der zu sendenen Daten und WriteLength (pBuffRel) - Speicher, der zu sendene Daten enthält.

Zum Schluss müssen die Handles freigegeben und der Port geschlossen werden.
    // Release handles
    nErr = AdsSyncReadWriteReq(
        pAddr,
        0xf081,		// ADS list-write command
        0x2,		// number of ADS-sub commands
        cbRelRes,		// we expect an ADS-error-return-code for each ADS-sub command
        pBuffRelRes,	// provide space for the response containing the return codes
        cbRel,		// cbReq : send 40 bytes (IG1, IO1, Len1, IG2, IO2, Len2, Data1, Data2)
        pBuffRel );	// buffer with data

    cout << "disconnect: ";
        if (nErr == 0)
        {
            cout << "OK" << '\n';

            // Skip to handle 1 and examine the value
            ULONG nVarHandle = *( (ULONG*)pBuffRes );
            if (nVarHandle != 0)
            {
                cout << " > handle1: ";
                cout << "ERROR [" << nVarHandle << "]" << '\n';
            }

            // Skip to handle 2 and examine the value
            nVarHandle = *( (ULONG*)pBuffRes + 2 );
            if (nVarHandle != 0)
            {
                cout << " > handle2: ";
                cout << "ERROR [" << nVarHandle << "]" << '\n';
            }
        }
        else {cout << "ERROR [" << nErr << "]" << '\n';};

    // Close the communication port
    nErr = AdsPortClose();
    cout << "close port: ";
        if (nErr == 0) {cout << "OK" << '\n' << "-------------------" << '\n';}
        else {cout << "ERROR [" << nErr << "]" << '\n' << "-------------------" << '\n';}

    cout.flush();

    // Wait for key press
    getch();
}

Als Antwort erhalten wir einen ADS Fehlercode für jeden Handle den wir versuchen freizugeben.


Beckhoff Information System - German

TwinCAT ADS-DLL: Beispiele

Übertragen von Strukturen an die SPS

Download

Beschreibung

Dieses Beispiel zeigt die Übertragung einer Struktur an die SPS per ADS. Die Struktur besteht aus Elementen von verschiedenen Datentypen.

Quellcode

#include <stdio.h>
#include <tchar.h>
#include "windows.h"

#include "c:\twincat\adsapi\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\adsapi\tcadsdll\include\tcadsapi.h"

// Create new struct
typedef struct PlcStruct {
    INT16	shortVal;
    INT32	intVal;
    byte	byteVal;
    DOUBLE	doubleVal;
    FLOAT	floatVal;
} SPlcVar, *pSPlcVar;

int _tmain(int argc, _TCHAR* argv[])
{
    long     nErr, nPort;
    AmsAddr  Addr;
    PAmsAddr pAddr = &Addr;
    ULONG     lHdlVar;

    // New struct. Assign test values
    PlcStruct PlcVar;

    PlcVar.shortVal = 1;
    PlcVar.intVal = 2;
    PlcVar.byteVal = 3;
    PlcVar.doubleVal = 4.04;
    PlcVar.floatVal = (FLOAT)5.05;

    // Declare PLC variable which should notify changes
    char      szVar []={"MAIN.PLCVar"};

    // Extract values from struct and write to byte array
    // Circumvent memory holes caused by padding
    BYTE *pData = new BYTE[19];
    int nIOffs = 0;

    memcpy_s(&pData[nIOffs], 19, &PlcVar.shortVal, 2);
    nIOffs += 2;
    memcpy_s(&pData[nIOffs], 17, &PlcVar.intVal, 4);
    nIOffs += 4;
    memcpy_s(&pData[nIOffs], 13, &PlcVar.byteVal, 1);
    nIOffs++;
    memcpy_s(&pData[nIOffs], 12, &PlcVar.doubleVal, 8);
    nIOffs += 8;
    memcpy_s(&pData[nIOffs], 4, &PlcVar.floatVal, 4);

    // Open communication port on the ADS router
    nPort = AdsPortOpen();
    nErr = AdsGetLocalAddress(pAddr);
    if (nErr) printf("Error: Ads: Open port: %d\n", nErr);
    pAddr->port = 801;

    // Get variable handle
    nErr = AdsSyncReadWriteReq(pAddr,
                ADSIGRP_SYM_HNDBYNAME,
                0x0,
                sizeof(lHdlVar),
                &lHdlVar,
                sizeof(szVar),
                szVar);

    // Write the struct to the Plc
    AdsSyncWriteReq(	pAddr,
            ADSIGRP_SYM_VALBYHND, 	// IndexGroup
            lHdlVar,			// IndexOffset
            0x13,			// Size of struct
            (void*) pData);

    if (nErr) printf("Error: Ads: Write struct: %d\n", nErr);
    else  printf("Structure successful written\n");

    // Close communication
    delete [] pData;

    //Release handle of plc variable
    nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlVar), &lHdlVar);
    if (nErr) printf("Error: AdsSyncWriteReq: %d \n", nErr);

    nErr = AdsPortClose();
    if (nErr) printf("Error: Ads: Close port: %d\n", nErr);

    getchar();
}


Beckhoff Information System - German

TwinCAT ADS-DLL: Beispiele

Lesen und Schreiben von DATE/TIME-Variablen

Download

Beschreibung

Die SPS enthält die TIME-Variable MAIN.Time1 und die DT-Variable MAIN.Date1. Dieses Beispielprojekt zeigt, wie diese Werte gelesen, angezeigt und geschrieben werden können:

Quellcode

#include <time.h>

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

#define TIME_LENGHT 56
#define DATE_LENGHT 62
#define MON_START 1
#define YEAR_START 1900

int _tmain(int argc, _TCHAR* argv[])
{
    long lErr, lPort;
    long lTime, lMs, lSek, lMin, lHour, lDay;
    AmsAddr  Addr;
    PAmsAddr pAddr = &Addr;
    DWORD dwTime, dwDate;
    ULONG     lHdlTime, lHdlDate;

    // Declare PLC variable
    char      szPlcTime []={"MAIN.Time1"};
    char      szPlcDate []={"MAIN.Date1"};

    // Open the communication
    lPort = AdsPortOpen();
    lErr = AdsGetLocalAddress(pAddr);
    if(lErr) printf_s((char*)"Error: Getting local adress: 0x%i  \n", lErr);

    // TwinCAT2 RTS1 Port = 801, TwinCAT3 RTS1 Port = 851
      pAddr->port = 801;

    // Get variable handle
        lErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlTime), &lHdlTime, sizeof(szPlcTime), szPlcTime);
    lErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlDate), &lHdlDate, sizeof(szPlcDate), szPlcDate);

    // Read from MAIN.Time1
    lErr = AdsSyncReadReq(pAddr,
                            ADSIGRP_SYM_VALBYHND,	// IndexGroup
                            lHdlTime,			// IndexOffset
                            0x4,			// Size of DWORD
                            &dwTime);
    if(lErr) printf_s((char*)"Error: Read time variable: 0x%i  \n", lErr);

    //Convert DWORD to Time
    lTime = (long)dwTime;
    lMs		= (lTime % 1000);
    lSek	= (lTime / 1000) % 60;
    lMin	= (lTime / 60000) % 60;
    lHour	= (lTime / 3600000) % 24;
    lDay	= (lTime / 86400000) % 365;

    wchar_t szTime[TIME_LENGHT];
    wsprintf(szTime, L"Time from PLC: %dd %dh %dm %ds %dms \n",
                        lDay,
                        lHour,
                        lMin,
                        lSek,
                        lMs);
    wprintf_s(szTime);

    //Write to MAIN.Time1
    //Manipulate DWORD for demonstration
    dwTime += 3600000;  //Add 3600000ms (One hour)

    //AdsWrite
    lErr = AdsSyncWriteReq(pAddr,
                            ADSIGRP_SYM_VALBYHND,	//IndexGroup
                            lHdlTime,			//IndexOffset
                            0x4,
                            &dwTime);
    if(lErr) printf_s((char*)"Error: Write time variable: 0x%i \n", lErr);

    //Read from MAIN.Date1
    //AdsRead
    lErr = AdsSyncReadReq(pAddr,
                            ADSIGRP_SYM_VALBYHND,	//IndexGroup
                            lHdlDate,			//IndexOffset
                            0x4,
                            &dwDate);
    if(lErr) printf_s((char*)"Error: Read date variable: 0x%i  \n", lErr);

    //Convert long to date
    time_t tDate(dwDate);
    tm tmDate;

    gmtime_s(&tmDate, &tDate);

    wchar_t szDate[DATE_LENGHT];
    wsprintf(szDate, L"Date from PLC: %d/%d/%d %d:%d:%d \n",
                tmDate.tm_mday,
                tmDate.tm_mon + MON_START,
                tmDate.tm_year + YEAR_START,
                tmDate.tm_hour,
                tmDate.tm_min,
                tmDate.tm_sec);
    wprintf_s(szDate);

    //Write to MAIN.Date1
    //Manipulate DWORD for demonstration
    dwDate += 3600; //Add 3600s (One hour)

    //AdsWrite
    lErr = AdsSyncWriteReq(pAddr,
                            ADSIGRP_SYM_VALBYHND,	//IndexGroup
                            lHdlDate,			//IndexOffset
                            0x4,
                            &dwDate);
    if(lErr) printf_s((char*)L"Error: Write time variable: 0x%i \n", lErr);

    //Releases handles of plc variable
    lErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlTime), &lHdlTime);
    if (lErr) printf("Error: AdsSyncWriteReq: %d \n", lErr);
    lErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, 0, sizeof(lHdlDate), &lHdlDate);
    if (lErr) printf("Error: AdsSyncWriteReq: %d \n", lErr);

    //Close the communication
    lErr = AdsPortClose();
    if(lErr) printf_s((char*)L"Error: Closing connection: 0x%i \n", lErr);

    printf_s("\nPress enter to exit..");
    getchar();
}


TcAdsDllCe Setupe

TwinCAT ADS-DLL

Einbindung in Visual Studio

Um die TcAdsDllCe in ihrem Visual Studio Projekt zu verwenden, müssen Sie folgende Schritte durchführen:

  • Project Settings Dialog öffnen
  • Link Tab auswählen und im Editierfeld Object/library modules die für ihren Prozessortyp spezifische Bibliothek auswählen:

- StrongArm: TwinCATDirectory\AdsApi\TcAdsDll\CE\lib\arm\TcAdsDllCe.lib

- MIPS: StrongArm: TwinCATDirectory\AdsApi\TcAdsDll\CE\lib\mips\TcAdsDllCe.lib

- X86: TwinCATDirectory\AdsApi\TcAdsDll\CE\lib\x86\TcAdsDllCe.lib

Die Methoden der TcAdsDllCE.dll sind kompatibel zur TcAdsDll.dll.

Zusätzlich benötigen sie das passende SDK für Windows CE. Sie finden es auf unseren FTP Server.

Verbindung mit Visual Studio

Visual Studio kann sich direkt mit einem Windows CE Gerät verbinden, um z.B die Applikation zu übertragen und online zu debuggen.

Lese Sie zum Konfigurieren der Verbindung bitte folgenden MSDN-Artikel.


TcAdsDllCE Samples Übersicht

TwinCAT ADS-DLL: CE Beispiele

Übersicht

Beschreibung | Quelltexte
Beispiel 1: Auslesen einer SPS-Variablen | AdsReadTestPPC01.exe
Beispiel 2: Wert in eine SPS-Variable schreiben | AdsWriteTestPPC01.exe
Beispiel 3: Zugriff per Variablenname | AdsReadWriteTestPPC01.exe
Beispiel 4: Ereignisgesteuertes Lesen | AdsDNotificTestPPC01.exe


ADS Return Codes

Error codes: 0x000..., 0x500..., 0x700..., 0x1000..., 0x274C...

Global Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x0 | 0 | no error |  |
0x1 | 1 | Internal error |  |
0x2 | 2 | No Rtime |  |
0x3 | 3 | Allocation locked memory error |  |
0x4 | 4 | Insert mailbox error | No ADS mailbox was available to process this message. | Reduce the number of ADS calls (e.g ADS-Sum commands or Max Delay Parameter)
0x5 | 5 | Wrong receive HMSG |  |
0x6 | 6 | target port not found | ADS Server not started |
0x7 | 7 | target machine not found | Missing ADS routes |
0x8 | 8 | Unknown command ID |  |
0x9 | 9 | Bad task ID |  |
0xA | 10 | No IO |  |
0xB | 11 | Unknown ADS command |  |
0xC | 12 | Win 32 error |  |
0xD | 13 | Port not connected |  |
0xE | 14 | Invalid ADS length |  |
0xF | 15 | Invalid AMS Net ID |  |
0x10 | 16 | Low Installation level |  |
0x11 | 17 | No debug available |  |
0x12 | 18 | Port disabled |  |
0x13 | 19 | Port already connected |  |
0x14 | 20 | ADS Sync Win32 error |  |
0x15 | 21 | ADS Sync Timeout |  |
0x16 | 22 | ADS Sync AMS error |  |
0x17 | 23 | ADS Sync no index map |  |
0x18 | 24 | Invalid ADS port |  |
0x19 | 25 | No memory |  |
0x1A | 26 | TCP send error |  |
0x1B | 27 | Host unreachable |  |
0x1C | 28 | Invalid AMS fragment |  |

Router Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x500 | 1280 | ROUTERERR_NOLOCKEDMEMORY | No locked memory can be allocated |
0x501 | 1281 | ROUTERERR_RESIZEMEMORY | The size of the router memory could not be changed |
0x502 | 1282 | ROUTERERR_MAILBOXFULL | The mailbox has reached the maximum number of possible messages. The current sent message was rejected | Check the connection between the communication partners
0x503 | 1283 | ROUTERERR_DEBUGBOXFULL | The mailbox has reached the maximum number of possible messages. The sent message will not be displayed in the debug monitor | Check the connection to the debug monitor
0x504 | 1284 | ROUTERERR_UNKNOWNPORTTYPE | The port type is unknown |
0x505 | 1285 | ROUTERERR_NOTINITIALIZED | Router is not initialised |
0x506 | 1286 | ROUTERERR_PORTALREADYINUSE | The desired port number is already assigned |
0x507 | 1287 | ROUTERERR_NOTREGISTERED | Port not registered |
0x508 | 1288 | ROUTERERR_NOMOREQUEUES | The maximum number of Ports reached |
0x509 | 1289 | ROUTERERR_INVALIDPORT | The port is invalid. |
0x50A | 1290 | ROUTERERR_NOTACTIVATED | TwinCAT Router not active |
0x50B | 1291 | ROUTERERR_FRAGMENTBOXFULL |  |
0x50C | 1292 | ROUTERERR_FRAGMENTTIMEOUT |  |
0x50D | 1293 | ROUTERERR_TOBEREMOVED |  |

General ADS Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x700 | 1792 | error class <device error> |  |
0x701 | 1793 | Service is not supported by server |  |
0x702 | 1794 | invalid index group |  |
0x703 | 1795 | invalid index offset |  |
0x704 | 1796 | reading/writing not permitted |  |
0x705 | 1797 | parameter size not correct |  |
0x706 | 1798 | invalid parameter value(s) |  |
0x707 | 1799 | device is not in a ready state |  |
0x708 | 1800 | device is busy |  |
0x709 | 1801 | invalid context (must be in Windows) |  |
0x70A | 1802 | out of memory |  |
0x70B | 1803 | invalid parameter value(s) |  |
0x70C | 1804 | not found (files, ...) |  |
0x70D | 1805 | syntax error in command or file |  |
0x70E | 1806 | objects do not match |  |
0x70F | 1807 | object already exists |  |
0x710 | 1808 | symbol not found |  |
0x711 | 1809 | symbol version invalid | Onlinechange | Release handle and get a new one
0x712 | 1810 | server is in invalid state |  |
0x713 | 1811 | AdsTransMode not supported |  |
0x714 | 1812 | Notification handle is invalid | Onlinechange | Release handle and get a new one
0x715 | 1813 | Notification client not registered |  |
0x716 | 1814 | no more notification handles |  |
0x717 | 1815 | size for watch too big |  |
0x718 | 1816 | device not initialized |  |
0x719 | 1817 | device has a timeout |  |
0x71A | 1818 | query interface failed |  |
0x71B | 1819 | wrong interface required |  |
0x71C | 1820 | class ID is invalid |  |
0x71D | 1821 | object ID is invalid |  |
0x71E | 1822 | request is pending |  |
0x71F | 1823 | request is aborted |  |
0x720 | 1824 | signal warning |  |
0x721 | 1825 | invalid array index |  |
0x722 | 1826 | symbol not active | Onlinechange | Release handle and get a new one
0x723 | 1827 | access denied |  |
0x724 | 1828 | missing license |  | Activate license for TwinCAT 3 function
0x72c | 1836 | exception occured during system start |  | Check each device transistions
0x740 | 1856 | Error class <client error> |  |
0x741 | 1857 | invalid parameter at service |  |
0x742 | 1858 | polling list is empty |  |
0x743 | 1859 | var connection already in use |  |
0x744 | 1860 | invoke ID in use |  |
0x745 | 1861 | timeout elapsed |  | Check ADS routes of sender and receiver and your firewall setting
0x746 | 1862 | error in win32 subsystem |  |
0x747 | 1863 | Invalid client timeout value |  |
0x748 | 1864 | ads-port not opened |  |
0x750 | 1872 | internal error in ads sync |  |
0x751 | 1873 | hash table overflow |  |
0x752 | 1874 | key not found in hash |  |
0x753 | 1875 | no more symbols in cache |  |
0x754 | 1876 | invalid response received |  |
0x755 | 1877 | sync port is locked |  |

RTime Error Codes

Hex | Dec | Description | Possible Causes
0x1000 | 4096 | RTERR_INTERNAL | Internal fatal error in the TwinCAT real-time system
0x1001 | 4097 | RTERR_BADTIMERPERIODS | Timer value not vaild
0x1002 | 4098 | RTERR_INVALIDTASKPTR | Task pointer has the invalid value ZERO
0x1003 | 4099 | RTERR_INVALIDSTACKPTR | Task stack pointer has the invalid value ZERO
0x1004 | 4100 | RTERR_PRIOEXISTS | The demand task priority is already assigned
0x1005 | 4101 | RTERR_NOMORETCB | No more free TCB (Task Control Block) available. Maximum number of TCBs is 64
0x1006 | 4102 | RTERR_NOMORESEMAS | No more free semaphores available. Maximum number of semaphores is 64
0x1007 | 4103 | RTERR_NOMOREQUEUES | No more free queue available. Maximum number of queue is 64
0x1008 | 4104 | TwinCAT reserved. |
0x1009 | 4105 | TwinCAT reserved. |
0x100A | 4106 | TwinCAT reserved. |
0x100B | 4107 | TwinCAT reserved. |
0x100C | 4108 | TwinCAT reserved. |
0x100D | 4109 | RTERR_EXTIRQALREADYDEF | An external synchronisation interrupt is already applied
0x100E | 4110 | RTERR_EXTIRQNOTDEF | No external synchronsiation interrupt applied
0x100F | 4111 | RTERR_EXTIRQINSTALLFAILED | The apply of the external synchronisation interrupt failed
0x1010 | 4112 | RTERR_IRQLNOTLESSOREQUAL | Call of a service function in the wrong context
0x1017 | 4119 | RTERR_VMXNOTSUPPORTED | Intel VT-x extension is not supported.
0x1018 | 4120 | RTERR_VMXDISABLED | Intel VT-x extension is not enabled in BIOS.
0x1019 | 4121 | RTERR_VMXCONTROLSMISSING | Missing feature in Intel VT-x extension.
0x101A | 4122 | RTERR_VMXENABLEFAILS | Enabling Intel VT-x fails.

TCP Winsock Error Codes

Hex | Dec | Description | Possible Causes | Solution
0x274c | 10060 | A socket operation was attempted to an unreachable host | Host unreachable | Check network connection via ping
0x274d | 10061 | A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. | Host unreachable | Check network connection via ping
0x2751 | 10065 | No connection could be made because the target machine actively refused it |  |
 |  | Further Winsock error codes: Win32 Error Codes |  |


TcAdsDllCe Overview

TwinCAT ADS-DLLCe

TcAdsDllCe

Die TcAdsDllCe erlaubt ADS-Kommunikation über TCP/IP auf Windows CE Geräten.

Die TcAdsDllCe unterstützt die Plattform Pocket PC 3.0 und Handheld PC 2000 auf folgenden Geräten:

Prozessor | Installations-Dateien
StrongARM | setup\TcAdsDllCe.StrongARM.CAB
MIPS | setup\TcAdsDllCe.MIPS.CAB
Intel I486 compatible | setup\TcAdsDllCe.I486.CAB
Intel Pentium compatible | setup\TcAdsDllCe.I586.CAB

Um die TcAdsDllCe.dll auf ihrem Gerät zu installieren, sind folgende Schritte durchzuführen:

  • Mit Hilfe des Explorers und Active Sync die entsprechende TcAdsDllCe.XXX.CAB in das Verzeichnis \Windows\Start Menu ihres CE Gerätes kopieren.
  • Auf dem CE-Gerät die Datei TcAdsDllCe.XXX.CAB vom Start Menu aus starten.
  • Danach kann die Datei wieder entfernt werden.