Notes on the documentation

This description is only intended for the use of trained specialists in control and automation engineering who are familiar with the applicable national standards.
It is essential that the following notes and explanations are followed when installing and commissioning these components.

The responsible staff must ensure that the application or use of the products described satisfy all the requirements for safety, including all the relevant laws, regulations, guidelines and standards.

Disclaimer

The documentation has been prepared with care. The products described are, however, constantly under development.
For that reason the documentation is not in every case checked for consistency with performance data, standards or other characteristics.
In the event that it contains technical or editorial errors, we retain the right to make alterations at any time and without warning.
No claims for the modification of products that have already been supplied may be made on the basis of the data, diagrams and descriptions in this documentation.

Trademarks

Beckhoff®, TwinCAT®, EtherCAT®, Safety over EtherCAT®, TwinSAFE® and XFC® are registered trademarks of and licensed by Beckhoff Automation GmbH.
Other designations used in this publication may be trademarks whose use by third parties for their own purposes could violate the rights of the owners.

Patent Pending

The TwinCAT Technology is covered, including but not limited to the following patent applications and patents:
EP0851348, US6167425 with corresponding applications or registrations in various other countries.

Copyright

© Beckhoff Automation GmbH & Co. KG, Germany.
The reproduction, distribution and utilization of this document as well as the communication of its contents to others without express authorization are prohibited.
Offenders will be held liable for the payment of damages. All rights reserved in the event of the grant of a patent, utility model or design.


Übersicht

TwinCAT Sample - Java

Overview

TcJavaToAds

Additional information can be found in the TwinCAT Ads Java Reference.

Description | Sources
Example 1: Using the AdsToJava.dll | Sample01.zip
Example 2: Access by variable name | Sample02.zip
Example 3: Access an array in the PLC | Sample03.zip
Example 4: Transmitting structures to the PLC | Sample04.zip
Example 5: Reading the PLC variable declaration of an individual variable | Sample05.zip
Example 6: Write flag synchronously into the PLC | Sample06.zip
Example 7: Read flag synchronously from the PLC | Sample07.zip
Example 8: Delete a handle of a PLC variable | Sample08.zip
Example 9: Event driven reading | Sample09.zip
Example 10: Reading of SMB values | Sample10.zip
Example 11: Event-Driven Detection of Changes to the Symbol Table | Sample11.zip
Example 12: ADS-sum command: Get and release several handles | Sample12.zip
Example 13: ADS-sum command: Reading and writing several variables | Sample13.zip

ADS Web Service

Description | Sources
Example 1: Creating a consumers to read and write PLC-Variables in Java | Sample01.zip


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 ADS 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 |  |


Using the AdsToJava.dll

Notice: If you want use callback functions you have to be sure that you are using the AdsToJava.dll 1.1.0.2 or greater!

Task

The PLC contains values that will be read and written from a Java Client using the AdsToJava.dll.

Description

This Sample program uses the AdsToJava.dll to read and write from the PLC.
The class CallAdsFuncs uses the de.beckhoff.jni package to implement the Dll.

To compile and run this this sample you have to use following commands (the path to javac/java has to be set in the environment path):

javac -classpath "lib/TcJavaToAds.jar;classes" -d . *.java
java -classpath "lib/TcJavaToAds.jar;classes" sample1/Application1

download sample

CallAdsFuncs.java

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.JNILong;
import de.beckhoff.jni.tcads.AmsAddr;
import de.beckhoff.jni.tcads.AdsVersion;
import de.beckhoff.jni.tcads.AdsCallDllFunction;

public class CallAdsFuncs {
  public CallAdsFuncs() {
  }

  long port = 0;
  AmsAddr addr= new AmsAddr();
  long hUser = 0;
  JNILong pNotification = new JNILong();

  public long openPort(boolean localNetAddr, String txtNetString, int Prt) {
      if (port == 0) {
        AdsVersion lAdsVersion = AdsCallDllFunction.adsGetDllVersion();
        System.out.println("AdsVersion: " + new Integer(lAdsVersion.getVersion())
                           + "." + new Integer(lAdsVersion.getRevision())
                           + "." + lAdsVersion.getBuild());

        port = AdsCallDllFunction.adsPortOpen();
        //System.out.println("Port: " + port);

        if (localNetAddr == true) {
          addr.setNetIdString(txtNetString);
        }
        else {
          long nErr = AdsCallDllFunction.getLocalAddress(addr); //local netid
          if (nErr != 0) {
            System.out.println("getLocalAddress() failed with " + nErr);
            AdsCallDllFunction.adsPortClose();
            return 0;
          }
        }
        addr.mPort = Prt;
      }
    return port;
  }

  public long closePort(){
    if (port != 0){
      long nErr = AdsCallDllFunction.adsPortClose();
      //System.out.println("Ads port closed");

      port = 0;

      if (nErr != 0)
        System.out.println("adsPortClose() failed with " + nErr);
    }
    return port;
  }

  public long readByIGrpOffs(JNIByteBuffer databuff,long lj_idxGrp,long lj_idxOffs){
    long nErr = 0;

    //read by IndexGroup and IndexOffset
    if (port != 0)
      nErr = AdsCallDllFunction.adsSyncReadReq(addr, lj_idxGrp, lj_idxOffs,
        databuff.getUsedBytesCount(), databuff);
    else
      nErr = 1864; // error 1864 (0x748) ads-port not opened

    return nErr;
  }

  public long writeByIGrpOffs(JNIByteBuffer databuff,long lj_idxGrp,long lj_idxOffs){
    long nErr = 0;

    //write by IndexGroup and IndexOffset
    if (port != 0)
      nErr = AdsCallDllFunction.adsSyncWriteReq(addr, lj_idxGrp, lj_idxOffs,
        databuff.getUsedBytesCount(), databuff);
    else
      nErr = 1864; // error 1864 (0x748) ads-port not opened

    return nErr;
  }

  public long getHandleBySymbol(JNIByteBuffer hdlbuff, JNIByteBuffer symbuff){
    long nErr = 0;

    //get handle by symbol name (symbol like "MAIN.iCounter" name in symbuff)
    if (port != 0)
      nErr = AdsCallDllFunction.adsSyncReadWriteReq(addr, 0xF003, 0x0,
          hdlbuff.getUsedBytesCount(), hdlbuff, //buffer for getting handle
          symbuff.getUsedBytesCount(), symbuff); //buffer containg symbolpath
    else
      nErr = 1864; // error 1864 (0x748) ads-port not opened

    return nErr;
  }

  public long readByHandle(JNIByteBuffer databuff, long symHandle){
    long nErr = 0;

    if (port != 0)
    { // read variable by handle
      if (symHandle != 0)
        nErr = AdsCallDllFunction.adsSyncReadReq(addr, 0xF005, symHandle,
          databuff.getUsedBytesCount(), databuff);
      else
        nErr = 1809; // error 1809 (0x711) invalid symbol handle
    }
    else
      nErr = 1864; // error 1864 (0x748) ads-port not opened

    return nErr;
  }

  public long writeByHandle(JNIByteBuffer databuff, long symHandle){
    long nErr = 0;

    if (port != 0)
    { //write variable by handle
      if (symHandle != 0)
        nErr = AdsCallDllFunction.adsSyncWriteReq(addr, 0xF005, symHandle,
          databuff.getUsedBytesCount(), databuff);
      else
        nErr = 1809; // error 1809 (0x711) invalid symbol handle
    }
    else
      nErr = 1864; // error 1864 (0x748) ads-port not opened

    return nErr;
  }

  public long readBySymbol(JNIByteBuffer databuff, JNIByteBuffer symbuff){
    JNIByteBuffer hdlbuff = new JNIByteBuffer(4);
    long nErr = 0;
    long symHandle = 0;

    if (port != 0)
    {
      //get handle by symbol name (symbol like "MAIN.iCounter" name in symbuff)
      nErr = AdsCallDllFunction.adsSyncReadWriteReq(addr, 0xF003, 0x0,
          hdlbuff.getUsedBytesCount(), hdlbuff,  //buffer for getting handle
          symbuff.getUsedBytesCount(), symbuff); //buffer containg symbolpath

      if (nErr != 0)
        return nErr;

      //get handle
      byte[] byteArr = new byte[4];
      byteArr = hdlbuff.getByteArray();
      symHandle = Convert.ByteArrToInt(byteArr);

      // read variable by handle
      nErr = AdsCallDllFunction.adsSyncReadReq(addr, 0xF005, symHandle,
        databuff.getUsedBytesCount(), databuff);
    }
    else
      nErr = 1864; // error 1864 (0x748) ads-port not opened

    return nErr;
  }

  public long writeBySymbol(JNIByteBuffer databuff, JNIByteBuffer symbuff){
    JNIByteBuffer hdlbuff = new JNIByteBuffer(4);
    long nErr = 0;
    long symHandle = 0;

    if (port != 0) {
      //get handle by symbol name (symbol like "MAIN.iCounter" name in symbuff)
      nErr = AdsCallDllFunction.adsSyncReadWriteReq(addr, 0xF003, 0x0,
          hdlbuff.getUsedBytesCount(), hdlbuff, //buffer for getting handle
          symbuff.getUsedBytesCount(), symbuff); //buffer containg symbolpath

      if (nErr != 0)
        return nErr;

     //get handle
      byte[] byteArr = new byte[4];
      byteArr = hdlbuff.getByteArray();
      symHandle = Convert.ByteArrToInt(byteArr);

      //write variable by handle
      nErr = AdsCallDllFunction.adsSyncWriteReq(addr, 0xF005, symHandle,
        databuff.getUsedBytesCount(), databuff);
    }
    else
      nErr = 1864; // error 1864 (0x748) ads-port not opened

    return nErr;
  }
}

PLC program

PROGRAM MAIN
VAR
    MdiCounter AT %MD2 : DINT := 20;
    MiCounter AT %MW2 : INT := 20;
    MsiCounter AT %MB2 : SINT := 20;
    MfCounter AT %MD6 : REAL := 1.1;
    MstrText AT %MD10 : STRING := 'qwertz';

    diCounter : DINT := 40;
    iCounter : INT := 40;
    siCounter : SINT := 40;
    fCounter : REAL := 9.3;
    strText : STRING := 'asdfgh';

    QdiCounter AT %QD8 : DINT := 60;
    QiCounter AT %QW8 : INT := 60;
    QsiCounter AT %QB8 : SINT := 60;
    QfCounter AT %QD12 : REAL := 17.4;
    QstrText AT %QD20 : STRING := 'yxcvbn';

    Timer : TON := (PT := T#100ms);
END_VAR

Timer(IN := TRUE);
IF Timer.Q THEN
Timer(IN := FALSE);

MdiCounter := MdiCounter + 1;
MfCounter := MfCounter + 1.1;

diCounter := diCounter + 1;
iCounter := iCounter + 1;
siCounter := siCounter + 1;
fCounter := fCounter + 1.1;

QdiCounter := QdiCounter + 1;
QfCounter := QfCounter + 1.1;
END_IF

Unpack the example program 'Access by variable name'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample01.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Access by variable name

The following program accesses a PLC variable that does not have an address. Access must therefore be made by the variable name:

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AmsAddr;
import de.beckhoff.jni.tcads.AdsCallDllFunction;

public class Main
{
    public static void main(String[] args)
    {
        long err;
        AmsAddr addr = new AmsAddr();
        JNIByteBuffer handleBuff = new JNIByteBuffer(Integer.SIZE / Byte.SIZE);
        JNIByteBuffer symbolBuff = new JNIByteBuffer(
                Convert.StringToByteArr("MAIN.PLCVar",true));
        JNIByteBuffer dataBuff = new JNIByteBuffer(Integer.SIZE / Byte.SIZE);

        // Open communication
        AdsCallDllFunction.adsPortOpen();
        err = AdsCallDllFunction.getLocalAddress(addr);
        addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

        if (err != 0) {
            System.out.println("Error: Open communication: 0x"
                    + Long.toHexString(err));
        } else {
            System.out.println("Success: Open communication!");
        }

        // Get handle by symbol name
        err = AdsCallDllFunction.adsSyncReadWriteReq(addr,
                                            AdsCallDllFunction.ADSIGRP_SYM_HNDBYNAME,
                                            0x0,
                                            handleBuff.getUsedBytesCount(),
                                            handleBuff,
                                            symbolBuff.getUsedBytesCount(),
                                            symbolBuff);
        if(err!=0) {
            System.out.println("Error: Get handle: 0x"
                    + Long.toHexString(err));
        } else {
            System.out.println("Success: Get handle!");
        }

        // Handle: byte[] to int
        int hdlBuffToInt = Convert.ByteArrToInt(handleBuff.getByteArray());

        // Read value by handle
        err = AdsCallDllFunction.adsSyncReadReq(addr,
                                            AdsCallDllFunction.ADSIGRP_SYM_VALBYHND,
                                            hdlBuffToInt,
                                            0x4,
                                            dataBuff);
        if(err!=0)
        {
            System.out.println("Error: Read by handle: 0x"
                                + Long.toHexString(err));
        }
        else
        {
            // Data: byte[] to int
            int intVal = Convert.ByteArrToInt(dataBuff.getByteArray());
            System.out.println("Success: PLCVar value: " + intVal);
        }

        // Release handle
        err = AdsCallDllFunction.adsSyncWriteReq(addr,
                AdsCallDllFunction.ADSIGRP_SYM_RELEASEHND,
                0,
                handleBuff.getUsedBytesCount(),
                handleBuff);

        if(err!=0) {
            System.out.println("Error: Release Handle: 0x"
                    + Long.toHexString(err));
        } else {
            System.out.println("Success: Release Handle!");
        }

        // Close communication
        err = AdsCallDllFunction.adsPortClose();
        if(err!=0) {
            System.out.println("Error: Close Communication: 0x"
                    + Long.toHexString(err));
        }

        try{
            System.in.read();
        }
        catch (Exception e){
            System.out.println("Error: Close program");
        }
    }
}

Unpack the example program 'Access by variable name'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample01.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Accessing an array in the PLC

An array, located in the PLC, is to be read by means of a read command. The variable is addressed here by indexOffset and indexGroup. The procedure is only slightly different from that for reading discrete variables:

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AmsAddr;
import de.beckhoff.jni.tcads.AdsCallDllFunction;

public class Main
{
    public static void main(String[] args)
    {
        long err;
        AmsAddr addr = new AmsAddr();
        JNIByteBuffer dataBuff = new JNIByteBuffer(200);

        // Open communication
        AdsCallDllFunction.adsPortOpen();
        err = AdsCallDllFunction.getLocalAddress(addr);
        addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

        if (err != 0) {
            System.out.println("Error: Open communication: 0x"
                    + Long.toHexString(err));
        } else {
            System.out.println("Success: Open communication!");
        }

        // Read value by IndexGroup and IndexOffset
        err = AdsCallDllFunction.adsSyncReadReq(addr,
                                            0x4020,     // Index Group
                                            0x0,        // Index Offset
                                            200,
                                            dataBuff);
        if(err!=0)
        {
            System.out.println("Error: Read by handle: 0x"
                    + Long.toHexString(err));
        }
        else
        {
            for (int i = 0; i < dataBuff.getUsedBytesCount(); i=i+2)
            {
                // PLC datatype int consists of two bytes. Get them.
                byte lowByte = dataBuff.getByteArray()[i];
                byte highByte = dataBuff.getByteArray()[i+1];
                // Create new byte[]. Little endian!
                byte[] valBytes = { lowByte, highByte };
                // Integer value: byte[] to int
                int valInt = Convert.ByteArrToShort(valBytes);
                System.out.println("Value of PLCVar[" + i/2 + "]: " + valInt);
            }
        }

        // Close communication
        err = AdsCallDllFunction.adsPortClose();
        if(err!=0) {
            System.out.println("Error: Close Communication: 0x"
                    + Long.toHexString(err));
        }

        try{
            System.in.read();
        }
        catch (Exception e){
            System.out.println("Error: Close program");
        }
    }
}

Unpack the example program 'Access by variable name'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample03.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Transmitting structures to the PLC

This example shows how to write a structure to the PLC via ADS. The elements in the structure have different data types:

import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AmsAddr;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class Main
{
    public static void main(String[] args)
    {
        long err;
        AmsAddr addr = new AmsAddr();

        TransferObject transfer = new TransferObject(); // See additional class
        JNIByteBuffer dataBuff = new JNIByteBuffer(19);

        // Open communication
        AdsCallDllFunction.adsPortOpen();
        err = AdsCallDllFunction.getLocalAddress(addr);
        addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

        if (err != 0) {
            System.out.println("Error: Open communication: 0x"
                    + Long.toHexString(err));
        } else {
            System.out.println("Success: Open communication!");
        }

        // Use JNIByteBuffer as a backing array for ByteBuffer
        ByteBuffer bb = ByteBuffer.wrap(dataBuff.getByteArray());

        // Write elements to buffer. Litte Endian!
        bb.order(ByteOrder.LITTLE_ENDIAN);

        bb.putShort(transfer.getShortVal());
        bb.putInt(transfer.getIntVal());
        bb.put(transfer.getByteVal());
        bb.putDouble(transfer.getDoubleVal());
        bb.putFloat(transfer.getFloatVal());

        // Write struct to PLC
        err = AdsCallDllFunction.adsSyncWriteReq(addr,
                0x4020,     // Index Group
                0x0,        // Index Offset
                19,
                dataBuff);
        if(err!=0) {
            System.out.println("Error: Write request: 0x"
                    + Long.toHexString(err));
        } else {
            System.out.println("Success: Write struct!");
        }

        // Close communication
        err = AdsCallDllFunction.adsPortClose();
        if(err!=0) {
            System.out.println("Error: Close Communication: 0x"
                    + Long.toHexString(err));
        }
    }
}

The object to be transferred:

public class Struct
{
    private short shortVal;
    private int intVal;
    private byte byteVal;
    private double doubleVal;
    private float floatVal;

    public TransferObject()
    {
        this.shortVal = Short.MAX_VALUE;
        this.intVal = Integer.MIN_VALUE;
        this.byteVal = 3;
        this.doubleVal = 4.1234;
        this.floatVal = 5.4321f;
    }

    public short getShortVal() {
        return shortVal;
    }
    public int getIntVal() {
        return intVal;
    }
    public byte getByteVal() {
        return byteVal;
    }
    public double getDoubleVal() {
        return doubleVal;
    }
    public float getFloatVal() {
        return floatVal;
    }
}

Unpack the example program 'Transmitting structures to the PLC'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample04.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Reading the PLC variable declaration of an individual variable

The following information is transferred when accessing the variable declaration:

  • Variable name
  • Data type
  • Length
  • Address (IndexGroup / IndexOffset)
  • Comment
The AdsSyncReadWriteReq() call is used to read the variable information. The variable name is transferred to the function via parameter pWriteData. After the call the requested information is contained in a byte buffer of type JNIByteBuffer. The buffer in handed into the constructor of a variable of type AdsSymbolEntry. In this class the individual PLC information items are stored. The fields entryLenght, typeLength and commentLength contain the length of the belonging String. The Strings are - in this order - situated behind the containing class. In the next step, the data type of the variable is evaluated via adsDatatypeString. There, the right label to the value is searched. If the data type is UDINT or ARRAY OF UDINT, the value of this variable is also read:

import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AmsAddr;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.Convert;
import de.beckhoff.jni.tcads.AdsSymbolEntry;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class Main
{
    // TODO: Check real values of each constant.
    private static final int ADST_VOID = 0;
    private static final int ADST_INT8 = 16;
    private static final int ADST_UINT8 = 17;
    private static final int ADST_INT16 = 2;
    private static final int ADST_UINT16 = 18;
    private static final int ADST_INT32 = 3;
    private static final int ADST_UINT32 = 19;
    private static final int ADST_INT64 = 20;
    private static final int ADST_UINT64 = 21;
    private static final int ADST_REAL32 = 4;
    private static final int ADST_REAL64 = 5;
    private static final int ADST_STRING = 30;
    private static final int ADST_WSTRING = 31;
    private static final int ADST_REAL80 = 32;
    private static final int ADST_BIT = 33;
    private static final int ADST_BIGTYPE = 65;
    private static final int ADST_MAXTYPES = 67;

    private static final ValueString[] adsDatatypeString = new ValueString[]{
        new ValueString(ADST_VOID, "ADST_VOID"),
        new ValueString(ADST_INT8, "ADST_INT8"),
        new ValueString(ADST_UINT8, "ADST_UINT8"),
        new ValueString(ADST_INT16, "ADST_INT16"),
        new ValueString(ADST_UINT16, "ADST_UINT16"),
        new ValueString(ADST_INT32, "ADST_INT32"),
        new ValueString(ADST_UINT32, "ADST_UINT32"),
        new ValueString(ADST_INT64, "ADST_INT64"),
        new ValueString(ADST_UINT64, "ADST_UINT64"),
        new ValueString(ADST_REAL32, "ADST_REAL32"),
        new ValueString(ADST_REAL64, "ADST_REAL64"),
        new ValueString(ADST_STRING, "ADST_STRING"),
        new ValueString(ADST_WSTRING, "ADST_WSTRING"),
        new ValueString(ADST_REAL80, "ADST_REAL80"),
        new ValueString(ADST_BIT, "ADST_BIT"),
        new ValueString(ADST_BIGTYPE, "ADST_BIGTYPE"),
        new ValueString(ADST_MAXTYPES, "ADST_MAXTYPES")
    };

    public static void main(String[] args)
    {
        long err;
        AmsAddr addr = new AmsAddr();
        JNIByteBuffer readBuff = new JNIByteBuffer(0xFFFF);
        JNIByteBuffer writeBuff;
        String userInput = null;
        BufferedReader buffReader = null;

        // Open communication
        AdsCallDllFunction.adsPortOpen();
        err = AdsCallDllFunction.getLocalAddress(addr);
        addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

        if (err != 0) {
            System.out.println("Error: Open communication: 0x"
                    + Long.toHexString(err));
        } else {
            System.out.println("Success: Open communication!");
        }

        while (true) {
            try {
                // User chooses which variable declaration to display
                System.out.print("Enter variable Name: ");

                buffReader = new BufferedReader(new InputStreamReader(System.in));
                userInput = buffReader.readLine();

                System.out.println(userInput);

                // Initialize writeBuff with user data
                writeBuff = new JNIByteBuffer(
                        Convert.StringToByteArr(userInput, false));

                // Get variable declaration
                err = AdsCallDllFunction.adsSyncReadWriteReq(
                                    addr,
                                    AdsCallDllFunction.ADSIGRP_SYM_INFOBYNAMEEX,
                                    0,
                                    readBuff.getUsedBytesCount(),
                                    readBuff,
                                    writeBuff.getUsedBytesCount(),
                                    writeBuff);

                if(err!=0) {
                    System.out.println("Error: AdsSyncReadReq: 0x"
                            + Long.toHexString(err));
                } else {
                    // Convert stream to AdsSymbolEntry
                    AdsSymbolEntry adsSymbolEntry =
                            new AdsSymbolEntry(readBuff.getByteArray());

                    // Write information to stdout
                    System.out.println("Name:\t\t"
                                        + adsSymbolEntry.getName());
                    System.out.println("Index Group:\t"
                                        + adsSymbolEntry.getiGroup());
                    System.out.println("Index Offset:\t"
                                        + adsSymbolEntry.getiOffs());
                    System.out.println("Size:\t\t"
                                        + adsSymbolEntry.getSize());
                    System.out.println("Type:\t\t"
                                        + adsSymbolEntry.getType());
                    System.out.println("Comment:\t"
                                        + adsSymbolEntry.getComment());

                    // Example: Separate treatment of data types
                    switch(adsSymbolEntry.getDataType()) {
                        case ADST_UINT32:
                            System.out.println("Datatype:\tADST_UINT32");

                            int elems = adsSymbolEntry.getSize()
                                    / (Integer.SIZE / Byte.SIZE);

                            JNIByteBuffer uint32Buff = new JNIByteBuffer(
                                                    adsSymbolEntry.getSize());

                            err = AdsCallDllFunction.adsSyncReadReq(addr,
                                    adsSymbolEntry.getiGroup(),
                                    adsSymbolEntry.getiOffs(),
                                    adsSymbolEntry.getSize(),
                                    uint32Buff);

                            if(err!=0) {
                                System.out.println("Error: AdsSyncReadReq: 0x"
                                    + Long.toHexString(err));
                            } else {
                                ByteBuffer bb = ByteBuffer.allocate(0);
                                bb = ByteBuffer.wrap(uint32Buff.getByteArray());
                                bb.order(ByteOrder.LITTLE_ENDIAN);

                                System.out.print("Value:\t\t");

                                // In case of an array print each value.
                                for (int i = 0; i < elems; i++)
                                    System.out.print(bb.getInt() + "\t");

                                System.out.println("\n");
                            }
                            break;

                        default:
                            // Iterate through ValueString[] and try to find a
                            // datatype-match
                            for (int i = 0; i < adsDatatypeString.length; i++) {
                                if (adsDatatypeString[i].getValue() ==
                                        adsSymbolEntry.getDataType()) {

                                    System.out.println("Datatype: "
                                            + "\t"
                                            + adsDatatypeString[i].getLabel());
                                }
                            }
                            break;
                    }
                }

                // User chooses to repeat process or not
                System.out.print("Exit(y/n): ");
                buffReader=new BufferedReader(new InputStreamReader(System.in));
                userInput = buffReader.readLine();

                if("y".equals(userInput.toLowerCase()))
                    break;

            } catch (Exception ex) {
                System.out.print(ex.getMessage());
            }
        }

        try {
            buffReader.close();
        } catch (Exception ex) {
            System.out.print(ex.getMessage());
        }

        // Close communication
        err = AdsCallDllFunction.adsPortClose();
        if(err!=0) {
            System.out.println("Error: Close Communication: 0x"
                    + Long.toHexString(err));
        }
    }
}

public class ValueString {
    private int value;
    private String label;

    ValueString(int value, String label) {
        this.value = value;
        this.label = label;
    }

    public int getValue() {
        return value;
    }

    public String getLabel() {
        return label;
    }
}

Unpack the example program 'Reading the PLC variable declaration of an individual variable'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample05.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Write flag synchronously into the PLC

In this example program, the value that the user has entered is written into flag double word 0:

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AmsAddr;

public class Main {
    public static void main(String[] args) {
        try{
            long err;
            AmsAddr addr = new AmsAddr();

            // Open communication
            AdsCallDllFunction.adsPortOpen();
            err = AdsCallDllFunction.getLocalAddress(addr);
            addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

            if (err != 0) {
                System.out.println("Error: Open communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Open communication!");
            }

            // Specify IndexGroup, IndexOffset and write PLCVar
            err = AdsCallDllFunction.adsSyncWriteReq(addr,
                                    0x4020,     // Index Group
                                    0x0,        // Index Offset
                                    Integer.SIZE / Byte.SIZE,
                                    new JNIByteBuffer(Convert.IntToByteArr(1024)));
            if(err!=0) {
                System.out.println("Error: Write by adress: 0x"
                        + Long.toHexString(err));
            }

            // Close communication
            err = AdsCallDllFunction.adsPortClose();
            if(err!=0) {
                System.out.println("Error: Close Communication: 0x"
                        + Long.toHexString(err));
            }

            System.out.println("Press enter to continue..");
            System.in.read();
        }
        catch (Exception ex){
            System.out.println(ex.getMessage());
        }
    }
}

Unpack the example program 'Write flag synchronously into the PLC'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample06.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Read flag synchronously from the PLC

In this example program the value in flag double word 0 in the PLC is read and displayed:

import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AmsAddr;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class Main {
    public static void main(String[] args) {
        try{
            long err;
            AmsAddr addr = new AmsAddr();
            JNIByteBuffer buff = new JNIByteBuffer(Integer.SIZE / Byte.SIZE);
            ByteBuffer bb = ByteBuffer.allocate(0);

            // Open communication
            AdsCallDllFunction.adsPortOpen();
            err = AdsCallDllFunction.getLocalAddress(addr);
            addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

            if (err != 0) {
                System.out.println("Error: Open communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Open communication!");
            }

            // Specify IndexGroup, IndexOffset and read PLCVar
            err = AdsCallDllFunction.adsSyncReadReq(addr,
                                                0x4020,     // Index Group
                                                0x0,        // Index Offset
                                                buff.getUsedBytesCount(),
                                                buff);
            if (err != 0) {
                System.out.println("Error: Read by adress: 0x"
                        + Long.toHexString(err));
            } else {
                bb = ByteBuffer.wrap(buff.getByteArray());
                bb.order(ByteOrder.LITTLE_ENDIAN);

                System.out.println("" + bb.getInt());
            }

            // Close communication
            err = AdsCallDllFunction.adsPortClose();
            if(err!=0) {
                System.out.println("Error: Close Communication: 0x"
                        + Long.toHexString(err));
            }

            System.out.println("Press enter to continue..");
            System.in.read();
        }
        catch (Exception ex){
            System.out.println(ex.getMessage());
        }
    }
}

Unpack the example program 'Read flag synchronously from the PLC'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample07.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Delete a handle of a PLC variable

This Sample shows how to delete a handle of a PLC variable:

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AmsAddr;

public class Main {
    public static void main(String[] args) {
        long err;
        AmsAddr addr = new AmsAddr();
        JNIByteBuffer symBuff;
        JNIByteBuffer handleBuff = new JNIByteBuffer(Integer.SIZE / Byte.SIZE);

        try {
            // Open communication
            AdsCallDllFunction.adsPortOpen();
            err = AdsCallDllFunction.getLocalAddress(addr);
            addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

            if (err != 0) {
                System.out.println("Error: Open communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Open communication!");
            }

            // Convert Symbol name to byte buffer
            symBuff = new JNIByteBuffer(Convert.StringToByteArr(
                    "MAIN.PLCVar",
                    true));

            // Get handle via symbol name
            err = AdsCallDllFunction.adsSyncReadWriteReq(addr,
                    AdsCallDllFunction.ADSIGRP_SYM_HNDBYNAME,
                    0,
                    handleBuff.getUsedBytesCount(),
                    handleBuff,                 //buffer for getting handle
                    symBuff.getUsedBytesCount(),
                    symBuff);                   //buffer containg symbol name

            if(err!=0) {
                System.out.println("Error: Get Handle: 0x" + Long.toHexString(err));
            } else {
                System.out.println("Success: Get Handle!");
            }

            // Release handle
            err = AdsCallDllFunction.adsSyncWriteReq(addr,
                    AdsCallDllFunction.ADSIGRP_SYM_RELEASEHND,
                    0,
                    handleBuff.getUsedBytesCount(),
                    handleBuff);

            if(err!=0) {
                System.out.println("Error: Release Handle: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Handle removed!");
            }

            // Close communication
            err = AdsCallDllFunction.adsPortClose();
            if(err!=0) {
                System.out.println("Error: Close Communication: 0x"
                        + Long.toHexString(err));
            }

            System.out.println("Press enter to continue..");
            System.in.read();

        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

Unpack the example program 'Delete a handle of a PLC variable'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample08.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Event driven reading

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

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

The following program starts a notification on flag double word 0 in the PLC. Each time the PLC variable changes, the callback function is invoked. The callback function receives a variable of type AdsNotificationHeader() as one of its parameters. This structure contains all the necessary information (value, time stamp, ...).

Advice: Don't use time intensive executions in callbacks.

import de.beckhoff.jni.AdsConstants;
import de.beckhoff.jni.JNILong;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AdsNotificationAttrib;
import de.beckhoff.jni.tcads.AdsCallbackObject;
import de.beckhoff.jni.tcads.AmsAddr;

public class Main {
    public static void main(String[] args) {
        try {
            long err;
            AmsAddr addr = new AmsAddr();
            JNILong notification = new JNILong();

            // Open communication
            AdsCallDllFunction.adsPortOpen();
            err = AdsCallDllFunction.getLocalAddress(addr);
            addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

            if (err != 0) {
                System.out.println("Error: Open communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Open communication!");
            }

            // Specify attributes of the notificationRequest
            AdsNotificationAttrib attr = new AdsNotificationAttrib();
            attr.setCbLength(Integer.SIZE / Byte.SIZE);
            attr.setNTransMode(AdsConstants.ADSTRANS_SERVERONCHA);
            attr.setDwChangeFilter(10000000);   // 1 sec
            attr.setNMaxDelay(20000000);        // 2 sec

            // Create and add listener
            AdsListener listener = new AdsListener();
            AdsCallbackObject callObject = new AdsCallbackObject();
            callObject.addListenerCallbackAdsState(listener);

            // Create notificationHandle
            err = AdsCallDllFunction.adsSyncAddDeviceNotificationReq(
                addr,
                0x4020,     // IndexGroup
                0x0,        // IndexOffset
                attr,       // The defined AdsNotificationAttrib object
                42,         // Choose arbitrary number
                notification);
            if(err!=0) {
                System.out.println("Error: Add notification: 0x"
                        + Long.toHexString(err));
            }

            // Read as long as user does not press return
            System.out.println("Press enter to continue..");
            System.in.read();

            // Delete notificationHandle
            err = AdsCallDllFunction.adsSyncDelDeviceNotificationReq(
                    addr,
                    notification);
            if(err!=0) {
                System.out.println("Error: Remove notification: 0x"
                        + Long.toHexString(err));
            }

            // Delete listener
            callObject.removeListenerCallbackAdsState(listener);

            //Close communication
            err = AdsCallDllFunction.adsPortClose();
            if(err!=0) {
                System.out.println("Error: Close Communication: 0x"
                        + Long.toHexString(err));
            }
        } catch(Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

The implementation of the CallbackListenerAdsState interface:

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.tcads.AdsNotificationHeader;
import de.beckhoff.jni.tcads.AmsAddr;
import de.beckhoff.jni.tcads.CallbackListenerAdsState;
import java.util.Date;

public class AdsListener implements CallbackListenerAdsState {
    private final static long SPAN = 11644473600000L;

    // Callback function
    public void onEvent(AmsAddr addr,
                    AdsNotificationHeader notification,
                    long user) {

        // The PLC timestamp is coded in Windows FILETIME.
        // Nano secs since 01.01.1601.
        long dateInMillis = notification.getNTimeStamp();

        // Date accepts millisecs since 01.01.1970.
        // Convert to millisecs and substract span.
        Date notificationDate = new Date(dateInMillis / 10000 - SPAN);

        System.out.println("Value:\t\t"
                + Convert.ByteArrToInt(notification.getData()));
        System.out.println("Notification:\t" + notification.getHNotification());
        System.out.println("Time:\t\t" + notificationDate.toString());
        System.out.println("User:\t\t" + user);
        System.out.println("ServerNetID:\t" + addr.getNetIdString() + "\n");
    }
}

Unpack the example program 'Event driven reading'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample09.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Reading of SMB values from TwinCAT I/O driver

Even if only TwinCAT-CP is installed on the controller or HMI device, the SMB device can get accessed by the ADS client from several applications.

The visualization allows a continuous supervision of operation temperatures, fan speeds or even the different power supply voltages. That way, the crossing of a critical treshold value can be detected (i.e. if an implementation of an advanced maintenance system is considered). The following example shows a possible implementation under Java (For proper functioning a SMB-Device has to be appended to the current System Manager configuration):

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AdsState;
import de.beckhoff.jni.tcads.AmsAddr;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.nio.ByteBuffer;
import java.util.Timer;

public class Main {
    final static short SMB_ID = 32;

    public static void main(String[] args) {
        long err = 0;
        AmsAddr addr = new AmsAddr();

        Visualization visu = new Visualization();
        visu.addWindowListener(new WindowAdapter() {
            // When the form is closed the ADS port is closed as well
            @Override
            public void windowClosing(WindowEvent e) {
                AdsCallDllFunction.adsPortClose();
            }
        });

        try {
            // Open communication
            AdsCallDllFunction.adsPortOpen();
            err = AdsCallDllFunction.getLocalAddress(addr);
            addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

            if (err != 0) {
                System.out.println("Error: Open communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Open communication!");
            }

            //Gets the device count of devices appended to active configuration
            //if TwinCAT is in run mode
            AdsState adsState = new AdsState();
            AdsState deviceState = new AdsState();

            err = AdsCallDllFunction.adsSyncReadStateReq(addr,
                    adsState,
                    deviceState);
            if(err!=0) {
                visu.setInfoMsg("Error: Read device state: 0x"
                        + Long.toHexString(err));
            }

            if (adsState.getState() != AdsState.ADSSTATE_RUN ) {
                visu.setInfoMsg("Bad TwinCAT state: "
                        + "Please restart the application.");
            } else {
                //Get devices count
                JNIByteBuffer deviceCountBuff = new JNIByteBuffer(Integer.SIZE / Byte.SIZE);
                int devCount = 0;

                err = AdsCallDllFunction.adsSyncReadReq(addr,
                        0x5000,
                        0x2,
                        deviceCountBuff.getUsedBytesCount(),
                        deviceCountBuff);
                if(err!=0) {
                    visu.setInfoMsg("Error: Read device count: 0x"
                            + Long.toHexString(err));
                }

                devCount = Convert.ByteArrToInt(
                        deviceCountBuff.getByteArray()) + 1;

                //Gets the device count of devices appended to active
                //configuration as zero-based index and gets the devices ID's of
                //all active devices for the remaining indices (inverted sort)
                JNIByteBuffer deviceBuff = new JNIByteBuffer(
                        (Short.SIZE * devCount) / Byte.SIZE);
                ByteBuffer devices;

                err = AdsCallDllFunction.adsSyncReadReq(addr,
                        0x5000,
                        0x1,
                        deviceBuff.getUsedBytesCount(),
                        deviceBuff);
                if(err!=0) {
                    visu.setInfoMsg("Error: Read devices: 0x"
                            + Long.toHexString(err));
                }

                devices = ByteBuffer.wrap(deviceBuff.getByteArray());

                for (int i = 1; i <= devCount; i++) {
                    JNIByteBuffer identNrBuff =
                            new JNIByteBuffer(Short.SIZE / Byte.SIZE);
                    short identNr = 0;

                    //gets the device identification number
                    //(Motherboard System Management Bus(SMB) => 32)
                    err = AdsCallDllFunction.adsSyncReadReq(addr,
                            0x5000 + devices.getShort(i),
                            0x7,
                            identNrBuff.getUsedBytesCount(),
                            identNrBuff);
                    if(err!=0) {
                        visu.setInfoMsg("Error: Read ident numbers: 0x"
                                + Long.toHexString(err));
                    } else {
                        identNr = Convert.ByteArrToShort(
                                identNrBuff.getByteArray());

                        //checks whether the device is of type SMB
                        if (identNr == SMB_ID) {
                            int indexGroup = 0x9000 + devices.getShort(i);

                            // If a SMB-Device was found a TimerTask is started
                            // to read the specified input from the device once
                            // every second
                            Timer tmr = new Timer();
                            tmr.schedule(new SMBTask(indexGroup, addr, visu),
                                    0,
                                    1);

                            break;
                        }
                    }
                }
            }

        } catch (Exception ex) {
            visu.setInfoMsg(ex.getMessage());
        }
    }
}

This view allows to gather the IndexOffset from the particular input.

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AmsAddr;
import java.util.TimerTask;

class SMBTask extends TimerTask {
    private int indexGroup;
    private AmsAddr addr;
    private Visualization visu;

    public SMBTask(int indexGroup, AmsAddr addr, Visualization visu) {
        this.indexGroup = indexGroup;
        this.addr = addr;
        this.visu = visu;
    }

    @Override
    public void run() {
        long err = 0;
        JNIByteBuffer valueBuff = new JNIByteBuffer(Integer.SIZE / Byte.SIZE);

        //IOffset may be adjusted to monitor different inputs e.g. temperature
        err = AdsCallDllFunction.adsSyncReadReq(this.addr,
                this.indexGroup,
                0x4,
                valueBuff.getUsedBytesCount(),
                valueBuff);

        if(err!=0) {
            visu.setInfoMsg("Error: Retrieve value: 0x" + Long.toHexString(err));
        } else {
            visu.setCurValue(Convert.ByteArrToInt(valueBuff.getByteArray()));
        }
    }
}

Unpack the example program 'Reading of SMB values from TwinCAT I/O driver'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample10.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


Event-Driven Detection of Changes to the Symbol Table

ADS devices that support symbol names (PLC, NC, ...) store those names in an internal table. A handle is assigned here to each symbol. The symbol handle is necessary in order to be able to access the variables. If the symbol table changes because, for instance, a new PLC program is written into the controller, the handles must be ascertained once again. The example below illustrates how changes to the symbol table can be detected:

import de.beckhoff.jni.AdsConstants;
import de.beckhoff.jni.JNILong;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AdsCallbackObject;
import de.beckhoff.jni.tcads.AdsNotificationAttrib;
import de.beckhoff.jni.tcads.AmsAddr;

public class Main {

    public static void main(String[] args) {
        long err = 0;
        AmsAddr addr = new AmsAddr();

        try {
            // Open communication
            AdsCallDllFunction.adsPortOpen();
            err = AdsCallDllFunction.getLocalAddress(addr);
            addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

            if (err != 0) {
                System.out.println("Error: Open communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Open communication!");
            }

            JNILong notification = new JNILong();

            AdsNotificationAttrib attr = new AdsNotificationAttrib();
            attr.setCbLength(1);
            attr.setNTransMode(AdsConstants.ADSTRANS_SERVERONCHA);
            attr.setNMaxDelay(5000000);
            attr.setNCycleTime(5000000);

            // Create and add listener
            AdsListener listener = new AdsListener();
            AdsCallbackObject callObject = new AdsCallbackObject();
            callObject.addListenerCallbackAdsState(listener);

            AdsCallDllFunction.adsSyncAddDeviceNotificationReq(addr,
                    AdsCallDllFunction.ADSIGRP_SYM_VERSION,     // IndexGroup
                    0,              // IndexOffset
                    attr,           // The defined AdsNotificationAttrib object
                    0,              // Choose arbitrary number
                    notification);
            if (err != 0) {
                System.out.println("Error: Add notification: 0x"
                        + Long.toHexString(err));
            }

            // Read as long as user does not press return
            System.out.println("Press enter to continue..\n");
            System.in.read();

            // Delete notificationHandle
            err = AdsCallDllFunction.adsSyncDelDeviceNotificationReq(
                    addr,
                    notification);

            if (err != 0) {
                System.out.println("Error: Remove notification: 0x"
                        + Long.toHexString(err));
            }

            // Delete listener
            callObject.removeListenerCallbackAdsState(listener);

            //Close communication
            err = AdsCallDllFunction.adsPortClose();
            if (err != 0) {
                System.out.println("Error: Close Communication: 0x"
                        + Long.toHexString(err));
            }

        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }
}

The implementation of the CallbackListenerAdsState interface:

import de.beckhoff.jni.tcads.AdsNotificationHeader;
import de.beckhoff.jni.tcads.AmsAddr;
import de.beckhoff.jni.tcads.CallbackListenerAdsState;

public class AdsListener implements CallbackListenerAdsState {
    // Callback function
    @Override
    public void onEvent(AmsAddr addr,
                    AdsNotificationHeader notification,
                    long user) {

        System.out.println("Symboltabelle hat sich geaendert!");
    }
}

Unpack the example program 'Event-Driven Detection of Changes to the Symbol Table'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample11.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


ADS-sum command: Get and release several handles

System requirements:

  • TwinCAT v2.11 Build >= 1550

This sample shows how to get and release several handles with the ADS-sum command. It's constructed like the AdsSyncReadWriteRequest and is used as container to transport the sub commands.

1. Get handles
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import de.beckhoff.jni.Convert;
import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AmsAddr;

public class Main {

    private static final String VAR_NAME1 = ".bVar01";
    private static final String VAR_NAME2 = ".bVar02";

    public static void main (String[] args) {
        long err = 0;

        try {
            AmsAddr addr = new AmsAddr();

            // Create request and response buffer
            JNIByteBuffer jniReqBuff;
            JNIByteBuffer jniResBuff = new JNIByteBuffer(new byte[24]);

            // Construct data objects
            RequestData req1 = new RequestData();
            req1.setIndexGroup(AdsCallDllFunction.ADSIGRP_SYM_HNDBYNAME);
            req1.setIndexOffset(0x0);
            req1.setReadLength(Integer.SIZE / Byte.SIZE);
            req1.setWriteLength(VAR_NAME1.length());

            RequestData req2 = new RequestData();
            req2.setIndexGroup(AdsCallDllFunction.ADSIGRP_SYM_HNDBYNAME);
            req2.setIndexOffset(0x0);
            req2.setReadLength(Integer.SIZE / Byte.SIZE);
            req2.setWriteLength(VAR_NAME2.length());

            // Concatenate byte[] representations of RequestData objects and
            // variable names (see picture 1)
            int reqBuffSize = RequestData.SIZE * 2 / Byte.SIZE
                    + req1.getWriteLength() + req2.getWriteLength();

            ByteBuffer reqBuff = ByteBuffer.allocate(reqBuffSize);
            reqBuff.order(ByteOrder.LITTLE_ENDIAN);

            reqBuff.put(req1.toByteArray());
            reqBuff.put(req2.toByteArray());
            reqBuff.put(Convert.StringToByteArr(VAR_NAME1, false));
            reqBuff.put(Convert.StringToByteArr(VAR_NAME2, false));

            // Need to let a JNIByteBuffer wrap the byte[] to be able to send it
            jniReqBuff = new JNIByteBuffer(reqBuff.array());

The data that will be written via the sum-command have to be appended to the bytearray. Since in this case we want to get handles by name the appended data has to be the PLC variable names.

For the communication an open port is necessary. After that the local address is handed over. If it comes to transmission the port is assigned to the address of the run time system 1 first.

The parameters for the sum command consist of IndexGroup (0xf082) - call the sum command, IndexOffset (0x2) - count of sub commands, ReadLength (0x18) - size of the data which can be read, ReadData (pBuffRes) - memory which read data assumes, WriteLength (cbReq) - size of the data which can be send and WriteLength (pBuffReq) - memory which contains data that can be sent.

            // Open communication
            AdsCallDllFunction.adsPortOpen();
            err = AdsCallDllFunction.getLocalAddress(addr);
            addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

            if (err != 0) {
                System.out.println("Error: Open communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Open communication!");
            }

            err = AdsCallDllFunction.adsSyncReadWriteReq(
                            addr,
                            0xf082,                         // ADS list-read-write command
                            0x2,                            // number of ADS-sub commands
                            jniResBuff.getUsedBytesCount(), // we expect an ADS error
                                                            //  return-code for each ADS-sub command
                            jniResBuff,                     // provide space for the response
                                                            // containing the return codes
                            jniReqBuff.getUsedBytesCount(), // send 48 bytes(IG1,
                                                            // IO1, RLen1, WLen1, IG2, IO2, RLen2,
                                                            // WLen2, Data1, Data2)
                            jniReqBuff);

            // Check return codes
            ByteBuffer resBuff = ByteBuffer.wrap(jniResBuff.getByteArray());
            resBuff.order(ByteOrder.LITTLE_ENDIAN);
            if (err != 0) {
                System.out.println("Error: Get handles: 0x"
                        + Long.toHexString(err));
            } else {

                // Extract error codes from response (see picture 2)
                // Pattern is: err1, len1, .., errN, lenN, data1, .., dataN
                int req1Err = resBuff.getInt((Integer.SIZE / Byte.SIZE) * 0);
                int req2Err = resBuff.getInt((Integer.SIZE / Byte.SIZE) * 2);

                if (req1Err != 0) {
                    System.out.println("Error: Get handle1: 0x"
                            + Integer.toHexString(req1Err));
                } else if (req2Err != 0) {
                    System.out.println("Error: Get handle2: 0x"
                            + Integer.toHexString(req2Err));
                } else {
                    System.out.println("Success: Get handles!");
                }
            }

When the request has been sent the response buffer will contain ADS-error-code and length of each handle we tried to retrieve. In this case the length will always be 4.

Handle request return-code

2. Release handles
            // Extract handles from response(see picture 2)
            int hnd1 = resBuff.getInt((Integer.SIZE / Byte.SIZE) * 4);
            int hnd2 = resBuff.getInt((Integer.SIZE / Byte.SIZE) * 5);

            //// Release handles
            // Construct buffers
            int relBuffSize =
                    (ReleaseData.SIZE * 2 / Byte.SIZE) + (Integer.SIZE * 2 / Byte.SIZE);
            int relResBuffSize = Integer.SIZE * 2 / Byte.SIZE;

            ByteBuffer relBuff = ByteBuffer.allocate(relBuffSize);
            relBuff.order(ByteOrder.LITTLE_ENDIAN);
            JNIByteBuffer jniRelBuff = new JNIByteBuffer(relBuffSize);
            JNIByteBuffer jniRelResBuff = new JNIByteBuffer(relResBuffSize);

            // Construct data objects
            ReleaseData rel1 = new ReleaseData();
            rel1.setIndexGroup(AdsCallDllFunction.ADSIGRP_SYM_RELEASEHND);
            rel1.setIndexOffset(0);
            rel1.setLength(Integer.SIZE / Byte.SIZE);

            ReleaseData rel2 = new ReleaseData();
            rel2.setIndexGroup(AdsCallDllFunction.ADSIGRP_SYM_RELEASEHND);
            rel2.setIndexOffset(0);
            rel2.setLength(Integer.SIZE / Byte.SIZE);

            // Concatenate byte[] representations of ReleaseData objects and
            // handles (see picture 3)
            relBuff.put(rel1.toByteArray());
            relBuff.put(rel2.toByteArray());
            relBuff.putInt(hnd1);
            relBuff.putInt(hnd2);

            // Need to let a JNIByteBuffer wrap the byte[] to be able to send it
            jniRelBuff.setByteArray(relBuff.array(), true);

The handles that will be released have to be appended to the bytearray.

The existing connection is used to release the handles.

The parameters for the sum command consist of IndexGroup (0xf081) - call the sum command, IndexOffset (0x2) - count of sub commands, ReadLength (cbRelRes) - size of the data which can be read, ReadData (pBuffRelRes) - memory which read data assumes, WriteLength (cbRel) - size of the data which can be send and WriteLength (pBuffRel) - memory which contains data that can be sent.

Last thing left is releasing the handles and closing the port.

            // Release handles - Second task cleared
            err = AdsCallDllFunction.adsSyncReadWriteReq(
                            addr,
                            0xf081, 		// ADS list-write command
                            0x2, 		// number of ADS-sub commands
                            relResBuffSize,     // we expect an ADS-error-return-code for
                                                // each ADS-sub command
                            jniRelResBuff, 	// provide space for the response containing
                                                // the return codes
                            relBuffSize, 	// send 32 bytes (IG1, IO1, Len1, IG2, IO2,
                                                // Len2, Data1, Data2)
                            jniRelBuff);	// buffer with data

            // Check return codes
            if (err != 0) {
                System.out.println("Error: Release handles: 0x"
                        + Long.toHexString(err));
            } else {
                ByteBuffer relResBuff =
                        ByteBuffer.wrap(jniRelResBuff.getByteArray());
                relResBuff.order(ByteOrder.LITTLE_ENDIAN);

                // Extract error codes from response (see picture 4)
                // Pattern is: err1, .., errN
                int rel1Err = relResBuff.getInt((Integer.SIZE / Byte.SIZE) * 0);
                int rel2Err = relResBuff.getInt((Integer.SIZE / Byte.SIZE) * 1);

                if (rel1Err != 0) {
                    System.out.println("Error: Release handle1: 0x"
                                    + Integer.toHexString(rel1Err));
                } else if (rel2Err != 0) {
                    System.out.println("Error: Release handle2: 0x"
                                    + Integer.toHexString(rel2Err));
                } else {
                    System.out.println("Success: Release handles!");
                }
            }

            System.out.println("\nPress enter to continue..");
            System.in.read();

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            // Close communication
            err = AdsCallDllFunction.adsPortClose();

            if (err != 0) {
                System.out.println("Error: Close communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Close communication!");
            }
        }
    }
}

Again when the request has been sent the response buffer will contain ADS-error-codes.

Handle release request return-code

Unpack the example program 'ADS-sum command: Get and release several handles'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample12.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


ADS-Sum Command: Reading and writing several variables

System requirements:

  • TwinCAT v2.11 Build >= 1550

Using the ADS Sum Command it is possible to read or write several variables in one command. Designed as TcAdsClient.ReadWrite it is used as a container, which transports all sub-commands in one ADS stream.

1. Get the PLC variables symbol information
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import de.beckhoff.jni.JNIByteBuffer;
import de.beckhoff.jni.tcads.AdsCallDllFunction;
import de.beckhoff.jni.tcads.AmsAddr;

public class Main {

    private final static RequestData[] REQ_DATA = new RequestData[]{
        new RequestData("uintValue", 0x4020, 0x0, 0x2),
        new RequestData("boolValue", 0x4020, 0x8, 0x1),
        new RequestData("dintValue", 0x4020, 0x10, 0x4)
    };
    // The data that is going to be written in the third step
    private final static char CHAR_VAL = Character.MAX_VALUE;
    private final static byte BOOL_VAL = 1;
    private final static int INT_VAL = Integer.MIN_VALUE;
    private final static int DATA_LEN = 7;
    private final static int RESP_ERR_LEN = 4;

    public static void main(String[] args) {

        long err;
        AmsAddr addr = new AmsAddr();

        try {
            // Open communication
            AdsCallDllFunction.adsPortOpen();
            err = AdsCallDllFunction.getLocalAddress(addr);
            addr.setPort(AdsCallDllFunction.AMSPORT_R0_PLC_RTS1);

            if (err != 0) {
                System.out.println("Error: Open communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Open communication!");
            }

When PLC variables are going to be written or read without using handles, IndexGroup and IndexOffset need to be known. To aquire this information about all depending variables usuably, their symbol information need to be retrieved. In this example the values are assumed to be constant and are defined in REQ_DATA.

The second step is to convert the bytestream into AdsSymbolEntry objects from which the needed information can be read.

2. Read values

The IndexGroup and IndexOffset read from the AdsSymbolEntry object can now be used to write data into the RequestBuffer. When writing data to the buffer it must correspond in pattern to the image below. IG1 stands for the IndexGroup of the first sub-command. IO2 stands for the IndexOffset of the second sub-command. The thirs sub-commands response length is labled with the name L3. Since the response length is determined by the size of the datatype that is going to be read, their lengths are equal.

As the response of the sum-command we expect a buffer. This buffer will contain Ads error codes and data for each sub-command sent. The buffer has a certain length: 3 * 4byte (Error) + 7Byte (the sum of all response lengths).

Sum Command's Parameters consists of IndexGroup (0xF080) - sum command read call, IndexOffset (0x3) - number of sub commands, ResponseBuffer length, ResponseBuffer, RequestBuffer length, RequestBuffer.

            // Create request buffer (see picture 1)
            JNIByteBuffer jniRequestBuffer = null;
            ByteBuffer requestBuffer =
                    ByteBuffer.allocate(RequestData.SIZE * REQ_DATA.length);
            requestBuffer.order(ByteOrder.LITTLE_ENDIAN);

            for (int i = 0; i < REQ_DATA.length; i++) {
                requestBuffer.put(REQ_DATA[i].toByteArray());
            }
            // Need to let a JNIByteBuffer wrap the byte[] to be able to send it
            jniRequestBuffer = new JNIByteBuffer(requestBuffer.array());

            // Create response buffer (see picture 2)
            JNIByteBuffer jniResponseBuffer = new JNIByteBuffer(
                    REQ_DATA.length * RESP_ERR_LEN + DATA_LEN);

            // Read the values via sum command
            err = AdsCallDllFunction.adsSyncReadWriteReq(
                    addr,
                    0xF080,
                    REQ_DATA.length,
                    jniResponseBuffer.getUsedBytesCount(),
                    jniResponseBuffer,
                    jniRequestBuffer.getUsedBytesCount(),
                    jniRequestBuffer);

            if (err != 0) {
                System.out.println("Error: Get values: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Get values!");

                // Evaluate the sub commands error codes and response values
                // (see picture)
                ByteBuffer responseBuffer =
                        ByteBuffer.wrap(jniResponseBuffer.getByteArray());
                responseBuffer.order(ByteOrder.LITTLE_ENDIAN);

                // In each loop the errPosition is increased by the Size of an
                // error (int) and the valPosition by the size of the
                // corresponding data type

                int[] respErrs = new int[REQ_DATA.length];
                for (int i = 0; i < respErrs.length; i++) {
                    respErrs[i] = responseBuffer.getInt();
                }

                for (int i = 0; i < REQ_DATA.length; i++) {
                    String varName = REQ_DATA[i].getVarName();
                    int val = 0;

                    if (respErrs[i] == 0) {
                        switch (REQ_DATA[i].getLength()) {
                            case 1:
                                val = (int) responseBuffer.get();
                                break;
                            case 2:
                                val = responseBuffer.getChar();
                                break;
                            case 4:
                                val = responseBuffer.getInt();
                                break;
                            default:
                                break;
                        }

                        System.out.println(varName + ": " + val + "\t");
                    } else {
                        System.out.println(varName + ": Error: 0x"
                                + Long.toHexString(err) + "\t");
                    }
                }
            }

3. Write values

Writing data is similar to reading. The data to be written have to be appended to the RequestBuffer created in step 2 (see picture). The other parameters as IndexGroup and IndexOffset stay unchanged. Just one small adjustment on the final sum-command has to be made (see below).

As the response of the sum-command we expect a buffer. This time the buffer will contain Ads error codes but no data since instead of reading we are know writing data. As a result the ResponseBuffer shortens: 3 * 4byte (Error).

At last a change to the IndexGroup of the sum-command has to bem made. The new IndexGroup has to be 0xF081 (sum-command write).

            //// Write values
            // Construct buffers. Fill request buffer with the data from above
            // and append the data that is going to be written (see picture 3)
            byte[] requestBufferCopy = requestBuffer.array();
            requestBuffer = ByteBuffer.allocate(
                    requestBuffer.capacity()
                    + DATA_LEN);
            requestBuffer.order(ByteOrder.LITTLE_ENDIAN);

            requestBuffer.put(requestBufferCopy);
            requestBuffer.putChar(CHAR_VAL);
            requestBuffer.put(BOOL_VAL);
            requestBuffer.putInt(INT_VAL);

            // Need to let a JNIByteBuffer wrap the byte[] to be able to send it
            jniRequestBuffer.setByteArray(requestBuffer.array(), true);
            // Adjust the length of the response buffer (compare pictures 2 & 4)
            jniResponseBuffer.setUsedBytesCount(8 * REQ_DATA.length, true);

            // Write the values via sum command
            err = AdsCallDllFunction.adsSyncReadWriteReq(
                    addr,
                    0xF081,
                    REQ_DATA.length,
                    jniResponseBuffer.getUsedBytesCount(),
                    jniResponseBuffer,
                    jniRequestBuffer.getUsedBytesCount(),
                    jniRequestBuffer);

            if (err != 0) {
                System.out.println("Error: Write values: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Write values!");

                // Evaluate the sub command error codes
                ByteBuffer responseBuffer =
                        ByteBuffer.wrap(jniResponseBuffer.getByteArray());
                responseBuffer.order(ByteOrder.LITTLE_ENDIAN);

                for (int i = 0; i < REQ_DATA.length; i++) {
                    String varName = REQ_DATA[i].getVarName();
                    err = responseBuffer.getInt();

                    if (err == 0) {
                        System.out.println(varName + ": Success\t");
                    } else {
                        System.out.println(varName + ": Error: 0x"
                                + Long.toHexString(err) + "\t");
                    }
                }
            }

            System.out.println("\nPress enter to continue..");
            System.in.read();

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            // Close communication
            err = AdsCallDllFunction.adsPortClose();

            if (err != 0) {
                System.out.println("Error: Close communication: 0x"
                        + Long.toHexString(err));
            } else {
                System.out.println("Success: Close communication!");
            }
        }
    }
}

Unpack the example program 'ADS-Sum Command: Reading and writing several variables'

To be able to execute the *.jar sample you need to run 'java -classpath "Sample13.jar;[Path to TcJavaToAds.jar] Main' from the console (path might be "C:TwinCAT\Ads Api\AdsToJava\*"). Make sure the current path is correct and you have added java to your environment variables.


TwinCAT ADS Web Service: Java-Sample

TwinCAT ADS Web Service: Java example

Consumer in Java to read and write PLC variables

This sample explains how to create an ADS-WebService-Consumer to read and write PLC-Variables via ADS-HTTP.

Unzip the sample 'WebServiceTestConsumer (Java)'.

Copy the files "TcAdsSOAP.java" and "Base64.java" to the folder of your Java-application and compile them.
Add the following line to the Java-file in which you want to use the ADS WebService:

TcAdsSOAP tcSoap = new TcAdsSOAP("http://192.168.0.2/TcAdsWebService/TcAdsWebService.dll");

The address must be the URL of your TwinCAT ADS WebService-DLL-file .

reading PLC-variables

To read PLC-variables use the following method of the TcAdsSoap-object:

DataInputStream SOAPCall(String netId, int port, long group, long offset, int cblen)

  • String netId: String indicating the AMS-Net-Id of the PLC
  • int port: Port-number of the Runtime-System
  • long indexGroup: IndexGroup of the variables to read
  • long indexOffset: First byte to read
  • int cblen: Number of bytes to read
  • return value: DataInputStream with the read data, throws exception on failure

The 3 methods

short ReadInt(String netId, int port, long group, long offset)
boolean ReadBool(String netId, int port, long group, long offset)
String ReadString(String netId, int port, long group, long offset, int cblen)

convert the variables to their corresponding type.

Example:

boolean Bool1;
try
{
    Bool1 = tcSoap.ReadBool("192.168.0.2.1.1", 801, 16416, 0)
}
catch (Exception e)
{
    System.out.println("Error");
}

Write PLC-variables

boolean SOAPWrite(String netId, int port, long group, long offset, byte[] data)

  • String netId: String indicating the AMS-Net-Id of the PLC
  • int port: Port-number of the Runtime-System
  • long indexGroup: IndexGroup of the variable
  • long indexOffset: First byte to write to
  • byte[] pData: Byte-array containing the data to write
  • return value: "true" if successful, throws exception on failure

the 3 methods

boolean WriteBool(String netId, int port, long group, long offset, boolean data)
boolean WriteInt(String netId, int port, long group, long offset, int data)
boolean WriteString(String netId, int port, long group, long offset, String data)

convert the variables to a byte-arrays.

Example:

try
{
    tcSoap.WriteBool("192.168.0.2.1.1", 801, 16416, true)
}
catch (Exception e)
{
    System.out.println("Error");
}

Sample application:

PLC-program, IEC1131 daclaration of the variables:

PROGRAM MAIN
VAR
    PlcVarBool    AT %MX0.0  : BOOL := TRUE;
    PlcVarInt     AT %MW1    : INT := 1234;
    PlcVarString  AT %MD3    : STRING := 'Hello Automation';
END_VAR

The source-code:

public class AdSWebServiceSample
{
    public static void main(String args[])
    {
        TcAdsSOAP tcSoap = new TcAdsSOAP("http://192.168.0.2/TcAdsWebService/TcAdsWebService.dll");
        try
        {
            String netId = "192.168.0.2.1.1";
            int port = 801;
            int offset = 16416;
            /* write bool */
            boolean testBool = true;
            if (tcSoap.WriteBool(netId,port,offset,0,testBool))
                System.out.println("Boolean "+testBool+" Written");

            /* write int */
            int testInt = 1234;
            if (tcSoap.WriteInt(netId,port,offset,1,testInt))
                System.out.println("Integer "+testInt+" Written");

            /* write string */
            String testString = "Hello Automation";
            if (tcSoap.WriteString(netId,port,offset,3,testString))
                System.out.println("String "+testString+" Written");

            /* read bool */
            System.out.println(String.valueOf(tcSoap.ReadBool(netId,port,offset,0) ));

            /* read int */
            System.out.println(String.valueOf(tcSoap.ReadInt(netId,port,offset,1) ));

            /* read string */
            System.out.println(tcSoap.ReadString(netId,port,offset,3,81));

        }
        catch(Exception ex)
        {
            System.out.println(String.valueOf(ex));
        }
    }
}

Complete sourcecode