You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2018/02/02 08:08:42 UTC

[incubator-plc4x] branch feature/Beckhoff_ADS_protocol updated: added simple ADSPlcConnection implementation.

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a commit to branch feature/Beckhoff_ADS_protocol
in repository https://gitbox.apache.org/repos/asf/incubator-plc4x.git


The following commit(s) were added to refs/heads/feature/Beckhoff_ADS_protocol by this push:
     new bba1470  added simple ADSPlcConnection implementation.
bba1470 is described below

commit bba1470b70012e7194aaef59c2585247d97b1878
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Fri Feb 2 09:08:37 2018 +0100

    added simple ADSPlcConnection implementation.
---
 .../org/apache/plc4x/java/ads/ADSPlcDriver.java    |   4 +-
 .../plc4x/java/ads/api/generic/types/AMSNetId.java |  16 +++
 .../plc4x/java/ads/api/generic/types/AMSPort.java  |  12 +-
 .../types/AMSPort.java => package-info.java}       |  28 +---
 .../java/ads/connection/ADSPlcConnection.java      | 154 +++++++++++++++++++++
 .../types/AMSPort.java => model/ADSAddress.java}   |  28 ++--
 .../types/AMSPort.java => model/package-info.java} |  27 +---
 7 files changed, 202 insertions(+), 67 deletions(-)

diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
index 0b2ea3c..d1bcd0d 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/ADSPlcDriver.java
@@ -37,9 +37,9 @@ import java.util.regex.Pattern;
 public class ADSPlcDriver implements PlcDriver {
 
     private static final Pattern ADS_ADDRESS_PATTERN =
-        Pattern.compile("^(?<targetAmsNetId>" + AMSNetId.AMS_NET_ID_REGEX + "):(?<targetAmsPort>" + AMSPort.AMS_PORT_REGEX + ")"
+        Pattern.compile("^(?<targetAmsNetId>" + AMSNetId.AMS_NET_ID_PATTERN + "):(?<targetAmsPort>" + AMSPort.AMS_PORT_PATTERN + ")"
             + "/"
-            + "(?<sourceAmsNetId>" + AMSNetId.AMS_NET_ID_REGEX + "):(?<sourceAmsPort>" + AMSPort.AMS_PORT_REGEX + ")");
+            + "(?<sourceAmsNetId>" + AMSNetId.AMS_NET_ID_PATTERN + "):(?<sourceAmsPort>" + AMSPort.AMS_PORT_PATTERN + ")");
     private static final Pattern ADS_URI_PATTERN = Pattern.compile("^ads://(?<host>\\w+)/" + ADS_ADDRESS_PATTERN);
 
     @Override
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSNetId.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSNetId.java
index c69299e..73fc07d 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSNetId.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSNetId.java
@@ -18,8 +18,12 @@
  */
 package org.apache.plc4x.java.ads.api.generic.types;
 
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.plc4x.java.ads.api.util.ByteValue;
 
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
 /**
  * The AMSNetId consists of 6 bytes and addresses the transmitter or receiver. One possible AMSNetId would be e.g.. 172.16.17.10.1.1. The storage arrangement in this example is as follows:
  * <p>
@@ -33,6 +37,9 @@ import org.apache.plc4x.java.ads.api.util.ByteValue;
  */
 public class AMSNetId extends ByteValue {
 
+    public static final Pattern AMS_NET_ID_PATTERN =
+        Pattern.compile("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
+
     public static final int NUM_BYTES = 6;
 
     AMSNetId(byte... values) {
@@ -47,4 +54,13 @@ public class AMSNetId extends ByteValue {
     public static AMSNetId of(int octed1, int octed2, int octed3, int octed4, int octed5, int octed6) {
         return new AMSNetId((byte) octed1, (byte) octed2, (byte) octed3, (byte) octed4, (byte) octed5, (byte) octed6);
     }
+
+    public static AMSNetId of(String address) {
+        if (!AMS_NET_ID_PATTERN.matcher(address).matches()) {
+            throw new IllegalArgumentException(address + " must match " + AMS_NET_ID_PATTERN);
+        }
+        String[] split = address.split("\\.");
+        byte[] bytes = ArrayUtils.toPrimitive(Stream.of(split).map(Integer::parseInt).map(Integer::byteValue).toArray(Byte[]::new));
+        return new AMSNetId(bytes);
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
index d030a3a..e5e9c28 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
@@ -21,10 +21,13 @@ package org.apache.plc4x.java.ads.api.generic.types;
 import org.apache.plc4x.java.ads.api.util.ByteValue;
 
 import java.nio.ByteBuffer;
+import java.util.regex.Pattern;
 
 public class AMSPort extends ByteValue {
 
-    private static final int NUM_BYTES = 2;
+    public static final Pattern AMS_PORT_PATTERN = Pattern.compile("\\d+");
+
+    public static final int NUM_BYTES = 2;
 
     AMSPort(byte... value) {
         super(value);
@@ -38,4 +41,11 @@ public class AMSPort extends ByteValue {
     public static AMSPort of(int port) {
         return new AMSPort(ByteBuffer.allocate(NUM_BYTES).putInt(port).array());
     }
+
+    public static AMSPort of(String port) {
+        if (!AMS_PORT_PATTERN.matcher(port).matches()) {
+            throw new IllegalArgumentException(port + " must match " + AMS_PORT_PATTERN);
+        }
+        return new AMSPort(ByteBuffer.allocate(NUM_BYTES).putInt(Integer.parseInt(port)).array());
+    }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/package-info.java
similarity index 59%
copy from plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
copy to plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/package-info.java
index d030a3a..6bca8c9 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/package-info.java
@@ -16,26 +16,8 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.ads.api.generic.types;
-
-import org.apache.plc4x.java.ads.api.util.ByteValue;
-
-import java.nio.ByteBuffer;
-
-public class AMSPort extends ByteValue {
-
-    private static final int NUM_BYTES = 2;
-
-    AMSPort(byte... value) {
-        super(value);
-        assertLength(NUM_BYTES);
-    }
-
-    public static AMSPort of(byte... values) {
-        return new AMSPort(values);
-    }
-
-    public static AMSPort of(int port) {
-        return new AMSPort(ByteBuffer.allocate(NUM_BYTES).putInt(port).array());
-    }
-}
+/**
+ * This package contains a replication of the specification found online.
+ * @see <a href="https://infosys.beckhoff.com/english.php?content=../content/1033/tcadsamsspec/html/tcadsamsspec_intro.htm">ADS SPEC</a>
+ */
+package org.apache.plc4x.java.ads.api;
\ No newline at end of file
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/ADSPlcConnection.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/ADSPlcConnection.java
new file mode 100644
index 0000000..a6b7ba6
--- /dev/null
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/ADSPlcConnection.java
@@ -0,0 +1,154 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+package org.apache.plc4x.java.ads.connection;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.channel.*;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
+import org.apache.plc4x.java.ads.model.ADSAddress;
+import org.apache.plc4x.java.ads.netty.Plc4XADSProtocol;
+import org.apache.plc4x.java.api.connection.AbstractPlcConnection;
+import org.apache.plc4x.java.api.connection.PlcReader;
+import org.apache.plc4x.java.api.connection.PlcWriter;
+import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
+import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.api.messages.*;
+import org.apache.plc4x.java.api.model.Address;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ADSPlcConnection extends AbstractPlcConnection implements PlcReader, PlcWriter {
+
+    private static final int TCP_PORT = 48898;
+
+    private final String hostName;
+
+    private final int pduSize;
+
+    private final AMSNetId targetAmsNetId;
+    private final AMSPort targetAmsPort;
+    private final AMSNetId sourceAmsNetId;
+    private final AMSPort sourceAmsPort;
+
+    private EventLoopGroup workerGroup;
+    private Channel channel;
+
+    public ADSPlcConnection(String hostName, AMSNetId targetAmsNetId, AMSPort targetAmsPort, AMSNetId sourceAmsNetId, AMSPort sourceAmsPort) {
+        this.hostName = hostName;
+        this.pduSize = 1024;
+        this.targetAmsNetId = targetAmsNetId;
+        this.targetAmsPort = targetAmsPort;
+        this.sourceAmsNetId = sourceAmsNetId;
+        this.sourceAmsPort = sourceAmsPort;
+    }
+
+    public String getHostName() {
+        return hostName;
+    }
+
+    public int getPduSize() {
+        return pduSize;
+    }
+
+    @Override
+    public void connect() throws PlcConnectionException {
+        workerGroup = new NioEventLoopGroup();
+
+        try {
+            // As we don't just want to wait till the connection is established,
+            // define a future we can use to signal back that the ads session is
+            // finished initializing.
+            CompletableFuture<Void> sessionSetupCompleteFuture = new CompletableFuture<>();
+
+            InetAddress serverInetAddress = InetAddress.getByName(hostName);
+
+            Bootstrap bootstrap = new Bootstrap();
+            bootstrap.group(workerGroup);
+            bootstrap.channel(NioSocketChannel.class);
+            bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
+            bootstrap.option(ChannelOption.TCP_NODELAY, true);
+            bootstrap.handler(new ChannelInitializer() {
+                @Override
+                protected void initChannel(Channel channel) throws Exception {
+                    // Build the protocol stack for communicating with the ads protocol.
+                    ChannelPipeline pipeline = channel.pipeline();
+                    pipeline.addLast(new Plc4XADSProtocol());
+                }
+            });
+            // Start the client.
+            ChannelFuture f = bootstrap.connect(serverInetAddress, TCP_PORT).sync();
+            f.awaitUninterruptibly();
+            // Wait till the session is finished initializing.
+            channel = f.channel();
+
+            sessionSetupCompleteFuture.get();
+        } catch (UnknownHostException e) {
+            throw new PlcConnectionException("Unknown Host " + hostName, e);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new PlcConnectionException(e);
+        } catch (ExecutionException e) {
+            throw new PlcConnectionException(e);
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        workerGroup.shutdownGracefully();
+    }
+
+    @Override
+    public Address parseAddress(String addressString) throws PlcException {
+        Matcher matcher = Pattern.compile("(?<targetAmsNetId>" + AMSNetId.AMS_NET_ID_PATTERN + "):(?<targetAmsPort>" + AMSPort.AMS_PORT_PATTERN + ")").matcher(addressString);
+        if (!matcher.matches()) {
+            throw new PlcConnectionException(
+                "Address string doesn't match the format '{targetAmsNetId}:{targetAmsPort}'");
+        }
+        AMSNetId targetAmsNetId = AMSNetId.of(matcher.group("targetAmsNetId"));
+        AMSPort targetAmsPort = AMSPort.of(matcher.group("targetAmsPort"));
+        return new ADSAddress(targetAmsNetId, targetAmsPort);
+    }
+
+    @Override
+    public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
+        CompletableFuture<PlcReadResponse> readFuture = new CompletableFuture<>();
+        PlcRequestContainer<PlcReadRequest, PlcReadResponse> container =
+            new PlcRequestContainer<>(readRequest, readFuture);
+        channel.writeAndFlush(container);
+        return readFuture;
+    }
+
+    @Override
+    public CompletableFuture<PlcWriteResponse> write(PlcWriteRequest writeRequest) {
+        CompletableFuture<PlcWriteResponse> writeFuture = new CompletableFuture<>();
+        PlcRequestContainer<PlcWriteRequest, PlcWriteResponse> container =
+            new PlcRequestContainer<>(writeRequest, writeFuture);
+        channel.writeAndFlush(container);
+        return writeFuture;
+    }
+
+}
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/ADSAddress.java
similarity index 60%
copy from plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
copy to plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/ADSAddress.java
index d030a3a..66bb617 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/ADSAddress.java
@@ -16,26 +16,18 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.ads.api.generic.types;
+package org.apache.plc4x.java.ads.model;
 
-import org.apache.plc4x.java.ads.api.util.ByteValue;
+import org.apache.plc4x.java.ads.api.generic.types.AMSNetId;
+import org.apache.plc4x.java.ads.api.generic.types.AMSPort;
+import org.apache.plc4x.java.api.model.Address;
 
-import java.nio.ByteBuffer;
+public class ADSAddress implements Address {
+    public final AMSNetId targetAmsNetId;
+    public final AMSPort targetAmsPort;
 
-public class AMSPort extends ByteValue {
-
-    private static final int NUM_BYTES = 2;
-
-    AMSPort(byte... value) {
-        super(value);
-        assertLength(NUM_BYTES);
-    }
-
-    public static AMSPort of(byte... values) {
-        return new AMSPort(values);
-    }
-
-    public static AMSPort of(int port) {
-        return new AMSPort(ByteBuffer.allocate(NUM_BYTES).putInt(port).array());
+    public ADSAddress(AMSNetId targetAmsNetId, AMSPort targetAmsPort) {
+        this.targetAmsNetId = targetAmsNetId;
+        this.targetAmsPort = targetAmsPort;
     }
 }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/package-info.java
similarity index 59%
copy from plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
copy to plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/package-info.java
index d030a3a..a99929a 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/generic/types/AMSPort.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/model/package-info.java
@@ -16,26 +16,7 @@
  specific language governing permissions and limitations
  under the License.
  */
-package org.apache.plc4x.java.ads.api.generic.types;
-
-import org.apache.plc4x.java.ads.api.util.ByteValue;
-
-import java.nio.ByteBuffer;
-
-public class AMSPort extends ByteValue {
-
-    private static final int NUM_BYTES = 2;
-
-    AMSPort(byte... value) {
-        super(value);
-        assertLength(NUM_BYTES);
-    }
-
-    public static AMSPort of(byte... values) {
-        return new AMSPort(values);
-    }
-
-    public static AMSPort of(int port) {
-        return new AMSPort(ByteBuffer.allocate(NUM_BYTES).putInt(port).array());
-    }
-}
+/**
+ * This package contains plc4x specific modeling.
+ */
+package org.apache.plc4x.java.ads.model;
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
sruehl@apache.org.