You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2021/03/31 10:02:05 UTC

[plc4x] branch develop updated: plc4go: ads first draft of symbolic addressing implemented + Test currently doesn't run due to issues on xml (un)marshalling

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

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


The following commit(s) were added to refs/heads/develop by this push:
     new 49337d9  plc4go: ads first draft of symbolic addressing implemented + Test currently doesn't run due to issues on xml (un)marshalling
49337d9 is described below

commit 49337d9140e2b794f2708e94d77124277b8b2f88
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed Mar 31 12:01:47 2021 +0200

    plc4go: ads first draft of symbolic addressing implemented
    + Test currently doesn't run due to issues on xml (un)marshalling
---
 go.mod                                           |   2 +-
 go.sum                                           |   7 +
 plc4go/cmd/main/drivers/tests/ads_driver_test.go |   2 +-
 plc4go/internal/plc4go/ads/Field.go              | 125 ++++++++++++----
 plc4go/internal/plc4go/ads/FieldHandler.go       |  13 +-
 plc4go/internal/plc4go/ads/Reader.go             | 180 ++++++++++++++++-------
 plc4go/internal/plc4go/ads/Writer.go             |   2 +-
 7 files changed, 239 insertions(+), 92 deletions(-)

diff --git a/go.mod b/go.mod
index de83014..bad7f35 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,6 @@ module github.com/apache/plc4x
 go 1.15
 
 require (
-	github.com/apache/plc4x/plc4go v0.0.0-20210323102244-4bca718b85a1 // indirect
+	github.com/apache/plc4x/plc4go v0.0.0-20210331081511-003c50ba4f7e // indirect
 	github.com/sirupsen/logrus v1.7.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 35506dc..11e6fe6 100644
--- a/go.sum
+++ b/go.sum
@@ -16,20 +16,26 @@ github.com/apache/plc4x/plc4go v0.0.0-20210319203815-34cd8e7cee37 h1:v6058YVP+Cs
 github.com/apache/plc4x/plc4go v0.0.0-20210319203815-34cd8e7cee37/go.mod h1:1mws7SPHWDbyheFlOoxKystKWmq5m4itv1uf/rvMzzQ=
 github.com/apache/plc4x/plc4go v0.0.0-20210323102244-4bca718b85a1 h1:s2kHU276v57Y1t2IeC90g2/Wr5vkEKzliWoGLbndmvU=
 github.com/apache/plc4x/plc4go v0.0.0-20210323102244-4bca718b85a1/go.mod h1:9m7z5psLsDpPfcVPIdd/L9qmn1lCojPLxfhTrvhQwjw=
+github.com/apache/plc4x/plc4go v0.0.0-20210331081511-003c50ba4f7e h1:LFclJ70mgE7gU4ZGLHub4LwBA9TCULXObHlRdrye+2Q=
+github.com/apache/plc4x/plc4go v0.0.0-20210331081511-003c50ba4f7e/go.mod h1:NfO8uGKPGwDxn1GqOb4oNhAtPF7St1A9LRk1J/qSlWU=
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE=
 github.com/elastic/go-licenser v0.3.1/go.mod h1:D8eNQk70FOCVBl3smCGQt/lv7meBeQno2eI1S5apiHQ=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
 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.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
 github.com/icza/bitio v1.0.0/go.mod h1:0jGnlLAx8MKMr9VGnn/4YrvZiprkvBelsVIbA9Jjr9A=
 github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
 github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
 github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
 github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
 github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@@ -76,4 +82,5 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 gotest.tools/gotestsum v1.6.2/go.mod h1:H74H1IvjJE+E/1INpsn2P4+QW0uLDgL/T2sVajPHmTM=
+gotest.tools/gotestsum v1.6.3/go.mod h1:fTR9ZhxC/TLAAx2/WMk/m3TkMB9eEI89gdEzhiRVJT8=
 gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
diff --git a/plc4go/cmd/main/drivers/tests/ads_driver_test.go b/plc4go/cmd/main/drivers/tests/ads_driver_test.go
index 15da0f0..002aefb 100644
--- a/plc4go/cmd/main/drivers/tests/ads_driver_test.go
+++ b/plc4go/cmd/main/drivers/tests/ads_driver_test.go
@@ -30,7 +30,7 @@ func TestAdsDriver(t *testing.T) {
 	testutils.RunDriverTestsuite(t, ads.NewDriver(), "assets/testing/protocols/ads/DriverTestsuite.xml",
 		// TODO: tests assumes proper multi requests which is currently not implemented yet
 		"Multi-element direct read request",
-		// TODO: symbolic addressing not implemented yet
+		// TODO: xml marshaller/unmarshaler produces empty elements which break the tests
 		"Single element symbolic read request",
 		// TODO: symbolic addressing not implemented yet
 		"Single element symbolic read request (Address previously resolved)",
diff --git a/plc4go/internal/plc4go/ads/Field.go b/plc4go/internal/plc4go/ads/Field.go
index c79f746..7b878ca 100644
--- a/plc4go/internal/plc4go/ads/Field.go
+++ b/plc4go/internal/plc4go/ads/Field.go
@@ -30,18 +30,11 @@ import (
 
 type PlcField struct {
 	FieldType        FieldType
-	IndexGroup       uint32
-	IndexOffset      uint32
-	SymbolicAddress  string
 	StringLength     int32
 	NumberOfElements int64
 	Datatype         model2.AdsDataType
 }
 
-func (m PlcField) GetAddressString() string {
-	return fmt.Sprintf("%dx%05d%05d%s%05d%05d:%s", m.FieldType, m.IndexGroup, m.IndexOffset, m.SymbolicAddress, m.StringLength, m.NumberOfElements, m.Datatype.String())
-}
-
 func (m PlcField) GetTypeName() string {
 	return m.FieldType.GetName()
 }
@@ -50,38 +43,41 @@ func (m PlcField) GetQuantity() uint16 {
 	return 1
 }
 
-func NewAdsPlcField(fieldType FieldType, indexGroup uint32, indexOffset uint32, adsDataType model2.AdsDataType, stringLength int32, numberOfElements int64) (model.PlcField, error) {
-	return PlcField{
-		FieldType:        fieldType,
-		IndexGroup:       indexGroup,
-		IndexOffset:      indexOffset,
-		SymbolicAddress:  "",
-		StringLength:     stringLength,
-		NumberOfElements: numberOfElements,
-		Datatype:         adsDataType,
-	}, nil
+type DirectPlcField struct {
+	IndexGroup  uint32
+	IndexOffset uint32
+	PlcField
 }
 
-func NewAdsSymbolicPlcField(fieldType FieldType, symbolicAddress string, adsDataType model2.AdsDataType, stringLength int32, numberOfElements int64) (model.PlcField, error) {
-	return PlcField{
-		FieldType:        fieldType,
-		IndexGroup:       0,
-		IndexOffset:      0,
-		SymbolicAddress:  symbolicAddress,
-		StringLength:     stringLength,
-		NumberOfElements: numberOfElements,
-		Datatype:         adsDataType,
+func (m DirectPlcField) GetAddressString() string {
+	return fmt.Sprintf("%dx%05d%05d%05d%05d:%s", m.FieldType, m.IndexGroup, m.IndexOffset, m.StringLength, m.NumberOfElements, m.Datatype.String())
+}
+
+func newDirectAdsPlcField(indexGroup uint32, indexOffset uint32, adsDataType model2.AdsDataType, stringLength int32, numberOfElements int64) (model.PlcField, error) {
+	fieldType := DirectAdsField
+	if stringLength > 0 {
+		fieldType = DirectAdsStringField
+	}
+	return DirectPlcField{
+		IndexGroup:  indexGroup,
+		IndexOffset: indexOffset,
+		PlcField: PlcField{
+			FieldType:        fieldType,
+			StringLength:     stringLength,
+			NumberOfElements: numberOfElements,
+			Datatype:         adsDataType,
+		},
 	}, nil
 }
 
-func CastToAdsFieldFromPlcField(plcField model.PlcField) (PlcField, error) {
-	if adsField, ok := plcField.(PlcField); ok {
+func castToDirectAdsFieldFromPlcField(plcField model.PlcField) (DirectPlcField, error) {
+	if adsField, ok := plcField.(DirectPlcField); ok {
 		return adsField, nil
 	}
-	return PlcField{}, errors.New("couldn't cast to AdsPlcField")
+	return DirectPlcField{}, errors.New("couldn't cast to AdsPlcField")
 }
 
-func (m PlcField) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+func (m DirectPlcField) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
 	log.Trace().Msg("MarshalXML")
 	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: m.FieldType.GetName()}}); err != nil {
 		return err
@@ -93,11 +89,76 @@ func (m PlcField) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
 	if err := e.EncodeElement(m.IndexOffset, xml.StartElement{Name: xml.Name{Local: "indexOffset"}}); err != nil {
 		return err
 	}
-	if m.SymbolicAddress != "" {
-		if err := e.EncodeElement(m.SymbolicAddress, xml.StartElement{Name: xml.Name{Local: "symbolicAddress"}}); err != nil {
+	if m.StringLength > 0 {
+		if err := e.EncodeElement(m.StringLength, xml.StartElement{Name: xml.Name{Local: "stringLength"}}); err != nil {
 			return err
 		}
 	}
+	if err := e.EncodeElement(m.NumberOfElements, xml.StartElement{Name: xml.Name{Local: "numberOfElements"}}); err != nil {
+		return err
+	}
+	if err := e.EncodeElement(m.Datatype.String(), xml.StartElement{Name: xml.Name{Local: "dataType"}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeToken(xml.EndElement{Name: xml.Name{Local: m.FieldType.GetName()}}); err != nil {
+		return err
+	}
+	return nil
+}
+
+type SymbolicPlcField struct {
+	SymbolicAddress string
+	PlcField
+}
+
+func (m SymbolicPlcField) GetAddressString() string {
+	return fmt.Sprintf("%dx%s%05d%05d:%s", m.FieldType, m.SymbolicAddress, m.StringLength, m.NumberOfElements, m.Datatype.String())
+}
+
+func newAdsSymbolicPlcField(symbolicAddress string, adsDataType model2.AdsDataType, stringLength int32, numberOfElements int64) (model.PlcField, error) {
+	fieldType := SymbolicField
+	if stringLength > 0 {
+		fieldType = SymbolicStringField
+	}
+	return SymbolicPlcField{
+		SymbolicAddress: symbolicAddress,
+		PlcField: PlcField{
+			FieldType:        fieldType,
+			StringLength:     stringLength,
+			NumberOfElements: numberOfElements,
+			Datatype:         adsDataType,
+		},
+	}, nil
+}
+
+func needsResolving(plcField model.PlcField) bool {
+	switch plcField.(type) {
+	case SymbolicPlcField:
+		return true
+	case DirectPlcField:
+		return false
+	default:
+		return false
+	}
+}
+
+func castToSymbolicPlcFieldFromPlcField(plcField model.PlcField) (SymbolicPlcField, error) {
+	if adsField, ok := plcField.(SymbolicPlcField); ok {
+		return adsField, nil
+	}
+	return SymbolicPlcField{}, errors.New("couldn't cast to AdsPlcField")
+}
+
+func (m SymbolicPlcField) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
+	log.Trace().Msg("MarshalXML")
+	if err := e.EncodeToken(xml.StartElement{Name: xml.Name{Local: m.FieldType.GetName()}}); err != nil {
+		return err
+	}
+
+	if err := e.EncodeElement(m.SymbolicAddress, xml.StartElement{Name: xml.Name{Local: "symbolicAddress"}}); err != nil {
+		return err
+	}
 	if m.StringLength > 0 {
 		if err := e.EncodeElement(m.StringLength, xml.StartElement{Name: xml.Name{Local: "stringLength"}}); err != nil {
 			return err
diff --git a/plc4go/internal/plc4go/ads/FieldHandler.go b/plc4go/internal/plc4go/ads/FieldHandler.go
index 1825a62..3bfe85d 100644
--- a/plc4go/internal/plc4go/ads/FieldHandler.go
+++ b/plc4go/internal/plc4go/ads/FieldHandler.go
@@ -100,7 +100,7 @@ func (m FieldHandler) ParseQuery(query string) (apiModel.PlcField, error) {
 			numberOfElements = 1
 		}
 
-		return NewAdsPlcField(DirectAdsStringField, indexGroup, indexOffset, model2.AdsDataTypeByName(match["adsDataType"]), int32(stringLength), int64(numberOfElements))
+		return newDirectAdsPlcField(indexGroup, indexOffset, model2.AdsDataTypeByName(match["adsDataType"]), int32(stringLength), int64(numberOfElements))
 	} else if match := utils.GetSubgroupMatches(m.directAdsField, query); match != nil {
 		var indexGroup uint32
 		if indexGroupHexString := match["indexGroupHex"]; indexGroupHexString != "" {
@@ -137,7 +137,7 @@ func (m FieldHandler) ParseQuery(query string) (apiModel.PlcField, error) {
 			log.Trace().Msg("Falling back to number of elements 1")
 			numberOfElements = 1
 		}
-		return NewAdsPlcField(DirectAdsField, indexGroup, indexOffset, adsDataType, int32(0), int64(numberOfElements))
+		return newDirectAdsPlcField(indexGroup, indexOffset, adsDataType, int32(0), int64(numberOfElements))
 	} else if match := utils.GetSubgroupMatches(m.symbolicAdsStringField, query); match != nil {
 		stringLength, err := strconv.Atoi(match["stringLength"])
 		if err != nil {
@@ -147,13 +147,14 @@ func (m FieldHandler) ParseQuery(query string) (apiModel.PlcField, error) {
 		if err != nil {
 			return nil, errors.Wrap(err, "Error decoding number of elements")
 		}
-		return NewAdsSymbolicPlcField(SymbolicStringField, match["symbolicAddress"], model2.AdsDataTypeByName(match["adsDataType"]), int32(stringLength), int64(numberOfElements))
-	} else if match := utils.GetSubgroupMatches(m.symbolicAdsStringField, query); match != nil {
+		return newAdsSymbolicPlcField(match["symbolicAddress"], model2.AdsDataTypeByName(match["adsDataType"]), int32(stringLength), int64(numberOfElements))
+	} else if match := utils.GetSubgroupMatches(m.symbolicAdsField, query); match != nil {
 		numberOfElements, err := strconv.Atoi(match["numberOfElements"])
 		if err != nil {
-			return nil, errors.Wrap(err, "Error decoding number of elements")
+			log.Trace().Msg("Falling back to number of elements 1")
+			numberOfElements = 1
 		}
-		return NewAdsSymbolicPlcField(SymbolicField, match["symbolicAddress"], model2.AdsDataTypeByName(match["adsDataType"]), int32(0), int64(numberOfElements))
+		return newAdsSymbolicPlcField(match["symbolicAddress"], model2.AdsDataTypeByName(match["adsDataType"]), int32(0), int64(numberOfElements))
 	} else {
 		return nil, errors.Errorf("Invalid address format for address '%s'", query)
 	}
diff --git a/plc4go/internal/plc4go/ads/Reader.go b/plc4go/internal/plc4go/ads/Reader.go
index de73688..a177762 100644
--- a/plc4go/internal/plc4go/ads/Reader.go
+++ b/plc4go/internal/plc4go/ads/Reader.go
@@ -28,6 +28,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/rs/zerolog/log"
 	"math"
+	"sync"
 	"sync/atomic"
 	"time"
 )
@@ -39,6 +40,8 @@ type Reader struct {
 	sourceAmsNetId        readWriteModel.AmsNetId
 	sourceAmsPort         uint16
 	messageCodec          spi.MessageCodec
+	fieldMapping          map[SymbolicPlcField]DirectPlcField
+	mappingLock           sync.Mutex
 }
 
 func NewReader(messageCodec spi.MessageCodec, targetAmsNetId readWriteModel.AmsNetId, targetAmsPort uint16, sourceAmsNetId readWriteModel.AmsNetId, sourceAmsPort uint16) *Reader {
@@ -49,6 +52,7 @@ func NewReader(messageCodec spi.MessageCodec, targetAmsNetId readWriteModel.AmsN
 		sourceAmsNetId:        sourceAmsNetId,
 		sourceAmsPort:         sourceAmsPort,
 		messageCodec:          messageCodec,
+		fieldMapping:          make(map[SymbolicPlcField]DirectPlcField),
 	}
 }
 
@@ -68,7 +72,30 @@ func (m *Reader) Read(readRequest model.PlcReadRequest) <-chan model.PlcReadRequ
 		// If we are requesting only one field, use a
 		fieldName := readRequest.GetFieldNames()[0]
 		field := readRequest.GetField(fieldName)
-		adsField, err := CastToAdsFieldFromPlcField(field)
+		if needsResolving(field) {
+			// TODO: resolve field
+			adsField, err := castToSymbolicPlcFieldFromPlcField(field)
+			if err != nil {
+				result <- model.PlcReadRequestResult{
+					Request:  readRequest,
+					Response: nil,
+					Err:      errors.Wrap(err, "invalid field item type"),
+				}
+				log.Debug().Msgf("Invalid field item type %T", field)
+				return
+			}
+			field, err = m.resolveField(adsField)
+			if err != nil {
+				result <- model.PlcReadRequestResult{
+					Request:  readRequest,
+					Response: nil,
+					Err:      errors.Wrap(err, "invalid field item type"),
+				}
+				log.Debug().Msgf("Invalid field item type %T", field)
+				return
+			}
+		}
+		adsField, err := castToDirectAdsFieldFromPlcField(field)
 		if err != nil {
 			result <- model.PlcReadRequestResult{
 				Request:  readRequest,
@@ -108,67 +135,118 @@ func (m *Reader) Read(readRequest model.PlcReadRequest) <-chan model.PlcReadRequ
 			return
 		}
 
-		// Calculate a new transaction identifier
-		transactionIdentifier := atomic.AddUint32(&m.transactionIdentifier, 1)
-		if transactionIdentifier > math.MaxUint8 {
-			transactionIdentifier = 1
-			atomic.StoreUint32(&m.transactionIdentifier, 1)
-		}
-		log.Debug().Msgf("Calculated transaction identifier %x", transactionIdentifier)
-		userdata.InvokeId = transactionIdentifier
+		m.sendOverTheWire(userdata, readRequest, result)
+	}()
+	return result
+}
 
-		// Assemble the finished tcp paket
-		log.Trace().Msg("Assemble tcp paket")
-		amsTcpPaket := readWriteModel.AmsTCPPacket{
-			Userdata: &userdata,
-		}
+func (m *Reader) sendOverTheWire(userdata readWriteModel.AmsPacket, readRequest model.PlcReadRequest, result chan model.PlcReadRequestResult) {
+	// Calculate a new transaction identifier
+	transactionIdentifier := atomic.AddUint32(&m.transactionIdentifier, 1)
+	if transactionIdentifier > math.MaxUint8 {
+		transactionIdentifier = 1
+		atomic.StoreUint32(&m.transactionIdentifier, 1)
+	}
+	log.Debug().Msgf("Calculated transaction identifier %x", transactionIdentifier)
+	userdata.InvokeId = transactionIdentifier
 
-		// Send the TCP Paket over the wire
-		log.Trace().Msg("Send TCP Paket")
-		if err = m.messageCodec.SendRequest(
-			amsTcpPaket,
-			func(message interface{}) bool {
-				paket := readWriteModel.CastAmsTCPPacket(message)
-				return paket.Userdata.InvokeId == transactionIdentifier
-			},
-			func(message interface{}) error {
-				// Convert the response into an amsTcpPaket
-				log.Trace().Msg("convert response to amsTcpPaket")
-				amsTcpPaket := readWriteModel.CastAmsTCPPacket(message)
-				// Convert the ads response into a PLC4X response
-				log.Trace().Msg("convert response to PLC4X response")
-				readResponse, err := m.ToPlc4xReadResponse(*amsTcpPaket, readRequest)
+	// Assemble the finished tcp paket
+	log.Trace().Msg("Assemble tcp paket")
+	amsTcpPaket := readWriteModel.AmsTCPPacket{
+		Userdata: &userdata,
+	}
 
-				if err != nil {
-					result <- model.PlcReadRequestResult{
-						Request: readRequest,
-						Err:     errors.Wrap(err, "Error decoding response"),
-					}
-					// TODO: should we return the error here?
-					return nil
-				}
-				result <- model.PlcReadRequestResult{
-					Request:  readRequest,
-					Response: readResponse,
-				}
-				return nil
-			},
-			func(err error) error {
+	// Send the TCP Paket over the wire
+	log.Trace().Msg("Send TCP Paket")
+	if err := m.messageCodec.SendRequest(
+		amsTcpPaket,
+		func(message interface{}) bool {
+			paket := readWriteModel.CastAmsTCPPacket(message)
+			return paket.Userdata.InvokeId == transactionIdentifier
+		},
+		func(message interface{}) error {
+			// Convert the response into an amsTcpPaket
+			log.Trace().Msg("convert response to amsTcpPaket")
+			amsTcpPaket := readWriteModel.CastAmsTCPPacket(message)
+			// Convert the ads response into a PLC4X response
+			log.Trace().Msg("convert response to PLC4X response")
+			readResponse, err := m.ToPlc4xReadResponse(*amsTcpPaket, readRequest)
+
+			if err != nil {
 				result <- model.PlcReadRequestResult{
 					Request: readRequest,
-					Err:     errors.Wrap(err, "got timeout while waiting for response"),
+					Err:     errors.Wrap(err, "Error decoding response"),
 				}
+				// TODO: should we return the error here?
 				return nil
-			},
-			time.Second*1); err != nil {
+			}
 			result <- model.PlcReadRequestResult{
 				Request:  readRequest,
-				Response: nil,
-				Err:      errors.Wrap(err, "error sending message"),
+				Response: readResponse,
 			}
+			return nil
+		},
+		func(err error) error {
+			result <- model.PlcReadRequestResult{
+				Request: readRequest,
+				Err:     errors.Wrap(err, "got timeout while waiting for response"),
+			}
+			return nil
+		},
+		time.Second*1); err != nil {
+		result <- model.PlcReadRequestResult{
+			Request:  readRequest,
+			Response: nil,
+			Err:      errors.Wrap(err, "error sending message"),
 		}
+	}
+}
+
+func (m *Reader) resolveField(symbolicField SymbolicPlcField) (DirectPlcField, error) {
+	if directPlcField, ok := m.fieldMapping[symbolicField]; ok {
+		return directPlcField, nil
+	}
+	m.mappingLock.Lock()
+	defer m.mappingLock.Unlock()
+	// In case a previous one has already
+	if directPlcField, ok := m.fieldMapping[symbolicField]; ok {
+		return directPlcField, nil
+	}
+	userdata := readWriteModel.AmsPacket{
+		TargetAmsNetId: &m.targetAmsNetId,
+		TargetAmsPort:  m.targetAmsPort,
+		SourceAmsNetId: &m.sourceAmsNetId,
+		SourceAmsPort:  m.sourceAmsPort,
+		CommandId:      readWriteModel.CommandId_ADS_READ_WRITE,
+		State:          readWriteModel.NewState(false, false, false, false, false, true, false, false, false),
+		ErrorCode:      0,
+		InvokeId:       0,
+		Data:           nil,
+	}
+	userdata.Data = readWriteModel.NewAdsReadWriteRequest(
+		uint32(readWriteModel.ReservedIndexGroups_ADSIGRP_SYM_HNDBYNAME),
+		0,
+		4,
+		nil,
+		utils.ByteArrayToInt8Array([]byte(symbolicField.SymbolicAddress+"\000")),
+	)
+	result := make(chan model.PlcReadRequestResult)
+	go func() {
+		m.sendOverTheWire(userdata, nil, result)
 	}()
-	return result
+	response := <-result
+	if response.Err != nil {
+		log.Debug().Err(response.Err).Msg("Error during resolve")
+		return DirectPlcField{}, response.Err
+	}
+	handle := response.Response.GetValue(response.Response.GetFieldNames()[0]).GetUint32()
+	directPlcField := DirectPlcField{
+		IndexGroup:  uint32(readWriteModel.ReservedIndexGroups_ADSIGRP_SYM_VALBYHND),
+		IndexOffset: handle,
+		PlcField:    symbolicField.PlcField,
+	}
+	m.fieldMapping[symbolicField] = directPlcField
+	return directPlcField, nil
 }
 
 func (m *Reader) ToPlc4xReadResponse(amsTcpPaket readWriteModel.AmsTCPPacket, readRequest model.PlcReadRequest) (model.PlcReadResponse, error) {
@@ -185,7 +263,7 @@ func (m *Reader) ToPlc4xReadResponse(amsTcpPaket readWriteModel.AmsTCPPacket, re
 	// Get the field from the request
 	log.Trace().Msg("get a field from request")
 	fieldName := readRequest.GetFieldNames()[0]
-	field, err := CastToAdsFieldFromPlcField(readRequest.GetField(fieldName))
+	field, err := castToDirectAdsFieldFromPlcField(readRequest.GetField(fieldName))
 	if err != nil {
 		return nil, errors.Wrap(err, "error casting to ads-field")
 	}
diff --git a/plc4go/internal/plc4go/ads/Writer.go b/plc4go/internal/plc4go/ads/Writer.go
index 253d91c..3525e7b 100644
--- a/plc4go/internal/plc4go/ads/Writer.go
+++ b/plc4go/internal/plc4go/ads/Writer.go
@@ -67,7 +67,7 @@ func (m Writer) Write(writeRequest model.PlcWriteRequest) <-chan model.PlcWriteR
 
 		// Get the ads field instance from the request
 		field := writeRequest.GetField(fieldName)
-		adsField, err := CastToAdsFieldFromPlcField(field)
+		adsField, err := castToDirectAdsFieldFromPlcField(field)
 		if err != nil {
 			result <- model.PlcWriteRequestResult{
 				Request:  writeRequest,