Hinweise zur Dokumentation

Diese Beschreibung wendet sich ausschließlich an ausgebildetes Fachpersonal der Steuerungs- und Automatisierungstechnik, das mit den geltenden nationalen Normen vertraut ist.
Zur Installation und Inbetriebnahme der Komponenten ist die Beachtung der nachfolgenden Hinweise und Erklärungen unbedingt notwendig.

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

Disclaimer

Diese Dokumentation wurde sorgfältig erstellt. Die beschriebenen Produkte werden jedoch ständig weiter entwickelt.
Deshalb ist die Dokumentation nicht in jedem Fall vollständig auf die Übereinstimmung mit den beschriebenen Leistungsdaten, Normen oder sonstigen Merkmalen geprüft.
Falls sie technische oder redaktionelle Fehler enthält, behalten wir uns das Recht vor, Änderungen jederzeit und ohne Ankündigung vorzunehmen.
Aus den Angaben, Abbildungen und Beschreibungen in dieser Dokumentation können keine Ansprüche auf Änderung bereits gelieferter Produkte geltend gemacht werden.

Marken

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

Patente

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

Die TwinCAT Technologie ist patentrechtlich geschützt, insbesondere durch folgende Anmeldungen und Patente:
EP0851348, US6167425 mit den entsprechenden Anmeldungen und Eintragungen in verschiedenen anderen Ländern.

Copyright

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


Übersicht

TcAdsToJava

Weitere Informationen können in der TwinCAT Ads Java Referenz eingesehen werden.

Beschreibung | Beispiel
Beispiel 1: Using the AdsToJava.dll | Sample01.zip
Beispiel 2: Zugriff per Variablenname | Sample02.zip
Beispiel 3: Zugriff auf ein Array in der SPS | Sample03.zip
Beispiel 4: Übertragen von Strukturen an die SPS | Sample04.zip
Beispiel 5: Auslesen der SPS-Variablendeklaration einer einzelnen Variablen | Sample05.zip
Beispiel 6: Merker synchron in SPS schreiben | Sample06.zip
Beispiel 7: Merker synchron aus SPS lesen | Sample07.zip
Beispiel 8: Handle einer Variablen löschen | Sample08.zip
Beispiel 9: Ereignisgesteuertes Lesen | Sample09.zip
Beispiel 10: Auslesen von SMB-Werten | Sample10.zip
Beispiel 11: Änderungen an der Symboltabelle ereignisgesteuert erkennen | Sample11.zip
Beispiel 12: ADS-Summenkommando: Holen und Freigeben von mehreren Handles | Sample12.zip
Beispiel 13: ADS-Summenkommando: Lesen und Schreiben von mehreren Variablen | Sample13.zip

ADS Web Service

Beschreibung Beispiel


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


Benutzen der AdsToJava.dll

Achtung: Falls Callback-Funktionen benutzt werden, muss sichergestellt sein, dass die AdsToJava.dll in der Version 1.1.0.2 oder größer installiert ist.

In diesem Beispiel werden die grundlegenden Funktionalitäten der Java Version des ADS-Stacks vorgestellt.

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

download sample

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample01.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Zugriff per Variablenname

Das folgende Programm greift auf eine SPS-Variable zu, die keine Adresse besitzt. Der Zugriff muss deshalb per Variablenname erfolgen:

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

Beispielprogramm 'Zugriff per Variablenname' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample02.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Zugriff auf ein Array in der SPS

Ein Array, welches sich in der SPS befindet, soll mit einen Lesebefehl ausgelesen werden. Die Variable wird hierbei per Variablenname angesprochen. Die Vorgehensweise weicht nur geringfügig von dem Auslesen diskreter Variablen ab:

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

Beispielprogramm 'Zugriff auf ein Array in der SPS' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample03.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Übertragen von Strukturen an die SPS

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

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

Das zu übertragende Objekt:

public class TransferObject
{
    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;
    }
}

Beispielprogramm 'Übertragen von Strukturen an die SPS' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample04.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Auslesen der SPS-Variablen Deklaration einer einzelnen Variablen

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

  • Variablenname
  • Datentyp
  • Länge
  • Adresse (IndexGroup / IndexOffset)
  • Kommentar
Mit dem AdsSyncReadWriteReq() Aufruf wird die Information der Variablen ausgelesen. Der Name der Variablen wird der Funktion im Parameter pWriteData übergeben. Nach dem Aufruf steht die angeforderte Information in einem Buffer vom Typ JNIByteBuffer. Der Buffer wird in den Konstruktor einer Variablen vom Typ AdsSymbolEntry gereicht. In dieser Klasse sind die einzelnen Informationen der SPS-Variablen abgelegt. Die Felder entryLenght, typeLength und commentLength geben die Längen der hinter der Klasse befindlichen zugehörigen Strings an. Als nächstes wird der Datentyp der Variable ermittelt, indem im adsDatatypeString das zum Value passende Label gesucht wird. Falls der Datentyp dem Typ UDINT oder ARRAY OF UDINT entspricht wird zusätzlich der Wert dieser Variable ausgelesen:

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

Beispielprogramm 'Auslesen der SPS-Variablendeklaration einer einzelnen Variablen' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample05.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Merker synchron in die SPS schreiben

Bei diesem Beispielprogramm wird der Wert, den der Bediener eingegeben hat, in das Merkerdoppelwort 0 geschrieben:

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

Beispielprogramm 'Merker synchron in die SPS schreiben' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample06.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Merker synchron aus der SPS lesen

Bei diesem Beispielprogramm wird der Wert aus dem Merkerdoppelwort 0 der SPS ausgelesen und angezeigt:

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

Beispielprogramm 'Merker synchron aus der SPS lesen' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample07.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Handle einer SPS Variablen löschen

In diesem Beispiel wird ein Handle einer SPS Variable geholt und im Anschluss gelöscht:

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

Beispielprogramm 'Handle einer SPS Variablen löschen' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample08.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Ereignisgesteuertes Lesen

Sollen in einer Bedieneroberfläche kontinuierlich Werte aus der SPS oder NC angezeigt werden, so ist das benutzen von AdsSyncReadReq() sehr aufwendig, da diese Funktion zyklisch aufgerufen werden muss. Durch das Definieren sogenannter Notifications (Meldung) kann ein TwinCAT Server dazu veranlasst werden, Werte über ADS an ein anderes ADS-Gerät zu übertragen. Hierbei wird unterschieden ob der TwinCAT Server die Werte zyklisch oder nur bei Veränderung übertragen soll.

Mit der Funktion AdsSyncAddDeviceNotificationReq() wird eine Notification gestartet. Die Callback-Funktion wird anschließend selbständig von TwinCAT aufgerufen. Mit AdsSyncDelDeviceNotificationReq() wird die Notification wieder beendet. Da die Anzahl der Notifications begrenzt ist, sollten Sie in Ihrem Programm dafür sorgen, das nicht mehr benötigte Notifications gelöscht werden. Weiter Informationen finden Sie bei der Beschreibung zur Struktur AdsNotificationAttrib.

Das folgende Programm startet eine Notification auf das Merkerdoppeltwort 0 in der SPS. Bei jeder Änderung der SPS-Variablen, wird die Callback-Funktion aufgerufen. Als Parameter enthält die Callback-Funktion unter anderem eine Variable vom Typ AdsNotificationHeader(). In dieser Struktur sind alle notwendigen Informationen (Wert, Zeitstempel, ...) enthalten.

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

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

Die Implementierung des 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");
    }
}

Beispielprogramm 'Ereignisgesteuertes Lesen' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample09.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Auslesen von SMB-Werten aus dem TwinCAT I/O Treiber

Falls statt einer TwinCAT PLC Laufzeitumgebung nur TwinCAT-CP zur Verfügung steht, kann das SMB-Gerät auch mit Hilfe des ADS-Clients ausgelesen werden. Die Visualisierung erlaubt ein ständiges Überwachen der gewünschten Temperatur- oder Lüfterwerte. Somit kann frühzeitig erkannt werden, ob sich die aktuelle Temperatur einem kritischen Wert annähert. Nachfolgend ein Beispiel einer möglichen Implementierung in Java (Für eine korrekte Funktionsweise muss ein SMB-Gerät in der aktuellen System Manager-Konfiguration eingetragen sein):

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

In dieser Ansicht kann der jeweilige IndexOffset der verschiedenen Eingänge abgelesen werden.

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

Beispielprogramm 'Auslesen von SMB-Werten aus dem TwinCAT I/O Treiber' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample10.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


Änderungen an der Symboltabelle ereignisgesteuert erkennen

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

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

Implementierung des 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!");
    }
}

Beispielprogramm 'Änderungen an der Symboltabelle ereignisgesteuert erkennen' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample11.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


ADS-Summenkommando: Holen und Freigeben von mehreren Handles

Systemvoraussetzungen:

  • TwinCAT v2.11 Build >= 1550

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

1. Handles holen
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());

Die Namen der Variablen deren Handles wir erhalten wollen, werden an das Ende des Bytearrays gehängt.

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

Die Parameter für das Summenkommando bestehen aus IndexGroup (0xf082) - Aufruf des Summenkommandos, IndexOffset (0x2) - Anzahl der Unterkommandos, ReadLength (0x18) - Größenangabe der zu lesenden Daten, ReadData (pBuffRes) - Speicher, der gelesene Daten entgegen nimmt, WriteLength (cbReq) - Größenangabe der zu sendenen Daten und WriteLength (pBuffReq) - Speicher, der zu sendende Daten enthält.

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

Nach dem Senden des Request, erwarten wir einen ADS Fehlercode und Länge für jeden Handle den wir versuchen zu bekommen. In diesem Fall wird die Länge immer 4 Bytes betragen.

Handle request return-code

2. Handles freigeben
            // 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);

Die zu den Kommandos zugehörigen Handles werden an das Ende des Bytearrays gehängt.

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

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

Zum Schluss müssen die Handles freigegeben und der Port geschlossen werden.

            // Release handles - 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!");
            }
        }
    }
}

Als Antwort erhalten wir einen ADS Fehlercode für jedes Handle das wir versuchen freizugeben.

Handle release request return-code

Beispielprogramm 'ADS-Summenkommando: Holen und Freigeben von mehreren Handles' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample12.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


ADS-Summenkommando: Lesen und Schreiben von mehreren Variablen

Systemvoraussetzungen:

  • TwinCAT v2.11 Build >= 1550

Dieses Beispiel zeigt, wie man unter Zuhilfenahme des ADS-Summenkommandos, viele Variablen lesen oder schreiben kann. Aufgebaut als TcAdsClient.ReadWrite dient es als Behälter, in dem die Unterkommandos in einem ADS-Stream transportiert werden.

1. Symbolinformationen der SPS-Variablen holen
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!");
            }

Wenn Variablen in der SPS beschrieben oder gelesen werden sollen, ohne dabei auf Handles zurückzugreifen, dann müssen IndexGroup und IndexOffset bekannt sein. Um diese Werte von allen relevanten Variablen zu erhalten, müssen üblicherweise die Symbolinformationen ausgelesen werden. In diesem Beispiel sind die Werte als konstant angenommen und in REQ_DATA definiert.

2. Werte lesen

Der RequestBuffer wird nun mit den Daten aus REQ_DATA nach unten stehendem Schema beschrieben. Dabei steht IG1 für die IndexGroup des ersten Subkommandos. IO2 für den IndexOffset des zweiten und L3 für die Länge der erwarteten Antwort des dritten Subkommandos.

Als Antwort des Summenkommandos erhalten wir pro Subkommando einen Ads-Fehlercode (Err), sowie den angeforderten Wert (Data). Entsprechend beträgt die Länge des Buffers in diesem Fall: 3 * 4Byte (Error) + 7Byte (Die Summe aller Längen der erwarteten Antworten).

Das Summenkommando selbst wird mit den Parametern IndexGroup: 0xF080 (Summenkommando-Lesen), IndexOffset: 0x3 (Anzahl Subkommandos), ResponseBuffer-Länge, ResponseBuffer, RequestBuffer-Länge, RequestBuffer aufgerufen.

            // 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. Werte schreiben

Um Daten schreiben zu können müssen diese Daten lediglich an den RequestBuffer aus dem oberen Teil angehängt werden (siehe Bild). Alle anderen Parameter bleiben unverändert. Lediglich eine Anpassung des Summenkommandos ist nötig (siehe unten).

Als Antwort des Summenkommandos erhalten wir pro Subkommando einen Ads-Fehlercode (Err), allerdings dieses Mal keine Daten, da wir einen Schreibvorgang ausgeführt haben. Entsprechend beträgt die Länge des Buffers in diesem Fall: 3 * 4Byte (Error).

Beim Summenkommando muss nun noch der Wert für die IndexGroup auf 0xF081 (Summenkommando-Schreiben) geändert werden.

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

Beispielprogramm 'ADS-Summenkommando: Lesen und Schreiben von mehreren Variablen' entpacken.

Um das *.jar Sample ausführen zu können, muss in der Konsole im korrekten Verzeichnis der Befehl 'java -classpath "Sample13.jar;[Pfad zu TcJavaToAds.jar] Main' ausgeführt werden (Beispielpfad: "C:TwinCAT\Ads Api\AdsToJava\*"). Dazu muss java in den Umgebungsvariablen eingetragen sein.


TwinCAT ADS Web Service: Java-Beispiel

Consumer in Java zum Lesen und Schreiben von SPS-Variablen

In diesem Beispiel wird erklärt, wie ein ADS-WebService-Consumer zum Lesen und Schreiben von SPS-Variablen via ADS-HTTP erstellt wird.
Zuerst müssen Sie das Beispielprogramm AdsWebServiceSampleJava.zip entpacken.

1. Erstellen eines WebService-Consumers

Kopieren Sie die Dateien TcAdsSOAP.java und Base64.java in den Ordner Ihrer Java Datei.
Fügen Sie folgende Zeile in die Java Datei ein, in der Sie den WebService verwenden wollen:

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

2. SPS-Variablen auslesen

Zum Auslesen der SPS-Variablen benutzen Sie eine der folgenden Funktionen der TcAdsSOAP Klasse:
DataInputStream SOAPCall(String netId, int port, long indexGroup, long indexOffset, int cblen)

  • netId: String, der die AMS-Net-Id angibt, auf der die SPS zu finden ist
  • port: Port-Nummer des Laufzeitsystems
  • indexGroup: IndexGroup der zu lesenden Variablen
  • indexOffset: Erstes Byte, das gelesen werden soll
  • cblen: Anzahl der Bytes, die gelesen werden sollen
  • return value: DataInputStream mit den gelesenen Daten, throws Exception bei Fehlschlag

Die drei Methoden übernehmen das Konvertieren der Ausgangsvariablen in den entsprechenden Typ:
short ReadInt(String netId, int port, long indexGroup, long indexOffset)
boolean ReadBool(String netId, int port, long indexGroup, long indexOffset)
String ReadString(String netId, int port, long indexGroup, long indexOffset, int cblen)

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

3. SPS-Variablen schreiben

boolean SOAPWrite(String netId, int port, long indexGroup, long indexOffset, byte[] pData)

  • netId: String, der die AMS-Net-Id angibt, auf der die SPS zu finden ist
  • port: Port-Nummer des Laufzeitsystems
  • indexGroup: IndexGroup der SPS- Variablen
  • indexOffset: Erstes Byte, in das geschrieben werden soll
  • pData: Byte-Array, dass die zu schreibenden Daten enthält.
  • return value: true bei Erfolg, throws Exception bei Fehlschlag

Die drei Methoden übernehmen das Konvertieren der Eingangsvariablen in einen ByteArray:
boolean WriteBool(String netId, int port, long indexGroup, long indexOffset, boolean pData)
boolean WriteInt(String netId, int port, long indexGroup, long indexOffset, int pData)
boolean WriteString(String netId, int port, long indexGroup, long indexOffset,String pData)

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

4. SPS-Programm, Variablendeklaration

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

5. Java Applikation, der Quelltext:

public class AdsWebServiceSample
{
    public static void main(String args[])
    {
        TcAdsSOAP tcSoap = new TcAdsSOAP("http://172.16.6.241/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));
        }
    }
}

5. Java Applikation, der Download:

WebServiceSampleJava.zip