This is an automated email from the ASF dual-hosted git repository.
cdutz 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 c97330a PLC4X-29 - [S7] Implement PDU Fragmentation
c97330a is described below
commit c97330a4dfc8df4a2355225fb3fce46a292ef24b
Author: Christofer Dutz <[email protected]>
AuthorDate: Wed Feb 7 16:37:57 2018 +0100
PLC4X-29 - [S7] Implement PDU Fragmentation
- Added the ability to add options to the connection string (similar to
options in jdbc)
- Added some functionality to determine the iso tp TPDUsizes based on the
provided desired pdu size.
---
examples/kafka-bridge/pom.xml | 1 -
plc4j/protocols/s7/pom.xml | 5 ++
.../java/isotp/netty/model/types/TpduSize.java | 37 ++++++++---
.../java/org/apache/plc4x/java/s7/S7PlcDriver.java | 5 +-
.../plc4x/java/s7/connection/S7PlcConnection.java | 73 +++++++++++++++++++---
.../isotp/netty/model/types/IsotpTypeTests.java | 49 +++++++++++++++
.../java/s7/connection/S7PlcConnectionTests.java | 6 +-
pom.xml | 5 ++
8 files changed, 159 insertions(+), 22 deletions(-)
diff --git a/examples/kafka-bridge/pom.xml b/examples/kafka-bridge/pom.xml
index f28a7ec..b922d8b 100644
--- a/examples/kafka-bridge/pom.xml
+++ b/examples/kafka-bridge/pom.xml
@@ -88,7 +88,6 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
- <version>3.7</version>
</dependency>
<dependency>
diff --git a/plc4j/protocols/s7/pom.xml b/plc4j/protocols/s7/pom.xml
index 1ce4dc4..f03de0f 100644
--- a/plc4j/protocols/s7/pom.xml
+++ b/plc4j/protocols/s7/pom.xml
@@ -59,6 +59,11 @@
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
diff --git
a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/isotp/netty/model/types/TpduSize.java
b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/isotp/netty/model/types/TpduSize.java
index 3a8837b..2e662bf 100644
---
a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/isotp/netty/model/types/TpduSize.java
+++
b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/isotp/netty/model/types/TpduSize.java
@@ -22,26 +22,47 @@ import java.util.HashMap;
import java.util.Map;
public enum TpduSize {
- SIZE_8192((byte) 0x0d),
- SIZE_4096((byte) 0x0c),
- SIZE_2048((byte) 0x0b),
- SIZE_1024((byte) 0x0a),
- SIZE_512((byte) 0x09),
- SIZE_256((byte) 0x08),
- SIZE_128((byte) 0x07);
+ SIZE_128((byte) 0x07, 128),
+ SIZE_256((byte) 0x08, 256),
+ SIZE_512((byte) 0x09, 512),
+ SIZE_1024((byte) 0x0a, 1024),
+ SIZE_2048((byte) 0x0b, 2048),
+ SIZE_4096((byte) 0x0c, 4096),
+ SIZE_8192((byte) 0x0d, 8192);
private static Map<Byte, TpduSize> map = null;
private final byte code;
+ private final int value;
- TpduSize(byte code) {
+ TpduSize(byte code, int value) {
this.code = code;
+ this.value = value;
}
public byte getCode() {
return code;
}
+ public int getValue() {
+ return value;
+ }
+
+ public static TpduSize valueForGivenSize(int pduSize) {
+ assert pduSize > 0;
+ for (TpduSize tpduSize : values()) {
+ if(tpduSize.getValue() <= pduSize) {
+ return tpduSize;
+ }
+ }
+ // If the requested pdu size is greater than 8MB,
+ // Simply use that as the given size is simple a
+ // requested size, if the remote responds with a
+ // lower value the application has to live with
+ // this anyway.
+ return SIZE_8192;
+ }
+
public static TpduSize valueOf(byte code) {
if (map == null) {
map = new HashMap<>();
diff --git
a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/S7PlcDriver.java
b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/S7PlcDriver.java
index 9156f18..3e279dd 100644
--- a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/S7PlcDriver.java
+++ b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/S7PlcDriver.java
@@ -36,7 +36,7 @@ import java.util.regex.Pattern;
*/
public class S7PlcDriver implements PlcDriver {
- private static final Pattern S7_URI_PATTERN =
Pattern.compile("^s7://(?<host>.*)/(?<rack>\\d{1,4})/(?<slot>\\d{1,4})(\\?.*)?");
+ private static final Pattern S7_URI_PATTERN =
Pattern.compile("^s7://(?<host>.*)/(?<rack>\\d{1,4})/(?<slot>\\d{1,4})(?<params>\\?.*)?");
@Override
public String getProtocolCode() {
@@ -58,7 +58,8 @@ public class S7PlcDriver implements PlcDriver {
String host = matcher.group("host");
int rack = Integer.parseInt(matcher.group("rack"));
int slot = Integer.parseInt(matcher.group("slot"));
- return new S7PlcConnection(host, rack, slot);
+ String params = matcher.group("params").substring(1);
+ return new S7PlcConnection(host, rack, slot, params);
}
@Override
diff --git
a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/connection/S7PlcConnection.java
b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/connection/S7PlcConnection.java
index 477993f..c0f4793 100644
---
a/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/connection/S7PlcConnection.java
+++
b/plc4j/protocols/s7/src/main/java/org/apache/plc4x/java/s7/connection/S7PlcConnection.java
@@ -22,6 +22,7 @@ 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.commons.lang3.StringUtils;
import org.apache.plc4x.java.api.connection.AbstractPlcConnection;
import org.apache.plc4x.java.api.connection.PlcReader;
import org.apache.plc4x.java.api.connection.PlcWriter;
@@ -67,16 +68,53 @@ public class S7PlcConnection extends AbstractPlcConnection
implements PlcReader,
private final String hostName;
private final int rack;
private final int slot;
- private final int pduSize;
+
+ private final int paramPduSize;
+ private final short paramMaxAmqCaller;
+ private final short paramMaxAmqCallee;
private EventLoopGroup workerGroup;
private Channel channel;
- public S7PlcConnection(String hostName, int rack, int slot) {
+ public S7PlcConnection(String hostName, int rack, int slot, String params)
{
this.hostName = hostName;
this.rack = rack;
this.slot = slot;
- this.pduSize = 1024;
+
+ int paramPduSize = 1024;
+ short paramMaxAmqCaller = 8;
+ short paramMaxAmqCallee = 8;
+
+ if(!StringUtils.isEmpty(params)) {
+ for (String param : params.split("&")) {
+ String[] paramElements = param.split("=");
+ String paramName = paramElements[0];
+ if(paramElements.length == 2) {
+ String paramValue = paramElements[1];
+ switch (paramName) {
+ case "pdu-size":
+ paramPduSize = Integer.parseInt(paramValue);
+ break;
+ case "max-amq-caller":
+ paramMaxAmqCaller = Short.parseShort(paramValue);
+ break;
+ case "max-amq-callee":
+ paramMaxAmqCallee = Short.parseShort(paramValue);
+ break;
+ default:
+ logger.debug("Unknown parameter {}={}", paramName,
paramValue);
+ }
+ } else {
+ logger.debug("Unknown no-value parameter {}", paramName);
+ }
+ }
+ }
+ this.paramPduSize = paramPduSize;
+ this.paramMaxAmqCaller = paramMaxAmqCaller;
+ this.paramMaxAmqCallee = paramMaxAmqCallee;
+
+ logger.info("Configured S7cConnection with: host-name {}, rack {},
slot {}, pdu-size {}, max-amq-caller {}, " +
+ "max-amq-callee {}", hostName, rack, slot, paramPduSize,
paramMaxAmqCaller, paramMaxAmqCallee);
}
public String getHostName() {
@@ -91,8 +129,16 @@ public class S7PlcConnection extends AbstractPlcConnection
implements PlcReader,
return slot;
}
- public int getPduSize() {
- return pduSize;
+ public int getParamPduSize() {
+ return paramPduSize;
+ }
+
+ public int getParamMaxAmqCaller() {
+ return paramMaxAmqCaller;
+ }
+
+ public int getParamMaxAmqCallee() {
+ return paramMaxAmqCallee;
}
@Override
@@ -107,6 +153,15 @@ public class S7PlcConnection extends AbstractPlcConnection
implements PlcReader,
InetAddress serverInetAddress = InetAddress.getByName(hostName);
+ // IsoTP uses pre defined sizes. Find the smallest box,
+ // that would be able to contain the requested pdu size.
+ TpduSize isoTpTpduSize = TpduSize.valueForGivenSize(paramPduSize);
+
+ // If the pdu size was very big, it might have exceeded the
+ // maximum of 8MB defined in the IsoTP protocol, so we have
+ // to generally downgrade the requested size.
+ int pduSize = (isoTpTpduSize.getValue() < paramPduSize) ?
isoTpTpduSize.getValue() : paramPduSize;
+
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
@@ -114,7 +169,7 @@ public class S7PlcConnection extends AbstractPlcConnection
implements PlcReader,
bootstrap.option(ChannelOption.TCP_NODELAY, true);
bootstrap.handler(new ChannelInitializer() {
@Override
- protected void initChannel(Channel channel) throws Exception {
+ protected void initChannel(Channel channel) {
// Build the protocol stack for communicating with the s7
protocol.
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new ChannelInboundHandlerAdapter() {
@@ -129,8 +184,8 @@ public class S7PlcConnection extends AbstractPlcConnection
implements PlcReader,
}
});
pipeline.addLast(new IsoOnTcpProtocol());
- pipeline.addLast(new IsoTPProtocol((byte) rack, (byte)
slot, TpduSize.SIZE_1024));
- pipeline.addLast(new S7Protocol((short) 8, (short) 8,
(short) 1024));
+ pipeline.addLast(new IsoTPProtocol((byte) rack, (byte)
slot, isoTpTpduSize));
+ pipeline.addLast(new S7Protocol(paramMaxAmqCaller,
paramMaxAmqCallee, (short) pduSize));
pipeline.addLast(new Plc4XS7Protocol());
}
});
@@ -156,7 +211,7 @@ public class S7PlcConnection extends AbstractPlcConnection
implements PlcReader,
}
@Override
- public void close() throws Exception {
+ public void close() {
if((channel != null) && channel.isOpen()) {
// Send the PLC a message that the connection is being closed.
DisconnectRequestTpdu disconnectRequest = new
DisconnectRequestTpdu(
diff --git
a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/isotp/netty/model/types/IsotpTypeTests.java
b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/isotp/netty/model/types/IsotpTypeTests.java
index 47acf5b..f85ee4a 100644
---
a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/isotp/netty/model/types/IsotpTypeTests.java
+++
b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/isotp/netty/model/types/IsotpTypeTests.java
@@ -152,6 +152,7 @@ class IsotpTypeTests {
assertTrue(TpduSize.valueOf((byte)0x07) == TpduSize.SIZE_128, "0x07
incorrectly mapped");
assertTrue(tpduSize.getCode() == (byte)0x07, "code is not 0x07");
+ assertEquals(tpduSize.getValue(), 128, "the value is not 128");
}
@Test
@@ -162,5 +163,53 @@ class IsotpTypeTests {
assertNull(tpduSize, "expected tpdu size to be null");
}
+ /**
+ * If we are requesting exactly the size of one of the iso tp
+ * pdu sizes, then exactly that box should be returned.
+ */
+ @Test
+ @Tag("fast")
+ void tpduValueForGivenExactFit() {
+ TpduSize tpduSize = TpduSize.valueForGivenSize(256);
+
+ assertEquals(tpduSize, TpduSize.SIZE_256, "expected tpdu size of 256");
+ assertEquals(tpduSize.getValue(), 128, "the value is not 128");
+ }
+
+ /**
+ * In this case we have a given value that is in-between the boundaries of
+ * a pdu box, the method should return the next larger box.
+ */
+ @Test
+ @Tag("fast")
+ void tpduValueForGivenIntermediateSize() {
+ TpduSize tpduSize = TpduSize.valueForGivenSize(123);
+
+ assertEquals(tpduSize, TpduSize.SIZE_256, "expected tpdu size of 256");
+ }
+
+ /**
+ * This test should cause an exception as the tpdu size has to be greater
+ * than 0 in any case.
+ */
+ @Test
+ @Tag("fast")
+ void tpduValueForGivenTooSmallSize() {
+ TpduSize tpduSize = TpduSize.valueForGivenSize(-1);
+
+ assertEquals(tpduSize, TpduSize.SIZE_256, "expected tpdu size of 256");
+ }
+
+ /**
+ * In this test the tpdu size is greater than the maximum defined by the
iso tp
+ * protocol spec, so it is automatically downgraded to the maximum valid
value.
+ */
+ @Test
+ @Tag("fast")
+ void tpduValueForGivenTooGreatSize() {
+ TpduSize tpduSize = TpduSize.valueForGivenSize(10000);
+
+ assertEquals(tpduSize, TpduSize.SIZE_8192, "expected tpdu size of
8192");
+ }
}
\ No newline at end of file
diff --git
a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionTests.java
b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionTests.java
index 64d126f..3d8c100 100644
---
a/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionTests.java
+++
b/plc4j/protocols/s7/src/test/java/org/apache/plc4x/java/s7/connection/S7PlcConnectionTests.java
@@ -36,7 +36,7 @@ class S7PlcConnectionTests {
@BeforeEach
void setUp() {
- s7PlcConnection = new S7PlcConnection("localhost", 1, 2);
+ s7PlcConnection = new S7PlcConnection("localhost", 1, 2, "");
}
@AfterEach
@@ -49,7 +49,9 @@ class S7PlcConnectionTests {
assertTrue(s7PlcConnection.getHostName().equalsIgnoreCase("localhost"),
"Hostname is incorrect");
assertTrue(s7PlcConnection.getRack() == 1, "Rack is incorrect");
assertTrue(s7PlcConnection.getSlot() == 2, "Slot is incorrect");
- assertTrue(s7PlcConnection.getPduSize() == 1024, "Pdu size is
incorrect"); // Why is this hard coded?
+ assertTrue(s7PlcConnection.getParamPduSize() == 1024, "Pdu size is
incorrect");
+ assertTrue(s7PlcConnection.getParamMaxAmqCaller() == 8, "Max AMQ
Caller size is incorrect");
+ assertTrue(s7PlcConnection.getParamMaxAmqCallee() == 8, "Max AMQ
Callee size is incorrect");
}
@Test
diff --git a/pom.xml b/pom.xml
index 71c035f..936d3c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -176,6 +176,11 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.7</version>
+ </dependency>
</dependencies>
</dependencyManagement>
--
To stop receiving notification emails like this one, please contact
[email protected].