Linking into Visual C++

TwinCAT TcAdsDLL: Reference

联接Visual C++

所需文件

你需要用到以下文件来开发程序:

  • TcAdsDll.dll - 动态函数库
  • TcAdsDll.lib - 为了使用 TcAdsDll.dll所要联接的库
  • TcAdsApi.h - ADS 函数的声明
  • TcAdsDef.h - 结构体和常量的声明

TcAdsDll.dll 位于 Windows NT/2000/XP 'System32' 目录中. TcAdsDll.lib, TcAdsApi.h 和 TcAdsDef.h 位于 TwinCAT 'ADS Api\TcAdsDll\Include' 和 'ADS Api\TcAdsDll\Lib' 目录中.

需要包含的头文件.

为了能够在项目中使用 TcAdsDll 的功能,项目的头文件中必须含有 文件TcAdsApi.h 和 TcAdsDef.h .

#include "C:\TwinCAT\ADS Api\TcAdsDll\Include\tcAdsDef.h"

#include "C:\TwinCAT\ADS Api\TcAdsDll\Include\TcAdsApi.h"

在项目中加载库

为了能够使用TcAdsDll的功能，你必须加载 TcAdsDll.lib.

在 Visual Studio中，选择“Project Settings” 菜单项. 在“ Project Settings ”对话框中的“Settings For”中选择: All configuration. 同时在“Object/library modules” 一栏中添加TcAdsDll.Lib 的路径.


Overview

TwinCAT TcAdsDLL: Examples

概览（本页所有程序基于）

描述 | 源程序正文
例子 1: 读取DLL版 本号 | ADS-DLL Sample01.rar
例子 2: 同步写数据到PLC | ADS-DLL Sample02.rar
例子 3: 同步读PLC数据 | ADS-DLL Sample03.rar
例子 4: 读取ADS状态 | ADS-DLL Sample04.rar
例子 5: 读取 ADS信息 | ADS-DLL Sample05.rar
例子 6: 启动/停止 PLC | ADS-DLL Sample06.rar
例子 7: 读取PLC中 的数组 | ADS-DLL Sample07.rar
例子 8: 以事件驱动方式访问PLC | ADS-DLL Sample08.rar
例子 9: 以变量名方式访问PLC | ADS-DLL Sample09.rar
例子 10: 读取PLC中声明的变量 (static, see also sample 16) | ADS-DLL Sample10.rar
例子 11: 事件驱动方式监控PLC及TwinCAT Router中变量的变化状态 | ADS-DLL Sample11.rar
例子 12: 事件驱动方式监控PLC中符号表的状态 | ADS-DLL Sample12.rar
例子 13: 见示例 | ADS-DLL Sample13.rar
例子 14: 读取PLC变量的所有属性 | ADS-DLL Sample14.rar
例子 15: 见示例 |
例子 16: 高效读取PLC变量的所有属性 (dynamic) | ADS-DLL Sample16.rar
例子 17: 批量数据读写 | ADS-DLL Sample17.rar
例子 18: 删除变量句柄 |
例子 19: 见示例 |
例子 20: 向PLC写入结构体 | ADS-DLL Sample20.rar
例子 21: 读写日期/时间 | ADS-DLL Sample21.rar


Read DLL version

TwinCAT TcAdsDLL: Examples

读取DLL版本号

该程序演示如何读取了DLL的版本号.

#include <iostream.h>
#include <conio.h>
#include <windows.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\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();
}

打开示例程序'读 取DLL 版本号'.


Write flag synchronously into the PLC

TwinCAT TcAdsDLL: Examples

同步写数据到PLC

该示例演示了同步写数据到PLC.

#include <iostream.h>
#include <windows.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"

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

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

  // Read value from user that is to be written to the PLC
  cout << "Value: ";
  cin >> dwData;

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

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

打开示例程序 '同步写数据到PLC'


Read flag synchronously from the PLC

TwinCAT TcAdsDLL: Examples

同步读PLC数据

该示例程序演示了同步读PLC数据 .

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"

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

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

  // Read value from MD0 and display
  do
  {
    nErr = AdsSyncReadReq(pAddr, 0x4020, 0x0, 0x4, &dwData);
    if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
    cout << dwData << '\n';
    cout.flush();
  }
  while (getch() == '\r');    // Read the next value (use Carriage return as delimitter), stop otherwise

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

打开示例程序 ' 同步读PLC数据'


Read ADS status

TwinCAT TcAdsDLL: Examples

读取ADS状态

该程序将读取PLC当前状态. ADSSTATE类型的变量包含信息,如, PLC当前处于运行状态还是停止状态.

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"

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

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

  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'); // continue on a carriage return, finish for any other key

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

打开示例程序 '读取ADS状态'.


Read ADS information

TwinCAT TcAdsDLL: Examples

读取ADS信息

每个ADS设备都包含一个版本号和ID号.该示例程序将从PLC中读取这些信息并显示.

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"

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

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

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

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

打开示例程序' 读取ADS信息' .


Start/stop PLC

TwinCAT TcAdsDLL: Examples

启动/停止 PLC

该示例程序将启动或停止PLC中的run-time system 1.

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"

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

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

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

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

打开示例程序 'Start/stop PLC'.


Access an array in the PLC

TwinCAT TcAdsDLL: Examples

读取PLC中的数组

使用函数AdsSyncReadReq()来读取PLC中的数组. 函数AdsSyncReadReq()的变量中包含了数组的长度，数组中第一个元素的地址.

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\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';
}

打开示例程序 '读取PLC中的数组'


Event driven reading

TwinCAT TcAdsDLL: Examples

以事件驱动方式解读PLC

如果使用函数AdsSyncReadReq()连续读取来自于PLC或NC的数据,则程序的运行效率会很低, 因为该函数必须被循环调用.除了上述方法外,我们还可以通过定义notifications (messages)读取来自于PLC或NC的数据. 两种方法的区别在于TwinCAT服务器循环地传输数据还是仅仅在数据发生变化时才开始传输数据.

使用AdsSyncAddDeviceNotificationReq()函数定义Notification (messages)的相关属性.仅当PLC或NC的数据发生变化时,回调函数才被 TwinCAT自动调用.因为notifications的数量是有限制的,所以你应当确保 当notifications不再被程序使用时,才删除它.更多相关信息请查阅 AdsNotificationAttrib结构.

在该示例程序中,PLC中定义了一个word型变量,该变量绑定了一个notification. 每当该变量改变时, 回调函数被调用. 回调函数以 AdsNotificationHeader()提供的一个变量作为其参数.AdsNotificationHeader()结构包含了所有需要的信息(value, time stamp, ...).

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include <winbase.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"

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

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

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

  // Specify attributes of the notification
  adsNotificationAttrib.cbLength = 4;
  adsNotificationAttrib.nTransMode = ADSTRANS_SERVERONCHA;
  adsNotificationAttrib.nMaxDelay = 20000000; // 2sec
  adsNotificationAttrib.nCycleTime = 10000000; // 1sec

  // 32-bit variable (including pointers) that are passed to the callback function
  hUser = 3474573467;

  // Start transmission of the PLC variables
  nErr = AdsSyncAddDeviceNotificationReq(pAddr, 0x4020, 0, &adsNotificationAttrib, Callback, hUser, &hNotification);
  if (nErr) cerr << "Error: AdsSyncAddDeviceNotificationReq: " << nErr << '\n';
  cout << "Notification: " << hNotification << "\n\n";
  cout.flush();

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

  // Finish transmission of the PLC variables
  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 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 << ". Call:\n";

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

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

  // Calculate time in local representation
  GetTimeZoneInformation(&TimeZoneInformation);
  SystemTimeToTzSpecificLocalTime(&TimeZoneInformation, &SystemTime, &LocalTime);

  // Output time stamp
  cout << LocalTime.wHour << ":" << LocalTime.wMinute << ":" << LocalTime.wSecond << '.' << LocalTime.wMilliseconds <<
       " den: " << LocalTime.wDay << '.' << LocalTime.wMonth << '.' << LocalTime.wYear << '\n';

  // Size of buffer in bytes
  cout << "SampleSize: " << pNotification->cbSampleSize << '\n';

  // 32-bit variable (including pointers) that are set by AddNotification // (See main)
  cout << "hUser: " << hUser << '\n';

  // Output ADS address of the sender
  cout << "ServerNetId: ";
  for (nIndex = 0; nIndex < 6; nIndex++)
    cout << (int)pAddr->netId.b[nIndex] << ".";
  cout << "\nPort: " << pAddr->port << "\n\n";
  cout.flush();
}

打开示例程序'以事件驱动方式解读PLC'.


Access by variable name

TwinCAT ADS-DLL: Examples

以变量名方式访问PLC

该示例程序演示了如何以变量名方式访问PLC .该变量没有地址.若要进行访问必须以变量名的方式进行 . 一旦示例程序中的PLC变量值超过10,该变量会被重置成0.

所有ADS设备产生的数据在被外部访问时需要通过 IndexGroups和IndexOffset. 一个IndexGroup可以理解为一个表格的地址 . 该表格的每一个入口地址由IndexOffset确定. 以TwinCAT PLC为例, IndexGroups被存储于input/output 或flags区域. 同样的,如果要在TwinCAT PLC中使用IndexGroups 时(即PLC与PLC之间的通讯时),需要通过那些可能被赋予了地址的系统函数.

IndexGroups ADSIGRP_SYM_HNDBYNAME 和ADSIGRP_ SYM_VALBYHND 对于示例程序意义非凡. IndexGroup ADSIGRP_SYM_HNDBYNAME被用来请求一个句柄, 该句柄来自于以名字作为标识的PLC 变量. 该PLC变量可以在句柄和IndexGroup ADSIGRP_SYM_VALBYHND的帮助下被访问. 该变量的句柄被作为IndexOffset传递.

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"

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;

  // Fetch handle for an <szVar> PLC variable
  nErr = AdsSyncReadWriteReq(pAddr, ADSIGRP_SYM_HNDBYNAME, 0x0, sizeof(lHdlVar), &lHdlVar, sizeof(szVar), szVar);
  if (nErr) cerr << "Error: AdsSyncReadWriteReq: " << nErr << '\n';
  do
  {
    // Read value of a PLC variable (by handle)
    nErr = AdsSyncReadReq( pAddr, ADSIGRP_SYM_VALBYHND, lHdlVar, sizeof(nData), &nData );
    if (nErr)
      cerr << "Fehler: AdsSyncReadReq: " << nErr << '\n';
    else
      cout << "Wert: " << nData << '\n';
    cout.flush();
    if (Data > 10)
    {
      // Reset 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');  // read next value with RETURN, else end

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

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

打开示例程序'以变量名方式访问PLC' .


Read PLC variable declaration

TwinCAT ADS-DLL: Examples

读取PLC中声明的变量

读取PLC中声明的变量时,包含以下信息:

  • Variable name
  • Data type
  • Length
  • Address (IndexGroup / IndexOffset)
  • Comment

所有上述列出的信息在数据流中传递 . 在这些信息被读取前 , 第一个AdsSyncReadReq()函数用来获取变量的长度. 数据本身通过第二个AdsSyncReadReq()被传递. pchSymbols 变量是一个指针, 用来指向PLC中的定义的变量. FOR-loop 将相应的数据区域拷贝至pAdsSymbolEntry结构 . pAdsSymbolEntry结构存储了PLC变量的相关信息信息. 宏命令PADSSYMBOLNAME, PADSSYMBOLTYPE 和PADSSYMBOLCOMMENT 简化了这些数据的读取过程.

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include <assert.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\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 = AMSPORT_R0_PLC_RTS1;

  // Read the length of the variable declaration
  nErr = AdsSyncReadReq(pAddr, ADSIGRP_SYM_UPLOADINFO, 0x0, sizeof(tAdsSymbolUploadInfo), &tAdsSymbolUploadInfo);
  if (nErr) cerr << "Error: AdsSyncReadReq: " << nErr << '\n';
  pchSymbols = new char[tAdsSymbolUploadInfo.nSymSize];
  assert(pchSymbols);

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

  // Output information about the PLC variables
  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();
  }
  getch();

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

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

打开示例程序'读取PLC中声明的变量'.


Detect status change in TwinCAT router and the PLC

TwinCAT ADS-DLL: Examples

事件驱动方式监控PLC及TwinCAT Router中变量的变化状态

当一个应用程序正在运行时, 实时监控TwinCAT 和/或者TwinCAT组件的状态是一件很重要的工作; 例如, PLC是否处于运行状态 . 当TwinCAT 和/或者TwinCAT组件的状态发生改变时,利用回调函数可以很有效地侦查到这些状态的改变.
该示例程序演示了如何监控PLC (run-time system 1)和TwinCAT router的状态.
AdsAmsRegisterRouterNotification() 用来监控ADS router的状态 ,通过该函数, 使得回调函数能够在每一次TwinCAT router 状态改变时被调用. ADS router的当前状态可以通过传递来的参数被查询到.
AdsSyncAddDeviceNotificationReq() 被用来监控PLC的状态. 该函数返回给回调函数的数据代表了当前PLC的状态.

#include <iostream.h>
#include <conio.h>
#include <windows.h>
#include <winbase.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\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';
  pAddr->port = AMSPORT_R0_PLC_RTS1;

  nErr = AdsAmsRegisterRouterNotification(&RouterCall);
  if (nErr) cerr << "Error: AdsAmsRegisterRouterNotification: " << nErr << '\n';

  // Invoke notification
  adsNotificationAttrib.cbLength       = sizeof(short);
  adsNotificationAttrib.nTransMode     = ADSTRANS_SERVERONCHA;
  adsNotificationAttrib.nMaxDelay      = 0; // jede Aenderung sofort melden
  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 ();
}

打开示例程序 '事件驱动方式监控PLC及TwinCAT Router中变量的变化状态' .


Event-Driven Detection of Changes to the Symbol Table

TwinCAT ADS-DLL: Examples

事件驱动方式监控PLC中符号表的状态

ADS设备支持符号名(PLC, NC, ...) . ADS将这些符号名储存于内部表中. 每一个符号都被分配了一个句柄.

符号句柄是必须存在的, 目的是为了访问变量 (也可参考 Example例子 9).

如果符号表改变了,当一个新的PLC程序会被写入控制器, 那些符号句柄也必须再一次被重新确定. 该示例举例说明了如何监控符号表的变化.

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include <winbase.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\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';
  pAddr->port = AMSPORT_R0_PLC_RTS1;

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

打开示例程序'事件驱动方式监控PLC中符号表的状态'.


Read PLC variable declaration

TwinCAT ADS-DLL: Examples

读取PLC变量的所有属性

当访问PLC变量声明时,将会传递以下信息:

  • Variable name
  • Data type
  • Length
  • Address (IndexGroup / IndexOffset)
  • Comment

当读取PLC变量信息时,需要调用AdsSyncReadWriteReq() . PLC变量名通过AdsSyncReadWriteReq()的参数 pWriteData 传递 . 该函数被调用后 , 在传递的信息中含有变量 pAdsSymbolEntry . PLC变量中的每一个独立的变量被存储于pAdsSymbolEntry结构.
宏命令PADSSYMBOLNAME, PADSSYMBOLTYPE和PADSSYMBOLCOMMENT简化了这些数据的读取过程.
PLC变量的数据类型通过 pAdsSymbolEntry->dataType 被 读出
如果PLC变量的数据类型是UDINT 或者UDINT的数组, PLC变量的值仍然可以被读取.

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

#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\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';
    pAddr->port = AMSPORT_R0_PLC_RTS1;

    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';
}

打开示例程序 '读取PLC变量的所有属性' .


ADS-Summenkommando: Holen und Freigeben von mehreren Handles

TwinCAT ADS-DLL: Samples

ADS批量创建,释放句柄

该示例说明了如何用ADS批量创建,释放句柄.

1. 得到句柄

首先,加载头文件.
#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"
下一步,定义一个结构 , 声明变量和分配静态内存.
    // 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;

将那些需要传递的数值写于结构体末端.

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

建立通信,获取通讯端口号及run time 1t的地址.

sum command的参数由以下几项构成:IndexGroup (0xf082) - 调用sum command, IndexOffset (0x2) - 计算sub commands, ReadLength (0x18) - 读取数据的长度, ReadData (pBuffRes) - 可读取数据的存储器, WriteLength (cbReq) - 数据长度,该长度可发送,可编辑(pBuffReq) - 包含可发送数据的存储器
    // Open communication port on the ADS router
    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';};

2. 释放句柄

定义一个结构, 声明变量 , 再次分配静态内存.
    // 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;

再次将那些需要传递的数值写于结构体末端.

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

    // Skip to next structure
    pDataRel++;

    // pDataReq-> structure 2
    pDataRel->indexGroup	= ADSIGRP_IOIMAGE_RWIB;
    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) );

释放句柄.

sum command的参数由以下几项构成:IndexGroup (0xf081) - 调用 sum command, IndexOffset (0x2) - 计算 sub commands, ReadLength (cbRelRes) - 可被读取的数据长度, ReadData (pBuffRelRes) - 可读取数据的存储器, WriteLength (cbRel) - 数据长度,该长度可发送,可编辑(pBuffRel) - 包含可发送数据的存储器 . 最后,释放句柄关闭端口
    // 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();
}

下载Visual C++ 例子:

VC++ 例子


Handle of a PLC variable

TwinCAT TcAdsDLL: Examples

删除变量句柄

该示例说明如何删除一个PLC变量的句柄

#include <iostream.h>
#include <windows.h>
#include <conio.h>
#include "AdsDef.h"
#include "AdsAPI.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;
// get 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';
// Read value of PLC variable (per 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();
// delete handle of PLC variable
nErr = AdsSyncWriteReq(pAddr, ADSIGRP_SYM_RELEASEHND, lHdlVar, 0x0,0x0);
if (nErr) cerr << "Error: AdsSyncWriteReq: " << nErr
<< '\n';

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

查看完整代码示例删除变量句柄


Beckhoff Information System - English

TwinCAT TcAdsDLL: Examples

向PLC写入结构体

该示例说明如何通过ADS向PLC写入一个结构体. 结构体内的成员的变量类型各不相同:

#include "stdafx.h"
#include "windows.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\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;

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

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

    // Write the struct to the Plc
    AdsSyncWriteReq(pAddr,
                    0x4040,		//IndexGroup
                    0x0,		//IndexOffset
                    0x13,
                    (void*) pData);
    if (nErr) printf("Error: Ads: Write struct: %d\n", nErr);

    // Close communication
    delete [] pData;

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

    getchar();
}

打开示例程序 '向PLC写入结构体' .


Beckhoff Information System - English

TwinCAT TcAdsDLL: Examples

读写日期/时间

PLC含有TIME变量MAIN.Time1和DT变量MAIN.Date1. 该示例说明如何读,写和显示这些变量:

#include "stdafx.h"
#include "ReadWriteDateTime.h"
#include "ReadWriteDateTimeDlg.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsdef.h"
#include "c:\twincat\ads api\tcadsdll\include\tcadsapi.h"

#define TIME_LENGHT 26
#define DATE_LENGHT 32
#define MON_START 1
#define YEAR_START 1900

void CReadWriteDateTimeDlg::OnBnClickedButtonread()
{
    long lErr, lPort;
    AmsAddr  Addr;
    PAmsAddr pAddr = &Addr;
    DWORD dwTime, dwDate;

    //Open the communication
    lPort = AdsPortOpen();
    lErr = AdsGetLocalAddress(pAddr);
    if(lErr) SubmitError(L"Error: Getting local adress: 0x", lErr);
    pAddr->port = AMSPORT_R0_PLC_RTS1;

    //Read from MAIN.Time1
    lErr = AdsSyncReadReq(pAddr,
                0x4040,		//IndexGroup
                0x0,		//IndexOffset
                0x4,
                &dwTime);
    if(lErr) SubmitError(L"Error: Read time variable: 0x", lErr);

    //Convert long to Time
    long lTime, lMs, lSek, lMin, lHour, lDay;

    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"%dd %dh %dm %ds %dms", lDay, lHour, lMin, lSek, lMs);

    CEdit *pTime = static_cast<CEdit*>(GetDlgItem(IDC_EDITTIME));
    pTime->SetWindowTextW(szTime);

    //Read from MAIN.Date1
    lErr = AdsSyncReadReq(pAddr,
                0x4040,		//IndexGroup
                0x4,		//IndexOffset
                0x4,
                &dwDate);
    if(lErr) SubmitError(L"Error: Read date variable: 0x", lErr);

    //Convert long to date
    time_t tDate(dwDate);
    wchar_t szDate[DATE_LENGHT];
    tm tmDate;

    gmtime_s(&tmDate, &tDate);
    wsprintf(szDate,
        L"%d/%d/%d %d:%d:%d",
        tmDate.tm_mday,
        tmDate.tm_mon + MON_START,
        tmDate.tm_year + YEAR_START,
        tmDate.tm_hour,
        tmDate.tm_min,
        tmDate.tm_sec);

    CEdit* pDate = static_cast<CEdit*>(GetDlgItem(IDC_EDITDATE));
    pDate->SetWindowTextW(szDate);

    //Close the communication
    lErr = AdsPortClose();
    if(lErr) SubmitError(L"Error: Closing connection: 0x", lErr);
}

void CReadWriteDateTimeDlg::OnBnClickedButtonwrite()
{
    long lErr, lPort;
    AmsAddr  Addr;
    PAmsAddr pAddr = &Addr;

    //Open the communication
    lPort = AdsPortOpen();
    lErr = AdsGetLocalAddress(pAddr);
    if(lErr) SubmitError(L"Error: Getting local adress: 0x", lErr);
    pAddr->port = AMSPORT_R0_PLC_RTS1;

    //Convert time to long
    wchar_t szTime[TIME_LENGHT];
    CEdit *pTime = static_cast<CEdit*>(GetDlgItem(IDC_EDITTIME));
    pTime->GetWindowTextW(szTime, TIME_LENGHT);

    long lTime, lDay, lHour, lMin, lSek, lMs;
    swscanf_s(szTime,
            L"%dd %dh %dm %ds %dms",
            &lDay, &lHour, &lMin, &lSek, &lMs);
    lTime = lDay * 86400000 + lHour * 3600000 + lMin * 60000 + lSek * 1000 + lMs;

    //Write to MAIN.Time1
    lErr = AdsSyncWriteReq(pAddr,
                0x4040,		//IndexGroup
                0x0,		//IndexOffset
                0x4,
                &lTime);
    if(lErr) SubmitError(L"Error: Write time variable: 0x", lErr);

    //Convert date to long
    wchar_t szDate[DATE_LENGHT];
    CEdit *pDate = static_cast<CEdit*>(GetDlgItem(IDC_EDITDATE));
    pDate->GetWindowTextW(szDate, DATE_LENGHT);

    tm tmDate;
    time_t tDate = time(0);

    swscanf_s(szDate,
            L"%d/%d/%d %d:%d:%d",
            &tmDate.tm_mday,
            &tmDate.tm_mon,
            &tmDate.tm_year,
            &tmDate.tm_hour,
            &tmDate.tm_min,
            &tmDate.tm_sec);

    tmDate.tm_mon = tmDate.tm_mon - MON_START;
    tmDate.tm_year = tmDate.tm_year - YEAR_START;

    //Adjust to current timezone and daylight saving time status
    tDate = mktime(&tmDate);
    TIME_ZONE_INFORMATION tzi;
    GetTimeZoneInformation(&tzi);
    tDate = tDate + abs(tzi.DaylightBias + tzi.Bias) * 60;

    //Write to MAIN.Date1
    lErr = AdsSyncWriteReq(pAddr,
                0x4040,		//IndexGroup
                0x4,		//IndexOffset
                0x4,
                &tDate);
    if(lErr) SubmitError(L"Error: Write time variable: 0x", lErr);

    //Close the communication
    lErr = AdsPortClose();
    if(lErr) SubmitError(L"Error: Closing connection: 0x", lErr);
}

void CReadWriteDateTimeDlg::SubmitError(LPCTSTR sDesc, long lErr)
{
    wchar_t szMessage[MAX_PATH];
    wsprintf(szMessage, L"%s%i", sDesc, lErr);

    CStatic *pEntry = static_cast<CStatic*>(GetDlgItem(IDC_STATICERROR));
    pEntry->SetWindowTextW(szMessage);
}

打开示例程序 '读写日期/时间'.


Linking in Embedded Visual C++

TwinCAT TcAdsDLLCe: Reference

联接嵌入式Visual C++

为了在你的嵌入式 Embedded Visual C++项目中使用TcAdsDllCe, 你必须遵循以下步骤:

  • 打开Project Settings 对话框
  • 选择Link标签,在Object/library modules编辑框中添加您处理器中的库路径:

- StrongArm: TwinCATDirectory\ADS Api\AdsDllCe\lib\arm\TcAdsDllCe.lib

- MIPS: StrongArm: TwinCATDirectory\ADS Api\AdsDllCe\lib\mips\TcAdsDllCe.lib

- X86: TwinCATDirectory\ADS Api\TcAdsDllCe\lib\x86\TcAdsDllCe.lib

TcAdsDllCE.dll的相关函数与TcAdsDll.dll的相关函数兼容.


TcAdsDllCE Samples Übersicht

TwinCAT TcAdsDLLCe: Examples

概览

描述 | 源程序
例子1: 读取SPS变量的值 | AdsReadTestPPC01.rar
例子2: 写入SPS变量的值 | AdsWriteTestPPC01.rar
例子3: 变量名方式访问 | AdsReadWriteTestPPC01.exe
例子4: 事件驱动方式读取变量 | AdsDNotificTestPPC01.exe


Adaptations for the Measurement Studio

To be able to use the TcAdsDll.dll in Measurement Studio, one has to include the header file def.h. This file contains missing definitions and the the structure __int64, that are not supported by the Measurement Studio. The necessary modifications are demonstrated with the help of a small sample program. The example Ereignisgesteuertes Lesen has been used as template.

Heade file def.h:

#define __inline
#define _declspec __declspec

typedef struct
{
unsigned long LowPart;
unsigned long HighPart;
} __int64;

Sample program:

#include <ansi_c.h>
#include <windows.h>
#include <winbase.h>
#include <utility.h>

#include "def.h"

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

static void __stdcall NotificationCallback(AmsAddr*pAddr, AdsNotificationHeader*
pNotification, unsigned long hUser)
{
SYSTEMTIME SystemTime, LocalTime;
FILETIME FileTime;
TIME_ZONE_INFORMATION TimeZoneInformation;

FileTime.dwLowDateTime = pNotification->nTimeStamp.LowPart;
FileTime.dwHighDateTime = pNotification->nTimeStamp.HighPart;
FileTimeToSystemTime(&FileTime, &SystemTime);

// Zeit umrechnen in lokaler Zeiteinheit
GetTimeZoneInformation(&TimeZoneInformation);
SystemTimeToTzSpecificLocalTime(&TimeZoneInformation, &SystemTime,
&LocalTime);

// Zeitstempel ausgeben
printf("%i:%i:%i.%i den: %i.%i.%i\n",LocalTime.wHour,
LocalTime.wMinute, LocalTime.wSecond, LocalTime.wSecond,
LocalTime.wDay, LocalTime.wMonth, LocalTime.wYear) ;

printf("NotificationCallback: nValue = %i\n", *(int *)pNotification->data);
}

int main(int argc, char *argv[])
{
AdsNotificationAttrib adsNotificationAttrib;
int nValue = 0;
int nErr = ADSERR_NOERR;
int port = AdsPortOpen();
int hNotification;
unsigned long hUser;

AmsNetId netId = {172,16,8,56,1,1};
AmsAddr addr;
addr.netId = netId;
addr.port = 801;

nErr = AdsSyncReadReq( &addr, 0x4020, 0x0, sizeof(nValue), &nValue);

if( nErr != ADSERR_NOERR )
printf("Error AdsSyncRead:%i\n", nErr);
else
printf("nValue = %i\n",nValue);

adsNotificationAttrib.cbLength = 4;
adsNotificationAttrib.nTransMode = ADSTRANS_SERVERONCHA;
adsNotificationAttrib.nMaxDelay = 20000000; // 2sec
adsNotificationAttrib.nCycleTime = 10000000; // 1sec

hUser = 3474573467;

nErr = AdsSyncAddDeviceNotificationReq( &addr, 0x4020, 0x0, &adsNotificationAttrib,
NotificationCallback, hUser, &hNotification);

if( nErr != ADSERR_NOERR )
{
printf("Error AdsSyncAddDeviceNotificationReq:%i\n",
nErr);
return 0;
}

while(!KeyHit());

nErr = AdsSyncDelDeviceNotificationReq( &addr, hNotification);
if( nErr != ADSERR_NOERR )
printf("Error AdsSyncDelDeviceNotificationReq:%i\n",
nErr);

AdsPortClose();

return 0;
}

Unpack the 'Adaptations for the Measurement Studio' sample.


Upload PLC-variabledeclaration (dynamic)

TwinCAT ADS-DLL: Sample

Upload PLC-variabledeclaration (dynamic) (2/2)

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)
    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.
  • comment

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

// Read major symbol information 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.

See full coded sample 'SPS-Variablendeklaration (dynamisch) auslesen'


TwinCAT DataBase Server CE

Übersicht