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