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/10/30 13:42:01 UTC

[plc4x] branch feature/plc4go updated: - Ported the SingleFieldOptimizer to go (SingleItemRequestItemInterceptor)

This is an automated email from the ASF dual-hosted git repository.

cdutz pushed a commit to branch feature/plc4go
in repository https://gitbox.apache.org/repos/asf/plc4x.git


The following commit(s) were added to refs/heads/feature/plc4go by this push:
     new a0b7c0d  - Ported the SingleFieldOptimizer to go (SingleItemRequestItemInterceptor)
a0b7c0d is described below

commit a0b7c0dc131b808fdda03c296d3415966ee18060
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Fri Oct 30 14:41:53 2020 +0100

    - Ported the SingleFieldOptimizer to go (SingleItemRequestItemInterceptor)
---
 .../java/spi/optimizer/SingleFieldOptimizer.java   |   2 +-
 .../main/resources/protocols/modbus/modbus.mspec   |   2 +-
 sandbox/plc4go/cmd/main/drivers/modbus_test.go     |  47 +++---
 .../internal/plc4go/modbus/ModbusConnection.go     |  55 ++++---
 .../plc4go/internal/plc4go/modbus/ModbusDriver.go  | 110 +++++++------
 .../plc4go/internal/plc4go/modbus/ModbusReader.go  |   2 +
 .../plc4go/internal/plc4go/modbus/ModbusWriter.go  |   8 +-
 .../modbus/readwrite/model/ModbusPDUError.go       | 179 ++++++++++-----------
 .../internal/plc4go/model/DefaultPlcReadRequest.go |  88 ++++++++--
 .../plc4go/model/DefaultPlcWriteRequest.go         |   4 +-
 .../RequestInterceptor.go}                         |  18 ++-
 .../interceptors/SingleItemRequestInterceptor.go   |  85 ++++++++++
 .../plc4go/pkg/plc4go/model/plc_write_response.go  |   2 +-
 13 files changed, 375 insertions(+), 227 deletions(-)

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 a1fc1fa..5038479 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
@@ -62,7 +62,7 @@ public class SingleFieldOptimizer extends BaseOptimizer {
         List<PlcRequest> subRequests = new ArrayList<>(writeRequest.getNumberOfFields());
         for (String fieldName : writeRequest.getFieldNames()) {
             PlcField field = writeRequest.getField(fieldName);
-            PlcValue value = ((DefaultPlcWriteRequest) writeRequest).getPlcValue(fieldName);
+            PlcValue value = writeRequest.getPlcValue(fieldName);
             PlcWriteRequest subRequest = new DefaultPlcWriteRequest(
                 ((DefaultPlcWriteRequest) writeRequest).getWriter(),
                 new LinkedHashMap<>(Collections.singletonMap(fieldName, new FieldValueItem(field, value))));
diff --git a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
index 412bd2b..6b28d84 100644
--- a/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
+++ b/protocols/modbus/src/main/resources/protocols/modbus/modbus.mspec
@@ -60,7 +60,7 @@
     [discriminator uint 7      'functionFlag']
     [typeSwitch 'errorFlag','functionFlag','response'
         ['true'                     ModbusPDUError
-            [simple     uint 8      'exceptionCode']
+            [enum ModbusErrorCode  'exceptionCode']
         ]
 
         // Bit Access
diff --git a/sandbox/plc4go/cmd/main/drivers/modbus_test.go b/sandbox/plc4go/cmd/main/drivers/modbus_test.go
index 905b4b6..4eae9d0 100644
--- a/sandbox/plc4go/cmd/main/drivers/modbus_test.go
+++ b/sandbox/plc4go/cmd/main/drivers/modbus_test.go
@@ -19,18 +19,18 @@
 package drivers
 
 import (
-    "encoding/hex"
-    "encoding/xml"
-    "fmt"
-    "net"
-    "os"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/transports/tcp"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
-    "strings"
-    "testing"
+	"encoding/hex"
+	"encoding/xml"
+	"fmt"
+	"net"
+	"os"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/transports/tcp"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
+	"strings"
+	"testing"
 )
 
 func TestModbus(t *testing.T) {
@@ -55,14 +55,14 @@ func test(t *testing.T, rawMessage string, response bool) {
 		t.Errorf("Error parsing: %s", err)
 	}
 	if adu != nil {
-	    serialized, err := xml.Marshal(adu)
-	    if err != nil {
-	        fmt.Println("Hurz!" + err.Error())
-	        return
-        }
-        fmt.Println(string(serialized))
-	    var deserializedAdu *model.ModbusTcpADU
-	    xml.Unmarshal(serialized, &deserializedAdu)
+		serialized, err := xml.Marshal(adu)
+		if err != nil {
+			fmt.Println("Hurz!" + err.Error())
+			return
+		}
+		fmt.Println(string(serialized))
+		var deserializedAdu *model.ModbusTcpADU
+		xml.Unmarshal(serialized, &deserializedAdu)
 
 		wb := utils.WriteBufferNew()
 		val := model.CastIModbusTcpADU(deserializedAdu)
@@ -151,7 +151,8 @@ func TestPlc4goDriver(t *testing.T) {
 
 	// Prepare a read-request
 	rrb := connection.ReadRequestBuilder()
-	rrb.AddItem("field", "holding-register:0:REAL")
+	rrb.AddItem("field1", "holding-register:1:REAL")
+	rrb.AddItem("field2", "holding-register:3:REAL")
 	readRequest, err := rrb.Build()
 	if err != nil {
 		t.Errorf("error preparing read-request: %s", connectionResult.Err.Error())
@@ -169,7 +170,7 @@ func TestPlc4goDriver(t *testing.T) {
 	}
 
 	// Do something with the response
-    value := rrr.Response.GetValue("field")
+	value := rrr.Response.GetValue("field")
 
-	fmt.Printf("\n\nResult: %f\n", value.GetFloat32())
+	fmt.Printf("\n\nResult: %f\n", value)
 }
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusConnection.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusConnection.go
index 90c474c..607f4f3 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusConnection.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusConnection.go
@@ -19,11 +19,12 @@
 package modbus
 
 import (
-    internalModel "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/model"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/transports"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+	internalModel "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/model"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi/interceptors"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/transports"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
 )
 
 type ConnectionMetadata struct {
@@ -43,29 +44,31 @@ func (m ConnectionMetadata) CanSubscribe() bool {
 }
 
 type ModbusConnection struct {
-    transactionIdentifier uint16
-    messageCodec spi.MessageCodec
-    options map[string][]string
-	fieldHandler spi.PlcFieldHandler
-	valueHandler spi.PlcValueHandler
+	transactionIdentifier uint16
+	messageCodec          spi.MessageCodec
+	options               map[string][]string
+	fieldHandler          spi.PlcFieldHandler
+	valueHandler          spi.PlcValueHandler
+	requestInterceptor    internalModel.RequestInterceptor
 	plc4go.PlcConnection
 }
 
-func NewModbusConnection(transactionIdentifier uint16, messageCodec spi.MessageCodec, options map[string][]string, fieldHandler spi.PlcFieldHandler, valueHandler spi.PlcValueHandler) ModbusConnection {
+func NewModbusConnection(transactionIdentifier uint16, messageCodec spi.MessageCodec, options map[string][]string, fieldHandler spi.PlcFieldHandler) ModbusConnection {
 	return ModbusConnection{
-        transactionIdentifier: transactionIdentifier,
-        messageCodec: messageCodec,
-	    options: options,
-		fieldHandler: fieldHandler,
-		valueHandler: valueHandler,
+		transactionIdentifier: transactionIdentifier,
+		messageCodec:          messageCodec,
+		options:               options,
+		fieldHandler:          fieldHandler,
+		valueHandler:          NewValueHandler(),
+		requestInterceptor:    interceptors.NewSingleItemRequestInterceptor(),
 	}
 }
 
 func (m ModbusConnection) Connect() <-chan plc4go.PlcConnectionConnectResult {
 	ch := make(chan plc4go.PlcConnectionConnectResult)
 	go func() {
-	    err := m.messageCodec.Connect()
-        ch <- plc4go.NewPlcConnectionConnectResult(m, err)
+		err := m.messageCodec.Connect()
+		ch <- plc4go.NewPlcConnectionConnectResult(m, err)
 	}()
 	return ch
 }
@@ -92,12 +95,13 @@ func (m ModbusConnection) GetMetadata() model.PlcConnectionMetadata {
 }
 
 func (m ModbusConnection) ReadRequestBuilder() model.PlcReadRequestBuilder {
-	return internalModel.NewDefaultPlcReadRequestBuilder(m.fieldHandler,
-	    NewModbusReader(m.transactionIdentifier, m.messageCodec))
+	return internalModel.NewDefaultPlcReadRequestBuilderWithInterceptor(m.fieldHandler,
+		NewModbusReader(m.transactionIdentifier, m.messageCodec), m.requestInterceptor)
 }
 
 func (m ModbusConnection) WriteRequestBuilder() model.PlcWriteRequestBuilder {
-	return internalModel.NewDefaultPlcWriteRequestBuilder(m.fieldHandler, m.valueHandler, NewModbusWriter())
+	return internalModel.NewDefaultPlcWriteRequestBuilder(
+		m.fieldHandler, m.valueHandler, NewModbusWriter(m.requestInterceptor))
 }
 
 func (m ModbusConnection) SubscriptionRequestBuilder() model.PlcSubscriptionRequestBuilder {
@@ -109,9 +113,8 @@ func (m ModbusConnection) UnsubscriptionRequestBuilder() model.PlcUnsubscription
 }
 
 func (m ModbusConnection) GetTransportInstance() transports.TransportInstance {
-    if mc, ok := m.messageCodec.(spi.TransportInstanceExposer); ok {
-        return mc.GetTransportInstance()
-    }
-    return nil
+	if mc, ok := m.messageCodec.(spi.TransportInstanceExposer); ok {
+		return mc.GetTransportInstance()
+	}
+	return nil
 }
-
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusDriver.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusDriver.go
index bb68052..529ae30 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusDriver.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusDriver.go
@@ -19,30 +19,28 @@
 package modbus
 
 import (
-    "encoding/json"
-    "errors"
-    "fmt"
-    "math"
-    "net/url"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/transports"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
-    "sync/atomic"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"math"
+	"net/url"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/transports"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
+	"sync/atomic"
 )
 
 type ModbusDriver struct {
-    transactionIdCounter int32
-	fieldHandler spi.PlcFieldHandler
-	valueHandler spi.PlcValueHandler
+	transactionIdCounter int32
+	fieldHandler         spi.PlcFieldHandler
 	plc4go.PlcDriver
 }
 
 func NewModbusDriver() *ModbusDriver {
 	return &ModbusDriver{
-	    transactionIdCounter: 0,
-		fieldHandler: NewFieldHandler(),
-		valueHandler: NewValueHandler(),
+		transactionIdCounter: 0,
+		fieldHandler:         NewFieldHandler(),
 	}
 }
 
@@ -55,7 +53,7 @@ func (m ModbusDriver) GetProtocolName() string {
 }
 
 func (m ModbusDriver) GetDefaultTransport() string {
-    return "tcp"
+	return "tcp"
 }
 
 func (m ModbusDriver) CheckQuery(query string) error {
@@ -64,46 +62,46 @@ func (m ModbusDriver) CheckQuery(query string) error {
 }
 
 func (m ModbusDriver) GetConnection(transportUrl url.URL, transports map[string]transports.Transport, options map[string][]string) <-chan plc4go.PlcConnectionConnectResult {
-    // Get an the transport specified in the url
-    transport, ok := transports[transportUrl.Scheme]
-    if !ok {
-        ch := make(chan plc4go.PlcConnectionConnectResult)
-        ch <- plc4go.NewPlcConnectionConnectResult(nil, errors.New("couldn't find transport for given transport url " + transportUrl.String()))
-        return ch
-    }
-    // Provide a default-port to the transport, which is used, if the user doesn't provide on in the connection string.
-    options["defaultTcpPort"] = []string{"502"}
-    // Have the transport create a new transport-instance.
-    transportInstance, err := transport.CreateTransportInstance(transportUrl, options)
-    if err != nil {
-        ch := make(chan plc4go.PlcConnectionConnectResult)
-        ch <- plc4go.NewPlcConnectionConnectResult(nil, errors.New("couldn't initialize transport configuration for given transport url " + transportUrl.String()))
-        return ch
-    }
-    // Generate a new transaction id
-    transactionId := atomic.AddInt32(&m.transactionIdCounter, 1)
-    if transactionId > math.MaxUint16 {
-        transactionId = 1
-        atomic.StoreInt32(&m.transactionIdCounter, 1)
-    }
+	// Get an the transport specified in the url
+	transport, ok := transports[transportUrl.Scheme]
+	if !ok {
+		ch := make(chan plc4go.PlcConnectionConnectResult)
+		ch <- plc4go.NewPlcConnectionConnectResult(nil, errors.New("couldn't find transport for given transport url "+transportUrl.String()))
+		return ch
+	}
+	// Provide a default-port to the transport, which is used, if the user doesn't provide on in the connection string.
+	options["defaultTcpPort"] = []string{"502"}
+	// Have the transport create a new transport-instance.
+	transportInstance, err := transport.CreateTransportInstance(transportUrl, options)
+	if err != nil {
+		ch := make(chan plc4go.PlcConnectionConnectResult)
+		ch <- plc4go.NewPlcConnectionConnectResult(nil, errors.New("couldn't initialize transport configuration for given transport url "+transportUrl.String()))
+		return ch
+	}
+	// Generate a new transaction id
+	transactionId := atomic.AddInt32(&m.transactionIdCounter, 1)
+	if transactionId > math.MaxUint16 {
+		transactionId = 1
+		atomic.StoreInt32(&m.transactionIdCounter, 1)
+	}
 
-    // Create a new codec for taking care of encoding/decoding of messages
-    defaultChanel := make(chan interface{})
-    go func() {
-        for {
-            msg := <-defaultChanel
-            adu := model.CastModbusTcpADU(msg)
-            serialized, err := json.Marshal(adu)
-            if err != nil {
-                fmt.Errorf("got error serializing adu: %s\n", err.Error())
-            } else {
-                fmt.Printf("got message in the default handler %s\n", serialized)
-            }
-        }
-    }()
-    codec := NewModbusMessageCodec(transportInstance, nil)
+	// Create a new codec for taking care of encoding/decoding of messages
+	defaultChanel := make(chan interface{})
+	go func() {
+		for {
+			msg := <-defaultChanel
+			adu := model.CastModbusTcpADU(msg)
+			serialized, err := json.Marshal(adu)
+			if err != nil {
+				fmt.Errorf("got error serializing adu: %s\n", err.Error())
+			} else {
+				fmt.Printf("got message in the default handler %s\n", serialized)
+			}
+		}
+	}()
+	codec := NewModbusMessageCodec(transportInstance, nil)
 
-    // Create the new connection
-	connection := NewModbusConnection(uint16(transactionId), codec, options, m.fieldHandler, m.valueHandler)
+	// Create the new connection
+	connection := NewModbusConnection(uint16(transactionId), codec, options, m.fieldHandler)
 	return connection.Connect()
 }
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
index fcf3edd..d8ad0a8 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
@@ -180,6 +180,8 @@ func toPlc4xResponse(requestAdu modbusModel.ModbusTcpADU, responseAdu modbusMode
 	case modbusModel.ModbusPDUReadHoldingRegistersResponse:
 		pdu := modbusModel.CastModbusPDUReadHoldingRegistersResponse(responseAdu.Pdu)
 		data = utils.Int8ToUint8(pdu.Value)
+	case modbusModel.ModbusPDUError:
+		return nil, errors.New("got an error from remote")
 	default:
 		return nil, errors.New("unsupported response type")
 	}
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusWriter.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusWriter.go
index 897d5a0..0a6b8f3 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusWriter.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusWriter.go
@@ -19,16 +19,20 @@
 package modbus
 
 import (
+	model2 "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/model"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
 )
 
 type ModbusWriter struct {
+	writeRequestInterceptor model2.WriteRequestInterceptor
 	spi.PlcWriter
 }
 
-func NewModbusWriter() ModbusWriter {
-	return ModbusWriter{}
+func NewModbusWriter(writeRequestInterceptor model2.WriteRequestInterceptor) ModbusWriter {
+	return ModbusWriter{
+		writeRequestInterceptor: writeRequestInterceptor,
+	}
 }
 
 func (m ModbusWriter) Write(writeRequest model.PlcWriteRequest) <-chan model.PlcWriteRequestResult {
diff --git a/sandbox/plc4go/internal/plc4go/modbus/readwrite/model/ModbusPDUError.go b/sandbox/plc4go/internal/plc4go/modbus/readwrite/model/ModbusPDUError.go
index b5f4d18..72d1b57 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/readwrite/model/ModbusPDUError.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/readwrite/model/ModbusPDUError.go
@@ -19,145 +19,144 @@
 package model
 
 import (
-    "encoding/xml"
-    "errors"
-    "io"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+	"encoding/xml"
+	"errors"
+	"io"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
 )
 
 // The data-structure of this message
 type ModbusPDUError struct {
-    ExceptionCode uint8
-    ModbusPDU
+	ExceptionCode IModbusErrorCode
+	ModbusPDU
 }
 
 // The corresponding interface
 type IModbusPDUError interface {
-    IModbusPDU
-    Serialize(io utils.WriteBuffer) error
+	IModbusPDU
+	Serialize(io utils.WriteBuffer) error
 }
 
 // Accessors for discriminator values.
 func (m ModbusPDUError) ErrorFlag() bool {
-    return true
+	return true
 }
 
 func (m ModbusPDUError) FunctionFlag() uint8 {
-    return 0
+	return 0
 }
 
 func (m ModbusPDUError) Response() bool {
-    return false
+	return false
 }
 
 func (m ModbusPDUError) initialize() spi.Message {
-    return m
+	return m
 }
 
-func NewModbusPDUError(exceptionCode uint8) ModbusPDUInitializer {
-    return &ModbusPDUError{ExceptionCode: exceptionCode}
+func NewModbusPDUError(exceptionCode IModbusErrorCode) ModbusPDUInitializer {
+	return &ModbusPDUError{ExceptionCode: exceptionCode}
 }
 
 func CastIModbusPDUError(structType interface{}) IModbusPDUError {
-    castFunc := func(typ interface{}) IModbusPDUError {
-        if iModbusPDUError, ok := typ.(IModbusPDUError); ok {
-            return iModbusPDUError
-        }
-        return nil
-    }
-    return castFunc(structType)
+	castFunc := func(typ interface{}) IModbusPDUError {
+		if iModbusPDUError, ok := typ.(IModbusPDUError); ok {
+			return iModbusPDUError
+		}
+		return nil
+	}
+	return castFunc(structType)
 }
 
 func CastModbusPDUError(structType interface{}) ModbusPDUError {
-    castFunc := func(typ interface{}) ModbusPDUError {
-        if sModbusPDUError, ok := typ.(ModbusPDUError); ok {
-            return sModbusPDUError
-        }
-        if sModbusPDUError, ok := typ.(*ModbusPDUError); ok {
-            return *sModbusPDUError
-        }
-        return ModbusPDUError{}
-    }
-    return castFunc(structType)
+	castFunc := func(typ interface{}) ModbusPDUError {
+		if sModbusPDUError, ok := typ.(ModbusPDUError); ok {
+			return sModbusPDUError
+		}
+		if sModbusPDUError, ok := typ.(*ModbusPDUError); ok {
+			return *sModbusPDUError
+		}
+		return ModbusPDUError{}
+	}
+	return castFunc(structType)
 }
 
 func (m ModbusPDUError) LengthInBits() uint16 {
-    var lengthInBits uint16 = m.ModbusPDU.LengthInBits()
+	var lengthInBits uint16 = m.ModbusPDU.LengthInBits()
 
-    // Simple field (exceptionCode)
-    lengthInBits += 8
+	// Enum Field (exceptionCode)
+	lengthInBits += 8
 
-    return lengthInBits
+	return lengthInBits
 }
 
 func (m ModbusPDUError) LengthInBytes() uint16 {
-    return m.LengthInBits() / 8
+	return m.LengthInBits() / 8
 }
 
 func ModbusPDUErrorParse(io *utils.ReadBuffer) (ModbusPDUInitializer, error) {
 
-    // Simple Field (exceptionCode)
-    exceptionCode, _exceptionCodeErr := io.ReadUint8(8)
-    if _exceptionCodeErr != nil {
-        return nil, errors.New("Error parsing 'exceptionCode' field " + _exceptionCodeErr.Error())
-    }
+	// Enum field (exceptionCode)
+	exceptionCode, _exceptionCodeErr := ModbusErrorCodeParse(io)
+	if _exceptionCodeErr != nil {
+		return nil, errors.New("Error parsing 'exceptionCode' field " + _exceptionCodeErr.Error())
+	}
 
-    // Create the instance
-    return NewModbusPDUError(exceptionCode), nil
+	// Create the instance
+	return NewModbusPDUError(exceptionCode), nil
 }
 
 func (m ModbusPDUError) Serialize(io utils.WriteBuffer) error {
-    ser := func() error {
-
-    // Simple Field (exceptionCode)
-    exceptionCode := uint8(m.ExceptionCode)
-    _exceptionCodeErr := io.WriteUint8(8, (exceptionCode))
-    if _exceptionCodeErr != nil {
-        return errors.New("Error serializing 'exceptionCode' field " + _exceptionCodeErr.Error())
-    }
-
-        return nil
-    }
-    return ModbusPDUSerialize(io, m.ModbusPDU, CastIModbusPDU(m), ser)
+	ser := func() error {
+
+		// Enum field (exceptionCode)
+		exceptionCode := CastModbusErrorCode(m.ExceptionCode)
+		_exceptionCodeErr := exceptionCode.Serialize(io)
+		if _exceptionCodeErr != nil {
+			return errors.New("Error serializing 'exceptionCode' field " + _exceptionCodeErr.Error())
+		}
+
+		return nil
+	}
+	return ModbusPDUSerialize(io, m.ModbusPDU, CastIModbusPDU(m), ser)
 }
 
 func (m *ModbusPDUError) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
-    for {
-        token, err := d.Token()
-        if err != nil {
-            if err == io.EOF {
-                return nil
-            }
-            return err
-        }
-        switch token.(type) {
-        case xml.StartElement:
-            tok := token.(xml.StartElement)
-            switch tok.Name.Local {
-            case "exceptionCode":
-                var data uint8
-                if err := d.DecodeElement(&data, &tok); err != nil {
-                    return err
-                }
-                m.ExceptionCode = data
-            }
-        }
-    }
+	for {
+		token, err := d.Token()
+		if err != nil {
+			if err == io.EOF {
+				return nil
+			}
+			return err
+		}
+		switch token.(type) {
+		case xml.StartElement:
+			tok := token.(xml.StartElement)
+			switch tok.Name.Local {
+			case "exceptionCode":
+				var data *ModbusErrorCode
+				if err := d.DecodeElement(&data, &tok); err != nil {
+					return err
+				}
+				m.ExceptionCode = data
+			}
+		}
+	}
 }
 
 func (m ModbusPDUError) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
-    if err := e.EncodeToken(xml.StartElement{Name: start.Name, Attr: []xml.Attr{
-            {Name: xml.Name{Local: "className"}, Value: "org.apache.plc4x.java.modbus.readwrite.ModbusPDUError"},
-        }}); err != nil {
-        return err
-    }
-    if err := e.EncodeElement(m.ExceptionCode, xml.StartElement{Name: xml.Name{Local: "exceptionCode"}}); err != nil {
-        return err
-    }
-    if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
-        return err
-    }
-    return nil
+	if err := e.EncodeToken(xml.StartElement{Name: start.Name, Attr: []xml.Attr{
+		{Name: xml.Name{Local: "className"}, Value: "org.apache.plc4x.java.modbus.readwrite.ModbusPDUError"},
+	}}); err != nil {
+		return err
+	}
+	if err := e.EncodeElement(m.ExceptionCode, xml.StartElement{Name: xml.Name{Local: "exceptionCode"}}); err != nil {
+		return err
+	}
+	if err := e.EncodeToken(xml.EndElement{Name: start.Name}); err != nil {
+		return err
+	}
+	return nil
 }
-
diff --git a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
index 5a747be..afbd09f 100644
--- a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
+++ b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
@@ -23,20 +23,27 @@ import (
 	"errors"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+	"time"
 )
 
 type DefaultPlcReadRequestBuilder struct {
-	reader       spi.PlcReader
-	fieldHandler spi.PlcFieldHandler
-	queries      map[string]string
+	reader                 spi.PlcReader
+	fieldHandler           spi.PlcFieldHandler
+	queries                map[string]string
+	readRequestInterceptor ReadRequestInterceptor
 	model.PlcReadRequestBuilder
 }
 
 func NewDefaultPlcReadRequestBuilder(fieldHandler spi.PlcFieldHandler, reader spi.PlcReader) *DefaultPlcReadRequestBuilder {
+	return NewDefaultPlcReadRequestBuilderWithInterceptor(fieldHandler, reader, nil)
+}
+
+func NewDefaultPlcReadRequestBuilderWithInterceptor(fieldHandler spi.PlcFieldHandler, reader spi.PlcReader, readRequestInterceptor ReadRequestInterceptor) *DefaultPlcReadRequestBuilder {
 	return &DefaultPlcReadRequestBuilder{
-		reader:       reader,
-		fieldHandler: fieldHandler,
-		queries:      map[string]string{},
+		reader:                 reader,
+		fieldHandler:           fieldHandler,
+		queries:                map[string]string{},
+		readRequestInterceptor: readRequestInterceptor,
 	}
 }
 
@@ -55,31 +62,78 @@ func (m *DefaultPlcReadRequestBuilder) Build() (model.PlcReadRequest, error) {
 		fields[name] = field
 	}
 	return DefaultPlcReadRequest{
-		fields: fields,
-		reader: m.reader,
+		Fields:                 fields,
+		Reader:                 m.reader,
+		ReadRequestInterceptor: m.readRequestInterceptor,
 	}, nil
 }
 
 type DefaultPlcReadRequest struct {
-	fields map[string]model.PlcField
-	reader spi.PlcReader
+	Fields                 map[string]model.PlcField
+	Reader                 spi.PlcReader
+	ReadRequestInterceptor ReadRequestInterceptor
 	model.PlcReadRequest
 }
 
+func NewDefaultPlcReadRequest(fields map[string]model.PlcField, reader spi.PlcReader, readRequestInterceptor ReadRequestInterceptor) DefaultPlcReadRequest {
+	return DefaultPlcReadRequest{
+		Fields:                 fields,
+		Reader:                 reader,
+		ReadRequestInterceptor: readRequestInterceptor,
+	}
+}
+
 func (m DefaultPlcReadRequest) Execute() <-chan model.PlcReadRequestResult {
-	return m.reader.Read(m)
+	// Shortcut, if no interceptor is defined
+	if m.ReadRequestInterceptor == nil {
+		return m.Reader.Read(m)
+	}
+
+	// Split the requests up into multiple ones.
+	readRequests := m.ReadRequestInterceptor.InterceptReadRequest(m)
+	// Shortcut for single-request-requests
+	if len(readRequests) == 1 {
+		return m.Reader.Read(readRequests[0])
+	} else {
+		// Create a sub-result-channel slice
+		var subResultChannels []<-chan model.PlcReadRequestResult
+
+		// Iterate over all requests and add the result-channels to the list
+		for _, subRequest := range readRequests {
+			subResultChannels = append(subResultChannels, m.Reader.Read(subRequest))
+			// TODO: Replace this with a real queueing of requests.
+			time.Sleep(time.Millisecond * 20)
+		}
+
+		// Create a new result-channel, which completes as soon as all sub-result-channels have returned
+		resultChannel := make(chan model.PlcReadRequestResult)
+		go func() {
+			var subResults []model.PlcReadRequestResult
+			// Iterate over all sub-results
+			for _, subResultChannel := range subResultChannels {
+				subResult := <-subResultChannel
+				subResults = append(subResults, subResult)
+			}
+			// As soon as all are done, process the results
+			result := m.ReadRequestInterceptor.ProcessReadResponses(m, subResults)
+			// Return the final result
+			resultChannel <- result
+		}()
+
+		return resultChannel
+	}
 }
 
 func (m DefaultPlcReadRequest) GetFieldNames() []string {
-	fieldNames := []string{}
-	for name := range m.fields {
+	var fieldNames []string
+	for name := range m.Fields {
 		fieldNames = append(fieldNames, name)
 	}
 	return fieldNames
 }
 
 func (m DefaultPlcReadRequest) GetField(name string) model.PlcField {
-	if field, ok := m.fields[name]; ok {
+	if field, ok := m.Fields[name]; ok {
 		return field
 	}
 	return nil
@@ -90,10 +144,10 @@ func (m DefaultPlcReadRequest) MarshalXML(e *xml.Encoder, start xml.StartElement
 		return err
 	}
 
-	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "fields"}}); err != nil {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "Fields"}}); err != nil {
 		return err
 	}
-	for fieldName, field := range m.fields {
+	for fieldName, field := range m.Fields {
 		if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: fieldName}}); err != nil {
 			return err
 		}
@@ -104,7 +158,7 @@ func (m DefaultPlcReadRequest) MarshalXML(e *xml.Encoder, start xml.StartElement
 			return err
 		}
 	}
-	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "fields"}}); err != nil {
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "Fields"}}); err != nil {
 		return err
 	}
 
diff --git a/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go b/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
index 91e5a63..5c90853 100644
--- a/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
+++ b/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
@@ -88,7 +88,7 @@ func (m DefaultPlcWriteRequest) MarshalXML(e *xml.Encoder, start xml.StartElemen
 		return err
 	}
 
-	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "fields"}}); err != nil {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "Fields"}}); err != nil {
 		return err
 	}
 	for fieldName, field := range m.fields {
@@ -102,7 +102,7 @@ func (m DefaultPlcWriteRequest) MarshalXML(e *xml.Encoder, start xml.StartElemen
 			return err
 		}
 	}
-	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "fields"}}); err != nil {
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "Fields"}}); err != nil {
 		return err
 	}
 
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusWriter.go b/sandbox/plc4go/internal/plc4go/model/RequestInterceptor.go
similarity index 59%
copy from sandbox/plc4go/internal/plc4go/modbus/ModbusWriter.go
copy to sandbox/plc4go/internal/plc4go/model/RequestInterceptor.go
index 897d5a0..614057b 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusWriter.go
+++ b/sandbox/plc4go/internal/plc4go/model/RequestInterceptor.go
@@ -16,21 +16,23 @@
 // specific language governing permissions and limitations
 // under the License.
 //
-package modbus
+package model
 
 import (
-	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
 )
 
-type ModbusWriter struct {
-	spi.PlcWriter
+type ReadRequestInterceptor interface {
+	InterceptReadRequest(readRequest model.PlcReadRequest) []model.PlcReadRequest
+	ProcessReadResponses(readRequest model.PlcReadRequest, readResults []model.PlcReadRequestResult) model.PlcReadRequestResult
 }
 
-func NewModbusWriter() ModbusWriter {
-	return ModbusWriter{}
+type WriteRequestInterceptor interface {
+	InterceptWriteRequest(writeRequest model.PlcWriteRequest) []model.PlcWriteRequest
+	ProcessWriteResponses(writeRequest model.PlcWriteRequest, writeResults []model.PlcWriteRequestResult) model.PlcWriteRequestResult
 }
 
-func (m ModbusWriter) Write(writeRequest model.PlcWriteRequest) <-chan model.PlcWriteRequestResult {
-	return make(chan model.PlcWriteRequestResult)
+type RequestInterceptor interface {
+	ReadRequestInterceptor
+	WriteRequestInterceptor
 }
diff --git a/sandbox/plc4go/internal/plc4go/spi/interceptors/SingleItemRequestInterceptor.go b/sandbox/plc4go/internal/plc4go/spi/interceptors/SingleItemRequestInterceptor.go
new file mode 100644
index 0000000..6027e62
--- /dev/null
+++ b/sandbox/plc4go/internal/plc4go/spi/interceptors/SingleItemRequestInterceptor.go
@@ -0,0 +1,85 @@
+//
+// 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 interceptors
+
+import (
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/model"
+	apiModel "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+)
+
+type SingleItemRequestInterceptor struct {
+}
+
+func NewSingleItemRequestInterceptor() SingleItemRequestInterceptor {
+	return SingleItemRequestInterceptor{}
+}
+
+func (m SingleItemRequestInterceptor) InterceptReadRequest(readRequest apiModel.PlcReadRequest) []apiModel.PlcReadRequest {
+	// If this request just has one field, go the shortcut
+	if len(readRequest.GetFieldNames()) == 1 {
+		return []apiModel.PlcReadRequest{readRequest}
+	}
+	// In all other cases, create a new read request containing only one item
+	defaultReadRequest := readRequest.(model.DefaultPlcReadRequest)
+	var readRequests []apiModel.PlcReadRequest
+	for _, fieldName := range readRequest.GetFieldNames() {
+		field := readRequest.GetField(fieldName)
+		subReadRequest := model.NewDefaultPlcReadRequest(
+			map[string]apiModel.PlcField{fieldName: field},
+			defaultReadRequest.Reader,
+			defaultReadRequest.ReadRequestInterceptor)
+		readRequests = append(readRequests, subReadRequest)
+	}
+	return readRequests
+}
+
+func (m SingleItemRequestInterceptor) ProcessReadResponses(readRequest apiModel.PlcReadRequest, readResults []apiModel.PlcReadRequestResult) apiModel.PlcReadRequestResult {
+	if len(readResults) == 1 {
+		return readResults[0]
+	}
+
+	responseCodes := map[string]apiModel.PlcResponseCode{}
+	val := map[string]values.PlcValue{}
+	var err error
+	for _, readResult := range readResults {
+		if readResult.Err != nil {
+			// TODO: Handle the case that multiple requests had errors that are different
+			err = readResult.Err
+		} else if readResult.Response != nil {
+			for _, fieldName := range readResult.Response.GetRequest().GetFieldNames() {
+				responseCodes[fieldName] = readResult.Response.GetResponseCode(fieldName)
+				val[fieldName] = readResult.Response.GetValue(fieldName)
+			}
+		}
+	}
+	return apiModel.PlcReadRequestResult{
+		Request:  readRequest,
+		Response: model.NewDefaultPlcReadResponse(readRequest, responseCodes, val),
+		Err:      err,
+	}
+}
+
+func (m SingleItemRequestInterceptor) InterceptWriteRequest(writeRequest apiModel.PlcWriteRequest) []apiModel.PlcWriteRequest {
+	return []apiModel.PlcWriteRequest{writeRequest}
+}
+
+func (m SingleItemRequestInterceptor) ProcessWriteResponses(writeRequest apiModel.PlcWriteRequest, writeResponses []apiModel.PlcWriteRequestResult) apiModel.PlcWriteRequestResult {
+	return apiModel.PlcWriteRequestResult{}
+}
diff --git a/sandbox/plc4go/pkg/plc4go/model/plc_write_response.go b/sandbox/plc4go/pkg/plc4go/model/plc_write_response.go
index a3c57da..db3abbe 100644
--- a/sandbox/plc4go/pkg/plc4go/model/plc_write_response.go
+++ b/sandbox/plc4go/pkg/plc4go/model/plc_write_response.go
@@ -18,5 +18,5 @@
 //
 package model
 
-type PlcWriteResponse struct {
+type PlcWriteResponse interface {
 }