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 <sru...@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 sru...@apache.org.