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

sruehl pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git

commit 47f2add3ef9f320bc79ede844902ce507f084e45
Author: Sebastian Rühl <[email protected]>
AuthorDate: Tue May 19 16:34:25 2026 +0200

    refactor(plc4go): internalize external dependencies
---
 plc4go/go.mod                                      |   6 -
 plc4go/go.sum                                      |  17 --
 plc4go/internal/bacnetip/Discoverer.go             |   6 +-
 plc4go/pkg/api/cache/PlcConnectionCache.go         |   5 +-
 plc4go/pkg/api/cache/PlcConnectionCache_test.go    |  20 +-
 plc4go/pkg/api/cache/connectionContainer.go        |   6 +-
 plc4go/pkg/api/cache/connectionContainer_test.go   |  22 +-
 plc4go/pkg/api/cache/plcConnectionLease_test.go    |  30 +--
 .../protocols/df1/readwrite/model/StaticHelper.go  |  33 ++-
 plc4go/spi/codegen/fields/FieldReaderVirtual.go    |  39 ++--
 plc4go/spi/codegen/fields/virtualConv.go           | 171 ++++++++++++++
 plc4go/spi/codegen/fields/virtualConv_test.go      | 247 +++++++++++++++++++++
 plc4go/spi/testutils/DriverTestRunner.go           |   2 +-
 plc4go/spi/testutils/ParserSerializerTestRunner.go |   2 +-
 plc4go/spi/testutils/xmldom/xmldom.go              | 220 ++++++++++++++++++
 plc4go/spi/testutils/xmldom/xmldom_test.go         | 183 +++++++++++++++
 16 files changed, 908 insertions(+), 101 deletions(-)

diff --git a/plc4go/go.mod b/plc4go/go.mod
index cf76051e76..15d1e10e1a 100644
--- a/plc4go/go.mod
+++ b/plc4go/go.mod
@@ -22,9 +22,7 @@ module github.com/apache/plc4x/plc4go
 go 1.26
 
 require (
-       github.com/IBM/netaddr v1.5.0
        github.com/ajankovic/xdiff v0.0.1
-       github.com/cstockton/go-conv v1.0.0
        github.com/fatih/color v1.19.0
        github.com/google/uuid v1.6.0
        github.com/gopacket/gopacket v1.5.0
@@ -32,17 +30,13 @@ require (
        github.com/libp2p/go-reuseport v0.4.0
        github.com/pkg/errors v0.9.1
        github.com/rs/zerolog v1.35.1
-       github.com/snksoft/crc v1.1.0
        github.com/stretchr/testify v1.11.1
-       github.com/subchen/go-xmldom v1.1.2
-       github.com/viney-shih/go-lock v1.1.2
        golang.org/x/net v0.54.0
        golang.org/x/text v0.37.0
        golang.org/x/tools v0.45.0
 )
 
 require (
-       github.com/antchfx/xpath v1.3.5 // indirect
        github.com/bitfield/gotestdox v0.2.2 // indirect
        github.com/brunoga/deep v1.3.1 // indirect
        github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
diff --git a/plc4go/go.sum b/plc4go/go.sum
index 882a34104d..b6a345fda8 100644
--- a/plc4go/go.sum
+++ b/plc4go/go.sum
@@ -1,10 +1,5 @@
-github.com/IBM/netaddr v1.5.0 h1:IJlFZe1+nFs09TeMB/HOP4+xBnX2iM/xgiDOgZgTJq0=
-github.com/IBM/netaddr v1.5.0/go.mod 
h1:DDBPeYgbFzoXHjSz9Jwk7K8wmWV4+a/Kv0LqRnb8we4=
 github.com/ajankovic/xdiff v0.0.1 
h1:V1cj8t5xwYzm6ZGPqPOlAc9AIajXuTEn41D/1MJBWMM=
 github.com/ajankovic/xdiff v0.0.1/go.mod 
h1:SUmEZ67uB97I0zkiuQ+lb+LOms9ipn8X+p+2RdJV710=
-github.com/antchfx/xpath v0.0.0-20170515025933-1f3266e77307/go.mod 
h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
-github.com/antchfx/xpath v1.3.5 h1:PqbXLC3TkfeZyakF5eeh3NTWEbYl4VHNVeufANzDbKQ=
-github.com/antchfx/xpath v1.3.5/go.mod 
h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
 github.com/bitfield/gotestdox v0.2.2 
h1:x6RcPAbBbErKLnapz1QeAlf3ospg8efBsedU93CDsnE=
 github.com/bitfield/gotestdox v0.2.2/go.mod 
h1:D+gwtS0urjBrzguAkTM2wodsTQYFHdpx8eqRJ3N+9pY=
 github.com/brunoga/deep v1.3.1 h1:bSrL6FhAZa6JlVv4vsi7Hg8SLwroDb1kgDERRVipBCo=
@@ -12,8 +7,6 @@ github.com/brunoga/deep v1.3.1/go.mod 
h1:GDV6dnXqn80ezsLSZ5Wlv1PdKAWAO4L5PnKYtv2
 github.com/clipperhouse/uax29/v2 v2.6.0 
h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
 github.com/clipperhouse/uax29/v2 v2.6.0/go.mod 
h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod 
h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
-github.com/cstockton/go-conv v1.0.0 
h1:zj/q/0MpQ/97XfiC9glWiohO8lhgR4TTnHYZifLTv6I=
-github.com/cstockton/go-conv v1.0.0/go.mod 
h1:HuiHkkRgOA0IoBNPC7ysG7kNpjDYlgM7Kj62yQPxjy4=
 github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc 
h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -85,8 +78,6 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod 
h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
 github.com/rs/zerolog v1.35.1 h1:m7xQeoiLIiV0BCEY4Hs+j2NG4Gp2o2KPKmhnnLiazKI=
 github.com/rs/zerolog v1.35.1/go.mod 
h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod 
h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/snksoft/crc v1.1.0 h1:HkLdI4taFlgGGG1KvsWMpz78PkOC9TkPVpTV/cuWn48=
-github.com/snksoft/crc v1.1.0/go.mod 
h1:5/gUOsgAm7OmIhb6WJzw7w5g2zfJi4FrHYgGPdshE+A=
 github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
 github.com/spf13/cobra v1.10.2/go.mod 
h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
 github.com/spf13/pflag v1.0.9/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -95,17 +86,11 @@ github.com/spf13/pflag v1.0.10/go.mod 
h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A
 github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
 github.com/stretchr/objx v0.5.3/go.mod 
h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
-github.com/stretchr/testify v1.2.1/go.mod 
h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod 
h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.5.1/go.mod 
h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.11.1 
h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
 github.com/stretchr/testify v1.11.1/go.mod 
h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
-github.com/subchen/go-xmldom v1.1.2 
h1:7evI2YqfYYOnuj+PBwyaOZZYjl3iWq35P6KfBUw9jeU=
-github.com/subchen/go-xmldom v1.1.2/go.mod 
h1:6Pg/HuX5/T4Jlj0IPJF1sRxKVoI/rrKP6LIMge9d5/8=
 github.com/vektra/mockery/v3 v3.6.3 
h1:YaBokm33LG0E8w1TccCkcJr/6tRf+zSYiHS8ct4ttCc=
 github.com/vektra/mockery/v3 v3.6.3/go.mod 
h1:kh58mpWIghfUCfFkNCP72TJmuJUR/dKnrqBZ3HspVq8=
-github.com/viney-shih/go-lock v1.1.2 
h1:3TdGTiHZCPqBdTvFbQZQN/TRZzKF3KWw2rFEyKz3YqA=
-github.com/viney-shih/go-lock v1.1.2/go.mod 
h1:Yijm78Ljteb3kRiJrbLAxVntkUukGu5uzSxq/xV7OO8=
 github.com/vishvananda/netlink v1.1.0 
h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
 github.com/vishvananda/netlink v1.1.0/go.mod 
h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
 github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 
h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
@@ -125,7 +110,6 @@ golang.org/x/mod v0.36.0 
h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
 golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
 golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
 golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
 golang.org/x/sync v0.20.0/go.mod 
h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -140,7 +124,6 @@ golang.org/x/tools v0.45.0/go.mod 
h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c 
h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod 
h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools/gotestsum v1.13.0 h1:+Lh454O9mu9AMG1APV4o0y7oDYKyik/3kBOiCqiEpRo=
diff --git a/plc4go/internal/bacnetip/Discoverer.go 
b/plc4go/internal/bacnetip/Discoverer.go
index b96f61312f..6052b3072d 100644
--- a/plc4go/internal/bacnetip/Discoverer.go
+++ b/plc4go/internal/bacnetip/Discoverer.go
@@ -29,7 +29,6 @@ import (
        "sync"
        "time"
 
-       "github.com/IBM/netaddr"
        "github.com/libp2p/go-reuseport"
        "github.com/pkg/errors"
        "github.com/rs/zerolog"
@@ -367,7 +366,10 @@ func (d *Discoverer) buildupCommunicationChannels(ctx 
context.Context, interface
                        }
 
                        _, cidr, _ := net.ParseCIDR(unicastAddress.String())
-                       broadcastAddr := netaddr.BroadcastAddr(cidr)
+                       broadcastAddr := make(net.IP, len(cidr.IP))
+                       for i := range broadcastAddr {
+                               broadcastAddr[i] = cidr.IP[i] | ^cidr.Mask[i]
+                       }
                        // Handle undirected
                        broadcastConnection, err := 
reuseport.ListenPacket("udp4", fmt.Sprintf("%v:%d", broadcastAddr, bacNetPort))
                        if err != nil {
diff --git a/plc4go/pkg/api/cache/PlcConnectionCache.go 
b/plc4go/pkg/api/cache/PlcConnectionCache.go
index 4ff7a58442..eed96f8339 100644
--- a/plc4go/pkg/api/cache/PlcConnectionCache.go
+++ b/plc4go/pkg/api/cache/PlcConnectionCache.go
@@ -28,7 +28,6 @@ import (
 
        "github.com/pkg/errors"
        "github.com/rs/zerolog"
-       "github.com/viney-shih/go-lock"
 
        "github.com/apache/plc4x/plc4go/pkg/api"
        "github.com/apache/plc4x/plc4go/pkg/api/config"
@@ -52,7 +51,7 @@ func NewPlcConnectionCache(driverManager 
plc4go.PlcDriverManager, withConnection
                driverManager: driverManager,
                maxLeaseTime:  maxLeaseTime,
                maxWaitTime:   maxLeaseTime * 5,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
                log:           log,
@@ -109,7 +108,7 @@ type plcConnectionCache struct {
        maxLeaseTime time.Duration
        maxWaitTime  time.Duration
 
-       cacheLock   lock.RWMutex
+       cacheLock   *sync.RWMutex
        connections map[string]*connectionContainer
        tracer      tracer.Tracer
 
diff --git a/plc4go/pkg/api/cache/PlcConnectionCache_test.go 
b/plc4go/pkg/api/cache/PlcConnectionCache_test.go
index fdb8bb227b..e3031bf280 100644
--- a/plc4go/pkg/api/cache/PlcConnectionCache_test.go
+++ b/plc4go/pkg/api/cache/PlcConnectionCache_test.go
@@ -23,12 +23,12 @@ import (
        "context"
        "fmt"
        "strings"
+       "sync"
        "testing"
        "time"
 
        "github.com/stretchr/testify/assert"
        "github.com/stretchr/testify/require"
-       "github.com/viney-shih/go-lock"
 
        "github.com/apache/plc4x/plc4go/internal/simulated"
        "github.com/apache/plc4x/plc4go/pkg/api"
@@ -293,7 +293,7 @@ func TestPlcConnectionCache_ReusingAnExistingConnection(t 
*testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  5 * time.Second,
                maxWaitTime:   25 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -374,7 +374,7 @@ func 
TestPlcConnectionCache_MultipleConcurrentConnectionRequests(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  5 * time.Second,
                maxWaitTime:   25 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -385,8 +385,8 @@ func 
TestPlcConnectionCache_MultipleConcurrentConnectionRequests(t *testing.T) {
                t.Errorf("Expected %d connections in the cache but got %d", 0, 
len(cache.connections))
        }
 
-       floodGate := lock.NewCASMutex() // floodgate is use because we want 
both get connection to get executed in short order
-       floodGate.Lock()                // We use a cas mutex write lock to 
lock the floodgate
+       floodGate := &sync.RWMutex{} // floodgate is use because we want both 
get connection to get executed in short order
+       floodGate.Lock()             // We use a cas mutex write lock to lock 
the floodgate
 
        // Read once from the cache.
        firstRun := executeAndTestReadFromPlc(
@@ -473,7 +473,7 @@ func TestPlcConnectionCache_ConnectWithError(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  5 * time.Second,
                maxWaitTime:   25 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -509,7 +509,7 @@ func 
TestPlcConnectionCache_ReturningConnectionWithPingError(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  5 * time.Second,
                maxWaitTime:   25 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -557,7 +557,7 @@ func TestPlcConnectionCache_PingTimeout(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  5 * time.Second,
                maxWaitTime:   25 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -611,7 +611,7 @@ func 
TestPlcConnectionCache_SecondCallGetNewConnectionAfterPingTimeout(t *testin
                driverManager: driverManager,
                maxLeaseTime:  5 * time.Second,
                maxWaitTime:   25 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -707,7 +707,7 @@ func TestPlcConnectionCache_MaximumWaitTimeReached(t 
*testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
diff --git a/plc4go/pkg/api/cache/connectionContainer.go 
b/plc4go/pkg/api/cache/connectionContainer.go
index 20776b5742..1c343d7811 100644
--- a/plc4go/pkg/api/cache/connectionContainer.go
+++ b/plc4go/pkg/api/cache/connectionContainer.go
@@ -22,16 +22,16 @@ package cache
 import (
        "context"
        "fmt"
+       "sync"
 
        "github.com/pkg/errors"
        "github.com/rs/zerolog"
-       "github.com/viney-shih/go-lock"
 
        plc4go "github.com/apache/plc4x/plc4go/pkg/api"
 )
 
 type connectionContainer struct {
-       lock             lock.RWMutex
+       lock             *sync.RWMutex
        connectionString string
        driverManager    plc4go.PlcDriverManager
        tracerEnabled    bool
@@ -58,7 +58,7 @@ func newConnectionContainer(log zerolog.Logger, driverManager 
plc4go.PlcDriverMa
        return &connectionContainer{
                driverManager:    driverManager,
                connectionString: connectionString,
-               lock:             lock.NewCASMutex(),
+               lock:             &sync.RWMutex{},
                leaseCounter:     0,
                closed:           false,
                state:            StateInitialized,
diff --git a/plc4go/pkg/api/cache/connectionContainer_test.go 
b/plc4go/pkg/api/cache/connectionContainer_test.go
index 451b709295..a3a74869fd 100644
--- a/plc4go/pkg/api/cache/connectionContainer_test.go
+++ b/plc4go/pkg/api/cache/connectionContainer_test.go
@@ -22,10 +22,10 @@ package cache
 import (
        "context"
        "fmt"
+       "sync"
        "testing"
 
        "github.com/stretchr/testify/assert"
-       "github.com/viney-shih/go-lock"
 
        "github.com/apache/plc4x/plc4go/internal/simulated"
        plc4go "github.com/apache/plc4x/plc4go/pkg/api"
@@ -36,7 +36,7 @@ import (
 
 func Test_connectionContainer_String(t1 *testing.T) {
        type fields struct {
-               lock             lock.RWMutex
+               lock             *sync.RWMutex
                connectionString string
                driverManager    plc4go.PlcDriverManager
                tracerEnabled    bool
@@ -83,7 +83,7 @@ func Test_connectionContainer_String(t1 *testing.T) {
 
 func Test_connectionContainer_addListener(t1 *testing.T) {
        type fields struct {
-               lock             lock.RWMutex
+               lock             *sync.RWMutex
                connectionString string
                driverManager    plc4go.PlcDriverManager
                tracerEnabled    bool
@@ -105,7 +105,7 @@ func Test_connectionContainer_addListener(t1 *testing.T) {
                {
                        name: "add it",
                        fields: fields{
-                               lock: lock.NewCASMutex(),
+                               lock: &sync.RWMutex{},
                        },
                },
        }
@@ -131,7 +131,7 @@ func Test_connectionContainer_addListener(t1 *testing.T) {
 
 func Test_connectionContainer_connect(t1 *testing.T) {
        type fields struct {
-               lock             lock.RWMutex
+               lock             *sync.RWMutex
                connectionString string
                driverManager    plc4go.PlcDriverManager
                tracerEnabled    bool
@@ -151,7 +151,7 @@ func Test_connectionContainer_connect(t1 *testing.T) {
                        name: "connect fresh",
                        fields: fields{
                                connectionString: "simulated://1.2.3.4:42",
-                               lock:             lock.NewCASMutex(),
+                               lock:             &sync.RWMutex{},
                        },
                        setup: func(t *testing.T, fields *fields) {
                                logger := testutils.ProduceTestingLogger(t)
@@ -190,7 +190,7 @@ func Test_connectionContainer_connect(t1 *testing.T) {
 
 func Test_connectionContainer_lease(t1 *testing.T) {
        type fields struct {
-               lock             lock.RWMutex
+               lock             *sync.RWMutex
                connectionString string
                driverManager    plc4go.PlcDriverManager
                tracerEnabled    bool
@@ -214,7 +214,7 @@ func Test_connectionContainer_lease(t1 *testing.T) {
                        name: "lease fresh",
                        fields: fields{
                                connectionString: "simulated://1.2.3.4:42",
-                               lock:             lock.NewCASMutex(),
+                               lock:             &sync.RWMutex{},
                        },
                        args: args{
                                ctx: t1.Context(),
@@ -258,7 +258,7 @@ func Test_connectionContainer_lease(t1 *testing.T) {
 
 func Test_connectionContainer_returnConnection(t1 *testing.T) {
        type fields struct {
-               lock             lock.RWMutex
+               lock             *sync.RWMutex
                connectionString string
                driverManager    plc4go.PlcDriverManager
                tracerEnabled    bool
@@ -283,7 +283,7 @@ func Test_connectionContainer_returnConnection(t1 
*testing.T) {
                        name: "return connection fresh",
                        fields: fields{
                                connectionString: "simulated://1.2.3.4:42",
-                               lock:             lock.NewCASMutex(),
+                               lock:             &sync.RWMutex{},
                        },
                        args: args{
                                state: StateInitialized,
@@ -304,7 +304,7 @@ func Test_connectionContainer_returnConnection(t1 
*testing.T) {
                        name: "return unconnected connection",
                        fields: fields{
                                connectionString: "simulated://1.2.3.4:42",
-                               lock:             lock.NewCASMutex(),
+                               lock:             &sync.RWMutex{},
                        },
                        args: args{
                                state: StateInUse,
diff --git a/plc4go/pkg/api/cache/plcConnectionLease_test.go 
b/plc4go/pkg/api/cache/plcConnectionLease_test.go
index 49904879ee..8f01210133 100644
--- a/plc4go/pkg/api/cache/plcConnectionLease_test.go
+++ b/plc4go/pkg/api/cache/plcConnectionLease_test.go
@@ -20,11 +20,11 @@
 package cache
 
 import (
+       "sync"
        "testing"
        "time"
 
        "github.com/stretchr/testify/assert"
-       "github.com/viney-shih/go-lock"
 
        "github.com/apache/plc4x/plc4go/internal/simulated"
        plc4go "github.com/apache/plc4x/plc4go/pkg/api"
@@ -45,7 +45,7 @@ func TestLeasedPlcConnection_IsTraceEnabled(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -79,7 +79,7 @@ func TestLeasedPlcConnection_GetTracer(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -108,7 +108,7 @@ func TestLeasedPlcConnection_GetConnectionId(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -135,7 +135,7 @@ func TestLeasedPlcConnection_Connect(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -162,7 +162,7 @@ func TestLeasedPlcConnection_BlockingClose(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -191,7 +191,7 @@ func TestLeasedPlcConnection_Close(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -220,7 +220,7 @@ func TestLeasedPlcConnection_IsConnected(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -251,7 +251,7 @@ func TestLeasedPlcConnection_Ping(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -289,7 +289,7 @@ func TestLeasedPlcConnection_GetMetadata(t *testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -333,7 +333,7 @@ func TestLeasedPlcConnection_ReadRequestBuilder(t 
*testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -374,7 +374,7 @@ func TestLeasedPlcConnection_WriteRequestBuilder(t 
*testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -415,7 +415,7 @@ func TestLeasedPlcConnection_SubscriptionRequestBuilder(t 
*testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -456,7 +456,7 @@ func TestLeasedPlcConnection_UnsubscriptionRequestBuilder(t 
*testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
@@ -505,7 +505,7 @@ func TestLeasedPlcConnection_BrowseRequestBuilder(t 
*testing.T) {
                driverManager: driverManager,
                maxLeaseTime:  1 * time.Second,
                maxWaitTime:   5 * time.Second,
-               cacheLock:     lock.NewCASMutex(),
+               cacheLock:     &sync.RWMutex{},
                connections:   make(map[string]*connectionContainer),
                tracer:        nil,
        }
diff --git a/plc4go/protocols/df1/readwrite/model/StaticHelper.go 
b/plc4go/protocols/df1/readwrite/model/StaticHelper.go
index 12a6c01360..af0807227d 100644
--- a/plc4go/protocols/df1/readwrite/model/StaticHelper.go
+++ b/plc4go/protocols/df1/readwrite/model/StaticHelper.go
@@ -22,29 +22,38 @@ package model
 import (
        "context"
 
-       "github.com/snksoft/crc"
-
        "github.com/apache/plc4x/plc4go/spi/utils"
 )
 
-var table *crc.Table
-
-func init() {
-       // CRC-16/DF-1
-       table = crc.NewTable(&crc.Parameters{Width: 16, Polynomial: 0x8005, 
Init: 0x0000, ReflectIn: true, ReflectOut: true, FinalXor: 0x0000})
+// crc16df1Update accumulates CRC-16/DF-1 (CRC-16/ARC variant) over data.
+// Parameters: poly=0x8005, init=0x0000, reflectIn=true, reflectOut=true, 
finalXor=0.
+// The reflected polynomial is 0xA001.
+func crc16df1Update(crc uint16, data []byte) uint16 {
+       const refPoly uint16 = 0xA001
+       for _, b := range data {
+               crc ^= uint16(b)
+               for range 8 {
+                       if crc&1 != 0 {
+                               crc = (crc >> 1) ^ refPoly
+                       } else {
+                               crc >>= 1
+                       }
+               }
+       }
+       return crc
 }
 
 func CrcCheck(ctx context.Context, destinationAddress uint8, sourceAddress 
uint8, command DF1Command) func() (uint16, error) {
        return func() (uint16, error) {
-               df1Crc := table.InitCrc()
-               df1Crc = table.UpdateCrc(df1Crc, []byte{destinationAddress, 
sourceAddress})
+               df1Crc := uint16(0)
+               df1Crc = crc16df1Update(df1Crc, []byte{destinationAddress, 
sourceAddress})
                bytes, err := command.Serialize()
                if err != nil {
                        return 0, err
                }
-               df1Crc = table.UpdateCrc(df1Crc, bytes)
-               df1Crc = table.UpdateCrc(df1Crc, []byte{0x03})
-               return table.CRC16(df1Crc), nil
+               df1Crc = crc16df1Update(df1Crc, bytes)
+               df1Crc = crc16df1Update(df1Crc, []byte{0x03})
+               return df1Crc, nil
        }
 }
 
diff --git a/plc4go/spi/codegen/fields/FieldReaderVirtual.go 
b/plc4go/spi/codegen/fields/FieldReaderVirtual.go
index e8eeecc93b..d6b0b08da0 100644
--- a/plc4go/spi/codegen/fields/FieldReaderVirtual.go
+++ b/plc4go/spi/codegen/fields/FieldReaderVirtual.go
@@ -23,7 +23,6 @@ import (
        "context"
        "fmt"
 
-       "github.com/cstockton/go-conv"
        "github.com/rs/zerolog"
 
        "github.com/apache/plc4x/plc4go/spi/codegen"
@@ -44,37 +43,37 @@ func (f *FieldReaderVirtual[T]) ReadVirtualField(ctx 
context.Context, logicalNam
        f.log.Debug().Str("logicalName", logicalName).Msg("reading field")
        switch klass.(type) {
        case *bool:
-               return f.toT(conv.Bool(valueExpression))
+               return f.toT(convBool(valueExpression))
        case *int8:
-               return f.toT(conv.Int8(valueExpression))
+               return f.toT(convInt8(valueExpression))
        case *uint8:
-               return f.toT(conv.Uint8(valueExpression))
+               return f.toT(convUint8(valueExpression))
        case *int16:
-               return f.toT(conv.Int16(valueExpression))
+               return f.toT(convInt16(valueExpression))
        case *uint16:
-               return f.toT(conv.Uint16(valueExpression))
+               return f.toT(convUint16(valueExpression))
        case *int32:
-               return f.toT(conv.Int32(valueExpression))
+               return f.toT(convInt32(valueExpression))
        case *uint32:
-               return f.toT(conv.Uint32(valueExpression))
+               return f.toT(convUint32(valueExpression))
        case *int64:
-               return f.toT(conv.Int64(valueExpression))
+               return f.toT(convInt64(valueExpression))
        case *uint64:
-               return f.toT(conv.Uint64(valueExpression))
+               return f.toT(convUint64(valueExpression))
        case *int:
-               return f.toT(conv.Int(valueExpression))
+               return f.toT(convInt(valueExpression))
        case *uint:
-               return f.toT(conv.Uint(valueExpression))
-       //case *uintptr:
-       //      return f.toT(conv.Uintptr(valueExpression))
+               return f.toT(convUint(valueExpression))
+       case *uintptr:
+               return f.toT(convUintptr(valueExpression))
        case *float32:
-               return f.toT(conv.Float32(valueExpression))
+               return f.toT(convFloat32(valueExpression))
        case *float64:
-               return f.toT(conv.Float64(valueExpression))
-       //case *complex64:
-       //      return f.toT(conv.Complex64(valueExpression))
-       //case *complex128:
-       //      return f.toT(conv.Complex128(valueExpression))
+               return f.toT(convFloat64(valueExpression))
+       case *complex64:
+               return f.toT(convComplex64(valueExpression))
+       case *complex128:
+               return f.toT(convComplex128(valueExpression))
        case *string:
                return any(fmt.Sprintf("%v", valueExpression)).(T), nil
        }
diff --git a/plc4go/spi/codegen/fields/virtualConv.go 
b/plc4go/spi/codegen/fields/virtualConv.go
new file mode 100644
index 0000000000..6530cf8cd9
--- /dev/null
+++ b/plc4go/spi/codegen/fields/virtualConv.go
@@ -0,0 +1,171 @@
+/*
+ * 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 fields
+
+import (
+       "reflect"
+       "strconv"
+
+       "github.com/pkg/errors"
+)
+
+// convBool converts an arbitrary value to a bool. Booleans pass through, 
numerics are true when
+// non-zero, strings parse via strconv.ParseBool.
+func convBool(v any) (bool, error) {
+       if v == nil {
+               return false, nil
+       }
+       rv := reflect.ValueOf(v)
+       switch rv.Kind() {
+       case reflect.Bool:
+               return rv.Bool(), nil
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, 
reflect.Int64:
+               return rv.Int() != 0, nil
+       case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, 
reflect.Uint64, reflect.Uintptr:
+               return rv.Uint() != 0, nil
+       case reflect.Float32, reflect.Float64:
+               return rv.Float() != 0, nil
+       case reflect.Complex64, reflect.Complex128:
+               return rv.Complex() != 0, nil
+       case reflect.String:
+               return strconv.ParseBool(rv.String())
+       }
+       return false, errors.Errorf("cannot convert %T to bool", v)
+}
+
+// convInt64 converts an arbitrary value to int64. Booleans yield 0/1, floats 
truncate, complex
+// values drop the imaginary part, strings parse via strconv.ParseInt.
+func convInt64(v any) (int64, error) {
+       if v == nil {
+               return 0, nil
+       }
+       rv := reflect.ValueOf(v)
+       switch rv.Kind() {
+       case reflect.Bool:
+               if rv.Bool() {
+                       return 1, nil
+               }
+               return 0, nil
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, 
reflect.Int64:
+               return rv.Int(), nil
+       case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, 
reflect.Uint64, reflect.Uintptr:
+               return int64(rv.Uint()), nil
+       case reflect.Float32, reflect.Float64:
+               return int64(rv.Float()), nil
+       case reflect.Complex64, reflect.Complex128:
+               return int64(real(rv.Complex())), nil
+       case reflect.String:
+               return strconv.ParseInt(rv.String(), 10, 64)
+       }
+       return 0, errors.Errorf("cannot convert %T to int64", v)
+}
+
+// convUint64 converts an arbitrary value to uint64. Negative numbers wrap via 
two's complement.
+func convUint64(v any) (uint64, error) {
+       if v == nil {
+               return 0, nil
+       }
+       rv := reflect.ValueOf(v)
+       switch rv.Kind() {
+       case reflect.Bool:
+               if rv.Bool() {
+                       return 1, nil
+               }
+               return 0, nil
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, 
reflect.Int64:
+               return uint64(rv.Int()), nil
+       case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, 
reflect.Uint64, reflect.Uintptr:
+               return rv.Uint(), nil
+       case reflect.Float32, reflect.Float64:
+               return uint64(rv.Float()), nil
+       case reflect.Complex64, reflect.Complex128:
+               return uint64(real(rv.Complex())), nil
+       case reflect.String:
+               return strconv.ParseUint(rv.String(), 10, 64)
+       }
+       return 0, errors.Errorf("cannot convert %T to uint64", v)
+}
+
+// convFloat64 converts an arbitrary value to float64.
+func convFloat64(v any) (float64, error) {
+       if v == nil {
+               return 0, nil
+       }
+       rv := reflect.ValueOf(v)
+       switch rv.Kind() {
+       case reflect.Bool:
+               if rv.Bool() {
+                       return 1, nil
+               }
+               return 0, nil
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, 
reflect.Int64:
+               return float64(rv.Int()), nil
+       case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, 
reflect.Uint64, reflect.Uintptr:
+               return float64(rv.Uint()), nil
+       case reflect.Float32, reflect.Float64:
+               return rv.Float(), nil
+       case reflect.Complex64, reflect.Complex128:
+               return real(rv.Complex()), nil
+       case reflect.String:
+               return strconv.ParseFloat(rv.String(), 64)
+       }
+       return 0, errors.Errorf("cannot convert %T to float64", v)
+}
+
+// convComplex128 converts an arbitrary value to complex128. Numerics become 
the real part with
+// imaginary 0; strings parse via strconv.ParseComplex.
+func convComplex128(v any) (complex128, error) {
+       if v == nil {
+               return 0, nil
+       }
+       rv := reflect.ValueOf(v)
+       switch rv.Kind() {
+       case reflect.Bool:
+               if rv.Bool() {
+                       return 1, nil
+               }
+               return 0, nil
+       case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, 
reflect.Int64:
+               return complex(float64(rv.Int()), 0), nil
+       case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, 
reflect.Uint64, reflect.Uintptr:
+               return complex(float64(rv.Uint()), 0), nil
+       case reflect.Float32, reflect.Float64:
+               return complex(rv.Float(), 0), nil
+       case reflect.Complex64, reflect.Complex128:
+               return rv.Complex(), nil
+       case reflect.String:
+               return strconv.ParseComplex(rv.String(), 128)
+       }
+       return 0, errors.Errorf("cannot convert %T to complex128", v)
+}
+
+// Typed narrow wrappers so the call site can funnel directly: 
f.toT(convInt8(v)).
+
+func convInt(v any) (int, error)             { n, err := convInt64(v); return 
int(n), err }
+func convInt8(v any) (int8, error)           { n, err := convInt64(v); return 
int8(n), err }
+func convInt16(v any) (int16, error)         { n, err := convInt64(v); return 
int16(n), err }
+func convInt32(v any) (int32, error)         { n, err := convInt64(v); return 
int32(n), err }
+func convUint(v any) (uint, error)           { n, err := convUint64(v); return 
uint(n), err }
+func convUint8(v any) (uint8, error)         { n, err := convUint64(v); return 
uint8(n), err }
+func convUint16(v any) (uint16, error)       { n, err := convUint64(v); return 
uint16(n), err }
+func convUint32(v any) (uint32, error)       { n, err := convUint64(v); return 
uint32(n), err }
+func convUintptr(v any) (uintptr, error)     { n, err := convUint64(v); return 
uintptr(n), err }
+func convFloat32(v any) (float32, error)     { n, err := convFloat64(v); 
return float32(n), err }
+func convComplex64(v any) (complex64, error) { c, err := convComplex128(v); 
return complex64(c), err }
diff --git a/plc4go/spi/codegen/fields/virtualConv_test.go 
b/plc4go/spi/codegen/fields/virtualConv_test.go
new file mode 100644
index 0000000000..d3a0489f3d
--- /dev/null
+++ b/plc4go/spi/codegen/fields/virtualConv_test.go
@@ -0,0 +1,247 @@
+/*
+ * 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 fields
+
+import (
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+// ── convBool ───────────────────────────────────────────────────────────────
+
+func TestConvBool(t *testing.T) {
+       cases := []struct {
+               name string
+               in   any
+               want bool
+       }{
+               {"nil", nil, false},
+               {"true", true, true},
+               {"false", false, false},
+               {"int non-zero", int(5), true},
+               {"int zero", int(0), false},
+               {"int8 negative", int8(-1), true},
+               {"uint non-zero", uint(1), true},
+               {"uintptr non-zero", uintptr(7), true},
+               {"float non-zero", 1.5, true},
+               {"float zero", 0.0, false},
+               {"complex non-zero", complex(1, 0), true},
+               {"complex zero", complex(0, 0), false},
+               {"string true", "true", true},
+               {"string false", "false", false},
+       }
+       for _, tc := range cases {
+               t.Run(tc.name, func(t *testing.T) {
+                       got, err := convBool(tc.in)
+                       require.NoError(t, err)
+                       assert.Equal(t, tc.want, got)
+               })
+       }
+}
+
+func TestConvBool_UnsupportedType(t *testing.T) {
+       _, err := convBool([]int{1, 2})
+       assert.Error(t, err)
+}
+
+func TestConvBool_BadString(t *testing.T) {
+       _, err := convBool("not-a-bool")
+       assert.Error(t, err)
+}
+
+// ── convInt64 / signed narrow ──────────────────────────────────────────────
+
+func TestConvInt64(t *testing.T) {
+       cases := []struct {
+               name string
+               in   any
+               want int64
+       }{
+               {"nil", nil, 0},
+               {"true", true, 1},
+               {"false", false, 0},
+               {"int", int(-42), -42},
+               {"int8", int8(-1), -1},
+               {"int64 max", int64(1<<62 + 1), 1<<62 + 1},
+               {"uint", uint(7), 7},
+               {"uintptr", uintptr(8), 8},
+               {"float trunc", 3.9, 3},
+               {"complex real", complex(2.5, 99), 2},
+               {"string", "-123", -123},
+       }
+       for _, tc := range cases {
+               t.Run(tc.name, func(t *testing.T) {
+                       got, err := convInt64(tc.in)
+                       require.NoError(t, err)
+                       assert.Equal(t, tc.want, got)
+               })
+       }
+}
+
+func TestConvInt64_UnsupportedType(t *testing.T) {
+       _, err := convInt64(struct{}{})
+       assert.Error(t, err)
+}
+
+func TestSignedNarrowWrappers(t *testing.T) {
+       // Use values that trivially fit and verify the narrowing happens 
(cast).
+       i, err := convInt(int64(42))
+       require.NoError(t, err)
+       assert.Equal(t, 42, i)
+
+       i8, err := convInt8(int(127))
+       require.NoError(t, err)
+       assert.Equal(t, int8(127), i8)
+
+       i16, err := convInt16(int(-32768))
+       require.NoError(t, err)
+       assert.Equal(t, int16(-32768), i16)
+
+       i32, err := convInt32(int64(-1))
+       require.NoError(t, err)
+       assert.Equal(t, int32(-1), i32)
+}
+
+func TestSignedNarrowWrappers_Truncate(t *testing.T) {
+       // int8 holds values in [-128, 127]; 300 wraps via two's complement 
narrowing.
+       got, err := convInt8(int64(300))
+       require.NoError(t, err)
+       assert.Equal(t, int8(300-256), got)
+}
+
+// ── convUint64 / unsigned narrow ───────────────────────────────────────────
+
+func TestConvUint64(t *testing.T) {
+       cases := []struct {
+               name string
+               in   any
+               want uint64
+       }{
+               {"nil", nil, 0},
+               {"true", true, 1},
+               {"false", false, 0},
+               {"int positive", int(42), 42},
+               {"uint8", uint8(255), 255},
+               {"uintptr", uintptr(9), 9},
+               {"float trunc", 3.9, 3},
+               {"complex real", complex(7.0, 1.0), 7},
+               {"string", "1234", 1234},
+       }
+       for _, tc := range cases {
+               t.Run(tc.name, func(t *testing.T) {
+                       got, err := convUint64(tc.in)
+                       require.NoError(t, err)
+                       assert.Equal(t, tc.want, got)
+               })
+       }
+}
+
+func TestConvUint64_NegativeWraps(t *testing.T) {
+       // int(-1) wraps to uint64(max) via two's complement, matching go-conv 
behavior.
+       got, err := convUint64(int(-1))
+       require.NoError(t, err)
+       assert.Equal(t, uint64(0xFFFFFFFFFFFFFFFF), got)
+}
+
+func TestUnsignedNarrowWrappers(t *testing.T) {
+       u, err := convUint(uint64(7))
+       require.NoError(t, err)
+       assert.Equal(t, uint(7), u)
+
+       u8, err := convUint8(uint64(255))
+       require.NoError(t, err)
+       assert.Equal(t, uint8(255), u8)
+
+       u16, err := convUint16(uint64(0xFFFF))
+       require.NoError(t, err)
+       assert.Equal(t, uint16(0xFFFF), u16)
+
+       u32, err := convUint32(uint64(0xDEADBEEF))
+       require.NoError(t, err)
+       assert.Equal(t, uint32(0xDEADBEEF), u32)
+
+       up, err := convUintptr(uint64(42))
+       require.NoError(t, err)
+       assert.Equal(t, uintptr(42), up)
+}
+
+// ── convFloat64 / float narrow ─────────────────────────────────────────────
+
+func TestConvFloat64(t *testing.T) {
+       cases := []struct {
+               name string
+               in   any
+               want float64
+       }{
+               {"nil", nil, 0},
+               {"true", true, 1},
+               {"int", int(-3), -3},
+               {"uint", uint(7), 7},
+               {"float", 1.5, 1.5},
+               {"complex real", complex(2.25, 99), 2.25},
+               {"string", "3.14", 3.14},
+       }
+       for _, tc := range cases {
+               t.Run(tc.name, func(t *testing.T) {
+                       got, err := convFloat64(tc.in)
+                       require.NoError(t, err)
+                       assert.Equal(t, tc.want, got)
+               })
+       }
+}
+
+func TestConvFloat32(t *testing.T) {
+       got, err := convFloat32(float64(1.5))
+       require.NoError(t, err)
+       assert.Equal(t, float32(1.5), got)
+}
+
+// ── convComplex128 / convComplex64 ─────────────────────────────────────────
+
+func TestConvComplex128(t *testing.T) {
+       cases := []struct {
+               name string
+               in   any
+               want complex128
+       }{
+               {"nil", nil, 0},
+               {"true", true, 1},
+               {"int", int(3), complex(3, 0)},
+               {"uint", uint(4), complex(4, 0)},
+               {"float", 2.5, complex(2.5, 0)},
+               {"complex passthrough", complex(1, 2), complex(1, 2)},
+               {"string", "(1+2i)", complex(1, 2)},
+       }
+       for _, tc := range cases {
+               t.Run(tc.name, func(t *testing.T) {
+                       got, err := convComplex128(tc.in)
+                       require.NoError(t, err)
+                       assert.Equal(t, tc.want, got)
+               })
+       }
+}
+
+func TestConvComplex64(t *testing.T) {
+       got, err := convComplex64(complex(1.5, 2.0))
+       require.NoError(t, err)
+       assert.Equal(t, complex64(complex(1.5, 2.0)), got)
+}
diff --git a/plc4go/spi/testutils/DriverTestRunner.go 
b/plc4go/spi/testutils/DriverTestRunner.go
index f8b91375b3..501d48ea28 100644
--- a/plc4go/spi/testutils/DriverTestRunner.go
+++ b/plc4go/spi/testutils/DriverTestRunner.go
@@ -33,7 +33,6 @@ import (
 
        "github.com/pkg/errors"
        "github.com/stretchr/testify/assert"
-       "github.com/subchen/go-xmldom"
 
        "github.com/apache/plc4x/plc4go/pkg/api"
        "github.com/apache/plc4x/plc4go/pkg/api/config"
@@ -41,6 +40,7 @@ import (
        "github.com/apache/plc4x/plc4go/spi"
        "github.com/apache/plc4x/plc4go/spi/options"
        "github.com/apache/plc4x/plc4go/spi/options/converter"
+       "github.com/apache/plc4x/plc4go/spi/testutils/xmldom"
        "github.com/apache/plc4x/plc4go/spi/transports"
        "github.com/apache/plc4x/plc4go/spi/transports/test"
        "github.com/apache/plc4x/plc4go/spi/utils"
diff --git a/plc4go/spi/testutils/ParserSerializerTestRunner.go 
b/plc4go/spi/testutils/ParserSerializerTestRunner.go
index 5d1359da7e..f45f078d98 100644
--- a/plc4go/spi/testutils/ParserSerializerTestRunner.go
+++ b/plc4go/spi/testutils/ParserSerializerTestRunner.go
@@ -32,9 +32,9 @@ import (
 
        "github.com/pkg/errors"
        "github.com/stretchr/testify/assert"
-       "github.com/subchen/go-xmldom"
 
        "github.com/apache/plc4x/plc4go/spi/options"
+       "github.com/apache/plc4x/plc4go/spi/testutils/xmldom"
        "github.com/apache/plc4x/plc4go/spi/utils"
 )
 
diff --git a/plc4go/spi/testutils/xmldom/xmldom.go 
b/plc4go/spi/testutils/xmldom/xmldom.go
new file mode 100644
index 0000000000..15c853ab70
--- /dev/null
+++ b/plc4go/spi/testutils/xmldom/xmldom.go
@@ -0,0 +1,220 @@
+/*
+ * 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 xmldom provides a tiny DOM tree over encoding/xml for the test 
harness.
+// It replaces the subset of github.com/subchen/go-xmldom that plc4go's 
testutils used.
+package xmldom
+
+import (
+       "bytes"
+       "encoding/xml"
+       "fmt"
+       "io"
+       "strings"
+)
+
+type Attribute struct {
+       Name  string
+       Value string
+}
+
+type Node struct {
+       Document   *Document
+       Parent     *Node
+       Name       string
+       Attributes []*Attribute
+       Children   []*Node
+       Text       string
+}
+
+type Document struct {
+       ProcInst   string
+       Directives []string
+       Root       *Node
+}
+
+// Must panics if err is non-nil; otherwise returns doc.
+func Must(doc *Document, err error) *Document {
+       if err != nil {
+               panic(err)
+       }
+       return doc
+}
+
+// Parse reads an XML document from r and returns a DOM tree.
+func Parse(r io.Reader) (*Document, error) {
+       dec := xml.NewDecoder(r)
+       doc := &Document{}
+       var current *Node
+       for {
+               tok, err := dec.Token()
+               if err == io.EOF {
+                       break
+               }
+               if err != nil {
+                       return nil, err
+               }
+               switch t := tok.(type) {
+               case xml.StartElement:
+                       el := &Node{Document: doc, Parent: current, Name: 
t.Name.Local}
+                       for _, a := range t.Attr {
+                               el.Attributes = append(el.Attributes, 
&Attribute{Name: a.Name.Local, Value: a.Value})
+                       }
+                       if current != nil {
+                               current.Children = append(current.Children, el)
+                       }
+                       current = el
+                       if doc.Root == nil {
+                               doc.Root = el
+                       }
+               case xml.EndElement:
+                       if current != nil {
+                               current = current.Parent
+                       }
+               case xml.CharData:
+                       if current != nil {
+                               current.Text = string(bytes.TrimSpace(t))
+                       }
+               case xml.ProcInst:
+                       doc.ProcInst = fmt.Sprintf("<?%s %s?>", t.Target, 
string(t.Inst))
+               case xml.Directive:
+                       doc.Directives = append(doc.Directives, 
fmt.Sprintf("<!%s>", string(t)))
+               }
+       }
+       return doc, nil
+}
+
+func (n *Node) GetAttribute(name string) *Attribute {
+       for _, a := range n.Attributes {
+               if a.Name == name {
+                       return a
+               }
+       }
+       return nil
+}
+
+func (n *Node) GetAttributeValue(name string) string {
+       if a := n.GetAttribute(name); a != nil {
+               return a.Value
+       }
+       return ""
+}
+
+func (n *Node) GetChild(name string) *Node {
+       for _, c := range n.Children {
+               if c.Name == name {
+                       return c
+               }
+       }
+       return nil
+}
+
+func (n *Node) GetChildren(name string) []*Node {
+       var out []*Node
+       for _, c := range n.Children {
+               if c.Name == name {
+                       out = append(out, c)
+               }
+       }
+       return out
+}
+
+func (n *Node) FirstChild() *Node {
+       if len(n.Children) > 0 {
+               return n.Children[0]
+       }
+       return nil
+}
+
+// FindOneByName performs a depth-first search and returns the first node 
whose name matches.
+func (n *Node) FindOneByName(name string) *Node {
+       if n.Name == name {
+               return n
+       }
+       for _, c := range n.Children {
+               if x := c.FindOneByName(name); x != nil {
+                       return x
+               }
+       }
+       return nil
+}
+
+// FindByName performs a depth-first search and returns every node whose name 
matches.
+func (n *Node) FindByName(name string) []*Node {
+       var out []*Node
+       if n.Name == name {
+               out = append(out, n)
+       }
+       for _, c := range n.Children {
+               out = append(out, c.FindByName(name)...)
+       }
+       return out
+}
+
+// XMLPretty renders the node tree with two-space indentation, matching the 
format produced by
+// the previous xmldom library so golden-file test fixtures keep comparing 
equal.
+func (n *Node) XMLPretty() string {
+       buf := new(bytes.Buffer)
+       printXML(buf, n, 0, "  ")
+       return buf.String()
+}
+
+func printXML(buf *bytes.Buffer, n *Node, level int, indent string) {
+       pretty := len(indent) > 0
+       if pretty {
+               buf.WriteString(strings.Repeat(indent, level))
+       }
+       buf.WriteByte('<')
+       buf.WriteString(n.Name)
+       for _, a := range n.Attributes {
+               buf.WriteByte(' ')
+               buf.WriteString(a.Name)
+               buf.WriteString(`="`)
+               xml.Escape(buf, []byte(a.Value))
+               buf.WriteByte('"')
+       }
+       if len(n.Children) == 0 && len(n.Text) == 0 {
+               buf.WriteString(" />")
+               if pretty {
+                       buf.WriteByte('\n')
+               }
+               return
+       }
+       buf.WriteByte('>')
+       if len(n.Children) > 0 {
+               if pretty {
+                       buf.WriteByte('\n')
+               }
+               for _, c := range n.Children {
+                       printXML(buf, c, level+1, indent)
+               }
+       }
+       if len(n.Text) > 0 {
+               xml.EscapeText(buf, []byte(n.Text))
+       }
+       if len(n.Children) > 0 && pretty {
+               buf.WriteString(strings.Repeat(indent, level))
+       }
+       buf.WriteString("</")
+       buf.WriteString(n.Name)
+       buf.WriteByte('>')
+       if pretty {
+               buf.WriteByte('\n')
+       }
+}
diff --git a/plc4go/spi/testutils/xmldom/xmldom_test.go 
b/plc4go/spi/testutils/xmldom/xmldom_test.go
new file mode 100644
index 0000000000..377e1af471
--- /dev/null
+++ b/plc4go/spi/testutils/xmldom/xmldom_test.go
@@ -0,0 +1,183 @@
+/*
+ * 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 xmldom
+
+import (
+       "strings"
+       "testing"
+
+       "github.com/stretchr/testify/assert"
+       "github.com/stretchr/testify/require"
+)
+
+const sampleXML = `<?xml version="1.0" encoding="UTF-8"?>
+<testsuite byteOrder="LITTLE_ENDIAN">
+  <name>example</name>
+  <driver-parameters>
+    <parameter><name>host</name><value>1.2.3.4</value></parameter>
+    <parameter><name>port</name><value>1234</value></parameter>
+  </driver-parameters>
+  <testcase>
+    <name>case1</name>
+    <raw>DEADBEEF</raw>
+  </testcase>
+</testsuite>`
+
+func parseSample(t *testing.T) *Document {
+       t.Helper()
+       doc, err := Parse(strings.NewReader(sampleXML))
+       require.NoError(t, err)
+       require.NotNil(t, doc.Root)
+       return doc
+}
+
+func TestParse_BuildsRoot(t *testing.T) {
+       doc := parseSample(t)
+       assert.Equal(t, "testsuite", doc.Root.Name)
+       assert.Equal(t, "LITTLE_ENDIAN", 
doc.Root.GetAttributeValue("byteOrder"))
+}
+
+func TestParse_PreservesParentLinks(t *testing.T) {
+       doc := parseSample(t)
+       name := doc.Root.GetChild("name")
+       require.NotNil(t, name)
+       assert.Equal(t, doc.Root, name.Parent)
+}
+
+func TestParse_ProcInst(t *testing.T) {
+       doc := parseSample(t)
+       assert.Contains(t, doc.ProcInst, `xml`)
+       assert.Contains(t, doc.ProcInst, `version="1.0"`)
+}
+
+func TestParse_Error(t *testing.T) {
+       _, err := Parse(strings.NewReader("<unclosed>"))
+       assert.Error(t, err)
+}
+
+func TestMust_PanicsOnError(t *testing.T) {
+       assert.Panics(t, func() {
+               Must(nil, assert.AnError)
+       })
+}
+
+func TestMust_ReturnsDocOnSuccess(t *testing.T) {
+       doc := &Document{}
+       got := Must(doc, nil)
+       assert.Same(t, doc, got)
+}
+
+func TestGetAttribute_Missing(t *testing.T) {
+       doc := parseSample(t)
+       assert.Nil(t, doc.Root.GetAttribute("missing"))
+       assert.Equal(t, "", doc.Root.GetAttributeValue("missing"))
+}
+
+func TestGetChild(t *testing.T) {
+       doc := parseSample(t)
+       name := doc.Root.GetChild("name")
+       require.NotNil(t, name)
+       assert.Equal(t, "example", name.Text)
+       assert.Nil(t, doc.Root.GetChild("does-not-exist"))
+}
+
+func TestGetChildren(t *testing.T) {
+       doc := parseSample(t)
+       params := 
doc.Root.GetChild("driver-parameters").GetChildren("parameter")
+       assert.Len(t, params, 2)
+
+       // GetChildren is direct-children-only — does NOT descend.
+       directNames := doc.Root.GetChildren("name")
+       assert.Len(t, directNames, 1, "GetChildren should only match direct 
children")
+}
+
+func TestFirstChild(t *testing.T) {
+       doc := parseSample(t)
+       assert.Equal(t, "name", doc.Root.FirstChild().Name)
+
+       leaf := &Node{}
+       assert.Nil(t, leaf.FirstChild())
+}
+
+func TestFindOneByName_DepthFirst(t *testing.T) {
+       doc := parseSample(t)
+       // `name` appears under <testsuite>, under each <parameter>, and under 
<testcase>.
+       // Depth-first should hit the top-level <name> first.
+       found := doc.Root.FindOneByName("name")
+       require.NotNil(t, found)
+       assert.Equal(t, "example", found.Text)
+
+       assert.Nil(t, doc.Root.FindOneByName("does-not-exist"))
+}
+
+func TestFindByName_RecursiveAll(t *testing.T) {
+       doc := parseSample(t)
+       // <name> elements: 1 top-level, 2 in parameters, 1 in testcase = 4 
total.
+       all := doc.Root.FindByName("name")
+       assert.Len(t, all, 4)
+}
+
+func TestXMLPretty_SelfClosing(t *testing.T) {
+       n := &Node{Name: "leaf"}
+       assert.Equal(t, "<leaf />\n", n.XMLPretty())
+}
+
+func TestXMLPretty_WithText(t *testing.T) {
+       n := &Node{Name: "v", Text: "hello"}
+       assert.Equal(t, "<v>hello</v>\n", n.XMLPretty())
+}
+
+func TestXMLPretty_EscapesText(t *testing.T) {
+       n := &Node{Name: "v", Text: "a<b&c"}
+       out := n.XMLPretty()
+       assert.Contains(t, out, "&lt;")
+       assert.Contains(t, out, "&amp;")
+}
+
+func TestXMLPretty_AttributesAndChildren(t *testing.T) {
+       doc := parseSample(t)
+       tc := doc.Root.GetChild("testcase")
+       require.NotNil(t, tc)
+       out := tc.XMLPretty()
+       // Should contain nested elements with 2-space indentation.
+       assert.Contains(t, out, "<testcase>")
+       assert.Contains(t, out, "  <name>case1</name>")
+       assert.Contains(t, out, "  <raw>DEADBEEF</raw>")
+       assert.Contains(t, out, "</testcase>")
+}
+
+func TestXMLPretty_EscapesAttribute(t *testing.T) {
+       n := &Node{
+               Name:       "v",
+               Attributes: []*Attribute{{Name: "a", Value: `"<>&`}},
+       }
+       out := n.XMLPretty()
+       assert.Contains(t, out, `&#34;`)
+       assert.Contains(t, out, `&lt;`)
+       assert.Contains(t, out, `&amp;`)
+}
+
+func TestRoundTrip_TextMutation(t *testing.T) {
+       // The harness's normalizeXml mutates Node.Text in place; make sure 
that's possible.
+       doc := parseSample(t)
+       name := doc.Root.GetChild("name")
+       name.Text = "changed"
+       assert.Equal(t, "changed", doc.Root.GetChild("name").Text)
+}

Reply via email to