You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by cd...@apache.org on 2020/02/18 15:23:43 UTC
[plc4x] 04/04: - Implemented the automatic splitting of s7 requests
(read and write)
This is an automated email from the ASF dual-hosted git repository.
cdutz pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit f8e019f2af97c65f0286f84e3df37e8176913e2a
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Tue Feb 18 16:23:33 2020 +0100
- Implemented the automatic splitting of s7 requests (read and write)
---
.../apache/plc4x/java/s7/readwrite/S7Driver.java | 9 ++
.../java/s7/readwrite/context/S7DriverContext.java | 135 ++++++++++++++++
.../java/s7/readwrite/optimizer/S7Optimizer.java | 174 +++++++++++++++++++++
.../s7/readwrite/protocol/S7ProtocolLogic.java | 142 +++--------------
.../apache/plc4x/java/spi/Plc4xProtocolBase.java | 11 ++
.../java/spi/configuration/HasConfiguration.java | 2 -
.../connection/SingleProtocolStackConfigurer.java | 15 +-
.../plc4x/java/spi/context/DriverContext.java | 22 +++
.../plc4x/java/spi/optimizer/BaseOptimizer.java | 19 ++-
.../java/spi/optimizer/SingleFieldOptimizer.java | 5 +-
10 files changed, 403 insertions(+), 131 deletions(-)
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java
index 6d569b7..476c4c2 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/S7Driver.java
@@ -21,13 +21,16 @@ package org.apache.plc4x.java.s7.readwrite;
import io.netty.buffer.ByteBuf;
import org.apache.plc4x.java.api.PlcDriver;
import org.apache.plc4x.java.s7.readwrite.configuration.S7Configuration;
+import org.apache.plc4x.java.s7.readwrite.context.S7DriverContext;
import org.apache.plc4x.java.s7.readwrite.io.TPKTPacketIO;
+import org.apache.plc4x.java.s7.readwrite.optimizer.S7Optimizer;
import org.apache.plc4x.java.s7.readwrite.protocol.S7ProtocolLogic;
import org.apache.plc4x.java.s7.readwrite.field.S7PlcFieldHandler;
import org.apache.plc4x.java.spi.configuration.Configuration;
import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
+import org.apache.plc4x.java.spi.optimizer.BaseOptimizer;
import org.osgi.service.component.annotations.Component;
import java.util.function.Consumer;
@@ -69,6 +72,11 @@ public class S7Driver extends GeneratedDriverBase<TPKTPacket> {
}
@Override
+ protected BaseOptimizer getOptimizer() {
+ return new S7Optimizer();
+ }
+
+ @Override
protected S7PlcFieldHandler getFieldHandler() {
return new S7PlcFieldHandler();
}
@@ -77,6 +85,7 @@ public class S7Driver extends GeneratedDriverBase<TPKTPacket> {
protected ProtocolStackConfigurer<TPKTPacket> getStackConfigurer() {
return SingleProtocolStackConfigurer.builder(TPKTPacket.class, TPKTPacketIO.class)
.withProtocol(S7ProtocolLogic.class)
+ .withDriverContext(S7DriverContext.class)
.withPacketSizeEstimator(ByteLengthEstimator.class)
.withCorruptPacketRemover(CorruptPackageCleaner.class)
.build();
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/context/S7DriverContext.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/context/S7DriverContext.java
new file mode 100644
index 0000000..6f04c21
--- /dev/null
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/context/S7DriverContext.java
@@ -0,0 +1,135 @@
+/*
+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.s7.readwrite.context;
+
+import org.apache.plc4x.java.s7.readwrite.configuration.S7Configuration;
+import org.apache.plc4x.java.s7.readwrite.types.COTPTpduSize;
+import org.apache.plc4x.java.s7.readwrite.types.DeviceGroup;
+import org.apache.plc4x.java.s7.readwrite.types.S7ControllerType;
+import org.apache.plc4x.java.s7.readwrite.utils.S7TsapIdEncoder;
+import org.apache.plc4x.java.spi.configuration.HasConfiguration;
+import org.apache.plc4x.java.spi.context.DriverContext;
+
+public class S7DriverContext implements DriverContext, HasConfiguration<S7Configuration> {
+
+ private int callingTsapId;
+ private int calledTsapId;
+ private COTPTpduSize cotpTpduSize;
+ private int pduSize;
+ private int maxAmqCaller;
+ private int maxAmqCallee;
+ private S7ControllerType controllerType;
+
+ @Override
+ public void setConfiguration(S7Configuration configuration) {
+ this.callingTsapId = S7TsapIdEncoder.encodeS7TsapId(DeviceGroup.OTHERS,
+ configuration.localRack, configuration.localSlot);
+ this.calledTsapId = S7TsapIdEncoder.encodeS7TsapId(DeviceGroup.PG_OR_PC,
+ configuration.remoteRack, configuration.remoteSlot);
+
+ this.controllerType = configuration.controllerType == null ? S7ControllerType.ANY : S7ControllerType.valueOf(configuration.controllerType);
+ // The Siemens LOGO device seems to only work with very limited settings,
+ // so we're overriding some of the defaults.
+ if (this.controllerType == S7ControllerType.LOGO && configuration.pduSize == 1024) {
+ configuration.pduSize = 480;
+ }
+
+ // Initialize the parameters with initial version (Will be updated during the login process)
+ this.cotpTpduSize = getNearestMatchingTpduSize((short) configuration.getPduSize());
+ // The PDU size is theoretically not bound by the COTP TPDU size, however having a larger
+ // PDU size would make the code extremely complex. But even if the protocol would allow this
+ // I have never seen this happen in reality. Making is smaller would unnecessarily limit the
+ // size, so we're setting it to the maximum that can be included.
+ this.pduSize = cotpTpduSize.getSizeInBytes() - 16;
+ this.maxAmqCaller = configuration.maxAmqCaller;
+ this.maxAmqCallee = configuration.maxAmqCallee;
+ }
+
+ public int getCallingTsapId() {
+ return callingTsapId;
+ }
+
+ public void setCallingTsapId(int callingTsapId) {
+ this.callingTsapId = callingTsapId;
+ }
+
+ public int getCalledTsapId() {
+ return calledTsapId;
+ }
+
+ public void setCalledTsapId(int calledTsapId) {
+ this.calledTsapId = calledTsapId;
+ }
+
+ public COTPTpduSize getCotpTpduSize() {
+ return cotpTpduSize;
+ }
+
+ public void setCotpTpduSize(COTPTpduSize cotpTpduSize) {
+ this.cotpTpduSize = cotpTpduSize;
+ }
+
+ public int getPduSize() {
+ return pduSize;
+ }
+
+ public void setPduSize(int pduSize) {
+ this.pduSize = pduSize;
+ }
+
+ public int getMaxAmqCaller() {
+ return maxAmqCaller;
+ }
+
+ public void setMaxAmqCaller(int maxAmqCaller) {
+ this.maxAmqCaller = maxAmqCaller;
+ }
+
+ public int getMaxAmqCallee() {
+ return maxAmqCallee;
+ }
+
+ public void setMaxAmqCallee(int maxAmqCallee) {
+ this.maxAmqCallee = maxAmqCallee;
+ }
+
+ public S7ControllerType getControllerType() {
+ return controllerType;
+ }
+
+ public void setControllerType(S7ControllerType controllerType) {
+ this.controllerType = controllerType;
+ }
+
+ /**
+ * Iterate over all values until one is found that the given tpdu size will fit.
+ *
+ * @param tpduSizeParameter requested tpdu size.
+ * @return smallest {@link COTPTpduSize} which will fit a given size of tpdu.
+ */
+ protected COTPTpduSize getNearestMatchingTpduSize(short tpduSizeParameter) {
+ for (COTPTpduSize value : COTPTpduSize.values()) {
+ if (value.getSizeInBytes() >= tpduSizeParameter) {
+ return value;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/optimizer/S7Optimizer.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/optimizer/S7Optimizer.java
new file mode 100644
index 0000000..c8084a8
--- /dev/null
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/optimizer/S7Optimizer.java
@@ -0,0 +1,174 @@
+/*
+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.s7.readwrite.optimizer;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.messages.*;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.s7.readwrite.*;
+import org.apache.plc4x.java.s7.readwrite.context.S7DriverContext;
+import org.apache.plc4x.java.s7.readwrite.field.S7Field;
+import org.apache.plc4x.java.s7.readwrite.types.MemoryArea;
+import org.apache.plc4x.java.s7.readwrite.types.TransportSize;
+import org.apache.plc4x.java.spi.context.DriverContext;
+import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest;
+import org.apache.plc4x.java.spi.messages.DefaultPlcWriteRequest;
+import org.apache.plc4x.java.spi.optimizer.BaseOptimizer;
+
+import java.util.*;
+
+public class S7Optimizer extends BaseOptimizer {
+
+ public static final int EMPTY_READ_REQUEST_SIZE = new S7MessageRequest(0, new S7ParameterReadVarRequest(
+ new S7VarRequestParameterItem[0]), new S7PayloadReadVarRequest()).getLengthInBytes();
+ public static final int EMPTY_READ_RESPONSE_SIZE = new S7MessageResponse(0, new S7ParameterReadVarResponse(
+ (short) 0), new S7PayloadReadVarResponse(new S7VarPayloadDataItem[0]), (short) 0, (short) 0).getLengthInBytes();
+ public static final int EMPTY_WRITE_REQUEST_SIZE = new S7MessageRequest(0, new S7ParameterWriteVarRequest(
+ new S7VarRequestParameterItem[0]), new S7PayloadWriteVarRequest(new S7VarPayloadDataItem[0])).getLengthInBytes();
+ public static final int EMPTY_WRITE_RESPONSE_SIZE = new S7MessageResponse(0, new S7ParameterWriteVarResponse(
+ (short) 0), new S7PayloadWriteVarResponse(new S7VarPayloadStatusItem[0]), (short) 0, (short) 0).getLengthInBytes();
+ public static final int S7_ADDRESS_ANY_SIZE = 2 +
+ new S7AddressAny(TransportSize.INT, 1, 1, MemoryArea.DATA_BLOCKS, 1, (byte) 0).getLengthInBytes();
+
+ @Override
+ protected List<PlcRequest> processReadRequest(PlcReadRequest readRequest, DriverContext driverContext) {
+ S7DriverContext s7DriverContext = (S7DriverContext) driverContext;
+ List<PlcRequest> processedRequests = new LinkedList<>();
+
+ // This calculates the size of the header for the request and response.
+ int curRequestSize = EMPTY_READ_REQUEST_SIZE;
+ // An empty response has the same size as an empty request.
+ int curResponseSize = EMPTY_READ_RESPONSE_SIZE;
+
+ // List of all items in the current request.
+ LinkedHashMap<String, PlcField> curFields = new LinkedHashMap<>();
+
+ for (String fieldName : readRequest.getFieldNames()) {
+ S7Field field = (S7Field) readRequest.getField(fieldName);
+
+ int readRequestItemSize = S7_ADDRESS_ANY_SIZE;
+ int readResponseItemSize = 4 + (field.getNumElements() * field.getDataType().getSizeInBytes());
+ // If it's an odd number of bytes, add one to make it even
+ if (readResponseItemSize % 2 == 1) {
+ readResponseItemSize++;
+ }
+
+ // If adding the item would not exceed the sizes, add it to the current request.
+ if (((curRequestSize + readRequestItemSize) <= s7DriverContext.getPduSize()) &&
+ ((curResponseSize + readResponseItemSize) <= s7DriverContext.getPduSize())) {
+ // Increase the current request sizes.
+ curRequestSize += readRequestItemSize;
+ curResponseSize += readResponseItemSize;
+
+ // Add the item.
+ }
+ // If they would exceed, start a new request.
+ else {
+ // Create a new PlcReadRequest containing the current field item.
+ processedRequests.add(new DefaultPlcReadRequest(
+ ((DefaultPlcReadRequest) readRequest).getReader(), curFields));
+
+ // Reset the size and item lists.
+ curRequestSize = EMPTY_READ_REQUEST_SIZE;
+ curResponseSize = EMPTY_READ_RESPONSE_SIZE;
+ curFields = new LinkedHashMap<>();
+
+ // Splitting of huge fields not yet implemented, throw an exception instead.
+ if(((curRequestSize + readRequestItemSize) > s7DriverContext.getPduSize()) &&
+ ((curResponseSize + readResponseItemSize) > s7DriverContext.getPduSize())) {
+ throw new PlcRuntimeException("Field size exceeds maximum payload for one item.");
+ }
+ }
+ curFields.put(fieldName, field);
+ }
+
+ // Create a new PlcReadRequest from the remaining field items.
+ if(!curFields.isEmpty()) {
+ processedRequests.add(new DefaultPlcReadRequest(
+ ((DefaultPlcReadRequest) readRequest).getReader(), curFields));
+ }
+
+ return processedRequests;
+ }
+
+ @Override
+ protected List<PlcRequest> processWriteRequest(PlcWriteRequest writeRequest, DriverContext driverContext) {
+ S7DriverContext s7DriverContext = (S7DriverContext) driverContext;
+ List<PlcRequest> processedRequests = new LinkedList<>();
+
+ // This calculates the size of the header for the request and response.
+ int curRequestSize = EMPTY_WRITE_REQUEST_SIZE;
+ // An empty response has the same size as an empty request.
+ int curResponseSize = EMPTY_WRITE_RESPONSE_SIZE;
+
+ // List of all items in the current request.
+ LinkedHashMap<String, Pair<PlcField, PlcValue>> curFields = new LinkedHashMap<>();
+
+ for (String fieldName : writeRequest.getFieldNames()) {
+ S7Field field = (S7Field) writeRequest.getField(fieldName);
+ PlcValue value = ((DefaultPlcWriteRequest) writeRequest).getPlcValue(fieldName);
+
+ int writeRequestItemSize = S7_ADDRESS_ANY_SIZE + (field.getNumElements() * field.getDataType().getSizeInBytes());
+ // If it's an odd number of bytes, add one to make it even
+ if (writeRequestItemSize % 2 == 1) {
+ writeRequestItemSize++;
+ }
+ int writeResponseItemSize = 4;
+
+ // If adding the item would not exceed the sizes, add it to the current request.
+ if (((curRequestSize + writeRequestItemSize) <= s7DriverContext.getPduSize()) &&
+ ((curResponseSize + writeResponseItemSize) <= s7DriverContext.getPduSize())) {
+ // Increase the current request sizes.
+ curRequestSize += writeRequestItemSize;
+ curResponseSize += writeResponseItemSize;
+
+ // Add the item.
+ }
+ // If they would exceed, start a new request.
+ else {
+ // Create a new PlcWriteRequest containing the current field item.
+ processedRequests.add(new DefaultPlcWriteRequest(
+ ((DefaultPlcWriteRequest) writeRequest).getWriter(), curFields));
+
+ // Reset the size and item lists.
+ curRequestSize = EMPTY_WRITE_REQUEST_SIZE;
+ curResponseSize = EMPTY_WRITE_RESPONSE_SIZE;
+ curFields = new LinkedHashMap<>();
+
+ // Splitting of huge fields not yet implemented, throw an exception instead.
+ if(((curRequestSize + writeRequestItemSize) > s7DriverContext.getPduSize()) &&
+ ((curResponseSize + writeResponseItemSize) > s7DriverContext.getPduSize())) {
+ throw new PlcRuntimeException("Field size exceeds maximum payload for one item.");
+ }
+ }
+ curFields.put(fieldName, Pair.of(field, value));
+ }
+
+ // Create a new PlcWriteRequest from the remaining field items.
+ if(!curFields.isEmpty()) {
+ processedRequests.add(new DefaultPlcWriteRequest(
+ ((DefaultPlcWriteRequest) writeRequest).getWriter(), curFields));
+ }
+
+ return processedRequests;
+ }
+
+}
diff --git a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java
index d83939d..ff12050 100644
--- a/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java
+++ b/plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java
@@ -23,7 +23,6 @@ import io.netty.buffer.Unpooled;
import io.vavr.control.Either;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
-import org.apache.plc4x.java.api.exceptions.PlcException;
import org.apache.plc4x.java.api.exceptions.PlcProtocolException;
import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
import org.apache.plc4x.java.api.messages.PlcReadRequest;
@@ -72,22 +71,19 @@ import org.apache.plc4x.java.s7.readwrite.S7VarRequestParameterItemAddress;
import org.apache.plc4x.java.s7.readwrite.SzlDataTreeItem;
import org.apache.plc4x.java.s7.readwrite.SzlId;
import org.apache.plc4x.java.s7.readwrite.TPKTPacket;
-import org.apache.plc4x.java.s7.readwrite.configuration.S7Configuration;
+import org.apache.plc4x.java.s7.readwrite.context.S7DriverContext;
import org.apache.plc4x.java.s7.readwrite.io.DataItemIO;
-import org.apache.plc4x.java.s7.readwrite.optimizer.S7MessageProcessor;
import org.apache.plc4x.java.s7.readwrite.types.COTPProtocolClass;
import org.apache.plc4x.java.s7.readwrite.types.COTPTpduSize;
import org.apache.plc4x.java.s7.readwrite.types.DataTransportErrorCode;
import org.apache.plc4x.java.s7.readwrite.types.DataTransportSize;
-import org.apache.plc4x.java.s7.readwrite.types.DeviceGroup;
import org.apache.plc4x.java.s7.readwrite.types.S7ControllerType;
import org.apache.plc4x.java.s7.readwrite.types.SzlModuleTypeClass;
import org.apache.plc4x.java.s7.readwrite.types.SzlSublist;
import org.apache.plc4x.java.s7.readwrite.field.S7Field;
-import org.apache.plc4x.java.s7.readwrite.utils.S7TsapIdEncoder;
import org.apache.plc4x.java.spi.ConversationContext;
-import org.apache.plc4x.java.spi.configuration.HasConfiguration;
import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.apache.plc4x.java.spi.context.DriverContext;
import org.apache.plc4x.java.spi.generation.ParseException;
import org.apache.plc4x.java.spi.generation.ReadBuffer;
import org.apache.plc4x.java.spi.generation.WriteBuffer;
@@ -103,7 +99,6 @@ import org.slf4j.LoggerFactory;
import java.time.Duration;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -118,47 +113,19 @@ import java.util.function.Consumer;
* So we need to limit those.
* Thus, each request goes to a Work Queue and this Queue ensures, that only 3 are open at the same time.
*/
-public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements HasConfiguration<S7Configuration> {
+public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> {
private static final Logger logger = LoggerFactory.getLogger(S7ProtocolLogic.class);
public static final Duration REQUEST_TIMEOUT = Duration.ofMillis(10000);
- private int callingTsapId;
- private int calledTsapId;
- private COTPTpduSize cotpTpduSize;
- private int pduSize;
- private int maxAmqCaller;
- private int maxAmqCallee;
- private S7ControllerType controllerType;
-
+ private S7DriverContext driverContext;
private static final AtomicInteger tpduGenerator = new AtomicInteger(10);
private RequestTransactionManager tm;
- private S7MessageProcessor processor = null;
-
@Override
- public void setConfiguration(S7Configuration configuration) {
- this.callingTsapId = S7TsapIdEncoder.encodeS7TsapId(DeviceGroup.OTHERS,
- configuration.localRack, configuration.localSlot);
- this.calledTsapId = S7TsapIdEncoder.encodeS7TsapId(DeviceGroup.PG_OR_PC,
- configuration.remoteRack, configuration.remoteSlot);
-
- this.controllerType = configuration.controllerType == null ? S7ControllerType.ANY : S7ControllerType.valueOf(configuration.controllerType);
- // The Siemens LOGO device seems to only work with very limited settings,
- // so we're overriding some of the defaults.
- if (this.controllerType == S7ControllerType.LOGO && configuration.pduSize == 1024) {
- configuration.pduSize = 480;
- }
-
- // Initialize the parameters with initial version (Will be updated during the login process)
- this.cotpTpduSize = getNearestMatchingTpduSize((short) configuration.getPduSize());
- // The PDU size is theoretically not bound by the COTP TPDU size, however having a larger
- // PDU size would make the code extremely complex. But even if the protocol would allow this
- // I have never seen this happen in reality. Making is smaller would unnecessarily limit the
- // size, so we're setting it to the maximum that can be included.
- this.pduSize = cotpTpduSize.getSizeInBytes() - 16;
- this.maxAmqCaller = configuration.maxAmqCaller;
- this.maxAmqCallee = configuration.maxAmqCallee;
+ public void setDriverContext(DriverContext driverContext) {
+ super.setDriverContext(driverContext);
+ this.driverContext = (S7DriverContext) driverContext;
// Initialize Transaction Manager.
// Until the number of concurrent requests is successfully negotiated we set it to a
@@ -166,17 +133,14 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements Ha
// No concurrent requests can be sent anyway. It will be updated when receiving the
// S7ParameterSetupCommunication response.
this.tm = new RequestTransactionManager(1);
-
- // REMARK: Perhaps make this configurable.
- // TODO: Comment in to enable ...
- //this.processor = new DefaultS7MessageProcessor(tpduGenerator);
}
@Override
public void onConnect(ConversationContext<TPKTPacket> context) {
logger.debug("Sending COTP Connection Request");
// Open the session on ISO Transport Protocol first.
- TPKTPacket packet = new TPKTPacket(createCOTPConnectionRequest(calledTsapId, callingTsapId, cotpTpduSize));
+ TPKTPacket packet = new TPKTPacket(createCOTPConnectionRequest(
+ driverContext.getCalledTsapId(), driverContext.getCallingTsapId(), driverContext.getCotpTpduSize()));
context.sendRequest(packet)
.expectResponse(TPKTPacket.class, REQUEST_TIMEOUT)
@@ -196,20 +160,20 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements Ha
.handle(setupCommunication -> {
logger.debug("Got S7 Connection Response");
// Save some data from the response.
- maxAmqCaller = setupCommunication.getMaxAmqCaller();
- maxAmqCallee = setupCommunication.getMaxAmqCallee();
- pduSize = setupCommunication.getPduLength();
+ driverContext.setMaxAmqCaller(setupCommunication.getMaxAmqCaller());
+ driverContext.setMaxAmqCallee(setupCommunication.getMaxAmqCallee());
+ driverContext.setPduSize(setupCommunication.getPduLength());
// Update the number of concurrent requests to the negotiated number.
// I have never seen anything else than equal values for caller and
// callee, but if they were different, we're only limiting the outgoing
// requests.
- tm.setNumberOfConcurrentRequests(maxAmqCaller);
+ tm.setNumberOfConcurrentRequests(driverContext.getMaxAmqCallee());
// If the controller type is explicitly set, were finished with the login
// process. If it's set to ANY, we have to query the serial number information
// in order to detect the type of PLC.
- if (controllerType != S7ControllerType.ANY) {
+ if (driverContext.getControllerType() != S7ControllerType.ANY) {
// Send an event that connection setup is complete.
context.fireConnected();
return;
@@ -236,7 +200,6 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements Ha
@Override
public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
- CompletableFuture<PlcReadResponse> future = new CompletableFuture<>();
DefaultPlcReadRequest request = (DefaultPlcReadRequest) readRequest;
List<S7VarRequestParameterItem> requestItems = new ArrayList<>(request.getNumberOfFields());
for (PlcField field : request.getFields()) {
@@ -249,51 +212,8 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements Ha
new S7ParameterReadVarRequest(requestItems.toArray(new S7VarRequestParameterItem[0])),
new S7PayloadReadVarRequest());
- // If no processor is provided the request is output as-is without any modification.
- if (processor == null) {
- // Just send a single response and chain it as Response
- return toPlcReadResponse((InternalPlcReadRequest) readRequest, readInternal(s7MessageRequest));
- }
-
- try {
- // Do the preprocessing and eventually split up into multiple requests.
- final Collection<S7MessageRequest> s7MessageRequests = processor.processRequest(s7MessageRequest, pduSize);
-
- // Only if more than one sub-request is returned, do something special ...
- // otherwise just do the normal sending.
- if (s7MessageRequests.size() == 1) {
- return toPlcReadResponse((InternalPlcReadRequest) readRequest, readInternal(s7MessageRequest));
- }
-
- /////////////////////////////////////////////////////////////////
- // Here we are in the case that we have multiple requests,
- // so we need splitting
- /////////////////////////////////////////////////////////////////
- ParentFuture multiRequestFuture = new ParentFuture(
- result -> {
- try {
- final S7MessageResponse s7MessageResponse =
- processor.processResponse(s7MessageRequest, result);
- final PlcReadResponse plcReadResponse = (PlcReadResponse)
- decodeReadResponse(s7MessageResponse, ((InternalPlcReadRequest) readRequest));
- future.complete(plcReadResponse);
- } catch (PlcException e) {
- logger.error("Something went wrong", e);
- }
- });
- for (S7MessageRequest messageRequest : s7MessageRequests) {
- ChildFuture childFuture = new ChildFuture(messageRequest);
- multiRequestFuture.addChildFuture(childFuture);
-
- readInternal(messageRequest)
- // Forward everything to the remaining future
- .handle((res, ex) -> res != null ? childFuture.complete(res) : childFuture.completeExceptionally(ex));
- }
- return multiRequestFuture;
- } catch (PlcException e) {
- logger.error("Something went wrong", e);
- }
- return null;
+ // Just send a single response and chain it as Response
+ return toPlcReadResponse((InternalPlcReadRequest) readRequest, readInternal(s7MessageRequest));
}
/** Maps the S7ReadResponse of a PlcReadRequest to a PlcReadRespoonse */
@@ -404,7 +324,7 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements Ha
continue;
}
final String articleNumber = new String(readSzlResponseItemItem.getMlfb());
- controllerType = decodeControllerType(articleNumber);
+ driverContext.setControllerType(decodeControllerType(articleNumber));
// Send an event that connection setup is complete.
context.fireConnected();
@@ -426,16 +346,16 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements Ha
for (COTPParameter parameter : cotpPacketConnectionResponse.getParameters()) {
if (parameter instanceof COTPParameterCalledTsap) {
COTPParameterCalledTsap cotpParameterCalledTsap = (COTPParameterCalledTsap) parameter;
- calledTsapId = cotpParameterCalledTsap.getTsapId();
+ driverContext.setCalledTsapId(cotpParameterCalledTsap.getTsapId());
} else if (parameter instanceof COTPParameterCallingTsap) {
COTPParameterCallingTsap cotpParameterCallingTsap = (COTPParameterCallingTsap) parameter;
- if(cotpParameterCallingTsap.getTsapId() != callingTsapId) {
- callingTsapId = cotpParameterCallingTsap.getTsapId();
- logger.warn(String.format("Switching calling TSAP id to '%s'", callingTsapId));
+ if(cotpParameterCallingTsap.getTsapId() != driverContext.getCallingTsapId()) {
+ driverContext.setCallingTsapId(cotpParameterCallingTsap.getTsapId());
+ logger.warn(String.format("Switching calling TSAP id to '%s'", driverContext.getCallingTsapId()));
}
} else if (parameter instanceof COTPParameterTpduSize) {
COTPParameterTpduSize cotpParameterTpduSize = (COTPParameterTpduSize) parameter;
- cotpTpduSize = cotpParameterTpduSize.getTpduSize();
+ driverContext.setCotpTpduSize(cotpParameterTpduSize.getTpduSize());
} else {
logger.warn(String.format("Got unknown parameter type '%s'", parameter.getClass().getName()));
}
@@ -443,7 +363,8 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements Ha
// Send an S7 login message.
S7ParameterSetupCommunication s7ParameterSetupCommunication =
- new S7ParameterSetupCommunication(maxAmqCaller, maxAmqCallee, pduSize);
+ new S7ParameterSetupCommunication(
+ driverContext.getMaxAmqCaller(), driverContext.getMaxAmqCallee(), driverContext.getPduSize());
S7Message s7Message = new S7MessageRequest(0, s7ParameterSetupCommunication,
new S7PayloadSetupCommunication());
COTPPacketData cotpPacketData = new COTPPacketData(null, s7Message, true, (short) 1);
@@ -608,21 +529,6 @@ public class S7ProtocolLogic extends Plc4xProtocolBase<TPKTPacket> implements Ha
s7Field.getMemoryArea(), s7Field.getByteOffset(), s7Field.getBitOffset());
}
- /**
- * Iterate over all values until one is found that the given tpdu size will fit.
- *
- * @param tpduSizeParameter requested tpdu size.
- * @return smallest {@link COTPTpduSize} which will fit a given size of tpdu.
- */
- protected COTPTpduSize getNearestMatchingTpduSize(short tpduSizeParameter) {
- for (COTPTpduSize value : COTPTpduSize.values()) {
- if (value.getSizeInBytes() >= tpduSizeParameter) {
- return value;
- }
- }
- return null;
- }
-
private static class ChildFuture extends CompletableFuture<S7MessageResponse> {
private final S7MessageRequest request;
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xProtocolBase.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xProtocolBase.java
index e2c23fb..d6bca70 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xProtocolBase.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/Plc4xProtocolBase.java
@@ -28,6 +28,7 @@ import org.apache.plc4x.java.api.messages.PlcUnsubscriptionRequest;
import org.apache.plc4x.java.api.messages.PlcUnsubscriptionResponse;
import org.apache.plc4x.java.api.messages.PlcWriteRequest;
import org.apache.plc4x.java.api.messages.PlcWriteResponse;
+import org.apache.plc4x.java.spi.context.DriverContext;
import java.util.concurrent.CompletableFuture;
@@ -35,6 +36,16 @@ public abstract class Plc4xProtocolBase<T> {
protected ConversationContext<T> context;
+ protected DriverContext driverContext;
+
+ public void setDriverContext(DriverContext driverContext) {
+ this.driverContext = driverContext;
+ }
+
+ public DriverContext getDriverContext() {
+ return driverContext;
+ }
+
public void setContext(ConversationContext<T> context) {
this.context = context;
}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/HasConfiguration.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/HasConfiguration.java
index 0f84505..c5d306c 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/HasConfiguration.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/configuration/HasConfiguration.java
@@ -19,8 +19,6 @@
package org.apache.plc4x.java.spi.configuration;
-import org.apache.plc4x.java.spi.configuration.Configuration;
-
/**
* (Marker) Interface which can be used to tell PLC4X that a class (that is instantiated by PLC4X)
* has a Configuration.
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/SingleProtocolStackConfigurer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/SingleProtocolStackConfigurer.java
index 9787220..3f01cb6 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/SingleProtocolStackConfigurer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/SingleProtocolStackConfigurer.java
@@ -28,6 +28,7 @@ import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
import org.apache.plc4x.java.spi.Plc4xNettyWrapper;
import org.apache.plc4x.java.spi.Plc4xProtocolBase;
import org.apache.plc4x.java.spi.configuration.Configuration;
+import org.apache.plc4x.java.spi.context.DriverContext;
import org.apache.plc4x.java.spi.exceptions.InternalPlcRuntimeException;
import org.apache.plc4x.java.spi.generation.Message;
import org.apache.plc4x.java.spi.generation.MessageIO;
@@ -44,6 +45,7 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
private final Class<BASE_PACKET_CLASS> basePacketClass;
private boolean bigEndian = true;
private final Class<? extends Plc4xProtocolBase<BASE_PACKET_CLASS>> protocolClass;
+ private final Class<? extends DriverContext> driverContextClass;
private final MessageIO<BASE_PACKET_CLASS, BASE_PACKET_CLASS> protocolIO;
private final Class<? extends ToIntFunction<ByteBuf>> packetSizeEstimatorClass;
private final Class<? extends Consumer<ByteBuf>> corruptPacketRemoverClass;
@@ -58,6 +60,7 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
boolean bigEndian,
Object[] parserArgs,
Class<? extends Plc4xProtocolBase<BASE_PACKET_CLASS>> protocol,
+ Class<? extends DriverContext> driverContextClass,
MessageIO<BASE_PACKET_CLASS, BASE_PACKET_CLASS> protocolIO,
Class<? extends ToIntFunction<ByteBuf>> packetSizeEstimatorClass,
Class<? extends Consumer<ByteBuf>> corruptPacketRemoverClass) {
@@ -65,6 +68,7 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
this.bigEndian = bigEndian;
this.parserArgs = parserArgs;
this.protocolClass = protocol;
+ this.driverContextClass = driverContextClass;
this.protocolIO = protocolIO;
this.packetSizeEstimatorClass = packetSizeEstimatorClass;
this.corruptPacketRemoverClass = corruptPacketRemoverClass;
@@ -82,6 +86,9 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
Configuration configuration, ChannelPipeline pipeline) {
pipeline.addLast(getMessageCodec(configuration));
Plc4xProtocolBase<BASE_PACKET_CLASS> protocol = configure(configuration, createInstance(protocolClass));
+ if(driverContextClass != null) {
+ protocol.setDriverContext(configure(configuration, createInstance(driverContextClass)));
+ }
Plc4xNettyWrapper<BASE_PACKET_CLASS> context = new Plc4xNettyWrapper<>(pipeline, protocol, basePacketClass);
pipeline.addLast(context);
return protocol;
@@ -108,6 +115,7 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
private final Class<BASE_PACKET_CLASS> basePacketClass;
private final Class<? extends MessageIO<BASE_PACKET_CLASS, BASE_PACKET_CLASS>> messageIoClass;
+ private Class<? extends DriverContext> driverContextClass;
private boolean bigEndian = true;
private Object[] parserArgs;
private Class<? extends Plc4xProtocolBase<BASE_PACKET_CLASS>> protocol;
@@ -119,6 +127,11 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
this.messageIoClass = messageIoClass;
}
+ public SingleProtocolStackBuilder<BASE_PACKET_CLASS> withDriverContext(Class<? extends DriverContext> driverContextClass) {
+ this.driverContextClass = driverContextClass;
+ return this;
+ }
+
public SingleProtocolStackBuilder<BASE_PACKET_CLASS> littleEndian() {
this.bigEndian = false;
return this;
@@ -149,7 +162,7 @@ public class SingleProtocolStackConfigurer<BASE_PACKET_CLASS extends Message> im
try {
final MessageIO messageIo = messageIoClass.getDeclaredConstructor().newInstance();
return new SingleProtocolStackConfigurer<>(
- basePacketClass, bigEndian, parserArgs, protocol, messageIo, packetSizeEstimator, corruptPacketRemover);
+ basePacketClass, bigEndian, parserArgs, protocol, driverContextClass, messageIo, packetSizeEstimator, corruptPacketRemover);
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
throw new PlcRuntimeException("Error initializing MessageIO instance", e);
}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/context/DriverContext.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/context/DriverContext.java
new file mode 100644
index 0000000..7308f13
--- /dev/null
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/context/DriverContext.java
@@ -0,0 +1,22 @@
+/*
+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.spi.context;
+
+public interface DriverContext {
+}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/optimizer/BaseOptimizer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/optimizer/BaseOptimizer.java
index c91886a..9d93d47 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/optimizer/BaseOptimizer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/optimizer/BaseOptimizer.java
@@ -24,6 +24,7 @@ import org.apache.plc4x.java.api.messages.*;
import org.apache.plc4x.java.api.types.PlcResponseCode;
import org.apache.plc4x.java.api.value.PlcValue;
import org.apache.plc4x.java.spi.Plc4xProtocolBase;
+import org.apache.plc4x.java.spi.context.DriverContext;
import org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse;
import org.apache.plc4x.java.spi.messages.DefaultPlcWriteResponse;
import org.apache.plc4x.java.spi.messages.InternalPlcReadRequest;
@@ -36,7 +37,7 @@ import java.util.function.Function;
public abstract class BaseOptimizer {
- protected List<PlcRequest> processReadRequest(PlcReadRequest readRequest) {
+ protected List<PlcRequest> processReadRequest(PlcReadRequest readRequest, DriverContext driverContext) {
return Collections.singletonList(readRequest);
}
@@ -59,7 +60,7 @@ public abstract class BaseOptimizer {
return new DefaultPlcReadResponse((InternalPlcReadRequest) readRequest, fields);
}
- protected List<PlcRequest> processWriteRequest(PlcWriteRequest writeRequest) {
+ protected List<PlcRequest> processWriteRequest(PlcWriteRequest writeRequest, DriverContext driverContext) {
return Collections.singletonList(writeRequest);
}
@@ -80,7 +81,8 @@ public abstract class BaseOptimizer {
return new DefaultPlcWriteResponse((InternalPlcWriteRequest) writeRequest, fields);
}
- protected List<PlcRequest> processSubscriptionRequest(PlcSubscriptionRequest subscriptionRequest) {
+ protected List<PlcRequest> processSubscriptionRequest(PlcSubscriptionRequest subscriptionRequest,
+ DriverContext driverContext) {
return Collections.singletonList(subscriptionRequest);
}
@@ -90,7 +92,8 @@ public abstract class BaseOptimizer {
return null;
}
- protected List<PlcRequest> processUnsubscriptionRequest(PlcRequest unsubscriptionRequest) {
+ protected List<PlcRequest> processUnsubscriptionRequest(PlcRequest unsubscriptionRequest,
+ DriverContext driverContext) {
return Collections.singletonList(unsubscriptionRequest);
}
@@ -101,27 +104,27 @@ public abstract class BaseOptimizer {
}
public CompletableFuture<PlcReadResponse> optimizedRead(PlcReadRequest readRequest, Plc4xProtocolBase reader) {
- List<PlcRequest> subRequests = processReadRequest(readRequest);
+ List<PlcRequest> subRequests = processReadRequest(readRequest, reader.getDriverContext());
return send(readRequest, subRequests, request -> reader.read((PlcReadRequest) request),
response -> processReadResponses(readRequest, response));
}
public CompletableFuture<PlcWriteResponse> optimizedWrite(PlcWriteRequest writeRequest, Plc4xProtocolBase writer) {
- List<PlcRequest> subRequests = processWriteRequest(writeRequest);
+ List<PlcRequest> subRequests = processWriteRequest(writeRequest, writer.getDriverContext());
return send(writeRequest, subRequests, request -> writer.write((PlcWriteRequest) request),
response -> processWriteResponses(writeRequest, response));
}
public CompletableFuture<PlcSubscriptionResponse> optimizedSubscribe(
PlcSubscriptionRequest subscriptionRequest, Plc4xProtocolBase subscriber) {
- List<PlcRequest> subRequests = processSubscriptionRequest(subscriptionRequest);
+ List<PlcRequest> subRequests = processSubscriptionRequest(subscriptionRequest, subscriber.getDriverContext());
return send(subscriptionRequest, subRequests, request -> subscriber.subscribe((PlcSubscriptionRequest) request),
response -> processSubscriptionResponses(subscriptionRequest, response));
}
public CompletableFuture<PlcUnsubscriptionResponse> optmizedUnsubscribe(
PlcUnsubscriptionRequest unsubscriptionRequest, Plc4xProtocolBase subscriber) {
- List<PlcRequest> subRequests = processUnsubscriptionRequest(unsubscriptionRequest);
+ List<PlcRequest> subRequests = processUnsubscriptionRequest(unsubscriptionRequest, subscriber.getDriverContext());
return send(unsubscriptionRequest, subRequests, request -> subscriber.unsubscribe((PlcUnsubscriptionRequest) request),
response -> processUnsubscriptionResponses(unsubscriptionRequest, response));
}
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/optimizer/SingleFieldOptimizer.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/optimizer/SingleFieldOptimizer.java
index 34ee8dc..e917da6 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/optimizer/SingleFieldOptimizer.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/optimizer/SingleFieldOptimizer.java
@@ -24,6 +24,7 @@ import org.apache.plc4x.java.api.messages.PlcRequest;
import org.apache.plc4x.java.api.messages.PlcWriteRequest;
import org.apache.plc4x.java.api.model.PlcField;
import org.apache.plc4x.java.api.value.PlcValue;
+import org.apache.plc4x.java.spi.context.DriverContext;
import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest;
import org.apache.plc4x.java.spi.messages.DefaultPlcWriteRequest;
@@ -35,7 +36,7 @@ import java.util.List;
public class SingleFieldOptimizer extends BaseOptimizer {
@Override
- protected List<PlcRequest> processReadRequest(PlcReadRequest readRequest) {
+ protected List<PlcRequest> processReadRequest(PlcReadRequest readRequest, DriverContext driverContext) {
if(readRequest.getNumberOfFields() == 1) {
return Collections.singletonList(readRequest);
}
@@ -51,7 +52,7 @@ public class SingleFieldOptimizer extends BaseOptimizer {
}
@Override
- protected List<PlcRequest> processWriteRequest(PlcWriteRequest writeRequest) {
+ protected List<PlcRequest> processWriteRequest(PlcWriteRequest writeRequest, DriverContext driverContext) {
if(writeRequest.getNumberOfFields() == 1) {
return Collections.singletonList(writeRequest);
}