From b076cf9e8f6526ccaae6d05ad4e5ef8a1b2608c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20R=C3=BChl?= Date: Thu, 29 Aug 2024 13:10:57 +0200 Subject: [PATCH] feat(plc4go/bacnetip): add sequence support --- plc4go/internal/bacnetip/constructeddata.go | 33 +++++-- .../internal/bacnetip/primitivedata_Atomic.go | 12 ++- .../bacnetip/primitivedata_BitString.go | 11 ++- .../bacnetip/primitivedata_Boolean.go | 14 ++- .../bacnetip/primitivedata_CharacterString.go | 14 ++- .../internal/bacnetip/primitivedata_Date.go | 14 ++- .../internal/bacnetip/primitivedata_Double.go | 14 ++- .../bacnetip/primitivedata_Enumeration.go | 14 ++- .../bacnetip/primitivedata_Integer.go | 14 ++- .../internal/bacnetip/primitivedata_Null.go | 26 +++-- .../primitivedata_ObjectIdentifier.go | 14 ++- .../bacnetip/primitivedata_OctetString.go | 14 ++- .../internal/bacnetip/primitivedata_Real.go | 28 ++++-- .../internal/bacnetip/primitivedata_Time.go | 14 ++- .../bacnetip/primitivedata_Unsigned.go | 28 ++++-- .../bacnetip/primitivedata_Unsigned16.go | 28 ++++-- .../bacnetip/primitivedata_Unsigned8.go | 28 ++++-- .../tests/test_constructed_data/helpers.go | 82 ++++++++++++++- .../test_sequence_test.go | 99 +++++++++++++++++++ 19 files changed, 413 insertions(+), 88 deletions(-) diff --git a/plc4go/internal/bacnetip/constructeddata.go b/plc4go/internal/bacnetip/constructeddata.go index f6781b7ae33..32a2d9e243a 100644 --- a/plc4go/internal/bacnetip/constructeddata.go +++ b/plc4go/internal/bacnetip/constructeddata.go @@ -37,35 +37,49 @@ func init() { type Element interface { GetName() string - GetKlass() func(Args, KWArgs) (interface{ Encode(Arg) error }, error) + GetKlass() func(Args, KWArgs) (ElementKlass, error) GetContext() *int IsOptional() bool Encode(tagList Arg) error } +type ElementKlass interface { + Encode(Arg) error + GetAppTag() readWriteModel.BACnetDataType +} + // TODO: finish type _Element struct { Name string - Klass func(Args, KWArgs) (interface{ Encode(Arg) error }, error) + Klass func(Args, KWArgs) (ElementKlass, error) Context *int Optional bool } -func NewElement(name string, klass func(Args, KWArgs) (interface{ Encode(Arg) error }, error)) Element { +func NewElement(name string, klass func(Args, KWArgs) (ElementKlass, error), opts ...func(*_Element)) Element { e := &_Element{ Name: name, Klass: klass, } + for _, opt := range opts { + opt(e) + } return e } var _ Element = (*_Element)(nil) +func WithElementOptional(optional bool) func(*_Element) { + return func(e *_Element) { + e.Optional = optional + } +} + func (e *_Element) GetName() string { return e.Name } -func (e *_Element) GetKlass() func(Args, KWArgs) (interface{ Encode(Arg) error }, error) { +func (e *_Element) GetKlass() func(Args, KWArgs) (ElementKlass, error) { return e.Klass } @@ -309,21 +323,24 @@ func (a *Sequence) Decode(arg Arg) error { } else if isAtomic { // convert it to application encoding if context := element.GetContext(); context != nil { - if tag.GetTagClass() != readWriteModel.TagClass_CONTEXT_SPECIFIC_TAGS { // TODO: store the application tag klass into Boolean for example so we can inspect that here + if tag.GetTagClass() != readWriteModel.TagClass_CONTEXT_SPECIFIC_TAGS && tag.GetTagNumber() != uint(*context) { if !element.IsOptional() { - return errors.Errorf("%s expected context tag %d", element.GetName(), context) + return errors.Errorf("%s expected context tag %d", element.GetName(), *context) } else { // TODO: we don't do this //a.attr[element.GetName()] = nil continue } } - tag, err = tag.ContextToApp(uint(readWriteModel.BACnetDataType_BOOLEAN)) // TODO: store the application tag klass into Boolean for example so we can inspect that here + atomicTag := tag.(interface { + GetAppTag() readWriteModel.BACnetDataType + }) + tag, err = tag.ContextToApp(uint(atomicTag.GetAppTag())) if err != nil { return errors.Wrap(err, "error converting tag") } } else { - if tag.GetTagClass() != readWriteModel.TagClass_APPLICATION_TAGS { // TODO: store the application tag klass into Boolean for example so we can inspect that here + if tag.GetTagClass() != readWriteModel.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(elementKlass.GetAppTag()) { if !element.IsOptional() { return errors.Errorf("%s expected context tag %d", element.GetName(), context) } else { diff --git a/plc4go/internal/bacnetip/primitivedata_Atomic.go b/plc4go/internal/bacnetip/primitivedata_Atomic.go index 0096f9d0a2b..8d67ae783c0 100644 --- a/plc4go/internal/bacnetip/primitivedata_Atomic.go +++ b/plc4go/internal/bacnetip/primitivedata_Atomic.go @@ -19,7 +19,11 @@ package bacnetip -import "cmp" +import ( + "cmp" + + readWriteModel "github.com/apache/plc4x/plc4go/protocols/bacnetip/readwrite/model" +) type ComparableAndOrdered interface { comparable @@ -48,6 +52,8 @@ type Atomic[T ComparableAndOrdered] struct { AtomicContract[T] atomicRequirements AtomicRequirements + _appTag readWriteModel.BACnetDataType + value T } @@ -68,6 +74,10 @@ func (a *Atomic[T]) isAtomic() bool { return true } +func (a *Atomic[T]) GetAppTag() readWriteModel.BACnetDataType { + return a._appTag +} + func (a *Atomic[T]) Compare(other any) int { otherValue := other.(AtomicContract[T]).GetValue() // now compare the values diff --git a/plc4go/internal/bacnetip/primitivedata_BitString.go b/plc4go/internal/bacnetip/primitivedata_BitString.go index ca3c6f922c9..6b2669a55db 100644 --- a/plc4go/internal/bacnetip/primitivedata_BitString.go +++ b/plc4go/internal/bacnetip/primitivedata_BitString.go @@ -36,6 +36,8 @@ type BitStringExtension interface { } type BitString struct { + _appTag model.BACnetDataType + bitStringExtension BitStringExtension value []bool } @@ -46,6 +48,7 @@ func NewBitString(args Args) (*BitString, error) { func NewBitStringWithExtension(bitStringExtension BitStringExtension, args Args) (*BitString, error) { b := &BitString{ + _appTag: model.BACnetDataType_BIT_STRING, bitStringExtension: bitStringExtension, } if len(args) == 0 { @@ -92,12 +95,16 @@ func NewBitStringWithExtension(bitStringExtension BitStringExtension, args Args) return b, nil } +func (b *BitString) GetAppTag() model.BACnetDataType { + return b._appTag +} + func (b *BitString) Decode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_BIT_STRING) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(b._appTag) { return errors.New("bit string application tag required") } if len(tag.GetTagData()) == 0 { @@ -157,7 +164,7 @@ func (b *BitString) Encode(arg Arg) error { data = append(data, x) } - tag.setAppData(uint(model.BACnetDataType_BIT_STRING), data) + tag.setAppData(uint(b._appTag), data) return nil } diff --git a/plc4go/internal/bacnetip/primitivedata_Boolean.go b/plc4go/internal/bacnetip/primitivedata_Boolean.go index 3a31fbe138b..2e942c8f1ef 100644 --- a/plc4go/internal/bacnetip/primitivedata_Boolean.go +++ b/plc4go/internal/bacnetip/primitivedata_Boolean.go @@ -29,10 +29,14 @@ import ( type Boolean struct { *Atomic[int] //Note we need int as bool can't be used + + _appTag model.BACnetDataType } func NewBoolean(arg Arg) (*Boolean, error) { - b := &Boolean{} + b := &Boolean{ + _appTag: model.BACnetDataType_BOOLEAN, + } b.Atomic = NewAtomic[int](b) if arg == nil { @@ -66,12 +70,16 @@ func NewBoolean(arg Arg) (*Boolean, error) { return b, nil } +func (b *Boolean) GetAppTag() model.BACnetDataType { + return b._appTag +} + func (b *Boolean) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } //TODO: move tag number into member variable - tag.set(NewArgs(model.TagClass_APPLICATION_TAGS, model.BACnetDataType_BOOLEAN, b.value, []byte{})) + tag.set(NewArgs(model.TagClass_APPLICATION_TAGS, b._appTag, b.value, []byte{})) return nil } @@ -80,7 +88,7 @@ func (b *Boolean) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_BOOLEAN) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(b._appTag) { return errors.New("boolean application tag required") } if tag.GetTagLvt() > 1 { diff --git a/plc4go/internal/bacnetip/primitivedata_CharacterString.go b/plc4go/internal/bacnetip/primitivedata_CharacterString.go index 411b08bb847..8527d53d315 100644 --- a/plc4go/internal/bacnetip/primitivedata_CharacterString.go +++ b/plc4go/internal/bacnetip/primitivedata_CharacterString.go @@ -31,12 +31,16 @@ type CharacterString struct { *Atomic[string] *CommonMath + _appTag model.BACnetDataType + strEncoding byte strValue []byte } func NewCharacterString(arg Arg) (*CharacterString, error) { - c := &CharacterString{} + c := &CharacterString{ + _appTag: model.BACnetDataType_CHARACTER_STRING, + } c.Atomic = NewAtomic[string](c) if arg == nil { @@ -63,12 +67,16 @@ func NewCharacterString(arg Arg) (*CharacterString, error) { return c, nil } +func (c *CharacterString) GetAppTag() model.BACnetDataType { + return c._appTag +} + func (c *CharacterString) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - tag.setAppData(uint(model.BACnetDataType_CHARACTER_STRING), append([]byte{c.strEncoding}, c.strValue...)) + tag.setAppData(uint(c._appTag), append([]byte{c.strEncoding}, c.strValue...)) return nil } @@ -77,7 +85,7 @@ func (c *CharacterString) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_CHARACTER_STRING) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(c._appTag) { return errors.New("CharacterString application tag required") } if len(tag.GetTagData()) == 0 { diff --git a/plc4go/internal/bacnetip/primitivedata_Date.go b/plc4go/internal/bacnetip/primitivedata_Date.go index 41708e0ef02..3d33d185722 100644 --- a/plc4go/internal/bacnetip/primitivedata_Date.go +++ b/plc4go/internal/bacnetip/primitivedata_Date.go @@ -77,10 +77,14 @@ type DateTuple struct { type Date struct { value DateTuple + + _appTag model.BACnetDataType } func NewDate(arg Arg, args Args) (*Date, error) { - d := &Date{} + d := &Date{ + _appTag: model.BACnetDataType_DATE, + } year := 255 if len(args) > 0 { year = args[0].(int) @@ -245,6 +249,10 @@ func NewDate(arg Arg, args Args) (*Date, error) { return d, nil } +func (d *Date) GetAppTag() model.BACnetDataType { + return d._appTag +} + func (d *Date) calcDayOfWeek() { year, month, day, dayOfWeek := d.value.Year, d.value.Month, d.value.Day, d.value.DayOfWeek @@ -281,7 +289,7 @@ func (d *Date) Encode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - tag.setAppData(uint(model.BACnetDataType_DATE), []byte{byte(d.value.Year), byte(d.value.Month), byte(d.value.Day), byte(d.value.DayOfWeek)}) + tag.setAppData(uint(d._appTag), []byte{byte(d.value.Year), byte(d.value.Month), byte(d.value.Day), byte(d.value.DayOfWeek)}) return nil } @@ -291,7 +299,7 @@ func (d *Date) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_DATE) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(d._appTag) { return errors.New("Date application tag required") } if len(tag.GetTagData()) != 4 { diff --git a/plc4go/internal/bacnetip/primitivedata_Double.go b/plc4go/internal/bacnetip/primitivedata_Double.go index e1edd4602d2..d1fc6f438e7 100644 --- a/plc4go/internal/bacnetip/primitivedata_Double.go +++ b/plc4go/internal/bacnetip/primitivedata_Double.go @@ -32,10 +32,14 @@ import ( type Double struct { *Atomic[float64] *CommonMath + + _appTag model.BACnetDataType } func NewDouble(arg Arg) (*Double, error) { - b := &Double{} + b := &Double{ + _appTag: model.BACnetDataType_DOUBLE, + } b.Atomic = NewAtomic[float64](b) if arg == nil { @@ -63,6 +67,10 @@ func NewDouble(arg Arg) (*Double, error) { return b, nil } +func (d *Double) GetAppTag() model.BACnetDataType { + return d._appTag +} + func (d *Double) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { @@ -70,7 +78,7 @@ func (d *Double) Encode(arg Arg) error { } var _b = make([]byte, 8) binary.BigEndian.PutUint64(_b, math.Float64bits(d.value)) - tag.setAppData(uint(model.BACnetDataType_DOUBLE), _b) + tag.setAppData(uint(d._appTag), _b) return nil } @@ -79,7 +87,7 @@ func (d *Double) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_DOUBLE) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(d._appTag) { return errors.New("Double application tag required") } if len(tag.GetTagData()) != 8 { diff --git a/plc4go/internal/bacnetip/primitivedata_Enumeration.go b/plc4go/internal/bacnetip/primitivedata_Enumeration.go index f7738990352..da67326fbf2 100644 --- a/plc4go/internal/bacnetip/primitivedata_Enumeration.go +++ b/plc4go/internal/bacnetip/primitivedata_Enumeration.go @@ -51,13 +51,17 @@ type Enumerated struct { *Atomic[uint64] EnumeratedContract + _appTag model.BACnetDataType + _xlateTable map[any]any valueString string } func NewEnumerated(args ...any) (*Enumerated, error) { - e := &Enumerated{} + e := &Enumerated{ + _appTag: model.BACnetDataType_ENUMERATED, + } e.EnumeratedContract = e e.Atomic = NewAtomic[uint64](e) @@ -131,6 +135,10 @@ func NewEnumerated(args ...any) (*Enumerated, error) { return e, nil } +func (e *Enumerated) GetAppTag() model.BACnetDataType { + return e._appTag +} + func (e *Enumerated) GetEnumerations() map[string]uint64 { return make(map[string]uint64) } @@ -191,7 +199,7 @@ func (e *Enumerated) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_ENUMERATED) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(e._appTag) { return errors.New("bit string application tag required") } if len(tag.GetTagData()) == 0 { @@ -234,7 +242,7 @@ func (e *Enumerated) Encode(arg Arg) error { } // encode the tag - tag.setAppData(uint(model.BACnetDataType_ENUMERATED), data) + tag.setAppData(uint(e._appTag), data) return nil } diff --git a/plc4go/internal/bacnetip/primitivedata_Integer.go b/plc4go/internal/bacnetip/primitivedata_Integer.go index e83a428df6c..bfedd163025 100644 --- a/plc4go/internal/bacnetip/primitivedata_Integer.go +++ b/plc4go/internal/bacnetip/primitivedata_Integer.go @@ -32,10 +32,14 @@ import ( type Integer struct { *Atomic[int32] *CommonMath + + _appTag model.BACnetDataType } func NewInteger(arg Arg) (*Integer, error) { - i := &Integer{} + i := &Integer{ + _appTag: model.BACnetDataType_SIGNED_INTEGER, + } i.Atomic = NewAtomic[int32](i) if arg == nil { @@ -64,6 +68,10 @@ func NewInteger(arg Arg) (*Integer, error) { return i, nil } +func (i *Integer) GetAppTag() model.BACnetDataType { + return i._appTag +} + func (i *Integer) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { @@ -96,7 +104,7 @@ func (i *Integer) Encode(arg Arg) error { } } - tag.setAppData(uint(model.BACnetDataType_SIGNED_INTEGER), data) + tag.setAppData(uint(i._appTag), data) return nil } @@ -105,7 +113,7 @@ func (i *Integer) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_SIGNED_INTEGER) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(i._appTag) { return errors.New("Integer application tag required") } if len(tag.GetTagData()) == 0 { diff --git a/plc4go/internal/bacnetip/primitivedata_Null.go b/plc4go/internal/bacnetip/primitivedata_Null.go index 6c8ffd1a934..b9c04117456 100644 --- a/plc4go/internal/bacnetip/primitivedata_Null.go +++ b/plc4go/internal/bacnetip/primitivedata_Null.go @@ -29,10 +29,14 @@ import ( type Null struct { *Atomic[int] + + _appTag model.BACnetDataType } func NewNull(arg Arg) (*Null, error) { - b := &Null{} + b := &Null{ + _appTag: model.BACnetDataType_NULL, + } b.Atomic = NewAtomic[int](b) if arg == nil { @@ -54,21 +58,25 @@ func NewNull(arg Arg) (*Null, error) { return b, nil } -func (b *Null) Encode(arg Arg) error { +func (n *Null) GetAppTag() model.BACnetDataType { + return n._appTag +} + +func (n *Null) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - tag.set(NewArgs(model.TagClass_APPLICATION_TAGS, model.BACnetDataType_NULL, b.value, []byte{})) + tag.set(NewArgs(model.TagClass_APPLICATION_TAGS, n._appTag, n.value, []byte{})) return nil } -func (b *Null) Decode(arg Arg) error { +func (n *Null) Decode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_NULL) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(n._appTag) { return errors.New("Null application tag required") } if tag.GetTagLvt() > 1 { @@ -77,19 +85,19 @@ func (b *Null) Decode(arg Arg) error { // get the data if tag.GetTagLvt() == 1 { - b.value = 1 + n.value = 1 } return nil } -func (b *Null) IsValid(arg any) bool { +func (n *Null) IsValid(arg any) bool { _, ok := arg.(bool) return ok } -func (b *Null) String() string { +func (n *Null) String() string { value := "False" - if b.value == 1 { + if n.value == 1 { value = "True" } return fmt.Sprintf("Null(%s)", value) diff --git a/plc4go/internal/bacnetip/primitivedata_ObjectIdentifier.go b/plc4go/internal/bacnetip/primitivedata_ObjectIdentifier.go index 7ff8e72b32a..c873aa2020d 100644 --- a/plc4go/internal/bacnetip/primitivedata_ObjectIdentifier.go +++ b/plc4go/internal/bacnetip/primitivedata_ObjectIdentifier.go @@ -38,13 +38,17 @@ type ObjectIdentifierTuple struct { type ObjectIdentifier struct { //*Atomic[...] won't work here + _appTag model.BACnetDataType + objectTypeClass *ObjectType value ObjectIdentifierTuple } func NewObjectIdentifier(args Args) (*ObjectIdentifier, error) { - i := &ObjectIdentifier{} + i := &ObjectIdentifier{ + _appTag: model.BACnetDataType_BACNET_OBJECT_IDENTIFIER, + } var err error i.objectTypeClass, err = NewObjectType(nil) if err != nil { @@ -101,6 +105,10 @@ func NewObjectIdentifier(args Args) (*ObjectIdentifier, error) { return i, nil } +func (o *ObjectIdentifier) GetAppTag() model.BACnetDataType { + return o._appTag +} + func (o *ObjectIdentifier) setTuple(objType any, objInstance int) error { switch objType.(type) { case int: @@ -165,7 +173,7 @@ func (o *ObjectIdentifier) Encode(arg Arg) error { } data := make([]byte, 4) binary.BigEndian.PutUint32(data, uint32(o.getLong())) - tag.setAppData(uint(model.BACnetDataType_BACNET_OBJECT_IDENTIFIER), data) + tag.setAppData(uint(o._appTag), data) return nil } @@ -174,7 +182,7 @@ func (o *ObjectIdentifier) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_BACNET_OBJECT_IDENTIFIER) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(o._appTag) { return errors.New("ObjectIdentifier application tag required") } if len(tag.GetTagData()) != 4 { diff --git a/plc4go/internal/bacnetip/primitivedata_OctetString.go b/plc4go/internal/bacnetip/primitivedata_OctetString.go index 954d64f0eda..019ef9411ca 100644 --- a/plc4go/internal/bacnetip/primitivedata_OctetString.go +++ b/plc4go/internal/bacnetip/primitivedata_OctetString.go @@ -29,11 +29,15 @@ import ( ) type OctetString struct { + _appTag model.BACnetDataType + value []byte } func NewOctetString(arg Arg) (*OctetString, error) { - o := &OctetString{} + o := &OctetString{ + _appTag: model.BACnetDataType_OCTET_STRING, + } o.value = make([]byte, 0) if arg == nil { @@ -60,12 +64,16 @@ func NewOctetString(arg Arg) (*OctetString, error) { return o, nil } +func (o *OctetString) GetAppTag() model.BACnetDataType { + return o._appTag +} + func (o *OctetString) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - tag.setAppData(uint(model.BACnetDataType_OCTET_STRING), o.value) + tag.setAppData(uint(o._appTag), o.value) return nil } @@ -74,7 +82,7 @@ func (o *OctetString) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_OCTET_STRING) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(o._appTag) { return errors.New("OctetString application tag required") } diff --git a/plc4go/internal/bacnetip/primitivedata_Real.go b/plc4go/internal/bacnetip/primitivedata_Real.go index e0003bb904c..05d97d26e6f 100644 --- a/plc4go/internal/bacnetip/primitivedata_Real.go +++ b/plc4go/internal/bacnetip/primitivedata_Real.go @@ -32,10 +32,14 @@ import ( type Real struct { *Atomic[float32] *CommonMath + + _appTag model.BACnetDataType } func NewReal(arg Arg) (*Real, error) { - b := &Real{} + b := &Real{ + _appTag: model.BACnetDataType_REAL, + } b.Atomic = NewAtomic[float32](b) if arg == nil { @@ -64,23 +68,27 @@ func NewReal(arg Arg) (*Real, error) { return b, nil } -func (d *Real) Encode(arg Arg) error { +func (r *Real) GetAppTag() model.BACnetDataType { + return r._appTag +} + +func (r *Real) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } var _b = make([]byte, 4) - binary.BigEndian.PutUint32(_b, math.Float32bits(d.value)) - tag.setAppData(uint(model.BACnetDataType_REAL), _b) + binary.BigEndian.PutUint32(_b, math.Float32bits(r.value)) + tag.setAppData(uint(r._appTag), _b) return nil } -func (d *Real) Decode(arg Arg) error { +func (r *Real) Decode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_REAL) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(r._appTag) { return errors.New("Real application tag required") } if len(tag.GetTagData()) != 4 { @@ -88,11 +96,11 @@ func (d *Real) Decode(arg Arg) error { } // extract the data - d.value = math.Float32frombits(binary.BigEndian.Uint32(tag.GetTagData())) + r.value = math.Float32frombits(binary.BigEndian.Uint32(tag.GetTagData())) return nil } -func (d *Real) IsValid(arg any) bool { +func (r *Real) IsValid(arg any) bool { switch arg := arg.(type) { case float32: return true @@ -106,6 +114,6 @@ func (d *Real) IsValid(arg any) bool { } } -func (d *Real) String() string { - return fmt.Sprintf("Real(%g)", d.value) +func (r *Real) String() string { + return fmt.Sprintf("Real(%g)", r.value) } diff --git a/plc4go/internal/bacnetip/primitivedata_Time.go b/plc4go/internal/bacnetip/primitivedata_Time.go index e2c63e876bc..41fc778411d 100644 --- a/plc4go/internal/bacnetip/primitivedata_Time.go +++ b/plc4go/internal/bacnetip/primitivedata_Time.go @@ -41,10 +41,14 @@ type TimeTuple struct { type Time struct { value TimeTuple + + _appTag model.BACnetDataType } func NewTime(arg Arg, args Args) (*Time, error) { - d := &Time{} + d := &Time{ + _appTag: model.BACnetDataType_TIME, + } hour := 255 if len(args) > 0 { hour = args[0].(int) @@ -124,6 +128,10 @@ func NewTime(arg Arg, args Args) (*Time, error) { return d, nil } +func (t *Time) GetAppTag() model.BACnetDataType { + return t._appTag +} + func (t *Time) now(arg float32) { panic("implement me") // TODO } @@ -133,7 +141,7 @@ func (t *Time) Encode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - tag.setAppData(uint(model.BACnetDataType_TIME), []byte{byte(t.value.Hour), byte(t.value.Minute), byte(t.value.Second), byte(t.value.Hundredth)}) + tag.setAppData(uint(t._appTag), []byte{byte(t.value.Hour), byte(t.value.Minute), byte(t.value.Second), byte(t.value.Hundredth)}) return nil } @@ -142,7 +150,7 @@ func (t *Time) Decode(arg Arg) error { if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_TIME) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(t._appTag) { return errors.New("Time application tag required") } if len(tag.GetTagData()) != 4 { diff --git a/plc4go/internal/bacnetip/primitivedata_Unsigned.go b/plc4go/internal/bacnetip/primitivedata_Unsigned.go index 97e04000fb7..abc461f69e2 100644 --- a/plc4go/internal/bacnetip/primitivedata_Unsigned.go +++ b/plc4go/internal/bacnetip/primitivedata_Unsigned.go @@ -33,10 +33,14 @@ import ( type Unsigned struct { *Atomic[uint32] *CommonMath + + _appTag model.BACnetDataType } func NewUnsigned(arg Arg) (*Unsigned, error) { - i := &Unsigned{} + i := &Unsigned{ + _appTag: model.BACnetDataType_UNSIGNED_INTEGER, + } i.Atomic = NewAtomic[uint32](i) if arg == nil { @@ -75,29 +79,33 @@ func NewUnsigned(arg Arg) (*Unsigned, error) { return i, nil } -func (i *Unsigned) Encode(arg Arg) error { +func (u *Unsigned) GetAppTag() model.BACnetDataType { + return u._appTag +} + +func (u *Unsigned) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } data := make([]byte, 4) - binary.BigEndian.PutUint32(data, i.value) + binary.BigEndian.PutUint32(data, u.value) // reduce the value to the smallest number of bytes for len(data) > 1 && data[0] == 0 { data = data[1:] } - tag.setAppData(uint(model.BACnetDataType_UNSIGNED_INTEGER), data) + tag.setAppData(uint(u._appTag), data) return nil } -func (i *Unsigned) Decode(arg Arg) error { +func (u *Unsigned) Decode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_UNSIGNED_INTEGER) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(u._appTag) { return errors.New("Unsigned application tag required") } if len(tag.GetTagData()) == 0 { @@ -113,11 +121,11 @@ func (i *Unsigned) Decode(arg Arg) error { } // save the result - i.value = rslt + u.value = rslt return nil } -func (i *Unsigned) IsValid(arg any) bool { +func (u *Unsigned) IsValid(arg any) bool { switch arg := arg.(type) { case string: _, err := strconv.Atoi(arg) @@ -135,6 +143,6 @@ func (i *Unsigned) IsValid(arg any) bool { } } -func (i *Unsigned) String() string { - return fmt.Sprintf("Unsigned(%d)", i.value) +func (u *Unsigned) String() string { + return fmt.Sprintf("Unsigned(%d)", u.value) } diff --git a/plc4go/internal/bacnetip/primitivedata_Unsigned16.go b/plc4go/internal/bacnetip/primitivedata_Unsigned16.go index b14a1ac9419..a3028ddcacb 100644 --- a/plc4go/internal/bacnetip/primitivedata_Unsigned16.go +++ b/plc4go/internal/bacnetip/primitivedata_Unsigned16.go @@ -33,10 +33,14 @@ import ( type Unsigned16 struct { *Atomic[uint16] *CommonMath + + _appTag model.BACnetDataType } func NewUnsigned16(arg Arg) (*Unsigned16, error) { - i := &Unsigned16{} + i := &Unsigned16{ + _appTag: model.BACnetDataType_UNSIGNED_INTEGER, + } i.Atomic = NewAtomic[uint16](i) if arg == nil { @@ -70,29 +74,33 @@ func NewUnsigned16(arg Arg) (*Unsigned16, error) { return i, nil } -func (i *Unsigned16) Encode(arg Arg) error { +func (u *Unsigned16) GetAppTag() model.BACnetDataType { + return u._appTag +} + +func (u *Unsigned16) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } data := make([]byte, 4) - binary.BigEndian.PutUint32(data, uint32(i.value)) + binary.BigEndian.PutUint32(data, uint32(u.value)) // reduce the value to the smallest number of bytes for len(data) > 1 && data[0] == 0 { data = data[1:] } - tag.setAppData(uint(model.BACnetDataType_UNSIGNED_INTEGER), data) + tag.setAppData(uint(u._appTag), data) return nil } -func (i *Unsigned16) Decode(arg Arg) error { +func (u *Unsigned16) Decode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_UNSIGNED_INTEGER) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(u._appTag) { return errors.New("Unsigned16 application tag required") } if len(tag.GetTagData()) == 0 { @@ -108,11 +116,11 @@ func (i *Unsigned16) Decode(arg Arg) error { } // save the result - i.value = rslt + u.value = rslt return nil } -func (i *Unsigned16) IsValid(arg any) bool { +func (u *Unsigned16) IsValid(arg any) bool { switch arg := arg.(type) { case string: _, err := strconv.Atoi(arg) @@ -130,6 +138,6 @@ func (i *Unsigned16) IsValid(arg any) bool { } } -func (i *Unsigned16) String() string { - return fmt.Sprintf("Unsigned16(%d)", i.value) +func (u *Unsigned16) String() string { + return fmt.Sprintf("Unsigned16(%d)", u.value) } diff --git a/plc4go/internal/bacnetip/primitivedata_Unsigned8.go b/plc4go/internal/bacnetip/primitivedata_Unsigned8.go index 8c55905915c..7bc1258fc4f 100644 --- a/plc4go/internal/bacnetip/primitivedata_Unsigned8.go +++ b/plc4go/internal/bacnetip/primitivedata_Unsigned8.go @@ -33,10 +33,14 @@ import ( type Unsigned8 struct { *Atomic[uint8] *CommonMath + + _appTag model.BACnetDataType } func NewUnsigned8(arg Arg) (*Unsigned8, error) { - i := &Unsigned8{} + i := &Unsigned8{ + _appTag: model.BACnetDataType_UNSIGNED_INTEGER, + } i.Atomic = NewAtomic[uint8](i) if arg == nil { @@ -70,29 +74,33 @@ func NewUnsigned8(arg Arg) (*Unsigned8, error) { return i, nil } -func (i *Unsigned8) Encode(arg Arg) error { +func (u *Unsigned8) GetAppTag() model.BACnetDataType { + return u._appTag +} + +func (u *Unsigned8) Encode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } data := make([]byte, 4) - binary.BigEndian.PutUint32(data, uint32(i.value)) + binary.BigEndian.PutUint32(data, uint32(u.value)) // reduce the value to the smallest number of bytes for len(data) > 1 && data[0] == 0 { data = data[1:] } - tag.setAppData(uint(model.BACnetDataType_UNSIGNED_INTEGER), data) + tag.setAppData(uint(u._appTag), data) return nil } -func (i *Unsigned8) Decode(arg Arg) error { +func (u *Unsigned8) Decode(arg Arg) error { tag, ok := arg.(Tag) if !ok { return errors.Errorf("%T is not a Tag", arg) } - if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(model.BACnetDataType_UNSIGNED_INTEGER) { + if tag.GetTagClass() != model.TagClass_APPLICATION_TAGS || tag.GetTagNumber() != uint(u._appTag) { return errors.New("Unsigned8 application tag required") } if len(tag.GetTagData()) == 0 { @@ -108,11 +116,11 @@ func (i *Unsigned8) Decode(arg Arg) error { } // save the result - i.value = rslt + u.value = rslt return nil } -func (i *Unsigned8) IsValid(arg any) bool { +func (u *Unsigned8) IsValid(arg any) bool { switch arg := arg.(type) { case string: _, err := strconv.Atoi(arg) @@ -130,6 +138,6 @@ func (i *Unsigned8) IsValid(arg any) bool { } } -func (i *Unsigned8) String() string { - return fmt.Sprintf("Unsigned8(%d)", i.value) +func (u *Unsigned8) String() string { + return fmt.Sprintf("Unsigned8(%d)", u.value) } diff --git a/plc4go/internal/bacnetip/tests/test_constructed_data/helpers.go b/plc4go/internal/bacnetip/tests/test_constructed_data/helpers.go index 9978caa5ffe..53f8b541161 100644 --- a/plc4go/internal/bacnetip/tests/test_constructed_data/helpers.go +++ b/plc4go/internal/bacnetip/tests/test_constructed_data/helpers.go @@ -79,7 +79,7 @@ type SimpleSequence struct { func NewSimpleSequence(kwargs KWArgs) (*SimpleSequence, error) { s := &SimpleSequence{ sequenceElements: []Element{ - NewElement("hydrogen", func(args Args, _ KWArgs) (interface{ Encode(Arg) error }, error) { + NewElement("hydrogen", func(args Args, _ KWArgs) (ElementKlass, error) { var arg any if len(args) == 1 { arg = args[0] @@ -109,9 +109,89 @@ func (e *SimpleSequence) GetSequenceElements() []Element { type CompoundSequence1 struct { *Sequence *SequenceEquality + + sequenceElements []Element +} + +func NewCompoundSequence1(kwargs KWArgs) (*CompoundSequence1, error) { + s := &CompoundSequence1{ + sequenceElements: []Element{ + NewElement("hydrogen", func(args Args, _ KWArgs) (ElementKlass, error) { + var arg any + if len(args) == 1 { + arg = args[0] + } + boolean, err := NewBoolean(arg) + return boolean, err + }), + NewElement("helium", func(args Args, _ KWArgs) (ElementKlass, error) { + var arg any + if len(args) == 1 { + arg = args[0] + } + boolean, err := NewInteger(arg) + return boolean, err + }), + }, + } + var err error + s.Sequence, err = NewSequence(kwargs, WithSequenceContract(s)) + if err != nil { + return nil, errors.Wrap(err, "could not create sequence") + } + s.SequenceEquality = NewSequenceEquality(s) + return s, nil +} + +func (e *CompoundSequence1) SetSequence(sequence *Sequence) { + e.Sequence = sequence +} + +func (e *CompoundSequence1) GetSequenceElements() []Element { + return e.sequenceElements } type CompoundSequence2 struct { *Sequence *SequenceEquality + + sequenceElements []Element +} + +func NewCompoundSequence2(kwargs KWArgs) (*CompoundSequence2, error) { + s := &CompoundSequence2{ + sequenceElements: []Element{ + NewElement("lithium", func(args Args, _ KWArgs) (ElementKlass, error) { + var arg any + if len(args) == 1 { + arg = args[0] + } + boolean, err := NewBoolean(arg) + return boolean, err + }, WithElementOptional(true)), + NewElement("beryllium", func(args Args, _ KWArgs) (ElementKlass, error) { + var arg any + if len(args) == 1 { + arg = args[0] + } + boolean, err := NewInteger(arg) + return boolean, err + }), + }, + } + var err error + s.Sequence, err = NewSequence(kwargs, WithSequenceContract(s)) + if err != nil { + return nil, errors.Wrap(err, "could not create sequence") + } + s.SequenceEquality = NewSequenceEquality(s) + return s, nil +} + +func (e *CompoundSequence2) SetSequence(sequence *Sequence) { + e.Sequence = sequence +} + +func (e *CompoundSequence2) GetSequenceElements() []Element { + return e.sequenceElements } diff --git a/plc4go/internal/bacnetip/tests/test_constructed_data/test_sequence_test.go b/plc4go/internal/bacnetip/tests/test_constructed_data/test_sequence_test.go index 0b448601fbf..1d506b8ac72 100644 --- a/plc4go/internal/bacnetip/tests/test_constructed_data/test_sequence_test.go +++ b/plc4go/internal/bacnetip/tests/test_constructed_data/test_sequence_test.go @@ -90,3 +90,102 @@ func TestSimpleSequence(t *testing.T) { require.NoError(t, err) }) } + +func TestCompoundSequence1(t *testing.T) { + t.Run("test_missing_element", func(t *testing.T) { + // create a sequence with a missing required element + seq, err := NewCompoundSequence1(NoKWArgs) + require.NoError(t, err) + + // encode it in a tag list + tagList := NewTagList(nil) + err = seq.Encode(tagList) + require.Error(t, err) + + // create a sequence with a missing required element + seq, err = NewCompoundSequence1(NewKWArgs(KnownKey("hydrogen"), true)) + require.NoError(t, err) + + // encode it in a tag list + tagList = NewTagList(nil) + err = seq.Encode(tagList) + require.Error(t, err) + + // create a sequence with a missing required element + seq, err = NewCompoundSequence1(NewKWArgs(KnownKey("helium"), 2)) + require.NoError(t, err) + + // encode it in a tag list + tagList = NewTagList(nil) + err = seq.Encode(tagList) + require.Error(t, err) + + }) + t.Run("test_codec", func(t *testing.T) { + // create a sequence + seq, err := NewCompoundSequence1(NewKWArgs(KnownKey("hydrogen"), true, KnownKey("helium"), 2)) + require.NoError(t, err) + + // encode it in a tag list + tagList := NewTagList(nil) + err = seq.Encode(tagList) + + // create another sequence and decode the taglist + seq, err = NewCompoundSequence1(NoKWArgs) + require.NoError(t, err) + err = seq.Decode(tagList) + require.NoError(t, err) + }) +} + +func TestCompoundSequence2(t *testing.T) { + t.Run("test_missing_element", func(t *testing.T) { + // create a sequence with a missing required element + seq, err := NewCompoundSequence2(NoKWArgs) + require.NoError(t, err) + + // encode it in a tag list + tagList := NewTagList(nil) + err = seq.Encode(tagList) + require.Error(t, err) + + // create a sequence with a missing required element + seq, err = NewCompoundSequence2(NewKWArgs(KnownKey("lithium"), true)) + require.NoError(t, err) + + // encode it in a tag list + tagList = NewTagList(nil) + err = seq.Encode(tagList) + require.Error(t, err) + }) + t.Run("test_codec_1", func(t *testing.T) { + // create a sequence + seq, err := NewCompoundSequence2(NewKWArgs(KnownKey("beryllium"), 2)) + require.NoError(t, err) + + // encode it in a tag list + tagList := NewTagList(nil) + err = seq.Encode(tagList) + + // create another sequence and decode the taglist + seq, err = NewCompoundSequence2(NoKWArgs) + require.NoError(t, err) + err = seq.Decode(tagList) + require.NoError(t, err) + }) + t.Run("test_codec_2", func(t *testing.T) { + // create a sequence + seq, err := NewCompoundSequence2(NewKWArgs(KnownKey("lithium"), true, KnownKey("beryllium"), 2)) + require.NoError(t, err) + + // encode it in a tag list + tagList := NewTagList(nil) + err = seq.Encode(tagList) + + // create another sequence and decode the taglist + seq, err = NewCompoundSequence2(NoKWArgs) + require.NoError(t, err) + err = seq.Decode(tagList) + require.NoError(t, err) + }) +}