You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by jf...@apache.org on 2019/08/21 22:26:19 UTC
[plc4x] 01/02: Implemented a first Version of an Request Optimizer.
This is an automated email from the ASF dual-hosted git repository.
jfeinauer pushed a commit to branch feature/new-api
in repository https://gitbox.apache.org/repos/asf/plc4x.git
commit ac36cf7edda728ce13e8433587f5e5c28b92124b
Author: Julian Feinauer <j....@pragmaticminds.de>
AuthorDate: Wed Aug 21 23:32:00 2019 +0200
Implemented a first Version of an Request Optimizer.
---
.../plc4x/java/base/next/PlcConnectionImpl.java | 3 +-
.../plc4x/java/base/next/commands/ReadRequest.java | 14 ++-
.../java/base/next/optimizer/FieldRequest.java | 9 ++
.../java/base/next/optimizer/FieldResponse.java | 4 +
.../base/next/optimizer/InverseTransformation.java | 14 +++
.../plc4x/java/base/next/optimizer/MergeRule.java | 24 +++++
.../plc4x/java/base/next/optimizer/Optimizer.java | 86 ++++++++++++++++
.../java/base/next/optimizer/OptimizerBatch.java | 79 +++++++++++++++
.../java/base/next/optimizer/RequestBatch.java | 26 +++++
.../plc4x/java/base/next/request/DataType.java | 9 ++
.../plc4x/java/base/next/request/FieldAdress.java | 6 ++
.../plc4x/java/base/next/request/FieldRead.java | 12 +++
.../plc4x/java/base/next/request/FieldRequest.java | 14 +++
.../java/base/next/request/OffsetFieldAdress.java | 47 +++++++++
.../java/base/next/PlcDriverManagerImplTest.java | 2 +-
.../java/base/next/commands/ReadRequestTest.java | 81 +++++++++++++++
.../java/base/next/optimizer/OptimizerTest.java | 109 +++++++++++++++++++++
.../base/next/request/OffsetFieldAdressTest.java | 31 ++++++
18 files changed, 563 insertions(+), 7 deletions(-)
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/PlcConnectionImpl.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/PlcConnectionImpl.java
index 0e26690..15de565 100644
--- a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/PlcConnectionImpl.java
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/PlcConnectionImpl.java
@@ -43,7 +43,8 @@ public class PlcConnectionImpl implements PlcConnection {
// TODO do we parse here or in the protocol??
// Prepare the container here
final int transactionId = transactionCounter.getAndIncrement();
- final ReadRequest request = new ReadRequest(transactionId, fieldQuery);
+ // final ReadRequest request = new ReadRequest(transactionId, fieldQuery);
+ final ReadRequest request = new ReadRequest(transactionId, null);
// Create Request Information Object
final RequestInformation information = new RequestInformation(transactionId);
information.setRequest(request);
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/commands/ReadRequest.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/commands/ReadRequest.java
index 19898bd..fb296cd 100644
--- a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/commands/ReadRequest.java
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/commands/ReadRequest.java
@@ -1,16 +1,20 @@
package org.apache.plc4x.java.base.next.commands;
+import org.apache.plc4x.java.base.next.request.FieldRead;
+
+import java.util.List;
+
public class ReadRequest extends Message {
- private final String query;
+ private final List<FieldRead> fieldReads;
- public ReadRequest(int transactionId, String query) {
+ public ReadRequest(int transactionId, List<FieldRead> fieldReads) {
super(transactionId);
- this.query = query;
+ this.fieldReads = fieldReads;
}
- public String getQuery() {
- return query;
+ public List<FieldRead> getFieldReads() {
+ return fieldReads;
}
@Override public String toString() {
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/FieldRequest.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/FieldRequest.java
new file mode 100644
index 0000000..ec7cf22
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/FieldRequest.java
@@ -0,0 +1,9 @@
+package org.apache.plc4x.java.base.next.optimizer;
+
+public class FieldRequest implements Comparable<FieldRequest> {
+
+ @Override public int compareTo(FieldRequest o) {
+ return 0;
+ }
+
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/FieldResponse.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/FieldResponse.java
new file mode 100644
index 0000000..71b56a2
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/FieldResponse.java
@@ -0,0 +1,4 @@
+package org.apache.plc4x.java.base.next.optimizer;
+
+public class FieldResponse {
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/InverseTransformation.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/InverseTransformation.java
new file mode 100644
index 0000000..055ce86
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/InverseTransformation.java
@@ -0,0 +1,14 @@
+package org.apache.plc4x.java.base.next.optimizer;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * Inverse Transformation Interface which is generated by a @{@link MergeRule}.
+ *
+ * @param <RES> Response Type
+ */
+public interface InverseTransformation<RES extends FieldResponse> {
+
+ Pair<RES, RES> apply(RES response);
+
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/MergeRule.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/MergeRule.java
new file mode 100644
index 0000000..60d326a
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/MergeRule.java
@@ -0,0 +1,24 @@
+package org.apache.plc4x.java.base.next.optimizer;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+/**
+ * This is a Rule which defines how to merge twp instances of a {@link FieldRequest}
+ * and returns the merged read, if possible.
+ * It furthermore returns a {@link InverseTransformation} which has to be applied
+ * to the response.
+ *
+ * @param <REQ> Request Type
+ * @param <RES> Response Type
+ */
+public interface MergeRule<REQ extends FieldRequest, RES extends FieldResponse> {
+
+ /** Short description */
+ String description();
+
+ /** Checks if the rule can be applied to two specific requests */
+ boolean matches(REQ field1, REQ field2);
+
+ /** Applies the transformation */
+ Pair<REQ, InverseTransformation<RES>> apply(REQ field1, REQ field2);
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/Optimizer.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/Optimizer.java
new file mode 100644
index 0000000..9931051
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/Optimizer.java
@@ -0,0 +1,86 @@
+package org.apache.plc4x.java.base.next.optimizer;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Queue;
+import java.util.Set;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Generic Optimizer.
+ *
+ * @param <REQ> Type of Requests
+ * @param <RES> Type of Results
+ */
+public class Optimizer<REQ extends FieldRequest, RES extends FieldResponse> {
+
+ private static final Logger logger = LoggerFactory.getLogger(Optimizer.class);
+
+ private final List<MergeRule<REQ, RES>> rules;
+
+ public Optimizer(List<MergeRule<REQ, RES>> rules) {
+ this.rules = rules;
+ }
+
+ /**
+ * Split the Field Requests into one or more Batch Requests.
+ */
+ public Set<OptimizerBatch<REQ, RES>> optimize(List<REQ> requests) {
+ // Iterate and merge
+ final Queue<InverseTransformation<RES>> transformations
+ = new LinkedBlockingQueue<>();
+
+ // Initial Batch
+ final OptimizerBatch<REQ, RES> initialBatch = new OptimizerBatch<>(requests, transformations);
+
+ // Create the initial set
+ Set<OptimizerBatch<REQ, RES>> batches = new HashSet<>();
+ batches.add(initialBatch);
+
+ // Process all Batches
+ int sizeBefore;
+ do {
+ sizeBefore = batches.size();
+ iterate(batches);
+ } while (sizeBefore != batches.size());
+ return batches;
+ }
+
+ private void iterate(Set<OptimizerBatch<REQ, RES>> batches) {
+ logger.debug("Starting iteration with {} batches", batches.size());
+ // Defensive copy to not mess with iteration
+ final Set<OptimizerBatch<REQ, RES>> initial = new HashSet<>(batches);
+ for (OptimizerBatch<REQ, RES> batch : initial) {
+ final List requestList = batch.getRequests();
+ // Order the requests
+ for (int i = 0; i < requestList.size(); i++) {
+ for (int j = 0; j < requestList.size(); j++) {
+ if (i == j) {
+ continue;
+ }
+ // Create a new Batch
+ for (MergeRule<REQ, RES> rule : rules) {
+ logger.debug("Trying to merge {} and {} with rule '{}'", i, j, rule.description());
+ // Apply the Rule
+ final boolean matches = rule.matches(
+ batch.getRequests().get(i),
+ batch.getRequests().get(j)
+ );
+ if (matches) {
+ logger.debug("Rule {} matches for {} and {}", rule.description(), requestList.get(i), requestList.get(j));
+ batches.add(batch.applyRule(rule, i, j));
+ } else {
+ logger.debug("Rule {} does not match for {} and {}", rule.description(), requestList.get(i), requestList.get(j));
+ }
+ }
+ }
+ }
+ }
+ logger.debug("Finishing iteration with {} batches", batches.size());
+ }
+
+
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/OptimizerBatch.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/OptimizerBatch.java
new file mode 100644
index 0000000..757ddc4
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/OptimizerBatch.java
@@ -0,0 +1,79 @@
+package org.apache.plc4x.java.base.next.optimizer;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/** This is a batch of the optimization process */
+class OptimizerBatch<REQ extends FieldRequest, RES extends FieldResponse> {
+
+ private static final Logger logger = LoggerFactory.getLogger(OptimizerBatch.class);
+
+ private final List<REQ> requests;
+ private final Queue<InverseTransformation<RES>> transformations;
+
+ OptimizerBatch(List<REQ> requests, Queue<InverseTransformation<RES>> transformations) {
+ this.requests = requests;
+ this.transformations = transformations;
+ }
+
+ public List<REQ> getRequests() {
+ return requests;
+ }
+
+ public Queue<InverseTransformation<RES>> getTransformations() {
+ return transformations;
+ }
+
+ /** Applies a Rule to two items of this Batch and returns a new Optimizer Batch */
+ OptimizerBatch<REQ, RES> applyRule(MergeRule<REQ, RES> rule, int index1, int index2) {
+ assert index1 < requests.size();
+ assert index2 < requests.size();
+
+ final ArrayList<REQ> tmp = new ArrayList<>(requests);
+
+ final REQ first = tmp.get(index1);
+ final REQ second = tmp.get(index2);
+
+ tmp.remove(first);
+ tmp.remove(second);
+
+ final Pair<REQ, InverseTransformation<RES>> res
+ = rule.apply(first, second);
+
+ logger.debug("Result of merging {} and {} is {}", first, second, res.getLeft());
+
+ tmp.add(res.getLeft());
+
+ // Create a new Batch
+ final Queue<InverseTransformation<RES>> queue
+ = new LinkedBlockingQueue<>(getTransformations());
+ queue.add(res.getRight());
+
+ // Add Batches
+ return new OptimizerBatch<>(tmp, queue);
+ }
+
+ @Override public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ OptimizerBatch<?, ?> that = (OptimizerBatch<?, ?>) o;
+ return Objects.equals(requests, that.requests);
+ }
+
+ @Override public int hashCode() {
+ return Objects.hash(requests);
+ }
+
+ @Override public String toString() {
+ return "OptimizerBatch{" +
+ "requests=" + requests +
+ '}';
+ }
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/RequestBatch.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/RequestBatch.java
new file mode 100644
index 0000000..96e5946
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/optimizer/RequestBatch.java
@@ -0,0 +1,26 @@
+package org.apache.plc4x.java.base.next.optimizer;
+
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * One Batch of Requests.
+ */
+public class RequestBatch {
+
+ private final List<FieldRequest> requests;
+ private final Queue<InverseTransformation> transformations;
+
+ public RequestBatch(List<FieldRequest> requests, Queue<InverseTransformation> transformations) {
+ this.requests = requests;
+ this.transformations = transformations;
+ }
+
+ public List<FieldRequest> getRequests() {
+ return requests;
+ }
+
+ public Queue<InverseTransformation> getTransformations() {
+ return transformations;
+ }
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/DataType.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/DataType.java
new file mode 100644
index 0000000..3e1b112
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/DataType.java
@@ -0,0 +1,9 @@
+package org.apache.plc4x.java.base.next.request;
+
+public enum DataType {
+
+ BOOLEAN,
+ INT_8,
+ UINT_8,
+ INT_16
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldAdress.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldAdress.java
new file mode 100644
index 0000000..cec0dd5
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldAdress.java
@@ -0,0 +1,6 @@
+package org.apache.plc4x.java.base.next.request;
+
+/** Genereric Adress of a field. Can be subclassed by Drivers */
+public interface FieldAdress extends Comparable<FieldAdress> {
+
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldRead.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldRead.java
new file mode 100644
index 0000000..5d75bbf
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldRead.java
@@ -0,0 +1,12 @@
+package org.apache.plc4x.java.base.next.request;
+
+
+public class FieldRead extends FieldRequest {
+
+ private final DataType dataType;
+
+ public FieldRead(FieldAdress adress, DataType dataType) {
+ super(adress);
+ this.dataType = dataType;
+ }
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldRequest.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldRequest.java
new file mode 100644
index 0000000..c25d809
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/FieldRequest.java
@@ -0,0 +1,14 @@
+package org.apache.plc4x.java.base.next.request;
+
+public abstract class FieldRequest {
+
+ private final FieldAdress adress;
+
+ protected FieldRequest(FieldAdress adress) {
+ this.adress = adress;
+ }
+
+ public FieldAdress getAdress() {
+ return adress;
+ }
+}
diff --git a/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/OffsetFieldAdress.java b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/OffsetFieldAdress.java
new file mode 100644
index 0000000..9c404f4
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/main/java/org/apache/plc4x/java/base/next/request/OffsetFieldAdress.java
@@ -0,0 +1,47 @@
+package org.apache.plc4x.java.base.next.request;
+
+import java.util.Objects;
+
+/**
+ * The simplest Implementation of an Untyped {@link FieldAdress} based on an integer offset.
+ */
+public class OffsetFieldAdress implements FieldAdress {
+
+ private final int offset;
+ private final int length;
+
+ public OffsetFieldAdress(int offset, int length) {
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ @Override public int compareTo(FieldAdress o) {
+ assert o instanceof OffsetFieldAdress;
+ return Integer.compare(offset, ((OffsetFieldAdress) o).offset);
+ }
+
+ @Override public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ OffsetFieldAdress that = (OffsetFieldAdress) o;
+ return offset == that.offset;
+ }
+
+ @Override public int hashCode() {
+ return Objects.hash(offset);
+ }
+
+ @Override public String toString() {
+ return "OffsetFieldAdress{" +
+ "offset=" + offset +
+ '}';
+ }
+}
diff --git a/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/PlcDriverManagerImplTest.java b/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/PlcDriverManagerImplTest.java
index 6ba5b84..c463025 100644
--- a/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/PlcDriverManagerImplTest.java
+++ b/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/PlcDriverManagerImplTest.java
@@ -57,7 +57,7 @@ public class PlcDriverManagerImplTest {
}
@Override public void encode(ReadRequest command, ByteBuf out) {
- out.writeBytes(command.getQuery().getBytes());
+ out.writeBytes("Hallo".getBytes());
}
@Override public Response decode(ByteBuf buf) throws UnableToParseException {
diff --git a/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/commands/ReadRequestTest.java b/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/commands/ReadRequestTest.java
new file mode 100644
index 0000000..ec6f1ce
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/commands/ReadRequestTest.java
@@ -0,0 +1,81 @@
+package org.apache.plc4x.java.base.next.commands;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.plc4x.java.base.next.request.DataType;
+import org.apache.plc4x.java.base.next.request.FieldAdress;
+import org.apache.plc4x.java.base.next.request.FieldRead;
+import org.apache.plc4x.java.base.next.request.OffsetFieldAdress;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.function.Function;
+
+public class ReadRequestTest {
+
+ private Queue<Function<FieldRead, FieldRead>> queue = new LinkedBlockingQueue<>();
+
+ @Test
+ public void optimizerTests() {
+ List<Function<Pair<FieldRead, FieldRead>, FieldRead>> operations = new ArrayList<>();
+ List<Function<FieldRead, Pair<FieldRead, FieldRead>>> inverse = new ArrayList<>();
+
+ operations.add(new Function<Pair<FieldRead, FieldRead>, FieldRead>() {
+
+ @Override public FieldRead apply(Pair<FieldRead, FieldRead> fieldReadFieldReadPair) {
+ final FieldAdress a1 = fieldReadFieldReadPair.getLeft().getAdress();
+ final FieldAdress a2 = fieldReadFieldReadPair.getRight().getAdress();
+ assert a1 instanceof OffsetFieldAdress;
+ assert a2 instanceof OffsetFieldAdress;
+ final int start = ((OffsetFieldAdress) a1).getOffset();
+
+ // Register the inverse function, also
+ final Function<FieldRead, Pair<FieldRead, FieldRead>> inverse = new Function<FieldRead, Pair<FieldRead, FieldRead>>() {
+
+ @Override public Pair<FieldRead, FieldRead> apply(FieldRead fieldRead) {
+ // Result
+ // 0, ((OffsetFieldAdress) a1).getLength();
+ // ((OffsetFieldAdress) a2).getOffset(), ((OffsetFieldAdress) a2).getLength()
+ return null;
+ }
+
+ };
+
+ return null;
+ }
+
+ });
+
+ // Read request
+ final ReadRequest readRequest = new ReadRequest(0, Arrays.asList(
+ new FieldRead(new OffsetFieldAdress(200, 10), DataType.BOOLEAN),
+ new FieldRead(new OffsetFieldAdress(100, 10), DataType.BOOLEAN)
+ ));
+
+ // First, Unroll
+ final List<FieldRead> fields = readRequest.getFieldReads();
+ fields.sort(null);
+
+ // Now try to merge. If we try all combinations this is memory O(n!)
+ // So we have to do it a bit different
+ // First, merge 2
+ // Then only keep more efficient ones and continue
+ for (int i = 0; i < fields.size(); i++) {
+ for (int j = i + 1; j < fields.size(); j++) {
+ merge(fields, 0, 1);
+ }
+ }
+
+ }
+
+ /** Merge reads at o1 and o2 in the index */
+ static List<FieldRead> merge(List<FieldRead> fields, int o1, int o2) {
+ return Collections.emptyList();
+ }
+
+
+}
\ No newline at end of file
diff --git a/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/optimizer/OptimizerTest.java b/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/optimizer/OptimizerTest.java
new file mode 100644
index 0000000..b025b2b
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/optimizer/OptimizerTest.java
@@ -0,0 +1,109 @@
+package org.apache.plc4x.java.base.next.optimizer;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.Test;
+
+import java.sql.SQLOutput;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+public class OptimizerTest {
+
+ /**
+ * 10 / 10
+ * 20 / 5
+ * 15 / 5
+ * --
+ * 10 / 10
+ * 20 / 5
+ * --
+ * 10 / 10
+ * 15 / 10
+ */
+ @Test
+ public void optimizerTest() {
+ final List<MergeRule<TestRequest, FieldResponse>> rules = Arrays.asList(new TestMergeRule());
+ final List<TestRequest> requests = Arrays.asList(
+ new TestRequest(10, 10),
+ new TestRequest(20, 5),
+ new TestRequest(15, 5)
+ );
+ final Optimizer<TestRequest, FieldResponse> optimizer
+ = new Optimizer<>(rules);
+ final Set<OptimizerBatch<TestRequest, FieldResponse>> batches = optimizer.optimize(requests);
+
+ System.out.println(batches.size() + " Results have been found:");
+ for (OptimizerBatch<TestRequest, FieldResponse> batch : batches) {
+ System.out.println(" - " + batch.getRequests());
+ }
+ }
+
+
+ public static class TestRequest extends FieldRequest {
+
+ private final int offset;
+ private final int length;
+
+ public TestRequest(int offset, int length) {
+ this.offset = offset;
+ this.length = length;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ @Override public int compareTo(FieldRequest o) {
+ assert o instanceof TestRequest;
+ // return Integer.compare(offset, ((TestRequest) o).offset);
+ return 0;
+ }
+
+ @Override public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ TestRequest that = (TestRequest) o;
+ return offset == that.offset &&
+ length == that.length;
+ }
+
+ @Override public int hashCode() {
+ return Objects.hash(offset, length);
+ }
+
+ @Override public String toString() {
+ return "TestRequest{" +
+ "offset=" + offset +
+ ", length=" + length +
+ '}';
+ }
+ }
+
+ private static class TestMergeRule implements MergeRule<TestRequest, FieldResponse> {
+
+ @Override public String description() {
+ return "simple-merge";
+ }
+
+ @Override public boolean matches(TestRequest field1, TestRequest field2) {
+ return field1.getOffset() <= field2.getOffset();
+ }
+
+ @Override public Pair<TestRequest, InverseTransformation<FieldResponse>> apply(TestRequest field1, TestRequest field2) {
+ final int offset = field1.offset;
+ final int length = Math.max(field2.offset + field2.length, field1.offset + field1.length) - offset;
+ final TestRequest request = new TestRequest(offset, length);
+ final InverseTransformation transformation = response -> {
+ // TODO
+ return Pair.of(null, null);
+ };
+ return Pair.of(request, transformation);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/request/OffsetFieldAdressTest.java b/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/request/OffsetFieldAdressTest.java
new file mode 100644
index 0000000..49b9222
--- /dev/null
+++ b/plc4j/protocols/driver-bases/base/src/test/java/org/apache/plc4x/java/base/next/request/OffsetFieldAdressTest.java
@@ -0,0 +1,31 @@
+package org.apache.plc4x.java.base.next.request;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class OffsetFieldAdressTest {
+
+ @Test
+ public void compareTo() {
+ final OffsetFieldAdress o1 = new OffsetFieldAdress(10, 2);
+ final OffsetFieldAdress o2 = new OffsetFieldAdress(20, 2);
+
+ assertTrue(o1.compareTo(o2) < 0);
+ }
+
+ @Test
+ public void sort() {
+ final OffsetFieldAdress o1 = new OffsetFieldAdress(10, 2);
+ final OffsetFieldAdress o2 = new OffsetFieldAdress(20, 2);
+
+ final List<OffsetFieldAdress> list = Arrays.asList(o2, o1);
+ list.sort(null);
+
+ assertEquals(10, list.get(0).getOffset());
+ }
+}
\ No newline at end of file