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/07/26 15:11:04 UTC

[incubator-plc4x] branch master updated (bd291ae -> 810d649)

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

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


    from bd291ae  added type generator function to Plc4XS7ProtocolTest
     new c0b3745  added double support to S7
     new d7159aa  added PlcNotImplementedException to mark not yet implemented code parts.
     new 810d649  fixed double implementation in S7

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../exceptions/PlcNotImplementedException.java}    |  10 +-
 .../java/ads/connection/AdsTcpPlcConnection.java   | 175 +++++++++++----------
 .../java/modbus/netty/Plc4XModbusProtocol.java     |   7 +-
 .../plc4x/java/s7/netty/Plc4XS7Protocol.java       |  23 +--
 .../plc4x/java/s7/netty/util/S7TypeDecoder.java    |  27 +++-
 .../plc4x/java/s7/netty/util/S7TypeEncoder.java    |  29 +++-
 .../plc4x/java/s7/netty/Plc4XS7ProtocolTest.java   |   8 +-
 7 files changed, 169 insertions(+), 110 deletions(-)
 copy plc4j/{protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/commands/types/package-info.java => api/src/main/java/org/apache/plc4x/java/api/exceptions/PlcNotImplementedException.java} (74%)


[incubator-plc4x] 01/03: added double support to S7

Posted by sr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c0b3745bf6941f16a248cd16dc2a12d1fa280b7a
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Thu Jul 26 16:30:02 2018 +0200

    added double support to S7
---
 .../plc4x/java/s7/netty/Plc4XS7Protocol.java       | 16 ++++++++------
 .../plc4x/java/s7/netty/util/S7TypeDecoder.java    | 25 ++++++++++++++++++----
 .../plc4x/java/s7/netty/Plc4XS7ProtocolTest.java   |  8 +++++--
 3 files changed, 37 insertions(+), 12 deletions(-)

diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
index 7ef1f2d..1aa4c5f 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
@@ -89,17 +89,17 @@ public class Plc4XS7Protocol extends PlcMessageToMessageCodec<S7Message, PlcRequ
      * correlates needs to be notified about the problem. If a container is found, we can relay the
      * exception to that by calling completeExceptionally and passing in the exception.
      *
-     * @param ctx the current protocol layers context
+     * @param ctx   the current protocol layers context
      * @param cause the exception that was caught
      * @throws Exception throws an exception if something goes wrong internally
      */
     @Override
     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
-        if(cause instanceof PlcProtocolPayloadTooBigException) {
+        if (cause instanceof PlcProtocolPayloadTooBigException) {
             PlcProtocolPayloadTooBigException pptbe = (PlcProtocolPayloadTooBigException) cause;
-            if(pptbe.getPayload() instanceof S7RequestMessage) {
+            if (pptbe.getPayload() instanceof S7RequestMessage) {
                 S7RequestMessage request = (S7RequestMessage) pptbe.getPayload();
-                if(request.getParent() instanceof PlcRequestContainer) {
+                if (request.getParent() instanceof PlcRequestContainer) {
                     PlcRequestContainer requestContainer = (PlcRequestContainer) request.getParent();
 
                     // Remove the current request from the unconfirmed requests list.
@@ -108,8 +108,8 @@ public class Plc4XS7Protocol extends PlcMessageToMessageCodec<S7Message, PlcRequ
                     requestContainer.getResponseFuture().completeExceptionally(cause);
                 }
             }
-        } else if((cause instanceof IOException) && (cause.getMessage().contains("Connection reset by peer") ||
-                cause.getMessage().contains("Operation timed out"))) {
+        } else if ((cause instanceof IOException) && (cause.getMessage().contains("Connection reset by peer") ||
+            cause.getMessage().contains("Operation timed out"))) {
             String reason = cause.getMessage().contains("Connection reset by peer") ?
                 "Connection terminated unexpectedly" : "Remote host not responding";
             if (!requests.isEmpty()) {
@@ -249,6 +249,8 @@ public class Plc4XS7Protocol extends PlcMessageToMessageCodec<S7Message, PlcRequ
             return TransportSize.DATE_AND_TIME;
         } else if (datatype == Float.class) {
             return TransportSize.REAL;
+        } else if (datatype == Double.class) {
+            return TransportSize.REAL;
         } else if (datatype == Integer.class) {
             return TransportSize.DWORD;
         } else if (datatype == String.class) {
@@ -269,6 +271,8 @@ public class Plc4XS7Protocol extends PlcMessageToMessageCodec<S7Message, PlcRequ
             return null;
         } else if (datatype == Float.class) {
             return DataTransportSize.REAL;
+        } else if (datatype == Double.class) {
+            return DataTransportSize.REAL;
         } else if (datatype == Integer.class) {
             return DataTransportSize.BYTE_WORD_DWORD;
         } else if (datatype == String.class) {
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeDecoder.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeDecoder.java
index bd0ef97..08b5c91 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeDecoder.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeDecoder.java
@@ -54,18 +54,35 @@ public class S7TypeDecoder {
                 // Description of the Real number format:
                 // https://www.sps-lehrgang.de/zahlenformate-step7/#c144
                 // https://de.wikipedia.org/wiki/IEEE_754
-                int intValue = ((s7Data[i] & 0xff) << 24) | ((s7Data[i + 1] & 0xff) << 16) |
-                    ((s7Data[i + 2] & 0xff) << 8) | (s7Data[i + 3] & 0xff);
+                int intValue = ((s7Data[i] & 0xff) << 24)
+                    | ((s7Data[i + 1] & 0xff) << 16)
+                    | ((s7Data[i + 2] & 0xff) << 8)
+                    | (s7Data[i + 3] & 0xff);
                 result.add(Float.intBitsToFloat(intValue));
                 i += 4;
+            } else if (datatype == Double.class) {
+                // Description of the Real number format:
+                // https://www.sps-lehrgang.de/zahlenformate-step7/#c144
+                // https://de.wikipedia.org/wiki/IEEE_754
+                long longValue = (((long) (s7Data[i] & 0xff)) << 56)
+                    | (((long) (s7Data[i] & 0xff)) << 48)
+                    | (((long) (s7Data[i + 1] & 0xff)) << 40)
+                    | (((long) (s7Data[i + 2] & 0xff)) << 32)
+
+                    | (((long) (s7Data[i + 3] & 0xff)) << 24)
+                    | (((long) (s7Data[i + 4] & 0xff)) << 16)
+                    | (((long) (s7Data[i + 5] & 0xff)) << 8)
+                    | (((long) s7Data[i + 6] & 0xff));
+                result.add(Double.longBitsToDouble(longValue));
+                i += 8;
             } else if (datatype == String.class) {
                 // Every string value had a prefix of two bytes for which I have no idea, what the meaning is.
                 // This code assumes the string values doesn't contain UTF-8 values with a code of 0x00 as it
                 // uses this as termination char.
                 try {
                     int j = 0;
-                    for(; j < s7Data.length; j++) {
-                        if(s7Data[j] == 0) {
+                    for (; j < s7Data.length; j++) {
+                        if (s7Data[j] == 0) {
                             break;
                         }
                     }
diff --git a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/netty/Plc4XS7ProtocolTest.java b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/netty/Plc4XS7ProtocolTest.java
index b2eb489..f1e5eb7 100644
--- a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/netty/Plc4XS7ProtocolTest.java
+++ b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/netty/Plc4XS7ProtocolTest.java
@@ -73,7 +73,6 @@ public class Plc4XS7ProtocolTest extends NettyTestBase {
     private List<String> notYetSupportedDataType = Stream.of(
         Calendar.class,
         GregorianCalendar.class,
-        Double.class,
         BigInteger.class,
         byte[].class,
         Byte[].class
@@ -187,6 +186,8 @@ public class Plc4XS7ProtocolTest extends NettyTestBase {
             return (T) Calendar.getInstance();
         } else if (type == Float.class) {
             return (T) Float.valueOf(123f);
+        } else if (type == Double.class) {
+            return (T) Double.valueOf(123f);
         } else if (type == Integer.class) {
             return (T) Integer.valueOf(123);
         } else if (type == String.class) {
@@ -214,8 +215,11 @@ public class Plc4XS7ProtocolTest extends NettyTestBase {
             // TODO: what size is calender?
             data = new byte[]{(byte) 0b0000_0000};
         } else if (type == Float.class) {
-            size = DataTransportSize.BYTE_WORD_DWORD;
+            size = DataTransportSize.REAL;
             data = new byte[]{(byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000};
+        } else if (type == Double.class) {
+            size = DataTransportSize.REAL;
+            data = new byte[]{(byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000};
         } else if (type == Integer.class) {
             size = DataTransportSize.INTEGER;
             data = new byte[]{(byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000, (byte) 0b0000_0000};


[incubator-plc4x] 03/03: fixed double implementation in S7

Posted by sr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 810d649f4912c5f720f44f6aed41440a7b99fb27
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Thu Jul 26 17:11:00 2018 +0200

    fixed double implementation in S7
---
 .../plc4x/java/s7/netty/util/S7TypeEncoder.java      | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeEncoder.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeEncoder.java
index 4ff0f4b..19f9af6 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeEncoder.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeEncoder.java
@@ -49,6 +49,8 @@ public class S7TypeEncoder {
             throw new PlcNotImplementedException("calender not yet implemented in s7");
         } else if (valueType == Float.class) {
             result = encodeFloat(values, length);
+        } else if (valueType == Double.class) {
+            result = encodeDouble(values, length);
         } else if (valueType == String.class) {
             result = encodeString(values, length);
         } else {
@@ -89,6 +91,24 @@ public class S7TypeEncoder {
         return result;
     }
 
+    public static byte[] encodeDouble(Object[] values, int length) {
+        byte[] result;
+        result = new byte[length * 8];
+        for (int i = 0; i < length; i++) {
+            double doubleValue = (double) values[i];
+            long longValue = Double.doubleToLongBits(doubleValue);
+            result[(i * 8)] =     (byte) ((longValue & 0xFF000000_00000000L) >> 56);
+            result[(i * 8) + 1] = (byte) ((longValue & 0x00FF0000_00000000L) >> 48);
+            result[(i * 8) + 2] = (byte) ((longValue & 0x0000FF00_00000000L) >> 40);
+            result[(i * 8) + 3] = (byte) ((longValue & 0x000000FF_00000000L) >> 32);
+            result[(i * 8) + 4] = (byte) ((longValue & 0x00000000_FF000000L) >> 24);
+            result[(i * 8) + 5] = (byte) ((longValue & 0x00000000_00FF0000L) >> 16);
+            result[(i * 8) + 6] = (byte) ((longValue & 0x00000000_0000FF00L) >> 8);
+            result[(i * 8) + 7] = (byte) (longValue & 0x00000000_000000FFL);
+        }
+        return result;
+    }
+
     public static byte[] encodeInteger(Object[] values, int length) {
         byte[] result;
         result = new byte[length * 4];


[incubator-plc4x] 02/03: added PlcNotImplementedException to mark not yet implemented code parts.

Posted by sr...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d7159aa447e85c50440a1ceb36dfd30181a07d08
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Thu Jul 26 16:59:13 2018 +0200

    added PlcNotImplementedException to mark not yet implemented code parts.
---
 .../api/exceptions/PlcNotImplementedException.java |  28 ++++
 .../java/ads/connection/AdsTcpPlcConnection.java   | 175 +++++++++++----------
 .../java/modbus/netty/Plc4XModbusProtocol.java     |   7 +-
 .../plc4x/java/s7/netty/Plc4XS7Protocol.java       |   7 +-
 .../plc4x/java/s7/netty/util/S7TypeDecoder.java    |   2 +-
 .../plc4x/java/s7/netty/util/S7TypeEncoder.java    |   9 +-
 6 files changed, 132 insertions(+), 96 deletions(-)

diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/exceptions/PlcNotImplementedException.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/exceptions/PlcNotImplementedException.java
new file mode 100644
index 0000000..04e2fe3
--- /dev/null
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/exceptions/PlcNotImplementedException.java
@@ -0,0 +1,28 @@
+/*
+ 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.api.exceptions;
+
+/**
+ * indicates that a functionality is not implemented yet.
+ */
+public class PlcNotImplementedException extends PlcRuntimeException {
+    public PlcNotImplementedException(String message) {
+        super(message);
+    }
+}
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnection.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnection.java
index 124d8d6..e4458df 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnection.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/connection/AdsTcpPlcConnection.java
@@ -35,6 +35,7 @@ import org.apache.plc4x.java.ads.protocol.Payload2TcpProtocol;
 import org.apache.plc4x.java.ads.protocol.Plc4x2AdsProtocol;
 import org.apache.plc4x.java.ads.protocol.util.LittleEndianDecoder;
 import org.apache.plc4x.java.api.connection.PlcSubscriber;
+import org.apache.plc4x.java.api.exceptions.PlcNotImplementedException;
 import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.*;
@@ -51,7 +52,10 @@ import org.slf4j.LoggerFactory;
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.util.*;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
@@ -133,92 +137,93 @@ public class AdsTcpPlcConnection extends AdsAbstractPlcConnection implements Plc
     public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
         // TODO: Make this multi-value
         CompletableFuture<PlcSubscriptionResponse> future = new CompletableFuture<>();
-        if(subscriptionRequest.getNumberOfItems() == 1) {
-            SubscriptionRequestItem<?> subscriptionRequestItem = subscriptionRequest.getRequestItem().orElse(null);
-
-            Objects.requireNonNull(subscriptionRequestItem);
-            Objects.requireNonNull(subscriptionRequestItem.getConsumer());
-            Objects.requireNonNull(subscriptionRequestItem.getAddress());
-            Objects.requireNonNull(subscriptionRequestItem.getDatatype());
-
-            Address address = subscriptionRequestItem.getAddress();
-            Class<?> datatype = subscriptionRequestItem.getDatatype();
-
-            IndexGroup indexGroup;
-            IndexOffset indexOffset;
-            // If this is a symbolic address, it has to be resolved first.
-            // TODO: This is blocking, should be changed to be async.
-            if (address instanceof SymbolicAdsAddress) {
-                mapAddress((SymbolicAdsAddress) address);
-                AdsAddress adsAddress = addressMapping.get(address);
-                if (adsAddress == null) {
-                    throw new PlcRuntimeException("Unresolvable address" + address);
-                }
-                indexGroup = IndexGroup.of(adsAddress.getIndexGroup());
-                indexOffset = IndexOffset.of(adsAddress.getIndexOffset());
-            }
-            // If it's no symbolic address, we can continue immediately
-            // without having to do any resolving.
-            else if (address instanceof AdsAddress) {
-                AdsAddress adsAddress = (AdsAddress) address;
-                indexGroup = IndexGroup.of(adsAddress.getIndexGroup());
-                indexOffset = IndexOffset.of(adsAddress.getIndexOffset());
-            } else {
-                throw new IllegalArgumentException("Unsupported address type " + address.getClass());
-            }
+        if (subscriptionRequest.getNumberOfItems() != 1) {
+            throw new PlcNotImplementedException("Multirequest on subscribe not implemented yet");
+        }
+
+        SubscriptionRequestItem<?> subscriptionRequestItem = subscriptionRequest.getRequestItem().orElseThrow(NullPointerException::new);
 
-            // Prepare the subscription request itself.
-            AdsAddDeviceNotificationRequest adsAddDeviceNotificationRequest = AdsAddDeviceNotificationRequest.of(
-                targetAmsNetId,
-                targetAmsPort,
-                sourceAmsNetId,
-                sourceAmsPort,
-                Invoke.NONE,
-                indexGroup,
-                indexOffset,
-                LittleEndianDecoder.getLengthFor(datatype, 1),
-                TransmissionMode.DefinedValues.ADSTRANS_SERVERCYCLE,
-                MaxDelay.of(0),
-                CycleTime.of(4000000)
-            );
-
-            // Send the request to the plc and wait for a response
-            // TODO: This is blocking, should be changed to be async.
-            CompletableFuture<PlcProprietaryResponse<AdsAddDeviceNotificationResponse>> addDeviceFuture = new CompletableFuture<>();
-            channel.writeAndFlush(new PlcRequestContainer<>(new PlcProprietaryRequest<>(adsAddDeviceNotificationRequest), addDeviceFuture));
-            PlcProprietaryResponse<AdsAddDeviceNotificationResponse> addDeviceResponse = getFromFuture(addDeviceFuture, ADD_DEVICE_TIMEOUT);
-            AdsAddDeviceNotificationResponse response = addDeviceResponse.getResponse();
-
-            // Abort if we got anything but a successful response.
-            if (response.getResult().toAdsReturnCode() != AdsReturnCode.ADS_CODE_0) {
-                throw new PlcRuntimeException("Error code received " + response.getResult());
+        Objects.requireNonNull(subscriptionRequestItem.getConsumer());
+        Objects.requireNonNull(subscriptionRequestItem.getAddress());
+        Objects.requireNonNull(subscriptionRequestItem.getDatatype());
+
+        Address address = subscriptionRequestItem.getAddress();
+        Class<?> datatype = subscriptionRequestItem.getDatatype();
+
+        IndexGroup indexGroup;
+        IndexOffset indexOffset;
+        // If this is a symbolic address, it has to be resolved first.
+        // TODO: This is blocking, should be changed to be async.
+        if (address instanceof SymbolicAdsAddress) {
+            mapAddress((SymbolicAdsAddress) address);
+            AdsAddress adsAddress = addressMapping.get(address);
+            if (adsAddress == null) {
+                throw new PlcRuntimeException("Unresolvable address" + address);
             }
-            AdsSubscriptionHandle adsSubscriptionHandle = new AdsSubscriptionHandle(response.getNotificationHandle());
-            future.complete(new PlcSubscriptionResponse(subscriptionRequest, Collections.singletonList(
-                new SubscriptionResponseItem<>(subscriptionRequestItem, adsSubscriptionHandle, ResponseCode.OK))));
-
-            Consumer<AdsDeviceNotificationRequest> adsDeviceNotificationRequestConsumer =
-                adsDeviceNotificationRequest -> adsDeviceNotificationRequest.getAdsStampHeaders().forEach(adsStampHeader -> {
-                    Calendar timeStamp = Calendar.getInstance();
-                    timeStamp.setTime(adsStampHeader.getTimeStamp().getAsDate());
-
-                    adsStampHeader.getAdsNotificationSamples()
-                        .forEach(adsNotificationSample -> {
-                            Data data = adsNotificationSample.getData();
-                            try {
-                                @SuppressWarnings("unchecked")
-                                List<?> decodeData = LittleEndianDecoder.decodeData(datatype, data.getBytes());
-                                SubscriptionEventItem subscriptionEventItem =
-                                    new SubscriptionEventItem(subscriptionRequestItem, timeStamp, decodeData);
-                                subscriptionRequestItem.getConsumer().accept(subscriptionEventItem);
-                            } catch (PlcProtocolException | RuntimeException e) {
-                                LOGGER.error("Can't decode {}", data, e);
-                            }
-                        });
-                });
-            // TODO: What's this for? Is this still needed if we use the consumers in the subscriptions?
-            getChannel().pipeline().get(Plc4x2AdsProtocol.class).addConsumer(adsDeviceNotificationRequestConsumer);
+            indexGroup = IndexGroup.of(adsAddress.getIndexGroup());
+            indexOffset = IndexOffset.of(adsAddress.getIndexOffset());
+        }
+        // If it's no symbolic address, we can continue immediately
+        // without having to do any resolving.
+        else if (address instanceof AdsAddress) {
+            AdsAddress adsAddress = (AdsAddress) address;
+            indexGroup = IndexGroup.of(adsAddress.getIndexGroup());
+            indexOffset = IndexOffset.of(adsAddress.getIndexOffset());
+        } else {
+            throw new IllegalArgumentException("Unsupported address type " + address.getClass());
+        }
+
+        // Prepare the subscription request itself.
+        AdsAddDeviceNotificationRequest adsAddDeviceNotificationRequest = AdsAddDeviceNotificationRequest.of(
+            targetAmsNetId,
+            targetAmsPort,
+            sourceAmsNetId,
+            sourceAmsPort,
+            Invoke.NONE,
+            indexGroup,
+            indexOffset,
+            LittleEndianDecoder.getLengthFor(datatype, 1),
+            TransmissionMode.DefinedValues.ADSTRANS_SERVERCYCLE,
+            MaxDelay.of(0),
+            CycleTime.of(4000000)
+        );
+
+        // Send the request to the plc and wait for a response
+        // TODO: This is blocking, should be changed to be async.
+        CompletableFuture<PlcProprietaryResponse<AdsAddDeviceNotificationResponse>> addDeviceFuture = new CompletableFuture<>();
+        channel.writeAndFlush(new PlcRequestContainer<>(new PlcProprietaryRequest<>(adsAddDeviceNotificationRequest), addDeviceFuture));
+        PlcProprietaryResponse<AdsAddDeviceNotificationResponse> addDeviceResponse = getFromFuture(addDeviceFuture, ADD_DEVICE_TIMEOUT);
+        AdsAddDeviceNotificationResponse response = addDeviceResponse.getResponse();
+
+        // Abort if we got anything but a successful response.
+        if (response.getResult().toAdsReturnCode() != AdsReturnCode.ADS_CODE_0) {
+            throw new PlcRuntimeException("Error code received " + response.getResult());
         }
+        AdsSubscriptionHandle adsSubscriptionHandle = new AdsSubscriptionHandle(response.getNotificationHandle());
+        future.complete(new PlcSubscriptionResponse(subscriptionRequest, Collections.singletonList(
+            new SubscriptionResponseItem<>(subscriptionRequestItem, adsSubscriptionHandle, ResponseCode.OK))));
+
+        Consumer<AdsDeviceNotificationRequest> adsDeviceNotificationRequestConsumer =
+            adsDeviceNotificationRequest -> adsDeviceNotificationRequest.getAdsStampHeaders().forEach(adsStampHeader -> {
+                Calendar timeStamp = Calendar.getInstance();
+                timeStamp.setTime(adsStampHeader.getTimeStamp().getAsDate());
+
+                adsStampHeader.getAdsNotificationSamples()
+                    .forEach(adsNotificationSample -> {
+                        Data data = adsNotificationSample.getData();
+                        try {
+                            @SuppressWarnings("unchecked")
+                            List<?> decodeData = LittleEndianDecoder.decodeData(datatype, data.getBytes());
+                            SubscriptionEventItem subscriptionEventItem =
+                                new SubscriptionEventItem(subscriptionRequestItem, timeStamp, decodeData);
+                            subscriptionRequestItem.getConsumer().accept(subscriptionEventItem);
+                        } catch (PlcProtocolException | RuntimeException e) {
+                            LOGGER.error("Can't decode {}", data, e);
+                        }
+                    });
+            });
+        // TODO: What's this for? Is this still needed if we use the consumers in the subscriptions?
+        getChannel().pipeline().get(Plc4x2AdsProtocol.class).addConsumer(adsDeviceNotificationRequestConsumer);
         return future;
     }
 
@@ -226,7 +231,7 @@ public class AdsTcpPlcConnection extends AdsAbstractPlcConnection implements Plc
     public CompletableFuture<PlcUnsubscriptionResponse> unsubscribe(PlcUnsubscriptionRequest unsubscriptionRequest) {
         for (UnsubscriptionRequestItem unsubscriptionRequestItem : unsubscriptionRequest.getRequestItems()) {
             Objects.requireNonNull(unsubscriptionRequestItem);
-            if(unsubscriptionRequestItem.getSubscriptionHandle() instanceof AdsSubscriptionHandle) {
+            if (unsubscriptionRequestItem.getSubscriptionHandle() instanceof AdsSubscriptionHandle) {
                 AdsSubscriptionHandle adsSubscriptionHandle =
                     (AdsSubscriptionHandle) unsubscriptionRequestItem.getSubscriptionHandle();
                 AdsDeleteDeviceNotificationRequest adsDeleteDeviceNotificationRequest =
diff --git a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java
index 3c9e98b..3d5a1f6 100644
--- a/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java
+++ b/plc4j/protocols/modbus/src/main/java/org/apache/plc4x/java/modbus/netty/Plc4XModbusProtocol.java
@@ -28,6 +28,7 @@ import io.netty.channel.ChannelHandlerContext;
 import io.netty.handler.codec.MessageToMessageCodec;
 import org.apache.commons.lang3.ArrayUtils;
 import org.apache.plc4x.java.api.exceptions.PlcException;
+import org.apache.plc4x.java.api.exceptions.PlcNotImplementedException;
 import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
 import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.*;
@@ -72,7 +73,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload,
     private void encodeWriteRequest(PlcRequestContainer<PlcRequest, PlcResponse> msg, List<Object> out) throws PlcException {
         PlcWriteRequest request = (PlcWriteRequest) msg.getRequest();
         // TODO: support multiple requests
-        WriteRequestItem<?> writeRequestItem = request.getRequestItem().orElseThrow(() -> new PlcProtocolException("Only single message supported for now"));
+        WriteRequestItem<?> writeRequestItem = request.getRequestItem().orElseThrow(() -> new PlcNotImplementedException("Only single message supported for now"));
         // TODO: check if we can map like this. Implication is that we can only work with int, short, byte and boolean
         // TODO: for higher datatypes float, double etc we might need to split the bytes into chunks
         int quantity = writeRequestItem.getSize();
@@ -129,7 +130,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload,
     private void encodeReadRequest(PlcRequestContainer<PlcRequest, PlcResponse> msg, List<Object> out) throws PlcException {
         PlcReadRequest request = (PlcReadRequest) msg.getRequest();
         // TODO: support multiple requests
-        ReadRequestItem<?> readRequestItem = request.getRequestItem().orElseThrow(() -> new PlcProtocolException("Only single message supported for now"));
+        ReadRequestItem<?> readRequestItem = request.getRequestItem().orElseThrow(() -> new PlcNotImplementedException("Only single message supported for now"));
         // TODO: check if we can map like this. Implication is that we can only work with int, short, byte and boolean
         // TODO: for higher datatypes float, double etc we might need to split the bytes into chunks
         int quantity = readRequestItem.getSize();
@@ -175,7 +176,7 @@ public class Plc4XModbusProtocol extends MessageToMessageCodec<ModbusTcpPayload,
 
         // TODO: only single Item supported for now
         PlcRequest<?> request = plcRequestContainer.getRequest();
-        RequestItem requestItem = request.getRequestItem().orElseThrow(() -> new PlcProtocolException("Only single message supported for now"));
+        RequestItem requestItem = request.getRequestItem().orElseThrow(() -> new PlcNotImplementedException("Only single message supported for now"));
         Class<?> dataType = requestItem.getDatatype();
 
         ModbusPdu modbusPdu = msg.getModbusPdu();
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
index 1aa4c5f..894a560 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/Plc4XS7Protocol.java
@@ -19,10 +19,7 @@ under the License.
 package org.apache.plc4x.java.s7.netty;
 
 import io.netty.channel.ChannelHandlerContext;
-import org.apache.plc4x.java.api.exceptions.PlcException;
-import org.apache.plc4x.java.api.exceptions.PlcIoException;
-import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
-import org.apache.plc4x.java.api.exceptions.PlcProtocolPayloadTooBigException;
+import org.apache.plc4x.java.api.exceptions.*;
 import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.api.messages.items.ReadRequestItem;
 import org.apache.plc4x.java.api.messages.items.ReadResponseItem;
@@ -268,7 +265,7 @@ public class Plc4XS7Protocol extends PlcMessageToMessageCodec<S7Message, PlcRequ
             return DataTransportSize.BYTE_WORD_DWORD;
         } else if (datatype == Calendar.class) {
             // TODO: Decide what to do here ...
-            return null;
+            throw new PlcNotImplementedException("Calender support in S7 not yet implemented");
         } else if (datatype == Float.class) {
             return DataTransportSize.REAL;
         } else if (datatype == Double.class) {
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeDecoder.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeDecoder.java
index 08b5c91..68b85a5 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeDecoder.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeDecoder.java
@@ -92,7 +92,7 @@ public class S7TypeDecoder {
                 }
                 i += s7Data.length;
             } else {
-                throw new PlcProtocolException("Unsupported datatype " + datatype.getSimpleName());
+                throw new PlcProtocolException("Unsupported data type " + datatype.getSimpleName());
             }
         }
         return (List<T>) result;
diff --git a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeEncoder.java b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeEncoder.java
index 5c50e50..4ff0f4b 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeEncoder.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/netty/util/S7TypeEncoder.java
@@ -18,6 +18,9 @@
  */
 package org.apache.plc4x.java.s7.netty.util;
 
+import org.apache.plc4x.java.api.exceptions.PlcNotImplementedException;
+import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
+
 import java.util.Calendar;
 
 public class S7TypeEncoder {
@@ -26,7 +29,7 @@ public class S7TypeEncoder {
         // Utility class
     }
 
-    public static byte[] encodeData(Object[] values) {
+    public static byte[] encodeData(Object[] values) throws PlcProtocolException {
         final int length = values.length;
         if (length == 0) {
             return new byte[]{};
@@ -43,11 +46,13 @@ public class S7TypeEncoder {
             result = encodeInteger(values, length);
         } else if (valueType == Calendar.class) {
             // TODO: Decide what to do here ...
-            result = null;
+            throw new PlcNotImplementedException("calender not yet implemented in s7");
         } else if (valueType == Float.class) {
             result = encodeFloat(values, length);
         } else if (valueType == String.class) {
             result = encodeString(values, length);
+        } else {
+            throw new PlcProtocolException("Unsupported data type " + valueType);
         }
         return result;
     }