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/03/08 11:28:41 UTC

[incubator-plc4x] branch master updated: implemented serial protocol handler for ADS

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


The following commit(s) were added to refs/heads/master by this push:
     new 75dc652  implemented serial protocol handler for ADS
75dc652 is described below

commit 75dc65212ab66a0fd7feac3f37660202f58808c9
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Thu Mar 8 12:28:37 2018 +0100

    implemented serial protocol handler for ADS
---
 .../plc4x/java/ads/api/serial/AMSSerialFrame.java  |   1 -
 .../plc4x/java/ads/api/serial/types/UserData.java  |   2 +
 .../java/ads/api/serial/types/UserDataLength.java  |   3 +
 .../java/ads/protocol/ADS2SerialProtocol.java      | 133 +++++++++++++++++++++
 .../plc4x/java/ads/protocol/ADS2TcpProtocol.java   |  16 ++-
 5 files changed, 151 insertions(+), 4 deletions(-)

diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/AMSSerialFrame.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/AMSSerialFrame.java
index 3c4b97c..7e548cd 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/AMSSerialFrame.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/AMSSerialFrame.java
@@ -111,7 +111,6 @@ public class AMSSerialFrame implements ByteReadable {
         messageDigest.update(receiverAddress.getBytes());
         messageDigest.update(fragmentNumber.getBytes());
         messageDigest.update(userDataLength.getBytes());
-        messageDigest.update(amsPacketBytes);
         byte[] digest = messageDigest.digest(amsPacketBytes);
         if (digest.length > 2) {
             throw new PlcRuntimeException("Digest length too great " + digest.length);
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/types/UserData.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/types/UserData.java
index 10d2c0c..7b99cf6 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/types/UserData.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/types/UserData.java
@@ -26,6 +26,8 @@ import static java.util.Objects.requireNonNull;
 
 public class UserData extends ByteValue {
 
+    public static final UserData EMPTY = UserData.of();
+
     private UserData(byte... values) {
         super(values);
     }
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/types/UserDataLength.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/types/UserDataLength.java
index b8d0e9a..851cf91 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/types/UserDataLength.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/api/serial/types/UserDataLength.java
@@ -41,4 +41,7 @@ public class UserDataLength extends ByteValue {
         return new UserDataLength(byteBuf);
     }
 
+    public byte getAsByte() {
+        return value[0];
+    }
 }
\ No newline at end of file
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/ADS2SerialProtocol.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/ADS2SerialProtocol.java
new file mode 100644
index 0000000..a79cdb3
--- /dev/null
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/ADS2SerialProtocol.java
@@ -0,0 +1,133 @@
+/*
+ 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.protocol;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToMessageCodec;
+import org.apache.plc4x.java.ads.api.generic.AMSPacket;
+import org.apache.plc4x.java.ads.api.generic.types.Invoke;
+import org.apache.plc4x.java.ads.api.serial.AMSSerialAcknowledgeFrame;
+import org.apache.plc4x.java.ads.api.serial.AMSSerialFrame;
+import org.apache.plc4x.java.ads.api.serial.AMSSerialResetFrame;
+import org.apache.plc4x.java.ads.api.serial.types.*;
+import org.apache.plc4x.java.ads.api.tcp.AMSTCPHeader;
+import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class ADS2SerialProtocol extends MessageToMessageCodec<ByteBuf, AMSPacket> {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ADS2TcpProtocol.class);
+
+    private final ConcurrentMap<Invoke, AMSPacket> requests;
+
+    private final ADS2TcpProtocol ads2TcpProtocol;
+
+    public ADS2SerialProtocol() {
+        this.requests = new ConcurrentHashMap<>();
+        this.ads2TcpProtocol = new ADS2TcpProtocol(true);
+    }
+
+    /**
+     * Resets this protocol and discard all send requests.
+     */
+    public void reset() {
+        requests.clear();
+    }
+
+
+    @Override
+    protected void encode(ChannelHandlerContext channelHandlerContext, AMSPacket amsPacket, List<Object> out) throws Exception {
+        Invoke invokeId = amsPacket.getAmsHeader().getInvokeId();
+        if (invokeId != Invoke.NONE) {
+            requests.put(invokeId, amsPacket);
+        }
+        byte asLong = (byte) (invokeId.getAsLong() % 255);
+        out.add(amsPacket.toAmsSerialFrame(asLong).getByteBuf());
+    }
+
+    @Override
+    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> out) throws Exception {
+        MagicCookie magicCookie = MagicCookie.of(byteBuf);
+        TransmitterAddress transmitterAddress = TransmitterAddress.of(byteBuf);
+        ReceiverAddress receiverAddress = ReceiverAddress.of(byteBuf);
+        FragmentNumber fragmentNumber = FragmentNumber.of(byteBuf);
+        UserDataLength userDataLength = UserDataLength.of(byteBuf);
+        UserData userData;
+        byte userDataLengthAsByte = userDataLength.getAsByte();
+        if (userDataLengthAsByte > 0) {
+            byte[] userDataByteArray = new byte[userDataLengthAsByte];
+            byteBuf.readBytes(userDataByteArray);
+            userData = UserData.of(userDataByteArray);
+        } else {
+            userData = UserData.EMPTY;
+        }
+        CRC crc = CRC.of(byteBuf);
+
+        switch (magicCookie.getAsInt()) {
+            case AMSSerialFrame.ID:
+                // This is a lazy implementation. we just reuse the tcp implementation
+                ByteBuf fakeTcpHeader = AMSTCPHeader.of(0).getByteBuf();
+                ads2TcpProtocol.decode(channelHandlerContext, Unpooled.wrappedBuffer(fakeTcpHeader, userData.getByteBuf()), out);
+                AMSPacket amsPacket = (AMSPacket) out.get(0);
+                AMSSerialFrame amsSerialFrame = amsPacket.toAmsSerialFrame(fragmentNumber.getBytes()[0]);
+                LOGGER.debug("Ams Serial Frame received {}", amsSerialFrame);
+                break;
+            case AMSSerialAcknowledgeFrame.ID:
+                AMSSerialAcknowledgeFrame amsSerialAcknowledgeFrame = AMSSerialAcknowledgeFrame.of(magicCookie, transmitterAddress, receiverAddress, fragmentNumber, userDataLength, crc);
+                LOGGER.debug("Ams Serial ACK Frame received {}", amsSerialAcknowledgeFrame);
+                break;
+            case AMSSerialResetFrame.ID:
+                AMSSerialResetFrame amsSerialResetFrame = AMSSerialResetFrame.of(magicCookie, transmitterAddress, receiverAddress, fragmentNumber, userDataLength, crc);
+                LOGGER.debug("Ams Serial Reset Frame received {}", amsSerialResetFrame);
+                break;
+        }
+
+        MessageDigest messageDigest;
+        try {
+            messageDigest = MessageDigest.getInstance("CRC-16");
+        } catch (NoSuchAlgorithmException e) {
+            throw new PlcRuntimeException(e);
+        }
+        messageDigest.update(magicCookie.getBytes());
+        messageDigest.update(transmitterAddress.getBytes());
+        messageDigest.update(receiverAddress.getBytes());
+        messageDigest.update(fragmentNumber.getBytes());
+        messageDigest.update(userDataLength.getBytes());
+        byte[] digest = messageDigest.digest(userData.getBytes());
+        if (digest.length > 2) {
+            throw new PlcRuntimeException("Digest length too great " + digest.length);
+        }
+        if (!Arrays.equals(digest, crc.getBytes())) {
+            throw new PlcProtocolException("CRC checksum wrong");
+        }
+
+        byteBuf.release();
+    }
+}
diff --git a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/ADS2TcpProtocol.java b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/ADS2TcpProtocol.java
index 1f5ebf3..a38ee7e 100644
--- a/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/ADS2TcpProtocol.java
+++ b/plc4j/protocols/ads/src/main/java/org/apache/plc4x/java/ads/protocol/ADS2TcpProtocol.java
@@ -43,8 +43,15 @@ public class ADS2TcpProtocol extends MessageToMessageCodec<ByteBuf, AMSPacket> {
 
     private final ConcurrentMap<Invoke, AMSPacket> requests;
 
+    private final boolean ignoreBrokenPackages;
+
     public ADS2TcpProtocol() {
+        this(false);
+    }
+
+    public ADS2TcpProtocol(boolean ignoreBrokenPackages) {
         this.requests = new ConcurrentHashMap<>();
+        this.ignoreBrokenPackages = ignoreBrokenPackages;
     }
 
     /**
@@ -66,9 +73,14 @@ public class ADS2TcpProtocol extends MessageToMessageCodec<ByteBuf, AMSPacket> {
     @SuppressWarnings("unchecked")
     @Override
     protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> out) throws Exception {
+        // Tcp decoding
         // Reserved
         byteBuf.skipBytes(AMSTCPHeader.Reserved.NUM_BYTES);
         TcpLength packetLength = TcpLength.of(byteBuf);
+        AMSTCPHeader amstcpHeader = AMSTCPHeader.of(packetLength);
+        LOGGER.debug("AMS TCP Header {}", amstcpHeader);
+
+        // Ams decoding
         AMSNetId targetAmsNetId = AMSNetId.of(byteBuf);
         AMSPort targetAmsPort = AMSPort.of(byteBuf);
         AMSNetId sourceAmsNetId = AMSNetId.of(byteBuf);
@@ -87,8 +99,6 @@ public class ADS2TcpProtocol extends MessageToMessageCodec<ByteBuf, AMSPacket> {
             throw new IllegalStateException("Overflow in datalength: " + dataLength.getAsLong());
         }
         ByteBuf commandBuffer = byteBuf.readBytes((int) dataLength.getAsLong());
-        AMSTCPHeader amstcpHeader = AMSTCPHeader.of(packetLength);
-        LOGGER.debug("AMS TCP Header {}", amstcpHeader);
         AMSHeader amsHeader = AMSHeader.of(targetAmsNetId, targetAmsPort, sourceAmsNetId, sourceAmsPort, commandId, stateId, dataLength, errorCode, invoke);
         final AMSPacket amsPacket;
         switch (commandId) {
@@ -128,7 +138,7 @@ public class ADS2TcpProtocol extends MessageToMessageCodec<ByteBuf, AMSPacket> {
         }
         out.add(amsPacket);
         LOGGER.trace("Set amsPacket {} to out", amsPacket);
-        if (commandBuffer.readableBytes() > 0) {
+        if (!ignoreBrokenPackages && commandBuffer.readableBytes() > 0) {
             commandBuffer.release();
             byteBuf.release();
             throw new IllegalStateException("Unread bytes left: " + commandBuffer.readableBytes());

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