Skip to content

Commit

Permalink
refactor(plc4go/ads): Refactoring of the go ADS drier
Browse files Browse the repository at this point in the history
- Made writing of Structs work for ADS
  • Loading branch information
chrisdutz committed Nov 25, 2022
1 parent 76bcb48 commit 1612342
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 9 deletions.
2 changes: 1 addition & 1 deletion plc4go/examples/ads/write/Write.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func main() {
AddTagAddress("value.date", "MAIN.hurz_DATE", values.NewPlcDATE(date)).
AddTagAddress("value-time-of-day", "MAIN.hurz_TIME_OF_DAY", values.NewPlcTIME_OF_DAY(timeOfDay)).
AddTagAddress("value-date-and-time", "MAIN.hurz_DATE_AND_TIME", values.NewPlcDATE_AND_TIME(dateAndTime)).
//AddTagAddress("value-struct", "MAIN.hurz_Struct", values.NewPlcStruct(children)).
AddTagAddress("value-struct", "MAIN.hurz_Struct", values.NewPlcStruct(children)).
Build()
if err != nil {
panic(err)
Expand Down
109 changes: 101 additions & 8 deletions plc4go/internal/ads/ValueHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,14 @@ package ads

import (
"fmt"
"reflect"
"strconv"

"github.com/apache/plc4x/plc4go/pkg/api/model"
apiValues "github.com/apache/plc4x/plc4go/pkg/api/values"
driverModel "github.com/apache/plc4x/plc4go/protocols/ads/readwrite/model"
spiValues "github.com/apache/plc4x/plc4go/spi/values"
"github.com/pkg/errors"
)

type ValueHandler struct {
Expand All @@ -46,10 +50,10 @@ func NewValueHandlerWithDriverContext(driverContext *DriverContext, tagHandler T
}

func (t ValueHandler) NewPlcValue(tag model.PlcTag, value interface{}) (apiValues.PlcValue, error) {
return t.parseType(tag, tag.GetArrayInfo(), value)
return t.parseType(tag, value)
}

func (t ValueHandler) parseType(tag model.PlcTag, arrayInfo []model.ArrayInfo, value interface{}) (apiValues.PlcValue, error) {
func (t ValueHandler) parseType(tag model.PlcTag, value interface{}) (apiValues.PlcValue, error) {
// Resolve the symbolic tag to a direct tag, that has all the important information.
var directTag DirectPlcTag
switch tag.(type) {
Expand All @@ -64,16 +68,105 @@ func (t ValueHandler) parseType(tag model.PlcTag, arrayInfo []model.ArrayInfo, v
directTag = tag.(DirectPlcTag)
}

return t.AdsParseType(directTag.DataType, directTag.DataType.GetArrayInfo(), value)
}

func (t ValueHandler) AdsParseType(datatype driverModel.AdsDataTypeTableEntry, arrayInfo []driverModel.AdsDataTypeArrayInfo, value interface{}) (apiValues.PlcValue, error) {
// Do the normal resolution.
valueType := directTag.GetValueType()
if (arrayInfo != nil) && (len(arrayInfo) > 0) {
return t.ParseListType(directTag, arrayInfo, value)
} else if valueType == apiValues.Struct {
return t.ParseStructType(directTag, value)
return t.AdsParseListType(datatype, arrayInfo, value)
} else if datatype.GetNumChildren() > 0 {
return t.AdsParseStructType(datatype, value)
}
return t.AdsParseSimpleType(datatype, value)
}

func (t ValueHandler) AdsParseListType(dataType driverModel.AdsDataTypeTableEntry, arrayInfo []driverModel.AdsDataTypeArrayInfo, value interface{}) (apiValues.PlcValue, error) {
// We've reached the end of the recursion.
if len(arrayInfo) == 0 {
return t.AdsParseType(dataType, arrayInfo, value)
}

s := reflect.ValueOf(value)
if s.Kind() != reflect.Slice {
return nil, errors.New("couldn't cast value to []interface{}")
}
curValues := make([]interface{}, s.Len())
for i := 0; i < s.Len(); i++ {
curValues[i] = s.Index(i).Interface()
}

curArrayInfo := arrayInfo[0]
restArrayInfo := arrayInfo[1:]

// Check that the current slice has enough values.
if len(curValues) != int(curArrayInfo.GetNumElements()) {
return nil, errors.New("number of actual values " + strconv.Itoa(len(curValues)) +
" doesn't match tag size " + strconv.Itoa(int(curArrayInfo.GetNumElements())))
}

// Actually convert the current array info level.
var plcValues []apiValues.PlcValue
for i := uint32(0); i < curArrayInfo.GetNumElements(); i++ {
curValue := curValues[i]
plcValue, err := t.AdsParseListType(dataType, restArrayInfo, curValue)
if err != nil {
return nil, errors.New("error parsing PlcValue: " + err.Error())
}
plcValues = append(plcValues, plcValue)
}
return t.ParseSimpleType(directTag, value)

return spiValues.NewPlcList(plcValues), nil
}

func (t ValueHandler) ParseStructType(_ model.PlcTag, _ interface{}) (apiValues.PlcValue, error) {
func (t ValueHandler) AdsParseStructType(dataType driverModel.AdsDataTypeTableEntry, value interface{}) (apiValues.PlcValue, error) {
// Unfortunately it seems impossible to cast map[string]apiValues.PlcValue to map[string]interface{}
if plcStruct, ok := value.(spiValues.PlcStruct); ok {
parsedValues := map[string]apiValues.PlcValue{}
childValues := plcStruct.GetStruct()

for _, childTypeEntry := range dataType.GetChildren() {
childName := childTypeEntry.GetPropertyName()
childType := t.driverContext.dataTypeTable[childTypeEntry.GetDataTypeName()]
childArrayInfo := childType.GetArrayInfo()
childValue, ok := childValues[childTypeEntry.GetPropertyName()]
if !ok {
return nil, fmt.Errorf("missing child value named %s", childName)
}
parsedChildValue, err := t.AdsParseType(childType, childArrayInfo, childValue)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("error parsing child %s", childName))
}
parsedValues[childName] = parsedChildValue
}

return spiValues.NewPlcStruct(parsedValues), nil
} else if simpleMap, ok := value.(map[string]interface{}); ok {
parsedValues := map[string]apiValues.PlcValue{}

for _, childTypeEntry := range dataType.GetChildren() {
childName := childTypeEntry.GetPropertyName()
childType := t.driverContext.dataTypeTable[childTypeEntry.GetDataTypeName()]
childArrayInfo := childType.GetArrayInfo()
childValue, ok := simpleMap[childTypeEntry.GetPropertyName()]
if !ok {
return nil, fmt.Errorf("missing child value named %s", childName)
}
parsedChildValue, err := t.AdsParseType(childType, childArrayInfo, childValue)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("error parsing child %s", childName))
}
parsedValues[childName] = parsedChildValue
}

return spiValues.NewPlcStruct(parsedValues), nil
}

return nil, nil
}

func (t ValueHandler) AdsParseSimpleType(dataType driverModel.AdsDataTypeTableEntry, value interface{}) (apiValues.PlcValue, error) {
// Get the PlcValue type for this ads-datatype.
plcValueType := t.driverContext.getDataTypeForDataTypeTableEntry(dataType)
return t.NewPlcValueFromType(plcValueType, value)
}

0 comments on commit 1612342

Please sign in to comment.