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 09:53:54 UTC

[plc4x] branch feature/plc4go updated: - Finished implementing a first fully working version of the integration-test-framework for plc4go

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 1b33b3f  - Finished implementing a first fully working version of the integration-test-framework for plc4go
1b33b3f is described below

commit 1b33b3f816b087c2a135d65c72365d53bc540fe9
Author: Christofer Dutz <ch...@c-ware.de>
AuthorDate: Fri Oct 30 10:53:19 2020 +0100

    - Finished implementing a first fully working version of the integration-test-framework for plc4go
---
 .../plc4go/internal/plc4go/modbus/ModbusField.go   |  40 +-
 .../internal/plc4go/modbus/ModbusFieldHandler.go   |  52 +-
 .../plc4go/internal/plc4go/modbus/ModbusReader.go  | 325 ++++-----
 .../internal/plc4go/model/DefaultPlcReadRequest.go |  48 +-
 .../plc4go/model/DefaultPlcReadResponse.go         |  70 +-
 .../plc4go/model/DefaultPlcWriteRequest.go         |  30 +
 .../plc4go/internal/plc4go/model/values/BOOL.go    |  11 +-
 .../plc4go/internal/plc4go/model/values/BYTE.go    |  11 +-
 .../plc4go/internal/plc4go/model/values/CHAR.go    |  11 +-
 .../plc4go/internal/plc4go/model/values/DATE.go    |  12 +-
 .../internal/plc4go/model/values/DATE_AND_TIME.go  |  13 +-
 .../plc4go/internal/plc4go/model/values/DINT.go    |  12 +-
 .../plc4go/internal/plc4go/model/values/DWORD.go   |  11 +-
 sandbox/plc4go/internal/plc4go/model/values/INT.go |  12 +-
 .../plc4go/internal/plc4go/model/values/LINT.go    |  12 +-
 .../plc4go/internal/plc4go/model/values/LREAL.go   |  12 +-
 .../plc4go/internal/plc4go/model/values/LTIME.go   |  13 +-
 .../plc4go/internal/plc4go/model/values/LWORD.go   |  11 +-
 .../plc4go/internal/plc4go/model/values/NULL.go    |  14 +-
 .../plc4go/internal/plc4go/model/values/PlcList.go |  22 +-
 .../internal/plc4go/model/values/PlcStruct.go      |  22 +-
 .../plc4go/internal/plc4go/model/values/REAL.go    |  12 +-
 .../plc4go/internal/plc4go/model/values/SINT.go    |  11 +-
 .../plc4go/internal/plc4go/model/values/STRING.go  |  11 +-
 .../plc4go/internal/plc4go/model/values/TIME.go    |  13 +-
 .../internal/plc4go/model/values/TIME_OF_DAY.go    |  13 +-
 .../plc4go/internal/plc4go/model/values/UDINT.go   |  12 +-
 .../plc4go/internal/plc4go/model/values/UINT.go    |  12 +-
 .../plc4go/internal/plc4go/model/values/ULINT.go   |  12 +-
 .../plc4go/internal/plc4go/model/values/USINT.go   |  12 +-
 .../plc4go/internal/plc4go/model/values/WCHAR.go   |  12 +-
 .../plc4go/internal/plc4go/model/values/WORD.go    |  11 +-
 .../plc4go/internal/plc4go/model/values/WSTRING.go |  12 +-
 .../internal/plc4go/testutils/DriverTestRunner.go  | 775 +++++++++++----------
 .../plc4go/testutils/ParserSerializerTestRunner.go | 295 ++++----
 .../plc4go/internal/plc4go/testutils/TestUtils.go  |  52 ++
 .../plc4go/pkg/plc4go/model/plc_read_response.go   |   8 +-
 .../plc4go/pkg/plc4go/model/plc_response_code.go   |  63 ++
 38 files changed, 1310 insertions(+), 790 deletions(-)

diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusField.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusField.go
index 65a31df..d1f978a 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusField.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusField.go
@@ -19,23 +19,24 @@
 package modbus
 
 import (
+	"encoding/xml"
 	"errors"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
 	"strconv"
 )
 
 const (
-    MODBUS_PROTOCOL_ADDRESS_OFFSET = 1
+	MODBUS_PROTOCOL_ADDRESS_OFFSET = 1
 )
 
 type ModbusPlcField struct {
-	FieldType uint8
+	FieldType ModbusFieldType
 	Address   uint16
 	Quantity  uint16
 	Datatype  string
 }
 
-func NewModbusPlcField(fieldType uint8, address uint16, quantity uint16, datatype string) ModbusPlcField {
+func NewModbusPlcField(fieldType ModbusFieldType, address uint16, quantity uint16, datatype string) ModbusPlcField {
 	return ModbusPlcField{
 		FieldType: fieldType,
 		Address:   address - MODBUS_PROTOCOL_ADDRESS_OFFSET,
@@ -44,7 +45,7 @@ func NewModbusPlcField(fieldType uint8, address uint16, quantity uint16, datatyp
 	}
 }
 
-func NewModbusPlcFieldFromStrings(fieldType uint8, addressString string, quantityString string, datatype string) (model.PlcField, error) {
+func NewModbusPlcFieldFromStrings(fieldType ModbusFieldType, addressString string, quantityString string, datatype string) (model.PlcField, error) {
 	address, err := strconv.Atoi(addressString)
 	if err != nil {
 		return nil, errors.New("Couldn't parse address string '" + addressString + "' into an int")
@@ -57,12 +58,33 @@ func NewModbusPlcFieldFromStrings(fieldType uint8, addressString string, quantit
 }
 
 func (m ModbusPlcField) GetTypeName() string {
-    return m.Datatype
+	return m.Datatype
 }
 
 func CastFromPlcField(plcField model.PlcField) (ModbusPlcField, error) {
-    if modbusField, ok := plcField.(ModbusPlcField); ok {
-        return modbusField, nil
-    }
-    return ModbusPlcField{}, errors.New("coudln't cast to ModbusPlcField")
+	if modbusField, ok := plcField.(ModbusPlcField); ok {
+		return modbusField, nil
+	}
+	return ModbusPlcField{}, errors.New("couldn't cast to ModbusPlcField")
+}
+
+func (m ModbusPlcField) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: m.FieldType.GetName()}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeElement(m.Address, xml.StartElement{Name: xml.Name{Local: "address"}}); err != nil {
+		return err
+	}
+	if err := e.EncodeElement(m.Quantity, xml.StartElement{Name: xml.Name{Local: "numberOfElements"}}); err != nil {
+		return err
+	}
+	if err := e.EncodeElement(m.Datatype, xml.StartElement{Name: xml.Name{Local: "dataType"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: m.FieldType.GetName()}}); err != nil {
+		return err
+	}
+	return nil
 }
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusFieldHandler.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusFieldHandler.go
index 746b6a2..8147439 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusFieldHandler.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusFieldHandler.go
@@ -21,19 +21,37 @@ package modbus
 import (
 	"errors"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
 	"regexp"
 )
 
+type ModbusFieldType uint8
+
 const (
-	MODBUS_FIELD_COIL              = uint8(0)
-	MODBUS_FIELD_DISCRETE_INPUT    = uint8(1)
-	MODBUS_FIELD_INPUT_REGISTER    = uint8(3)
-	MODBUS_FIELD_HOLDING_REGISTER  = uint8(4)
-	MODBUS_FIELD_EXTENDED_REGISTER = uint8(6)
+	MODBUS_FIELD_COIL              ModbusFieldType = 0x00
+	MODBUS_FIELD_DISCRETE_INPUT    ModbusFieldType = 0x01
+	MODBUS_FIELD_INPUT_REGISTER    ModbusFieldType = 0x03
+	MODBUS_FIELD_HOLDING_REGISTER  ModbusFieldType = 0x04
+	MODBUS_FIELD_EXTENDED_REGISTER ModbusFieldType = 0x06
 )
 
+func (m ModbusFieldType) GetName() string {
+	switch m {
+	case MODBUS_FIELD_COIL:
+		return "ModbusFieldHoldingRegister"
+	case MODBUS_FIELD_DISCRETE_INPUT:
+		return "ModbusFieldDiscreteInput"
+	case MODBUS_FIELD_INPUT_REGISTER:
+		return "ModbusFieldInputRegister"
+	case MODBUS_FIELD_HOLDING_REGISTER:
+		return "ModbusFieldHoldingRegister"
+	case MODBUS_FIELD_EXTENDED_REGISTER:
+		return "ModbusFieldExtendedRegister"
+	}
+	return ""
+}
+
 type FieldHandler struct {
 	plc4xCoilPattern               *regexp.Regexp
 	numericCoilPattern             *regexp.Regexp
@@ -67,25 +85,25 @@ func NewFieldHandler() FieldHandler {
 
 func (m FieldHandler) ParseQuery(query string) (model.PlcField, error) {
 	if match := utils.GetSubgropMatches(m.plc4xCoilPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_COIL, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_COIL, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.numericCoilPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_COIL, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_COIL, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.plc4xDiscreteInputPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_DISCRETE_INPUT, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_DISCRETE_INPUT, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.numericDiscreteInputPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_DISCRETE_INPUT, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_DISCRETE_INPUT, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.plc4xInputRegisterPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_INPUT_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_INPUT_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.numericInputRegisterPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_INPUT_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_INPUT_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.plc4xHoldingRegisterPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_HOLDING_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_HOLDING_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.numericHoldingRegisterPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_HOLDING_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_HOLDING_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.plc4xExtendedRegisterPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_EXTENDED_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_EXTENDED_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	} else if match := utils.GetSubgropMatches(m.numericExtendedRegisterPattern, query); match != nil {
-		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_EXTENDED_REGISTER, match["address"], match["quantity"], "IEC61131_" + match["datatype"])
+		return NewModbusPlcFieldFromStrings(MODBUS_FIELD_EXTENDED_REGISTER, match["address"], match["quantity"], "IEC61131_"+match["datatype"])
 	}
 	return nil, errors.New("Invalid address format for address '" + query + "'")
 }
diff --git a/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go b/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
index 74c0cdb..fcf3edd 100644
--- a/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
+++ b/sandbox/plc4go/internal/plc4go/modbus/ModbusReader.go
@@ -19,188 +19,189 @@
 package modbus
 
 import (
-    "errors"
-    "fmt"
-    "math"
-    modbusModel "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
-    plc4goModel "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/utils"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
-    "sync/atomic"
+	"errors"
+	"fmt"
+	"math"
+	modbusModel "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
+	plc4goModel "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/utils"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+	"sync/atomic"
 )
 
 type ModbusReader struct {
-    transactionIdentifier uint16
-    unitIdentifier int32
-    messageCodec spi.MessageCodec
+	transactionIdentifier uint16
+	unitIdentifier        int32
+	messageCodec          spi.MessageCodec
 	spi.PlcReader
 }
 
 func NewModbusReader(transactionIdentifier uint16, messageCodec spi.MessageCodec) ModbusReader {
 	return ModbusReader{
-        transactionIdentifier: transactionIdentifier,
-        unitIdentifier: 0,
-        messageCodec: messageCodec,
-    }
+		transactionIdentifier: transactionIdentifier,
+		unitIdentifier:        0,
+		messageCodec:          messageCodec,
+	}
 }
 
 func (m ModbusReader) Read(readRequest model.PlcReadRequest) <-chan model.PlcReadRequestResult {
-    result := make(chan model.PlcReadRequestResult)
-    // If we are requesting only one field, use a
-    if len(readRequest.GetFieldNames()) == 1 {
-        fieldName := readRequest.GetFieldNames()[0]
-        field := readRequest.GetField(fieldName)
-        modbusField, err := CastFromPlcField(field)
-        if err != nil {
-            result <- model.PlcReadRequestResult{
-                Request: readRequest,
-                Response: nil,
-                Err: errors.New("invalid field item type"),
-            }
-            return result
-        }
-        numWords := uint16(math.Ceil(float64(modbusField.Quantity * uint16(modbusModel.ModbusDataTypeSizesValueOf(modbusField.Datatype).DataTypeSize())) / float64(2)))
-        var pdu modbusModel.IModbusPDU = nil
-        switch modbusField.FieldType {
-        case MODBUS_FIELD_COIL:
-            pdu = modbusModel.ModbusPDUReadCoilsRequest{
-                StartingAddress: modbusField.Address,
-                Quantity:        modbusField.Quantity,
-            }
-        case MODBUS_FIELD_DISCRETE_INPUT:
-            pdu = modbusModel.ModbusPDUReadDiscreteInputsRequest{
-                StartingAddress: modbusField.Address,
-                Quantity:        modbusField.Quantity,
-            }
-        case MODBUS_FIELD_INPUT_REGISTER:
-            pdu = modbusModel.ModbusPDUReadInputRegistersRequest{
-                StartingAddress: modbusField.Address,
-                Quantity:        numWords,
-            }
-        case MODBUS_FIELD_HOLDING_REGISTER:
-            pdu = modbusModel.ModbusPDUReadHoldingRegistersRequest{
-                StartingAddress: modbusField.Address,
-                Quantity:        numWords,
-            }
-        case MODBUS_FIELD_EXTENDED_REGISTER:
-            result <- model.PlcReadRequestResult{
-                Request: readRequest,
-                Response: nil,
-                Err: errors.New("modbus currently doesn't support extended register requests"),
-            }
-            return result
-        default:
-            result <- model.PlcReadRequestResult{
-                Request: readRequest,
-                Response: nil,
-                Err: errors.New("unsupported field type"),
-            }
-            return result
-        }
+	result := make(chan model.PlcReadRequestResult)
+	// If we are requesting only one field, use a
+	if len(readRequest.GetFieldNames()) == 1 {
+		fieldName := readRequest.GetFieldNames()[0]
+		field := readRequest.GetField(fieldName)
+		modbusField, err := CastFromPlcField(field)
+		if err != nil {
+			result <- model.PlcReadRequestResult{
+				Request:  readRequest,
+				Response: nil,
+				Err:      errors.New("invalid field item type"),
+			}
+			return result
+		}
+		numWords := uint16(math.Ceil(float64(modbusField.Quantity*uint16(modbusModel.ModbusDataTypeSizesValueOf(modbusField.Datatype).DataTypeSize())) / float64(2)))
+		var pdu modbusModel.IModbusPDU = nil
+		switch modbusField.FieldType {
+		case MODBUS_FIELD_COIL:
+			pdu = modbusModel.ModbusPDUReadCoilsRequest{
+				StartingAddress: modbusField.Address,
+				Quantity:        modbusField.Quantity,
+			}
+		case MODBUS_FIELD_DISCRETE_INPUT:
+			pdu = modbusModel.ModbusPDUReadDiscreteInputsRequest{
+				StartingAddress: modbusField.Address,
+				Quantity:        modbusField.Quantity,
+			}
+		case MODBUS_FIELD_INPUT_REGISTER:
+			pdu = modbusModel.ModbusPDUReadInputRegistersRequest{
+				StartingAddress: modbusField.Address,
+				Quantity:        numWords,
+			}
+		case MODBUS_FIELD_HOLDING_REGISTER:
+			pdu = modbusModel.ModbusPDUReadHoldingRegistersRequest{
+				StartingAddress: modbusField.Address,
+				Quantity:        numWords,
+			}
+		case MODBUS_FIELD_EXTENDED_REGISTER:
+			result <- model.PlcReadRequestResult{
+				Request:  readRequest,
+				Response: nil,
+				Err:      errors.New("modbus currently doesn't support extended register requests"),
+			}
+			return result
+		default:
+			result <- model.PlcReadRequestResult{
+				Request:  readRequest,
+				Response: nil,
+				Err:      errors.New("unsupported field type"),
+			}
+			return result
+		}
 
-        // Calculate a new unit identifier
-        unitIdentifier := atomic.AddInt32(&m.unitIdentifier, 1)
-        if unitIdentifier > math.MaxUint8 {
-            unitIdentifier = 0
-            atomic.StoreInt32(&m.unitIdentifier, 0)
-        }
+		// Calculate a new unit identifier
+		unitIdentifier := atomic.AddInt32(&m.unitIdentifier, 1)
+		if unitIdentifier > math.MaxUint8 {
+			unitIdentifier = 0
+			atomic.StoreInt32(&m.unitIdentifier, 0)
+		}
 
-        // Assemble the finished ADU
-        requestAdu := modbusModel.ModbusTcpADU{
-            TransactionIdentifier: m.transactionIdentifier,
-            UnitIdentifier:        uint8(unitIdentifier),
-            Pdu:                   pdu,
-        }
+		// Assemble the finished ADU
+		requestAdu := modbusModel.ModbusTcpADU{
+			TransactionIdentifier: m.transactionIdentifier,
+			UnitIdentifier:        uint8(unitIdentifier),
+			Pdu:                   pdu,
+		}
 
-        // Send the ADU over the wire
-        err = m.messageCodec.Send(requestAdu)
-        if err != nil {
-            result <- model.PlcReadRequestResult{
-                Request: readRequest,
-                Response: nil,
-                Err: errors.New("error sending message: " + err.Error()),
-            }
-        }
+		// Send the ADU over the wire
+		err = m.messageCodec.Send(requestAdu)
+		if err != nil {
+			result <- model.PlcReadRequestResult{
+				Request:  readRequest,
+				Response: nil,
+				Err:      errors.New("error sending message: " + err.Error()),
+			}
+		}
 
-        // Register an expected response
-        check := func(response interface{})bool {
-            responseAdu := modbusModel.CastModbusTcpADU(response)
-            return responseAdu.TransactionIdentifier == m.transactionIdentifier &&
-                responseAdu.UnitIdentifier == requestAdu.UnitIdentifier
-        }
-        // Register a callback to handle the response
-        responseChan := m.messageCodec.Expect(check)
-        go func() {
-            response := <-responseChan
-            // Convert the response into an ADU
-            responseAdu := modbusModel.CastModbusTcpADU(response)
-            // Convert the modbus response into a PLC4X response
-            readResponse, err := toPlc4xResponse(requestAdu, responseAdu, readRequest)
+		// Register an expected response
+		check := func(response interface{}) bool {
+			responseAdu := modbusModel.CastModbusTcpADU(response)
+			return responseAdu.TransactionIdentifier == m.transactionIdentifier &&
+				responseAdu.UnitIdentifier == requestAdu.UnitIdentifier
+		}
+		// Register a callback to handle the response
+		responseChan := m.messageCodec.Expect(check)
+		go func() {
+			response := <-responseChan
+			// Convert the response into an ADU
+			responseAdu := modbusModel.CastModbusTcpADU(response)
+			// Convert the modbus response into a PLC4X response
+			readResponse, err := toPlc4xResponse(requestAdu, responseAdu, readRequest)
 
-            if err != nil {
-                result <- model.PlcReadRequestResult{
-                    Request: readRequest,
-                    Err: errors.New("Error decoding response: " + err.Error()),
-                }
-            } else {
-                result <- model.PlcReadRequestResult{
-                    Request:  readRequest,
-                    Response: readResponse,
-                }
-            }
-        }()
-    } else {
-        result <- model.PlcReadRequestResult{
-            Request: readRequest,
-            Response: nil,
-            Err: errors.New("modbus only supports single-item requests"),
-        }
-    }
-    fmt.Printf("Read Request %s", readRequest)
+			if err != nil {
+				result <- model.PlcReadRequestResult{
+					Request: readRequest,
+					Err:     errors.New("Error decoding response: " + err.Error()),
+				}
+			} else {
+				result <- model.PlcReadRequestResult{
+					Request:  readRequest,
+					Response: readResponse,
+				}
+			}
+		}()
+	} else {
+		result <- model.PlcReadRequestResult{
+			Request:  readRequest,
+			Response: nil,
+			Err:      errors.New("modbus only supports single-item requests"),
+		}
+	}
+	fmt.Printf("Read Request %s", readRequest)
 	return result
 }
 
-func toPlc4xResponse(requestAdu modbusModel.ModbusTcpADU, responseAdu modbusModel.ModbusTcpADU, readRequest model.PlcReadRequest) (model.PlcReadResponse,error) {
-    var data []uint8
-    switch responseAdu.Pdu.(type) {
-    case modbusModel.ModbusPDUReadDiscreteInputsResponse:
-        pdu := modbusModel.CastModbusPDUReadDiscreteInputsResponse(responseAdu.Pdu)
-        data = utils.Int8ToUint8(pdu.Value)
-        // Pure Boolean ...
-    case modbusModel.ModbusPDUReadCoilsResponse:
-        pdu := modbusModel.CastModbusPDUReadCoilsResponse(&responseAdu.Pdu)
-        data = utils.Int8ToUint8(pdu.Value)
-        // Pure Boolean ...
-    case modbusModel.ModbusPDUReadInputRegistersResponse:
-        pdu := modbusModel.CastModbusPDUReadInputRegistersResponse(responseAdu.Pdu)
-        data = utils.Int8ToUint8(pdu.Value)
-        // DataIo ...
-    case modbusModel.ModbusPDUReadHoldingRegistersResponse:
-        pdu := modbusModel.CastModbusPDUReadHoldingRegistersResponse(responseAdu.Pdu)
-        data = utils.Int8ToUint8(pdu.Value)
-    default:
-        return nil, errors.New("unsupported response type")
-    }
+func toPlc4xResponse(requestAdu modbusModel.ModbusTcpADU, responseAdu modbusModel.ModbusTcpADU, readRequest model.PlcReadRequest) (model.PlcReadResponse, error) {
+	var data []uint8
+	switch responseAdu.Pdu.(type) {
+	case modbusModel.ModbusPDUReadDiscreteInputsResponse:
+		pdu := modbusModel.CastModbusPDUReadDiscreteInputsResponse(responseAdu.Pdu)
+		data = utils.Int8ToUint8(pdu.Value)
+		// Pure Boolean ...
+	case modbusModel.ModbusPDUReadCoilsResponse:
+		pdu := modbusModel.CastModbusPDUReadCoilsResponse(&responseAdu.Pdu)
+		data = utils.Int8ToUint8(pdu.Value)
+		// Pure Boolean ...
+	case modbusModel.ModbusPDUReadInputRegistersResponse:
+		pdu := modbusModel.CastModbusPDUReadInputRegistersResponse(responseAdu.Pdu)
+		data = utils.Int8ToUint8(pdu.Value)
+		// DataIo ...
+	case modbusModel.ModbusPDUReadHoldingRegistersResponse:
+		pdu := modbusModel.CastModbusPDUReadHoldingRegistersResponse(responseAdu.Pdu)
+		data = utils.Int8ToUint8(pdu.Value)
+	default:
+		return nil, errors.New("unsupported response type")
+	}
 
-    // Get the field from the request
-    fieldName := readRequest.GetFieldNames()[0]
-    field, err := CastFromPlcField(readRequest.GetField(fieldName))
-    if err != nil {
-        return nil, errors.New("error casting to modbus-field")
-    }
+	// Get the field from the request
+	fieldName := readRequest.GetFieldNames()[0]
+	field, err := CastFromPlcField(readRequest.GetField(fieldName))
+	if err != nil {
+		return nil, errors.New("error casting to modbus-field")
+	}
 
-    // Decode the data according to the information from the request
-    rb := utils.ReadBufferNew(data)
-    value, err := modbusModel.DataItemParse(rb, field.Datatype, field.Quantity)
-    if err != nil {
-        return nil, err
-    }
-    values := map[string]values.PlcValue{}
-    values[fieldName] = value
+	// Decode the data according to the information from the request
+	rb := utils.ReadBufferNew(data)
+	value, err := modbusModel.DataItemParse(rb, field.Datatype, field.Quantity)
+	if err != nil {
+		return nil, err
+	}
+	responseCodes := map[string]model.PlcResponseCode{}
+	values := map[string]values.PlcValue{}
+	values[fieldName] = value
+	responseCodes[fieldName] = model.PlcResponseCode_OK
 
-    // Return the response
-    return plc4goModel.NewDefaultPlcReadResponse(values), nil
+	// Return the response
+	return plc4goModel.NewDefaultPlcReadResponse(readRequest, responseCodes, values), nil
 }
-
diff --git a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
index b33002d..5a747be 100644
--- a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
+++ b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadRequest.go
@@ -19,6 +19,7 @@
 package model
 
 import (
+	"encoding/xml"
 	"errors"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
@@ -70,16 +71,45 @@ func (m DefaultPlcReadRequest) Execute() <-chan model.PlcReadRequestResult {
 }
 
 func (m DefaultPlcReadRequest) GetFieldNames() []string {
-    fieldNames := []string{}
-    for name := range m.fields {
-        fieldNames = append(fieldNames, name)
-    }
-    return fieldNames
+	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 {
-        return field
-    }
-    return nil
+	if field, ok := m.fields[name]; ok {
+		return field
+	}
+	return nil
+}
+
+func (m DefaultPlcReadRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcReadRequest"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "fields"}}); err != nil {
+		return err
+	}
+	for fieldName, field := range m.fields {
+		if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: fieldName}}); err != nil {
+			return err
+		}
+		if err := e.EncodeElement(field, xml.StartElement{Name: xml.Name{Local: "field"}}); err != nil {
+			return err
+		}
+		if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: fieldName}}); err != nil {
+			return err
+		}
+	}
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "fields"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcReadRequest"}}); err != nil {
+		return err
+	}
+	return nil
 }
diff --git a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadResponse.go b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadResponse.go
index 2a68bfb..a7a2ca6 100644
--- a/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadResponse.go
+++ b/sandbox/plc4go/internal/plc4go/model/DefaultPlcReadResponse.go
@@ -19,22 +19,78 @@
 package model
 
 import (
+	"encoding/xml"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
 )
 
 type DefaultPlcReadResponse struct {
-	values map[string]values.PlcValue
+	request       model.PlcReadRequest
+	responseCodes map[string]model.PlcResponseCode
+	values        map[string]values.PlcValue
 	model.PlcReadResponse
 }
 
-func NewDefaultPlcReadResponse(values map[string]values.PlcValue) DefaultPlcReadResponse {
-    return DefaultPlcReadResponse {
-        values: values,
-    }
+func NewDefaultPlcReadResponse(request model.PlcReadRequest, responseCodes map[string]model.PlcResponseCode, values map[string]values.PlcValue) DefaultPlcReadResponse {
+	return DefaultPlcReadResponse{
+		request:       request,
+		responseCodes: responseCodes,
+		values:        values,
+	}
+}
+
+func (m DefaultPlcReadResponse) GetFieldNames() []string {
+	var fieldNames []string
+	for fieldName, _ := range m.values {
+		fieldNames = append(fieldNames, fieldName)
+	}
+	return fieldNames
+}
+
+func (m DefaultPlcReadResponse) GetRequest() model.PlcReadRequest {
+	return m.request
+}
+
+func (m DefaultPlcReadResponse) GetResponseCode(name string) model.PlcResponseCode {
+	return m.responseCodes[name]
 }
 
 func (m DefaultPlcReadResponse) GetValue(name string) values.PlcValue {
-    return m.values[name]
+	return m.values[name]
 }
 
+func (m DefaultPlcReadResponse) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcReadResponse"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeElement(m.request, xml.StartElement{Name: xml.Name{Local: "PlcReadRequest"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "values"}}); err != nil {
+		return err
+	}
+	for _, fieldName := range m.GetFieldNames() {
+		if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: fieldName},
+			Attr: []xml.Attr{
+				{Name: xml.Name{Local: "result"}, Value: m.GetResponseCode(fieldName).GetName()},
+			}}); err != nil {
+			return err
+		}
+		if err := e.EncodeElement(m.GetValue(fieldName), xml.StartElement{Name: xml.Name{Local: "field"}}); err != nil {
+			return err
+		}
+		if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: fieldName}}); err != nil {
+			return err
+		}
+	}
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "values"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcReadResponse"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go b/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
index 51845b4..91e5a63 100644
--- a/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
+++ b/sandbox/plc4go/internal/plc4go/model/DefaultPlcWriteRequest.go
@@ -19,6 +19,7 @@
 package model
 
 import (
+	"encoding/xml"
 	"errors"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/spi"
 	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
@@ -81,3 +82,32 @@ type DefaultPlcWriteRequest struct {
 func (m DefaultPlcWriteRequest) Execute() <-chan model.PlcWriteRequestResult {
 	return m.writer.Write(m)
 }
+
+func (m DefaultPlcWriteRequest) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcWriteRequest"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "fields"}}); err != nil {
+		return err
+	}
+	for fieldName, field := range m.fields {
+		if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: fieldName}}); err != nil {
+			return err
+		}
+		if err := e.EncodeElement(field, xml.StartElement{Name: xml.Name{Local: "field"}}); err != nil {
+			return err
+		}
+		if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: fieldName}}); err != nil {
+			return err
+		}
+	}
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "fields"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcWriteRequest"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/BOOL.go b/sandbox/plc4go/internal/plc4go/model/values/BOOL.go
index 48c1a6b..8b13824 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/BOOL.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/BOOL.go
@@ -18,9 +18,11 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcBOOL struct {
 	value bool
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcBOOL(value bool) PlcBOOL {
@@ -51,3 +53,10 @@ func (m PlcBOOL) GetBooleanAt(index uint32) bool {
 func (m PlcBOOL) GetBooleanArray() []bool {
 	return []bool{m.value}
 }
+
+func (m PlcBOOL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcBOOL"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/BYTE.go b/sandbox/plc4go/internal/plc4go/model/values/BYTE.go
index 41e9ea6..bf01ff4 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/BYTE.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/BYTE.go
@@ -18,9 +18,11 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcBYTE struct {
 	value uint8
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcBYTE(value uint8) PlcBYTE {
@@ -54,3 +56,10 @@ func (m PlcBYTE) GetBooleanArray() []bool {
 		m.value>>4&1 == 1, m.value>>5&1 == 1,
 		m.value>>6&1 == 1, m.value>>7&1 == 1}
 }
+
+func (m PlcBYTE) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcBYTE"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/CHAR.go b/sandbox/plc4go/internal/plc4go/model/values/CHAR.go
index 37c0751..27e785f 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/CHAR.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/CHAR.go
@@ -18,9 +18,11 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcCHAR struct {
 	value []byte
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcCHAR(value uint8) PlcCHAR {
@@ -36,3 +38,10 @@ func (m PlcCHAR) IsString() bool {
 func (m PlcCHAR) GetString() string {
 	return string(m.value)
 }
+
+func (m PlcCHAR) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcCHAR"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/DATE.go b/sandbox/plc4go/internal/plc4go/model/values/DATE.go
index f6274a6..be64679 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/DATE.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/DATE.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "time"
+	"encoding/xml"
+	"time"
 )
 
 type PlcDATE struct {
 	value time.Time
-    PlcValueAdapter
+	PlcValueAdapter
 }
 
 func NewPlcDATE(value time.Time) PlcDATE {
@@ -40,3 +41,10 @@ func (m PlcDATE) IsTime() bool {
 func (m PlcDATE) GetTime() time.Time {
 	return m.value
 }
+
+func (m PlcDATE) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcDATE"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/DATE_AND_TIME.go b/sandbox/plc4go/internal/plc4go/model/values/DATE_AND_TIME.go
index 665a941..6396b1e 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/DATE_AND_TIME.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/DATE_AND_TIME.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "time"
+	"encoding/xml"
+	"time"
 )
 
 type PlcDATEANDTIME struct {
 	value time.Time
-    PlcValueAdapter
+	PlcValueAdapter
 }
 
 func NewPlcDATEANDTIME(value time.Time) PlcDATEANDTIME {
@@ -36,6 +37,14 @@ func NewPlcDATEANDTIME(value time.Time) PlcDATEANDTIME {
 func (m PlcDATEANDTIME) IsTime() bool {
 	return true
 }
+
 func (m PlcDATEANDTIME) GetTime() time.Time {
 	return m.value
 }
+
+func (m PlcDATEANDTIME) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcDATEANDTIME"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/DINT.go b/sandbox/plc4go/internal/plc4go/model/values/DINT.go
index 80ff2cb..2405412 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/DINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/DINT.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcDINT struct {
 	value int32
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcDINT(value int32) PlcDINT {
@@ -123,3 +124,10 @@ func (m PlcDINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetInt32())
 }
+
+func (m PlcDINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcDINT"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/DWORD.go b/sandbox/plc4go/internal/plc4go/model/values/DWORD.go
index e50ad54..c01adab 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/DWORD.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/DWORD.go
@@ -18,9 +18,11 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcDWORD struct {
 	value uint32
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcDWORD(value uint32) PlcDWORD {
@@ -66,3 +68,10 @@ func (m PlcDWORD) GetBooleanArray() []bool {
 		m.value>>28&1 == 1, m.value>>29&1 == 1,
 		m.value>>30&1 == 1, m.value>>31&1 == 1}
 }
+
+func (m PlcDWORD) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcDWORD"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/INT.go b/sandbox/plc4go/internal/plc4go/model/values/INT.go
index 27faf7e..f9796c7 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/INT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/INT.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcINT struct {
 	value int16
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcINT(value int16) PlcINT {
@@ -116,3 +117,10 @@ func (m PlcINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetInt8())
 }
+
+func (m PlcINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcINT"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/LINT.go b/sandbox/plc4go/internal/plc4go/model/values/LINT.go
index 62d3f51..c7e13b0 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/LINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/LINT.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcLINT struct {
 	value int64
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcLINT(value int64) PlcLINT {
@@ -130,3 +131,10 @@ func (m PlcLINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetInt64())
 }
+
+func (m PlcLINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcLINT"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/LREAL.go b/sandbox/plc4go/internal/plc4go/model/values/LREAL.go
index 3ed9ffa..84914f3 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/LREAL.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/LREAL.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcLREAL struct {
 	value float64
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcLREAL(value float64) PlcLREAL {
@@ -142,3 +143,10 @@ func (m PlcLREAL) GetFloat32() float32 {
 func (m PlcLREAL) GetFloat64() float64 {
 	return m.value
 }
+
+func (m PlcLREAL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcLREAL"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/LTIME.go b/sandbox/plc4go/internal/plc4go/model/values/LTIME.go
index 0125f0f..738fd18 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/LTIME.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/LTIME.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "time"
+	"encoding/xml"
+	"time"
 )
 
 type PlcLTIME struct {
 	value uint64
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcLTIME(value uint64) PlcLTIME {
@@ -36,6 +37,14 @@ func NewPlcLTIME(value uint64) PlcLTIME {
 func (m PlcLTIME) IsDuration() bool {
 	return true
 }
+
 func (m PlcLTIME) GetDuration() time.Duration {
 	return time.Duration(m.value)
 }
+
+func (m PlcLTIME) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcLTIME"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/LWORD.go b/sandbox/plc4go/internal/plc4go/model/values/LWORD.go
index f43bb50..072c740 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/LWORD.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/LWORD.go
@@ -18,9 +18,11 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcLWORD struct {
 	value uint64
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcLWORD(value uint64) PlcLWORD {
@@ -82,3 +84,10 @@ func (m PlcLWORD) GetBooleanArray() []bool {
 		m.value>>60&1 == 1, m.value>>61&1 == 1,
 		m.value>>62&1 == 1, m.value>>63&1 == 1}
 }
+
+func (m PlcLWORD) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcLWORD"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/NULL.go b/sandbox/plc4go/internal/plc4go/model/values/NULL.go
index eb45ac6..df25ba4 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/NULL.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/NULL.go
@@ -18,10 +18,22 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcNULL struct {
-    PlcValueAdapter
+	PlcValueAdapter
 }
 
 func NewPlcNULL() PlcNULL {
 	return PlcNULL{}
 }
+
+func (m PlcNULL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcNULL"}}); err != nil {
+		return err
+	}
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcNULL"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/PlcList.go b/sandbox/plc4go/internal/plc4go/model/values/PlcList.go
index 0831e20..daec134 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/PlcList.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/PlcList.go
@@ -18,7 +18,10 @@
 //
 package values
 
-import api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+import (
+	"encoding/xml"
+	api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+)
 
 type PlcList struct {
 	values []api.PlcValue
@@ -46,3 +49,20 @@ func (m PlcList) GetIndex(i uint32) api.PlcValue {
 func (m PlcList) GetList() []api.PlcValue {
 	return m.values
 }
+
+func (m PlcList) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcList"}}); err != nil {
+		return err
+	}
+
+	for _, value := range m.values {
+		if err := e.EncodeElement(value, xml.StartElement{Name: xml.Name{Local: "-set-by-element-"}}); err != nil {
+			return err
+		}
+	}
+
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcList"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/PlcStruct.go b/sandbox/plc4go/internal/plc4go/model/values/PlcStruct.go
index 3266d67..6670969 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/PlcStruct.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/PlcStruct.go
@@ -18,7 +18,10 @@
 //
 package values
 
-import api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+import (
+	"encoding/xml"
+	api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
+)
 
 type PlcStruct struct {
 	values map[string]api.PlcValue
@@ -60,3 +63,20 @@ func (m PlcStruct) GetValue(key string) api.PlcValue {
 func (m PlcStruct) GetStruct() map[string]api.PlcValue {
 	return m.values
 }
+
+func (m PlcStruct) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: "PlcNULL"}}); err != nil {
+		return err
+	}
+
+	for fieldName, fieldValue := range m.values {
+		if err := e.EncodeElement(fieldValue, xml.StartElement{Name: xml.Name{Local: fieldName}}); err != nil {
+			return err
+		}
+	}
+
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: "PlcNULL"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/REAL.go b/sandbox/plc4go/internal/plc4go/model/values/REAL.go
index a662446..7c641a5 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/REAL.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/REAL.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcREAL struct {
 	value float32
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcREAL(value float32) PlcREAL {
@@ -136,3 +137,10 @@ func (m PlcREAL) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetFloat32())
 }
+
+func (m PlcREAL) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcREAL"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/SINT.go b/sandbox/plc4go/internal/plc4go/model/values/SINT.go
index ba57a7f..9bcbc67 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/SINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/SINT.go
@@ -18,9 +18,11 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcSINT struct {
 	value int8
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcSINT(value int8) PlcSINT {
@@ -105,3 +107,10 @@ func (m PlcSINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetInt8())
 }
+
+func (m PlcSINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcSINT"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/STRING.go b/sandbox/plc4go/internal/plc4go/model/values/STRING.go
index ab7f598..48fe1b4 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/STRING.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/STRING.go
@@ -18,9 +18,11 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcSTRING struct {
 	value string
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcSTRING(value string) PlcSTRING {
@@ -36,3 +38,10 @@ func (m PlcSTRING) IsString() bool {
 func (m PlcSTRING) GetString() string {
 	return m.value
 }
+
+func (m PlcSTRING) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcSTRING"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/TIME.go b/sandbox/plc4go/internal/plc4go/model/values/TIME.go
index 54c5ad4..38c54c6 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/TIME.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/TIME.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "time"
+	"encoding/xml"
+	"time"
 )
 
 type PlcTIME struct {
 	value uint32
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcTIME(value uint32) PlcTIME {
@@ -36,6 +37,14 @@ func NewPlcTIME(value uint32) PlcTIME {
 func (m PlcTIME) IsDuration() bool {
 	return true
 }
+
 func (m PlcTIME) GetDuration() time.Duration {
 	return time.Duration(m.value)
 }
+
+func (m PlcTIME) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcTIME"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/TIME_OF_DAY.go b/sandbox/plc4go/internal/plc4go/model/values/TIME_OF_DAY.go
index e9b73e6..1cd136f 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/TIME_OF_DAY.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/TIME_OF_DAY.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "time"
+	"encoding/xml"
+	"time"
 )
 
 type PlcTIMEOFDAY struct {
 	value time.Time
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcTIMEOFDAY(value time.Time) PlcTIMEOFDAY {
@@ -37,6 +38,14 @@ func NewPlcTIMEOFDAY(value time.Time) PlcTIMEOFDAY {
 func (m PlcTIMEOFDAY) IsTime() bool {
 	return true
 }
+
 func (m PlcTIMEOFDAY) GetTime() time.Time {
 	return m.value
 }
+
+func (m PlcTIMEOFDAY) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcTIMEOFDAY"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/UDINT.go b/sandbox/plc4go/internal/plc4go/model/values/UDINT.go
index 3104c18..6b78443 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/UDINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/UDINT.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcUDINT struct {
 	value uint32
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcUDINT(value uint32) PlcUDINT {
@@ -116,3 +117,10 @@ func (m PlcUDINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetUint32())
 }
+
+func (m PlcUDINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcUDINT"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/UINT.go b/sandbox/plc4go/internal/plc4go/model/values/UINT.go
index 8b59e47..3e35ff9 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/UINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/UINT.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcUINT struct {
 	value uint16
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcUINT(value uint16) PlcUINT {
@@ -102,3 +103,10 @@ func (m PlcUINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetUint16())
 }
+
+func (m PlcUINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcUINT"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/ULINT.go b/sandbox/plc4go/internal/plc4go/model/values/ULINT.go
index d4f9a63..2e7def5 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/ULINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/ULINT.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcULINT struct {
 	value uint64
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcULINT(value uint64) PlcULINT {
@@ -130,3 +131,10 @@ func (m PlcULINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetUint32())
 }
+
+func (m PlcULINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcULINT"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/USINT.go b/sandbox/plc4go/internal/plc4go/model/values/USINT.go
index 25a935e..9015c90 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/USINT.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/USINT.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "math"
+	"encoding/xml"
+	"math"
 )
 
 type PlcUSINT struct {
 	value uint8
-    PlcSimpleNumericValueAdapter
+	PlcSimpleNumericValueAdapter
 }
 
 func NewPlcUSINT(value uint8) PlcUSINT {
@@ -88,3 +89,10 @@ func (m PlcUSINT) GetFloat64() float64 {
 	//TODO: Check if this is ok
 	return float64(m.GetUint8())
 }
+
+func (m PlcUSINT) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcUSINT"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/WCHAR.go b/sandbox/plc4go/internal/plc4go/model/values/WCHAR.go
index f8e9186..4439062 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/WCHAR.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/WCHAR.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "unicode/utf16"
+	"encoding/xml"
+	"unicode/utf16"
 )
 
 type PlcWCHAR struct {
 	value []rune
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcWCHAR(value uint16) PlcWCHAR {
@@ -40,3 +41,10 @@ func (m PlcWCHAR) IsString() bool {
 func (m PlcWCHAR) GetString() string {
 	return string(m.value)
 }
+
+func (m PlcWCHAR) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcWCHAR"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/WORD.go b/sandbox/plc4go/internal/plc4go/model/values/WORD.go
index f53eef1..2e947c1 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/WORD.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/WORD.go
@@ -18,9 +18,11 @@
 //
 package values
 
+import "encoding/xml"
+
 type PlcWORD struct {
 	value uint16
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcWORD(value uint16) PlcWORD {
@@ -58,3 +60,10 @@ func (m PlcWORD) GetBooleanArray() []bool {
 		m.value>>12&1 == 1, m.value>>13&1 == 1,
 		m.value>>14&1 == 1, m.value>>15&1 == 1}
 }
+
+func (m PlcWORD) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcWORD"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/model/values/WSTRING.go b/sandbox/plc4go/internal/plc4go/model/values/WSTRING.go
index ed631de..f8e65c9 100644
--- a/sandbox/plc4go/internal/plc4go/model/values/WSTRING.go
+++ b/sandbox/plc4go/internal/plc4go/model/values/WSTRING.go
@@ -19,12 +19,13 @@
 package values
 
 import (
-    "unicode/utf16"
+	"encoding/xml"
+	"unicode/utf16"
 )
 
 type PlcWSTRING struct {
 	value []rune
-    PlcSimpleValueAdapter
+	PlcSimpleValueAdapter
 }
 
 func NewPlcWSTRING(value []uint16) PlcWSTRING {
@@ -40,3 +41,10 @@ func (m PlcWSTRING) IsString() bool {
 func (m PlcWSTRING) GetString() string {
 	return string(m.value)
 }
+
+func (m PlcWSTRING) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	if err := e.EncodeElement(m.value, xml.StartElement{Name: xml.Name{Local: "PlcWSTRING"}}); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/internal/plc4go/testutils/DriverTestRunner.go b/sandbox/plc4go/internal/plc4go/testutils/DriverTestRunner.go
index dc7cda3..aea4eda 100644
--- a/sandbox/plc4go/internal/plc4go/testutils/DriverTestRunner.go
+++ b/sandbox/plc4go/internal/plc4go/testutils/DriverTestRunner.go
@@ -19,415 +19,436 @@
 package testutils
 
 import (
-    "encoding/hex"
-    "errors"
-    "fmt"
-    "github.com/subchen/go-xmldom"
-    "os"
-    "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/test"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
-    api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
-    "strconv"
-    "testing"
-    "time"
+	"encoding/hex"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"github.com/subchen/go-xmldom"
+	"os"
+	"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/test"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go"
+	api "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/model"
+	"strconv"
+	"testing"
+	"time"
 )
 
 type DriverTestsuite struct {
-    name             string
-    driverName       string
-    driverParameters map[string]string
-    setupSteps       []TestStep
-    teardownSteps    []TestStep
-    testcases        []Testcase
+	name             string
+	driverName       string
+	driverParameters map[string]string
+	setupSteps       []TestStep
+	teardownSteps    []TestStep
+	testcases        []Testcase
 }
 
 func (m DriverTestsuite) Run(driverManager plc4go.PlcDriverManager, testcase Testcase) error {
-    // Get a connection
-    connectionChan := driverManager.GetConnection(m.driverName + ":test://hurz")
-    connectionResult := <-connectionChan
-
-    if connectionResult.Err != nil {
-        return errors.New("error getting a connection: " + connectionResult.Err.Error())
-    }
-
-    // Run the setup steps
-    for _, testStep := range m.setupSteps {
-        m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
-    }
-
-    // Run the actual scenario steps
-    for _, testStep := range testcase.steps {
-        m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
-    }
-
-    // Run the teardown steps
-    for _, testStep := range m.teardownSteps {
-        m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
-    }
-
-    return nil
+	// Get a connection
+	connectionChan := driverManager.GetConnection(m.driverName + ":test://hurz")
+	connectionResult := <-connectionChan
+
+	if connectionResult.Err != nil {
+		return errors.New("error getting a connection: " + connectionResult.Err.Error())
+	}
+
+	// Run the setup steps
+	for _, testStep := range m.setupSteps {
+		m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
+	}
+
+	// Run the actual scenario steps
+	for _, testStep := range testcase.steps {
+		m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
+	}
+
+	// Run the teardown steps
+	for _, testStep := range m.teardownSteps {
+		m.ExecuteStep(connectionResult.Connection, &testcase, testStep)
+	}
+
+	return nil
 }
 
 func (m DriverTestsuite) ExecuteStep(connection plc4go.PlcConnection, testcase *Testcase, step TestStep) error {
-    mc, ok := connection.(spi.TransportInstanceExposer)
-    if !ok {
-        return errors.New("couldn't access connections transport instance")
-    }
-    transportInstance := mc.GetTransportInstance()
-
-    switch step.stepType {
-    case StepType_API_REQUEST:
-        switch step.payload.Name {
-        case "TestReadRequest":
-            // Assemble a read-request according to the information in the test xml
-            rrb := connection.ReadRequestBuilder()
-            for _, fieldNode := range step.payload.GetChild("fields").GetChildren("field") {
-                fieldName := fieldNode.GetChild("name").Text
-                fieldAddress := fieldNode.GetChild("address").Text
-                rrb.AddItem(fieldName, fieldAddress)
-            }
-            readRequest, err := rrb.Build()
-            if err != nil {
-                return errors.New("Error creating read-request: " + err.Error())
-            }
-
-            // Execute the read-request and store the response-channel in the testcase.
-            if testcase.readRequestResultChannel != nil {
-                return errors.New("testcase read-request result channel already occupied")
-            }
-            testcase.readRequestResultChannel = readRequest.Execute()
-        case "TestWriteRequest":
-            wrb := connection.WriteRequestBuilder()
-            for _, fieldNode := range step.payload.GetChild("fields").GetChildren("field") {
-                fieldName := fieldNode.GetChild("name").Text
-                fieldAddress := fieldNode.GetChild("address").Text
-                fieldValue := fieldNode.GetChild("value").Text
-                wrb.AddItem(fieldName, fieldAddress, fieldValue)
-            }
-            writeRequest, err := wrb.Build()
-            if err != nil {
-                return errors.New("Error creating write-request: " + err.Error())
-            }
-            if testcase.writeRequestResultChannel != nil {
-                return errors.New("testcase write-request result channel already occupied")
-            }
-            testcase.writeRequestResultChannel = writeRequest.Execute()
-        }
-    case StepType_API_RESPONSE:
-        switch step.payload.Name {
-        case "DefaultPlcReadResponse":
-            if testcase.readRequestResultChannel == nil {
-                return errors.New("no read response expected")
-            }
-            readResponse := <- testcase.readRequestResultChannel
-            // TODO: Implement comparing of the results ...
-            fmt.Println(readResponse)
-        case "DefaultPlcWriteResponse":
-            if testcase.writeRequestResultChannel == nil {
-                return errors.New("no write response expected")
-            }
-            writeResponse := <- testcase.writeRequestResultChannel
-            // TODO: Implement comparing of the results ...
-            fmt.Println(writeResponse)
-        }
-    case StepType_OUTGOING_PLC_MESSAGE:
-        typeName := step.payload.Name
-        payloadString := step.payload.XML()
-
-        // Parse the xml into a real model
-        message, err := model.ModbusXmlParserHelper{}.Parse(typeName, payloadString)
-        if err != nil {
-            return errors.New("error parsing xml: " + err.Error())
-        }
-
-        // Serialize the model into bytes
-        ser, ok := message.(utils.Serializable)
-        if !ok {
-            return errors.New("error converting type into Serializable type: " + err.Error())
-        }
-        wb := utils.WriteBufferNew()
-        err = ser.Serialize(*wb)
-        if err != nil {
-            return errors.New("error serializing message: " + err.Error())
-        }
-        expectedRawOutput := wb.GetBytes()
-
-        // Read exactly this amount of bytes from the transport
-        rawOutput, err := transportInstance.Read(uint32(len(expectedRawOutput)))
-        if err != nil {
-            return errors.New("error getting bytes from transport: " + err.Error())
-        }
-
-        // Compare the bytes read with the ones we expect
-        for i := range expectedRawOutput {
-            if expectedRawOutput[i] != rawOutput[i] {
-                return errors.New("actual output doesn't match expected output")
-            }
-        }
-        // If there's a difference, parse the input and display it to simplify debugging
-    case StepType_OUTGOING_PLC_BYTES:
-        // Read exactly this amount of bytes from the transport
-        expectedRawInput, err := hex.DecodeString(step.payload.Text)
-        if err != nil {
-            return errors.New("error decoding hex-encoded byte data: " + err.Error())
-        }
-        rawInput, err := transportInstance.Read(uint32(len(expectedRawInput)))
-        if err != nil {
-            return errors.New("error getting bytes from transport: " + err.Error())
-        }
-
-        // Compare the bytes read with the ones we expect
-        for i := range expectedRawInput {
-            if expectedRawInput[i] != rawInput[i] {
-                return errors.New("actual output doesn't match expected output")
-            }
-        }
-        // If there's a difference, parse the input and display it to simplify debugging
-    case StepType_INCOMING_PLC_MESSAGE:
-        typeName := step.payload.Name
-        payloadString := step.payload.XML()
-
-        // Parse the xml into a real model
-        message, err := model.ModbusXmlParserHelper{}.Parse(typeName, payloadString)
-        if err != nil {
-            return errors.New("error parsing xml: " + err.Error())
-        }
-
-        // Serialize the model into bytes
-        ser, ok := message.(utils.Serializable)
-        if !ok {
-            return errors.New("error converting type into Serializable type: " + err.Error())
-        }
-        wb := utils.WriteBufferNew()
-        err = ser.Serialize(*wb)
-        if err != nil {
-            return errors.New("error serializing message: " + err.Error())
-        }
-
-        // Send these bytes to the transport
-        err = transportInstance.Write(wb.GetBytes())
-        if err != nil {
-            return errors.New("error writing data to transport: " + err.Error())
-        }
-    case StepType_INCOMING_PLC_BYTES:
-        // Get the raw hex-data.
-        rawInput, err := hex.DecodeString(step.payload.Text)
-        if err != nil {
-            return errors.New("error decoding hex-encoded byte data: " + err.Error())
-        }
-
-        // Send these bytes to the transport
-        err = transportInstance.Write(rawInput)
-        if err != nil {
-            return errors.New("error writing data to transport: " + err.Error())
-        }
-    case StepType_DELAY:
-        // Get the number of milliseconds
-        delay, err := strconv.Atoi(step.payload.Text)
-        if err != nil {
-            return errors.New("invalid delay format: " + err.Error())
-        }
-        // Sleep for that long
-        time.Sleep(time.Duration(delay))
-    case StepType_TERMINATE:
-        // Simply close the transport connection
-        err := transportInstance.Close()
-        if err != nil {
-            return errors.New("error closing transport: " + err.Error())
-        }
-    }
-    return nil
+	mc, ok := connection.(spi.TransportInstanceExposer)
+	if !ok {
+		return errors.New("couldn't access connections transport instance")
+	}
+	transportInstance := mc.GetTransportInstance()
+
+	switch step.stepType {
+	case StepType_API_REQUEST:
+		switch step.payload.Name {
+		case "TestReadRequest":
+			// Assemble a read-request according to the information in the test xml
+			rrb := connection.ReadRequestBuilder()
+			for _, fieldNode := range step.payload.GetChild("fields").GetChildren("field") {
+				fieldName := fieldNode.GetChild("name").Text
+				fieldAddress := fieldNode.GetChild("address").Text
+				rrb.AddItem(fieldName, fieldAddress)
+			}
+			readRequest, err := rrb.Build()
+			if err != nil {
+				return errors.New("Error creating read-request: " + err.Error())
+			}
+
+			// Execute the read-request and store the response-channel in the testcase.
+			if testcase.readRequestResultChannel != nil {
+				return errors.New("testcase read-request result channel already occupied")
+			}
+			testcase.readRequestResultChannel = readRequest.Execute()
+		case "TestWriteRequest":
+			wrb := connection.WriteRequestBuilder()
+			for _, fieldNode := range step.payload.GetChild("fields").GetChildren("field") {
+				fieldName := fieldNode.GetChild("name").Text
+				fieldAddress := fieldNode.GetChild("address").Text
+				fieldValue := fieldNode.GetChild("value").Text
+				wrb.AddItem(fieldName, fieldAddress, fieldValue)
+			}
+			writeRequest, err := wrb.Build()
+			if err != nil {
+				return errors.New("Error creating write-request: " + err.Error())
+			}
+			if testcase.writeRequestResultChannel != nil {
+				return errors.New("testcase write-request result channel already occupied")
+			}
+			testcase.writeRequestResultChannel = writeRequest.Execute()
+		}
+	case StepType_API_RESPONSE:
+		switch step.payload.Name {
+		case "PlcReadResponse":
+			if testcase.readRequestResultChannel == nil {
+				return errors.New("no read response expected")
+			}
+			readRequestResult := <-testcase.readRequestResultChannel
+			// Serialize the response to XML
+			actualResponse, err := xml.Marshal(readRequestResult.Response)
+			if err != nil {
+				return errors.New("error serializing response: " + err.Error())
+			}
+			// Get the reference XML
+			referenceSerialized := step.payload.XML()
+			// Compare the results
+			err = CompareResults(actualResponse, []byte(referenceSerialized))
+			if err != nil {
+				return errors.New("Error comparing the results: " + err.Error())
+			}
+		case "PlcWriteResponse":
+			if testcase.writeRequestResultChannel == nil {
+				return errors.New("no write response expected")
+			}
+			writeResponseResult := <-testcase.writeRequestResultChannel
+			// Serialize the response to XML
+			actualResponse, err := xml.Marshal(writeResponseResult.Response)
+			if err != nil {
+				return errors.New("error serializing response: " + err.Error())
+			}
+			// Get the reference XML
+			referenceSerialized := step.payload.XML()
+			// Compare the results
+			err = CompareResults(actualResponse, []byte(referenceSerialized))
+			if err != nil {
+				return errors.New("Error comparing the results: " + err.Error())
+			}
+		}
+	case StepType_OUTGOING_PLC_MESSAGE:
+		typeName := step.payload.Name
+		payloadString := step.payload.XML()
+
+		// Parse the xml into a real model
+		message, err := model.ModbusXmlParserHelper{}.Parse(typeName, payloadString)
+		if err != nil {
+			return errors.New("error parsing xml: " + err.Error())
+		}
+
+		// Serialize the model into bytes
+		ser, ok := message.(utils.Serializable)
+		if !ok {
+			return errors.New("error converting type into Serializable type: " + err.Error())
+		}
+		wb := utils.WriteBufferNew()
+		err = ser.Serialize(*wb)
+		if err != nil {
+			return errors.New("error serializing message: " + err.Error())
+		}
+		expectedRawOutput := wb.GetBytes()
+
+		// Read exactly this amount of bytes from the transport
+		rawOutput, err := transportInstance.Read(uint32(len(expectedRawOutput)))
+		if err != nil {
+			return errors.New("error getting bytes from transport: " + err.Error())
+		}
+
+		// Compare the bytes read with the ones we expect
+		for i := range expectedRawOutput {
+			if expectedRawOutput[i] != rawOutput[i] {
+				return errors.New("actual output doesn't match expected output")
+			}
+		}
+		// If there's a difference, parse the input and display it to simplify debugging
+	case StepType_OUTGOING_PLC_BYTES:
+		// Read exactly this amount of bytes from the transport
+		expectedRawInput, err := hex.DecodeString(step.payload.Text)
+		if err != nil {
+			return errors.New("error decoding hex-encoded byte data: " + err.Error())
+		}
+		rawInput, err := transportInstance.Read(uint32(len(expectedRawInput)))
+		if err != nil {
+			return errors.New("error getting bytes from transport: " + err.Error())
+		}
+
+		// Compare the bytes read with the ones we expect
+		for i := range expectedRawInput {
+			if expectedRawInput[i] != rawInput[i] {
+				return errors.New("actual output doesn't match expected output")
+			}
+		}
+		// If there's a difference, parse the input and display it to simplify debugging
+	case StepType_INCOMING_PLC_MESSAGE:
+		typeName := step.payload.Name
+		payloadString := step.payload.XML()
+
+		// Parse the xml into a real model
+		message, err := model.ModbusXmlParserHelper{}.Parse(typeName, payloadString)
+		if err != nil {
+			return errors.New("error parsing xml: " + err.Error())
+		}
+
+		// Serialize the model into bytes
+		ser, ok := message.(utils.Serializable)
+		if !ok {
+			return errors.New("error converting type into Serializable type: " + err.Error())
+		}
+		wb := utils.WriteBufferNew()
+		err = ser.Serialize(*wb)
+		if err != nil {
+			return errors.New("error serializing message: " + err.Error())
+		}
+
+		// Send these bytes to the transport
+		err = transportInstance.Write(wb.GetBytes())
+		if err != nil {
+			return errors.New("error writing data to transport: " + err.Error())
+		}
+	case StepType_INCOMING_PLC_BYTES:
+		// Get the raw hex-data.
+		rawInput, err := hex.DecodeString(step.payload.Text)
+		if err != nil {
+			return errors.New("error decoding hex-encoded byte data: " + err.Error())
+		}
+
+		// Send these bytes to the transport
+		err = transportInstance.Write(rawInput)
+		if err != nil {
+			return errors.New("error writing data to transport: " + err.Error())
+		}
+	case StepType_DELAY:
+		// Get the number of milliseconds
+		delay, err := strconv.Atoi(step.payload.Text)
+		if err != nil {
+			return errors.New("invalid delay format: " + err.Error())
+		}
+		// Sleep for that long
+		time.Sleep(time.Duration(delay))
+	case StepType_TERMINATE:
+		// Simply close the transport connection
+		err := transportInstance.Close()
+		if err != nil {
+			return errors.New("error closing transport: " + err.Error())
+		}
+	}
+	return nil
 }
 
-func (m DriverTestsuite) ParseXml(referenceXml *xmldom.Node, parserArguments []string)  {
-    normalizeXml(referenceXml)
-    //referenceSerialized := referenceXml.FirstChild().XML()
+func (m DriverTestsuite) ParseXml(referenceXml *xmldom.Node, parserArguments []string) {
+	normalizeXml(referenceXml)
+	//referenceSerialized := referenceXml.FirstChild().XML()
 }
 
 type Testcase struct {
-    name  string
-    steps                    []TestStep
-    readRequestResultChannel <-chan api.PlcReadRequestResult
-    writeRequestResultChannel <-chan api.PlcWriteRequestResult
+	name                      string
+	steps                     []TestStep
+	readRequestResultChannel  <-chan api.PlcReadRequestResult
+	writeRequestResultChannel <-chan api.PlcWriteRequestResult
 }
 
 type TestStep struct {
-    name            string
-    stepType        StepType
-    parserArguments []string
-    payload         xmldom.Node
+	name            string
+	stepType        StepType
+	parserArguments []string
+	payload         xmldom.Node
 }
 
 type StepType uint8
 
 const (
-    StepType_OUTGOING_PLC_MESSAGE StepType = 0x01
-    StepType_OUTGOING_PLC_BYTES   StepType = 0x02
-    StepType_INCOMING_PLC_MESSAGE StepType = 0x03
-    StepType_INCOMING_PLC_BYTES   StepType = 0x04
-    StepType_API_REQUEST          StepType = 0x05
-    StepType_API_RESPONSE         StepType = 0x06
-    StepType_DELAY                StepType = 0x07
-    StepType_TERMINATE            StepType = 0x08
+	StepType_OUTGOING_PLC_MESSAGE StepType = 0x01
+	StepType_OUTGOING_PLC_BYTES   StepType = 0x02
+	StepType_INCOMING_PLC_MESSAGE StepType = 0x03
+	StepType_INCOMING_PLC_BYTES   StepType = 0x04
+	StepType_API_REQUEST          StepType = 0x05
+	StepType_API_RESPONSE         StepType = 0x06
+	StepType_DELAY                StepType = 0x07
+	StepType_TERMINATE            StepType = 0x08
 )
 
 func RunDriverTestsuite(t *testing.T, driver plc4go.PlcDriver, testPath string) {
-    // Read the test-specification as XML file
-    rootNode, err := ParseDriverTestsuiteXml(testPath)
-    if err != nil {
-        t.Error(err.Error())
-        t.Fail()
-        return
-    }
-
-    // Parse the contents of the test-specification
-    testsuite, err := ParseDriverTestsuite(*rootNode)
-    if err != nil {
-        t.Error(err.Error())
-        t.Fail()
-        return
-    }
-
-    // Initialize the driver manager
-    driverManager := plc4go.NewPlcDriverManager()
-    driverManager.RegisterTransport(test.NewTestTransport())
-    driverManager.RegisterDriver(driver)
-
-    for _, testcase := range testsuite.testcases {
-        testsuite.Run(driverManager, testcase)
-    }
-    // Execute the tests in the testsuite
-    fmt.Printf(testsuite.name)
+	// Read the test-specification as XML file
+	rootNode, err := ParseDriverTestsuiteXml(testPath)
+	if err != nil {
+		t.Error(err.Error())
+		t.Fail()
+		return
+	}
+
+	// Parse the contents of the test-specification
+	testsuite, err := ParseDriverTestsuite(*rootNode)
+	if err != nil {
+		t.Error(err.Error())
+		t.Fail()
+		return
+	}
+
+	// Initialize the driver manager
+	driverManager := plc4go.NewPlcDriverManager()
+	driverManager.RegisterTransport(test.NewTestTransport())
+	driverManager.RegisterDriver(driver)
+
+	for _, testcase := range testsuite.testcases {
+		testsuite.Run(driverManager, testcase)
+	}
+	// Execute the tests in the testsuite
+	fmt.Printf(testsuite.name)
 }
 
 func ParseDriverTestsuiteXml(testPath string) (*xmldom.Node, error) {
-    // Get the current working directory
-    path, err := os.Getwd()
-    if err != nil {
-        return nil, err
-    }
-
-    // Check if the test-file is available
-    info, err := os.Stat(path + "/../../../../" + testPath)
-    if os.IsNotExist(err) {
-        return nil, errors.New("test-File doesn't exist")
-    }
-    if info.IsDir() {
-        return nil, errors.New("test-file refers to a directory")
-    }
-
-    // Open a reader for this file
-    dat, err := os.Open(path + "/../../../../" + testPath)
-    if err != nil {
-        return nil, errors.New("error opening file")
-    }
-
-    // Read the xml
-    node := xmldom.Must(xmldom.Parse(dat)).Root
-    return node, nil
+	// Get the current working directory
+	path, err := os.Getwd()
+	if err != nil {
+		return nil, err
+	}
+
+	// Check if the test-file is available
+	info, err := os.Stat(path + "/../../../../" + testPath)
+	if os.IsNotExist(err) {
+		return nil, errors.New("test-File doesn't exist")
+	}
+	if info.IsDir() {
+		return nil, errors.New("test-file refers to a directory")
+	}
+
+	// Open a reader for this file
+	dat, err := os.Open(path + "/../../../../" + testPath)
+	if err != nil {
+		return nil, errors.New("error opening file")
+	}
+
+	// Read the xml
+	node := xmldom.Must(xmldom.Parse(dat)).Root
+	return node, nil
 }
 
 func ParseDriverTestsuite(node xmldom.Node) (*DriverTestsuite, error) {
-    if node.Name != "driver-testsuite" {
-        return nil, errors.New("invalid document structure")
-    }
-    var testsuiteName string
-    var driverName string
-    var setupSteps []TestStep
-    var teardownSteps []TestStep
-    var testcases []Testcase
-    for _, childPtr := range node.Children {
-        child := *childPtr
-        if child.Name == "name" {
-            testsuiteName = child.Text
-        } else if child.Name == "driver-name" {
-            driverName = child.Text
-        } else if child.Name == "setup" {
-            steps, err := ParseDriverTestsuiteSteps(child)
-            if err != nil {
-                return nil, errors.New("error parsing setup steps")
-            }
-            setupSteps = steps
-        } else if child.Name == "teardown" {
-            steps, err := ParseDriverTestsuiteSteps(child)
-            if err != nil {
-                return nil, errors.New("error teardown setup steps")
-            }
-            teardownSteps = steps
-        } else if child.Name == "testcase" {
-            testcaseName := child.FindOneByName("name").Text
-            stepsNode := child.FindOneByName("steps")
-            steps, err := ParseDriverTestsuiteSteps(*stepsNode)
-            if err != nil {
-                return nil, errors.New("error parsing testcase " + testcaseName + ": " + err.Error())
-            }
-            testcase := Testcase{
-                name:  testcaseName,
-                steps: steps,
-            }
-            testcases = append(testcases, testcase)
-        } else {
-            return nil, errors.New("invalid document structure")
-        }
-    }
-
-    return &DriverTestsuite{
-        name:          testsuiteName,
-        driverName:    driverName,
-        setupSteps:    setupSteps,
-        teardownSteps: teardownSteps,
-        testcases:     testcases,
-    }, nil
+	if node.Name != "driver-testsuite" {
+		return nil, errors.New("invalid document structure")
+	}
+	var testsuiteName string
+	var driverName string
+	var setupSteps []TestStep
+	var teardownSteps []TestStep
+	var testcases []Testcase
+	for _, childPtr := range node.Children {
+		child := *childPtr
+		if child.Name == "name" {
+			testsuiteName = child.Text
+		} else if child.Name == "driver-name" {
+			driverName = child.Text
+		} else if child.Name == "setup" {
+			steps, err := ParseDriverTestsuiteSteps(child)
+			if err != nil {
+				return nil, errors.New("error parsing setup steps")
+			}
+			setupSteps = steps
+		} else if child.Name == "teardown" {
+			steps, err := ParseDriverTestsuiteSteps(child)
+			if err != nil {
+				return nil, errors.New("error teardown setup steps")
+			}
+			teardownSteps = steps
+		} else if child.Name == "testcase" {
+			testcaseName := child.FindOneByName("name").Text
+			stepsNode := child.FindOneByName("steps")
+			steps, err := ParseDriverTestsuiteSteps(*stepsNode)
+			if err != nil {
+				return nil, errors.New("error parsing testcase " + testcaseName + ": " + err.Error())
+			}
+			testcase := Testcase{
+				name:  testcaseName,
+				steps: steps,
+			}
+			testcases = append(testcases, testcase)
+		} else {
+			return nil, errors.New("invalid document structure")
+		}
+	}
+
+	return &DriverTestsuite{
+		name:          testsuiteName,
+		driverName:    driverName,
+		setupSteps:    setupSteps,
+		teardownSteps: teardownSteps,
+		testcases:     testcases,
+	}, nil
 }
 
 func ParseDriverTestsuiteSteps(node xmldom.Node) ([]TestStep, error) {
-    var testSteps []TestStep
-    for _, step := range node.Children {
-        name := step.GetAttributeValue("name")
-        var stepType StepType
-        switch step.Name {
-        case "api-request":
-            stepType = StepType_API_REQUEST
-        case "api-response":
-            stepType = StepType_API_RESPONSE
-        case "outgoing-plc-message":
-            stepType = StepType_OUTGOING_PLC_MESSAGE
-        case "incoming-plc-message":
-            stepType = StepType_INCOMING_PLC_MESSAGE
-        case "outgoing-plc-bytes":
-            stepType = StepType_OUTGOING_PLC_BYTES
-        case "incoming-plc-bytes":
-            stepType = StepType_INCOMING_PLC_BYTES
-        case "delay":
-            stepType = StepType_DELAY
-        case "terminate":
-            stepType = StepType_TERMINATE
-        }
-        var parserArguments []string
-        var payload *xmldom.Node
-        for _, childNode := range step.Children {
-            if childNode.Name == "parser-arguments" {
-                for _, parserArgumentNode := range childNode.Children {
-                    parserArguments = append(parserArguments, parserArgumentNode.Text)
-                }
-            } else if payload == nil {
-                payload = childNode
-            } else {
-                return nil, errors.New("test step can only contain a single payload element")
-            }
-        }
-        if payload == nil {
-            return nil, errors.New("missing payload element")
-        }
-        testSteps = append(testSteps, TestStep{
-            name:            name,
-            stepType:        stepType,
-            parserArguments: parserArguments,
-            payload:         *payload,
-        })
-    }
-    return testSteps, nil
+	var testSteps []TestStep
+	for _, step := range node.Children {
+		name := step.GetAttributeValue("name")
+		var stepType StepType
+		switch step.Name {
+		case "api-request":
+			stepType = StepType_API_REQUEST
+		case "api-response":
+			stepType = StepType_API_RESPONSE
+		case "outgoing-plc-message":
+			stepType = StepType_OUTGOING_PLC_MESSAGE
+		case "incoming-plc-message":
+			stepType = StepType_INCOMING_PLC_MESSAGE
+		case "outgoing-plc-bytes":
+			stepType = StepType_OUTGOING_PLC_BYTES
+		case "incoming-plc-bytes":
+			stepType = StepType_INCOMING_PLC_BYTES
+		case "delay":
+			stepType = StepType_DELAY
+		case "terminate":
+			stepType = StepType_TERMINATE
+		}
+		var parserArguments []string
+		var payload *xmldom.Node
+		for _, childNode := range step.Children {
+			if childNode.Name == "parser-arguments" {
+				for _, parserArgumentNode := range childNode.Children {
+					parserArguments = append(parserArguments, parserArgumentNode.Text)
+				}
+			} else if payload == nil {
+				payload = childNode
+			} else {
+				return nil, errors.New("test step can only contain a single payload element")
+			}
+		}
+		if payload == nil {
+			return nil, errors.New("missing payload element")
+		}
+		testSteps = append(testSteps, TestStep{
+			name:            name,
+			stepType:        stepType,
+			parserArguments: parserArguments,
+			payload:         *payload,
+		})
+	}
+	return testSteps, nil
 }
diff --git a/sandbox/plc4go/internal/plc4go/testutils/ParserSerializerTestRunner.go b/sandbox/plc4go/internal/plc4go/testutils/ParserSerializerTestRunner.go
index 20de594..8f0ba09 100644
--- a/sandbox/plc4go/internal/plc4go/testutils/ParserSerializerTestRunner.go
+++ b/sandbox/plc4go/internal/plc4go/testutils/ParserSerializerTestRunner.go
@@ -19,185 +19,156 @@
 package testutils
 
 import (
-    "encoding/hex"
-    "encoding/xml"
-    "errors"
-    "fmt"
-    "github.com/ajankovic/xdiff"
-    "github.com/ajankovic/xdiff/parser"
-    "github.com/subchen/go-xmldom"
+	"encoding/hex"
+	"encoding/xml"
+	"fmt"
+	"github.com/subchen/go-xmldom"
 	"os"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
-    "plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
-    "strconv"
-    "strings"
-    "testing"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/modbus/readwrite/model"
+	"plc4x.apache.org/plc4go-modbus-driver/v0/internal/plc4go/utils"
+	"strconv"
+	"strings"
+	"testing"
 )
 
 func RunParserSerializerTestsuite(t *testing.T, testPath string) {
-    // Get the current working directory
-    path, err := os.Getwd()
-    if err != nil {
-        t.Error(err)
-    }
+	// Get the current working directory
+	path, err := os.Getwd()
+	if err != nil {
+		t.Error(err)
+	}
 
-    // Check if the test-file is available
-    info, err := os.Stat(path + "/../../../../" + testPath)
-    if os.IsNotExist(err) {
-        t.Error("Test-File doesn't exist")
-    }
-    if info.IsDir() {
-        t.Error("Test-File refers to a directory")
-    }
+	// Check if the test-file is available
+	info, err := os.Stat(path + "/../../../../" + testPath)
+	if os.IsNotExist(err) {
+		t.Error("Test-File doesn't exist")
+	}
+	if info.IsDir() {
+		t.Error("Test-File refers to a directory")
+	}
 
-    // Open a reader for this file
-    dat, err := os.Open(path + "/../../../../" + testPath)
-    if err != nil {
-        t.Error("Error opening file")
-    }
+	// Open a reader for this file
+	dat, err := os.Open(path + "/../../../../" + testPath)
+	if err != nil {
+		t.Error("Error opening file")
+	}
 
-    // Read the xml
-    node := xmldom.Must(xmldom.Parse(dat)).Root
+	// Read the xml
+	node := xmldom.Must(xmldom.Parse(dat)).Root
 
-    if node.Name != "testsuite" {
-        t.Error("Invalid document structure")
-    }
-    var testsuiteName string
-    for _, childPtr := range node.Children {
-        curFailed := false
-        child := *childPtr
-        if child.Name == "name" {
-            testsuiteName = child.Text
-        } else if child.Name != "testcase" {
-            t.Error("Invalid document structure")
-            curFailed = true
-        } else {
-            t.Logf("running testsuite: %s test: %s", testsuiteName, (*(child.FindOneByName("name"))).Text)
-            rawInputText := (*(child.FindOneByName("raw"))).Text
-            rootType := (*(child.FindOneByName("root-type"))).Text
-            parserArgumentsXml := child.FindOneByName("parser-arguments")
-            var parserArguments []string
-            if parserArgumentsXml != nil {
-                for _, parserArgumentXml := range parserArgumentsXml.Children {
-                    parserArguments = append(parserArguments, parserArgumentXml.Text)
-                }
-            }
-            referenceXml := child.FindOneByName("xml")
-            normalizeXml(referenceXml)
-            referenceSerialized := referenceXml.FirstChild().XML()
+	if node.Name != "testsuite" {
+		t.Error("Invalid document structure")
+	}
+	var testsuiteName string
+	for _, childPtr := range node.Children {
+		curFailed := false
+		child := *childPtr
+		if child.Name == "name" {
+			testsuiteName = child.Text
+		} else if child.Name != "testcase" {
+			t.Error("Invalid document structure")
+			curFailed = true
+		} else {
+			t.Logf("running testsuite: %s test: %s", testsuiteName, (*(child.FindOneByName("name"))).Text)
+			rawInputText := (*(child.FindOneByName("raw"))).Text
+			rootType := (*(child.FindOneByName("root-type"))).Text
+			parserArgumentsXml := child.FindOneByName("parser-arguments")
+			var parserArguments []string
+			if parserArgumentsXml != nil {
+				for _, parserArgumentXml := range parserArgumentsXml.Children {
+					parserArguments = append(parserArguments, parserArgumentXml.Text)
+				}
+			}
+			referenceXml := child.FindOneByName("xml")
+			normalizeXml(referenceXml)
+			referenceSerialized := referenceXml.FirstChild().XML()
 
-            // Get the raw input by decoding the hex-encoded binary input
-            rawInput, err := hex.DecodeString(rawInputText)
-            if err != nil {
-                t.Errorf("Error decoding test input")
-                t.Fail()
-                curFailed = true
-            }
-            readBuffer := utils.ReadBufferNew(rawInput)
+			// Get the raw input by decoding the hex-encoded binary input
+			rawInput, err := hex.DecodeString(rawInputText)
+			if err != nil {
+				t.Errorf("Error decoding test input")
+				t.Fail()
+				curFailed = true
+			}
+			readBuffer := utils.ReadBufferNew(rawInput)
 
-            // Parse the input according to the settings of the testcase
-            helper := new(model.ModbusParserHelper)
-            msg, err := helper.Parse(rootType, parserArguments, readBuffer)
-            if err != nil {
-                t.Error("Error parsing input data: " + err.Error())
-                t.Fail()
-                curFailed = true
-            }
+			// Parse the input according to the settings of the testcase
+			helper := new(model.ModbusParserHelper)
+			msg, err := helper.Parse(rootType, parserArguments, readBuffer)
+			if err != nil {
+				t.Error("Error parsing input data: " + err.Error())
+				t.Fail()
+				curFailed = true
+			}
 
-            // Serialize the parsed object to XML
-            actualSerialized, err := xml.Marshal(msg)
-            if err != nil {
-                t.Error("Error serializing the actual message: " + err.Error())
-                t.Fail()
-                curFailed = true
-            }
+			// Serialize the parsed object to XML
+			actualSerialized, err := xml.Marshal(msg)
+			if err != nil {
+				t.Error("Error serializing the actual message: " + err.Error())
+				t.Fail()
+				curFailed = true
+			}
 
-            // Compare the actual and the expected xml
-            err = compareResults(actualSerialized, []byte(referenceSerialized))
-            if err != nil {
-                t.Error("Error comparing the results: " + err.Error())
-                t.Fail()
-                curFailed = true
-            }
+			// Compare the actual and the expected xml
+			err = CompareResults(actualSerialized, []byte(referenceSerialized))
+			if err != nil {
+				t.Error("Error comparing the results: " + err.Error())
+				t.Fail()
+				curFailed = true
+			}
 
-            // If all was ok, serialize the object again
-            s, ok := msg.(utils.Serializable)
-            if !ok {
-                t.Error("Couldn't cast message to Serializable")
-                t.Fail()
-                curFailed = true
-            }
-            writeBuffer := utils.WriteBufferNew()
-            err = s.Serialize(*writeBuffer)
-            if !ok {
-                t.Error("Couldn't serialize message back to byte array")
-                t.Fail()
-                curFailed = true
-            }
+			// If all was ok, serialize the object again
+			s, ok := msg.(utils.Serializable)
+			if !ok {
+				t.Error("Couldn't cast message to Serializable")
+				t.Fail()
+				curFailed = true
+			}
+			writeBuffer := utils.WriteBufferNew()
+			err = s.Serialize(*writeBuffer)
+			if !ok {
+				t.Error("Couldn't serialize message back to byte array")
+				t.Fail()
+				curFailed = true
+			}
 
-            // Check if the output matches in size and content
-            rawOutput := writeBuffer.GetBytes()
-            if len(rawInput) != len(rawOutput) {
-                t.Error("Couldn't serialize message back to byte array")
-                t.Fail()
-                curFailed = true
-            }
-            for i, val := range rawInput {
-                if rawOutput[i] != val {
-                    t.Error("Raw output doesn't match input at position: " + strconv.Itoa(i))
-                    t.Fail()
-                    curFailed = true
-                }
-            }
+			// Check if the output matches in size and content
+			rawOutput := writeBuffer.GetBytes()
+			if len(rawInput) != len(rawOutput) {
+				t.Error("Couldn't serialize message back to byte array")
+				t.Fail()
+				curFailed = true
+			}
+			for i, val := range rawInput {
+				if rawOutput[i] != val {
+					t.Error("Raw output doesn't match input at position: " + strconv.Itoa(i))
+					t.Fail()
+					curFailed = true
+				}
+			}
 
-            if curFailed {
-                // All worked
-                t.Logf("FAILED")
-            } else {
-                // All worked
-                t.Logf("SUCCESS")
-            }
-        }
-    }
-    fmt.Printf("name = %v\n", node.Name)
+			if curFailed {
+				// All worked
+				t.Logf("FAILED")
+			} else {
+				// All worked
+				t.Logf("SUCCESS")
+			}
+		}
+	}
+	fmt.Printf("name = %v\n", node.Name)
 }
 
 // Mainly remove linebreaks from text content.
 func normalizeXml(input *xmldom.Node) {
-    if len(input.Children) > 0 {
-        for _, child := range input.Children {
-            normalizeXml(child)
-        }
-    }
-    if len(input.Text) > 0 {
-        if strings.Contains(input.Text, "\n") {
-            input.Text = strings.Replace(input.Text, "\n", "", -1)
-        }
-    }
+	if len(input.Children) > 0 {
+		for _, child := range input.Children {
+			normalizeXml(child)
+		}
+	}
+	if len(input.Text) > 0 {
+		if strings.Contains(input.Text, "\n") {
+			input.Text = strings.Replace(input.Text, "\n", "", -1)
+		}
+	}
 }
-
-func compareResults(actualString []byte, referenceString []byte) error {
-    // Now parse the xml strings of the actual and the reference in xdiff's dom
-    p := parser.New()
-    actual, err := p.ParseBytes(actualString)
-    if err != nil {
-        return errors.New("Error parsing actual input: " + err.Error())
-    }
-    reference, err := p.ParseBytes(referenceString)
-    if err != nil {
-        return errors.New("Error parsing reference input: " + err.Error())
-    }
-    // Use XDiff to actually do the comparison
-    diff, err := xdiff.Compare(actual, reference)
-    if err != nil {
-        return errors.New("Error comparing xml trees: " + err.Error())
-    }
-    if diff != nil {
-        enc := xdiff.NewTextEncoder(os.Stdout)
-        if err := enc.Encode(diff); err != nil {
-            return errors.New("Error outputting results: " + err.Error())
-        }
-        return errors.New("there were differences")
-    }
-    return nil
-}
\ No newline at end of file
diff --git a/sandbox/plc4go/internal/plc4go/testutils/TestUtils.go b/sandbox/plc4go/internal/plc4go/testutils/TestUtils.go
new file mode 100644
index 0000000..a3e21ff
--- /dev/null
+++ b/sandbox/plc4go/internal/plc4go/testutils/TestUtils.go
@@ -0,0 +1,52 @@
+//
+// 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 testutils
+
+import (
+	"errors"
+	"github.com/ajankovic/xdiff"
+	"github.com/ajankovic/xdiff/parser"
+	"os"
+)
+
+func CompareResults(actualString []byte, referenceString []byte) error {
+	// Now parse the xml strings of the actual and the reference in xdiff's dom
+	p := parser.New()
+	actual, err := p.ParseBytes(actualString)
+	if err != nil {
+		return errors.New("Error parsing actual input: " + err.Error())
+	}
+	reference, err := p.ParseBytes(referenceString)
+	if err != nil {
+		return errors.New("Error parsing reference input: " + err.Error())
+	}
+	// Use XDiff to actually do the comparison
+	diff, err := xdiff.Compare(actual, reference)
+	if err != nil {
+		return errors.New("Error comparing xml trees: " + err.Error())
+	}
+	if diff != nil {
+		enc := xdiff.NewTextEncoder(os.Stdout)
+		if err := enc.Encode(diff); err != nil {
+			return errors.New("Error outputting results: " + err.Error())
+		}
+		return errors.New("there were differences")
+	}
+	return nil
+}
diff --git a/sandbox/plc4go/pkg/plc4go/model/plc_read_response.go b/sandbox/plc4go/pkg/plc4go/model/plc_read_response.go
index f8ed246..b265766 100644
--- a/sandbox/plc4go/pkg/plc4go/model/plc_read_response.go
+++ b/sandbox/plc4go/pkg/plc4go/model/plc_read_response.go
@@ -21,8 +21,8 @@ package model
 import "plc4x.apache.org/plc4go-modbus-driver/v0/pkg/plc4go/values"
 
 type PlcReadResponse interface {
-
-    GetValue(name string) values.PlcValue
-    PlcResponse
-
+	GetRequest() PlcReadRequest
+	GetResponseCode(name string) PlcResponseCode
+	GetValue(name string) values.PlcValue
+	PlcResponse
 }
diff --git a/sandbox/plc4go/pkg/plc4go/model/plc_response_code.go b/sandbox/plc4go/pkg/plc4go/model/plc_response_code.go
new file mode 100644
index 0000000..be5ec40
--- /dev/null
+++ b/sandbox/plc4go/pkg/plc4go/model/plc_response_code.go
@@ -0,0 +1,63 @@
+//
+// 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 model
+
+type PlcResponseCode uint8
+
+const (
+	PlcResponseCode_OK               PlcResponseCode = 0x01
+	PlcResponseCode_NOT_FOUND        PlcResponseCode = 0x02
+	PlcResponseCode_ACCESS_DENIED    PlcResponseCode = 0x03
+	PlcResponseCode_INVALID_ADDRESS  PlcResponseCode = 0x04
+	PlcResponseCode_INVALID_DATATYPE PlcResponseCode = 0x05
+	PlcResponseCode_INVALID_DATA     PlcResponseCode = 0x06
+	PlcResponseCode_INTERNAL_ERROR   PlcResponseCode = 0x07
+	PlcResponseCode_REMOTE_BUSY      PlcResponseCode = 0x08
+	PlcResponseCode_REMOTE_ERROR     PlcResponseCode = 0x09
+	PlcResponseCode_UNSUPPORTED      PlcResponseCode = 0x010
+	PlcResponseCode_RESPONSE_PENDING PlcResponseCode = 0x11
+)
+
+func (m PlcResponseCode) GetName() string {
+	switch m {
+	case PlcResponseCode_OK:
+		return "OK"
+	case PlcResponseCode_NOT_FOUND:
+		return "NOT_FOUND"
+	case PlcResponseCode_ACCESS_DENIED:
+		return "ACCESS_DENIED"
+	case PlcResponseCode_INVALID_ADDRESS:
+		return "INVALID_ADDRESS"
+	case PlcResponseCode_INVALID_DATATYPE:
+		return "INVALID_DATATYPE"
+	case PlcResponseCode_INVALID_DATA:
+		return "INVALID_DATA"
+	case PlcResponseCode_INTERNAL_ERROR:
+		return "INTERNAL_ERROR"
+	case PlcResponseCode_REMOTE_BUSY:
+		return "REMOTE_BUSY"
+	case PlcResponseCode_REMOTE_ERROR:
+		return "REMOTE_ERROR"
+	case PlcResponseCode_UNSUPPORTED:
+		return "UNSUPPORTED"
+	case PlcResponseCode_RESPONSE_PENDING:
+		return "RESPONSE_PENDING"
+	}
+	return ""
+}