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 2022/08/10 11:29:22 UTC

[plc4x] 05/07: chore(ads): Extended the PLC4J API with a first draft of a Discovery and Browse-API

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

cdutz pushed a commit to branch tmp/ads-symbol-discovery
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 19acbb9ed02c8783f403ce2d4373050667cb00c9
Author: christoferdutz <ch...@c-ware.de>
AuthorDate: Wed Aug 10 08:49:33 2022 +0200

    chore(ads): Extended the PLC4J API with a first draft of a Discovery and Browse-API
---
 .../testing/protocols/ads/DriverTestsuite.xml      |   76 +-
 .../hello_world_plc4go_bacnet_discovery.go         |    2 +-
 .../discovery/hello_world_plc4go_knx_discovery.go  |    2 +-
 plc4go/internal/bacnetip/Discoverer.go             |    4 +-
 plc4go/internal/bacnetip/Driver.go                 |    2 +-
 plc4go/internal/knxnetip/Discoverer.go             |    2 +-
 plc4go/internal/knxnetip/Driver.go                 |    2 +-
 plc4go/internal/simulated/Driver.go                |    2 +-
 plc4go/internal/simulated/Driver_test.go           |    2 +-
 plc4go/internal/spi/PlcDiscoverer.go               |    2 +-
 plc4go/internal/spi/default/DefaultDriver.go       |    2 +-
 .../internal/spi/model/DefaultPlcDiscoveryEvent.go |   10 +-
 plc4go/pkg/api/driver.go                           |    2 +-
 plc4go/pkg/api/driverManager.go                    |    4 +-
 plc4go/pkg/api/model/plc_discovery.go              |    5 +-
 plc4go/protocols/ads/readwrite/ParserHelper.go     |    8 +
 plc4go/protocols/ads/readwrite/XmlParserHelper.go  |    8 +
 .../ads/readwrite/model/AdsDataTypeArrayInfo.go    |  210 ++
 .../ads/readwrite/model/AdsDataTypeTableEntry.go   |  724 +++++++
 .../model/AdsSignificantGroupAddresses.go          |  137 ++
 .../ads/readwrite/model/AdsSymbolTableEntry.go     |  896 +++++++++
 .../protocols/ads/readwrite/model/AdsTableSizes.go |  284 +++
 .../org/apache/plc4x/java/api/PlcConnection.java   |    4 +-
 .../plc4x/java/api/messages/PlcBrowseRequest.java  |    2 +-
 .../plc4x/java/api/messages/PlcDiscoveryItem.java  |    4 +-
 .../java/api/messages/PlcDiscoveryRequest.java     |    2 +
 .../java/api/metadata/PlcConnectionMetadata.java   |    4 +-
 .../org/apache/plc4x/java/ads/AdsPlcDriver.java    |   16 +
 .../plc4x/java/ads/discovery/AdsPlcDiscoverer.java |  223 ++-
 .../plc4x/java/ads/protocol/AdsProtocolLogic.java  |    8 +-
 .../apache/plc4x/java/cbus/RandomPackagesTest.java |  403 ----
 .../org/apache/plc4x/java/cbus/ReferenceTest.java  | 2052 --------------------
 .../plc4x/java/mock/connection/MockConnection.java |   33 +-
 .../apache/plc4x/java/opcua/OpcuaPlcDriver.java    |    7 +-
 .../profinet/discovery/ProfinetPlcDiscoverer.java  |    7 +
 .../simulated/connection/SimulatedConnection.java  |    2 +-
 .../pom.xml                                        |    9 +-
 .../helloplc4x/discoverandbrowse/HelloPlc4x.java   |   60 +
 .../src/main/resources/logback.xml                 |    5 +-
 .../pom.xml                                        |    8 +-
 .../java/examples/helloplc4x/read}/CliOptions.java |    2 +-
 .../java/examples/helloplc4x/read}/HelloPlc4x.java |    5 +-
 .../src/main/resources/logback.xml                 |    0
 .../pom.xml                                        |    8 +-
 .../examples/helloplc4x/subscribe}/CliOptions.java |    2 +-
 .../subscribe}/HelloPlc4xSubscription.java         |    2 +-
 .../src/main/resources/logback.xml                 |    2 +-
 plc4j/examples/hello-world-plc4x-write/pom.xml     |    2 +-
 plc4j/examples/pom.xml                             |    5 +-
 .../java/spi/connection/AbstractPlcConnection.java |   33 +-
 .../spi/connection/DefaultNettyPlcConnection.java  |    4 +-
 .../java/spi/connection/GeneratedDriverBase.java   |    6 +-
 ...ryRequest.java => DefaultPlcBrowseRequest.java} |   57 +-
 ...yRequest.java => DefaultPlcBrowseResponse.java} |   60 +-
 .../java/spi/messages/DefaultPlcDiscoveryItem.java |    7 +-
 .../spi/messages/DefaultPlcDiscoveryRequest.java   |   31 +-
 .../plc4x/java/spi/messages/PlcBrowser.java}       |   29 +-
 .../connectionpool2/CachedBrowseRequest.java}      |   24 +-
 .../CachedBrowseRequestBuilder.java}               |   25 +-
 .../utils/connectionpool2/CachedPlcConnection.java |   73 +
 .../connectionpool/PooledPlcDriverManagerTest.java |   15 +-
 .../java/opm/PlcEntityManagerComplexTest.java      |    6 +
 .../knxnetip/readwrite/model/KnxManufacturer.cs    |   19 +-
 .../plc4x/java/discovery/DiscoveryConnection.java  |   22 +-
 64 files changed, 2883 insertions(+), 2791 deletions(-)

diff --git a/plc4go/assets/testing/protocols/ads/DriverTestsuite.xml b/plc4go/assets/testing/protocols/ads/DriverTestsuite.xml
index fb0aa1c2f..70a6dd51a 100644
--- a/plc4go/assets/testing/protocols/ads/DriverTestsuite.xml
+++ b/plc4go/assets/testing/protocols/ads/DriverTestsuite.xml
@@ -113,9 +113,9 @@
               <data>
                 <AdsData>
                   <AdsReadRequest>
-                    <indexGroup dataType="uint" bitLength="32">4040</indexGroup>
-                    <indexOffset dataType="uint" bitLength="32">8</indexOffset>
-                    <length dataType="uint" bitLength="32">1</length>
+                    <indexGroup dataType="uint" bitLength="32">61455</indexGroup>
+                    <indexOffset dataType="uint" bitLength="32">0</indexOffset>
+                    <length dataType="uint" bitLength="32">24</length>
                   </AdsReadRequest>
                 </AdsData>
               </data>
@@ -238,7 +238,7 @@
       <outgoing-plc-message name="Send Ads Read Request">
         <AmsTCPPacket>
           <reserved dataType="uint" bitLength="16">0</reserved>
-          <length dataType="uint" bitLength="32">72</length>
+          <length dataType="uint" bitLength="32">44</length>
           <userdata>
             <AmsPacket>
               <targetAmsNetId>
@@ -264,7 +264,7 @@
               </sourceAmsNetId>
               <sourceAmsPort dataType="uint" bitLength="16">48898</sourceAmsPort>
               <commandId>
-                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ_WRITE">9</CommandId>
+                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ">2</CommandId>
               </commandId>
               <state>
                 <State>
@@ -280,34 +280,16 @@
                   <reserved dataType="int" bitLength="7">0</reserved>
                 </State>
               </state>
-              <length dataType="uint" bitLength="32">40</length>
+              <length dataType="uint" bitLength="32">12</length>
               <errorCode dataType="uint" bitLength="32">0</errorCode>
               <invokeId dataType="uint" bitLength="32">1</invokeId>
               <data>
                 <AdsData>
-                  <AdsReadWriteRequest>
-                    <indexGroup dataType="uint" bitLength="32">61568</indexGroup>
-                    <indexOffset dataType="uint" bitLength="32">2</indexOffset>
-                    <readLength dataType="uint" bitLength="32">10</readLength>
-                    <writeLength dataType="uint" bitLength="32">24</writeLength>
-                    <items isList="true">
-                      <AdsMultiRequestItem>
-                        <AdsMultiRequestItemRead>
-                          <itemIndexGroup dataType="uint" bitLength="32">4040</itemIndexGroup>
-                          <itemIndexOffset dataType="uint" bitLength="32">8</itemIndexOffset>
-                          <itemReadLength dataType="uint" bitLength="32">1</itemReadLength>
-                        </AdsMultiRequestItemRead>
-                      </AdsMultiRequestItem>
-                      <AdsMultiRequestItem>
-                        <AdsMultiRequestItemRead>
-                          <itemIndexGroup dataType="uint" bitLength="32">4040</itemIndexGroup>
-                          <itemIndexOffset dataType="uint" bitLength="32">12</itemIndexOffset>
-                          <itemReadLength dataType="uint" bitLength="32">1</itemReadLength>
-                        </AdsMultiRequestItemRead>
-                      </AdsMultiRequestItem>
-                    </items>
-                    <data dataType="byte" bitLength="0">0x</data>
-                  </AdsReadWriteRequest>
+                  <AdsReadRequest>
+                    <indexGroup dataType="uint" bitLength="32">61455</indexGroup>
+                    <indexOffset dataType="uint" bitLength="32">0</indexOffset>
+                    <length dataType="uint" bitLength="32">24</length>
+                  </AdsReadRequest>
                 </AdsData>
               </data>
             </AmsPacket>
@@ -441,7 +423,7 @@
       <outgoing-plc-message name="Send Resolve Symbolic Address Request">
         <AmsTCPPacket>
           <reserved dataType="uint" bitLength="16">0</reserved>
-          <length dataType="uint" bitLength="32">74</length>
+          <length dataType="uint" bitLength="32">44</length>
           <userdata>
             <AmsPacket>
               <targetAmsNetId>
@@ -467,7 +449,7 @@
               </sourceAmsNetId>
               <sourceAmsPort dataType="uint" bitLength="16">48898</sourceAmsPort>
               <commandId>
-                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ_WRITE">9</CommandId>
+                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ">2</CommandId>
               </commandId>
               <state>
                 <State>
@@ -483,20 +465,16 @@
                   <reserved dataType="int" bitLength="7">0</reserved>
                 </State>
               </state>
-              <length dataType="uint" bitLength="32">42</length>
+              <length dataType="uint" bitLength="32">12</length>
               <errorCode dataType="uint" bitLength="32">0</errorCode>
               <invokeId dataType="uint" bitLength="32">1</invokeId>
               <data>
                 <AdsData>
-                  <AdsReadWriteRequest>
-                    <indexGroup dataType="uint" bitLength="32">61443</indexGroup>
+                  <AdsReadRequest>
+                    <indexGroup dataType="uint" bitLength="32">61455</indexGroup>
                     <indexOffset dataType="uint" bitLength="32">0</indexOffset>
-                    <readLength dataType="uint" bitLength="32">4</readLength>
-                    <writeLength dataType="uint" bitLength="32">26</writeLength>
-                    <items isList="true">
-                    </items>
-                    <data dataType="byte" bitLength="208">0x6d61696e2e665f74726967446174656947656c6573656e2e4d00</data>
-                  </AdsReadWriteRequest>
+                    <length dataType="uint" bitLength="32">24</length>
+                  </AdsReadRequest>
                 </AdsData>
               </data>
             </AmsPacket>
@@ -737,7 +715,7 @@
       <outgoing-plc-message name="Send Resolve Symbolic Address Request">
         <AmsTCPPacket>
           <reserved dataType="uint" bitLength="16">0</reserved>
-          <length dataType="uint" bitLength="32">74</length>
+          <length dataType="uint" bitLength="32">44</length>
           <userdata>
             <AmsPacket>
               <targetAmsNetId>
@@ -763,7 +741,7 @@
               </sourceAmsNetId>
               <sourceAmsPort dataType="uint" bitLength="16">48898</sourceAmsPort>
               <commandId>
-                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ_WRITE">9</CommandId>
+                <CommandId dataType="uint" bitLength="16" stringRepresentation="ADS_READ">2</CommandId>
               </commandId>
               <state>
                 <State>
@@ -779,20 +757,16 @@
                   <reserved dataType="int" bitLength="7">0</reserved>
                 </State>
               </state>
-              <length dataType="uint" bitLength="32">42</length>
+              <length dataType="uint" bitLength="32">12</length>
               <errorCode dataType="uint" bitLength="32">0</errorCode>
               <invokeId dataType="uint" bitLength="32">1</invokeId>
               <data>
                 <AdsData>
-                  <AdsReadWriteRequest>
-                    <indexGroup dataType="uint" bitLength="32">61443</indexGroup>
+                  <AdsReadRequest>
+                    <indexGroup dataType="uint" bitLength="32">61455</indexGroup>
                     <indexOffset dataType="uint" bitLength="32">0</indexOffset>
-                    <readLength dataType="uint" bitLength="32">4</readLength>
-                    <writeLength dataType="uint" bitLength="32">26</writeLength>
-                    <items isList="true">
-                    </items>
-                    <data dataType="byte" bitLength="208">0x6d61696e2e665f74726967446174656947656c6573656e2e4d00</data>
-                  </AdsReadWriteRequest>
+                    <length dataType="uint" bitLength="32">24</length>
+                  </AdsReadRequest>
                 </AdsData>
               </data>
             </AmsPacket>
diff --git a/plc4go/examples/bacnet/discovery/hello_world_plc4go_bacnet_discovery.go b/plc4go/examples/bacnet/discovery/hello_world_plc4go_bacnet_discovery.go
index c0d4302eb..2f16ec288 100644
--- a/plc4go/examples/bacnet/discovery/hello_world_plc4go_bacnet_discovery.go
+++ b/plc4go/examples/bacnet/discovery/hello_world_plc4go_bacnet_discovery.go
@@ -39,7 +39,7 @@ func main() {
 	var connectionStrings []string
 	if len(os.Args) < 2 {
 		// Try to auto-find bacnet devices via broadcast-message discovery
-		if err := driverManager.Discover(func(event model.PlcDiscoveryEvent) {
+		if err := driverManager.Discover(func(event model.PlcDiscoveryItem) {
 			connStr := event.GetProtocolCode() + "://" + event.GetTransportUrl().Host
 			log.Info().Str("connection string", connStr).Stringer("event", event.(fmt.Stringer)).Msg("Found Bacnet Gateway")
 
diff --git a/plc4go/examples/knx/discovery/hello_world_plc4go_knx_discovery.go b/plc4go/examples/knx/discovery/hello_world_plc4go_knx_discovery.go
index 5febd5785..72e00d01c 100644
--- a/plc4go/examples/knx/discovery/hello_world_plc4go_knx_discovery.go
+++ b/plc4go/examples/knx/discovery/hello_world_plc4go_knx_discovery.go
@@ -40,7 +40,7 @@ func main() {
 	var connectionStrings []string
 	if len(os.Args) < 2 {
 		// Try to auto-find KNX gateways via broadcast-message discovery
-		_ = driverManager.Discover(func(event model.PlcDiscoveryEvent) {
+		_ = driverManager.Discover(func(event model.PlcDiscoveryItem) {
 			connStr := event.GetProtocolCode() + "://" + event.GetTransportUrl().Host
 			log.Info().Str("connection string", connStr).Msg("Found KNX Gateway")
 
diff --git a/plc4go/internal/bacnetip/Discoverer.go b/plc4go/internal/bacnetip/Discoverer.go
index 77abe6c8b..601bf7eb6 100644
--- a/plc4go/internal/bacnetip/Discoverer.go
+++ b/plc4go/internal/bacnetip/Discoverer.go
@@ -48,7 +48,7 @@ func NewDiscoverer() *Discoverer {
 	return &Discoverer{}
 }
 
-func (d *Discoverer) Discover(callback func(event apiModel.PlcDiscoveryEvent), discoveryOptions ...options.WithDiscoveryOption) error {
+func (d *Discoverer) Discover(callback func(event apiModel.PlcDiscoveryItem), discoveryOptions ...options.WithDiscoveryOption) error {
 	interfaces, err := extractInterfaces(discoveryOptions)
 	if err != nil {
 		return errors.Wrap(err, "error extracting interfaces")
@@ -226,7 +226,7 @@ func broadcastAndDiscover(ctx context.Context, communicationChannels []communica
 	return incomingBVLCChannel, nil
 }
 
-func handleIncomingBVLCs(ctx context.Context, callback func(event apiModel.PlcDiscoveryEvent), incomingBVLCChannel chan receivedBvlcMessage) {
+func handleIncomingBVLCs(ctx context.Context, callback func(event apiModel.PlcDiscoveryItem), incomingBVLCChannel chan receivedBvlcMessage) {
 	for {
 		select {
 		case receivedBvlc := <-incomingBVLCChannel:
diff --git a/plc4go/internal/bacnetip/Driver.go b/plc4go/internal/bacnetip/Driver.go
index 70efb1975..6528730fa 100644
--- a/plc4go/internal/bacnetip/Driver.go
+++ b/plc4go/internal/bacnetip/Driver.go
@@ -133,6 +133,6 @@ func (m *Driver) SupportsDiscovery() bool {
 	return true
 }
 
-func (m *Driver) Discover(callback func(event apiModel.PlcDiscoveryEvent), discoveryOptions ...options.WithDiscoveryOption) error {
+func (m *Driver) Discover(callback func(event apiModel.PlcDiscoveryItem), discoveryOptions ...options.WithDiscoveryOption) error {
 	return NewDiscoverer().Discover(callback, discoveryOptions...)
 }
diff --git a/plc4go/internal/knxnetip/Discoverer.go b/plc4go/internal/knxnetip/Discoverer.go
index 468e19ea3..9ca7ec8c8 100644
--- a/plc4go/internal/knxnetip/Discoverer.go
+++ b/plc4go/internal/knxnetip/Discoverer.go
@@ -44,7 +44,7 @@ func NewDiscoverer() *Discoverer {
 	return &Discoverer{}
 }
 
-func (d *Discoverer) Discover(callback func(event apiModel.PlcDiscoveryEvent), discoveryOptions ...options.WithDiscoveryOption) error {
+func (d *Discoverer) Discover(callback func(event apiModel.PlcDiscoveryItem), discoveryOptions ...options.WithDiscoveryOption) error {
 	udpTransport := udp.NewTransport()
 
 	// Create a connection string for the KNX broadcast discovery address.
diff --git a/plc4go/internal/knxnetip/Driver.go b/plc4go/internal/knxnetip/Driver.go
index c14a6feb8..3208ef444 100644
--- a/plc4go/internal/knxnetip/Driver.go
+++ b/plc4go/internal/knxnetip/Driver.go
@@ -77,6 +77,6 @@ func (m Driver) SupportsDiscovery() bool {
 	return true
 }
 
-func (m Driver) Discover(callback func(event apiModel.PlcDiscoveryEvent), discoveryOptions ...options.WithDiscoveryOption) error {
+func (m Driver) Discover(callback func(event apiModel.PlcDiscoveryItem), discoveryOptions ...options.WithDiscoveryOption) error {
 	return NewDiscoverer().Discover(callback, discoveryOptions...)
 }
diff --git a/plc4go/internal/simulated/Driver.go b/plc4go/internal/simulated/Driver.go
index 7e590fd4b..fb3bf6b4b 100644
--- a/plc4go/internal/simulated/Driver.go
+++ b/plc4go/internal/simulated/Driver.go
@@ -74,6 +74,6 @@ func (d *Driver) SupportsDiscovery() bool {
 	return false
 }
 
-func (d *Driver) Discover(_ func(event model.PlcDiscoveryEvent), _ ...options.WithDiscoveryOption) error {
+func (d *Driver) Discover(_ func(event model.PlcDiscoveryItem), _ ...options.WithDiscoveryOption) error {
 	return errors.New("unsupported operation")
 }
diff --git a/plc4go/internal/simulated/Driver_test.go b/plc4go/internal/simulated/Driver_test.go
index 63c608534..294d9e91e 100644
--- a/plc4go/internal/simulated/Driver_test.go
+++ b/plc4go/internal/simulated/Driver_test.go
@@ -73,7 +73,7 @@ func TestDriver_Discover(t *testing.T) {
 		valueHandler ValueHandler
 	}
 	type args struct {
-		callback         func(event model.PlcDiscoveryEvent)
+		callback         func(event model.PlcDiscoveryItem)
 		discoveryOptions []options.WithDiscoveryOption
 	}
 	tests := []struct {
diff --git a/plc4go/internal/spi/PlcDiscoverer.go b/plc4go/internal/spi/PlcDiscoverer.go
index b5a5dbfa8..520b388ed 100644
--- a/plc4go/internal/spi/PlcDiscoverer.go
+++ b/plc4go/internal/spi/PlcDiscoverer.go
@@ -25,5 +25,5 @@ import (
 )
 
 type PlcDiscoverer interface {
-	Discover(callback func(event model.PlcDiscoveryEvent), discoveryOptions ...options.WithDiscoveryOption) error
+	Discover(callback func(event model.PlcDiscoveryItem), discoveryOptions ...options.WithDiscoveryOption) error
 }
diff --git a/plc4go/internal/spi/default/DefaultDriver.go b/plc4go/internal/spi/default/DefaultDriver.go
index b41117516..8e57d35f4 100644
--- a/plc4go/internal/spi/default/DefaultDriver.go
+++ b/plc4go/internal/spi/default/DefaultDriver.go
@@ -87,7 +87,7 @@ func (d *defaultDriver) SupportsDiscovery() bool {
 	return false
 }
 
-func (d *defaultDriver) Discover(_ func(event apiModel.PlcDiscoveryEvent), _ ...options.WithDiscoveryOption) error {
+func (d *defaultDriver) Discover(_ func(event apiModel.PlcDiscoveryItem), _ ...options.WithDiscoveryOption) error {
 	panic("not available")
 }
 
diff --git a/plc4go/internal/spi/model/DefaultPlcDiscoveryEvent.go b/plc4go/internal/spi/model/DefaultPlcDiscoveryEvent.go
index aae283b01..78671c956 100644
--- a/plc4go/internal/spi/model/DefaultPlcDiscoveryEvent.go
+++ b/plc4go/internal/spi/model/DefaultPlcDiscoveryEvent.go
@@ -21,6 +21,7 @@ package model
 
 import (
 	"fmt"
+	"github.com/apache/plc4x/plc4go/pkg/api/values"
 	"net/url"
 )
 
@@ -30,6 +31,7 @@ type DefaultPlcDiscoveryEvent struct {
 	TransportUrl  url.URL
 	Options       map[string][]string
 	Name          string
+	Attributes    map[string]values.PlcValue
 }
 
 func (d *DefaultPlcDiscoveryEvent) GetProtocolCode() string {
@@ -52,7 +54,11 @@ func (d *DefaultPlcDiscoveryEvent) GetName() string {
 	return d.Name
 }
 
-func (d *DefaultPlcDiscoveryEvent) GetConnectionString() string {
+func (d *DefaultPlcDiscoveryEvent) GetAttributes() map[string]values.PlcValue {
+	return d.Attributes
+}
+
+func (d *DefaultPlcDiscoveryEvent) GetConnectionUrl() string {
 	if d.Options != nil {
 		panic("Not implemented")
 	}
@@ -60,5 +66,5 @@ func (d *DefaultPlcDiscoveryEvent) GetConnectionString() string {
 }
 
 func (d *DefaultPlcDiscoveryEvent) String() string {
-	return fmt.Sprintf("PlcDiscoveryEvent{Name:%s,%s}", d.Name, d.GetConnectionString())
+	return fmt.Sprintf("PlcDiscoveryEvent{Name:%s,%s}", d.Name, d.GetConnectionUrl())
 }
diff --git a/plc4go/pkg/api/driver.go b/plc4go/pkg/api/driver.go
index 2dd4553cf..38257599d 100644
--- a/plc4go/pkg/api/driver.go
+++ b/plc4go/pkg/api/driver.go
@@ -48,5 +48,5 @@ type PlcDriver interface {
 
 	// Discover TODO: document me
 	// FIXME: this leaks spi in the signature move to spi driver or create interfaces. Can also be done by moving spi in a proper module
-	Discover(callback func(event model.PlcDiscoveryEvent), discoveryOptions ...options.WithDiscoveryOption) error
+	Discover(callback func(event model.PlcDiscoveryItem), discoveryOptions ...options.WithDiscoveryOption) error
 }
diff --git a/plc4go/pkg/api/driverManager.go b/plc4go/pkg/api/driverManager.go
index 1d6bb8812..4b5c6ae26 100644
--- a/plc4go/pkg/api/driverManager.go
+++ b/plc4go/pkg/api/driverManager.go
@@ -41,7 +41,7 @@ type PlcDriverManager interface {
 	GetConnection(connectionString string) <-chan PlcConnectionConnectResult
 
 	// Discover Execute all available discovery methods on all available drivers using all transports
-	Discover(callback func(event model.PlcDiscoveryEvent), discoveryOptions ...WithDiscoveryOption) error
+	Discover(callback func(event model.PlcDiscoveryItem), discoveryOptions ...WithDiscoveryOption) error
 }
 
 func NewPlcDriverManager() PlcDriverManager {
@@ -282,7 +282,7 @@ func (m *plcDriverManger) GetConnection(connectionString string) <-chan PlcConne
 	return driver.GetConnection(transportUrl, m.transports, configOptions)
 }
 
-func (m *plcDriverManger) Discover(callback func(event model.PlcDiscoveryEvent), discoveryOptions ...WithDiscoveryOption) error {
+func (m *plcDriverManger) Discover(callback func(event model.PlcDiscoveryItem), discoveryOptions ...WithDiscoveryOption) error {
 	// Check if we've got at least one option to restrict to certain protocols only.
 	// If there is at least one, we only check that protocol, if there are none, all
 	// available protocols are checked.
diff --git a/plc4go/pkg/api/model/plc_discovery.go b/plc4go/pkg/api/model/plc_discovery.go
index 29dc7f740..7ff197305 100644
--- a/plc4go/pkg/api/model/plc_discovery.go
+++ b/plc4go/pkg/api/model/plc_discovery.go
@@ -20,13 +20,16 @@
 package model
 
 import (
+	"github.com/apache/plc4x/plc4go/pkg/api/values"
 	"net/url"
 )
 
-type PlcDiscoveryEvent interface {
+type PlcDiscoveryItem interface {
 	GetProtocolCode() string
 	GetTransportCode() string
 	GetTransportUrl() url.URL
 	GetOptions() map[string][]string
 	GetName() string
+	GetAttributes() map[string]values.PlcValue
+	GetConnectionUrl() string
 }
diff --git a/plc4go/protocols/ads/readwrite/ParserHelper.go b/plc4go/protocols/ads/readwrite/ParserHelper.go
index 802fd848c..86e18e668 100644
--- a/plc4go/protocols/ads/readwrite/ParserHelper.go
+++ b/plc4go/protocols/ads/readwrite/ParserHelper.go
@@ -44,6 +44,8 @@ func (m AdsParserHelper) Parse(typeName string, arguments []string, io utils.Rea
 			return nil, errors.Wrap(err, "Error parsing")
 		}
 		return model.DataItemParse(io, dataFormatName, stringLength)
+	case "AdsTableSizes":
+		return model.AdsTableSizesParse(io)
 	case "AdsMultiRequestItem":
 		indexGroup, err := utils.StrToUint32(arguments[0])
 		if err != nil {
@@ -52,6 +54,8 @@ func (m AdsParserHelper) Parse(typeName string, arguments []string, io utils.Rea
 		return model.AdsMultiRequestItemParse(io, indexGroup)
 	case "AmsSerialAcknowledgeFrame":
 		return model.AmsSerialAcknowledgeFrameParse(io)
+	case "AdsDataTypeArrayInfo":
+		return model.AdsDataTypeArrayInfoParse(io)
 	case "AdsData":
 		commandId, _ := model.CommandIdByName(arguments[0])
 		response, err := utils.StrToBool(arguments[1])
@@ -59,6 +63,8 @@ func (m AdsParserHelper) Parse(typeName string, arguments []string, io utils.Rea
 			return nil, errors.Wrap(err, "Error parsing")
 		}
 		return model.AdsDataParse(io, commandId, response)
+	case "AdsDataTypeTableEntry":
+		return model.AdsDataTypeTableEntryParse(io)
 	case "AmsNetId":
 		return model.AmsNetIdParse(io)
 	case "AdsStampHeader":
@@ -69,6 +75,8 @@ func (m AdsParserHelper) Parse(typeName string, arguments []string, io utils.Rea
 		return model.AdsConstantsParse(io)
 	case "AdsNotificationSample":
 		return model.AdsNotificationSampleParse(io)
+	case "AdsSymbolTableEntry":
+		return model.AdsSymbolTableEntryParse(io)
 	case "AmsTCPPacket":
 		return model.AmsTCPPacketParse(io)
 	case "State":
diff --git a/plc4go/protocols/ads/readwrite/XmlParserHelper.go b/plc4go/protocols/ads/readwrite/XmlParserHelper.go
index e09831b45..780d1836b 100644
--- a/plc4go/protocols/ads/readwrite/XmlParserHelper.go
+++ b/plc4go/protocols/ads/readwrite/XmlParserHelper.go
@@ -53,6 +53,8 @@ func (m AdsXmlParserHelper) Parse(typeName string, xmlString string, parserArgum
 		}
 		stringLength := int32(parsedInt1)
 		return model.DataItemParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)), dataFormatName, stringLength)
+	case "AdsTableSizes":
+		return model.AdsTableSizesParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "AdsMultiRequestItem":
 		parsedUint0, err := strconv.ParseUint(parserArguments[0], 10, 32)
 		if err != nil {
@@ -62,10 +64,14 @@ func (m AdsXmlParserHelper) Parse(typeName string, xmlString string, parserArgum
 		return model.AdsMultiRequestItemParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)), indexGroup)
 	case "AmsSerialAcknowledgeFrame":
 		return model.AmsSerialAcknowledgeFrameParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
+	case "AdsDataTypeArrayInfo":
+		return model.AdsDataTypeArrayInfoParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "AdsData":
 		commandId, _ := model.CommandIdByName(parserArguments[0])
 		response := parserArguments[1] == "true"
 		return model.AdsDataParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)), commandId, response)
+	case "AdsDataTypeTableEntry":
+		return model.AdsDataTypeTableEntryParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "AmsNetId":
 		return model.AmsNetIdParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "AdsStampHeader":
@@ -76,6 +82,8 @@ func (m AdsXmlParserHelper) Parse(typeName string, xmlString string, parserArgum
 		return model.AdsConstantsParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "AdsNotificationSample":
 		return model.AdsNotificationSampleParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
+	case "AdsSymbolTableEntry":
+		return model.AdsSymbolTableEntryParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "AmsTCPPacket":
 		return model.AmsTCPPacketParse(utils.NewXmlReadBuffer(strings.NewReader(xmlString)))
 	case "State":
diff --git a/plc4go/protocols/ads/readwrite/model/AdsDataTypeArrayInfo.go b/plc4go/protocols/ads/readwrite/model/AdsDataTypeArrayInfo.go
new file mode 100644
index 000000000..9d997e1ba
--- /dev/null
+++ b/plc4go/protocols/ads/readwrite/model/AdsDataTypeArrayInfo.go
@@ -0,0 +1,210 @@
+/*
+ * 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
+ *
+ *   https://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
+
+import (
+	"github.com/apache/plc4x/plc4go/internal/spi/utils"
+	"github.com/pkg/errors"
+)
+
+// Code generated by code-generation. DO NOT EDIT.
+
+// AdsDataTypeArrayInfo is the corresponding interface of AdsDataTypeArrayInfo
+type AdsDataTypeArrayInfo interface {
+	utils.LengthAware
+	utils.Serializable
+	// GetLowerBound returns LowerBound (property field)
+	GetLowerBound() uint32
+	// GetNumElements returns NumElements (property field)
+	GetNumElements() uint32
+	// GetUpperBound returns UpperBound (virtual field)
+	GetUpperBound() uint32
+}
+
+// AdsDataTypeArrayInfoExactly can be used when we want exactly this type and not a type which fulfills AdsDataTypeArrayInfo.
+// This is useful for switch cases.
+type AdsDataTypeArrayInfoExactly interface {
+	AdsDataTypeArrayInfo
+	isAdsDataTypeArrayInfo() bool
+}
+
+// _AdsDataTypeArrayInfo is the data-structure of this message
+type _AdsDataTypeArrayInfo struct {
+	LowerBound  uint32
+	NumElements uint32
+}
+
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+/////////////////////// Accessors for property fields.
+///////////////////////
+
+func (m *_AdsDataTypeArrayInfo) GetLowerBound() uint32 {
+	return m.LowerBound
+}
+
+func (m *_AdsDataTypeArrayInfo) GetNumElements() uint32 {
+	return m.NumElements
+}
+
+///////////////////////
+///////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+/////////////////////// Accessors for virtual fields.
+///////////////////////
+
+func (m *_AdsDataTypeArrayInfo) GetUpperBound() uint32 {
+	return uint32(uint32(m.GetLowerBound()) + uint32(m.GetNumElements()))
+}
+
+///////////////////////
+///////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+
+// NewAdsDataTypeArrayInfo factory function for _AdsDataTypeArrayInfo
+func NewAdsDataTypeArrayInfo(lowerBound uint32, numElements uint32) *_AdsDataTypeArrayInfo {
+	return &_AdsDataTypeArrayInfo{LowerBound: lowerBound, NumElements: numElements}
+}
+
+// Deprecated: use the interface for direct cast
+func CastAdsDataTypeArrayInfo(structType interface{}) AdsDataTypeArrayInfo {
+	if casted, ok := structType.(AdsDataTypeArrayInfo); ok {
+		return casted
+	}
+	if casted, ok := structType.(*AdsDataTypeArrayInfo); ok {
+		return *casted
+	}
+	return nil
+}
+
+func (m *_AdsDataTypeArrayInfo) GetTypeName() string {
+	return "AdsDataTypeArrayInfo"
+}
+
+func (m *_AdsDataTypeArrayInfo) GetLengthInBits() uint16 {
+	return m.GetLengthInBitsConditional(false)
+}
+
+func (m *_AdsDataTypeArrayInfo) GetLengthInBitsConditional(lastItem bool) uint16 {
+	lengthInBits := uint16(0)
+
+	// Simple field (lowerBound)
+	lengthInBits += 32
+
+	// Simple field (numElements)
+	lengthInBits += 32
+
+	// A virtual field doesn't have any in- or output.
+
+	return lengthInBits
+}
+
+func (m *_AdsDataTypeArrayInfo) GetLengthInBytes() uint16 {
+	return m.GetLengthInBits() / 8
+}
+
+func AdsDataTypeArrayInfoParse(readBuffer utils.ReadBuffer) (AdsDataTypeArrayInfo, error) {
+	positionAware := readBuffer
+	_ = positionAware
+	if pullErr := readBuffer.PullContext("AdsDataTypeArrayInfo"); pullErr != nil {
+		return nil, errors.Wrap(pullErr, "Error pulling for AdsDataTypeArrayInfo")
+	}
+	currentPos := positionAware.GetPos()
+	_ = currentPos
+
+	// Simple Field (lowerBound)
+	_lowerBound, _lowerBoundErr := readBuffer.ReadUint32("lowerBound", 32)
+	if _lowerBoundErr != nil {
+		return nil, errors.Wrap(_lowerBoundErr, "Error parsing 'lowerBound' field of AdsDataTypeArrayInfo")
+	}
+	lowerBound := _lowerBound
+
+	// Simple Field (numElements)
+	_numElements, _numElementsErr := readBuffer.ReadUint32("numElements", 32)
+	if _numElementsErr != nil {
+		return nil, errors.Wrap(_numElementsErr, "Error parsing 'numElements' field of AdsDataTypeArrayInfo")
+	}
+	numElements := _numElements
+
+	// Virtual field
+	_upperBound := uint32(lowerBound) + uint32(numElements)
+	upperBound := uint32(_upperBound)
+	_ = upperBound
+
+	if closeErr := readBuffer.CloseContext("AdsDataTypeArrayInfo"); closeErr != nil {
+		return nil, errors.Wrap(closeErr, "Error closing for AdsDataTypeArrayInfo")
+	}
+
+	// Create the instance
+	return &_AdsDataTypeArrayInfo{
+		LowerBound:  lowerBound,
+		NumElements: numElements,
+	}, nil
+}
+
+func (m *_AdsDataTypeArrayInfo) Serialize(writeBuffer utils.WriteBuffer) error {
+	positionAware := writeBuffer
+	_ = positionAware
+	if pushErr := writeBuffer.PushContext("AdsDataTypeArrayInfo"); pushErr != nil {
+		return errors.Wrap(pushErr, "Error pushing for AdsDataTypeArrayInfo")
+	}
+
+	// Simple Field (lowerBound)
+	lowerBound := uint32(m.GetLowerBound())
+	_lowerBoundErr := writeBuffer.WriteUint32("lowerBound", 32, (lowerBound))
+	if _lowerBoundErr != nil {
+		return errors.Wrap(_lowerBoundErr, "Error serializing 'lowerBound' field")
+	}
+
+	// Simple Field (numElements)
+	numElements := uint32(m.GetNumElements())
+	_numElementsErr := writeBuffer.WriteUint32("numElements", 32, (numElements))
+	if _numElementsErr != nil {
+		return errors.Wrap(_numElementsErr, "Error serializing 'numElements' field")
+	}
+	// Virtual field
+	if _upperBoundErr := writeBuffer.WriteVirtual("upperBound", m.GetUpperBound()); _upperBoundErr != nil {
+		return errors.Wrap(_upperBoundErr, "Error serializing 'upperBound' field")
+	}
+
+	if popErr := writeBuffer.PopContext("AdsDataTypeArrayInfo"); popErr != nil {
+		return errors.Wrap(popErr, "Error popping for AdsDataTypeArrayInfo")
+	}
+	return nil
+}
+
+func (m *_AdsDataTypeArrayInfo) isAdsDataTypeArrayInfo() bool {
+	return true
+}
+
+func (m *_AdsDataTypeArrayInfo) String() string {
+	if m == nil {
+		return "<nil>"
+	}
+	writeBuffer := utils.NewBoxedWriteBufferWithOptions(true, true)
+	if err := writeBuffer.WriteSerializable(m); err != nil {
+		return err.Error()
+	}
+	return writeBuffer.GetBox().String()
+}
diff --git a/plc4go/protocols/ads/readwrite/model/AdsDataTypeTableEntry.go b/plc4go/protocols/ads/readwrite/model/AdsDataTypeTableEntry.go
new file mode 100644
index 000000000..aa8c945e8
--- /dev/null
+++ b/plc4go/protocols/ads/readwrite/model/AdsDataTypeTableEntry.go
@@ -0,0 +1,724 @@
+/*
+ * 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
+ *
+ *   https://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
+
+import (
+	"fmt"
+	"github.com/apache/plc4x/plc4go/internal/spi/utils"
+	"github.com/pkg/errors"
+)
+
+// Code generated by code-generation. DO NOT EDIT.
+
+// Constant values.
+const AdsDataTypeTableEntry_NAMETERMINATOR uint8 = 0x00
+const AdsDataTypeTableEntry_TYPENAMETERMINATOR uint8 = 0x00
+const AdsDataTypeTableEntry_COMMENTTERMINATOR uint8 = 0x00
+
+// AdsDataTypeTableEntry is the corresponding interface of AdsDataTypeTableEntry
+type AdsDataTypeTableEntry interface {
+	utils.LengthAware
+	utils.Serializable
+	// GetEntryLength returns EntryLength (property field)
+	GetEntryLength() uint32
+	// GetVersion returns Version (property field)
+	GetVersion() uint32
+	// GetHashValue returns HashValue (property field)
+	GetHashValue() uint32
+	// GetTypeHashValue returns TypeHashValue (property field)
+	GetTypeHashValue() uint32
+	// GetSize returns Size (property field)
+	GetSize() uint32
+	// GetOffs returns Offs (property field)
+	GetOffs() uint32
+	// GetDataType returns DataType (property field)
+	GetDataType() uint32
+	// GetFlags returns Flags (property field)
+	GetFlags() uint32
+	// GetArrayDimensions returns ArrayDimensions (property field)
+	GetArrayDimensions() uint16
+	// GetNumChildren returns NumChildren (property field)
+	GetNumChildren() uint16
+	// GetName returns Name (property field)
+	GetName() string
+	// GetTypeName returns TypeName (property field)
+	GetTypeName() string
+	// GetComment returns Comment (property field)
+	GetComment() string
+	// GetArrayInfo returns ArrayInfo (property field)
+	GetArrayInfo() []AdsDataTypeArrayInfo
+	// GetChildren returns Children (property field)
+	GetChildren() []AdsDataTypeTableEntry
+	// GetRest returns Rest (property field)
+	GetRest() []byte
+}
+
+// AdsDataTypeTableEntryExactly can be used when we want exactly this type and not a type which fulfills AdsDataTypeTableEntry.
+// This is useful for switch cases.
+type AdsDataTypeTableEntryExactly interface {
+	AdsDataTypeTableEntry
+	isAdsDataTypeTableEntry() bool
+}
+
+// _AdsDataTypeTableEntry is the data-structure of this message
+type _AdsDataTypeTableEntry struct {
+	EntryLength     uint32
+	Version         uint32
+	HashValue       uint32
+	TypeHashValue   uint32
+	Size            uint32
+	Offs            uint32
+	DataType        uint32
+	Flags           uint32
+	ArrayDimensions uint16
+	NumChildren     uint16
+	Name            string
+	TypeName        string
+	Comment         string
+	ArrayInfo       []AdsDataTypeArrayInfo
+	Children        []AdsDataTypeTableEntry
+	Rest            []byte
+}
+
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+/////////////////////// Accessors for property fields.
+///////////////////////
+
+func (m *_AdsDataTypeTableEntry) GetEntryLength() uint32 {
+	return m.EntryLength
+}
+
+func (m *_AdsDataTypeTableEntry) GetVersion() uint32 {
+	return m.Version
+}
+
+func (m *_AdsDataTypeTableEntry) GetHashValue() uint32 {
+	return m.HashValue
+}
+
+func (m *_AdsDataTypeTableEntry) GetTypeHashValue() uint32 {
+	return m.TypeHashValue
+}
+
+func (m *_AdsDataTypeTableEntry) GetSize() uint32 {
+	return m.Size
+}
+
+func (m *_AdsDataTypeTableEntry) GetOffs() uint32 {
+	return m.Offs
+}
+
+func (m *_AdsDataTypeTableEntry) GetDataType() uint32 {
+	return m.DataType
+}
+
+func (m *_AdsDataTypeTableEntry) GetFlags() uint32 {
+	return m.Flags
+}
+
+func (m *_AdsDataTypeTableEntry) GetArrayDimensions() uint16 {
+	return m.ArrayDimensions
+}
+
+func (m *_AdsDataTypeTableEntry) GetNumChildren() uint16 {
+	return m.NumChildren
+}
+
+func (m *_AdsDataTypeTableEntry) GetName() string {
+	return m.Name
+}
+
+func (m *_AdsDataTypeTableEntry) GetTypeName() string {
+	return m.TypeName
+}
+
+func (m *_AdsDataTypeTableEntry) GetComment() string {
+	return m.Comment
+}
+
+func (m *_AdsDataTypeTableEntry) GetArrayInfo() []AdsDataTypeArrayInfo {
+	return m.ArrayInfo
+}
+
+func (m *_AdsDataTypeTableEntry) GetChildren() []AdsDataTypeTableEntry {
+	return m.Children
+}
+
+func (m *_AdsDataTypeTableEntry) GetRest() []byte {
+	return m.Rest
+}
+
+///////////////////////
+///////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+/////////////////////// Accessors for const fields.
+///////////////////////
+
+func (m *_AdsDataTypeTableEntry) GetNameTerminator() uint8 {
+	return AdsDataTypeTableEntry_NAMETERMINATOR
+}
+
+func (m *_AdsDataTypeTableEntry) GetTypeNameTerminator() uint8 {
+	return AdsDataTypeTableEntry_TYPENAMETERMINATOR
+}
+
+func (m *_AdsDataTypeTableEntry) GetCommentTerminator() uint8 {
+	return AdsDataTypeTableEntry_COMMENTTERMINATOR
+}
+
+///////////////////////
+///////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+
+// NewAdsDataTypeTableEntry factory function for _AdsDataTypeTableEntry
+func NewAdsDataTypeTableEntry(entryLength uint32, version uint32, hashValue uint32, typeHashValue uint32, size uint32, offs uint32, dataType uint32, flags uint32, arrayDimensions uint16, numChildren uint16, name string, typeName string, comment string, arrayInfo []AdsDataTypeArrayInfo, children []AdsDataTypeTableEntry, rest []byte) *_AdsDataTypeTableEntry {
+	return &_AdsDataTypeTableEntry{EntryLength: entryLength, Version: version, HashValue: hashValue, TypeHashValue: typeHashValue, Size: size, Offs: offs, DataType: dataType, Flags: flags, ArrayDimensions: arrayDimensions, NumChildren: numChildren, Name: name, TypeName: typeName, Comment: comment, ArrayInfo: arrayInfo, Children: children, Rest: rest}
+}
+
+// Deprecated: use the interface for direct cast
+func CastAdsDataTypeTableEntry(structType interface{}) AdsDataTypeTableEntry {
+	if casted, ok := structType.(AdsDataTypeTableEntry); ok {
+		return casted
+	}
+	if casted, ok := structType.(*AdsDataTypeTableEntry); ok {
+		return *casted
+	}
+	return nil
+}
+
+func (m *_AdsDataTypeTableEntry) GetTypeName() string {
+	return "AdsDataTypeTableEntry"
+}
+
+func (m *_AdsDataTypeTableEntry) GetLengthInBits() uint16 {
+	return m.GetLengthInBitsConditional(false)
+}
+
+func (m *_AdsDataTypeTableEntry) GetLengthInBitsConditional(lastItem bool) uint16 {
+	lengthInBits := uint16(0)
+
+	// Simple field (entryLength)
+	lengthInBits += 32
+
+	// Simple field (version)
+	lengthInBits += 32
+
+	// Simple field (hashValue)
+	lengthInBits += 32
+
+	// Simple field (typeHashValue)
+	lengthInBits += 32
+
+	// Simple field (size)
+	lengthInBits += 32
+
+	// Simple field (offs)
+	lengthInBits += 32
+
+	// Simple field (dataType)
+	lengthInBits += 32
+
+	// Simple field (flags)
+	lengthInBits += 32
+
+	// Implicit Field (nameLength)
+	lengthInBits += 16
+
+	// Implicit Field (typeNameLength)
+	lengthInBits += 16
+
+	// Implicit Field (commentLength)
+	lengthInBits += 16
+
+	// Simple field (arrayDimensions)
+	lengthInBits += 16
+
+	// Simple field (numChildren)
+	lengthInBits += 16
+
+	// Simple field (name)
+	lengthInBits += uint16(int32(GetSTR_LEN()(m.GetName())) * int32(int32(8)))
+
+	// Const Field (nameTerminator)
+	lengthInBits += 8
+
+	// Simple field (typeName)
+	lengthInBits += uint16(int32(GetSTR_LEN()(m.GetTypeName())) * int32(int32(8)))
+
+	// Const Field (typeNameTerminator)
+	lengthInBits += 8
+
+	// Simple field (comment)
+	lengthInBits += uint16(int32(GetSTR_LEN()(m.GetComment())) * int32(int32(8)))
+
+	// Const Field (commentTerminator)
+	lengthInBits += 8
+
+	// Array field
+	if len(m.ArrayInfo) > 0 {
+		for i, element := range m.ArrayInfo {
+			last := i == len(m.ArrayInfo)-1
+			lengthInBits += element.(interface{ GetLengthInBitsConditional(bool) uint16 }).GetLengthInBitsConditional(last)
+		}
+	}
+
+	// Array field
+	if len(m.Children) > 0 {
+		for i, element := range m.Children {
+			last := i == len(m.Children)-1
+			lengthInBits += element.(interface{ GetLengthInBitsConditional(bool) uint16 }).GetLengthInBitsConditional(last)
+		}
+	}
+
+	// Array field
+	if len(m.Rest) > 0 {
+		lengthInBits += 8 * uint16(len(m.Rest))
+	}
+
+	return lengthInBits
+}
+
+func (m *_AdsDataTypeTableEntry) GetLengthInBytes() uint16 {
+	return m.GetLengthInBits() / 8
+}
+
+func AdsDataTypeTableEntryParse(readBuffer utils.ReadBuffer) (AdsDataTypeTableEntry, error) {
+	positionAware := readBuffer
+	_ = positionAware
+	if pullErr := readBuffer.PullContext("AdsDataTypeTableEntry"); pullErr != nil {
+		return nil, errors.Wrap(pullErr, "Error pulling for AdsDataTypeTableEntry")
+	}
+	currentPos := positionAware.GetPos()
+	_ = currentPos
+	var startPos = positionAware.GetPos()
+	var curPos uint16
+
+	// Simple Field (entryLength)
+	_entryLength, _entryLengthErr := readBuffer.ReadUint32("entryLength", 32)
+	if _entryLengthErr != nil {
+		return nil, errors.Wrap(_entryLengthErr, "Error parsing 'entryLength' field of AdsDataTypeTableEntry")
+	}
+	entryLength := _entryLength
+
+	// Simple Field (version)
+	_version, _versionErr := readBuffer.ReadUint32("version", 32)
+	if _versionErr != nil {
+		return nil, errors.Wrap(_versionErr, "Error parsing 'version' field of AdsDataTypeTableEntry")
+	}
+	version := _version
+
+	// Simple Field (hashValue)
+	_hashValue, _hashValueErr := readBuffer.ReadUint32("hashValue", 32)
+	if _hashValueErr != nil {
+		return nil, errors.Wrap(_hashValueErr, "Error parsing 'hashValue' field of AdsDataTypeTableEntry")
+	}
+	hashValue := _hashValue
+
+	// Simple Field (typeHashValue)
+	_typeHashValue, _typeHashValueErr := readBuffer.ReadUint32("typeHashValue", 32)
+	if _typeHashValueErr != nil {
+		return nil, errors.Wrap(_typeHashValueErr, "Error parsing 'typeHashValue' field of AdsDataTypeTableEntry")
+	}
+	typeHashValue := _typeHashValue
+
+	// Simple Field (size)
+	_size, _sizeErr := readBuffer.ReadUint32("size", 32)
+	if _sizeErr != nil {
+		return nil, errors.Wrap(_sizeErr, "Error parsing 'size' field of AdsDataTypeTableEntry")
+	}
+	size := _size
+
+	// Simple Field (offs)
+	_offs, _offsErr := readBuffer.ReadUint32("offs", 32)
+	if _offsErr != nil {
+		return nil, errors.Wrap(_offsErr, "Error parsing 'offs' field of AdsDataTypeTableEntry")
+	}
+	offs := _offs
+
+	// Simple Field (dataType)
+	_dataType, _dataTypeErr := readBuffer.ReadUint32("dataType", 32)
+	if _dataTypeErr != nil {
+		return nil, errors.Wrap(_dataTypeErr, "Error parsing 'dataType' field of AdsDataTypeTableEntry")
+	}
+	dataType := _dataType
+
+	// Simple Field (flags)
+	_flags, _flagsErr := readBuffer.ReadUint32("flags", 32)
+	if _flagsErr != nil {
+		return nil, errors.Wrap(_flagsErr, "Error parsing 'flags' field of AdsDataTypeTableEntry")
+	}
+	flags := _flags
+
+	// Implicit Field (nameLength) (Used for parsing, but its value is not stored as it's implicitly given by the objects content)
+	nameLength, _nameLengthErr := readBuffer.ReadUint16("nameLength", 16)
+	_ = nameLength
+	if _nameLengthErr != nil {
+		return nil, errors.Wrap(_nameLengthErr, "Error parsing 'nameLength' field of AdsDataTypeTableEntry")
+	}
+
+	// Implicit Field (typeNameLength) (Used for parsing, but its value is not stored as it's implicitly given by the objects content)
+	typeNameLength, _typeNameLengthErr := readBuffer.ReadUint16("typeNameLength", 16)
+	_ = typeNameLength
+	if _typeNameLengthErr != nil {
+		return nil, errors.Wrap(_typeNameLengthErr, "Error parsing 'typeNameLength' field of AdsDataTypeTableEntry")
+	}
+
+	// Implicit Field (commentLength) (Used for parsing, but its value is not stored as it's implicitly given by the objects content)
+	commentLength, _commentLengthErr := readBuffer.ReadUint16("commentLength", 16)
+	_ = commentLength
+	if _commentLengthErr != nil {
+		return nil, errors.Wrap(_commentLengthErr, "Error parsing 'commentLength' field of AdsDataTypeTableEntry")
+	}
+
+	// Simple Field (arrayDimensions)
+	_arrayDimensions, _arrayDimensionsErr := readBuffer.ReadUint16("arrayDimensions", 16)
+	if _arrayDimensionsErr != nil {
+		return nil, errors.Wrap(_arrayDimensionsErr, "Error parsing 'arrayDimensions' field of AdsDataTypeTableEntry")
+	}
+	arrayDimensions := _arrayDimensions
+
+	// Simple Field (numChildren)
+	_numChildren, _numChildrenErr := readBuffer.ReadUint16("numChildren", 16)
+	if _numChildrenErr != nil {
+		return nil, errors.Wrap(_numChildrenErr, "Error parsing 'numChildren' field of AdsDataTypeTableEntry")
+	}
+	numChildren := _numChildren
+
+	// Simple Field (name)
+	_name, _nameErr := readBuffer.ReadString("name", uint32((nameLength)*(8)))
+	if _nameErr != nil {
+		return nil, errors.Wrap(_nameErr, "Error parsing 'name' field of AdsDataTypeTableEntry")
+	}
+	name := _name
+
+	// Const Field (nameTerminator)
+	nameTerminator, _nameTerminatorErr := readBuffer.ReadUint8("nameTerminator", 8)
+	if _nameTerminatorErr != nil {
+		return nil, errors.Wrap(_nameTerminatorErr, "Error parsing 'nameTerminator' field of AdsDataTypeTableEntry")
+	}
+	if nameTerminator != AdsDataTypeTableEntry_NAMETERMINATOR {
+		return nil, errors.New("Expected constant value " + fmt.Sprintf("%d", AdsDataTypeTableEntry_NAMETERMINATOR) + " but got " + fmt.Sprintf("%d", nameTerminator))
+	}
+
+	// Simple Field (typeName)
+	_typeName, _typeNameErr := readBuffer.ReadString("typeName", uint32((typeNameLength)*(8)))
+	if _typeNameErr != nil {
+		return nil, errors.Wrap(_typeNameErr, "Error parsing 'typeName' field of AdsDataTypeTableEntry")
+	}
+	typeName := _typeName
+
+	// Const Field (typeNameTerminator)
+	typeNameTerminator, _typeNameTerminatorErr := readBuffer.ReadUint8("typeNameTerminator", 8)
+	if _typeNameTerminatorErr != nil {
+		return nil, errors.Wrap(_typeNameTerminatorErr, "Error parsing 'typeNameTerminator' field of AdsDataTypeTableEntry")
+	}
+	if typeNameTerminator != AdsDataTypeTableEntry_TYPENAMETERMINATOR {
+		return nil, errors.New("Expected constant value " + fmt.Sprintf("%d", AdsDataTypeTableEntry_TYPENAMETERMINATOR) + " but got " + fmt.Sprintf("%d", typeNameTerminator))
+	}
+
+	// Simple Field (comment)
+	_comment, _commentErr := readBuffer.ReadString("comment", uint32((commentLength)*(8)))
+	if _commentErr != nil {
+		return nil, errors.Wrap(_commentErr, "Error parsing 'comment' field of AdsDataTypeTableEntry")
+	}
+	comment := _comment
+
+	// Const Field (commentTerminator)
+	commentTerminator, _commentTerminatorErr := readBuffer.ReadUint8("commentTerminator", 8)
+	if _commentTerminatorErr != nil {
+		return nil, errors.Wrap(_commentTerminatorErr, "Error parsing 'commentTerminator' field of AdsDataTypeTableEntry")
+	}
+	if commentTerminator != AdsDataTypeTableEntry_COMMENTTERMINATOR {
+		return nil, errors.New("Expected constant value " + fmt.Sprintf("%d", AdsDataTypeTableEntry_COMMENTTERMINATOR) + " but got " + fmt.Sprintf("%d", commentTerminator))
+	}
+
+	// Array field (arrayInfo)
+	if pullErr := readBuffer.PullContext("arrayInfo", utils.WithRenderAsList(true)); pullErr != nil {
+		return nil, errors.Wrap(pullErr, "Error pulling for arrayInfo")
+	}
+	// Count array
+	arrayInfo := make([]AdsDataTypeArrayInfo, arrayDimensions)
+	// This happens when the size is set conditional to 0
+	if len(arrayInfo) == 0 {
+		arrayInfo = nil
+	}
+	{
+		for curItem := uint16(0); curItem < uint16(arrayDimensions); curItem++ {
+			_item, _err := AdsDataTypeArrayInfoParse(readBuffer)
+			if _err != nil {
+				return nil, errors.Wrap(_err, "Error parsing 'arrayInfo' field of AdsDataTypeTableEntry")
+			}
+			arrayInfo[curItem] = _item.(AdsDataTypeArrayInfo)
+		}
+	}
+	if closeErr := readBuffer.CloseContext("arrayInfo", utils.WithRenderAsList(true)); closeErr != nil {
+		return nil, errors.Wrap(closeErr, "Error closing for arrayInfo")
+	}
+
+	// Array field (children)
+	if pullErr := readBuffer.PullContext("children", utils.WithRenderAsList(true)); pullErr != nil {
+		return nil, errors.Wrap(pullErr, "Error pulling for children")
+	}
+	// Count array
+	children := make([]AdsDataTypeTableEntry, numChildren)
+	// This happens when the size is set conditional to 0
+	if len(children) == 0 {
+		children = nil
+	}
+	{
+		for curItem := uint16(0); curItem < uint16(numChildren); curItem++ {
+			_item, _err := AdsDataTypeTableEntryParse(readBuffer)
+			if _err != nil {
+				return nil, errors.Wrap(_err, "Error parsing 'children' field of AdsDataTypeTableEntry")
+			}
+			children[curItem] = _item.(AdsDataTypeTableEntry)
+		}
+	}
+	if closeErr := readBuffer.CloseContext("children", utils.WithRenderAsList(true)); closeErr != nil {
+		return nil, errors.Wrap(closeErr, "Error closing for children")
+	}
+	// Byte Array field (rest)
+	numberOfBytesrest := int(uint16(entryLength) - uint16(curPos))
+	rest, _readArrayErr := readBuffer.ReadByteArray("rest", numberOfBytesrest)
+	if _readArrayErr != nil {
+		return nil, errors.Wrap(_readArrayErr, "Error parsing 'rest' field of AdsDataTypeTableEntry")
+	}
+
+	if closeErr := readBuffer.CloseContext("AdsDataTypeTableEntry"); closeErr != nil {
+		return nil, errors.Wrap(closeErr, "Error closing for AdsDataTypeTableEntry")
+	}
+
+	// Create the instance
+	return &_AdsDataTypeTableEntry{
+		EntryLength:     entryLength,
+		Version:         version,
+		HashValue:       hashValue,
+		TypeHashValue:   typeHashValue,
+		Size:            size,
+		Offs:            offs,
+		DataType:        dataType,
+		Flags:           flags,
+		ArrayDimensions: arrayDimensions,
+		NumChildren:     numChildren,
+		Name:            name,
+		TypeName:        typeName,
+		Comment:         comment,
+		ArrayInfo:       arrayInfo,
+		Children:        children,
+		Rest:            rest,
+	}, nil
+}
+
+func (m *_AdsDataTypeTableEntry) Serialize(writeBuffer utils.WriteBuffer) error {
+	positionAware := writeBuffer
+	_ = positionAware
+	if pushErr := writeBuffer.PushContext("AdsDataTypeTableEntry"); pushErr != nil {
+		return errors.Wrap(pushErr, "Error pushing for AdsDataTypeTableEntry")
+	}
+
+	// Simple Field (entryLength)
+	entryLength := uint32(m.GetEntryLength())
+	_entryLengthErr := writeBuffer.WriteUint32("entryLength", 32, (entryLength))
+	if _entryLengthErr != nil {
+		return errors.Wrap(_entryLengthErr, "Error serializing 'entryLength' field")
+	}
+
+	// Simple Field (version)
+	version := uint32(m.GetVersion())
+	_versionErr := writeBuffer.WriteUint32("version", 32, (version))
+	if _versionErr != nil {
+		return errors.Wrap(_versionErr, "Error serializing 'version' field")
+	}
+
+	// Simple Field (hashValue)
+	hashValue := uint32(m.GetHashValue())
+	_hashValueErr := writeBuffer.WriteUint32("hashValue", 32, (hashValue))
+	if _hashValueErr != nil {
+		return errors.Wrap(_hashValueErr, "Error serializing 'hashValue' field")
+	}
+
+	// Simple Field (typeHashValue)
+	typeHashValue := uint32(m.GetTypeHashValue())
+	_typeHashValueErr := writeBuffer.WriteUint32("typeHashValue", 32, (typeHashValue))
+	if _typeHashValueErr != nil {
+		return errors.Wrap(_typeHashValueErr, "Error serializing 'typeHashValue' field")
+	}
+
+	// Simple Field (size)
+	size := uint32(m.GetSize())
+	_sizeErr := writeBuffer.WriteUint32("size", 32, (size))
+	if _sizeErr != nil {
+		return errors.Wrap(_sizeErr, "Error serializing 'size' field")
+	}
+
+	// Simple Field (offs)
+	offs := uint32(m.GetOffs())
+	_offsErr := writeBuffer.WriteUint32("offs", 32, (offs))
+	if _offsErr != nil {
+		return errors.Wrap(_offsErr, "Error serializing 'offs' field")
+	}
+
+	// Simple Field (dataType)
+	dataType := uint32(m.GetDataType())
+	_dataTypeErr := writeBuffer.WriteUint32("dataType", 32, (dataType))
+	if _dataTypeErr != nil {
+		return errors.Wrap(_dataTypeErr, "Error serializing 'dataType' field")
+	}
+
+	// Simple Field (flags)
+	flags := uint32(m.GetFlags())
+	_flagsErr := writeBuffer.WriteUint32("flags", 32, (flags))
+	if _flagsErr != nil {
+		return errors.Wrap(_flagsErr, "Error serializing 'flags' field")
+	}
+
+	// Implicit Field (nameLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+	nameLength := uint16(GetSTR_LEN()(m.GetName()))
+	_nameLengthErr := writeBuffer.WriteUint16("nameLength", 16, (nameLength))
+	if _nameLengthErr != nil {
+		return errors.Wrap(_nameLengthErr, "Error serializing 'nameLength' field")
+	}
+
+	// Implicit Field (typeNameLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+	typeNameLength := uint16(GetSTR_LEN()(m.GetTypeName()))
+	_typeNameLengthErr := writeBuffer.WriteUint16("typeNameLength", 16, (typeNameLength))
+	if _typeNameLengthErr != nil {
+		return errors.Wrap(_typeNameLengthErr, "Error serializing 'typeNameLength' field")
+	}
+
+	// Implicit Field (commentLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+	commentLength := uint16(GetSTR_LEN()(m.GetComment()))
+	_commentLengthErr := writeBuffer.WriteUint16("commentLength", 16, (commentLength))
+	if _commentLengthErr != nil {
+		return errors.Wrap(_commentLengthErr, "Error serializing 'commentLength' field")
+	}
+
+	// Simple Field (arrayDimensions)
+	arrayDimensions := uint16(m.GetArrayDimensions())
+	_arrayDimensionsErr := writeBuffer.WriteUint16("arrayDimensions", 16, (arrayDimensions))
+	if _arrayDimensionsErr != nil {
+		return errors.Wrap(_arrayDimensionsErr, "Error serializing 'arrayDimensions' field")
+	}
+
+	// Simple Field (numChildren)
+	numChildren := uint16(m.GetNumChildren())
+	_numChildrenErr := writeBuffer.WriteUint16("numChildren", 16, (numChildren))
+	if _numChildrenErr != nil {
+		return errors.Wrap(_numChildrenErr, "Error serializing 'numChildren' field")
+	}
+
+	// Simple Field (name)
+	name := string(m.GetName())
+	_nameErr := writeBuffer.WriteString("name", uint32((GetSTR_LEN()(m.GetName()))*(8)), "UTF-8", (name))
+	if _nameErr != nil {
+		return errors.Wrap(_nameErr, "Error serializing 'name' field")
+	}
+
+	// Const Field (nameTerminator)
+	_nameTerminatorErr := writeBuffer.WriteUint8("nameTerminator", 8, 0x00)
+	if _nameTerminatorErr != nil {
+		return errors.Wrap(_nameTerminatorErr, "Error serializing 'nameTerminator' field")
+	}
+
+	// Simple Field (typeName)
+	typeName := string(m.GetTypeName())
+	_typeNameErr := writeBuffer.WriteString("typeName", uint32((GetSTR_LEN()(m.GetTypeName()))*(8)), "UTF-8", (typeName))
+	if _typeNameErr != nil {
+		return errors.Wrap(_typeNameErr, "Error serializing 'typeName' field")
+	}
+
+	// Const Field (typeNameTerminator)
+	_typeNameTerminatorErr := writeBuffer.WriteUint8("typeNameTerminator", 8, 0x00)
+	if _typeNameTerminatorErr != nil {
+		return errors.Wrap(_typeNameTerminatorErr, "Error serializing 'typeNameTerminator' field")
+	}
+
+	// Simple Field (comment)
+	comment := string(m.GetComment())
+	_commentErr := writeBuffer.WriteString("comment", uint32((GetSTR_LEN()(m.GetComment()))*(8)), "UTF-8", (comment))
+	if _commentErr != nil {
+		return errors.Wrap(_commentErr, "Error serializing 'comment' field")
+	}
+
+	// Const Field (commentTerminator)
+	_commentTerminatorErr := writeBuffer.WriteUint8("commentTerminator", 8, 0x00)
+	if _commentTerminatorErr != nil {
+		return errors.Wrap(_commentTerminatorErr, "Error serializing 'commentTerminator' field")
+	}
+
+	// Array Field (arrayInfo)
+	if pushErr := writeBuffer.PushContext("arrayInfo", utils.WithRenderAsList(true)); pushErr != nil {
+		return errors.Wrap(pushErr, "Error pushing for arrayInfo")
+	}
+	for _, _element := range m.GetArrayInfo() {
+		_elementErr := writeBuffer.WriteSerializable(_element)
+		if _elementErr != nil {
+			return errors.Wrap(_elementErr, "Error serializing 'arrayInfo' field")
+		}
+	}
+	if popErr := writeBuffer.PopContext("arrayInfo", utils.WithRenderAsList(true)); popErr != nil {
+		return errors.Wrap(popErr, "Error popping for arrayInfo")
+	}
+
+	// Array Field (children)
+	if pushErr := writeBuffer.PushContext("children", utils.WithRenderAsList(true)); pushErr != nil {
+		return errors.Wrap(pushErr, "Error pushing for children")
+	}
+	for _, _element := range m.GetChildren() {
+		_elementErr := writeBuffer.WriteSerializable(_element)
+		if _elementErr != nil {
+			return errors.Wrap(_elementErr, "Error serializing 'children' field")
+		}
+	}
+	if popErr := writeBuffer.PopContext("children", utils.WithRenderAsList(true)); popErr != nil {
+		return errors.Wrap(popErr, "Error popping for children")
+	}
+
+	// Array Field (rest)
+	// Byte Array field (rest)
+	if err := writeBuffer.WriteByteArray("rest", m.GetRest()); err != nil {
+		return errors.Wrap(err, "Error serializing 'rest' field")
+	}
+
+	if popErr := writeBuffer.PopContext("AdsDataTypeTableEntry"); popErr != nil {
+		return errors.Wrap(popErr, "Error popping for AdsDataTypeTableEntry")
+	}
+	return nil
+}
+
+func (m *_AdsDataTypeTableEntry) isAdsDataTypeTableEntry() bool {
+	return true
+}
+
+func (m *_AdsDataTypeTableEntry) String() string {
+	if m == nil {
+		return "<nil>"
+	}
+	writeBuffer := utils.NewBoxedWriteBufferWithOptions(true, true)
+	if err := writeBuffer.WriteSerializable(m); err != nil {
+		return err.Error()
+	}
+	return writeBuffer.GetBox().String()
+}
diff --git a/plc4go/protocols/ads/readwrite/model/AdsSignificantGroupAddresses.go b/plc4go/protocols/ads/readwrite/model/AdsSignificantGroupAddresses.go
new file mode 100644
index 000000000..8f70a6bba
--- /dev/null
+++ b/plc4go/protocols/ads/readwrite/model/AdsSignificantGroupAddresses.go
@@ -0,0 +1,137 @@
+/*
+ * 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
+ *
+ *   https://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
+
+import (
+	"github.com/apache/plc4x/plc4go/internal/spi/utils"
+	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+)
+
+// Code generated by code-generation. DO NOT EDIT.
+
+// AdsSignificantGroupAddresses is an enum
+type AdsSignificantGroupAddresses uint32
+
+type IAdsSignificantGroupAddresses interface {
+	Serialize(writeBuffer utils.WriteBuffer) error
+}
+
+const (
+	AdsSignificantGroupAddresses_SYMBOL_TABLE    AdsSignificantGroupAddresses = 0x0000F00B
+	AdsSignificantGroupAddresses_DATA_TYPE_TABLE AdsSignificantGroupAddresses = 0x0000F00E
+	AdsSignificantGroupAddresses_TABLE_SIZES     AdsSignificantGroupAddresses = 0x0000F00F
+)
+
+var AdsSignificantGroupAddressesValues []AdsSignificantGroupAddresses
+
+func init() {
+	_ = errors.New
+	AdsSignificantGroupAddressesValues = []AdsSignificantGroupAddresses{
+		AdsSignificantGroupAddresses_SYMBOL_TABLE,
+		AdsSignificantGroupAddresses_DATA_TYPE_TABLE,
+		AdsSignificantGroupAddresses_TABLE_SIZES,
+	}
+}
+
+func AdsSignificantGroupAddressesByValue(value uint32) (enum AdsSignificantGroupAddresses, ok bool) {
+	switch value {
+	case 0x0000F00B:
+		return AdsSignificantGroupAddresses_SYMBOL_TABLE, true
+	case 0x0000F00E:
+		return AdsSignificantGroupAddresses_DATA_TYPE_TABLE, true
+	case 0x0000F00F:
+		return AdsSignificantGroupAddresses_TABLE_SIZES, true
+	}
+	return 0, false
+}
+
+func AdsSignificantGroupAddressesByName(value string) (enum AdsSignificantGroupAddresses, ok bool) {
+	switch value {
+	case "SYMBOL_TABLE":
+		return AdsSignificantGroupAddresses_SYMBOL_TABLE, true
+	case "DATA_TYPE_TABLE":
+		return AdsSignificantGroupAddresses_DATA_TYPE_TABLE, true
+	case "TABLE_SIZES":
+		return AdsSignificantGroupAddresses_TABLE_SIZES, true
+	}
+	return 0, false
+}
+
+func AdsSignificantGroupAddressesKnows(value uint32) bool {
+	for _, typeValue := range AdsSignificantGroupAddressesValues {
+		if uint32(typeValue) == value {
+			return true
+		}
+	}
+	return false
+}
+
+func CastAdsSignificantGroupAddresses(structType interface{}) AdsSignificantGroupAddresses {
+	castFunc := func(typ interface{}) AdsSignificantGroupAddresses {
+		if sAdsSignificantGroupAddresses, ok := typ.(AdsSignificantGroupAddresses); ok {
+			return sAdsSignificantGroupAddresses
+		}
+		return 0
+	}
+	return castFunc(structType)
+}
+
+func (m AdsSignificantGroupAddresses) GetLengthInBits() uint16 {
+	return 32
+}
+
+func (m AdsSignificantGroupAddresses) GetLengthInBytes() uint16 {
+	return m.GetLengthInBits() / 8
+}
+
+func AdsSignificantGroupAddressesParse(readBuffer utils.ReadBuffer) (AdsSignificantGroupAddresses, error) {
+	val, err := readBuffer.ReadUint32("AdsSignificantGroupAddresses", 32)
+	if err != nil {
+		return 0, errors.Wrap(err, "error reading AdsSignificantGroupAddresses")
+	}
+	if enum, ok := AdsSignificantGroupAddressesByValue(val); !ok {
+		log.Debug().Msgf("no value %x found for RequestType", val)
+		return AdsSignificantGroupAddresses(val), nil
+	} else {
+		return enum, nil
+	}
+}
+
+func (e AdsSignificantGroupAddresses) Serialize(writeBuffer utils.WriteBuffer) error {
+	return writeBuffer.WriteUint32("AdsSignificantGroupAddresses", 32, uint32(e), utils.WithAdditionalStringRepresentation(e.PLC4XEnumName()))
+}
+
+// PLC4XEnumName returns the name that is used in code to identify this enum
+func (e AdsSignificantGroupAddresses) PLC4XEnumName() string {
+	switch e {
+	case AdsSignificantGroupAddresses_SYMBOL_TABLE:
+		return "SYMBOL_TABLE"
+	case AdsSignificantGroupAddresses_DATA_TYPE_TABLE:
+		return "DATA_TYPE_TABLE"
+	case AdsSignificantGroupAddresses_TABLE_SIZES:
+		return "TABLE_SIZES"
+	}
+	return ""
+}
+
+func (e AdsSignificantGroupAddresses) String() string {
+	return e.PLC4XEnumName()
+}
diff --git a/plc4go/protocols/ads/readwrite/model/AdsSymbolTableEntry.go b/plc4go/protocols/ads/readwrite/model/AdsSymbolTableEntry.go
new file mode 100644
index 000000000..6c4a3920a
--- /dev/null
+++ b/plc4go/protocols/ads/readwrite/model/AdsSymbolTableEntry.go
@@ -0,0 +1,896 @@
+/*
+ * 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
+ *
+ *   https://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
+
+import (
+	"fmt"
+	"github.com/apache/plc4x/plc4go/internal/spi/utils"
+	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+)
+
+// Code generated by code-generation. DO NOT EDIT.
+
+// Constant values.
+const AdsSymbolTableEntry_NAMETERMINATOR uint8 = 0x00
+const AdsSymbolTableEntry_TYPENAMETERMINATOR uint8 = 0x00
+const AdsSymbolTableEntry_COMMENTTERMINATOR uint8 = 0x00
+
+// AdsSymbolTableEntry is the corresponding interface of AdsSymbolTableEntry
+type AdsSymbolTableEntry interface {
+	utils.LengthAware
+	utils.Serializable
+	// GetEntryLength returns EntryLength (property field)
+	GetEntryLength() uint32
+	// GetGroup returns Group (property field)
+	GetGroup() uint32
+	// GetOffset returns Offset (property field)
+	GetOffset() uint32
+	// GetSize returns Size (property field)
+	GetSize() uint32
+	// GetDataType returns DataType (property field)
+	GetDataType() uint32
+	// GetFlagMethodDeref returns FlagMethodDeref (property field)
+	GetFlagMethodDeref() bool
+	// GetFlagItfMethodAccess returns FlagItfMethodAccess (property field)
+	GetFlagItfMethodAccess() bool
+	// GetFlagReadOnly returns FlagReadOnly (property field)
+	GetFlagReadOnly() bool
+	// GetFlagTComInterfacePointer returns FlagTComInterfacePointer (property field)
+	GetFlagTComInterfacePointer() bool
+	// GetFlagTypeGuid returns FlagTypeGuid (property field)
+	GetFlagTypeGuid() bool
+	// GetFlagReferenceTo returns FlagReferenceTo (property field)
+	GetFlagReferenceTo() bool
+	// GetFlagBitValue returns FlagBitValue (property field)
+	GetFlagBitValue() bool
+	// GetFlagPersistent returns FlagPersistent (property field)
+	GetFlagPersistent() bool
+	// GetFlagExtendedFlags returns FlagExtendedFlags (property field)
+	GetFlagExtendedFlags() bool
+	// GetFlagInitOnReset returns FlagInitOnReset (property field)
+	GetFlagInitOnReset() bool
+	// GetFlagStatic returns FlagStatic (property field)
+	GetFlagStatic() bool
+	// GetFlagAttributes returns FlagAttributes (property field)
+	GetFlagAttributes() bool
+	// GetFlagContextMask returns FlagContextMask (property field)
+	GetFlagContextMask() bool
+	// GetName returns Name (property field)
+	GetName() string
+	// GetTypeName returns TypeName (property field)
+	GetTypeName() string
+	// GetComment returns Comment (property field)
+	GetComment() string
+	// GetRest returns Rest (property field)
+	GetRest() []byte
+}
+
+// AdsSymbolTableEntryExactly can be used when we want exactly this type and not a type which fulfills AdsSymbolTableEntry.
+// This is useful for switch cases.
+type AdsSymbolTableEntryExactly interface {
+	AdsSymbolTableEntry
+	isAdsSymbolTableEntry() bool
+}
+
+// _AdsSymbolTableEntry is the data-structure of this message
+type _AdsSymbolTableEntry struct {
+	EntryLength              uint32
+	Group                    uint32
+	Offset                   uint32
+	Size                     uint32
+	DataType                 uint32
+	FlagMethodDeref          bool
+	FlagItfMethodAccess      bool
+	FlagReadOnly             bool
+	FlagTComInterfacePointer bool
+	FlagTypeGuid             bool
+	FlagReferenceTo          bool
+	FlagBitValue             bool
+	FlagPersistent           bool
+	FlagExtendedFlags        bool
+	FlagInitOnReset          bool
+	FlagStatic               bool
+	FlagAttributes           bool
+	FlagContextMask          bool
+	Name                     string
+	TypeName                 string
+	Comment                  string
+	Rest                     []byte
+	// Reserved Fields
+	reservedField0 *uint8
+	reservedField1 *uint16
+}
+
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+/////////////////////// Accessors for property fields.
+///////////////////////
+
+func (m *_AdsSymbolTableEntry) GetEntryLength() uint32 {
+	return m.EntryLength
+}
+
+func (m *_AdsSymbolTableEntry) GetGroup() uint32 {
+	return m.Group
+}
+
+func (m *_AdsSymbolTableEntry) GetOffset() uint32 {
+	return m.Offset
+}
+
+func (m *_AdsSymbolTableEntry) GetSize() uint32 {
+	return m.Size
+}
+
+func (m *_AdsSymbolTableEntry) GetDataType() uint32 {
+	return m.DataType
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagMethodDeref() bool {
+	return m.FlagMethodDeref
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagItfMethodAccess() bool {
+	return m.FlagItfMethodAccess
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagReadOnly() bool {
+	return m.FlagReadOnly
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagTComInterfacePointer() bool {
+	return m.FlagTComInterfacePointer
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagTypeGuid() bool {
+	return m.FlagTypeGuid
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagReferenceTo() bool {
+	return m.FlagReferenceTo
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagBitValue() bool {
+	return m.FlagBitValue
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagPersistent() bool {
+	return m.FlagPersistent
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagExtendedFlags() bool {
+	return m.FlagExtendedFlags
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagInitOnReset() bool {
+	return m.FlagInitOnReset
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagStatic() bool {
+	return m.FlagStatic
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagAttributes() bool {
+	return m.FlagAttributes
+}
+
+func (m *_AdsSymbolTableEntry) GetFlagContextMask() bool {
+	return m.FlagContextMask
+}
+
+func (m *_AdsSymbolTableEntry) GetName() string {
+	return m.Name
+}
+
+func (m *_AdsSymbolTableEntry) GetTypeName() string {
+	return m.TypeName
+}
+
+func (m *_AdsSymbolTableEntry) GetComment() string {
+	return m.Comment
+}
+
+func (m *_AdsSymbolTableEntry) GetRest() []byte {
+	return m.Rest
+}
+
+///////////////////////
+///////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+/////////////////////// Accessors for const fields.
+///////////////////////
+
+func (m *_AdsSymbolTableEntry) GetNameTerminator() uint8 {
+	return AdsSymbolTableEntry_NAMETERMINATOR
+}
+
+func (m *_AdsSymbolTableEntry) GetTypeNameTerminator() uint8 {
+	return AdsSymbolTableEntry_TYPENAMETERMINATOR
+}
+
+func (m *_AdsSymbolTableEntry) GetCommentTerminator() uint8 {
+	return AdsSymbolTableEntry_COMMENTTERMINATOR
+}
+
+///////////////////////
+///////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+
+// NewAdsSymbolTableEntry factory function for _AdsSymbolTableEntry
+func NewAdsSymbolTableEntry(entryLength uint32, group uint32, offset uint32, size uint32, dataType uint32, flagMethodDeref bool, flagItfMethodAccess bool, flagReadOnly bool, flagTComInterfacePointer bool, flagTypeGuid bool, flagReferenceTo bool, flagBitValue bool, flagPersistent bool, flagExtendedFlags bool, flagInitOnReset bool, flagStatic bool, flagAttributes bool, flagContextMask bool, name string, typeName string, comment string, rest []byte) *_AdsSymbolTableEntry {
+	return &_AdsSymbolTableEntry{EntryLength: entryLength, Group: group, Offset: offset, Size: size, DataType: dataType, FlagMethodDeref: flagMethodDeref, FlagItfMethodAccess: flagItfMethodAccess, FlagReadOnly: flagReadOnly, FlagTComInterfacePointer: flagTComInterfacePointer, FlagTypeGuid: flagTypeGuid, FlagReferenceTo: flagReferenceTo, FlagBitValue: flagBitValue, FlagPersistent: flagPersistent, FlagExtendedFlags: flagExtendedFlags, FlagInitOnReset: flagInitOnReset, FlagStatic: flagStatic,  [...]
+}
+
+// Deprecated: use the interface for direct cast
+func CastAdsSymbolTableEntry(structType interface{}) AdsSymbolTableEntry {
+	if casted, ok := structType.(AdsSymbolTableEntry); ok {
+		return casted
+	}
+	if casted, ok := structType.(*AdsSymbolTableEntry); ok {
+		return *casted
+	}
+	return nil
+}
+
+func (m *_AdsSymbolTableEntry) GetTypeName() string {
+	return "AdsSymbolTableEntry"
+}
+
+func (m *_AdsSymbolTableEntry) GetLengthInBits() uint16 {
+	return m.GetLengthInBitsConditional(false)
+}
+
+func (m *_AdsSymbolTableEntry) GetLengthInBitsConditional(lastItem bool) uint16 {
+	lengthInBits := uint16(0)
+
+	// Simple field (entryLength)
+	lengthInBits += 32
+
+	// Simple field (group)
+	lengthInBits += 32
+
+	// Simple field (offset)
+	lengthInBits += 32
+
+	// Simple field (size)
+	lengthInBits += 32
+
+	// Simple field (dataType)
+	lengthInBits += 32
+
+	// Simple field (flagMethodDeref)
+	lengthInBits += 1
+
+	// Simple field (flagItfMethodAccess)
+	lengthInBits += 1
+
+	// Simple field (flagReadOnly)
+	lengthInBits += 1
+
+	// Simple field (flagTComInterfacePointer)
+	lengthInBits += 1
+
+	// Simple field (flagTypeGuid)
+	lengthInBits += 1
+
+	// Simple field (flagReferenceTo)
+	lengthInBits += 1
+
+	// Simple field (flagBitValue)
+	lengthInBits += 1
+
+	// Simple field (flagPersistent)
+	lengthInBits += 1
+
+	// Reserved Field (reserved)
+	lengthInBits += 3
+
+	// Simple field (flagExtendedFlags)
+	lengthInBits += 1
+
+	// Simple field (flagInitOnReset)
+	lengthInBits += 1
+
+	// Simple field (flagStatic)
+	lengthInBits += 1
+
+	// Simple field (flagAttributes)
+	lengthInBits += 1
+
+	// Simple field (flagContextMask)
+	lengthInBits += 1
+
+	// Reserved Field (reserved)
+	lengthInBits += 16
+
+	// Implicit Field (nameLength)
+	lengthInBits += 16
+
+	// Implicit Field (typeNameLength)
+	lengthInBits += 16
+
+	// Implicit Field (commentLength)
+	lengthInBits += 16
+
+	// Simple field (name)
+	lengthInBits += uint16(int32(GetSTR_LEN()(m.GetName())) * int32(int32(8)))
+
+	// Const Field (nameTerminator)
+	lengthInBits += 8
+
+	// Simple field (typeName)
+	lengthInBits += uint16(int32(GetSTR_LEN()(m.GetTypeName())) * int32(int32(8)))
+
+	// Const Field (typeNameTerminator)
+	lengthInBits += 8
+
+	// Simple field (comment)
+	lengthInBits += uint16(int32(GetSTR_LEN()(m.GetComment())) * int32(int32(8)))
+
+	// Const Field (commentTerminator)
+	lengthInBits += 8
+
+	// Array field
+	if len(m.Rest) > 0 {
+		lengthInBits += 8 * uint16(len(m.Rest))
+	}
+
+	return lengthInBits
+}
+
+func (m *_AdsSymbolTableEntry) GetLengthInBytes() uint16 {
+	return m.GetLengthInBits() / 8
+}
+
+func AdsSymbolTableEntryParse(readBuffer utils.ReadBuffer) (AdsSymbolTableEntry, error) {
+	positionAware := readBuffer
+	_ = positionAware
+	if pullErr := readBuffer.PullContext("AdsSymbolTableEntry"); pullErr != nil {
+		return nil, errors.Wrap(pullErr, "Error pulling for AdsSymbolTableEntry")
+	}
+	currentPos := positionAware.GetPos()
+	_ = currentPos
+	var startPos = positionAware.GetPos()
+	var curPos uint16
+
+	// Simple Field (entryLength)
+	_entryLength, _entryLengthErr := readBuffer.ReadUint32("entryLength", 32)
+	if _entryLengthErr != nil {
+		return nil, errors.Wrap(_entryLengthErr, "Error parsing 'entryLength' field of AdsSymbolTableEntry")
+	}
+	entryLength := _entryLength
+
+	// Simple Field (group)
+	_group, _groupErr := readBuffer.ReadUint32("group", 32)
+	if _groupErr != nil {
+		return nil, errors.Wrap(_groupErr, "Error parsing 'group' field of AdsSymbolTableEntry")
+	}
+	group := _group
+
+	// Simple Field (offset)
+	_offset, _offsetErr := readBuffer.ReadUint32("offset", 32)
+	if _offsetErr != nil {
+		return nil, errors.Wrap(_offsetErr, "Error parsing 'offset' field of AdsSymbolTableEntry")
+	}
+	offset := _offset
+
+	// Simple Field (size)
+	_size, _sizeErr := readBuffer.ReadUint32("size", 32)
+	if _sizeErr != nil {
+		return nil, errors.Wrap(_sizeErr, "Error parsing 'size' field of AdsSymbolTableEntry")
+	}
+	size := _size
+
+	// Simple Field (dataType)
+	_dataType, _dataTypeErr := readBuffer.ReadUint32("dataType", 32)
+	if _dataTypeErr != nil {
+		return nil, errors.Wrap(_dataTypeErr, "Error parsing 'dataType' field of AdsSymbolTableEntry")
+	}
+	dataType := _dataType
+
+	// Simple Field (flagMethodDeref)
+	_flagMethodDeref, _flagMethodDerefErr := readBuffer.ReadBit("flagMethodDeref")
+	if _flagMethodDerefErr != nil {
+		return nil, errors.Wrap(_flagMethodDerefErr, "Error parsing 'flagMethodDeref' field of AdsSymbolTableEntry")
+	}
+	flagMethodDeref := _flagMethodDeref
+
+	// Simple Field (flagItfMethodAccess)
+	_flagItfMethodAccess, _flagItfMethodAccessErr := readBuffer.ReadBit("flagItfMethodAccess")
+	if _flagItfMethodAccessErr != nil {
+		return nil, errors.Wrap(_flagItfMethodAccessErr, "Error parsing 'flagItfMethodAccess' field of AdsSymbolTableEntry")
+	}
+	flagItfMethodAccess := _flagItfMethodAccess
+
+	// Simple Field (flagReadOnly)
+	_flagReadOnly, _flagReadOnlyErr := readBuffer.ReadBit("flagReadOnly")
+	if _flagReadOnlyErr != nil {
+		return nil, errors.Wrap(_flagReadOnlyErr, "Error parsing 'flagReadOnly' field of AdsSymbolTableEntry")
+	}
+	flagReadOnly := _flagReadOnly
+
+	// Simple Field (flagTComInterfacePointer)
+	_flagTComInterfacePointer, _flagTComInterfacePointerErr := readBuffer.ReadBit("flagTComInterfacePointer")
+	if _flagTComInterfacePointerErr != nil {
+		return nil, errors.Wrap(_flagTComInterfacePointerErr, "Error parsing 'flagTComInterfacePointer' field of AdsSymbolTableEntry")
+	}
+	flagTComInterfacePointer := _flagTComInterfacePointer
+
+	// Simple Field (flagTypeGuid)
+	_flagTypeGuid, _flagTypeGuidErr := readBuffer.ReadBit("flagTypeGuid")
+	if _flagTypeGuidErr != nil {
+		return nil, errors.Wrap(_flagTypeGuidErr, "Error parsing 'flagTypeGuid' field of AdsSymbolTableEntry")
+	}
+	flagTypeGuid := _flagTypeGuid
+
+	// Simple Field (flagReferenceTo)
+	_flagReferenceTo, _flagReferenceToErr := readBuffer.ReadBit("flagReferenceTo")
+	if _flagReferenceToErr != nil {
+		return nil, errors.Wrap(_flagReferenceToErr, "Error parsing 'flagReferenceTo' field of AdsSymbolTableEntry")
+	}
+	flagReferenceTo := _flagReferenceTo
+
+	// Simple Field (flagBitValue)
+	_flagBitValue, _flagBitValueErr := readBuffer.ReadBit("flagBitValue")
+	if _flagBitValueErr != nil {
+		return nil, errors.Wrap(_flagBitValueErr, "Error parsing 'flagBitValue' field of AdsSymbolTableEntry")
+	}
+	flagBitValue := _flagBitValue
+
+	// Simple Field (flagPersistent)
+	_flagPersistent, _flagPersistentErr := readBuffer.ReadBit("flagPersistent")
+	if _flagPersistentErr != nil {
+		return nil, errors.Wrap(_flagPersistentErr, "Error parsing 'flagPersistent' field of AdsSymbolTableEntry")
+	}
+	flagPersistent := _flagPersistent
+
+	var reservedField0 *uint8
+	// Reserved Field (Compartmentalized so the "reserved" variable can't leak)
+	{
+		reserved, _err := readBuffer.ReadUint8("reserved", 3)
+		if _err != nil {
+			return nil, errors.Wrap(_err, "Error parsing 'reserved' field of AdsSymbolTableEntry")
+		}
+		if reserved != uint8(0x00) {
+			log.Info().Fields(map[string]interface{}{
+				"expected value": uint8(0x00),
+				"got value":      reserved,
+			}).Msg("Got unexpected response for reserved field.")
+			// We save the value, so it can be re-serialized
+			reservedField0 = &reserved
+		}
+	}
+
+	// Simple Field (flagExtendedFlags)
+	_flagExtendedFlags, _flagExtendedFlagsErr := readBuffer.ReadBit("flagExtendedFlags")
+	if _flagExtendedFlagsErr != nil {
+		return nil, errors.Wrap(_flagExtendedFlagsErr, "Error parsing 'flagExtendedFlags' field of AdsSymbolTableEntry")
+	}
+	flagExtendedFlags := _flagExtendedFlags
+
+	// Simple Field (flagInitOnReset)
+	_flagInitOnReset, _flagInitOnResetErr := readBuffer.ReadBit("flagInitOnReset")
+	if _flagInitOnResetErr != nil {
+		return nil, errors.Wrap(_flagInitOnResetErr, "Error parsing 'flagInitOnReset' field of AdsSymbolTableEntry")
+	}
+	flagInitOnReset := _flagInitOnReset
+
+	// Simple Field (flagStatic)
+	_flagStatic, _flagStaticErr := readBuffer.ReadBit("flagStatic")
+	if _flagStaticErr != nil {
+		return nil, errors.Wrap(_flagStaticErr, "Error parsing 'flagStatic' field of AdsSymbolTableEntry")
+	}
+	flagStatic := _flagStatic
+
+	// Simple Field (flagAttributes)
+	_flagAttributes, _flagAttributesErr := readBuffer.ReadBit("flagAttributes")
+	if _flagAttributesErr != nil {
+		return nil, errors.Wrap(_flagAttributesErr, "Error parsing 'flagAttributes' field of AdsSymbolTableEntry")
+	}
+	flagAttributes := _flagAttributes
+
+	// Simple Field (flagContextMask)
+	_flagContextMask, _flagContextMaskErr := readBuffer.ReadBit("flagContextMask")
+	if _flagContextMaskErr != nil {
+		return nil, errors.Wrap(_flagContextMaskErr, "Error parsing 'flagContextMask' field of AdsSymbolTableEntry")
+	}
+	flagContextMask := _flagContextMask
+
+	var reservedField1 *uint16
+	// Reserved Field (Compartmentalized so the "reserved" variable can't leak)
+	{
+		reserved, _err := readBuffer.ReadUint16("reserved", 16)
+		if _err != nil {
+			return nil, errors.Wrap(_err, "Error parsing 'reserved' field of AdsSymbolTableEntry")
+		}
+		if reserved != uint16(0x0000) {
+			log.Info().Fields(map[string]interface{}{
+				"expected value": uint16(0x0000),
+				"got value":      reserved,
+			}).Msg("Got unexpected response for reserved field.")
+			// We save the value, so it can be re-serialized
+			reservedField1 = &reserved
+		}
+	}
+
+	// Implicit Field (nameLength) (Used for parsing, but its value is not stored as it's implicitly given by the objects content)
+	nameLength, _nameLengthErr := readBuffer.ReadUint16("nameLength", 16)
+	_ = nameLength
+	if _nameLengthErr != nil {
+		return nil, errors.Wrap(_nameLengthErr, "Error parsing 'nameLength' field of AdsSymbolTableEntry")
+	}
+
+	// Implicit Field (typeNameLength) (Used for parsing, but its value is not stored as it's implicitly given by the objects content)
+	typeNameLength, _typeNameLengthErr := readBuffer.ReadUint16("typeNameLength", 16)
+	_ = typeNameLength
+	if _typeNameLengthErr != nil {
+		return nil, errors.Wrap(_typeNameLengthErr, "Error parsing 'typeNameLength' field of AdsSymbolTableEntry")
+	}
+
+	// Implicit Field (commentLength) (Used for parsing, but its value is not stored as it's implicitly given by the objects content)
+	commentLength, _commentLengthErr := readBuffer.ReadUint16("commentLength", 16)
+	_ = commentLength
+	if _commentLengthErr != nil {
+		return nil, errors.Wrap(_commentLengthErr, "Error parsing 'commentLength' field of AdsSymbolTableEntry")
+	}
+
+	// Simple Field (name)
+	_name, _nameErr := readBuffer.ReadString("name", uint32((nameLength)*(8)))
+	if _nameErr != nil {
+		return nil, errors.Wrap(_nameErr, "Error parsing 'name' field of AdsSymbolTableEntry")
+	}
+	name := _name
+
+	// Const Field (nameTerminator)
+	nameTerminator, _nameTerminatorErr := readBuffer.ReadUint8("nameTerminator", 8)
+	if _nameTerminatorErr != nil {
+		return nil, errors.Wrap(_nameTerminatorErr, "Error parsing 'nameTerminator' field of AdsSymbolTableEntry")
+	}
+	if nameTerminator != AdsSymbolTableEntry_NAMETERMINATOR {
+		return nil, errors.New("Expected constant value " + fmt.Sprintf("%d", AdsSymbolTableEntry_NAMETERMINATOR) + " but got " + fmt.Sprintf("%d", nameTerminator))
+	}
+
+	// Simple Field (typeName)
+	_typeName, _typeNameErr := readBuffer.ReadString("typeName", uint32((typeNameLength)*(8)))
+	if _typeNameErr != nil {
+		return nil, errors.Wrap(_typeNameErr, "Error parsing 'typeName' field of AdsSymbolTableEntry")
+	}
+	typeName := _typeName
+
+	// Const Field (typeNameTerminator)
+	typeNameTerminator, _typeNameTerminatorErr := readBuffer.ReadUint8("typeNameTerminator", 8)
+	if _typeNameTerminatorErr != nil {
+		return nil, errors.Wrap(_typeNameTerminatorErr, "Error parsing 'typeNameTerminator' field of AdsSymbolTableEntry")
+	}
+	if typeNameTerminator != AdsSymbolTableEntry_TYPENAMETERMINATOR {
+		return nil, errors.New("Expected constant value " + fmt.Sprintf("%d", AdsSymbolTableEntry_TYPENAMETERMINATOR) + " but got " + fmt.Sprintf("%d", typeNameTerminator))
+	}
+
+	// Simple Field (comment)
+	_comment, _commentErr := readBuffer.ReadString("comment", uint32((commentLength)*(8)))
+	if _commentErr != nil {
+		return nil, errors.Wrap(_commentErr, "Error parsing 'comment' field of AdsSymbolTableEntry")
+	}
+	comment := _comment
+
+	// Const Field (commentTerminator)
+	commentTerminator, _commentTerminatorErr := readBuffer.ReadUint8("commentTerminator", 8)
+	if _commentTerminatorErr != nil {
+		return nil, errors.Wrap(_commentTerminatorErr, "Error parsing 'commentTerminator' field of AdsSymbolTableEntry")
+	}
+	if commentTerminator != AdsSymbolTableEntry_COMMENTTERMINATOR {
+		return nil, errors.New("Expected constant value " + fmt.Sprintf("%d", AdsSymbolTableEntry_COMMENTTERMINATOR) + " but got " + fmt.Sprintf("%d", commentTerminator))
+	}
+	// Byte Array field (rest)
+	numberOfBytesrest := int(uint16(entryLength) - uint16(curPos))
+	rest, _readArrayErr := readBuffer.ReadByteArray("rest", numberOfBytesrest)
+	if _readArrayErr != nil {
+		return nil, errors.Wrap(_readArrayErr, "Error parsing 'rest' field of AdsSymbolTableEntry")
+	}
+
+	if closeErr := readBuffer.CloseContext("AdsSymbolTableEntry"); closeErr != nil {
+		return nil, errors.Wrap(closeErr, "Error closing for AdsSymbolTableEntry")
+	}
+
+	// Create the instance
+	return &_AdsSymbolTableEntry{
+		EntryLength:              entryLength,
+		Group:                    group,
+		Offset:                   offset,
+		Size:                     size,
+		DataType:                 dataType,
+		FlagMethodDeref:          flagMethodDeref,
+		FlagItfMethodAccess:      flagItfMethodAccess,
+		FlagReadOnly:             flagReadOnly,
+		FlagTComInterfacePointer: flagTComInterfacePointer,
+		FlagTypeGuid:             flagTypeGuid,
+		FlagReferenceTo:          flagReferenceTo,
+		FlagBitValue:             flagBitValue,
+		FlagPersistent:           flagPersistent,
+		FlagExtendedFlags:        flagExtendedFlags,
+		FlagInitOnReset:          flagInitOnReset,
+		FlagStatic:               flagStatic,
+		FlagAttributes:           flagAttributes,
+		FlagContextMask:          flagContextMask,
+		Name:                     name,
+		TypeName:                 typeName,
+		Comment:                  comment,
+		Rest:                     rest,
+		reservedField0:           reservedField0,
+		reservedField1:           reservedField1,
+	}, nil
+}
+
+func (m *_AdsSymbolTableEntry) Serialize(writeBuffer utils.WriteBuffer) error {
+	positionAware := writeBuffer
+	_ = positionAware
+	if pushErr := writeBuffer.PushContext("AdsSymbolTableEntry"); pushErr != nil {
+		return errors.Wrap(pushErr, "Error pushing for AdsSymbolTableEntry")
+	}
+
+	// Simple Field (entryLength)
+	entryLength := uint32(m.GetEntryLength())
+	_entryLengthErr := writeBuffer.WriteUint32("entryLength", 32, (entryLength))
+	if _entryLengthErr != nil {
+		return errors.Wrap(_entryLengthErr, "Error serializing 'entryLength' field")
+	}
+
+	// Simple Field (group)
+	group := uint32(m.GetGroup())
+	_groupErr := writeBuffer.WriteUint32("group", 32, (group))
+	if _groupErr != nil {
+		return errors.Wrap(_groupErr, "Error serializing 'group' field")
+	}
+
+	// Simple Field (offset)
+	offset := uint32(m.GetOffset())
+	_offsetErr := writeBuffer.WriteUint32("offset", 32, (offset))
+	if _offsetErr != nil {
+		return errors.Wrap(_offsetErr, "Error serializing 'offset' field")
+	}
+
+	// Simple Field (size)
+	size := uint32(m.GetSize())
+	_sizeErr := writeBuffer.WriteUint32("size", 32, (size))
+	if _sizeErr != nil {
+		return errors.Wrap(_sizeErr, "Error serializing 'size' field")
+	}
+
+	// Simple Field (dataType)
+	dataType := uint32(m.GetDataType())
+	_dataTypeErr := writeBuffer.WriteUint32("dataType", 32, (dataType))
+	if _dataTypeErr != nil {
+		return errors.Wrap(_dataTypeErr, "Error serializing 'dataType' field")
+	}
+
+	// Simple Field (flagMethodDeref)
+	flagMethodDeref := bool(m.GetFlagMethodDeref())
+	_flagMethodDerefErr := writeBuffer.WriteBit("flagMethodDeref", (flagMethodDeref))
+	if _flagMethodDerefErr != nil {
+		return errors.Wrap(_flagMethodDerefErr, "Error serializing 'flagMethodDeref' field")
+	}
+
+	// Simple Field (flagItfMethodAccess)
+	flagItfMethodAccess := bool(m.GetFlagItfMethodAccess())
+	_flagItfMethodAccessErr := writeBuffer.WriteBit("flagItfMethodAccess", (flagItfMethodAccess))
+	if _flagItfMethodAccessErr != nil {
+		return errors.Wrap(_flagItfMethodAccessErr, "Error serializing 'flagItfMethodAccess' field")
+	}
+
+	// Simple Field (flagReadOnly)
+	flagReadOnly := bool(m.GetFlagReadOnly())
+	_flagReadOnlyErr := writeBuffer.WriteBit("flagReadOnly", (flagReadOnly))
+	if _flagReadOnlyErr != nil {
+		return errors.Wrap(_flagReadOnlyErr, "Error serializing 'flagReadOnly' field")
+	}
+
+	// Simple Field (flagTComInterfacePointer)
+	flagTComInterfacePointer := bool(m.GetFlagTComInterfacePointer())
+	_flagTComInterfacePointerErr := writeBuffer.WriteBit("flagTComInterfacePointer", (flagTComInterfacePointer))
+	if _flagTComInterfacePointerErr != nil {
+		return errors.Wrap(_flagTComInterfacePointerErr, "Error serializing 'flagTComInterfacePointer' field")
+	}
+
+	// Simple Field (flagTypeGuid)
+	flagTypeGuid := bool(m.GetFlagTypeGuid())
+	_flagTypeGuidErr := writeBuffer.WriteBit("flagTypeGuid", (flagTypeGuid))
+	if _flagTypeGuidErr != nil {
+		return errors.Wrap(_flagTypeGuidErr, "Error serializing 'flagTypeGuid' field")
+	}
+
+	// Simple Field (flagReferenceTo)
+	flagReferenceTo := bool(m.GetFlagReferenceTo())
+	_flagReferenceToErr := writeBuffer.WriteBit("flagReferenceTo", (flagReferenceTo))
+	if _flagReferenceToErr != nil {
+		return errors.Wrap(_flagReferenceToErr, "Error serializing 'flagReferenceTo' field")
+	}
+
+	// Simple Field (flagBitValue)
+	flagBitValue := bool(m.GetFlagBitValue())
+	_flagBitValueErr := writeBuffer.WriteBit("flagBitValue", (flagBitValue))
+	if _flagBitValueErr != nil {
+		return errors.Wrap(_flagBitValueErr, "Error serializing 'flagBitValue' field")
+	}
+
+	// Simple Field (flagPersistent)
+	flagPersistent := bool(m.GetFlagPersistent())
+	_flagPersistentErr := writeBuffer.WriteBit("flagPersistent", (flagPersistent))
+	if _flagPersistentErr != nil {
+		return errors.Wrap(_flagPersistentErr, "Error serializing 'flagPersistent' field")
+	}
+
+	// Reserved Field (reserved)
+	{
+		var reserved uint8 = uint8(0x00)
+		if m.reservedField0 != nil {
+			log.Info().Fields(map[string]interface{}{
+				"expected value": uint8(0x00),
+				"got value":      reserved,
+			}).Msg("Overriding reserved field with unexpected value.")
+			reserved = *m.reservedField0
+		}
+		_err := writeBuffer.WriteUint8("reserved", 3, reserved)
+		if _err != nil {
+			return errors.Wrap(_err, "Error serializing 'reserved' field")
+		}
+	}
+
+	// Simple Field (flagExtendedFlags)
+	flagExtendedFlags := bool(m.GetFlagExtendedFlags())
+	_flagExtendedFlagsErr := writeBuffer.WriteBit("flagExtendedFlags", (flagExtendedFlags))
+	if _flagExtendedFlagsErr != nil {
+		return errors.Wrap(_flagExtendedFlagsErr, "Error serializing 'flagExtendedFlags' field")
+	}
+
+	// Simple Field (flagInitOnReset)
+	flagInitOnReset := bool(m.GetFlagInitOnReset())
+	_flagInitOnResetErr := writeBuffer.WriteBit("flagInitOnReset", (flagInitOnReset))
+	if _flagInitOnResetErr != nil {
+		return errors.Wrap(_flagInitOnResetErr, "Error serializing 'flagInitOnReset' field")
+	}
+
+	// Simple Field (flagStatic)
+	flagStatic := bool(m.GetFlagStatic())
+	_flagStaticErr := writeBuffer.WriteBit("flagStatic", (flagStatic))
+	if _flagStaticErr != nil {
+		return errors.Wrap(_flagStaticErr, "Error serializing 'flagStatic' field")
+	}
+
+	// Simple Field (flagAttributes)
+	flagAttributes := bool(m.GetFlagAttributes())
+	_flagAttributesErr := writeBuffer.WriteBit("flagAttributes", (flagAttributes))
+	if _flagAttributesErr != nil {
+		return errors.Wrap(_flagAttributesErr, "Error serializing 'flagAttributes' field")
+	}
+
+	// Simple Field (flagContextMask)
+	flagContextMask := bool(m.GetFlagContextMask())
+	_flagContextMaskErr := writeBuffer.WriteBit("flagContextMask", (flagContextMask))
+	if _flagContextMaskErr != nil {
+		return errors.Wrap(_flagContextMaskErr, "Error serializing 'flagContextMask' field")
+	}
+
+	// Reserved Field (reserved)
+	{
+		var reserved uint16 = uint16(0x0000)
+		if m.reservedField1 != nil {
+			log.Info().Fields(map[string]interface{}{
+				"expected value": uint16(0x0000),
+				"got value":      reserved,
+			}).Msg("Overriding reserved field with unexpected value.")
+			reserved = *m.reservedField1
+		}
+		_err := writeBuffer.WriteUint16("reserved", 16, reserved)
+		if _err != nil {
+			return errors.Wrap(_err, "Error serializing 'reserved' field")
+		}
+	}
+
+	// Implicit Field (nameLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+	nameLength := uint16(GetSTR_LEN()(m.GetName()))
+	_nameLengthErr := writeBuffer.WriteUint16("nameLength", 16, (nameLength))
+	if _nameLengthErr != nil {
+		return errors.Wrap(_nameLengthErr, "Error serializing 'nameLength' field")
+	}
+
+	// Implicit Field (typeNameLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+	typeNameLength := uint16(GetSTR_LEN()(m.GetTypeName()))
+	_typeNameLengthErr := writeBuffer.WriteUint16("typeNameLength", 16, (typeNameLength))
+	if _typeNameLengthErr != nil {
+		return errors.Wrap(_typeNameLengthErr, "Error serializing 'typeNameLength' field")
+	}
+
+	// Implicit Field (commentLength) (Used for parsing, but it's value is not stored as it's implicitly given by the objects content)
+	commentLength := uint16(GetSTR_LEN()(m.GetComment()))
+	_commentLengthErr := writeBuffer.WriteUint16("commentLength", 16, (commentLength))
+	if _commentLengthErr != nil {
+		return errors.Wrap(_commentLengthErr, "Error serializing 'commentLength' field")
+	}
+
+	// Simple Field (name)
+	name := string(m.GetName())
+	_nameErr := writeBuffer.WriteString("name", uint32((GetSTR_LEN()(m.GetName()))*(8)), "UTF-8", (name))
+	if _nameErr != nil {
+		return errors.Wrap(_nameErr, "Error serializing 'name' field")
+	}
+
+	// Const Field (nameTerminator)
+	_nameTerminatorErr := writeBuffer.WriteUint8("nameTerminator", 8, 0x00)
+	if _nameTerminatorErr != nil {
+		return errors.Wrap(_nameTerminatorErr, "Error serializing 'nameTerminator' field")
+	}
+
+	// Simple Field (typeName)
+	typeName := string(m.GetTypeName())
+	_typeNameErr := writeBuffer.WriteString("typeName", uint32((GetSTR_LEN()(m.GetTypeName()))*(8)), "UTF-8", (typeName))
+	if _typeNameErr != nil {
+		return errors.Wrap(_typeNameErr, "Error serializing 'typeName' field")
+	}
+
+	// Const Field (typeNameTerminator)
+	_typeNameTerminatorErr := writeBuffer.WriteUint8("typeNameTerminator", 8, 0x00)
+	if _typeNameTerminatorErr != nil {
+		return errors.Wrap(_typeNameTerminatorErr, "Error serializing 'typeNameTerminator' field")
+	}
+
+	// Simple Field (comment)
+	comment := string(m.GetComment())
+	_commentErr := writeBuffer.WriteString("comment", uint32((GetSTR_LEN()(m.GetComment()))*(8)), "UTF-8", (comment))
+	if _commentErr != nil {
+		return errors.Wrap(_commentErr, "Error serializing 'comment' field")
+	}
+
+	// Const Field (commentTerminator)
+	_commentTerminatorErr := writeBuffer.WriteUint8("commentTerminator", 8, 0x00)
+	if _commentTerminatorErr != nil {
+		return errors.Wrap(_commentTerminatorErr, "Error serializing 'commentTerminator' field")
+	}
+
+	// Array Field (rest)
+	// Byte Array field (rest)
+	if err := writeBuffer.WriteByteArray("rest", m.GetRest()); err != nil {
+		return errors.Wrap(err, "Error serializing 'rest' field")
+	}
+
+	if popErr := writeBuffer.PopContext("AdsSymbolTableEntry"); popErr != nil {
+		return errors.Wrap(popErr, "Error popping for AdsSymbolTableEntry")
+	}
+	return nil
+}
+
+func (m *_AdsSymbolTableEntry) isAdsSymbolTableEntry() bool {
+	return true
+}
+
+func (m *_AdsSymbolTableEntry) String() string {
+	if m == nil {
+		return "<nil>"
+	}
+	writeBuffer := utils.NewBoxedWriteBufferWithOptions(true, true)
+	if err := writeBuffer.WriteSerializable(m); err != nil {
+		return err.Error()
+	}
+	return writeBuffer.GetBox().String()
+}
diff --git a/plc4go/protocols/ads/readwrite/model/AdsTableSizes.go b/plc4go/protocols/ads/readwrite/model/AdsTableSizes.go
new file mode 100644
index 000000000..ebd3d2b1a
--- /dev/null
+++ b/plc4go/protocols/ads/readwrite/model/AdsTableSizes.go
@@ -0,0 +1,284 @@
+/*
+ * 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
+ *
+ *   https://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
+
+import (
+	"github.com/apache/plc4x/plc4go/internal/spi/utils"
+	"github.com/pkg/errors"
+)
+
+// Code generated by code-generation. DO NOT EDIT.
+
+// AdsTableSizes is the corresponding interface of AdsTableSizes
+type AdsTableSizes interface {
+	utils.LengthAware
+	utils.Serializable
+	// GetSymbolCount returns SymbolCount (property field)
+	GetSymbolCount() uint32
+	// GetSymbolLength returns SymbolLength (property field)
+	GetSymbolLength() uint32
+	// GetDataTypeCount returns DataTypeCount (property field)
+	GetDataTypeCount() uint32
+	// GetDataTypeLength returns DataTypeLength (property field)
+	GetDataTypeLength() uint32
+	// GetExtraCount returns ExtraCount (property field)
+	GetExtraCount() uint32
+	// GetExtraLength returns ExtraLength (property field)
+	GetExtraLength() uint32
+}
+
+// AdsTableSizesExactly can be used when we want exactly this type and not a type which fulfills AdsTableSizes.
+// This is useful for switch cases.
+type AdsTableSizesExactly interface {
+	AdsTableSizes
+	isAdsTableSizes() bool
+}
+
+// _AdsTableSizes is the data-structure of this message
+type _AdsTableSizes struct {
+	SymbolCount    uint32
+	SymbolLength   uint32
+	DataTypeCount  uint32
+	DataTypeLength uint32
+	ExtraCount     uint32
+	ExtraLength    uint32
+}
+
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+/////////////////////// Accessors for property fields.
+///////////////////////
+
+func (m *_AdsTableSizes) GetSymbolCount() uint32 {
+	return m.SymbolCount
+}
+
+func (m *_AdsTableSizes) GetSymbolLength() uint32 {
+	return m.SymbolLength
+}
+
+func (m *_AdsTableSizes) GetDataTypeCount() uint32 {
+	return m.DataTypeCount
+}
+
+func (m *_AdsTableSizes) GetDataTypeLength() uint32 {
+	return m.DataTypeLength
+}
+
+func (m *_AdsTableSizes) GetExtraCount() uint32 {
+	return m.ExtraCount
+}
+
+func (m *_AdsTableSizes) GetExtraLength() uint32 {
+	return m.ExtraLength
+}
+
+///////////////////////
+///////////////////////
+///////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////
+
+// NewAdsTableSizes factory function for _AdsTableSizes
+func NewAdsTableSizes(symbolCount uint32, symbolLength uint32, dataTypeCount uint32, dataTypeLength uint32, extraCount uint32, extraLength uint32) *_AdsTableSizes {
+	return &_AdsTableSizes{SymbolCount: symbolCount, SymbolLength: symbolLength, DataTypeCount: dataTypeCount, DataTypeLength: dataTypeLength, ExtraCount: extraCount, ExtraLength: extraLength}
+}
+
+// Deprecated: use the interface for direct cast
+func CastAdsTableSizes(structType interface{}) AdsTableSizes {
+	if casted, ok := structType.(AdsTableSizes); ok {
+		return casted
+	}
+	if casted, ok := structType.(*AdsTableSizes); ok {
+		return *casted
+	}
+	return nil
+}
+
+func (m *_AdsTableSizes) GetTypeName() string {
+	return "AdsTableSizes"
+}
+
+func (m *_AdsTableSizes) GetLengthInBits() uint16 {
+	return m.GetLengthInBitsConditional(false)
+}
+
+func (m *_AdsTableSizes) GetLengthInBitsConditional(lastItem bool) uint16 {
+	lengthInBits := uint16(0)
+
+	// Simple field (symbolCount)
+	lengthInBits += 32
+
+	// Simple field (symbolLength)
+	lengthInBits += 32
+
+	// Simple field (dataTypeCount)
+	lengthInBits += 32
+
+	// Simple field (dataTypeLength)
+	lengthInBits += 32
+
+	// Simple field (extraCount)
+	lengthInBits += 32
+
+	// Simple field (extraLength)
+	lengthInBits += 32
+
+	return lengthInBits
+}
+
+func (m *_AdsTableSizes) GetLengthInBytes() uint16 {
+	return m.GetLengthInBits() / 8
+}
+
+func AdsTableSizesParse(readBuffer utils.ReadBuffer) (AdsTableSizes, error) {
+	positionAware := readBuffer
+	_ = positionAware
+	if pullErr := readBuffer.PullContext("AdsTableSizes"); pullErr != nil {
+		return nil, errors.Wrap(pullErr, "Error pulling for AdsTableSizes")
+	}
+	currentPos := positionAware.GetPos()
+	_ = currentPos
+
+	// Simple Field (symbolCount)
+	_symbolCount, _symbolCountErr := readBuffer.ReadUint32("symbolCount", 32)
+	if _symbolCountErr != nil {
+		return nil, errors.Wrap(_symbolCountErr, "Error parsing 'symbolCount' field of AdsTableSizes")
+	}
+	symbolCount := _symbolCount
+
+	// Simple Field (symbolLength)
+	_symbolLength, _symbolLengthErr := readBuffer.ReadUint32("symbolLength", 32)
+	if _symbolLengthErr != nil {
+		return nil, errors.Wrap(_symbolLengthErr, "Error parsing 'symbolLength' field of AdsTableSizes")
+	}
+	symbolLength := _symbolLength
+
+	// Simple Field (dataTypeCount)
+	_dataTypeCount, _dataTypeCountErr := readBuffer.ReadUint32("dataTypeCount", 32)
+	if _dataTypeCountErr != nil {
+		return nil, errors.Wrap(_dataTypeCountErr, "Error parsing 'dataTypeCount' field of AdsTableSizes")
+	}
+	dataTypeCount := _dataTypeCount
+
+	// Simple Field (dataTypeLength)
+	_dataTypeLength, _dataTypeLengthErr := readBuffer.ReadUint32("dataTypeLength", 32)
+	if _dataTypeLengthErr != nil {
+		return nil, errors.Wrap(_dataTypeLengthErr, "Error parsing 'dataTypeLength' field of AdsTableSizes")
+	}
+	dataTypeLength := _dataTypeLength
+
+	// Simple Field (extraCount)
+	_extraCount, _extraCountErr := readBuffer.ReadUint32("extraCount", 32)
+	if _extraCountErr != nil {
+		return nil, errors.Wrap(_extraCountErr, "Error parsing 'extraCount' field of AdsTableSizes")
+	}
+	extraCount := _extraCount
+
+	// Simple Field (extraLength)
+	_extraLength, _extraLengthErr := readBuffer.ReadUint32("extraLength", 32)
+	if _extraLengthErr != nil {
+		return nil, errors.Wrap(_extraLengthErr, "Error parsing 'extraLength' field of AdsTableSizes")
+	}
+	extraLength := _extraLength
+
+	if closeErr := readBuffer.CloseContext("AdsTableSizes"); closeErr != nil {
+		return nil, errors.Wrap(closeErr, "Error closing for AdsTableSizes")
+	}
+
+	// Create the instance
+	return &_AdsTableSizes{
+		SymbolCount:    symbolCount,
+		SymbolLength:   symbolLength,
+		DataTypeCount:  dataTypeCount,
+		DataTypeLength: dataTypeLength,
+		ExtraCount:     extraCount,
+		ExtraLength:    extraLength,
+	}, nil
+}
+
+func (m *_AdsTableSizes) Serialize(writeBuffer utils.WriteBuffer) error {
+	positionAware := writeBuffer
+	_ = positionAware
+	if pushErr := writeBuffer.PushContext("AdsTableSizes"); pushErr != nil {
+		return errors.Wrap(pushErr, "Error pushing for AdsTableSizes")
+	}
+
+	// Simple Field (symbolCount)
+	symbolCount := uint32(m.GetSymbolCount())
+	_symbolCountErr := writeBuffer.WriteUint32("symbolCount", 32, (symbolCount))
+	if _symbolCountErr != nil {
+		return errors.Wrap(_symbolCountErr, "Error serializing 'symbolCount' field")
+	}
+
+	// Simple Field (symbolLength)
+	symbolLength := uint32(m.GetSymbolLength())
+	_symbolLengthErr := writeBuffer.WriteUint32("symbolLength", 32, (symbolLength))
+	if _symbolLengthErr != nil {
+		return errors.Wrap(_symbolLengthErr, "Error serializing 'symbolLength' field")
+	}
+
+	// Simple Field (dataTypeCount)
+	dataTypeCount := uint32(m.GetDataTypeCount())
+	_dataTypeCountErr := writeBuffer.WriteUint32("dataTypeCount", 32, (dataTypeCount))
+	if _dataTypeCountErr != nil {
+		return errors.Wrap(_dataTypeCountErr, "Error serializing 'dataTypeCount' field")
+	}
+
+	// Simple Field (dataTypeLength)
+	dataTypeLength := uint32(m.GetDataTypeLength())
+	_dataTypeLengthErr := writeBuffer.WriteUint32("dataTypeLength", 32, (dataTypeLength))
+	if _dataTypeLengthErr != nil {
+		return errors.Wrap(_dataTypeLengthErr, "Error serializing 'dataTypeLength' field")
+	}
+
+	// Simple Field (extraCount)
+	extraCount := uint32(m.GetExtraCount())
+	_extraCountErr := writeBuffer.WriteUint32("extraCount", 32, (extraCount))
+	if _extraCountErr != nil {
+		return errors.Wrap(_extraCountErr, "Error serializing 'extraCount' field")
+	}
+
+	// Simple Field (extraLength)
+	extraLength := uint32(m.GetExtraLength())
+	_extraLengthErr := writeBuffer.WriteUint32("extraLength", 32, (extraLength))
+	if _extraLengthErr != nil {
+		return errors.Wrap(_extraLengthErr, "Error serializing 'extraLength' field")
+	}
+
+	if popErr := writeBuffer.PopContext("AdsTableSizes"); popErr != nil {
+		return errors.Wrap(popErr, "Error popping for AdsTableSizes")
+	}
+	return nil
+}
+
+func (m *_AdsTableSizes) isAdsTableSizes() bool {
+	return true
+}
+
+func (m *_AdsTableSizes) String() string {
+	if m == nil {
+		return "<nil>"
+	}
+	writeBuffer := utils.NewBoxedWriteBufferWithOptions(true, true)
+	if err := writeBuffer.WriteSerializable(m); err != nil {
+		return err.Error()
+	}
+	return writeBuffer.GetBox().String()
+}
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/PlcConnection.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/PlcConnection.java
index 0a67d12f5..9ae7b44c8 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/PlcConnection.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/PlcConnection.java
@@ -101,8 +101,6 @@ public interface PlcConnection extends AutoCloseable {
      * @return browse request builder.
      * @throws PlcUnsupportedOperationException if the connection does not support browsing
      */
-    default PlcBrowseRequest.Builder browseRequestBuilder() {
-        throw new PlcNotImplementedException("Not implemented for this connection / driver");
-    }
+    PlcBrowseRequest.Builder browseRequestBuilder();
 
 }
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
index 2e19ea89f..ccd35fe0a 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
@@ -29,7 +29,7 @@ public interface PlcBrowseRequest extends PlcRequest {
         @Override
         PlcBrowseRequest build();
 
-        PlcReadRequest.Builder addItem(String name, String fieldQuery);
+        Builder addQuery(String name, String query);
 
     }
 
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItem.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItem.java
index 363fd27d5..a2373235e 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItem.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryItem.java
@@ -18,6 +18,8 @@
  */
 package org.apache.plc4x.java.api.messages;
 
+import org.apache.plc4x.java.api.value.PlcValue;
+
 import java.util.Map;
 
 public interface PlcDiscoveryItem {
@@ -50,7 +52,7 @@ public interface PlcDiscoveryItem {
     /**
      * @return returns a map of all additional attributes assigned to this item (Usually additional information, which is not directly needed for connecting, such as Versions, Names, Supported features etc.)
      */
-    Map<String, String> getAttributes();
+    Map<String, PlcValue> getAttributes();
 
     /**
      * @return returns a plc4x connection string that can be used in any PLC4X driver to connect to the given device (Generally just a concatenation of the other parts of this object)
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryRequest.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryRequest.java
index 213825097..cdeb2f3d3 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryRequest.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcDiscoveryRequest.java
@@ -31,6 +31,8 @@ public interface PlcDiscoveryRequest extends PlcRequest {
         @Override
         PlcDiscoveryRequest build();
 
+        Builder addQuery(String name, String query);
+
     }
 
 }
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/PlcConnectionMetadata.java b/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/PlcConnectionMetadata.java
index 68c520521..f0f6a5146 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/PlcConnectionMetadata.java
+++ b/plc4j/api/src/main/java/org/apache/plc4x/java/api/metadata/PlcConnectionMetadata.java
@@ -42,8 +42,6 @@ public interface PlcConnectionMetadata {
     /**
      * Indicates that the connection supports browsing.
      */
-    default boolean canBrowse() {
-        return false;
-    }
+    boolean canBrowse();
 
 }
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/AdsPlcDriver.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/AdsPlcDriver.java
index 44f338796..7404ce1e2 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/AdsPlcDriver.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/AdsPlcDriver.java
@@ -25,6 +25,7 @@ import org.apache.plc4x.java.ads.field.AdsFieldHandler;
 import org.apache.plc4x.java.ads.protocol.AdsProtocolLogic;
 import org.apache.plc4x.java.ads.readwrite.AmsTCPPacket;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
+import org.apache.plc4x.java.api.metadata.PlcDriverMetadata;
 import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryRequest;
 import org.apache.plc4x.java.spi.values.IEC61131ValueHandler;
 import org.apache.plc4x.java.api.value.PlcValueHandler;
@@ -73,6 +74,11 @@ public class AdsPlcDriver extends GeneratedDriverBase<AmsTCPPacket> {
         return true;
     }
 
+    @Override
+    protected boolean canBrowse() {
+        return true;
+    }
+
     @Override
     protected Class<? extends Configuration> getConfigurationType() {
         return AdsConfiguration.class;
@@ -93,6 +99,16 @@ public class AdsPlcDriver extends GeneratedDriverBase<AmsTCPPacket> {
         return new IEC61131ValueHandler();
     }
 
+    @Override
+    public PlcDriverMetadata getMetadata() {
+        return new PlcDriverMetadata() {
+            @Override
+            public boolean canDiscover() {
+                return true;
+            }
+        };
+    }
+
     /**
      * This protocol doesn't have a disconnect procedure, so there is no need to wait for a login to finish.
      * @return false
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/discovery/AdsPlcDiscoverer.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/discovery/AdsPlcDiscoverer.java
index 841ea6ad5..0ff0a38e5 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/discovery/AdsPlcDiscoverer.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/discovery/AdsPlcDiscoverer.java
@@ -25,10 +25,12 @@ import org.apache.plc4x.java.api.messages.PlcDiscoveryItemHandler;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryResponse;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.spi.generation.*;
 import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryItem;
 import org.apache.plc4x.java.spi.messages.DefaultPlcDiscoveryResponse;
 import org.apache.plc4x.java.spi.messages.PlcDiscoverer;
+import org.apache.plc4x.java.spi.values.PlcSTRING;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -53,6 +55,7 @@ public class AdsPlcDiscoverer implements PlcDiscoverer {
         Queue<PlcDiscoveryItem> values = new ConcurrentLinkedQueue<>();
 
         // Send out a discovery request to every non-loopback device with IPv4 address.
+        List<DatagramSocket> openSockets = new ArrayList<>();
         try {
             for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
                 if (!networkInterface.isLoopback()) {
@@ -60,116 +63,128 @@ public class AdsPlcDiscoverer implements PlcDiscoverer {
                         if (interfaceAddress.getAddress() instanceof Inet4Address) {
                             Inet4Address inet4Address = (Inet4Address) interfaceAddress.getAddress();
                             // Open a listening socket on the AMS discovery default port for taking in responses.
-                            try (DatagramSocket adsDiscoverySocket = new DatagramSocket(AdsDiscoveryConstants.ADSDISCOVERYUDPDEFAULTPORT, inet4Address)) {
-                                adsDiscoverySocket.setBroadcast(true);
-                                // Start listening for incoming messages.
-                                Thread thread = new Thread(() -> {
-                                    try {
-                                        while (true) {
-                                            // Wait for an incoming packet.
-                                            byte[] buffer = new byte[512];
-                                            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
-                                            adsDiscoverySocket.receive(packet);
-
-                                            InetAddress plcAddress = packet.getAddress();
-                                            ReadBuffer readBuffer = new ReadBufferByteBased(packet.getData(), ByteOrder.LITTLE_ENDIAN);
-                                            AdsDiscovery adsDiscoveryResponse = AdsDiscovery.staticParse(readBuffer);
-
-                                            // Check if this is actually a discovery response.
-                                            if ((adsDiscoveryResponse.getRequestId() == 0) &&
-                                                (adsDiscoveryResponse.getPortNumber() == AdsPortNumbers.SYSTEM_SERVICE) &&
-                                                    (adsDiscoveryResponse.getOperation() == Operation.DISCOVERY_RESPONSE)) {
-
-                                                AmsNetId remoteAmsNetId = adsDiscoveryResponse.getAmsNetId();
-                                                AdsDiscoveryBlockHostName hostNameBlock = null;
-                                                AdsDiscoveryBlockOsData osDataBlock = null;
-                                                AdsDiscoveryBlockVersion versionBlock = null;
-                                                AdsDiscoveryBlockFingerprint fingerprintBlock = null;
-                                                for (AdsDiscoveryBlock block : adsDiscoveryResponse.getBlocks()) {
-                                                    switch (block.getBlockType()) {
-                                                        case HOST_NAME:
-                                                            hostNameBlock = (AdsDiscoveryBlockHostName) block;
-                                                            break;
-                                                        case OS_DATA:
-                                                            osDataBlock = (AdsDiscoveryBlockOsData) block;
-                                                            break;
-                                                        case VERSION:
-                                                            versionBlock = (AdsDiscoveryBlockVersion) block;
-                                                            break;
-                                                        case FINGERPRINT:
-                                                            fingerprintBlock = (AdsDiscoveryBlockFingerprint) block;
-                                                            break;
-                                                        default:
-                                                            logger.info(String.format("Unexpected block type: %s", block.getBlockType().toString()));
-                                                    }
+                            DatagramSocket adsDiscoverySocket = new DatagramSocket(AdsDiscoveryConstants.ADSDISCOVERYUDPDEFAULTPORT, inet4Address);
+                            adsDiscoverySocket.setBroadcast(true);
+
+                            openSockets.add(adsDiscoverySocket);
+
+                            // Start listening for incoming messages.
+                            Thread thread = new Thread(() -> {
+                                try {
+                                    while (true) {
+                                        // Wait for an incoming packet.
+                                        byte[] buffer = new byte[512];
+                                        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
+                                        adsDiscoverySocket.receive(packet);
+
+                                        InetAddress plcAddress = packet.getAddress();
+                                        ReadBuffer readBuffer = new ReadBufferByteBased(packet.getData(), ByteOrder.LITTLE_ENDIAN);
+                                        AdsDiscovery adsDiscoveryResponse = AdsDiscovery.staticParse(readBuffer);
+
+                                        // Check if this is actually a discovery response.
+                                        if ((adsDiscoveryResponse.getRequestId() == 0) &&
+                                            (adsDiscoveryResponse.getPortNumber() == AdsPortNumbers.SYSTEM_SERVICE) &&
+                                            (adsDiscoveryResponse.getOperation() == Operation.DISCOVERY_RESPONSE)) {
+
+                                            AmsNetId remoteAmsNetId = adsDiscoveryResponse.getAmsNetId();
+                                            AdsDiscoveryBlockHostName hostNameBlock = null;
+                                            AdsDiscoveryBlockOsData osDataBlock = null;
+                                            AdsDiscoveryBlockVersion versionBlock = null;
+                                            AdsDiscoveryBlockFingerprint fingerprintBlock = null;
+                                            for (AdsDiscoveryBlock block : adsDiscoveryResponse.getBlocks()) {
+                                                switch (block.getBlockType()) {
+                                                    case HOST_NAME:
+                                                        hostNameBlock = (AdsDiscoveryBlockHostName) block;
+                                                        break;
+                                                    case OS_DATA:
+                                                        osDataBlock = (AdsDiscoveryBlockOsData) block;
+                                                        break;
+                                                    case VERSION:
+                                                        versionBlock = (AdsDiscoveryBlockVersion) block;
+                                                        break;
+                                                    case FINGERPRINT:
+                                                        fingerprintBlock = (AdsDiscoveryBlockFingerprint) block;
+                                                        break;
+                                                    default:
+                                                        logger.info(String.format("Unexpected block type: %s", block.getBlockType().toString()));
+                                                }
+                                            }
+
+                                            if (hostNameBlock != null) {
+                                                Map<String, String> options = new HashMap<>();
+                                                options.put("sourceAmsNetId", "65534");
+                                                options.put("sourceAmsPort", inet4Address.getHostAddress() + ".1.1");
+                                                options.put("targetAmsNetId", remoteAmsNetId.getOctet1() + "." + remoteAmsNetId.getOctet2() + "." + remoteAmsNetId.getOctet3() + "." + remoteAmsNetId.getOctet4() + "." + remoteAmsNetId.getOctet5() + "." + remoteAmsNetId.getOctet6());
+                                                // TODO: Check if this is legit, or if we can get the information from somewhere.
+                                                options.put("targetAmsPort", "851");
+
+                                                Map<String, PlcValue> attributes = new HashMap<>();
+                                                attributes.put("hostName", new PlcSTRING(hostNameBlock.getHostName().getText()));
+                                                if (versionBlock != null) {
+                                                    byte[] versionData = versionBlock.getVersionData();
+                                                    int patchVersion = ((int) versionData[3] & 0xFF) << 8 | ((int) versionData[2] & 0xFF);
+                                                    attributes.put("twinCatVersion", new PlcSTRING(String.format("%d.%d.%d", (short) versionData[0] & 0xFF, (short) versionData[1] & 0xFF, patchVersion)));
                                                 }
+                                                if (fingerprintBlock != null) {
+                                                    attributes.put("fingerprint", new PlcSTRING(new String(fingerprintBlock.getData())));
+                                                }
+                                                // TODO: Find out how to handle the OS Data
+
+                                                // Add an entry to the results.
+                                                PlcDiscoveryItem plcDiscoveryItem = new DefaultPlcDiscoveryItem(
+                                                    "ads", "tcp",
+                                                    plcAddress.getHostAddress() + ":" + AdsConstants.ADSTCPDEFAULTPORT,
+                                                    options, hostNameBlock.getHostName().getText(), attributes);
 
-                                                if (hostNameBlock != null) {
-                                                    Map<String, String> options = new HashMap<>();
-                                                    options.put("sourceAmsNetId", "65534");
-                                                    options.put("sourceAmsPort", inet4Address.getHostAddress() + ".1.1");
-                                                    options.put("targetAmsNetId", remoteAmsNetId.getOctet1() + "." + remoteAmsNetId.getOctet2() + "." + remoteAmsNetId.getOctet3() + "." + remoteAmsNetId.getOctet4() + "." + remoteAmsNetId.getOctet5() + "." + remoteAmsNetId.getOctet6());
-                                                    // TODO: Check if this is legit, or if we can get the information from somewhere.
-                                                    options.put("targetAmsPort", "851");
-
-                                                    Map<String, String> attributes = new HashMap<>();
-                                                    attributes.put("hostName", hostNameBlock.getHostName().getText());
-                                                    if(versionBlock != null) {
-                                                        byte[] versionData = versionBlock.getVersionData();
-                                                        int patchVersion = ((int) versionData[3] & 0xFF) << 8 | ((int) versionData[2] & 0xFF);
-                                                        attributes.put("twinCatVersion", String.format("%d.%d.%d", (short) versionData[0] & 0xFF, (short) versionData[1] & 0xFF, patchVersion));
-                                                    }
-                                                    if(fingerprintBlock != null) {
-                                                        attributes.put("fingerprint", new String(fingerprintBlock.getData()));
-                                                    }
-                                                    // TODO: Find out how to handle the OS Data
-
-                                                    // Add an entry to the results.
-                                                    PlcDiscoveryItem plcDiscoveryItem = new DefaultPlcDiscoveryItem(
-                                                        "ads", "tcp",
-                                                        plcAddress.getHostAddress() + ":" + AdsConstants.ADSTCPDEFAULTPORT,
-                                                        options, hostNameBlock.getHostName().getText(), attributes);
-
-                                                    // If we've got an explicit handler, pass the new item to that.
-                                                    if (handler != null) {
-                                                        handler.handle(plcDiscoveryItem);
-                                                    }
-
-                                                    // Simply add the item to the list.
-                                                    values.add(plcDiscoveryItem);
+                                                // If we've got an explicit handler, pass the new item to that.
+                                                if (handler != null) {
+                                                    handler.handle(plcDiscoveryItem);
                                                 }
+
+                                                // Simply add the item to the list.
+                                                values.add(plcDiscoveryItem);
                                             }
                                         }
-                                    } catch (IOException e) {
-                                        logger.error("Error reading ADS discovery response", e);
-                                    } catch (ParseException e) {
-                                        logger.error("Error parsing ADS discovery response", e);
                                     }
-                                });
-                                thread.start();
-
-                                // Send the discovery request.
-                                try {
-                                    // Create the discovery request message for this device.
-                                    AmsNetId amsNetId = new AmsNetId(inet4Address.getAddress()[0], inet4Address.getAddress()[1], inet4Address.getAddress()[2], inet4Address.getAddress()[3], (byte) 1, (byte) 1);
-                                    AdsDiscovery discoveryRequestMessage = new AdsDiscovery(0, Operation.DISCOVERY_REQUEST, amsNetId, AdsPortNumbers.SYSTEM_SERVICE, Collections.emptyList());
-
-                                    // Serialize the message.
-                                    WriteBufferByteBased writeBuffer = new WriteBufferByteBased(discoveryRequestMessage.getLengthInBytes(), ByteOrder.LITTLE_ENDIAN);
-                                    discoveryRequestMessage.serialize(writeBuffer);
-
-                                    // Get the broadcast address for this interface.
-                                    InetAddress broadcastAddress = interfaceAddress.getBroadcast();
-
-                                    // Create the UDP packet to the broadcast address.
-                                    DatagramPacket discoveryRequestPacket = new DatagramPacket(writeBuffer.getBytes(), writeBuffer.getBytes().length, broadcastAddress, AdsDiscoveryConstants.ADSDISCOVERYUDPDEFAULTPORT);
-                                    adsDiscoverySocket.send(discoveryRequestPacket);
-                                } catch (SerializationException e) {
-                                    logger.error("Error serializing ADS discovery request", e);
+                                } catch (SocketException e) {
+                                    // If we're closing the socket at the end, a "Socket closed"
+                                    // exception is thrown.
+                                    if(!"Socket closed".equals(e.getMessage())) {
+                                        logger.error("Error receiving ADS discovery response", e);
+                                    }
                                 } catch (IOException e) {
-                                    logger.error("Error sending ADS discover request", e);
+                                    logger.error("Error reading ADS discovery response", e);
+                                } catch (ParseException e) {
+                                    logger.error("Error parsing ADS discovery response", e);
                                 }
-                            } catch (SocketException e) {
+                            });
+                            thread.start();
+
+                            // Send the discovery request.
+                            try {
+                                // Create the discovery request message for this device.
+                                AmsNetId amsNetId = new AmsNetId(inet4Address.getAddress()[0], inet4Address.getAddress()[1], inet4Address.getAddress()[2], inet4Address.getAddress()[3], (byte) 1, (byte) 1);
+                                AdsDiscovery discoveryRequestMessage = new AdsDiscovery(0, Operation.DISCOVERY_REQUEST, amsNetId, AdsPortNumbers.SYSTEM_SERVICE, Collections.emptyList());
+
+                                // Serialize the message.
+                                WriteBufferByteBased writeBuffer = new WriteBufferByteBased(discoveryRequestMessage.getLengthInBytes(), ByteOrder.LITTLE_ENDIAN);
+                                discoveryRequestMessage.serialize(writeBuffer);
+
+                                // Get the broadcast address for this interface.
+                                InetAddress broadcastAddress = interfaceAddress.getBroadcast();
+
+                                // Create the UDP packet to the broadcast address.
+                                DatagramPacket discoveryRequestPacket = new DatagramPacket(writeBuffer.getBytes(), writeBuffer.getBytes().length, broadcastAddress, AdsDiscoveryConstants.ADSDISCOVERYUDPDEFAULTPORT);
+                                adsDiscoverySocket.send(discoveryRequestPacket);
+                            } catch (SerializationException e) {
+                                logger.error("Error serializing ADS discovery request", e);
+                            } catch (IOException e) {
+                                logger.error("Error sending ADS discover request", e);
+                            }
+
+                            try {
+                                Thread.sleep(3000);
+                            } catch (InterruptedException e) {
                                 throw new RuntimeException(e);
                             }
                         }
@@ -178,6 +193,10 @@ public class AdsPlcDiscoverer implements PlcDiscoverer {
             }
         } catch (SocketException e) {
             throw new RuntimeException(e);
+        } finally {
+            for (DatagramSocket openSocket : openSockets) {
+                openSocket.close();
+            }
         }
 
         // Create a timer that completes the future after a given time with all the responses it found till then.
diff --git a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
index 9641148d2..14a55710a 100644
--- a/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
+++ b/plc4j/drivers/ads/src/main/java/org/apache/plc4x/java/ads/protocol/AdsProtocolLogic.java
@@ -56,7 +56,7 @@ import java.util.function.Consumer;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
-public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements HasConfiguration<AdsConfiguration>, PlcSubscriber {
+public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements HasConfiguration<AdsConfiguration>, PlcSubscriber, PlcBrowser {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(AdsProtocolLogic.class);
 
@@ -191,6 +191,7 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
             if(throwable != null) {
                 LOGGER.error("Error fetching symbol and datatype table sizes");
             } else {
+                // TODO: Link the datatypes to the symbols.
                 for (AdsSymbolTableEntry symbol : symbols) {
                     System.out.println(symbol.getName());
                 }
@@ -208,6 +209,11 @@ public class AdsProtocolLogic extends Plc4xProtocolBase<AmsTCPPacket> implements
         // TODO: Here we have to clean up all of the handles this connection acquired.
     }
 
+    @Override
+    public CompletableFuture<PlcBrowseResponse> browse(PlcBrowseRequest browseRequest) {
+        return null;
+    }
+
     @Override
     public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
         // Get all ADS addresses in their resolved state.
diff --git a/plc4j/drivers/c-bus/src/test/java/org/apache/plc4x/java/cbus/RandomPackagesTest.java b/plc4j/drivers/c-bus/src/test/java/org/apache/plc4x/java/cbus/RandomPackagesTest.java
deleted file mode 100644
index ab42558ad..000000000
--- a/plc4j/drivers/c-bus/src/test/java/org/apache/plc4x/java/cbus/RandomPackagesTest.java
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * 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
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.plc4x.java.cbus;
-
-import org.apache.plc4x.java.cbus.readwrite.*;
-import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-import java.nio.charset.StandardCharsets;
-
-import static org.apache.plc4x.java.cbus.Util.assertMessageMatches;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class RandomPackagesTest {
-
-    public static final CBusOptions C_BUS_OPTIONS_WITH_SRCHK = new CBusOptions(false, false, false, false, false, false, false, false, true);
-    RequestContext requestContext;
-    CBusOptions cBusOptions;
-
-    @BeforeEach
-    void setUp() {
-        requestContext = new RequestContext(false);
-        cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, false);
-    }
-
-    @Disabled
-    @Test
-    void whatEverThisIs() throws Exception {
-        byte[] bytes = "\\3436303230303231303167\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void deviceManagementInstruction() throws Exception {
-        byte[] bytes = "@1A2001\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void setLight() throws Exception {
-        byte[] bytes = "\\0538000100g\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void identifyResponse() throws Exception {
-        byte[] bytes = "g.890150435F434E49454421\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        // We know we send an identify command so we set the cal flag
-        requestContext = new RequestContext(false);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void someResponse() throws Exception {
-        byte[] bytes = "nl.8220025C\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        requestContext = new RequestContext(false);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void someOtherResponse() throws Exception {
-        byte[] bytes = "\\0538000100g\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-
-    @Test
-    void identifyRequest2() throws Exception {
-        byte[] bytes = "21021A2102i\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void identifyResponse2() throws Exception {
-        byte[] bytes = "i.8902352E342E3030202010\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        // We know we send an identify command so we set the cal flag
-        requestContext = new RequestContext(false);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void recall() throws Exception {
-        byte[] bytes = "@1A2001\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void identifyTypeReply() throws Exception {
-        byte[] bytes = "h.890150435F434E49454421\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        requestContext = new RequestContext(false);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void write30to9755() throws Exception {
-        byte[] bytes = "A3309755s\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void strangeNotYetParsableCommandResponse() throws Exception {
-        byte[] bytes = "s.860202003230977D\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        requestContext = new RequestContext(false);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void statusRequestBinaryState() throws Exception {
-        byte[] bytes = "\\05FF00FAFF00v\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Disabled
-    @Test
-    void wat() throws Exception {
-        byte[] bytes = "D8FF0024000002000000000000000008000000000000000000\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        requestContext = new RequestContext(false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void WriteCommand() throws Exception {
-        byte[] bytes = "\\46310900A400410600r\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void statusReply() throws Exception {
-        byte[] bytes = "D8FF5800000000000000000000000000000000000000000000D1\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        requestContext = new RequestContext(false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void identifyUnitSummary() throws Exception {
-        byte[] bytes = "2110\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void identifyUnitSummaryResponse() throws Exception {
-        byte[] bytes = "o.8510020000FF6A\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        requestContext = new RequestContext(true);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void hvacAndCoolingSAL() throws Exception {
-        byte[] bytes = "0531AC0079042F0401430316000011\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        requestContext = new RequestContext(false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Disabled("apparently something is broken, the second cal data can't be parsed")
-    @Test
-    void calIdentifyReplyAndAnotherCal() throws Exception {
-        byte[] bytes = "h.860102008902312E362E30302020832138FFAE\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        requestContext = new RequestContext(false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void routedAcknowledge() throws Exception {
-        byte[] bytes = "r.8631020100320041D3\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        requestContext = new RequestContext(false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void gavValuesCurrentReply() throws Exception {
-        byte[] bytes = "w.860C02008A08000000C8000000000012\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        requestContext = new RequestContext(false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void SetHvacLevel() throws Exception {
-        byte[] bytes = "0531AC0036040108FF0000DC\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        requestContext = new RequestContext(false);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void salHvac() throws Exception {
-        byte[] bytes = "0531AC0036040142037F001F\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        requestContext = new RequestContext(false);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Disabled("Not clear yet what this is")
-    @Test
-    void closestFitIsAStatusRequestButWeDonTHaveAnyBytesBeforeThat() throws Exception {
-        byte[] bytes = "FAFF00r\r".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void ownSal() throws Exception {
-        byte[] bytes = "003809AF10\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void powerUpNotification() throws Exception {
-        byte[] bytes = "++\r\n".getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void incmoingMMI() throws Exception {
-        byte[] bytes = ("86040200F940380001000000000000000008000000000000000000000000FA\r\n").getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-    @Test
-    void justAnError() throws Exception {
-        byte[] bytes = ("!").getBytes(StandardCharsets.UTF_8);
-        ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-        cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-        CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-        assertThat(msg).isNotNull();
-        System.out.println(msg);
-
-        assertMessageMatches(bytes, msg);
-    }
-
-}
diff --git a/plc4j/drivers/c-bus/src/test/java/org/apache/plc4x/java/cbus/ReferenceTest.java b/plc4j/drivers/c-bus/src/test/java/org/apache/plc4x/java/cbus/ReferenceTest.java
deleted file mode 100644
index 3e626678b..000000000
--- a/plc4j/drivers/c-bus/src/test/java/org/apache/plc4x/java/cbus/ReferenceTest.java
+++ /dev/null
@@ -1,2052 +0,0 @@
-/*
- * 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
- *
- *   https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.plc4x.java.cbus;
-
-import org.apache.commons.codec.binary.Hex;
-import org.apache.plc4x.java.cbus.readwrite.*;
-import org.apache.plc4x.java.spi.generation.ReadBufferByteBased;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-
-import java.nio.charset.StandardCharsets;
-
-import static org.apache.plc4x.java.cbus.Util.assertMessageMatches;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ReferenceTest {
-
-    public static final CBusOptions C_BUS_OPTIONS_WITH_SRCHK = new CBusOptions(false, false, false, false, false, false, false, false, true);
-
-    RequestContext requestContext;
-    CBusOptions cBusOptions;
-
-    @BeforeEach
-    void setUp() {
-        requestContext = new RequestContext(false);
-        cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, false);
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/C-Bus%20Interface%20Requirements.pdf
-    @Nested
-    class InterfaceRequirementsTest {
-
-        // 8.2
-        @Nested
-        class Level1InterfaceImplementationRequirements {
-
-            // 8.2.4
-            @Nested
-            class SerialInterfaceInitialisation {
-
-                @Test
-                void Step_1_Reset() throws Exception {
-                    byte[] bytes = "~~~\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_2_SetInterfaceOptions3() throws Exception {
-                    byte[] bytes = "@A3420002\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_3_SetInterfaceOptions1_PUN() throws Exception {
-                    byte[] bytes = "@A3410058\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_4_SetInterfaceOptions1() throws Exception {
-                    byte[] bytes = "@A3300058\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-
-            // 8.2.5
-            @Nested
-            class ConfirmationOfTransmission {
-                @Test
-                void SomeCommand() throws Exception {
-                    byte[] bytes = "\\0538000121A1g\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void success() throws Exception {
-                    byte[] bytes = "g.".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void checksumFailure() throws Exception {
-                    byte[] bytes = "g!".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void tooManyRetransmissions() throws Exception {
-                    byte[] bytes = "g#".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void corruptionInTransmission() throws Exception {
-                    byte[] bytes = "g$".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void noSystemClock() throws Exception {
-                    byte[] bytes = "g%".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-        }
-
-        // 8.3
-        @Nested
-        class Level2InterfaceImplementationRequirements {
-
-            // 8.3.4
-            @Nested
-            class SerialInterfaceInitialisation {
-                @Test
-                void Step_1_Reset() throws Exception {
-                    byte[] bytes = "~~~\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_2_AnyApplicationFilter() throws Exception {
-                    byte[] bytes = "@A3210038\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_3_SetInterfaceOptions3() throws Exception {
-                    byte[] bytes = "@A3420002\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_4_SetInterfaceOptions1_PUN() throws Exception {
-                    byte[] bytes = "@A3410059\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_5_SetInterfaceOptions1() throws Exception {
-                    byte[] bytes = "@A3300059\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-
-            // 8.3.5
-            @Nested
-            class ProgrammingTheSerialInterfaceToFilterSALMessageTraffic {
-
-                @Test
-                void Step_1_SelectOnlyLighting() throws Exception {
-                    byte[] bytes = "@A3210038\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_2_SelectHeatingAsSecondApplication() throws Exception {
-                    byte[] bytes = "@A3220088\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-        }
-
-        // 8.4
-        @Nested
-        class Level3InterfaceImplementationRequirements {
-            // No specific tests
-        }
-
-        // 8.5
-        @Nested
-        class Level4InterfaceImplementationRequirements {
-
-            // 8.5.4
-            @Nested
-            class SerialInterfaceInitialisation {
-                @Test
-                void Step_1_Reset() throws Exception {
-                    byte[] bytes = "~~~\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_2_AnyApplicationFilter() throws Exception {
-                    byte[] bytes = "@A3210038\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_3_SetInterfaceOptions3() throws Exception {
-                    byte[] bytes = "@A342000A\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_4_SetInterfaceOptions1_PUN() throws Exception {
-                    byte[] bytes = "@A3410079\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Step_5_SetInterfaceOptions1() throws Exception {
-                    byte[] bytes = "@A3300079\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-        }
-
-        // 8.6
-        @Nested
-        class Level5InterfaceImplementationRequirements {
-            // No specific tests
-        }
-
-        // 8.7
-        @Nested
-        class Level6InterfaceImplementationRequirements {
-            // No specific tests
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Serial%20Interface%20User%20Guide.pdf
-    @Nested
-    class SerialInterfaceGuideTest {
-
-        // 3.4
-        @Nested
-        class Header {
-            @Test
-            void Point_point_multipoint_lowest_priority_class() throws Exception {
-                byte[] bytes = new byte[]{0x03};
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusHeader msg = CBusHeader.staticParse(readBufferByteBased);
-                assertThat(msg).isNotNull();
-
-                System.out.println(msg);
-            }
-
-            @Test
-            void Point_multipoint_lowest_priority_class() throws Exception {
-                byte[] bytes = new byte[]{0x05};
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusHeader msg = CBusHeader.staticParse(readBufferByteBased);
-                assertThat(msg).isNotNull();
-
-                System.out.println(msg);
-            }
-
-            @Test
-            void Point_point_lowest_priority_class() throws Exception {
-                byte[] bytes = new byte[]{0x06};
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusHeader msg = CBusHeader.staticParse(readBufferByteBased);
-                assertThat(msg).isNotNull();
-
-                System.out.println(msg);
-            }
-
-        }
-
-        // 4
-        @Nested
-        class SerialInterface {
-
-            // 4.2.3
-            @Test
-            void reset() throws Exception {
-                byte[] bytes = "~\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-
-                System.out.println(msg);
-            }
-
-            @Disabled("not implemented yet")
-            // 4.2.4
-            @Test
-            void cancel() throws Exception {
-                byte[] bytes = "AB0123?9876\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-
-                System.out.println(msg);
-            }
-
-            // 4.2.5
-            @Test
-            void smartConnectShortcut() throws Exception {
-                byte[] bytes = "\r|\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-
-                System.out.println(msg);
-            }
-
-            @Disabled("not implemented yet")
-            // 4.2.4
-            @Test
-            void confirmation() throws Exception {
-                // If you follow the spec a confirmation can occur at any place... seems strange
-                byte[] bytes = "AB0123n9876\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-
-                System.out.println(msg);
-            }
-
-            // 4.2.7
-            @Test
-            void directCommandAccess1() throws Exception {
-                byte[] bytes = "@2102\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 4.2.7
-            @Test
-            void directCommandAccess2() throws Exception {
-                // TODO: this should be the same as the @above but that is not yet implemented
-                byte[] bytes = "~2102\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-        }
-
-
-        // 4.2.9.1
-        @Nested
-        class PointToPointCommands {
-            @Test
-            void pointToPointCommandDirect() throws Exception {
-                byte[] bytes = "\\0603002102D4\r".getBytes(StandardCharsets.UTF_8);
-                cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void pointToPointCommandBridged() throws Exception {
-                byte[] bytes = "\\06420903210289\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-
-        // 4.2.9.2
-        @Nested
-        class PointToMultiPointCommands {
-            @Test
-            void pointToMultiPointCommandDirect() throws Exception {
-                byte[] bytes = "\\0538000108BA\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void pointToMultiPointCommandBridged() throws Exception {
-                byte[] bytes = "\\05FF007A38004A\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 4.2.9.3
-        @Nested
-        class PointToPointToMultoPointCommands {
-            @Test
-            void pointToPointToMultiPointCommand2() throws Exception {
-                byte[] bytes = "\\03420938010871\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 4.3.3.1
-        @Nested
-        class _CALReply {
-            @Test
-            void calRequest() throws Exception {
-                byte[] bytes = "\\0605002102\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void calReplyNormal() throws Exception {
-                byte[] bytes = "8902312E322E363620200A\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                requestContext = new RequestContext(false);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void calReplySmart() throws Exception {
-                // TODO: seems like the checksum is wrong here???
-                //byte[] bytes = "860593008902312E322E363620207F\r\n".getBytes(StandardCharsets.UTF_8);
-                byte[] bytes = "860593008902312E322E36362020EC\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                requestContext = new RequestContext(false);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 4.3.3.2
-        @Nested
-        class _MonitoredSAL {
-            @Test
-            void monitoredSal() throws Exception {
-                byte[] bytes = "0503380079083F\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                ReplyOrConfirmation msg = ReplyOrConfirmation.staticParse(readBufferByteBased, cBusOptions, requestContext);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 4.3.3.3
-        @Nested
-        class Confirmation {
-            @Test
-            void successful() throws Exception {
-                byte[] bytes = "g.".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                ReplyOrConfirmation msg = ReplyOrConfirmation.staticParse(readBufferByteBased, cBusOptions, requestContext);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void toManyRetransmissions() throws Exception {
-                byte[] bytes = "g#".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                ReplyOrConfirmation msg = ReplyOrConfirmation.staticParse(readBufferByteBased, cBusOptions, requestContext);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void corruption() throws Exception {
-                byte[] bytes = "g$".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                ReplyOrConfirmation msg = ReplyOrConfirmation.staticParse(readBufferByteBased, cBusOptions, requestContext);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void desync() throws Exception {
-                byte[] bytes = "g%".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                ReplyOrConfirmation msg = ReplyOrConfirmation.staticParse(readBufferByteBased, cBusOptions, requestContext);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void tooLong() throws Exception {
-                byte[] bytes = "g'".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                ReplyOrConfirmation msg = ReplyOrConfirmation.staticParse(readBufferByteBased, cBusOptions, requestContext);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 7.3
-        @Test
-        void StandardFormatStatusReply1() throws Exception {
-            byte[] bytes = "D8380068AA0140550550001000000014000000000000000000CF\r\n".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            requestContext = new RequestContext(true);
-            cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        @Test
-        void StandardFormatStatusReply2() throws Exception {
-            byte[] bytes = "D838580000000000000000000000000000000000000000000098\r\n".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            requestContext = new RequestContext(true);
-            cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        @Test
-        void StandardFormatStatusReply3() throws Exception {
-            byte[] bytes = "D638B000000000FF00000000000000000000000000000043\r\n".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            requestContext = new RequestContext(true);
-            cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        // 7.4
-        @Test
-        void ExtendedFormatStatusReply1() throws Exception {
-            byte[] bytes = "F9073800AAAA000095990000000055550000000000005555555548\r\n".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            requestContext = new RequestContext(true);
-            cBusOptions = new CBusOptions(false, false, false, true, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        @Test
-        void ExtendedFormatStatusReply2() throws Exception {
-            byte[] bytes = "F907380B0000000000005555000000000000000000000000000013\r\n".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            requestContext = new RequestContext(true);
-            cBusOptions = new CBusOptions(false, false, false, true, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        @Test
-        void ExtendedFormatStatusReply3() throws Exception {
-            byte[] bytes = "F70738160000000000000000000000000000000000000000B4\r\n".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            requestContext = new RequestContext(true);
-            cBusOptions = new CBusOptions(false, false, false, true, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        // 9.1
-        @Nested
-        class PointToMultiPointCommandsIntoLocalCBusNetwork {
-            @Test
-            void LightningOff() throws Exception {
-                byte[] bytes = "\\0538000114AE\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void LightningStatus() throws Exception {
-                byte[] bytes = "\\05FF007A38004A\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void LightningStatusReply1() throws Exception {
-                byte[] bytes = "D83800A8AA02000000000000000000000000000000000000009C\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                requestContext = new RequestContext(true);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void LightningStatusReply2() throws Exception {
-                byte[] bytes = "D838580000000000000000000000000000000000000000000098\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void LightningStatusReply3() throws Exception {
-                byte[] bytes = "D638B0000000000000000000000000000000000000000042\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void LightningStatusReply4() throws Exception {
-                // TODO: the command header seems wrong as it is missing a byte
-                //byte[] bytes = "86999900F8003800A8AA0200000000000000000000000000000000000000C4\r\n".getBytes(StandardCharsets.UTF_8);
-                byte[] bytes = "86999900F9003800A8AA0200000000000000000000000000000000000000C3\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, true, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-
-            @Test
-            void LightningStatusReply5() throws Exception {
-                // TODO: the command header seems wrong as it is missing a byte
-                // byte[] bytes = "86999900F800385800000000000000000000000000000000000000000000C0\r\n".getBytes(StandardCharsets.UTF_8);
-                byte[] bytes = "86999900F900385800000000000000000000000000000000000000000000BF\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, true, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void LightningStatusReply6() throws Exception {
-                // TODO: wrong checksum in this example???
-                // TODO: the command header seems wrong as it is missing a byte
-                //byte[] bytes = "86999900F60038B000000000000000000000000000000000000000008F\r\n".getBytes(StandardCharsets.UTF_8);
-                byte[] bytes = "86999900F70038B0000000000000000000000000000000000000000069\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, true, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 9.2
-        @Nested
-        class PointToPointCommandsIntoLocalCBusNetwork {
-            @Test
-            void RecallCurrentValueOfParameter0x30onUnit0x04() throws Exception {
-                // TODO: the section describes that on non smart mode the message doesn't have the last CR
-                byte[] bytes = "\\0604001A3001AB\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // TODO: due the usage of reserved we lose bits here so we need to fix that
-            @Disabled("TODO: due the usage of reserved we lose bits here so we need to fix that")
-            @Test
-            void Reply() throws Exception {
-                byte[] bytes = "8604990082300328\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-        }
-
-        // 9.3
-        @Nested
-        class PointToMultiPointCommandsIntoaRemoteCBusNetwork {
-            @Test
-            void IssueLightningOf() throws Exception {
-                // TODO: the section describes that on non smart mode the message doesn't have the last CR
-                byte[] bytes = "\\03421B53643801149C\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Disabled("the transformation from point to point to multipoint message is not yet implemented as described in 8.4... not sure if we will ever implement that")
-            @Test
-            void Reply() throws Exception {
-                byte[] bytes = Hex.decodeHex("0565380354432101148E");
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusCommand msg = CBusCommand.staticParse(readBufferByteBased, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-        }
-
-        // 9.4
-        @Test
-        void SwitchMode() throws Exception {
-            byte[] bytes = "~@A3300019\r".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-            assertMessageMatches(bytes, msg);
-        }
-
-        // 9.5
-        @Test
-        void MultipleCommands() throws Exception {
-            byte[] bytes = "\\05380001210122012301240A25010A2601D4\r".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        // 10.2.1
-        @Test
-        void testParameterSet() throws Exception {
-            byte[] bytes = "@A3470011\r".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        // 10.2.1
-        @Test
-        void testParameterSetObsolete() throws Exception {
-            byte[] bytes = "A3470011\r".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/C-Bus%20Quick%20Start%20Guide.pdf
-    @Nested
-    class CBusQuickStartGuideTest {
-
-        // 4.3
-        @Test
-        void checksums() throws Exception {
-            byte[] bytes = "\\0538007988C2g\r".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        //5.2
-        @Nested
-        class PCI_Setup {
-
-            // 5.2.1
-            @Nested
-            class MMIMessagesNotRequired {
-
-                @Test
-                void init() throws Exception {
-                    byte[] bytes = "~~~\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void writeSomething() throws Exception {
-                    byte[] bytes = "A3210038g\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void writeSomethingResponse() throws Exception {
-                    byte[] bytes = "g.322100AD\r\n".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-                    requestContext = new RequestContext(false);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void writeSomething2() throws Exception {
-                    byte[] bytes = "A3420002g\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void writeSomethingResponse2() throws Exception {
-                    byte[] bytes = "g.3242008C\r\n".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    requestContext = new RequestContext(false);
-                    cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void writeSomething3() throws Exception {
-                    byte[] bytes = "A3300059g\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void writeSomethingResponse3() throws Exception {
-                    byte[] bytes = "g.8600000032300018\r\n".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = C_BUS_OPTIONS_WITH_SRCHK;
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-
-            // 5.2.2
-            @Test
-            void MMIMessagesRequired() throws Exception {
-                byte[] bytes = "A3300079g\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 6
-        @Nested
-        class TransmittingCBusLightingControlCommands {
-            // 6.1
-            @Test
-            void TransmitAnONCommand() throws Exception {
-                byte[] bytes = "\\053800790842u\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 6.2
-            @Test
-            void TransmitAnOFFCommand() throws Exception {
-                byte[] bytes = "\\0538000108BAu\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 6.3
-            @Test
-            void TransmitAnRampToLevelCommand() throws Exception {
-                byte[] bytes = "\\0538005A08550Cu\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 7
-        @Nested
-        class ReceivingCBusLightingControlCommands {
-            // 7.1
-            @Test
-            void ReceiveAnONCommand() throws Exception {
-                byte[] bytes = "05003800790842\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 7.1
-            @Test
-            void ReceiveAnONCommandAlternative() throws Exception {
-                byte[] bytes = "0500380100790841\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 7.2
-            @Test
-            void ReceiveAnOFFCommand() throws Exception {
-                byte[] bytes = "050038000108BA\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 7.2
-            @Test
-            void ReceiveAnOFFCommandAlternative() throws Exception {
-                byte[] bytes = "05003801000108B9\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 7.3
-            @Test
-            void ReceiveAnRampToLevelCommand() throws Exception {
-                byte[] bytes = "050038005A08550C\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 7.3
-            @Test
-            void ReceiveAnRampToLevelCommandAlternative() throws Exception {
-                byte[] bytes = "05003801005A08550B\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Disabled("Needs to be implemented")
-            // 7.4
-            @Nested
-            class ReceivingOtherCommands {
-                @Test
-                void Case1() throws Exception {
-                    // Test with nn not 00 or 01... they should be discarded
-                    byte[] bytes = "05ss38nn....zz\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Case2() throws Exception {
-                    // Test with nn not 00 or 01... they should be discarded
-                    byte[] bytes = "05ss3800cc....zz\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Case2Alternative() throws Exception {
-                    // Test with nn not 00 or 01... they should be discarded
-                    byte[] bytes = "05ss380100cc....zz\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-
-        }
-
-        // 8
-        @Nested
-        class InterpretingTheMmi {
-            @Test
-            void BigMMI1() throws Exception {
-                byte[] bytes = "D8380068AA0140550550001000000014000000000000000000CF\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void BigMMI2() throws Exception {
-                byte[] bytes = "D838580000000000000000000000000000000000000000000098\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void BigMMI3() throws Exception {
-                byte[] bytes = "D638B000000000FF00000000000000000000000000000043\r\n".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-
-        // 9
-        @Nested
-        class Example {
-            @Nested
-            class ControlExamples {
-                @Test
-                void turnOnLight() throws Exception {
-                    byte[] bytes = "\\053800792129g\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void turnOffLight() throws Exception {
-                    byte[] bytes = "\\0538000121A1g\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void rampLight() throws Exception {
-                    byte[] bytes = "\\0538000A217F19g\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-
-            @Nested
-            class MontiorExamples {
-                @Test
-                void onCommand() throws Exception {
-                    byte[] bytes = "050B380079201F\r\n".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void offCommand() throws Exception {
-                    byte[] bytes = "050B3800012097\r\n".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Ramp() throws Exception {
-                    byte[] bytes = "050B38000220484E\r\n".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, true, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2002%20-%20C-Bus%20Lighting%20Application.pdf
-    @Nested
-    class LightningApplicationsTest {
-
-        // 2.9.6.4.4 Command Sequence
-        @Nested
-        class CommandSquence {
-
-            @Test
-            void StartDynamicIcon() throws Exception {
-                byte[] bytes = "\\053800A412080020\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void IconBitmap() throws Exception {
-                byte[] bytes = "\\053800A412080021\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void CompleteDynamicIcon() throws Exception {
-                byte[] bytes = "\\053800A412080022\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Nested
-            class ChineseTable {
-
-                @Test
-                void StartDynamicIcon() throws Exception {
-                    byte[] bytes = "\\053800A401080020\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void IconHeader() throws Exception {
-                    byte[] bytes = "\\053800A80104CA00130C0600\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void AppendDynamicIcon() throws Exception {
-                    byte[] bytes = "\\053800A401080021\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void WriteIconBitmapData1() throws Exception {
-                    byte[] bytes = "\\053800A80104AAF05500FF50\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void WriteIconBitmapData2() throws Exception {
-                    byte[] bytes = "\\053800A801040000F0F00F00\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void useDynamicIcon() throws Exception {
-                    byte[] bytes = "\\053800A401080022\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void displayIcon() throws Exception {
-                    byte[] bytes = "\\053800A60102CA010013\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-            }
-        }
-
-        // 2.11 Examples
-        @Nested
-        class Examples {
-            @Test
-            void switchElectricalLoads() throws Exception {
-                byte[] bytes = "\\0538007993B7\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void switchElectricalLoadsBridged() throws Exception {
-                byte[] bytes = "\\0356093879935A\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2005%20-%20C-Bus%20Security%20Application.pdf
-    @Nested
-    class SecurityApplicationsTest {
-
-        //5.11.1
-        @Nested
-        class SecuritySystemEmitsAlarmOn {
-            @Test
-            void AlarmOnWrongPrio() throws Exception {
-                byte[] bytes = "\\05D00079832F\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void AlarmOn() throws Exception {
-                byte[] bytes = "\\85D0007983AF\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-        }
-
-        //5.11.2
-        @Test
-        void Zone3Unsealed() throws Exception {
-            byte[] bytes = "\\05D0000A860398\r".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        //5.11.3
-        @Test
-        void ZoneName() throws Exception {
-            byte[] bytes = "\\05D000AD8D034B49544348452E2020202088\r".getBytes(StandardCharsets.UTF_8);
-            ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-            cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-            CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-            assertThat(msg).isNotNull();
-            System.out.println(msg);
-
-            assertMessageMatches(bytes, msg);
-        }
-
-        // 5.11.4
-        @Nested
-        class DeviceRequestsSecuritySystemtoArm {
-
-            @Test
-            void ArmSecurity() throws Exception {
-                byte[] bytes = "\\05D0000AA2FF80\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void ArmSecurityRemote() throws Exception {
-                byte[] bytes = "\\039209D00AA2FFE7\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-    }
-
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2006%20-%20C-Bus%20Metering%20Application.pdf
-    @Nested
-    class MeteringApplicationsTest {
-
-        //6.11.1
-        @Nested
-        class DeviceRequestsMeteringApplicationtoMeasureElectricity {
-            @Test
-            void LocalMeasurement() throws Exception {
-                byte[] bytes = "\\05D100090120\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void RemoteMeasurement() throws Exception {
-                byte[] bytes = "\\035609D10901C3\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-        }
-
-        //6.11.2
-        @Nested
-        class MeterMeasurementDevicesendsElectricityUse {
-            @Test
-            void LocalMeasurement() throws Exception {
-                byte[] bytes = "\\05D1000D810000DBF8C9\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void RemoteMeasurement() throws Exception {
-                byte[] bytes = "\\033709D10D810000DBF88B\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2007%20-%20C-Bus%20Trigger%20Control%20Application.pdf
-    @Nested
-    class TriggerControlApplicationsTest {
-
-        //7.12
-        @Nested
-        class Examples {
-            @Test
-            void LocalTrigger() throws Exception {
-                byte[] bytes = "\\05CA0002250109\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void RemoteTrigger() throws Exception {
-                byte[] bytes = "\\035609CA022501AC\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2008%20-%20C-Bus%20Enable%20Control%20Application.pdf
-    @Nested
-    class EnableControlApplicationsTest {
-
-        //8.11
-        @Nested
-        class Examples {
-            @Test
-            void LocalTrigger() throws Exception {
-                byte[] bytes = "\\05CB0002378275\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void RemoteTrigger() throws Exception {
-                // TODO: seems like the checksum is wrong here again...
-                //byte[] bytes = "\\035609CB02378216\r".getBytes(StandardCharsets.UTF_8);
-                byte[] bytes = "\\035609CB02378218\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2009%20-%20C-Bus%20Temperature%20Broadcast%20Application.pdf
-    @Nested
-    class TemperatureBroadcastApplicationsTest {
-
-        //9.11
-        @Nested
-        class Examples {
-
-            @Test
-            void temperatureBroadcast() throws Exception {
-                byte[] bytes = "\\051900020564\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2010%20-%20C-Bus%20Ventilation%20Application.pdf
-    @Nested
-    class VentilationApplicationTest {
-        // TODO: no tests described here but it should work by adjusting the values from Lightning...
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2009%20-%20C-Bus%20Temperature%20Control%20Application.pdf
-    @Nested
-    class AccessControlApplicationsTest {
-
-        //9.11
-        @Nested
-        class Examples {
-
-            @Test
-            void validAccessRequest() throws Exception {
-                byte[] bytes = "\\05D500A4010300017D\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void closeAccessPoint() throws Exception {
-                byte[] bytes = "\\05D5000201FF24\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void lockAccessPoint() throws Exception {
-                byte[] bytes = "\\05D5000AFFFF1E\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void lockAccessPointRemote() throws Exception {
-                byte[] bytes = "\\039209D50AFFFF85\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2021%20-%20C-Bus%20Media%20Transport%20Control%20Application.pdf
-    @Nested
-    class MediaTransportControlApplicationsTest {
-        // TODO: no tests described here
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2023%20-%20C-Bus%20Clock%20and%20Timekeeping%20Application.pdf
-    @Nested
-    class ClockAndTimekeeping {
-
-        //23.13
-        @Nested
-        class Examples {
-
-            @Test
-            void outputATimeCommand() throws Exception {
-                byte[] bytes = "\\05DF000D010A2B1700C2\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void outputADateCommand() throws Exception {
-                byte[] bytes = "\\05DF000E0207D502190411\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Disabled("This example just seems plain wrong... First of all there is no command for 0x10 defined and the argument which should be it 0x11 requires a argument of 0x03... So either documenation wrong or the example")
-            @Test
-            void outputARequestRefreshCommand() throws Exception {
-                byte[] bytes = "\\05DF00100C\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void outputARequestRefreshCommandFixedQuestionMark() throws Exception {
-                byte[] bytes = "\\05DF001103\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2024%20-%20C-Bus%20Telephony%20Application.pdf
-    @Nested
-    class Telephony {
-
-        //24.11
-        @Nested
-        class Examples {
-
-            @Test
-            void LineOnHook() throws Exception {
-                byte[] bytes = "\\05E000090111\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Disabled("Again it seems as this command is just wrong... there is no command definition for 2C")
-            @Test
-            void LineOffHook() throws Exception {
-                byte[] bytes = "\\05E0002C020230333935323734333231FD\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            @Test
-            void LineOffHookFixedQuestionMark() throws Exception {
-                byte[] bytes = "\\05E000AC02013033393532373433323168\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                cBusOptions = new CBusOptions(false, false, false, false, false, false, false, false, true);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-        }
-    }
-
-    // from: https://updates.clipsal.com/ClipsalSoftwareDownload/DL/downloads/OpenCBus/Chapter%2034%20-%20C-Bus%20Error%20Reporting%20Application.pdf
-    @Nested
-    class ErrorReporting {
-
-        //34.13
-        @Nested
-        class Examples {
-
-            // 34.13.1
-            @Test
-            void AllOk() throws Exception {
-                byte[] bytes = "\\05CE0015FF20DE0000\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-            // 34.13.2
-            @Test
-            void MinorFailure() throws Exception {
-                byte[] bytes = "\\05CE0015882A6721B4\r".getBytes(StandardCharsets.UTF_8);
-                ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                assertThat(msg).isNotNull();
-                System.out.println(msg);
-
-                assertMessageMatches(bytes, msg);
-            }
-
-
-            // 34.13.3
-            @Nested
-            class GeneralFailureWhichGetsAcknowledged {
-                @Test
-                void Reporting() throws Exception {
-                    byte[] bytes = "\\05CE00159023426633\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void Acknowledge() throws Exception {
-                    byte[] bytes = "\\05CE00259033426633\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-
-            @Nested
-            class LatchedExtremeFailureWhichGetsCleared {
-                @Test
-                void mostRecent() throws Exception {
-                    byte[] bytes = "\\05CE001569E1FE0100\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void mostSevere() throws Exception {
-                    byte[] bytes = "\\05CE001569CCFE0102\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void clearMostSevere() throws Exception {
-                    byte[] bytes = "\\05CE003569C9FE0102\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-
-                @Test
-                void newError() throws Exception {
-                    byte[] bytes = "\\05CE001569E9FE0100\r".getBytes(StandardCharsets.UTF_8);
-                    ReadBufferByteBased readBufferByteBased = new ReadBufferByteBased(bytes);
-                    CBusMessage msg = CBusMessage.staticParse(readBufferByteBased, false, requestContext, cBusOptions);
-                    assertThat(msg).isNotNull();
-                    System.out.println(msg);
-
-                    assertMessageMatches(bytes, msg);
-                }
-            }
-        }
-    }
-}
diff --git a/plc4j/drivers/mock/src/main/java/org/apache/plc4x/java/mock/connection/MockConnection.java b/plc4j/drivers/mock/src/main/java/org/apache/plc4x/java/mock/connection/MockConnection.java
index db91dfd7a..a9aa23825 100644
--- a/plc4j/drivers/mock/src/main/java/org/apache/plc4x/java/mock/connection/MockConnection.java
+++ b/plc4j/drivers/mock/src/main/java/org/apache/plc4x/java/mock/connection/MockConnection.java
@@ -30,17 +30,7 @@ import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.mock.field.MockField;
 import org.apache.plc4x.java.mock.field.MockFieldHandler;
 import org.apache.plc4x.java.mock.field.MockValueHandler;
-import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcReadResponse;
-import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionResponse;
-import org.apache.plc4x.java.spi.messages.DefaultPlcUnsubscriptionRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcUnsubscriptionResponse;
-import org.apache.plc4x.java.spi.messages.DefaultPlcWriteRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcWriteResponse;
-import org.apache.plc4x.java.spi.messages.PlcReader;
-import org.apache.plc4x.java.spi.messages.PlcSubscriber;
-import org.apache.plc4x.java.spi.messages.PlcWriter;
+import org.apache.plc4x.java.spi.messages.*;
 import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,7 +42,7 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
-public class MockConnection implements PlcConnection, PlcReader, PlcWriter, PlcSubscriber {
+public class MockConnection implements PlcConnection, PlcReader, PlcWriter, PlcSubscriber, PlcBrowser {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(MockConnection.class);
 
@@ -112,9 +102,28 @@ public class MockConnection implements PlcConnection, PlcReader, PlcWriter, PlcS
             public boolean canSubscribe() {
                 return true;
             }
+
+            @Override
+            public boolean canBrowse() {
+                return true;
+            }
         };
     }
 
+    @Override
+    public PlcBrowseRequest.Builder browseRequestBuilder() {
+        return new DefaultPlcBrowseRequest.Builder(this);
+    }
+
+    @Override
+    public CompletableFuture<PlcBrowseResponse> browse(PlcBrowseRequest browseRequest) {
+        return CompletableFuture.supplyAsync(() -> {
+            Validate.notNull(device, "No device is set in the mock connection!");
+            LOGGER.debug("Sending browse request to MockDevice");
+            return new DefaultPlcBrowseResponse(browseRequest);
+        });
+    }
+
     @Override
     public PlcReadRequest.Builder readRequestBuilder() {
         return new DefaultPlcReadRequest.Builder(this, new MockFieldHandler());
diff --git a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
index 324125a5a..ad0f4f08b 100644
--- a/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
+++ b/plc4j/drivers/opcua/src/main/java/org/apache/plc4x/java/opcua/OpcuaPlcDriver.java
@@ -103,6 +103,11 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> {
         return true;
     }
 
+    @Override
+    protected boolean canBrowse() {
+        return false;
+    }
+
     @Override
     protected OpcuaOptimizer getOptimizer() {
         return new OpcuaOptimizer();
@@ -223,7 +228,7 @@ public class OpcuaPlcDriver extends GeneratedDriverBase<OpcuaAPU> {
         }
 
         return new DefaultNettyPlcConnection(
-            canRead(), canWrite(), canSubscribe(),
+            canRead(), canWrite(), canSubscribe(), canBrowse(),
             getFieldHandler(),
             getValueHandler(),
             configuration,
diff --git a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
index 246b53c6f..8df6bca59 100644
--- a/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
+++ b/plc4j/drivers/profinet/src/main/java/org/apache/plc4x/java/profinet/discovery/ProfinetPlcDiscoverer.java
@@ -74,6 +74,13 @@ public class ProfinetPlcDiscoverer implements PlcDiscoverer {
         List<PlcDiscoveryItem> values = new ArrayList<>();
         try {
             for (PcapNetworkInterface dev : Pcaps.findAllDevs()) {
+                // It turned out on some MAC network devices without any ip addresses
+                // the compiling of the filter expression was causing errors. As
+                // currently there was no other way to detect this, this check seems
+                // to be sufficient.
+                if(dev.getAddresses().size() == 0) {
+                    continue;
+                }
                 if (!dev.isLoopBack()) {
                     for (LinkLayerAddress linkLayerAddress : dev.getLinkLayerAddresses()) {
                         org.pcap4j.util.MacAddress macAddress = (org.pcap4j.util.MacAddress) linkLayerAddress;
diff --git a/plc4j/drivers/simulated/src/main/java/org/apache/plc4x/java/simulated/connection/SimulatedConnection.java b/plc4j/drivers/simulated/src/main/java/org/apache/plc4x/java/simulated/connection/SimulatedConnection.java
index ac33c9e86..c580aea2c 100644
--- a/plc4j/drivers/simulated/src/main/java/org/apache/plc4x/java/simulated/connection/SimulatedConnection.java
+++ b/plc4j/drivers/simulated/src/main/java/org/apache/plc4x/java/simulated/connection/SimulatedConnection.java
@@ -78,7 +78,7 @@ public class SimulatedConnection extends AbstractPlcConnection implements PlcRea
     private final Map<Integer, Consumer<PlcSubscriptionEvent>> consumerIdMap = new ConcurrentHashMap<>();
 
     public SimulatedConnection(SimulatedDevice device) {
-        super(true, true, true, new SimulatedFieldHandler(), new IEC61131ValueHandler(), null);
+        super(true, true, true, false, new SimulatedFieldHandler(), new IEC61131ValueHandler(), null);
         this.device = device;
     }
 
diff --git a/plc4j/examples/hello-world-plc4x-subscription/pom.xml b/plc4j/examples/hello-world-plc4x-discover-and-browse/pom.xml
similarity index 86%
copy from plc4j/examples/hello-world-plc4x-subscription/pom.xml
copy to plc4j/examples/hello-world-plc4x-discover-and-browse/pom.xml
index 86febad26..671ea5a45 100644
--- a/plc4j/examples/hello-world-plc4x-subscription/pom.xml
+++ b/plc4j/examples/hello-world-plc4x-discover-and-browse/pom.xml
@@ -27,12 +27,12 @@
     <version>0.10.0-SNAPSHOT</version>
   </parent>
 
-  <artifactId>plc4j-examples-hello-world-plc4x-subscription</artifactId>
-  <name>PLC4J: Examples: Hello-World PLC4X (Subscription)</name>
-  <description>Hello world application for PLC4X using the subscription API.</description>
+  <artifactId>plc4j-examples-hello-world-plc4x-discover-and-browse</artifactId>
+  <name>PLC4J: Examples: Hello-World PLC4X (Discover And Browse)</name>
+  <description>Hello world application for PLC4X using the discovery and browse-API.</description>
 
   <properties>
-    <app.main.class>org.apache.plc4x.java.examples.helloplc4x.subscription.HelloPlc4xSubscription</app.main.class>
+    <app.main.class>org.apache.plc4x.java.examples.helloplc4x.discoverandbrowse.HelloPlc4x</app.main.class>
   </properties>
 
   <dependencies>
@@ -46,7 +46,6 @@
       <groupId>commons-cli</groupId>
       <artifactId>commons-cli</artifactId>
     </dependency>
-
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
diff --git a/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4x.java b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4x.java
new file mode 100644
index 000000000..b95d3a670
--- /dev/null
+++ b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/java/org/apache/plc4x/java/examples/helloplc4x/discoverandbrowse/HelloPlc4x.java
@@ -0,0 +1,60 @@
+/*
+ * 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
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.plc4x.java.examples.helloplc4x.discoverandbrowse;
+
+import org.apache.plc4x.java.PlcDriverManager;
+import org.apache.plc4x.java.api.PlcDriver;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryRequest;
+import org.apache.plc4x.java.api.messages.PlcDiscoveryResponse;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+
+public class HelloPlc4x {
+
+    private static final Logger logger = LoggerFactory.getLogger(HelloPlc4x.class);
+
+    public static void main(String[] args) throws Exception {
+        // Iterate over all installed drivers and execute their browse functionality (If they support it)
+        PlcDriverManager plcDriverManager = new PlcDriverManager();
+        for (String protocolCode : plcDriverManager.listDrivers()) {
+            // For some reason modbus is failing on my machine ... investigate
+            if(protocolCode.startsWith("modbus")) {
+                continue;
+            }
+            PlcDriver driver = plcDriverManager.getDriver(protocolCode);
+            if(driver.getMetadata().canDiscover()) {
+                logger.info("Performing discovery for {} protocol", driver.getProtocolName());
+
+                PlcDiscoveryRequest discoveryRequest = driver.discoveryRequestBuilder().build();
+
+                PlcDiscoveryResponse discoveryResponse = discoveryRequest.execute().get(60, TimeUnit.SECONDS);
+                if(discoveryResponse.getResponseCode() == PlcResponseCode.OK) {
+                    for (PlcDiscoveryItem value : discoveryResponse.getValues()) {
+                        logger.info(" - Found device with connection-url {}", value.getConnectionUrl());
+                    }
+                }
+            }
+        }
+    }
+
+}
diff --git a/plc4j/examples/hello-world-plc4x/src/main/resources/logback.xml b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
similarity index 91%
copy from plc4j/examples/hello-world-plc4x/src/main/resources/logback.xml
copy to plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
index eee180ec7..6359e3453 100644
--- a/plc4j/examples/hello-world-plc4x/src/main/resources/logback.xml
+++ b/plc4j/examples/hello-world-plc4x-discover-and-browse/src/main/resources/logback.xml
@@ -27,7 +27,10 @@
     </encoder>
   </appender>
 
-  <root level="info">
+  <!-- Pcap4j seems to be quite chatty -->
+  <logger name="org.pcap4j" level="WARN"/>
+
+  <root level="INFO">
     <appender-ref ref="STDOUT" />
   </root>
 
diff --git a/plc4j/examples/hello-world-plc4x/pom.xml b/plc4j/examples/hello-world-plc4x-read/pom.xml
similarity index 87%
rename from plc4j/examples/hello-world-plc4x/pom.xml
rename to plc4j/examples/hello-world-plc4x-read/pom.xml
index 233a03a88..c16e97cf8 100644
--- a/plc4j/examples/hello-world-plc4x/pom.xml
+++ b/plc4j/examples/hello-world-plc4x-read/pom.xml
@@ -27,12 +27,12 @@
     <version>0.10.0-SNAPSHOT</version>
   </parent>
 
-  <artifactId>plc4j-examples-hello-world-plc4x</artifactId>
-  <name>PLC4J: Examples: Hello-World PLC4X</name>
-  <description>Hello world application for PLC4X.</description>
+  <artifactId>plc4j-examples-hello-world-plc4x-read</artifactId>
+  <name>PLC4J: Examples: Hello-World PLC4X (Read)</name>
+  <description>Hello world application for PLC4X using the read-API.</description>
 
   <properties>
-    <app.main.class>org.apache.plc4x.java.examples.helloplc4x.HelloPlc4x</app.main.class>
+    <app.main.class>org.apache.plc4x.java.examples.helloplc4x.discoverandbrowse.HelloPlc4x</app.main.class>
   </properties>
 
   <dependencies>
diff --git a/plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/CliOptions.java b/plc4j/examples/hello-world-plc4x-read/src/main/java/org/apache/plc4x/java/examples/helloplc4x/read/CliOptions.java
similarity index 97%
rename from plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/CliOptions.java
rename to plc4j/examples/hello-world-plc4x-read/src/main/java/org/apache/plc4x/java/examples/helloplc4x/read/CliOptions.java
index 3a21d7cc1..718c6a767 100644
--- a/plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/CliOptions.java
+++ b/plc4j/examples/hello-world-plc4x-read/src/main/java/org/apache/plc4x/java/examples/helloplc4x/read/CliOptions.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.java.examples.helloplc4x;
+package org.apache.plc4x.java.examples.helloplc4x.read;
 
 import org.apache.commons.cli.*;
 
diff --git a/plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/HelloPlc4x.java b/plc4j/examples/hello-world-plc4x-read/src/main/java/org/apache/plc4x/java/examples/helloplc4x/read/HelloPlc4x.java
similarity index 96%
rename from plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/HelloPlc4x.java
rename to plc4j/examples/hello-world-plc4x-read/src/main/java/org/apache/plc4x/java/examples/helloplc4x/read/HelloPlc4x.java
index 3ecd91105..b74623d9b 100644
--- a/plc4j/examples/hello-world-plc4x/src/main/java/org/apache/plc4x/java/examples/helloplc4x/HelloPlc4x.java
+++ b/plc4j/examples/hello-world-plc4x-read/src/main/java/org/apache/plc4x/java/examples/helloplc4x/read/HelloPlc4x.java
@@ -16,19 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.java.examples.helloplc4x;
+package org.apache.plc4x.java.examples.helloplc4x.read;
 
 import org.apache.plc4x.java.PlcDriverManager;
 import org.apache.plc4x.java.api.PlcConnection;
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
 import org.apache.plc4x.java.api.messages.PlcReadRequest;
 import org.apache.plc4x.java.api.messages.PlcReadResponse;
 import org.apache.plc4x.java.api.types.PlcResponseCode;
-import org.apache.plc4x.java.api.value.PlcValue;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.concurrent.CompletionStage;
 import java.util.concurrent.TimeUnit;
 
 public class HelloPlc4x {
diff --git a/plc4j/examples/hello-world-plc4x/src/main/resources/logback.xml b/plc4j/examples/hello-world-plc4x-read/src/main/resources/logback.xml
similarity index 100%
rename from plc4j/examples/hello-world-plc4x/src/main/resources/logback.xml
rename to plc4j/examples/hello-world-plc4x-read/src/main/resources/logback.xml
diff --git a/plc4j/examples/hello-world-plc4x-subscription/pom.xml b/plc4j/examples/hello-world-plc4x-subscribe/pom.xml
similarity index 91%
rename from plc4j/examples/hello-world-plc4x-subscription/pom.xml
rename to plc4j/examples/hello-world-plc4x-subscribe/pom.xml
index 86febad26..5a1443789 100644
--- a/plc4j/examples/hello-world-plc4x-subscription/pom.xml
+++ b/plc4j/examples/hello-world-plc4x-subscribe/pom.xml
@@ -27,12 +27,12 @@
     <version>0.10.0-SNAPSHOT</version>
   </parent>
 
-  <artifactId>plc4j-examples-hello-world-plc4x-subscription</artifactId>
-  <name>PLC4J: Examples: Hello-World PLC4X (Subscription)</name>
-  <description>Hello world application for PLC4X using the subscription API.</description>
+  <artifactId>plc4j-examples-hello-world-plc4x-subscribe</artifactId>
+  <name>PLC4J: Examples: Hello-World PLC4X (Subscribe)</name>
+  <description>Hello world application for PLC4X using the subscription-API.</description>
 
   <properties>
-    <app.main.class>org.apache.plc4x.java.examples.helloplc4x.subscription.HelloPlc4xSubscription</app.main.class>
+    <app.main.class>org.apache.plc4x.java.examples.helloplc4x.subscribe.HelloPlc4xSubscription</app.main.class>
   </properties>
 
   <dependencies>
diff --git a/plc4j/examples/hello-world-plc4x-subscription/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscription/CliOptions.java b/plc4j/examples/hello-world-plc4x-subscribe/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscribe/CliOptions.java
similarity index 97%
rename from plc4j/examples/hello-world-plc4x-subscription/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscription/CliOptions.java
rename to plc4j/examples/hello-world-plc4x-subscribe/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscribe/CliOptions.java
index 694e907fe..acb6fc21f 100644
--- a/plc4j/examples/hello-world-plc4x-subscription/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscription/CliOptions.java
+++ b/plc4j/examples/hello-world-plc4x-subscribe/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscribe/CliOptions.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.java.examples.helloplc4x.subscription;
+package org.apache.plc4x.java.examples.helloplc4x.subscribe;
 
 import org.apache.commons.cli.*;
 
diff --git a/plc4j/examples/hello-world-plc4x-subscription/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscription/HelloPlc4xSubscription.java b/plc4j/examples/hello-world-plc4x-subscribe/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscribe/HelloPlc4xSubscription.java
similarity index 98%
rename from plc4j/examples/hello-world-plc4x-subscription/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscription/HelloPlc4xSubscription.java
rename to plc4j/examples/hello-world-plc4x-subscribe/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscribe/HelloPlc4xSubscription.java
index 2f827bf02..60b35a508 100644
--- a/plc4j/examples/hello-world-plc4x-subscription/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscription/HelloPlc4xSubscription.java
+++ b/plc4j/examples/hello-world-plc4x-subscribe/src/main/java/org/apache/plc4x/java/examples/helloplc4x/subscribe/HelloPlc4xSubscription.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.java.examples.helloplc4x.subscription;
+package org.apache.plc4x.java.examples.helloplc4x.subscribe;
 
 import org.apache.plc4x.java.PlcDriverManager;
 import org.apache.plc4x.java.api.PlcConnection;
diff --git a/plc4j/examples/hello-world-plc4x-subscription/src/main/resources/logback.xml b/plc4j/examples/hello-world-plc4x-subscribe/src/main/resources/logback.xml
similarity index 95%
rename from plc4j/examples/hello-world-plc4x-subscription/src/main/resources/logback.xml
rename to plc4j/examples/hello-world-plc4x-subscribe/src/main/resources/logback.xml
index 7d1b5f9ce..c990d53ab 100644
--- a/plc4j/examples/hello-world-plc4x-subscription/src/main/resources/logback.xml
+++ b/plc4j/examples/hello-world-plc4x-subscribe/src/main/resources/logback.xml
@@ -27,7 +27,7 @@
     </encoder>
   </appender>
 
-  <logger name="org.apache.plc4x.java.examples.helloplc4x.subscription.HelloPlc4xSubscription" level="info"/>
+  <logger name="org.apache.plc4x.java.examples.helloplc4x.subscribe.HelloPlc4xSubscription" level="info"/>
 
   <root level="warn">
     <appender-ref ref="STDOUT" />
diff --git a/plc4j/examples/hello-world-plc4x-write/pom.xml b/plc4j/examples/hello-world-plc4x-write/pom.xml
index 8a9ee8a9c..52d111762 100644
--- a/plc4j/examples/hello-world-plc4x-write/pom.xml
+++ b/plc4j/examples/hello-world-plc4x-write/pom.xml
@@ -29,7 +29,7 @@
 
   <artifactId>plc4j-examples-hello-world-plc4x-write</artifactId>
   <name>PLC4J: Examples: Hello-World PLC4X (Write)</name>
-  <description>Hello world application for PLC4X using the write API.</description>
+  <description>Hello world application for PLC4X using the write-API.</description>
 
   <properties>
     <app.main.class>org.apache.plc4x.java.examples.helloplc4x.write.HelloPlc4xWrite</app.main.class>
diff --git a/plc4j/examples/pom.xml b/plc4j/examples/pom.xml
index 6027e2076..7e97693cd 100644
--- a/plc4j/examples/pom.xml
+++ b/plc4j/examples/pom.xml
@@ -50,8 +50,9 @@
     <module>hello-opm</module>
     <module>hello-webservice</module>
     <module>hello-world-kotlin</module>
-    <module>hello-world-plc4x</module>
-    <module>hello-world-plc4x-subscription</module>
+    <module>hello-world-plc4x-discover-and-browse</module>
+    <module>hello-world-plc4x-read</module>
+    <module>hello-world-plc4x-subscribe</module>
     <module>hello-world-plc4x-write</module>
     <module>poll-loop</module>
     <module>plc4j-s7event</module>
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/AbstractPlcConnection.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/AbstractPlcConnection.java
index 2253e206e..8a2587a04 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/AbstractPlcConnection.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/AbstractPlcConnection.java
@@ -26,17 +26,10 @@ import org.apache.plc4x.java.api.metadata.PlcConnectionMetadata;
 import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
 import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
 import org.apache.plc4x.java.spi.Plc4xProtocolBase;
-import org.apache.plc4x.java.spi.messages.DefaultPlcReadRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcUnsubscriptionRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcWriteRequest;
-import org.apache.plc4x.java.spi.messages.PlcReader;
-import org.apache.plc4x.java.spi.messages.PlcSubscriber;
-import org.apache.plc4x.java.spi.messages.PlcWriter;
+import org.apache.plc4x.java.spi.messages.*;
 import org.apache.plc4x.java.spi.optimizer.BaseOptimizer;
 import org.apache.plc4x.java.api.value.PlcValueHandler;
 
-
 import java.util.Collection;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Consumer;
@@ -47,11 +40,12 @@ import java.util.function.Consumer;
  * Concrete implementations should override the methods indicating connection capabilities
  * and for obtaining respective request builders.
  */
-public abstract class AbstractPlcConnection implements PlcConnection, PlcConnectionMetadata, PlcReader, PlcWriter , PlcSubscriber {
+public abstract class AbstractPlcConnection implements PlcConnection, PlcConnectionMetadata, PlcReader, PlcWriter, PlcSubscriber, PlcBrowser {
 
     private boolean canRead = false;
     private boolean canWrite = false;
     private boolean canSubscribe = false;
+    private boolean canBrowse = false;
     private PlcFieldHandler fieldHandler;
     private PlcValueHandler valueHandler;
     private Plc4xProtocolBase<?> protocol;
@@ -64,11 +58,12 @@ public abstract class AbstractPlcConnection implements PlcConnection, PlcConnect
     protected AbstractPlcConnection() {
     }
 
-    protected AbstractPlcConnection(boolean canRead, boolean canWrite, boolean canSubscribe, PlcFieldHandler fieldHandler, PlcValueHandler valueHandler,
+    protected AbstractPlcConnection(boolean canRead, boolean canWrite, boolean canSubscribe, boolean canBrowse, PlcFieldHandler fieldHandler, PlcValueHandler valueHandler,
                                  BaseOptimizer optimizer) {
         this.canRead = canRead;
         this.canWrite = canWrite;
         this.canSubscribe = canSubscribe;
+        this.canBrowse = canBrowse;
         this.fieldHandler = fieldHandler;
         this.valueHandler = valueHandler;
         this.optimizer = optimizer;
@@ -105,6 +100,11 @@ public abstract class AbstractPlcConnection implements PlcConnection, PlcConnect
         return canSubscribe;
     }
 
+    @Override
+    public boolean canBrowse() {
+        return canBrowse;
+    }
+
     public PlcFieldHandler getPlcFieldHandler() {
         return this.fieldHandler;
     }
@@ -145,6 +145,14 @@ public abstract class AbstractPlcConnection implements PlcConnection, PlcConnect
         return new DefaultPlcUnsubscriptionRequest.Builder(this);
     }
 
+    @Override
+    public PlcBrowseRequest.Builder browseRequestBuilder() {
+        if (!canBrowse) {
+            throw new PlcUnsupportedOperationException("The connection does not support browsing");
+        }
+        return new DefaultPlcBrowseRequest.Builder(this);
+    }
+
     @Override
     public CompletableFuture<PlcReadResponse> read(PlcReadRequest readRequest) {
         if(optimizer != null) {
@@ -187,4 +195,9 @@ public abstract class AbstractPlcConnection implements PlcConnection, PlcConnect
         throw new NotImplementedException("");
     }
 
+    @Override
+    public CompletableFuture<PlcBrowseResponse> browse(PlcBrowseRequest browseRequest) {
+        throw new NotImplementedException("");
+    }
+
 }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/DefaultNettyPlcConnection.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/DefaultNettyPlcConnection.java
index ab8852906..303465976 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/DefaultNettyPlcConnection.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/DefaultNettyPlcConnection.java
@@ -64,12 +64,12 @@ public class DefaultNettyPlcConnection extends AbstractPlcConnection implements
     protected Channel channel;
     protected boolean connected;
 
-    public DefaultNettyPlcConnection(boolean canRead, boolean canWrite, boolean canSubscribe,
+    public DefaultNettyPlcConnection(boolean canRead, boolean canWrite, boolean canSubscribe, boolean canBrowse,
                                      PlcFieldHandler fieldHandler, PlcValueHandler valueHandler, Configuration configuration,
                                      ChannelFactory channelFactory, boolean awaitSessionSetupComplete,
                                      boolean awaitSessionDisconnectComplete, boolean awaitSessionDiscoverComplete,
                                      ProtocolStackConfigurer stackConfigurer, BaseOptimizer optimizer) {
-        super(canRead, canWrite, canSubscribe, fieldHandler, valueHandler, optimizer);
+        super(canRead, canWrite, canSubscribe, canBrowse, fieldHandler, valueHandler, optimizer);
         this.configuration = configuration;
         this.channelFactory = channelFactory;
         this.awaitSessionSetupComplete = awaitSessionSetupComplete;
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/GeneratedDriverBase.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/GeneratedDriverBase.java
index a5ea32f3d..7c65760ac 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/GeneratedDriverBase.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/connection/GeneratedDriverBase.java
@@ -58,6 +58,10 @@ public abstract class GeneratedDriverBase<BASE_PACKET extends Message> implement
         return false;
     }
 
+    protected boolean canBrowse() {
+        return false;
+    }
+
     protected boolean awaitSetupComplete() {
         return true;
     }
@@ -164,7 +168,7 @@ public abstract class GeneratedDriverBase<BASE_PACKET extends Message> implement
         }
 
         return new DefaultNettyPlcConnection(
-            canRead(), canWrite(), canSubscribe(),
+            canRead(), canWrite(), canSubscribe(), canBrowse(),
             getFieldHandler(),
             getValueHandler(),
             configuration,
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcBrowseRequest.java
similarity index 51%
copy from plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
copy to plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcBrowseRequest.java
index 3b0cccd14..3be055447 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcBrowseRequest.java
@@ -20,53 +20,70 @@ package org.apache.plc4x.java.spi.messages;
 
 import com.fasterxml.jackson.annotation.*;
 import org.apache.plc4x.java.api.messages.*;
-import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 import org.apache.plc4x.java.spi.utils.Serializable;
 
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
-public class DefaultPlcDiscoveryRequest implements PlcDiscoveryRequest, Serializable {
+public class DefaultPlcBrowseRequest implements PlcBrowseRequest, Serializable {
 
-    private final PlcDiscoverer discoverer;
+    private final PlcBrowser browser;
+
+    private final LinkedHashMap<String, String> queries;
 
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
-    public DefaultPlcDiscoveryRequest(@JsonProperty("discoverer") PlcDiscoverer discoverer) {
-        this.discoverer = discoverer;
+    public DefaultPlcBrowseRequest(@JsonProperty("browser") PlcBrowser browser,
+                                   @JsonProperty("queries") LinkedHashMap<String, String> queries) {
+        this.browser = browser;
+        this.queries = queries;
     }
 
     @Override
-    public CompletableFuture<? extends PlcDiscoveryResponse> execute() {
-        return discoverer.discover(this);
+    @JsonIgnore
+    public CompletableFuture<PlcBrowseResponse> execute() {
+        return browser.browse(this);
     }
 
-    @Override
-    public CompletableFuture<? extends PlcDiscoveryResponse> executeWithHandler(PlcDiscoveryItemHandler handler) {
-        return discoverer.discoverWithHandler(this, handler);
+    @JsonIgnore
+    public PlcBrowser getBrowser() {
+        return browser;
+    }
+
+    @JsonIgnore
+    public Map<String, String> getQueries() {
+        return queries;
     }
 
     @Override
     public void serialize(WriteBuffer writeBuffer) throws SerializationException {
-        writeBuffer.pushContext("PlcDiscoveryRequest");
+        writeBuffer.pushContext("PlcBrowseRequest");
+        writeBuffer.popContext("PlcBrowseRequest");
+    }
 
-        // TODO: Implement
+    public static class Builder implements PlcBrowseRequest.Builder {
 
-        writeBuffer.popContext("PlcDiscoveryRequest");
-    }
+        private final PlcBrowser browser;
 
-    public static class Builder implements PlcDiscoveryRequest.Builder {
+        private final LinkedHashMap<String, String> queries;
 
-        private final PlcDiscoverer discoverer;
+        public Builder(PlcBrowser browser) {
+            this.browser = browser;
+            queries = new LinkedHashMap<>();
+        }
 
-        public Builder(PlcDiscoverer discoverer) {
-            this.discoverer = discoverer;
+        @Override
+        public Builder addQuery(String name, String query) {
+            queries.put(name, query);
+            return this;
         }
 
         @Override
-        public PlcDiscoveryRequest build() {
-            return new DefaultPlcDiscoveryRequest(discoverer);
+        public PlcBrowseRequest build() {
+            return new DefaultPlcBrowseRequest(browser, queries);
         }
 
     }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcBrowseResponse.java
similarity index 51%
copy from plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
copy to plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcBrowseResponse.java
index 3b0cccd14..ae24cca36 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcBrowseResponse.java
@@ -19,56 +19,48 @@
 package org.apache.plc4x.java.spi.messages;
 
 import com.fasterxml.jackson.annotation.*;
-import org.apache.plc4x.java.api.messages.*;
-import org.apache.plc4x.java.spi.generation.ParseException;
+import org.apache.plc4x.java.api.exceptions.PlcInvalidFieldException;
+import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
+import org.apache.plc4x.java.api.messages.PlcBrowseRequest;
+import org.apache.plc4x.java.api.messages.PlcBrowseResponse;
+import org.apache.plc4x.java.api.messages.PlcReadRequest;
+import org.apache.plc4x.java.api.messages.PlcReadResponse;
+import org.apache.plc4x.java.api.model.PlcField;
+import org.apache.plc4x.java.api.types.PlcResponseCode;
+import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
+import org.apache.plc4x.java.spi.messages.utils.ResponseItem;
 import org.apache.plc4x.java.spi.utils.Serializable;
+import org.apache.plc4x.java.spi.values.PlcList;
+import org.apache.plc4x.java.spi.values.PlcStruct;
 
-import java.util.concurrent.CompletableFuture;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.*;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
-public class DefaultPlcDiscoveryRequest implements PlcDiscoveryRequest, Serializable {
+public class DefaultPlcBrowseResponse implements PlcBrowseResponse, Serializable {
 
-    private final PlcDiscoverer discoverer;
+    private final PlcBrowseRequest request;
 
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
-    public DefaultPlcDiscoveryRequest(@JsonProperty("discoverer") PlcDiscoverer discoverer) {
-        this.discoverer = discoverer;
+    public DefaultPlcBrowseResponse(@JsonProperty("request") PlcBrowseRequest request) {
+        this.request = request;
     }
 
     @Override
-    public CompletableFuture<? extends PlcDiscoveryResponse> execute() {
-        return discoverer.discover(this);
-    }
-
-    @Override
-    public CompletableFuture<? extends PlcDiscoveryResponse> executeWithHandler(PlcDiscoveryItemHandler handler) {
-        return discoverer.discoverWithHandler(this, handler);
+    public PlcBrowseRequest getRequest() {
+        return request;
     }
 
     @Override
     public void serialize(WriteBuffer writeBuffer) throws SerializationException {
-        writeBuffer.pushContext("PlcDiscoveryRequest");
-
-        // TODO: Implement
-
-        writeBuffer.popContext("PlcDiscoveryRequest");
-    }
-
-    public static class Builder implements PlcDiscoveryRequest.Builder {
-
-        private final PlcDiscoverer discoverer;
-
-        public Builder(PlcDiscoverer discoverer) {
-            this.discoverer = discoverer;
-        }
-
-        @Override
-        public PlcDiscoveryRequest build() {
-            return new DefaultPlcDiscoveryRequest(discoverer);
-        }
-
+        writeBuffer.pushContext("PlcBrowseResponse");
+        writeBuffer.popContext("PlcBrowseResponse");
     }
 
 }
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryItem.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryItem.java
index ab2292529..874d7b8ca 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryItem.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryItem.java
@@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
 import org.apache.plc4x.java.api.messages.PlcDiscoveryItem;
+import org.apache.plc4x.java.api.value.PlcValue;
 import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
@@ -39,7 +40,7 @@ public class DefaultPlcDiscoveryItem implements PlcDiscoveryItem, Serializable {
     private final Map<String, String> options;
     private final String name;
 
-    private final Map<String, String> attributes;
+    private final Map<String, PlcValue> attributes;
 
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
     public DefaultPlcDiscoveryItem(@JsonProperty("protocolCode") String protocolCode,
@@ -47,7 +48,7 @@ public class DefaultPlcDiscoveryItem implements PlcDiscoveryItem, Serializable {
                                    @JsonProperty("transportUrl") String transportUrl,
                                    @JsonProperty("options") Map<String, String> options,
                                    @JsonProperty("name") String name,
-                                   @JsonProperty("options") Map<String, String> attributes) {
+                                   @JsonProperty("options") Map<String, PlcValue> attributes) {
         this.protocolCode = protocolCode;
         this.transportCode = transportCode;
         this.transportUrl = transportUrl;
@@ -82,7 +83,7 @@ public class DefaultPlcDiscoveryItem implements PlcDiscoveryItem, Serializable {
     }
 
     @Override
-    public Map<String, String> getAttributes() {
+    public Map<String, PlcValue> getAttributes() {
         return attributes;
     }
 
diff --git a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
index 3b0cccd14..50cbe6ae2 100644
--- a/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/DefaultPlcDiscoveryRequest.java
@@ -20,11 +20,12 @@ package org.apache.plc4x.java.spi.messages;
 
 import com.fasterxml.jackson.annotation.*;
 import org.apache.plc4x.java.api.messages.*;
-import org.apache.plc4x.java.spi.generation.ParseException;
 import org.apache.plc4x.java.spi.generation.SerializationException;
 import org.apache.plc4x.java.spi.generation.WriteBuffer;
 import org.apache.plc4x.java.spi.utils.Serializable;
 
+import java.util.LinkedHashMap;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 
 @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "className")
@@ -32,16 +33,31 @@ public class DefaultPlcDiscoveryRequest implements PlcDiscoveryRequest, Serializ
 
     private final PlcDiscoverer discoverer;
 
+    private final LinkedHashMap<String, String> queries;
+
     @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
-    public DefaultPlcDiscoveryRequest(@JsonProperty("discoverer") PlcDiscoverer discoverer) {
+    public DefaultPlcDiscoveryRequest(@JsonProperty("discoverer") PlcDiscoverer discoverer,
+                                      @JsonProperty("queries") LinkedHashMap<String, String> queries) {
         this.discoverer = discoverer;
+        this.queries = queries;
     }
 
     @Override
+    @JsonIgnore
     public CompletableFuture<? extends PlcDiscoveryResponse> execute() {
         return discoverer.discover(this);
     }
 
+    @JsonIgnore
+    public PlcDiscoverer getDiscoverer() {
+        return discoverer;
+    }
+
+    @JsonIgnore
+    public Map<String, String> getQueries() {
+        return queries;
+    }
+
     @Override
     public CompletableFuture<? extends PlcDiscoveryResponse> executeWithHandler(PlcDiscoveryItemHandler handler) {
         return discoverer.discoverWithHandler(this, handler);
@@ -60,13 +76,22 @@ public class DefaultPlcDiscoveryRequest implements PlcDiscoveryRequest, Serializ
 
         private final PlcDiscoverer discoverer;
 
+        private final LinkedHashMap<String, String> queries;
+
         public Builder(PlcDiscoverer discoverer) {
             this.discoverer = discoverer;
+            queries = new LinkedHashMap<>();
+        }
+
+        @Override
+        public PlcDiscoveryRequest.Builder addQuery(String name, String query) {
+            queries.put(name, query);
+            return this;
         }
 
         @Override
         public PlcDiscoveryRequest build() {
-            return new DefaultPlcDiscoveryRequest(discoverer);
+            return new DefaultPlcDiscoveryRequest(discoverer, queries);
         }
 
     }
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/PlcBrowser.java
similarity index 58%
copy from plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
copy to plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/PlcBrowser.java
index 2e19ea89f..565f1b2fe 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
+++ b/plc4j/spi/src/main/java/org/apache/plc4x/java/spi/messages/PlcBrowser.java
@@ -16,21 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.java.api.messages;
+package org.apache.plc4x.java.spi.messages;
 
-import java.util.concurrent.CompletableFuture;
-
-public interface PlcBrowseRequest extends PlcRequest {
-
-    CompletableFuture<? extends PlcBrowseResponse> execute();
-
-    interface Builder extends PlcRequestBuilder {
+import org.apache.plc4x.java.api.messages.PlcBrowseRequest;
+import org.apache.plc4x.java.api.messages.PlcBrowseResponse;
 
-        @Override
-        PlcBrowseRequest build();
-
-        PlcReadRequest.Builder addItem(String name, String fieldQuery);
+import java.util.concurrent.CompletableFuture;
 
-    }
+/**
+ * Interface implemented by all PlcConnections that are able to browse remote resources.
+ */
+public interface PlcBrowser {
+
+    /**
+     * Reads a requested value from a PLC.
+     *
+     * @param browseRequest object describing the type and location of the value.
+     * @return a {@link CompletableFuture} giving async access to the returned value.
+     */
+    CompletableFuture<PlcBrowseResponse> browse(PlcBrowseRequest browseRequest);
 
 }
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java b/plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedBrowseRequest.java
similarity index 55%
copy from plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
copy to plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedBrowseRequest.java
index 2e19ea89f..2952b395d 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
+++ b/plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedBrowseRequest.java
@@ -16,21 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.java.api.messages;
+package org.apache.plc4x.java.utils.connectionpool2;
 
-import java.util.concurrent.CompletableFuture;
-
-public interface PlcBrowseRequest extends PlcRequest {
+import org.apache.plc4x.java.api.messages.PlcBrowseRequest;
+import org.apache.plc4x.java.api.messages.PlcBrowseResponse;
 
-    CompletableFuture<? extends PlcBrowseResponse> execute();
+import java.util.concurrent.CompletableFuture;
 
-    interface Builder extends PlcRequestBuilder {
+public class CachedBrowseRequest implements PlcBrowseRequest {
 
-        @Override
-        PlcBrowseRequest build();
+    private final CachedPlcConnection parent;
+    private final PlcBrowseRequest innerRequest;
 
-        PlcReadRequest.Builder addItem(String name, String fieldQuery);
+    public CachedBrowseRequest(CachedPlcConnection parent, PlcBrowseRequest innerRequest) {
+        this.parent = parent;
+        this.innerRequest = innerRequest;
+    }
 
+    @Override
+    public CompletableFuture<? extends PlcBrowseResponse> execute() {
+        // Only allowed if connection is still active
+        return parent.execute(innerRequest);
     }
 
 }
diff --git a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java b/plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedBrowseRequestBuilder.java
similarity index 53%
copy from plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
copy to plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedBrowseRequestBuilder.java
index 2e19ea89f..ff0cfc509 100644
--- a/plc4j/api/src/main/java/org/apache/plc4x/java/api/messages/PlcBrowseRequest.java
+++ b/plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedBrowseRequestBuilder.java
@@ -16,21 +16,28 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.plc4x.java.api.messages;
+package org.apache.plc4x.java.utils.connectionpool2;
 
-import java.util.concurrent.CompletableFuture;
+import org.apache.plc4x.java.api.messages.PlcBrowseRequest;
 
-public interface PlcBrowseRequest extends PlcRequest {
+public class CachedBrowseRequestBuilder implements PlcBrowseRequest.Builder {
 
-    CompletableFuture<? extends PlcBrowseResponse> execute();
+    private final CachedPlcConnection parent;
+    private final PlcBrowseRequest.Builder builder;
 
-    interface Builder extends PlcRequestBuilder {
-
-        @Override
-        PlcBrowseRequest build();
+    public CachedBrowseRequestBuilder(CachedPlcConnection parent, PlcBrowseRequest.Builder builder) {
+        this.parent = parent;
+        this.builder = builder;
+    }
 
-        PlcReadRequest.Builder addItem(String name, String fieldQuery);
+    @Override
+    public PlcBrowseRequest.Builder addQuery(String name, String query) {
+        return builder.addQuery(name, query);
+    }
 
+    @Override
+    public PlcBrowseRequest build() {
+        return new CachedBrowseRequest(parent, builder.build());
     }
 
 }
diff --git a/plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnection.java b/plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnection.java
index d05801216..462fc3358 100644
--- a/plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnection.java
+++ b/plc4j/tools/connection-cache/src/main/java/org/apache/plc4x/java/utils/connectionpool2/CachedPlcConnection.java
@@ -68,6 +68,34 @@ public class CachedPlcConnection implements PlcConnection, PlcConnectionMetadata
         }
     }
 
+    private CompletableFuture<? extends PlcBrowseResponse> wrapBrowseWithTimeout(CompletableFuture<? extends PlcBrowseResponse> future, long timeoutMillis) {
+        //schedule watcher
+        final CompletableFuture<PlcBrowseResponse> responseFuture = new CompletableFuture<>();
+        schedulerExecutor.schedule(() -> {
+            if (!future.isDone()) {
+                logger.debug("Timing out the PLC request!");
+                future.cancel(true);
+                responseFuture.completeExceptionally(new TimeoutException("Response did not finish in Time!"));
+            } else {
+                logger.trace("Unnecessary to cancel the request!");
+            }
+        }, timeoutMillis, TimeUnit.MILLISECONDS);
+        future.handle(new BiFunction<PlcBrowseResponse, Throwable, Object>() {
+            @Override
+            public Object apply(PlcBrowseResponse plcBrowseResponse, Throwable throwable) {
+                if (plcBrowseResponse != null) {
+                    logger.debug("Request finsihed successfull!");
+                    responseFuture.complete(plcBrowseResponse);
+                } else {
+                    logger.debug("Request failed", throwable);
+                    responseFuture.completeExceptionally(throwable);
+                }
+                return null;
+            }
+        });
+        return responseFuture;
+    }
+
     private CompletableFuture<? extends PlcReadResponse> wrapReadWithTimeout(CompletableFuture<? extends PlcReadResponse> future, long timeoutMillis) {
         //schedule watcher
         final CompletableFuture<PlcReadResponse> responseFuture = new CompletableFuture<>();
@@ -124,6 +152,34 @@ public class CachedPlcConnection implements PlcConnection, PlcConnectionMetadata
         return responseFuture;
     }
 
+    public CompletableFuture<? extends PlcBrowseResponse> execute(PlcBrowseRequest request) {
+        logger.trace("Trying to executing Request {}", request);
+        if (closed) {
+            throw new IllegalStateException("Trying to execute a Request on a closed Connection!");
+        }
+        try {
+            logger.trace("Executing Request {}", request);
+            final CompletableFuture<? extends PlcBrowseResponse> responseFuture = wrapBrowseWithTimeout(request.execute(), 5_000);
+            // The following code handles the case, that a read fails (which is handled async and thus not really connected
+            // to the connection, yet
+            // Thus, we register our own listener who gets the notification and reports the connection as broken
+            final CompletableFuture<PlcBrowseResponse> handledResponseFuture = responseFuture.handleAsync(new BiFunction<PlcBrowseResponse, Throwable, PlcBrowseResponse>() {
+                @Override
+                public PlcBrowseResponse apply(PlcBrowseResponse plcBrowseResponse, Throwable throwable) {
+                    if (throwable != null) {
+                        // Do something here...
+                        logger.warn("Request finished with exception. Reporting Connection as Broken", throwable);
+                        closeConnectionExceptionally(null);
+                    }
+                    return plcBrowseResponse;
+                }
+            });
+            return handledResponseFuture;
+        } catch (Exception e) {
+            return (CompletableFuture<? extends PlcBrowseResponse>) closeConnectionExceptionally(e);
+        }
+    }
+
     /**
      * Executes the Request.
      */
@@ -227,6 +283,14 @@ public class CachedPlcConnection implements PlcConnection, PlcConnectionMetadata
         return future;
     }
 
+    @Override
+    public PlcBrowseRequest.Builder browseRequestBuilder() {
+        if (closed) {
+            throw new IllegalStateException("Trying to build a Request on a closed Connection!");
+        }
+        return new CachedBrowseRequestBuilder(this, this.getActiveConnection().browseRequestBuilder());
+    }
+
     @Override
     public PlcReadRequest.Builder readRequestBuilder() {
         if (closed) {
@@ -253,6 +317,15 @@ public class CachedPlcConnection implements PlcConnection, PlcConnectionMetadata
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public boolean canBrowse() {
+        if (closed) {
+            return false;
+        } else {
+            return this.activeConnection.getMetadata().canBrowse();
+        }
+    }
+
     @Override
     public boolean canRead() {
         if (closed) {
diff --git a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java b/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java
index 8f87216fd..499edf1c6 100644
--- a/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java
+++ b/plc4j/tools/connection-pool/src/test/java/org/apache/plc4x/java/utils/connectionpool/PooledPlcDriverManagerTest.java
@@ -26,10 +26,7 @@ import org.apache.plc4x.java.api.authentication.PlcAuthentication;
 import org.apache.plc4x.java.api.authentication.PlcUsernamePasswordAuthentication;
 import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
 import org.apache.plc4x.java.api.exceptions.PlcUnsupportedOperationException;
-import org.apache.plc4x.java.api.messages.PlcReadRequest;
-import org.apache.plc4x.java.api.messages.PlcSubscriptionRequest;
-import org.apache.plc4x.java.api.messages.PlcUnsubscriptionRequest;
-import org.apache.plc4x.java.api.messages.PlcWriteRequest;
+import org.apache.plc4x.java.api.messages.*;
 import org.apache.plc4x.java.api.metadata.PlcConnectionMetadata;
 import org.apache.plc4x.java.api.PlcDriver;
 import org.assertj.core.api.WithAssertions;
@@ -331,6 +328,11 @@ class PooledPlcDriverManagerTest implements WithAssertions {
             return false;
         }
 
+        @Override
+        public boolean canBrowse() {
+            return false;
+        }
+
         @Override
         public void close() {
             connected = false;
@@ -356,6 +358,11 @@ class PooledPlcDriverManagerTest implements WithAssertions {
             throw new PlcUnsupportedOperationException("The connection does not support subscription");
         }
 
+        @Override
+        public PlcBrowseRequest.Builder browseRequestBuilder() {
+            throw new PlcUnsupportedOperationException("The connection does not support browsing");
+        }
+
         @Override
         public String toString() {
             return "DummyPlcConnection{" +
diff --git a/plc4j/tools/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerComplexTest.java b/plc4j/tools/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerComplexTest.java
index e7b5f86af..d039f16aa 100644
--- a/plc4j/tools/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerComplexTest.java
+++ b/plc4j/tools/opm/src/test/java/org/apache/plc4x/java/opm/PlcEntityManagerComplexTest.java
@@ -217,6 +217,12 @@ public class PlcEntityManagerComplexTest implements WithAssertions {
             public boolean canSubscribe() {
                 return true;
             }
+
+            @Override
+            public boolean canBrowse() {
+                return true;
+            }
+
         });
 
         PlcReader reader = readRequest -> {
diff --git a/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs b/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs
index 2f99ff138..bd0a28d2e 100644
--- a/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs
+++ b/plc4net/drivers/knxnetip/src/drivers/knxnetip/readwrite/model/KnxManufacturer.cs
@@ -618,8 +618,9 @@ namespace org.apache.plc4net.drivers.knxnetip.readwrite.model
         M_GORDIC = 591,
         M_DELTA_ELECTRONICS = 592,
         M_SHANGHAI_LEWIN_INTELLIGENT_TECHNOLOGY_CO__LTD_ = 593,
-        M_ABB___RESERVED = 594,
-        M_BUSCH_JAEGER_ELEKTRO___RESERVED = 595,
+        M_KG_POWER = 594,
+        M_ABB___RESERVED = 595,
+        M_BUSCH_JAEGER_ELEKTRO___RESERVED = 596,
     }
 
     public static class KnxManufacturerInfo
@@ -2279,10 +2280,13 @@ namespace org.apache.plc4net.drivers.knxnetip.readwrite.model
                 case KnxManufacturer.M_SHANGHAI_LEWIN_INTELLIGENT_TECHNOLOGY_CO__LTD_: { /* '593' */
                     return 651;
                 }
-                case KnxManufacturer.M_ABB___RESERVED: { /* '594' */
+                case KnxManufacturer.M_KG_POWER: { /* '594' */
+                    return 652;
+                }
+                case KnxManufacturer.M_ABB___RESERVED: { /* '595' */
                     return 43954;
                 }
-                case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '595' */
+                case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '596' */
                     return 43959;
                 }
                 case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO: { /* '6' */
@@ -4077,10 +4081,13 @@ namespace org.apache.plc4net.drivers.knxnetip.readwrite.model
                 case KnxManufacturer.M_SHANGHAI_LEWIN_INTELLIGENT_TECHNOLOGY_CO__LTD_: { /* '593' */
                     return "Shanghai Lewin Intelligent Technology Co.,Ltd.";
                 }
-                case KnxManufacturer.M_ABB___RESERVED: { /* '594' */
+                case KnxManufacturer.M_KG_POWER: { /* '594' */
+                    return "KG-POWER";
+                }
+                case KnxManufacturer.M_ABB___RESERVED: { /* '595' */
                     return "ABB - reserved";
                 }
-                case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '595' */
+                case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO___RESERVED: { /* '596' */
                     return "Busch-Jaeger Elektro - reserved";
                 }
                 case KnxManufacturer.M_BUSCH_JAEGER_ELEKTRO: { /* '6' */
diff --git a/sandbox/discovery/src/main/java/org/apache/plc4x/java/discovery/DiscoveryConnection.java b/sandbox/discovery/src/main/java/org/apache/plc4x/java/discovery/DiscoveryConnection.java
index bf0722515..f886606cf 100644
--- a/sandbox/discovery/src/main/java/org/apache/plc4x/java/discovery/DiscoveryConnection.java
+++ b/sandbox/discovery/src/main/java/org/apache/plc4x/java/discovery/DiscoveryConnection.java
@@ -29,9 +29,7 @@ import org.apache.plc4x.java.spi.discovery.ActiveDiscovery;
 import org.apache.plc4x.java.spi.discovery.BroadcastDiscovery;
 import org.apache.plc4x.java.spi.discovery.SupportsDiscovery;
 import org.apache.plc4x.java.spi.discovery.PassiveDiscovery;
-import org.apache.plc4x.java.spi.messages.DefaultPlcSubscriptionRequest;
-import org.apache.plc4x.java.spi.messages.DefaultPlcUnsubscriptionRequest;
-import org.apache.plc4x.java.spi.messages.PlcSubscriber;
+import org.apache.plc4x.java.spi.messages.*;
 
 import java.util.Collection;
 import java.util.HashMap;
@@ -40,7 +38,7 @@ import java.util.ServiceLoader;
 import java.util.concurrent.CompletableFuture;
 import java.util.function.Consumer;
 
-public class DiscoveryConnection implements PlcConnection, PlcSubscriber {
+public class DiscoveryConnection implements PlcConnection, PlcSubscriber, PlcBrowser {
 
     private boolean connected = false;
     private Map<String, ActiveDiscovery> activeDiscovery;
@@ -110,6 +108,11 @@ public class DiscoveryConnection implements PlcConnection, PlcSubscriber {
             public boolean canSubscribe() {
                 return true;
             }
+
+            @Override
+            public boolean canBrowse() {
+                return true;
+            }
         };
     }
 
@@ -140,6 +143,11 @@ public class DiscoveryConnection implements PlcConnection, PlcSubscriber {
         return new DefaultPlcUnsubscriptionRequest.Builder(this);
     }
 
+    @Override
+    public PlcBrowseRequest.Builder browseRequestBuilder() {
+        return new DefaultPlcBrowseRequest.Builder(this);
+    }
+
     @Override
     public CompletableFuture<PlcSubscriptionResponse> subscribe(PlcSubscriptionRequest subscriptionRequest) {
         // TODO: Implement ...
@@ -163,4 +171,10 @@ public class DiscoveryConnection implements PlcConnection, PlcSubscriber {
         // TODO: Implement ...
     }
 
+    @Override
+    public CompletableFuture<PlcBrowseResponse> browse(PlcBrowseRequest browseRequest) {
+        // TODO: Implement ...
+        return null;
+    }
+
 }