Skip to content

Commit

Permalink
Add linux support (beta)
Browse files Browse the repository at this point in the history
  • Loading branch information
vossjannik committed Jan 12, 2024
1 parent f7801e1 commit 14755b4
Show file tree
Hide file tree
Showing 46 changed files with 672 additions and 125 deletions.
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ Checks: >
*,
-google-runtime-int,
-fuchsia-trailing-return,
-fuchsia-default-arguments-calls,
-hicpp-vararg,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-type-union-access,
Expand Down
1 change: 1 addition & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"language": "en",
"words": [
"adstojava",
"adslib",
"amsnetid",
"beckhoff",
"bytewise",
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ dist/
dependencies/
node_modules/
Testing/
samples/[0-9][0-9]_*/*.class
samples/*/[0-9][0-9]_*/*.class
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "ADS"]
path = adslib_for_linux
url = https://github.com/Beckhoff/ADS.git
62 changes: 53 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

This library is intended for use in ADS client applications written in the Java
programming language. It was tested on Windows 10 and Tc/BSD 13.2.
The Linux support is still in the beta phase, as detailed below.

**It consists of two parts:**

Expand Down Expand Up @@ -104,17 +105,60 @@ Overview of the folder structure of this repository:

```txt
.
├── .vscode # Configuration files for Visual Studio Code
├── dist # Generated documentation, compiled library and samples
├── run # Helper scripts
├── cpp # C++ source files
├── src # Java source files and tests
├── samples # Java source files for the samples
├── build* # Full C++ build output: x64 (and win32)
├── target # Full Java build output including coverage report
├── plc # PLC projects for the tests and samples
├── .vscode # Configuration files for Visual Studio Code
├── adslib_for_linux # Contains the open-source ADS library that is used where the TcAdsDll is not available
├── dist # Generated documentation, compiled library and samples
├── run # Helper scripts
├── cpp # C++ source files
├── src # Java source files and tests
├── samples # Java source files for the samples
├── build* # Full C++ build output: x64 (and win32)
├── target # Full Java build output including coverage report
├── plc # PLC projects for the tests and samples
```

## Status of the Linux support

Beckhoff has an [open-source ADS library](ttps://github.com/Beckhoff/ADS) that
provides an API to communicate with TwinCAT devices via TCP/IP.
This makes it possible to port the "AdsToJava" library to systems without "TcAdsDll" support.

**Current status:** Everything compiles, the [02](/samples/adslib/02_AccessByVariableName/) sample was updated to work with the open-source ADS library on Linux.

**Known limitations:**

1. For many functions, the open-source ADS library only supports the newer version (`*Ex`, or `*Ex2`).
1. `AdsGetDllVersion`, `AdsGetLocalAddress`, `AdsAmsPortEnabled*`,
`AdsAmsRegisterRouterNotification`, and `AdsAmsUnRegisterRouterNotification`
are not supported by the open-source ADS library.

**Getting started:**

The open-source ADS library was added as a Git submodule called "adslib_for_linux".
Execute this command after cloning this repository to download the contents of the submodule:

```bash
git submodule update --init --recursive
```

It is compiled automatically by the `bootstrap.sh`, you don't need to do that yourself.

Since there is no TwinCAT AMS Router on systems without "TcAdsDll" support,
you have to use `adsAddLocalRoute` to define the association between the AMS NetId
and IP address on your target system. This is, for example, demonstrated in the
[this](/samples/adslib/02_AccessByVariableName/) sample.

You need an ADS route between your Linux system and your target system.
If you don't have a route, the target system will refuse your ADS connection.
Adding routes is explained elsewhere, here are a few resources for you:

- https://github.com/Beckhoff/ADS?tab=readme-ov-file#prepare-your-target-to-run-the-example
- A simple option is to use the **adstool**:

```bash
./adslib_for_linux/build/AdsTool/AdsTool <target_ip> addroute --addr=<linux_ip> --netid=<linux_netid> --password=<target_password>
```

## Contributing

The main purpose of this repository is to continue evolving this
Expand Down
1 change: 1 addition & 0 deletions adslib_for_linux
Submodule adslib_for_linux added at 4603f4
1 change: 1 addition & 0 deletions bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ set -e
. ./run/check_dependencies.sh
. ./run/clear.sh

. ./run/build_adslib.sh
. ./run/build_cpp.sh
. ./run/build_java.sh
. ./run/build_doc.sh
Expand Down
108 changes: 95 additions & 13 deletions cpp/AdsToJava.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#include "StdAfx.h"
#ifdef USE_OPENSOURCE_ADSLIB
#include "AdsLib.h"
#else
#include "TcAdsAPI.h"
#endif

#include "jni.h"

Expand All @@ -26,7 +30,10 @@
const size_t DEVICE_NAME_MAX_LEN =
16; // https://infosys.beckhoff.com/english.php?content=../content/1033/tcadsamsspec/html/tcadsamsspec_adscmd_readdeviceinfo.htm&id=1313400676238164711

#ifdef POSIX
#ifdef USE_OPENSOURCE_ADSLIB
// AdsPortClose is not supported by the open-source ADS lib
// (https://github.com/Beckhoff/ADS), only AdsPortCloseEx
#elif defined(POSIX)
__attribute__((destructor)) void destructor() {
// free resources of libTcAdsDll.so during a crash
AdsPortClose();
Expand Down Expand Up @@ -104,9 +111,13 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllDoWhenUnloadDll(
// Forwarding of Adsdll.dll callbacks to java

// ADS-State Callback-Function
void ADSSTDCALL AdsStateCallback(AmsAddr* pAddr,
AdsNotificationHeader* pNotification,
ADS_UINT32_OR_ULONG hUser) {
void ADSSTDCALL AdsStateCallback(
#ifdef USE_OPENSOURCE_ADSLIB
const AmsAddr* pAddr, const AdsNotificationHeader* pNotification,
#else
AmsAddr* pAddr, AdsNotificationHeader* pNotification,
#endif
ADS_NOTIFICATION_USER_HANDLE hUser) {
// attach to the current running thread
JNIEnv* env = nullptr;
jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), jvm);
Expand Down Expand Up @@ -219,6 +230,76 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllDoInitCallbacks(
//////////////////////////////////////////////
///////////////////////////// Adsdll.dll-calls

// AddLocalRoute
JNIEXPORT jlong JNICALL
Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAddLocalRoute(
JNIEnv* env [[maybe_unused]], jobject obj [[maybe_unused]],
jobject lj_AmsNetId [[maybe_unused]], jstring lj_IpAddr [[maybe_unused]]) {
#ifdef USE_OPENSOURCE_ADSLIB
// convert the parameters to cpp value types and make the ADS call
AmsNetId NetId;
PAmsNetId pNetId = &NetId;

JObjAmsNetId lJObjAmsNetId(env, lj_AmsNetId);
lJObjAmsNetId.getValuesOutJObject(pNetId);

const char* IpAddress = env->GetStringUTFChars(lj_IpAddr, nullptr);

// ADS call
return bhf::ads::AddLocalRoute(NetId, IpAddress);
#else
// only supported with the open-source ADS lib
// (https://github.com/Beckhoff/ADS)
return ADSERR_DEVICE_SRVNOTSUPP;
#endif
}

// DelLocalRoute
JNIEXPORT jlong JNICALL
Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllDelLocalRoute(
JNIEnv* env [[maybe_unused]], jobject obj [[maybe_unused]],
jobject lj_AmsNetId [[maybe_unused]]) {
#ifdef USE_OPENSOURCE_ADSLIB
// convert the parameters to cpp value types and make the ADS call
AmsNetId NetId;
PAmsNetId pNetId = &NetId;

JObjAmsNetId lJObjAmsNetId(env, lj_AmsNetId);
lJObjAmsNetId.getValuesOutJObject(pNetId);

// ADS call
bhf::ads::DelLocalRoute(NetId);
return ADSERR_NOERR;
#else
// only supported with the open-source ADS lib
// (https://github.com/Beckhoff/ADS)
return ADSERR_DEVICE_SRVNOTSUPP;
#endif
}

// SetLocalAddress
JNIEXPORT jlong JNICALL
Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllSetLocalAddress(
JNIEnv* env [[maybe_unused]], jobject obj [[maybe_unused]],
jobject lj_AmsNetId [[maybe_unused]]) {
#ifdef USE_OPENSOURCE_ADSLIB
// convert the parameters to cpp value types and make the ADS call
AmsNetId NetId;
PAmsNetId pNetId = &NetId;

JObjAmsNetId lJObjAmsNetId(env, lj_AmsNetId);
lJObjAmsNetId.getValuesOutJObject(pNetId);

// ADS call
bhf::ads::SetLocalAddress(NetId);
return ADSERR_NOERR;
#else
// only supported with the open-source ADS lib
// (https://github.com/Beckhoff/ADS)
return ADSERR_DEVICE_SRVNOTSUPP;
#endif
}

// AdsGetDllVersion
JNIEXPORT auto JNICALL
Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAdsGetDllVersion(
Expand Down Expand Up @@ -489,7 +570,7 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAdsSyncReadDeviceInfoReq(

// ADS call
std::string devName(DEVICE_NAME_MAX_LEN, '\0');
AdsVersion version;
AdsVersion version = {0, 0, 0};
nErr = AdsSyncReadDeviceInfoReq(pAddr, devName.data(), &version);

// Convert the result and assign it to the according java parameter
Expand Down Expand Up @@ -557,7 +638,7 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAdsSyncGetTimeout(
jobject lj_pMs) -> jlong // Get timeout in ms
{
// ADS call
LONG data = 0;
ADS_TIMEOUT data = 0;
const long nErr = AdsSyncGetTimeout(&data);

// Convert the result and assign it to the according java parameter
Expand All @@ -576,8 +657,8 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAdsSyncSetTimeout(
jlong lj_nMs) -> jlong // Set timeout in ms
{
// ADS call
const long nErr = static_cast<jlong>(
AdsSyncSetTimeout(static_cast<ADS_INT32_OR_LONG>(lj_nMs)));
const long nErr =
static_cast<jlong>(AdsSyncSetTimeout(static_cast<ADS_TIMEOUT>(lj_nMs)));
return nErr;
}

Expand All @@ -596,7 +677,8 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAdsSyncAddDeviceNotificatio
// convert the parameters to cpp value types and make the ADS call
AmsAddr Addr;
PAmsAddr pAddr = &Addr;
AdsNotificationAttrib lcpp_adsNotificationAttrib;
AdsNotificationAttrib lcpp_adsNotificationAttrib = {
0, ADSTRANS_NOTRANS, 0, {0}};

JObjAmsAddr lJObjAmsAddr(env, lj_pAddr);
lJObjAmsAddr.getValuesOutJObject(pAddr);
Expand Down Expand Up @@ -1056,12 +1138,11 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAdsSyncReadDeviceInfoReqEx(
AmsAddr Addr;
PAmsAddr pAddr = &Addr;
const auto lcpp_nPort = static_cast<ADS_INT32_OR_LONG>(lj_nPort);
AdsVersion version;

JObjAmsAddr lJObjAmsAddr(env, lj_AmsAddr);
lJObjAmsAddr.getValuesOutJObject(pAddr);

// ADS call
AdsVersion version = {0, 0, 0};
std::string devName(DEVICE_NAME_MAX_LEN, '\0');
const long nErr =
AdsSyncReadDeviceInfoReqEx(lcpp_nPort, pAddr, devName.data(), &version);
Expand Down Expand Up @@ -1135,7 +1216,7 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAdsSyncGetTimeoutEx(
jobject lj_pMs) -> jlong // Get timeout in ms
{
// ADS call
LONG data = 0;
ADS_TIMEOUT data = 0;
const long nErr =
AdsSyncGetTimeoutEx(static_cast<ADS_INT32_OR_LONG>(lj_nPort), &data);

Expand Down Expand Up @@ -1177,7 +1258,8 @@ Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAdsSyncAddDeviceNotificatio
// convert the parameters to cpp value types and make the ADS call
AmsAddr Addr;
PAmsAddr pAddr = &Addr;
AdsNotificationAttrib lcpp_adsNotificationAttrib;
AdsNotificationAttrib lcpp_adsNotificationAttrib = {
0, ADSTRANS_NOTRANS, 0, {0}};
const auto lcpp_nPort = static_cast<ADS_INT32_OR_LONG>(lj_nPort);

JObjAmsAddr lJObjAmsAddr(env, lj_pAddr);
Expand Down
18 changes: 18 additions & 0 deletions cpp/AdsToJava.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@ jmethodID mid_AdsStateCallback = 0;
jmethodID mid_AdsRouterCallback = 0;

extern "C" {
// AddLocalRoute
JNIEXPORT jlong JNICALL
Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllAddLocalRoute(
JNIEnv* env, jobject obj,
jobject lj_AmsNetId, // AMS NetId of ADS server
jstring lj_IpAddr); // IP address, where the ADS server can be found

// DelLocalRoute
JNIEXPORT jlong JNICALL
Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllDelLocalRoute(
JNIEnv* env, jobject obj,
jobject lj_AmsNetId); // AMS NetId of ADS server

// SetLocalAddress
JNIEXPORT jlong JNICALL
Java_de_beckhoff_jni_tcads_AdsCallDllFunction_callDllSetLocalAddress(
JNIEnv* env, jobject obj,
jobject lj_AmsNetId); // AMS NetId of ADS server

// AdsGetDllVersion
JNIEXPORT jlong JNICALL
Expand Down
11 changes: 11 additions & 0 deletions cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,14 @@ if (WIN32)
target_include_directories(AdsToJava-3 PRIVATE "$ENV{JAVA_HOME}/include/win32/")
elseif (EXISTS "$ENV{JAVA_HOME}/include/freebsd")
target_include_directories(AdsToJava-3 PRIVATE "$ENV{JAVA_HOME}/include/freebsd/")
elseif (EXISTS "$ENV{JAVA_HOME}/include/linux")
target_include_directories(AdsToJava-3 PRIVATE "$ENV{JAVA_HOME}/include/linux/")
else()
target_include_directories(AdsToJava-3 PRIVATE "$ENV{JAVA_HOME}/include/unix/")
endif()

if (EXISTS "dependencies")
# the "dependencies" directory can be used to force the build to use specific headers and libraries
target_include_directories(AdsToJava-3 PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../dependencies/AdsApi/TcAdsDll/Include/")
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
find_library(TC_ADS_DLL_LIBRARY NAMES "TcAdsDll" "libTcAdsDll" HINTS "${CMAKE_CURRENT_SOURCE_DIR}/../dependencies/AdsApi/TcAdsDll/Lib/" REQUIRED)
Expand Down Expand Up @@ -88,6 +91,14 @@ elseif (WIN32)
else()
find_library(TC_ADS_DLL_LIBRARY NAMES "TcAdsDll" HINTS "${TWINCAT_INSTALL_DIR}/../AdsApi/TcAdsDll/Lib/x64/" "${TWINCAT_INSTALL_DIR}/../AdsApi/TcAdsDll/x64/lib/" REQUIRED)
endif()
elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
# use https://github.com/Beckhoff/ADS from the git submodule
add_compile_definitions(USE_OPENSOURCE_ADSLIB)
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
message(FATAL_ERROR "Build script not implemented for 32bit Linux.")
endif()
target_include_directories(AdsToJava-3 PRIVATE "/usr/local/include/" "${CMAKE_CURRENT_SOURCE_DIR}/../adslib_for_linux/AdsLib/")
find_library(TC_ADS_DLL_LIBRARY NAMES "TcAdsDll" "libTcAdsDll" "libads.so" HINTS "/usr/local/lib/" "${CMAKE_CURRENT_SOURCE_DIR}/../adslib_for_linux/build/AdsLib/" REQUIRED)
else()
target_include_directories(AdsToJava-3 PRIVATE "/usr/local/include/")
if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "4")
Expand Down
7 changes: 5 additions & 2 deletions cpp/JObjAdsNotificationHeader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ JObjAdsNotificationHeader::JObjAdsNotificationHeader(JNIEnv* lEnv,
: JObjectBase(lEnv, lJObject) {}

void JObjAdsNotificationHeader::setValuesInJObject(
AdsNotificationHeader* pAdsNotificationHeader) {
const AdsNotificationHeader* pAdsNotificationHeader) {
setJObjectValue("mHNotification",
static_cast<jlong>(pAdsNotificationHeader->hNotification));
setJObjectValue("mNTimeStamp",
static_cast<jlong>(pAdsNotificationHeader->nTimeStamp));
setJObjectArray("data", &pAdsNotificationHeader->data[0], false);
const auto* data_ptr = reinterpret_cast<const uint8_t*>(
&pAdsNotificationHeader->cbSampleSize) +
sizeof(pAdsNotificationHeader->cbSampleSize);
setJObjectArray("data", data_ptr, false);
}
3 changes: 2 additions & 1 deletion cpp/JObjAdsNotificationHeader.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ class JObjAdsNotificationHeader : public JObjectBase {
JObjAdsNotificationHeader(JNIEnv* lEnv, jobject lJObject);
~JObjAdsNotificationHeader() = default;

void setValuesInJObject(AdsNotificationHeader* pAdsNotificationHeader);
void
setValuesInJObject(const AdsNotificationHeader* pAdsNotificationHeader);
};
2 changes: 1 addition & 1 deletion cpp/JObjAmsAddr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
JObjAmsAddr::JObjAmsAddr(JNIEnv* lEnv, jobject lJObject)
: JObjectBase(lEnv, lJObject) {}

void JObjAmsAddr::setValuesInJObject(PAmsAddr pAddr) {
void JObjAmsAddr::setValuesInJObject(const AmsAddr* pAddr) {
setJObjectValue("mPort", static_cast<jint>(pAddr->port));

// set both versions of the AmsNetId.
Expand Down
2 changes: 1 addition & 1 deletion cpp/JObjAmsAddr.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ class JObjAmsAddr : public JObjectBase {
JObjAmsAddr(JNIEnv* lEnv, jobject lJObject);
~JObjAmsAddr() = default;

void setValuesInJObject(PAmsAddr pAddr);
void setValuesInJObject(const AmsAddr* pAddr);
void getValuesOutJObject(PAmsAddr pAddr);
};
Loading

0 comments on commit 14755b4

Please sign in to comment.