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

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


The following commit(s) were added to refs/heads/develop by this push:
     new c76b28e  feature(plc4go): simulated driver, connection pool
c76b28e is described below

commit c76b28ee125970b611ae608e5884121ef790facc
Author: cdutz <[email protected]>
AuthorDate: Thu Jan 6 17:37:10 2022 +0100

    feature(plc4go): simulated driver, connection pool
---
 Jenkinsfile                                        |   2 +-
 plc4go/go.mod                                      |   6 +-
 plc4go/go.sum                                      |  70 +-
 plc4go/internal/plc4go/knxnetip/FieldHandler.go    |  15 -
 plc4go/internal/plc4go/s7/Driver.go                |   2 +-
 plc4go/internal/plc4go/simulated/Connection.go     | 162 +++++
 .../internal/plc4go/simulated/Connection_test.go   | 758 +++++++++++++++++++++
 plc4go/internal/plc4go/simulated/Device.go         |  76 +++
 plc4go/internal/plc4go/simulated/Device_test.go    | 288 ++++++++
 plc4go/internal/plc4go/simulated/Driver.go         |  79 +++
 plc4go/internal/plc4go/simulated/Driver_test.go    | 326 +++++++++
 plc4go/internal/plc4go/simulated/Field.go          |  71 ++
 plc4go/internal/plc4go/simulated/FieldHandler.go   | 103 +++
 .../internal/plc4go/simulated/FieldHandler_test.go | 169 +++++
 plc4go/internal/plc4go/simulated/Field_test.go     | 291 ++++++++
 plc4go/internal/plc4go/simulated/Reader.go         |  84 +++
 plc4go/internal/plc4go/simulated/Reader_test.go    | 202 ++++++
 plc4go/internal/plc4go/simulated/ValueHandler.go   |  37 +
 plc4go/internal/plc4go/simulated/Writer.go         |  76 +++
 plc4go/internal/plc4go/simulated/Writer_test.go    | 214 ++++++
 .../plc4go/utils/pool/PlcConnectionPool.go         | 158 +++++
 .../plc4go/utils/pool/PlcConnectionPool_test.go    |  88 +++
 .../plc4go/utils/pool/PooledPlcConnection.go       | 188 +++++
 plc4go/pkg/plc4go/pool/plc_connection_pool.go      |  20 +
 24 files changed, 3407 insertions(+), 78 deletions(-)

diff --git a/Jenkinsfile b/Jenkinsfile
index 92e83ea..a49f848 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -47,7 +47,7 @@ pipeline {
     }
 
     options {
-        // Kill this job after one hour.
+        // Kill this job after one day.
         timeout(time: 24, unit: 'HOURS')
         // When we have test-fails e.g. we don't need to run the remaining 
steps
         skipStagesAfterUnstable()
diff --git a/plc4go/go.mod b/plc4go/go.mod
index b783d8e..dcb2101 100644
--- a/plc4go/go.mod
+++ b/plc4go/go.mod
@@ -23,7 +23,6 @@ go 1.16
 
 require (
        github.com/ajankovic/xdiff v0.0.1
-       github.com/elastic/go-licenser v0.4.0 // indirect
        github.com/google/gopacket v1.1.19
        github.com/icza/bitio v1.0.0
        github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4
@@ -31,9 +30,8 @@ require (
        github.com/rs/zerolog v1.20.0
        github.com/snksoft/crc v1.1.0
        github.com/subchen/go-xmldom v1.1.2
-       github.com/tebeka/go2xunit v1.4.10 // indirect
+       github.com/viney-shih/go-lock v1.1.1
        golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
+       golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
        golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 // indirect
-       golang.org/x/tools v0.1.8 // indirect
-       gotest.tools/gotestsum v1.7.0 // indirect
 )
diff --git a/plc4go/go.sum b/plc4go/go.sum
index cf5a1e6..d3915ac 100644
--- a/plc4go/go.sum
+++ b/plc4go/go.sum
@@ -3,103 +3,59 @@ github.com/ajankovic/xdiff v0.0.1/go.mod 
h1:SUmEZ67uB97I0zkiuQ+lb+LOms9ipn8X+p+2
 github.com/antchfx/xpath v0.0.0-20170515025933-1f3266e77307 
h1:C735MoY/X+UOx6SECmHk5pVOj51h839Ph13pEoY8UmU=
 github.com/antchfx/xpath v0.0.0-20170515025933-1f3266e77307/go.mod 
h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod 
h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk=
-github.com/dnephin/pflag v1.0.7/go.mod 
h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE=
-github.com/elastic/go-licenser v0.4.0 
h1:jLq6A5SilDS/Iz1ABRkO6BHy91B9jBora8FwGRsDqUI=
-github.com/elastic/go-licenser v0.4.0/go.mod 
h1:V56wHMpmdURfibNBggaSBfqgPxyT1Tldns1i87iTEvU=
-github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
-github.com/fatih/color v1.10.0/go.mod 
h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/fsnotify/fsnotify v1.4.9 
h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
-github.com/fsnotify/fsnotify v1.4.9/go.mod 
h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/google/go-cmp v0.4.0/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod 
h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/davecgh/go-spew v1.1.0 
h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/google/gopacket v1.1.19 
h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
 github.com/google/gopacket v1.1.19/go.mod 
h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 
h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
-github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod 
h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 github.com/icza/bitio v1.0.0 h1:squ/m1SHyFeCA6+6Gyol1AxV9nmPPlJFT8c2vKdj3U8=
 github.com/icza/bitio v1.0.0/go.mod 
h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
 github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 
h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k=
 github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod 
h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
 github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4 
h1:G2ztCwXov8mRvP0ZfjE6nAlaCX2XbykaeHdbT6KwDz0=
 github.com/jacobsa/go-serial v0.0.0-20180131005756-15cf729a72d4/go.mod 
h1:2RvX5ZjVtsznNZPEt4xwJXNJrM3VTZoQf7V6gk0ysvs=
-github.com/jonboulle/clockwork v0.2.2 
h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
-github.com/jonboulle/clockwork v0.2.2/go.mod 
h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
-github.com/mattn/go-colorable v0.1.8 
h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
-github.com/mattn/go-colorable v0.1.8/go.mod 
h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-isatty v0.0.12 
h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
-github.com/mattn/go-isatty v0.0.12/go.mod 
h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 github.com/pkg/errors v0.8.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod 
h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 
h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod 
h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
 github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
 github.com/rs/zerolog v1.20.0/go.mod 
h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
 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/pflag v1.0.3/go.mod 
h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/stretchr/objx v0.1.0/go.mod 
h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.5.1 
h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod 
h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 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/tebeka/go2xunit v1.4.10 
h1:0UO+9YoLpXTZ0DL9XbTmIIibgmKBGiwroo8uhFMSyR0=
-github.com/tebeka/go2xunit v1.4.10/go.mod 
h1:wmc9jKT7KlU4QLU6DNTaIXNnYNOjKKNlp6mjOS0UrqY=
-github.com/yuin/goldmark v1.2.1/go.mod 
h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.4.0/go.mod 
h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.1/go.mod 
h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/viney-shih/go-lock v1.1.1 
h1:SwzDPPAiHpcwGCr5k8xD15d2gQSo8d4roRYd7TDV2eI=
+github.com/viney-shih/go-lock v1.1.1/go.mod 
h1:Yijm78Ljteb3kRiJrbLAxVntkUukGu5uzSxq/xV7OO8=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod 
h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod 
h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod 
h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod 
h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod 
h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod 
h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod 
h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod 
h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod 
h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod 
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f 
h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
 golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod 
h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c 
h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod 
h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod 
h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod 
h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211102192858-4dd72447c267 
h1:7zYaz3tjChtpayGDzu6H0hDAUM5zIGA2XW7kRNgQ0jc=
 golang.org/x/sys v0.0.0-20211102192858-4dd72447c267/go.mod 
h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 
h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod 
h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod 
h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod 
h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod 
h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod 
h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.1.0/go.mod 
h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.7/go.mod 
h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
-golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
-golang.org/x/tools v0.1.8/go.mod 
h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 
h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod 
h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gotest.tools/gotestsum v1.7.0 h1:RwpqwwFKBAa2h+F6pMEGpE707Edld0etUD3GhqqhDNc=
-gotest.tools/gotestsum v1.7.0/go.mod 
h1:V1m4Jw3eBerhI/A6qCxUE07RnCg7ACkKj9BYcAm09V8=
-gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod 
h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/plc4go/internal/plc4go/knxnetip/FieldHandler.go 
b/plc4go/internal/plc4go/knxnetip/FieldHandler.go
index 5dd11db..c33f7bb 100644
--- a/plc4go/internal/plc4go/knxnetip/FieldHandler.go
+++ b/plc4go/internal/plc4go/knxnetip/FieldHandler.go
@@ -29,21 +29,6 @@ import (
        "strconv"
 )
 
-type FieldType uint8
-
-const (
-       FieldCoil FieldType = 0x00
-)
-
-func (m FieldType) GetName() string {
-       switch m {
-       case FieldCoil:
-               // TODO: these looked like copy paste from modbus before change 
this name might be therefore completly wrong
-               return "KNXNETIPFIELDCOIL"
-       }
-       return ""
-}
-
 type FieldHandler struct {
        groupAddress3Level             *regexp.Regexp
        groupAddress2Level             *regexp.Regexp
diff --git a/plc4go/internal/plc4go/s7/Driver.go 
b/plc4go/internal/plc4go/s7/Driver.go
index 4ab02bb..b6d24c0 100644
--- a/plc4go/internal/plc4go/s7/Driver.go
+++ b/plc4go/internal/plc4go/s7/Driver.go
@@ -47,7 +47,7 @@ func NewDriver() plc4go.PlcDriver {
 
 func (m *Driver) GetConnection(transportUrl url.URL, transports 
map[string]transports.Transport, options map[string][]string) <-chan 
plc4go.PlcConnectionConnectResult {
        log.Debug().Stringer("transportUrl", &transportUrl).Msgf("Get 
connection for transport url with %d transport(s) and %d option(s)", 
len(transports), len(options))
-       // Get an the transport specified in the url
+       // Get the transport specified in the url
        transport, ok := transports[transportUrl.Scheme]
        if !ok {
                log.Error().Stringer("transportUrl", &transportUrl).Msgf("We 
couldn't find a transport for scheme %s", transportUrl.Scheme)
diff --git a/plc4go/internal/plc4go/simulated/Connection.go 
b/plc4go/internal/plc4go/simulated/Connection.go
new file mode 100644
index 0000000..74d63b4
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Connection.go
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+       _default "github.com/apache/plc4x/plc4go/internal/plc4go/spi/default"
+       internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "github.com/pkg/errors"
+       "strconv"
+       "time"
+)
+
+type Connection struct {
+       device       *Device
+       fieldHandler spi.PlcFieldHandler
+       valueHandler spi.PlcValueHandler
+       options      map[string][]string
+       connected    bool
+}
+
+func NewConnection(device *Device, fieldHandler spi.PlcFieldHandler, 
valueHandler spi.PlcValueHandler, options map[string][]string) *Connection {
+       connection := &Connection{
+               device:       device,
+               fieldHandler: fieldHandler,
+               valueHandler: valueHandler,
+               options:      options,
+               connected:    false,
+       }
+       return connection
+}
+
+func (c *Connection) Connect() <-chan plc4go.PlcConnectionConnectResult {
+       ch := make(chan plc4go.PlcConnectionConnectResult)
+       go func() {
+               // Check if the connection was already connected
+               if c.connected {
+                       // Return an error to the user.
+                       ch <- _default.NewDefaultPlcConnectionConnectResult(c, 
errors.New("already connected"))
+               }
+               if delayString, ok := c.options["connectionDelay"]; ok {
+                       // This is the length of the array, not the string
+                       if len(delayString) == 1 {
+                               delay, err := strconv.Atoi(delayString[0])
+                               if err == nil {
+                                       time.Sleep(time.Duration(delay) * 
time.Millisecond)
+                               }
+                       }
+               }
+               // Mark the connection as "connected"
+               c.connected = true
+               // Return the connection in a connected state to the user.
+               ch <- _default.NewDefaultPlcConnectionConnectResult(c, nil)
+       }()
+       return ch
+}
+
+func (c *Connection) BlockingClose() {
+       <-c.Close()
+}
+
+func (c *Connection) Close() <-chan plc4go.PlcConnectionCloseResult {
+       ch := make(chan plc4go.PlcConnectionCloseResult)
+       go func() {
+               // Check if the connection is connected
+               if !c.connected {
+                       // Return an error to the user.
+                       ch <- _default.NewDefaultPlcConnectionCloseResult(c, 
errors.New("not connected"))
+               }
+               if delayString, ok := c.options["closingDelay"]; ok {
+                       // This is the length of the array, not the string
+                       if len(delayString) == 1 {
+                               delay, err := strconv.Atoi(delayString[0])
+                               if err == nil {
+                                       time.Sleep(time.Duration(delay) * 
time.Millisecond)
+                               }
+                       }
+               }
+               // Mark the connection as "disconnected"
+               c.connected = false
+               // Return a new connection to the user.
+               ch <- _default.NewDefaultPlcConnectionCloseResult(c, nil)
+       }()
+       return ch
+}
+
+func (c *Connection) IsConnected() bool {
+       return c.connected
+}
+
+func (c *Connection) Ping() <-chan plc4go.PlcConnectionPingResult {
+       ch := make(chan plc4go.PlcConnectionPingResult)
+       go func() {
+               if delayString, ok := c.options["pingDelay"]; ok {
+                       // This is the length of the array, not the string
+                       if len(delayString) == 1 {
+                               delay, err := strconv.Atoi(delayString[0])
+                               if err == nil {
+                                       time.Sleep(time.Duration(delay) * 
time.Millisecond)
+                               }
+                       }
+               }
+               // Return a new connection to the user.
+               ch <- _default.NewDefaultPlcConnectionPingResult(nil)
+       }()
+       return ch
+}
+
+func (c *Connection) GetMetadata() model.PlcConnectionMetadata {
+       return _default.DefaultConnectionMetadata{
+               ConnectionAttributes: map[string]string{
+                       "connectionDelay": "Delay applied when connecting",
+                       "closingDelay":    "Delay applied when closing the 
connection",
+                       "pingDelay":       "Delay applied when executing a ping 
operation",
+                       "readDelay":       "Delay applied when executing a read 
operation",
+                       "writeDelay":      "Delay applied when executing a 
write operation",
+               },
+               ProvidesReading:     true,
+               ProvidesWriting:     true,
+               ProvidesSubscribing: false,
+               ProvidesBrowsing:    false,
+       }
+}
+
+func (c *Connection) ReadRequestBuilder() model.PlcReadRequestBuilder {
+       return internalModel.NewDefaultPlcReadRequestBuilder(c.fieldHandler, 
NewReader(c.device, c.options))
+}
+
+func (c *Connection) WriteRequestBuilder() model.PlcWriteRequestBuilder {
+       return internalModel.NewDefaultPlcWriteRequestBuilder(c.fieldHandler, 
c.valueHandler, NewWriter(c.device, c.options))
+}
+
+func (c *Connection) SubscriptionRequestBuilder() 
model.PlcSubscriptionRequestBuilder {
+       panic("not implemented")
+}
+
+func (c *Connection) UnsubscriptionRequestBuilder() 
model.PlcUnsubscriptionRequestBuilder {
+       panic("not implemented")
+}
+
+func (c *Connection) BrowseRequestBuilder() model.PlcBrowseRequestBuilder {
+       panic("not implemented")
+}
diff --git a/plc4go/internal/plc4go/simulated/Connection_test.go 
b/plc4go/internal/plc4go/simulated/Connection_test.go
new file mode 100644
index 0000000..6a74b56
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Connection_test.go
@@ -0,0 +1,758 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi"
+       _default "github.com/apache/plc4x/plc4go/internal/plc4go/spi/default"
+       internalModel "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "reflect"
+       "testing"
+       "time"
+)
+
+func TestConnection_Connect(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name         string
+               fields       fields
+               want         plc4go.PlcConnectionConnectResult
+               delayAtLeast time.Duration
+               wantErr      bool
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    false,
+                       },
+                       want: 
_default.NewDefaultPlcConnectionConnectResult(&Connection{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       }, nil),
+                       delayAtLeast: 0,
+                       wantErr:      false,
+               },
+               // If the connection was already connected, the
+               // connection should fail with an error.
+               {
+                       name: "already connected",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       want: 
_default.NewDefaultPlcConnectionConnectResult(&Connection{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       }, nil),
+                       delayAtLeast: 0,
+                       wantErr:      true,
+               },
+               // If the connection should simulate a delay, make sure it 
doesn't
+               // return immediately.
+               {
+                       name: "delayed connected",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options: map[string][]string{
+                                       "connectionDelay": {"1000"},
+                               },
+                               connected: false,
+                       },
+                       want: 
_default.NewDefaultPlcConnectionConnectResult(&Connection{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options: map[string][]string{
+                                       "connectionDelay": {"1000"},
+                               },
+                               connected: true,
+                       }, nil),
+                       delayAtLeast: time.Second * 1,
+                       wantErr:      false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       timeBeforeConnect := time.Now()
+                       connectionChan := c.Connect()
+                       select {
+                       case connectResult := <-connectionChan:
+                               timeAfterConnect := time.Now()
+                               // If an expected delay was defined, check if 
connecting
+                               // took at least this long.
+                               if tt.delayAtLeast > 0 {
+                                       connectionTime := 
timeAfterConnect.Sub(timeBeforeConnect)
+                                       if connectionTime < tt.delayAtLeast {
+                                               
t.Errorf("TestConnection.Connect() connected too fast. Expected at least %v but 
connected after %v", tt.delayAtLeast, connectionTime)
+                                       }
+                               }
+                               // If we wanted an error, but didn't get one or 
the other way around.
+                               if tt.wantErr != (connectResult.GetErr() != 
nil) {
+                                       t.Errorf("TestConnection.Connect() 
hasErr= %v, wantErr %v", connectResult.GetErr() != nil, tt.wantErr)
+                               } else if !tt.wantErr {
+                                       // Check if we're connected.
+                                       if !reflect.DeepEqual(connectResult, 
tt.want) {
+                                               
t.Errorf("TestConnection.Connect() = %v, want %v", connectResult, tt.want)
+                                       }
+                               }
+                       case <-time.After(3 * time.Second):
+                               t.Errorf("TestConnection.Connect() got timeout")
+                       }
+               })
+       }
+}
+
+func TestConnection_Close(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name         string
+               fields       fields
+               want         plc4go.PlcConnectionCloseResult
+               delayAtLeast time.Duration
+               wantErr      bool
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       want: 
_default.NewDefaultPlcConnectionCloseResult(&Connection{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    false,
+                       }, nil),
+                       delayAtLeast: 0,
+                       wantErr:      false,
+               },
+               {
+                       name: "not connected",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    false,
+                       },
+                       want: 
_default.NewDefaultPlcConnectionCloseResult(&Connection{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    false,
+                       }, nil),
+                       delayAtLeast: 0,
+                       wantErr:      true,
+               },
+               {
+                       name: "delayed close",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options: map[string][]string{
+                                       "closingDelay": {"1000"},
+                               },
+                               connected: true,
+                       },
+                       want: 
_default.NewDefaultPlcConnectionCloseResult(&Connection{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options: map[string][]string{
+                                       "closingDelay": {"1000"},
+                               },
+                               connected: false,
+                       }, nil),
+                       delayAtLeast: 1000,
+                       wantErr:      false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       timeBeforeClose := time.Now()
+                       closeChan := c.Close()
+                       select {
+                       case closeResult := <-closeChan:
+                               timeAfterClose := time.Now()
+                               // If an expected delay was defined, check if 
closing
+                               // took at least this long.
+                               if tt.delayAtLeast > 0 {
+                                       connectionTime := 
timeAfterClose.Sub(timeBeforeClose)
+                                       if connectionTime < tt.delayAtLeast {
+                                               
t.Errorf("TestConnection.Close() connected too fast. Expected at least %v but 
connected after %v", tt.delayAtLeast, connectionTime)
+                                       }
+                               }
+                               // If we wanted an error, but didn't get one or 
the other way around.
+                               if tt.wantErr != (closeResult.GetErr() != nil) {
+                                       t.Errorf("TestConnection.Close() 
hasErr= %v, wantErr %v", closeResult.GetErr() != nil, tt.wantErr)
+                               } else if !tt.wantErr {
+                                       if !reflect.DeepEqual(closeResult, 
tt.want) {
+                                               
t.Errorf("TestConnection.Close() = %v, want %v", closeResult, tt.want)
+                                       }
+                               }
+                       case <-time.After(3 * time.Second):
+                               t.Errorf("TestConnection.Close() got timeout")
+                       }
+               })
+       }
+}
+
+func TestConnection_BlockingClose(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name         string
+               fields       fields
+               delayAtLeast time.Duration
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       delayAtLeast: 0,
+               },
+               {
+                       name: "not connected",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    false,
+                       },
+                       delayAtLeast: 0,
+               },
+               {
+                       name: "delayed close",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options: map[string][]string{
+                                       "closingDelay": {"1000"},
+                               },
+                               connected: true,
+                       },
+                       delayAtLeast: 1000,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       timeBeforeClose := time.Now()
+                       executor := func() <-chan bool {
+                               ch := make(chan bool)
+                               go func() {
+                                       c.BlockingClose()
+                                       ch <- true
+                               }()
+                               return ch
+                       }
+                       select {
+                       case <-executor():
+                               timeAfterClose := time.Now()
+                               // If an expected delay was defined, check if 
closing
+                               // took at least this long.
+                               if tt.delayAtLeast > 0 {
+                                       connectionTime := 
timeAfterClose.Sub(timeBeforeClose)
+                                       if connectionTime < tt.delayAtLeast {
+                                               
t.Errorf("TestConnection.Close() connected too fast. Expected at least %v but 
connected after %v", tt.delayAtLeast, connectionTime)
+                                       }
+                               }
+                       case <-time.After(3 * time.Second):
+                               t.Errorf("TestConnection.Close() got timeout")
+                       }
+               })
+       }
+}
+
+func TestConnection_GetMetadata(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   model.PlcConnectionMetadata
+       }{
+               {
+                       name:   "simple",
+                       fields: fields{},
+                       want: _default.DefaultConnectionMetadata{
+                               ConnectionAttributes: map[string]string{
+                                       "connectionDelay": "Delay applied when 
connecting",
+                                       "closingDelay":    "Delay applied when 
closing the connection",
+                                       "pingDelay":       "Delay applied when 
executing a ping operation",
+                                       "readDelay":       "Delay applied when 
executing a read operation",
+                                       "writeDelay":      "Delay applied when 
executing a write operation",
+                               },
+                               ProvidesReading:     true,
+                               ProvidesWriting:     true,
+                               ProvidesSubscribing: false,
+                               ProvidesBrowsing:    false,
+                       },
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       if got := c.GetMetadata(); !reflect.DeepEqual(got, 
tt.want) {
+                               t.Errorf("GetMetadata() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
+
+func TestConnection_IsConnected(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   bool
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       want: true,
+               },
+               {
+                       name: "not connected",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    false,
+                       },
+                       want: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       if got := c.IsConnected(); got != tt.want {
+                               t.Errorf("IsConnected() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
+
+func TestConnection_Ping(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name         string
+               fields       fields
+               want         plc4go.PlcConnectionPingResult
+               delayAtLeast time.Duration
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       want:         
_default.NewDefaultPlcConnectionPingResult(nil),
+                       delayAtLeast: 0,
+               },
+               {
+                       name: "delayed ping",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options: map[string][]string{
+                                       "pingDelay": {"1000"},
+                               },
+                               connected: true,
+                       },
+                       want:         
_default.NewDefaultPlcConnectionPingResult(nil),
+                       delayAtLeast: 1000,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       timeBeforePing := time.Now()
+                       pingChan := c.Ping()
+                       select {
+                       case pingResult := <-pingChan:
+                               timeAfterPing := time.Now()
+                               // If an expected delay was defined, check if 
closing
+                               // took at least this long.
+                               if tt.delayAtLeast > 0 {
+                                       pingTime := 
timeAfterPing.Sub(timeBeforePing)
+                                       if pingTime < tt.delayAtLeast {
+                                               t.Errorf("TestConnection.Ping() 
completed too fast. Expected at least %v but returned after %v", 
tt.delayAtLeast, pingTime)
+                                       }
+                               }
+                               if !reflect.DeepEqual(pingResult, tt.want) {
+                                       t.Errorf("TestConnection.Ping() = %v, 
want %v", pingResult, tt.want)
+                               }
+                       case <-time.After(3 * time.Second):
+                               t.Errorf("TestConnection.Ping() got timeout")
+                       }
+               })
+       }
+}
+
+func TestConnection_BrowseRequestBuilder(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name    string
+               fields  fields
+               wantErr bool
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       wantErr: true,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       defer func() {
+                               if r := recover(); tt.wantErr && r == nil {
+                                       t.Errorf("The code did not panic")
+                               }
+                       }()
+                       c.BrowseRequestBuilder()
+               })
+       }
+}
+
+func TestConnection_ReadRequestBuilder(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   model.PlcReadRequestBuilder
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       want: 
internalModel.NewDefaultPlcReadRequestBuilder(NewFieldHandler(), 
NewReader(NewDevice("hurz"), map[string][]string{})),
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       if got := c.ReadRequestBuilder(); 
!reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("ReadRequestBuilder() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestConnection_SubscriptionRequestBuilder(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name    string
+               fields  fields
+               wantErr bool
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       wantErr: true,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       defer func() {
+                               if r := recover(); tt.wantErr && r == nil {
+                                       t.Errorf("The code did not panic")
+                               }
+                       }()
+                       c.SubscriptionRequestBuilder()
+               })
+       }
+}
+
+func TestConnection_UnsubscriptionRequestBuilder(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name    string
+               fields  fields
+               wantErr bool
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       wantErr: true,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       defer func() {
+                               if r := recover(); tt.wantErr && r == nil {
+                                       t.Errorf("The code did not panic")
+                               }
+                       }()
+                       c.UnsubscriptionRequestBuilder()
+               })
+       }
+}
+
+func TestConnection_WriteRequestBuilder(t *testing.T) {
+       type fields struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+               connected    bool
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   model.PlcWriteRequestBuilder
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               device:       NewDevice("hurz"),
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                               options:      map[string][]string{},
+                               connected:    true,
+                       },
+                       want: 
internalModel.NewDefaultPlcWriteRequestBuilder(NewFieldHandler(), 
NewValueHandler(), NewWriter(NewDevice("hurz"), map[string][]string{})),
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       c := &Connection{
+                               device:       tt.fields.device,
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                               options:      tt.fields.options,
+                               connected:    tt.fields.connected,
+                       }
+                       if got := c.WriteRequestBuilder(); 
!reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("WriteRequestBuilder() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestNewConnection(t *testing.T) {
+       type args struct {
+               device       *Device
+               fieldHandler spi.PlcFieldHandler
+               valueHandler spi.PlcValueHandler
+               options      map[string][]string
+       }
+       tests := []struct {
+               name string
+               args args
+               want *Connection
+       }{
+               // TODO: Add test cases.
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := NewConnection(tt.args.device, 
tt.args.fieldHandler, tt.args.valueHandler, tt.args.options); 
!reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("NewConnection() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
diff --git a/plc4go/internal/plc4go/simulated/Device.go 
b/plc4go/internal/plc4go/simulated/Device.go
new file mode 100644
index 0000000..53d5a30
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Device.go
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       
"github.com/apache/plc4x/plc4go/internal/plc4go/simulated/readwrite/model"
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+       "github.com/rs/zerolog/log"
+       "math/rand"
+)
+
+type Device struct {
+       Name  string
+       State map[SimulatedField]*values.PlcValue
+}
+
+func NewDevice(name string) *Device {
+       return &Device{
+               Name:  name,
+               State: make(map[SimulatedField]*values.PlcValue),
+       }
+}
+
+func (t *Device) Get(field SimulatedField) *values.PlcValue {
+       switch field.FieldType {
+       case FieldState:
+               return t.State[field]
+       case FieldRandom:
+               return t.getRandomValue(field)
+       }
+       return nil
+}
+
+func (t *Device) Set(field SimulatedField, value *values.PlcValue) {
+       switch field.FieldType {
+       case FieldState:
+               t.State[field] = value
+               break
+       case FieldRandom:
+               // TODO: Doesn't really make any sense to write a random
+               break
+       case FieldStdOut:
+               log.Debug().Msgf("TEST PLC STDOUT [%s]: %s", field.Name, 
(*value).GetString())
+               break
+       }
+}
+
+func (t *Device) getRandomValue(field SimulatedField) *values.PlcValue {
+       size := field.GetDataTypeSize().DataTypeSize()
+       data := make([]byte, uint16(size)*field.Quantity)
+       rand.Read(data)
+       readBuffer := utils.NewReadBufferByteBased(data)
+       plcValue, err := model.DataItemParse(readBuffer, 
field.DataTypeSize.String(), field.Quantity)
+       if err != nil {
+               panic("Unable to parse random bytes")
+       }
+       return &plcValue
+}
diff --git a/plc4go/internal/plc4go/simulated/Device_test.go 
b/plc4go/internal/plc4go/simulated/Device_test.go
new file mode 100644
index 0000000..870ece0
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Device_test.go
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       
"github.com/apache/plc4x/plc4go/internal/plc4go/simulated/readwrite/model"
+       values2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+       "github.com/rs/zerolog/log"
+       "reflect"
+       "testing"
+)
+
+func TestDevice_Get(t1 *testing.T) {
+       type fields struct {
+               Name  string
+               State map[SimulatedField]*values.PlcValue
+       }
+       type args struct {
+               field        SimulatedField
+               verifyOutput bool
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               args   args
+               want   *values.PlcValue
+       }{
+               {
+                       name: "simple state",
+                       fields: fields{
+                               Name: "hurz",
+                               State: map[SimulatedField]*values.PlcValue{
+                                       NewSimulatedField(FieldState, 
"boolField", model.SimulatedDataTypeSizes_BOOL, 1): 
ToReference(values2.NewPlcBOOL(true)),
+                               },
+                       },
+                       args: args{
+                               field:        NewSimulatedField(FieldState, 
"boolField", model.SimulatedDataTypeSizes_BOOL, 1),
+                               verifyOutput: true,
+                       },
+                       want: ToReference(values2.NewPlcBOOL(true)),
+               },
+               {
+                       name: "simple random",
+                       fields: fields{
+                               Name:  "hurz",
+                               State: map[SimulatedField]*values.PlcValue{},
+                       },
+                       args: args{
+                               field:        NewSimulatedField(FieldRandom, 
"boolField", model.SimulatedDataTypeSizes_BOOL, 1),
+                               verifyOutput: false,
+                       },
+                       want: ToReference(values2.NewPlcBOOL(true)),
+               },
+               {
+                       name: "simple stdout",
+                       fields: fields{
+                               Name:  "hurz",
+                               State: map[SimulatedField]*values.PlcValue{},
+                       },
+                       args: args{
+                               field:        NewSimulatedField(FieldStdOut, 
"boolField", model.SimulatedDataTypeSizes_BOOL, 1),
+                               verifyOutput: false,
+                       },
+                       want: nil,
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := &Device{
+                               Name:  tt.fields.Name,
+                               State: tt.fields.State,
+                       }
+                       got := t.Get(tt.args.field)
+                       if got != nil {
+                               log.Debug().Msgf("Result: %v", *got)
+                       } else {
+                               log.Debug().Msg("Result: nil")
+                       }
+                       if tt.args.verifyOutput && !reflect.DeepEqual(got, 
tt.want) {
+                               t1.Errorf("Get() = %v, want %v", got, tt.want)
+                       }
+               })
+       }
+}
+
+/*
+ * When first playing around with random values I only got "false" values.
+ * So I added this test in order to verify I'm actually getting random values.
+ */
+func TestDevice_Random(t1 *testing.T) {
+       type fields struct {
+               Name  string
+               State map[SimulatedField]*values.PlcValue
+       }
+       type args struct {
+               field   SimulatedField
+               numRuns int
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               args   args
+               want   *values.PlcValue
+       }{
+               {
+                       name: "simple random",
+                       fields: fields{
+                               Name:  "hurz",
+                               State: map[SimulatedField]*values.PlcValue{},
+                       },
+                       args: args{
+                               field:   NewSimulatedField(FieldRandom, 
"boolField", model.SimulatedDataTypeSizes_BOOL, 1),
+                               numRuns: 1000,
+                       },
+                       want: ToReference(values2.NewPlcBOOL(true)),
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := &Device{
+                               Name:  tt.fields.Name,
+                               State: tt.fields.State,
+                       }
+                       numTrue := 0
+                       numFalse := 0
+                       for i := 0; i < tt.args.numRuns; i++ {
+                               got := t.Get(tt.args.field)
+                               boolValue := (*got).GetBool()
+                               if boolValue {
+                                       numTrue++
+                               } else {
+                                       numFalse++
+                               }
+                       }
+                       if numTrue == 0 || numFalse == 0 {
+                               t1.Errorf("Random doesn't seem to work. In %d 
runs I got %d true and %d false values", tt.args.numRuns, numTrue, numFalse)
+                       } else {
+                               log.Info().Msgf("In %d runs I got %d true and 
%d false values", tt.args.numRuns, numTrue, numFalse)
+                       }
+               })
+       }
+}
+
+func TestDevice_Set(t1 *testing.T) {
+       type fields struct {
+               Name  string
+               State map[SimulatedField]*values.PlcValue
+       }
+       type args struct {
+               field         SimulatedField
+               value         *values.PlcValue
+               shouldBeSaved bool
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               args   args
+       }{
+               {
+                       name: "simple state",
+                       fields: fields{
+                               Name:  "hurz",
+                               State: map[SimulatedField]*values.PlcValue{},
+                       },
+                       args: args{
+                               field:         NewSimulatedField(FieldState, 
"boolField", model.SimulatedDataTypeSizes_BOOL, 1),
+                               value:         
ToReference(values2.NewPlcBOOL(true)),
+                               shouldBeSaved: true,
+                       },
+               },
+               {
+                       name: "simple random",
+                       fields: fields{
+                               Name:  "hurz",
+                               State: map[SimulatedField]*values.PlcValue{},
+                       },
+                       args: args{
+                               field:         NewSimulatedField(FieldRandom, 
"boolField", model.SimulatedDataTypeSizes_BOOL, 1),
+                               value:         
ToReference(values2.NewPlcBOOL(true)),
+                               shouldBeSaved: false,
+                       },
+               },
+               {
+                       name: "simple stdout",
+                       fields: fields{
+                               Name:  "hurz",
+                               State: map[SimulatedField]*values.PlcValue{},
+                       },
+                       args: args{
+                               field:         NewSimulatedField(FieldStdOut, 
"boolField", model.SimulatedDataTypeSizes_BOOL, 1),
+                               value:         
ToReference(values2.NewPlcBOOL(true)),
+                               shouldBeSaved: false,
+                       },
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := &Device{
+                               Name:  tt.fields.Name,
+                               State: tt.fields.State,
+                       }
+                       // It shouldn't exist in the map before
+                       if _, ok := tt.fields.State[tt.args.field]; ok {
+                               t1.Errorf("Value for %v already present in 
map", tt.args.field)
+                       }
+                       t.Set(tt.args.field, tt.args.value)
+                       // It should exist in the map after
+                       if _, ok := tt.fields.State[tt.args.field]; 
tt.args.shouldBeSaved != ok {
+                               if tt.args.shouldBeSaved {
+                                       t1.Errorf("Value for %v not present in 
map (it should)", tt.args.field)
+                               } else {
+                                       t1.Errorf("Value for %v present in map 
(is should not)", tt.args.field)
+                               }
+                       }
+               })
+       }
+}
+
+func TestDevice_getRandomValue(t1 *testing.T) {
+       type fields struct {
+               Name  string
+               State map[SimulatedField]*values.PlcValue
+       }
+       type args struct {
+               field SimulatedField
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               args   args
+               want   *values.PlcValue
+       }{
+               // TODO: Add test cases.
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := &Device{
+                               Name:  tt.fields.Name,
+                               State: tt.fields.State,
+                       }
+                       if got := t.getRandomValue(tt.args.field); 
!reflect.DeepEqual(got, tt.want) {
+                               t1.Errorf("getRandomValue() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestNewDevice(t *testing.T) {
+       type args struct {
+               name string
+       }
+       tests := []struct {
+               name string
+               args args
+               want *Device
+       }{
+               // TODO: Add test cases.
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := NewDevice(tt.args.name); 
!reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("NewDevice() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
+
+func ToReference(value values.PlcValue) *values.PlcValue {
+       return &value
+}
diff --git a/plc4go/internal/plc4go/simulated/Driver.go 
b/plc4go/internal/plc4go/simulated/Driver.go
new file mode 100644
index 0000000..80cf225
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Driver.go
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi/options"
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "github.com/pkg/errors"
+       "net/url"
+)
+
+type Driver struct {
+       fieldHandler FieldHandler
+       valueHandler ValueHandler
+}
+
+func NewDriver() plc4go.PlcDriver {
+       return &Driver{
+               fieldHandler: NewFieldHandler(),
+               valueHandler: NewValueHandler(),
+       }
+}
+
+// GetProtocolCode Get the short code used to identify this driver (As used in 
the connection string)
+func (d *Driver) GetProtocolCode() string {
+       return "simulated"
+}
+
+// GetProtocolName Get a human-readable name for this driver
+func (d *Driver) GetProtocolName() string {
+       return "Simulated PLC4X Datasource"
+}
+
+// GetDefaultTransport If the driver has a default form of transport, provide 
this and make
+// providing the transport code optional in the connection string
+func (d *Driver) GetDefaultTransport() string {
+       return "none"
+}
+
+// CheckQuery Have the driver parse the query string and provide feedback if 
it's not a valid one
+func (d *Driver) CheckQuery(query string) error {
+       _, err := d.fieldHandler.ParseQuery(query)
+       return err
+}
+
+// GetConnection Establishes a connection to a given PLC using the information 
in the connectionString
+func (d *Driver) GetConnection(_ url.URL, _ map[string]transports.Transport, 
options map[string][]string) <-chan plc4go.PlcConnectionConnectResult {
+       connection := NewConnection(NewDevice("test"), d.fieldHandler, 
d.valueHandler, options)
+       return connection.Connect()
+}
+
+// SupportsDiscovery returns true if this driver supports discovery
+// TODO: Actually the connection could support discovery to list up all fields 
in the Device
+func (d *Driver) SupportsDiscovery() bool {
+       return false
+}
+
+func (d *Driver) Discover(_ func(event model.PlcDiscoveryEvent), _ 
...options.WithDiscoveryOption) error {
+       return errors.New("unsupported operation")
+}
diff --git a/plc4go/internal/plc4go/simulated/Driver_test.go 
b/plc4go/internal/plc4go/simulated/Driver_test.go
new file mode 100644
index 0000000..ac92235
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Driver_test.go
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi/options"
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi/transports"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "net/url"
+       "testing"
+       "time"
+)
+
+func TestDriver_CheckQuery(t *testing.T) {
+       type fields struct {
+               fieldHandler FieldHandler
+               valueHandler ValueHandler
+       }
+       type args struct {
+               query string
+       }
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "valid query",
+                       fields: fields{
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                       },
+                       args: args{
+                               query: "STATE/test:UINT[2]",
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       d := &Driver{
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                       }
+                       if err := d.CheckQuery(tt.args.query); (err != nil) != 
tt.wantErr {
+                               t.Errorf("CheckQuery() error = %v, wantErr %v", 
err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func TestDriver_Discover(t *testing.T) {
+       type fields struct {
+               fieldHandler FieldHandler
+               valueHandler ValueHandler
+       }
+       type args struct {
+               callback         func(event model.PlcDiscoveryEvent)
+               discoveryOptions []options.WithDiscoveryOption
+       }
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "discovery fails",
+                       fields: fields{
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                       },
+                       args: args{
+                               // Can all be nil, as the call is expected to 
fail
+                               callback:         nil,
+                               discoveryOptions: nil,
+                       },
+                       wantErr: true,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       d := &Driver{
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                       }
+                       if err := d.Discover(tt.args.callback, 
tt.args.discoveryOptions...); (err != nil) != tt.wantErr {
+                               t.Errorf("Discover() error = %v, wantErr %v", 
err, tt.wantErr)
+                       }
+               })
+       }
+}
+
+func TestDriver_GetConnection(t *testing.T) {
+       type fields struct {
+               fieldHandler FieldHandler
+               valueHandler ValueHandler
+       }
+       type args struct {
+               in0     url.URL
+               in1     map[string]transports.Transport
+               options map[string][]string
+       }
+       tests := []struct {
+               name    string
+               fields  fields
+               args    args
+               wantErr bool
+       }{
+               {
+                       name: "simple no options",
+                       fields: fields{
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                       },
+                       // Input doesn't really matter, as the code simply 
ignores most of it.
+                       args: args{
+                               in0:     url.URL{},
+                               in1:     nil,
+                               options: nil,
+                       },
+                       wantErr: false,
+               },
+               {
+                       name: "simple with options",
+                       fields: fields{
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                       },
+                       // Input doesn't really matter, as the code simply 
ignores most of it.
+                       args: args{
+                               in0: url.URL{},
+                               in1: nil,
+                               options: map[string][]string{
+                                       "testOption": {"testValue"},
+                               },
+                       },
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       d := &Driver{
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                       }
+                       connectionChan := d.GetConnection(tt.args.in0, 
tt.args.in1, tt.args.options)
+                       select {
+                       case connectResult := <-connectionChan:
+                               if tt.wantErr && (connectResult.GetErr() == 
nil) {
+                                       
t.Errorf("PlcConnectionPool.GetConnection() = %v, wantErr %v", 
connectResult.GetErr(), tt.wantErr)
+                               } else if connectResult.GetErr() != nil {
+                                       
t.Errorf("PlcConnectionPool.GetConnection() error = %v, wantErr %v", 
connectResult.GetErr(), tt.wantErr)
+                               }
+                       case <-time.After(3 * time.Second):
+                               t.Errorf("PlcConnectionPool.GetConnection() got 
timeout")
+                       }
+               })
+       }
+}
+
+func TestDriver_GetDefaultTransport(t *testing.T) {
+       type fields struct {
+               fieldHandler FieldHandler
+               valueHandler ValueHandler
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   string
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                       },
+                       want: "none",
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       d := &Driver{
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                       }
+                       if got := d.GetDefaultTransport(); got != tt.want {
+                               t.Errorf("GetDefaultTransport() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestDriver_GetProtocolCode(t *testing.T) {
+       type fields struct {
+               fieldHandler FieldHandler
+               valueHandler ValueHandler
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   string
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                       },
+                       want: "simulated",
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       d := &Driver{
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                       }
+                       if got := d.GetProtocolCode(); got != tt.want {
+                               t.Errorf("GetProtocolCode() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestDriver_GetProtocolName(t *testing.T) {
+       type fields struct {
+               fieldHandler FieldHandler
+               valueHandler ValueHandler
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   string
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                       },
+                       want: "Simulated PLC4X Datasource",
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       d := &Driver{
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                       }
+                       if got := d.GetProtocolName(); got != tt.want {
+                               t.Errorf("GetProtocolName() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestDriver_SupportsDiscovery(t *testing.T) {
+       type fields struct {
+               fieldHandler FieldHandler
+               valueHandler ValueHandler
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   bool
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               fieldHandler: NewFieldHandler(),
+                               valueHandler: NewValueHandler(),
+                       },
+                       want: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       d := &Driver{
+                               fieldHandler: tt.fields.fieldHandler,
+                               valueHandler: tt.fields.valueHandler,
+                       }
+                       if got := d.SupportsDiscovery(); got != tt.want {
+                               t.Errorf("SupportsDiscovery() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestNewDriver(t *testing.T) {
+       tests := []struct {
+               name    string
+               wantErr bool
+       }{
+               {
+                       name:    "simple",
+                       wantErr: false,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       got := NewDriver()
+                       if got == nil && !tt.wantErr {
+                               t.Errorf("NewDriver() error creating")
+                       }
+               })
+       }
+}
diff --git a/plc4go/internal/plc4go/simulated/Field.go 
b/plc4go/internal/plc4go/simulated/Field.go
new file mode 100644
index 0000000..133da1c
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Field.go
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "fmt"
+       
"github.com/apache/plc4x/plc4go/internal/plc4go/simulated/readwrite/model"
+)
+
+type Field interface {
+       GetFieldType() *FieldType
+       GetName() string
+       GetDataTypeSize() *model.SimulatedDataTypeSizes
+}
+
+type SimulatedField struct {
+       FieldType    FieldType
+       Name         string
+       DataTypeSize model.SimulatedDataTypeSizes
+       Quantity     uint16
+}
+
+func NewSimulatedField(fieldType FieldType, name string, dataTypeSize 
model.SimulatedDataTypeSizes, quantity uint16) SimulatedField {
+       return SimulatedField{
+               FieldType:    fieldType,
+               Name:         name,
+               DataTypeSize: dataTypeSize,
+               Quantity:     quantity,
+       }
+}
+
+func (t SimulatedField) GetFieldType() FieldType {
+       return t.FieldType
+}
+
+func (t SimulatedField) GetName() string {
+       return t.Name
+}
+
+func (t SimulatedField) GetDataTypeSize() model.SimulatedDataTypeSizes {
+       return t.DataTypeSize
+}
+
+func (t SimulatedField) GetAddressString() string {
+       return fmt.Sprintf("%s/%s:%s[%d]", t.FieldType.Name(), t.Name, 
t.DataTypeSize.String(), t.Quantity)
+}
+
+func (t SimulatedField) GetTypeName() string {
+       return t.DataTypeSize.String()
+}
+
+func (t SimulatedField) GetQuantity() uint16 {
+       return t.Quantity
+}
diff --git a/plc4go/internal/plc4go/simulated/FieldHandler.go 
b/plc4go/internal/plc4go/simulated/FieldHandler.go
new file mode 100644
index 0000000..de0bfd3
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/FieldHandler.go
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "errors"
+       
"github.com/apache/plc4x/plc4go/internal/plc4go/simulated/readwrite/model"
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi/utils"
+       apiModel "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "regexp"
+       "strconv"
+)
+
+type FieldType uint8
+
+const (
+       FieldRandom FieldType = iota
+       FieldState
+       FieldStdOut
+)
+
+func (e FieldType) Name() string {
+       switch e {
+       case FieldRandom:
+               return "RANDOM"
+       case FieldState:
+               return "STATE"
+       case FieldStdOut:
+               return "STDOUT"
+       default:
+               return "UNKNOWN"
+       }
+}
+
+type FieldHandler struct {
+       simulatedQuery *regexp.Regexp
+}
+
+func NewFieldHandler() FieldHandler {
+       return FieldHandler{
+               simulatedQuery: 
regexp.MustCompile(`^(?P<type>\w+)/(?P<name>[a-zA-Z0-9_\\.]+):(?P<dataType>[a-zA-Z0-9]+)(\[(?P<numElements>\d+)])?$`),
+       }
+}
+
+func (m FieldHandler) ParseQuery(query string) (apiModel.PlcField, error) {
+       if match := utils.GetSubgroupMatches(m.simulatedQuery, query); match != 
nil {
+               fieldTypeName, ok := match["type"]
+               var fieldType FieldType
+               if ok {
+                       switch fieldTypeName {
+                       case "RANDOM":
+                               fieldType = FieldRandom
+                               break
+                       case "STATE":
+                               fieldType = FieldState
+                               break
+                       case "STDOUT":
+                               fieldType = FieldStdOut
+                       default:
+                               return nil, errors.New("unknown field type '" + 
fieldTypeName + "'")
+                       }
+               }
+               fieldName, ok := match["name"]
+               fieldDataTypeName, ok := match["dataType"]
+               var fieldDataType model.SimulatedDataTypeSizes
+               if ok {
+                       fieldDataType = 
model.SimulatedDataTypeSizesByName(fieldDataTypeName)
+                       if fieldDataType == 0 {
+                               return nil, errors.New("unknown field data-type 
'" + fieldDataTypeName + "'")
+                       }
+               }
+               fieldNumElementsText, ok := match["numElements"]
+               var fieldNumElements uint16
+               if ok && len(fieldNumElementsText) > 0 {
+                       num, err := strconv.Atoi(fieldNumElementsText)
+                       if err != nil {
+                               return nil, errors.New("invalid size '" + 
fieldNumElementsText + "'")
+                       }
+                       fieldNumElements = uint16(num)
+               } else {
+                       fieldNumElements = 1
+               }
+               return NewSimulatedField(fieldType, fieldName, fieldDataType, 
fieldNumElements), nil
+       }
+       return nil, errors.New("Invalid address format for address '" + query + 
"'")
+}
diff --git a/plc4go/internal/plc4go/simulated/FieldHandler_test.go 
b/plc4go/internal/plc4go/simulated/FieldHandler_test.go
new file mode 100644
index 0000000..281147a
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/FieldHandler_test.go
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       model2 
"github.com/apache/plc4x/plc4go/internal/plc4go/simulated/readwrite/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "reflect"
+       "testing"
+)
+
+func TestFieldHandler_ParseQuery(t *testing.T) {
+       type args struct {
+               query string
+       }
+       tests := []struct {
+               name    string
+               args    args
+               want    model.PlcField
+               wantErr bool
+       }{
+               {
+                       name: "simple random",
+                       args: args{
+                               query: "RANDOM/test_random:BOOL",
+                       },
+                       want:    NewSimulatedField(FieldRandom, "test_random", 
model2.SimulatedDataTypeSizes_BOOL, 1),
+                       wantErr: false,
+               },
+               {
+                       name: "simple random array",
+                       args: args{
+                               query: "RANDOM/test_random:BOOL[10]",
+                       },
+                       want:    NewSimulatedField(FieldRandom, "test_random", 
model2.SimulatedDataTypeSizes_BOOL, 10),
+                       wantErr: false,
+               },
+               {
+                       name: "simple state",
+                       args: args{
+                               query: "STATE/test_state:BOOL",
+                       },
+                       want:    NewSimulatedField(FieldState, "test_state", 
model2.SimulatedDataTypeSizes_BOOL, 1),
+                       wantErr: false,
+               },
+               {
+                       name: "simple state array",
+                       args: args{
+                               query: "STATE/test_state:BOOL[42]",
+                       },
+                       want:    NewSimulatedField(FieldState, "test_state", 
model2.SimulatedDataTypeSizes_BOOL, 42),
+                       wantErr: false,
+               },
+               {
+                       name: "simple stdout",
+                       args: args{
+                               query: "STDOUT/test_stdout:BOOL",
+                       },
+                       want:    NewSimulatedField(FieldStdOut, "test_stdout", 
model2.SimulatedDataTypeSizes_BOOL, 1),
+                       wantErr: false,
+               },
+               {
+                       name: "simple stdout array",
+                       args: args{
+                               query: "STDOUT/test_stdout:BOOL[23]",
+                       },
+                       want:    NewSimulatedField(FieldStdOut, "test_stdout", 
model2.SimulatedDataTypeSizes_BOOL, 23),
+                       wantErr: false,
+               },
+               {
+                       name: "error invalid type",
+                       args: args{
+                               query: "HURZ/test_stdout:BOOL[23]",
+                       },
+                       want:    nil,
+                       wantErr: true,
+               },
+               {
+                       name: "error invalid name format",
+                       args: args{
+                               query: "RANDOM/test/stdout:BOOL[23]",
+                       },
+                       want:    nil,
+                       wantErr: true,
+               },
+               {
+                       name: "error invalid datatype",
+                       args: args{
+                               query: "RANDOM/test_stdout:HURZ[23]",
+                       },
+                       want:    nil,
+                       wantErr: true,
+               },
+               {
+                       name: "error invalid array size",
+                       args: args{
+                               query: 
"RANDOM/test_stdout:BOOL[999999999999999999999999999999999999]",
+                       },
+                       want:    nil,
+                       wantErr: true,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       m := NewFieldHandler()
+                       got, err := m.ParseQuery(tt.args.query)
+                       if (err != nil) != tt.wantErr {
+                               t.Errorf("ParseQuery() error = %v, wantErr %v", 
err, tt.wantErr)
+                               return
+                       }
+                       if !reflect.DeepEqual(got, tt.want) {
+                               t.Errorf("ParseQuery() got = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
+
+func TestFieldType_Name(t *testing.T) {
+       tests := []struct {
+               name string
+               e    FieldType
+               want string
+       }{
+               {
+                       name: "simple random",
+                       e:    FieldRandom,
+                       want: "RANDOM",
+               },
+               {
+                       name: "simple state",
+                       e:    FieldState,
+                       want: "STATE",
+               },
+               {
+                       name: "simple stdout",
+                       e:    FieldStdOut,
+                       want: "STDOUT",
+               },
+               {
+                       name: "simple stdout",
+                       e:    10,
+                       want: "UNKNOWN",
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := tt.e.Name(); got != tt.want {
+                               t.Errorf("Name() = %v, want %v", got, tt.want)
+                       }
+               })
+       }
+}
diff --git a/plc4go/internal/plc4go/simulated/Field_test.go 
b/plc4go/internal/plc4go/simulated/Field_test.go
new file mode 100644
index 0000000..c4a961a
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Field_test.go
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       
"github.com/apache/plc4x/plc4go/internal/plc4go/simulated/readwrite/model"
+       "reflect"
+       "testing"
+)
+
+func TestNewSimulatedField(t *testing.T) {
+       type args struct {
+               fieldType    FieldType
+               name         string
+               dataTypeSize model.SimulatedDataTypeSizes
+               quantity     uint16
+       }
+       tests := []struct {
+               name string
+               args args
+               want SimulatedField
+       }{
+               {
+                       name: "simple",
+                       args: args{
+                               fieldType:    FieldRandom,
+                               name:         "test",
+                               dataTypeSize: model.SimulatedDataTypeSizes_BOOL,
+                               quantity:     1,
+                       },
+                       want: SimulatedField{
+                               FieldType:    FieldRandom,
+                               Name:         "test",
+                               DataTypeSize: model.SimulatedDataTypeSizes_BOOL,
+                               Quantity:     1,
+                       },
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       if got := NewSimulatedField(tt.args.fieldType, 
tt.args.name, tt.args.dataTypeSize, tt.args.quantity); !reflect.DeepEqual(got, 
tt.want) {
+                               t.Errorf("NewSimulatedField() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestSimulatedField_GetAddressString(t1 *testing.T) {
+       type fields struct {
+               FieldType    FieldType
+               Name         string
+               DataTypeSize model.SimulatedDataTypeSizes
+               Quantity     uint16
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   string
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               FieldType:    FieldRandom,
+                               Name:         "test",
+                               DataTypeSize: model.SimulatedDataTypeSizes_BOOL,
+                               Quantity:     1,
+                       },
+                       want: "RANDOM/test:BOOL[1]",
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := SimulatedField{
+                               FieldType:    tt.fields.FieldType,
+                               Name:         tt.fields.Name,
+                               DataTypeSize: tt.fields.DataTypeSize,
+                               Quantity:     tt.fields.Quantity,
+                       }
+                       if got := t.GetAddressString(); got != tt.want {
+                               t1.Errorf("GetAddressString() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestSimulatedField_GetDataTypeSize(t1 *testing.T) {
+       type fields struct {
+               FieldType    FieldType
+               Name         string
+               DataTypeSize model.SimulatedDataTypeSizes
+               Quantity     uint16
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   model.SimulatedDataTypeSizes
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               FieldType:    FieldRandom,
+                               Name:         "test",
+                               DataTypeSize: model.SimulatedDataTypeSizes_BOOL,
+                               Quantity:     1,
+                       },
+                       want: model.SimulatedDataTypeSizes_BOOL,
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := SimulatedField{
+                               FieldType:    tt.fields.FieldType,
+                               Name:         tt.fields.Name,
+                               DataTypeSize: tt.fields.DataTypeSize,
+                               Quantity:     tt.fields.Quantity,
+                       }
+                       if got := t.GetDataTypeSize(); got != tt.want {
+                               t1.Errorf("GetDataTypeSize() = %v, want %v", 
got, tt.want)
+                       }
+               })
+       }
+}
+
+func TestSimulatedField_GetFieldType(t1 *testing.T) {
+       type fields struct {
+               FieldType    FieldType
+               Name         string
+               DataTypeSize model.SimulatedDataTypeSizes
+               Quantity     uint16
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   FieldType
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               FieldType:    FieldRandom,
+                               Name:         "test",
+                               DataTypeSize: model.SimulatedDataTypeSizes_BOOL,
+                               Quantity:     1,
+                       },
+                       want: FieldRandom,
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := SimulatedField{
+                               FieldType:    tt.fields.FieldType,
+                               Name:         tt.fields.Name,
+                               DataTypeSize: tt.fields.DataTypeSize,
+                               Quantity:     tt.fields.Quantity,
+                       }
+                       if got := t.GetFieldType(); got != tt.want {
+                               t1.Errorf("GetFieldType() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
+
+func TestSimulatedField_GetName(t1 *testing.T) {
+       type fields struct {
+               FieldType    FieldType
+               Name         string
+               DataTypeSize model.SimulatedDataTypeSizes
+               Quantity     uint16
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   string
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               FieldType:    FieldRandom,
+                               Name:         "test",
+                               DataTypeSize: model.SimulatedDataTypeSizes_BOOL,
+                               Quantity:     1,
+                       },
+                       want: "test",
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := SimulatedField{
+                               FieldType:    tt.fields.FieldType,
+                               Name:         tt.fields.Name,
+                               DataTypeSize: tt.fields.DataTypeSize,
+                               Quantity:     tt.fields.Quantity,
+                       }
+                       if got := t.GetName(); got != tt.want {
+                               t1.Errorf("GetName() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
+
+func TestSimulatedField_GetQuantity(t1 *testing.T) {
+       type fields struct {
+               FieldType    FieldType
+               Name         string
+               DataTypeSize model.SimulatedDataTypeSizes
+               Quantity     uint16
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   uint16
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               FieldType:    FieldRandom,
+                               Name:         "test",
+                               DataTypeSize: model.SimulatedDataTypeSizes_BOOL,
+                               Quantity:     1,
+                       },
+                       want: 1,
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := SimulatedField{
+                               FieldType:    tt.fields.FieldType,
+                               Name:         tt.fields.Name,
+                               DataTypeSize: tt.fields.DataTypeSize,
+                               Quantity:     tt.fields.Quantity,
+                       }
+                       if got := t.GetQuantity(); got != tt.want {
+                               t1.Errorf("GetQuantity() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
+
+func TestSimulatedField_GetTypeName(t1 *testing.T) {
+       type fields struct {
+               FieldType    FieldType
+               Name         string
+               DataTypeSize model.SimulatedDataTypeSizes
+               Quantity     uint16
+       }
+       tests := []struct {
+               name   string
+               fields fields
+               want   string
+       }{
+               {
+                       name: "simple",
+                       fields: fields{
+                               FieldType:    FieldRandom,
+                               Name:         "test",
+                               DataTypeSize: model.SimulatedDataTypeSizes_BOOL,
+                               Quantity:     1,
+                       },
+                       want: "BOOL",
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := SimulatedField{
+                               FieldType:    tt.fields.FieldType,
+                               Name:         tt.fields.Name,
+                               DataTypeSize: tt.fields.DataTypeSize,
+                               Quantity:     tt.fields.Quantity,
+                       }
+                       if got := t.GetTypeName(); got != tt.want {
+                               t1.Errorf("GetTypeName() = %v, want %v", got, 
tt.want)
+                       }
+               })
+       }
+}
diff --git a/plc4go/internal/plc4go/simulated/Reader.go 
b/plc4go/internal/plc4go/simulated/Reader.go
new file mode 100644
index 0000000..48d7ab0
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Reader.go
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       model2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+       "strconv"
+       "time"
+)
+
+type Reader struct {
+       device  *Device
+       options map[string][]string
+}
+
+func NewReader(device *Device, options map[string][]string) Reader {
+       return Reader{
+               device:  device,
+               options: options,
+       }
+}
+
+func (r Reader) Read(readRequest model.PlcReadRequest) <-chan 
model.PlcReadRequestResult {
+       ch := make(chan model.PlcReadRequestResult)
+       go func() {
+               // Possibly add a delay.
+               if delayString, ok := r.options["readDelay"]; ok {
+                       if len(delayString) == 1 {
+                               delay, err := strconv.Atoi(delayString[0])
+                               if err == nil {
+                                       time.Sleep(time.Duration(delay) * 
time.Millisecond)
+                               }
+                       }
+               }
+
+               // Process the request
+               responseCodes := make(map[string]model.PlcResponseCode)
+               responseValues := make(map[string]values.PlcValue)
+               for _, fieldName := range readRequest.GetFieldNames() {
+                       field := readRequest.GetField(fieldName)
+                       simulatedField, ok := field.(SimulatedField)
+                       if !ok {
+                               responseCodes[fieldName] = 
model.PlcResponseCode_INVALID_ADDRESS
+                               responseValues[fieldName] = nil
+                       } else {
+                               value := r.device.Get(simulatedField)
+                               if value == nil {
+                                       responseCodes[fieldName] = 
model.PlcResponseCode_NOT_FOUND
+                                       responseValues[fieldName] = nil
+                               } else {
+                                       responseCodes[fieldName] = 
model.PlcResponseCode_OK
+                                       responseValues[fieldName] = *value
+                               }
+                       }
+               }
+
+               // Emit the response
+               ch <- &model2.DefaultPlcReadRequestResult{
+                       Request:  readRequest,
+                       Response: model2.NewDefaultPlcReadResponse(readRequest, 
responseCodes, responseValues),
+                       Err:      nil,
+               }
+       }()
+       return ch
+}
diff --git a/plc4go/internal/plc4go/simulated/Reader_test.go 
b/plc4go/internal/plc4go/simulated/Reader_test.go
new file mode 100644
index 0000000..1a84e63
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Reader_test.go
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "github.com/apache/plc4x/plc4go/internal/plc4go/s7"
+       model4 
"github.com/apache/plc4x/plc4go/internal/plc4go/s7/readwrite/model"
+       model2 
"github.com/apache/plc4x/plc4go/internal/plc4go/simulated/readwrite/model"
+       model3 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+       values2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+       "reflect"
+       "testing"
+       "time"
+)
+
+func TestReader_Read(t *testing.T) {
+       type fields struct {
+               device  *Device
+               options map[string][]string
+       }
+       type args struct {
+               fields     map[string]model.PlcField
+               fieldNames []string
+       }
+       tests := []struct {
+               name         string
+               fields       fields
+               args         args
+               want         model.PlcReadResponse
+               delayAtLeast time.Duration
+       }{
+               {
+                       name: "simple state",
+                       fields: fields{
+                               device: &Device{
+                                       Name: "hurz",
+                                       State: 
map[SimulatedField]*values.PlcValue{
+                                               NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1): 
ToReference(values2.NewPlcBOOL(true)),
+                                       },
+                               },
+                               options: map[string][]string{},
+                       },
+                       args: args{
+                               fields: map[string]model.PlcField{
+                                       "test": NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1),
+                               },
+                               fieldNames: []string{"test"},
+                       },
+                       want: model3.NewDefaultPlcReadResponse(nil,
+                               map[string]model.PlcResponseCode{
+                                       "test": model.PlcResponseCode_OK,
+                               },
+                               map[string]values.PlcValue{
+                                       "test": values2.NewPlcBOOL(true),
+                               }),
+                       delayAtLeast: 0,
+               },
+               {
+                       name: "simple state delayed",
+                       fields: fields{
+                               device: &Device{
+                                       Name: "hurz",
+                                       State: 
map[SimulatedField]*values.PlcValue{
+                                               NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1): 
ToReference(values2.NewPlcBOOL(true)),
+                                       },
+                               },
+                               options: map[string][]string{
+                                       "readDelay": {"1000"},
+                               },
+                       },
+                       args: args{
+                               fields: map[string]model.PlcField{
+                                       "test": NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1),
+                               },
+                               fieldNames: []string{"test"},
+                       },
+                       want: model3.NewDefaultPlcReadResponse(nil,
+                               map[string]model.PlcResponseCode{
+                                       "test": model.PlcResponseCode_OK,
+                               },
+                               map[string]values.PlcValue{
+                                       "test": values2.NewPlcBOOL(true),
+                               }),
+                       delayAtLeast: 1000,
+               },
+               {
+                       name: "state not found",
+                       fields: fields{
+                               device: &Device{
+                                       Name: "hurz",
+                                       State: 
map[SimulatedField]*values.PlcValue{
+                                               NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1): 
ToReference(values2.NewPlcBOOL(true)),
+                                       },
+                               },
+                               options: map[string][]string{},
+                       },
+                       args: args{
+                               fields: map[string]model.PlcField{
+                                       "test": NewSimulatedField(FieldState, 
"lalala", model2.SimulatedDataTypeSizes_BOOL, 1),
+                               },
+                               fieldNames: []string{"test"},
+                       },
+                       want: model3.NewDefaultPlcReadResponse(nil,
+                               map[string]model.PlcResponseCode{
+                                       "test": model.PlcResponseCode_NOT_FOUND,
+                               },
+                               map[string]values.PlcValue{
+                                       "test": nil,
+                               }),
+                       delayAtLeast: 0,
+               },
+               // Passing in a completely wrong type of field.
+               {
+                       name: "invalid field type",
+                       fields: fields{
+                               device: &Device{
+                                       Name: "hurz",
+                                       State: 
map[SimulatedField]*values.PlcValue{
+                                               NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1): 
ToReference(values2.NewPlcBOOL(true)),
+                                       },
+                               },
+                               options: map[string][]string{},
+                       },
+                       args: args{
+                               fields: map[string]model.PlcField{
+                                       "test": s7.PlcField{
+                                               FieldType:   s7.S7Field,
+                                               MemoryArea:  
model4.MemoryArea_DATA_BLOCKS,
+                                               BlockNumber: 1,
+                                               ByteOffset:  1,
+                                               BitOffset:   0,
+                                               NumElements: 1,
+                                               Datatype:    
model4.TransportSize_BOOL,
+                                       },
+                               },
+                               fieldNames: []string{"test"},
+                       },
+                       want: model3.NewDefaultPlcReadResponse(nil,
+                               map[string]model.PlcResponseCode{
+                                       "test": 
model.PlcResponseCode_INVALID_ADDRESS,
+                               },
+                               map[string]values.PlcValue{
+                                       "test": nil,
+                               }),
+                       delayAtLeast: 0,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       r := NewReader(tt.fields.device, tt.fields.options)
+                       readRequest := 
model3.NewDefaultPlcReadRequest(tt.args.fields, tt.args.fieldNames, r, nil)
+                       timeBeforeReadRequest := time.Now()
+                       readResponseChannel := r.Read(readRequest)
+                       select {
+                       case readResponse := <-readResponseChannel:
+                               timeAfterReadRequest := time.Now()
+                               // If an expected delay was defined, check if 
closing
+                               // took at least this long.
+                               if tt.delayAtLeast > 0 {
+                                       pingTime := 
timeAfterReadRequest.Sub(timeBeforeReadRequest)
+                                       if pingTime < tt.delayAtLeast {
+                                               t.Errorf("Reader.Read() 
completed too fast. Expected at least %v but returned after %v", 
tt.delayAtLeast, pingTime)
+                                       }
+                               }
+                               if 
!reflect.DeepEqual(readResponse.GetRequest(), readRequest) {
+                                       t.Errorf("Reader.Read() ReadRequest = 
%v, want %v", readResponse.GetRequest(), readRequest)
+                               }
+                               for _, fieldName := range 
readRequest.GetFieldNames() {
+                                       if 
!reflect.DeepEqual(readResponse.GetResponse().GetResponseCode(fieldName), 
tt.want.GetResponseCode(fieldName)) {
+                                               t.Errorf("Reader.Read() 
PlcResponse.ResponseCode = %v, want %v",
+                                                       
readResponse.GetResponse().GetResponseCode(fieldName), 
tt.want.GetResponseCode(fieldName))
+                                       }
+                                       if 
!reflect.DeepEqual(readResponse.GetResponse().GetValue(fieldName), 
tt.want.GetValue(fieldName)) {
+                                               t.Errorf("Reader.Read() 
PlcResponse.Value = %v, want %v",
+                                                       
readResponse.GetResponse().GetValue(fieldName), tt.want.GetValue(fieldName))
+                                       }
+                               }
+                       case <-time.After(3 * time.Second):
+                               t.Errorf("Reader.Read() got timeout")
+                       }
+               })
+       }
+}
diff --git a/plc4go/internal/plc4go/simulated/ValueHandler.go 
b/plc4go/internal/plc4go/simulated/ValueHandler.go
new file mode 100644
index 0000000..dc1e155
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/ValueHandler.go
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+)
+
+type ValueHandler struct {
+}
+
+func NewValueHandler() ValueHandler {
+       return ValueHandler{}
+}
+
+func (v ValueHandler) NewPlcValue(field model.PlcField, value interface{}) 
(values.PlcValue, error) {
+       //TODO implement me
+       panic("implement me")
+}
diff --git a/plc4go/internal/plc4go/simulated/Writer.go 
b/plc4go/internal/plc4go/simulated/Writer.go
new file mode 100644
index 0000000..e830b7f
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Writer.go
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       model2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "strconv"
+       "time"
+)
+
+type Writer struct {
+       device  *Device
+       options map[string][]string
+}
+
+func NewWriter(device *Device, options map[string][]string) Writer {
+       return Writer{
+               device:  device,
+               options: options,
+       }
+}
+
+func (w Writer) Write(writeRequest model.PlcWriteRequest) <-chan 
model.PlcWriteRequestResult {
+       ch := make(chan model.PlcWriteRequestResult)
+       go func() {
+               // Possibly add a delay.
+               if delayString, ok := w.options["writeDelay"]; ok {
+                       if len(delayString) == 1 {
+                               delay, err := strconv.Atoi(delayString[0])
+                               if err == nil {
+                                       time.Sleep(time.Duration(delay) * 
time.Millisecond)
+                               }
+                       }
+               }
+
+               // Process the request
+               responseCodes := map[string]model.PlcResponseCode{}
+               for _, fieldName := range writeRequest.GetFieldNames() {
+                       field := writeRequest.GetField(fieldName)
+                       simulatedField, ok := field.(SimulatedField)
+                       if !ok {
+                               responseCodes[fieldName] = 
model.PlcResponseCode_INVALID_ADDRESS
+                       } else {
+                               plcValue := writeRequest.GetValue(fieldName)
+                               w.device.Set(simulatedField, &plcValue)
+                               responseCodes[fieldName] = 
model.PlcResponseCode_OK
+                       }
+               }
+
+               // Emit the response
+               ch <- &model2.DefaultPlcWriteRequestResult{
+                       Request:  writeRequest,
+                       Response: 
model2.NewDefaultPlcWriteResponse(writeRequest, responseCodes),
+                       Err:      nil,
+               }
+       }()
+       return ch
+}
diff --git a/plc4go/internal/plc4go/simulated/Writer_test.go 
b/plc4go/internal/plc4go/simulated/Writer_test.go
new file mode 100644
index 0000000..7312446
--- /dev/null
+++ b/plc4go/internal/plc4go/simulated/Writer_test.go
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package simulated
+
+import (
+       "github.com/apache/plc4x/plc4go/internal/plc4go/s7"
+       model4 
"github.com/apache/plc4x/plc4go/internal/plc4go/s7/readwrite/model"
+       model2 
"github.com/apache/plc4x/plc4go/internal/plc4go/simulated/readwrite/model"
+       model3 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/model"
+       values2 "github.com/apache/plc4x/plc4go/internal/plc4go/spi/values"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/values"
+       "reflect"
+       "testing"
+       "time"
+)
+
+func TestWriter_Write(t *testing.T) {
+       type fields struct {
+               device  *Device
+               options map[string][]string
+       }
+       type args struct {
+               fields     map[string]model.PlcField
+               values     map[string]values.PlcValue
+               fieldNames []string
+       }
+       tests := []struct {
+               name         string
+               fields       fields
+               args         args
+               want         model.PlcWriteResponse
+               newState     map[SimulatedField]*values.PlcValue
+               delayAtLeast time.Duration
+       }{
+               {
+                       name: "simple state",
+                       fields: fields{
+                               device: &Device{
+                                       Name:  "hurz",
+                                       State: 
map[SimulatedField]*values.PlcValue{},
+                               },
+                               options: map[string][]string{},
+                       },
+                       args: args{
+                               fields: map[string]model.PlcField{
+                                       "test": NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1),
+                               },
+                               values: map[string]values.PlcValue{
+                                       "test": values2.NewPlcBOOL(true),
+                               },
+                               fieldNames: []string{"test"},
+                       },
+                       want: model3.NewDefaultPlcWriteResponse(nil,
+                               map[string]model.PlcResponseCode{
+                                       "test": model.PlcResponseCode_OK,
+                               }),
+                       newState: map[SimulatedField]*values.PlcValue{
+                               NewSimulatedField(FieldState, "test", 
model2.SimulatedDataTypeSizes_BOOL, 1): ToReference(values2.NewPlcBOOL(true)),
+                       },
+                       delayAtLeast: 0,
+               },
+               {
+                       name: "simple state overwrite",
+                       fields: fields{
+                               device: &Device{
+                                       Name: "hurz",
+                                       State: 
map[SimulatedField]*values.PlcValue{
+                                               NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1): 
ToReference(values2.NewPlcBOOL(true)),
+                                       },
+                               },
+                               options: map[string][]string{},
+                       },
+                       args: args{
+                               fields: map[string]model.PlcField{
+                                       "test": NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1),
+                               },
+                               values: map[string]values.PlcValue{
+                                       "test": values2.NewPlcBOOL(false),
+                               },
+                               fieldNames: []string{"test"},
+                       },
+                       want: model3.NewDefaultPlcWriteResponse(nil,
+                               map[string]model.PlcResponseCode{
+                                       "test": model.PlcResponseCode_OK,
+                               }),
+                       newState: map[SimulatedField]*values.PlcValue{
+                               NewSimulatedField(FieldState, "test", 
model2.SimulatedDataTypeSizes_BOOL, 1): ToReference(values2.NewPlcBOOL(false)),
+                       },
+                       delayAtLeast: 0,
+               },
+               {
+                       name: "simple state delayed",
+                       fields: fields{
+                               device: &Device{
+                                       Name: "hurz",
+                                       State: 
map[SimulatedField]*values.PlcValue{
+                                               NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1): 
ToReference(values2.NewPlcBOOL(true)),
+                                       },
+                               },
+                               options: map[string][]string{
+                                       "writeDelay": {"1000"},
+                               },
+                       },
+                       args: args{
+                               fields: map[string]model.PlcField{
+                                       "test": NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1),
+                               },
+                               values: map[string]values.PlcValue{
+                                       "test": values2.NewPlcBOOL(false),
+                               },
+                               fieldNames: []string{"test"},
+                       },
+                       want: model3.NewDefaultPlcWriteResponse(nil,
+                               map[string]model.PlcResponseCode{
+                                       "test": model.PlcResponseCode_OK,
+                               }),
+                       newState: map[SimulatedField]*values.PlcValue{
+                               NewSimulatedField(FieldState, "test", 
model2.SimulatedDataTypeSizes_BOOL, 1): ToReference(values2.NewPlcBOOL(false)),
+                       },
+                       delayAtLeast: 1000,
+               },
+               // Passing in a completely wrong type of field.
+               {
+                       name: "invalid field type",
+                       fields: fields{
+                               device: &Device{
+                                       Name: "hurz",
+                                       State: 
map[SimulatedField]*values.PlcValue{
+                                               NewSimulatedField(FieldState, 
"test", model2.SimulatedDataTypeSizes_BOOL, 1): 
ToReference(values2.NewPlcBOOL(true)),
+                                       },
+                               },
+                               options: map[string][]string{},
+                       },
+                       args: args{
+                               fields: map[string]model.PlcField{
+                                       "test": s7.PlcField{
+                                               FieldType:   s7.S7Field,
+                                               MemoryArea:  
model4.MemoryArea_DATA_BLOCKS,
+                                               BlockNumber: 1,
+                                               ByteOffset:  1,
+                                               BitOffset:   0,
+                                               NumElements: 1,
+                                               Datatype:    
model4.TransportSize_BOOL,
+                                       },
+                               },
+                               values: map[string]values.PlcValue{
+                                       "test": values2.NewPlcBOOL(false),
+                               },
+                               fieldNames: []string{"test"},
+                       },
+                       want: model3.NewDefaultPlcWriteResponse(nil,
+                               map[string]model.PlcResponseCode{
+                                       "test": 
model.PlcResponseCode_INVALID_ADDRESS,
+                               }),
+                       newState: map[SimulatedField]*values.PlcValue{
+                               NewSimulatedField(FieldState, "test", 
model2.SimulatedDataTypeSizes_BOOL, 1): ToReference(values2.NewPlcBOOL(true)),
+                       },
+                       delayAtLeast: 0,
+               },
+       }
+       for _, tt := range tests {
+               t.Run(tt.name, func(t *testing.T) {
+                       w := NewWriter(tt.fields.device, tt.fields.options)
+                       writeRequest := 
model3.NewDefaultPlcWriteRequest(tt.args.fields, tt.args.fieldNames, 
tt.args.values, w, nil)
+                       timeBeforeWriteRequest := time.Now()
+                       writeResponseChannel := w.Write(writeRequest)
+                       select {
+                       case writeResponse := <-writeResponseChannel:
+                               timeAfterWriteRequest := time.Now()
+                               // If an expected delay was defined, check if 
closing
+                               // took at least this long.
+                               if tt.delayAtLeast > 0 {
+                                       pingTime := 
timeAfterWriteRequest.Sub(timeBeforeWriteRequest)
+                                       if pingTime < tt.delayAtLeast {
+                                               t.Errorf("Writer.Write() 
completed too fast. Expected at least %v but returned after %v", 
tt.delayAtLeast, pingTime)
+                                       }
+                               }
+                               if 
!reflect.DeepEqual(writeResponse.GetRequest(), writeRequest) {
+                                       t.Errorf("Writer.Write() ReadRequest = 
%v, want %v", writeResponse.GetRequest(), writeRequest)
+                               }
+                               for _, fieldName := range 
writeRequest.GetFieldNames() {
+                                       if 
!reflect.DeepEqual(writeResponse.GetResponse().GetResponseCode(fieldName), 
tt.want.GetResponseCode(fieldName)) {
+                                               t.Errorf("Writer.Write() 
PlcResponse.ResponseCode = %v, want %v",
+                                                       
writeResponse.GetResponse().GetResponseCode(fieldName), 
tt.want.GetResponseCode(fieldName))
+                                       }
+                               }
+                               if !reflect.DeepEqual(tt.fields.device.State, 
tt.newState) {
+                                       t.Errorf("Writer.Write() Device State = 
%v, want %v",
+                                               tt.fields.device.State, 
tt.newState)
+                               }
+                       case <-time.After(3 * time.Second):
+                               t.Errorf("Reader.Read() got timeout")
+                       }
+               })
+       }
+}
diff --git a/plc4go/internal/plc4go/utils/pool/PlcConnectionPool.go 
b/plc4go/internal/plc4go/utils/pool/PlcConnectionPool.go
new file mode 100644
index 0000000..f1be9e5
--- /dev/null
+++ b/plc4go/internal/plc4go/utils/pool/PlcConnectionPool.go
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package pool
+
+import (
+       "github.com/apache/plc4x/plc4go/internal/plc4go/spi/default"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go"
+       "github.com/pkg/errors"
+       "github.com/viney-shih/go-lock"
+       "time"
+)
+
+type PlcConnectionPool struct {
+       driverManager plc4go.PlcDriverManager
+
+       // Maximum duration a connection can be used per lease.
+       // If the connection is used for a longer time, it is forcefully 
removed from the client.
+       maxLeaseTime time.Duration
+       maxWaitTime  time.Duration
+
+       poolLock    lock.RWMutex
+       connections map[string]*PooledPlcConnection
+}
+
+func NewPlcConnectionPool(driverManager plc4go.PlcDriverManager) 
*PlcConnectionPool {
+       return NewPlcConnectionPoolWithMaxLeaseTime(driverManager, 
time.Second*20)
+}
+
+func NewPlcConnectionPoolWithMaxLeaseTime(driverManager 
plc4go.PlcDriverManager, maxLeaseTime time.Duration) *PlcConnectionPool {
+       return &PlcConnectionPool{
+               driverManager: driverManager,
+               maxLeaseTime:  maxLeaseTime,
+               maxWaitTime:   maxLeaseTime * 5,
+               poolLock:      lock.NewCASMutex(),
+               connections:   make(map[string]*PooledPlcConnection),
+       }
+}
+
+func (t *PlcConnectionPool) GetConnection(connectionString string) <-chan 
plc4go.PlcConnectionConnectResult {
+       ch := make(chan plc4go.PlcConnectionConnectResult)
+
+       go func() {
+               // Check if we've already got this connection
+               t.poolLock.Lock()
+               defer t.poolLock.Unlock()
+               // Try to get a connection for the given url.
+               // If this returns ok, this means there is already a connection
+               // available, so we'll try to use that. If it fails, we need
+               // to create a completely new connection.
+               if connection, ok := t.connections[connectionString]; ok {
+                       // Use an existing connection
+
+                       // Check if the connection is available.
+                       if connection.state == StateIdle {
+                               // As soon as we have the lock, return the 
connection.
+                               pooledConnection := 
t.connections[connectionString]
+                               ch <- 
_default.NewDefaultPlcConnectionConnectResult(pooledConnection, nil)
+                       } else if connection.state == StateInUse {
+                               // If the connection is currently busy, add the 
new channel to the queue for this connection.
+                               pooledConnection := 
t.connections[connectionString]
+                               pooledConnection.enqueue(ch)
+                       } else {
+                               ch <- 
_default.NewDefaultPlcConnectionConnectResult(nil, errors.New("timeout"))
+                       }
+               } else {
+                       // Create a new connection.
+
+                       pooledPlcConnection := 
NewPooledPlcConnection(connectionString, t)
+                       t.connections[connectionString] = pooledPlcConnection
+
+                       // Initialize the new connection.
+                       connectionResultChan := 
t.driverManager.GetConnection(connectionString)
+
+                       // Allow us to finish this function and return the lock 
quickly
+                       go func() {
+                               // Wait for the connection to be established.
+                               connectionResult := <-connectionResultChan
+
+                               // If the connection was successful, pass the 
active connection into the container.
+                               // If something went wrong, we have to remove 
the connection from the pool and return the error.
+                               if connectionResult.GetErr() == nil {
+                                       // Make sure we have the lock here so 
all following operations will execute atomically.
+                                       pooledPlcConnection.lock.Lock()
+                                       defer pooledPlcConnection.lock.Unlock()
+
+                                       // Inject the real connection into the 
container.
+                                       pooledPlcConnection.activeConnection = 
connectionResult.GetConnection()
+                                       // Mark the connection as being used.
+                                       pooledPlcConnection.state = StateInUse
+
+                                       // Return the pooled connection to the 
client.
+                                       ch <- 
_default.NewDefaultPlcConnectionCloseResult(pooledPlcConnection, nil)
+                               } else {
+                                       // Mark the connection as broken.
+                                       pooledPlcConnection.state = StateInvalid
+
+                                       // Remove the broken connection from 
the pool.
+                                       t.poolLock.Lock()
+                                       defer t.poolLock.Unlock()
+                                       delete(t.connections, connectionString)
+
+                                       // Forward the error to the client.
+                                       ch <- 
_default.NewDefaultPlcConnectionConnectResult(nil, connectionResult.GetErr())
+                               }
+                       }()
+               }
+       }()
+
+       return ch
+}
+
+func (t *PlcConnectionPool) returnConnection(pooledConnection 
*PooledPlcConnection) error {
+       // If the connection is marked as "invalid", destroy it and remove it 
from the pool.
+       if pooledConnection.state == StateInvalid {
+               // At least try to close the invalid connection.
+               pooledConnection.activeConnection.Close()
+
+               // TODO: Either try to reconnect or cancel all waiting 
connections
+
+               // Remove the connection from the pool.
+               t.poolLock.Lock()
+               defer t.poolLock.Unlock()
+               delete(t.connections, pooledConnection.connectionString)
+               return nil
+       }
+
+       pooledConnection.lock.Lock()
+       defer pooledConnection.lock.Unlock()
+       // Check how many others are waiting for this connection.
+       if len(pooledConnection.queue) > 0 {
+               // There are waiting clients, give the connection to the next 
client in the line.
+               next := pooledConnection.queue[0]
+               pooledConnection.queue = pooledConnection.queue[1:]
+               next <- 
_default.NewDefaultPlcConnectionConnectResult(pooledConnection, nil)
+       } else {
+               // Otherwise, just mark the connection as idle.
+               pooledConnection.state = StateIdle
+       }
+
+       return nil
+}
diff --git a/plc4go/internal/plc4go/utils/pool/PlcConnectionPool_test.go 
b/plc4go/internal/plc4go/utils/pool/PlcConnectionPool_test.go
new file mode 100644
index 0000000..919e375
--- /dev/null
+++ b/plc4go/internal/plc4go/utils/pool/PlcConnectionPool_test.go
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package pool
+
+import (
+       "github.com/apache/plc4x/plc4go/internal/plc4go/simulated"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go"
+       "testing"
+       "time"
+)
+
+func TestPlcConnectionPool_GetConnection(t1 *testing.T) {
+       type fields struct {
+               driverManager plc4go.PlcDriverManager
+       }
+       type args struct {
+               connectionString string
+       }
+       tests := []struct {
+               name        string
+               fields      fields
+               args        args
+               wantErr     bool
+               wantTimeout bool
+       }{
+               {name: "simple",
+                       fields: fields{
+                               driverManager: func() plc4go.PlcDriverManager {
+                                       driverManager := 
plc4go.NewPlcDriverManager()
+                                       
driverManager.RegisterDriver(simulated.NewDriver())
+                                       return driverManager
+                               }(),
+                       }, args: args{
+                               connectionString: "simulated://1.2.3.4:42",
+                       },
+                       wantErr:     false,
+                       wantTimeout: false,
+               },
+               {name: "simpleWithTimeout",
+                       fields: fields{
+                               driverManager: func() plc4go.PlcDriverManager {
+                                       driverManager := 
plc4go.NewPlcDriverManager()
+                                       
driverManager.RegisterDriver(simulated.NewDriver())
+                                       return driverManager
+                               }(),
+                       }, args: args{
+                               connectionString: 
"simulated://1.2.3.4:42?connectionDelay=5",
+                       },
+                       wantErr:     false,
+                       wantTimeout: true,
+               },
+       }
+       for _, tt := range tests {
+               t1.Run(tt.name, func(t1 *testing.T) {
+                       t := NewPlcConnectionPool(tt.fields.driverManager)
+                       got := t.GetConnection(tt.args.connectionString)
+                       select {
+                       case connectResult := <-got:
+                               if tt.wantErr && (connectResult.GetErr() == 
nil) {
+                                       
t1.Errorf("PlcConnectionPool.GetConnection() = %v, wantErr %v", 
connectResult.GetErr(), tt.wantErr)
+                               } else if connectResult.GetErr() != nil {
+                                       
t1.Errorf("PlcConnectionPool.GetConnection() error = %v, wantErr %v", 
connectResult.GetErr(), tt.wantErr)
+                               }
+                       case <-time.After(3 * time.Second):
+                               if !tt.wantTimeout {
+                                       
t1.Errorf("PlcConnectionPool.GetConnection() got timeout")
+                               }
+                       }
+               })
+       }
+}
diff --git a/plc4go/internal/plc4go/utils/pool/PooledPlcConnection.go 
b/plc4go/internal/plc4go/utils/pool/PooledPlcConnection.go
new file mode 100644
index 0000000..20765cc
--- /dev/null
+++ b/plc4go/internal/plc4go/utils/pool/PooledPlcConnection.go
@@ -0,0 +1,188 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package pool
+
+import (
+       _default "github.com/apache/plc4x/plc4go/internal/plc4go/spi/default"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go"
+       "github.com/apache/plc4x/plc4go/pkg/plc4go/model"
+       "github.com/viney-shih/go-lock"
+       "time"
+)
+
+type PooledPlcConnectionState int32
+
+const (
+       StateInitialized PooledPlcConnectionState = iota
+       StateIdle
+       StateInUse
+       StateInvalid
+)
+
+type PooledPlcConnection struct {
+       connectionString string
+       // Reference to the pool (used for giving back connection)
+       connectionPool *PlcConnectionPool
+
+       // The lock for manipulating the pools state.
+       lock lock.Mutex
+       // The actual connection being pooled.
+       activeConnection plc4go.PlcConnection
+       // The current state of this connection.
+       state PooledPlcConnectionState
+       // Queue of waiting clients.
+       queue []chan plc4go.PlcConnectionConnectResult
+}
+
+func NewPooledPlcConnection(connectionString string, connectionPool 
*PlcConnectionPool) *PooledPlcConnection {
+       return &PooledPlcConnection{
+               connectionString: connectionString,
+               state:            StateInitialized,
+               lock:             lock.NewCASMutex(),
+               queue:            make([]chan 
plc4go.PlcConnectionConnectResult, 0),
+               connectionPool:   connectionPool,
+       }
+}
+
+func (t *PooledPlcConnection) setActiveConnection(activeConnection 
plc4go.PlcConnection) {
+       t.lock.Lock()
+       defer t.lock.Unlock()
+       t.activeConnection = activeConnection
+}
+
+func (t *PooledPlcConnection) setState(state PooledPlcConnectionState) {
+       t.lock.Lock()
+       defer t.lock.Unlock()
+       t.state = state
+}
+
+func (t *PooledPlcConnection) enqueue(ch chan 
plc4go.PlcConnectionConnectResult) {
+       t.lock.Lock()
+       defer t.lock.Unlock()
+       t.queue = append(t.queue, ch)
+}
+
+func (t *PooledPlcConnection) Connect() <-chan 
plc4go.PlcConnectionConnectResult {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       panic("Called 'Connect' on a pooled connection")
+}
+
+func (t *PooledPlcConnection) BlockingClose() {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+
+       // Call close and wait for the operation to finish.
+       <-t.Close()
+}
+
+func (t *PooledPlcConnection) Close() <-chan plc4go.PlcConnectionCloseResult {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+
+       result := make(chan plc4go.PlcConnectionCloseResult)
+
+       go func() {
+               // Check if the connection is still alive, if it is, put it 
back into the pool
+               pingResults := t.Ping()
+               pingTimeout := time.NewTimer(time.Second * 5)
+               select {
+               case pingResult := <-pingResults:
+                       {
+                               if pingResult.GetErr() != nil {
+                                       // Mark the connection as broken ...
+                                       t.state = StateInvalid
+                               }
+                       }
+               case <-pingTimeout.C:
+                       {
+                               // Mark the connection as broken ...
+                               t.state = StateInvalid
+                       }
+               }
+
+               // Return the connection to the pool and don't actually close 
it.
+               err := t.connectionPool.returnConnection(t)
+
+               // Finish closing the connection.
+               result <- _default.NewDefaultPlcConnectionCloseResult(t, err)
+       }()
+
+       return result
+}
+
+func (t *PooledPlcConnection) IsConnected() bool {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       return t.activeConnection.IsConnected()
+}
+
+func (t *PooledPlcConnection) Ping() <-chan plc4go.PlcConnectionPingResult {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       return t.activeConnection.Ping()
+}
+
+func (t *PooledPlcConnection) GetMetadata() model.PlcConnectionMetadata {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       return t.activeConnection.GetMetadata()
+}
+
+func (t *PooledPlcConnection) ReadRequestBuilder() model.PlcReadRequestBuilder 
{
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       return t.activeConnection.ReadRequestBuilder()
+}
+
+func (t *PooledPlcConnection) WriteRequestBuilder() 
model.PlcWriteRequestBuilder {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       return t.activeConnection.WriteRequestBuilder()
+}
+
+func (t *PooledPlcConnection) SubscriptionRequestBuilder() 
model.PlcSubscriptionRequestBuilder {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       return t.activeConnection.SubscriptionRequestBuilder()
+}
+
+func (t *PooledPlcConnection) UnsubscriptionRequestBuilder() 
model.PlcUnsubscriptionRequestBuilder {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       return t.activeConnection.UnsubscriptionRequestBuilder()
+}
+
+func (t *PooledPlcConnection) BrowseRequestBuilder() 
model.PlcBrowseRequestBuilder {
+       if t.activeConnection == nil {
+               panic("No active connection")
+       }
+       return t.activeConnection.BrowseRequestBuilder()
+}
diff --git a/plc4go/pkg/plc4go/pool/plc_connection_pool.go 
b/plc4go/pkg/plc4go/pool/plc_connection_pool.go
new file mode 100644
index 0000000..1a91b9d
--- /dev/null
+++ b/plc4go/pkg/plc4go/pool/plc_connection_pool.go
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package pool

Reply via email to