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.