From 34f29bbbc90525e6e61f5cbd97eed69892d04982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20R=C3=BChl?= Date: Thu, 29 Aug 2024 19:58:22 +0200 Subject: [PATCH] feat(plc4go/bacnetip): progressed APDU --- plc4go/internal/bacnetip/apdu_APCI.go | 10 ++-- plc4go/internal/bacnetip/apdu_APCISequence.go | 57 +++++++++++++----- .../bacnetip/apdu_ComplexAckSequence.go | 60 ++++++++++++++++++- .../bacnetip/apdu_UnconfirmedRequestPDU.go | 39 +++++++----- plc4go/internal/bacnetip/apdu_WhoIsRequest.go | 30 +++++++--- plc4go/internal/bacnetip/constructeddata.go | 22 ++++++- .../tests/test_constructed_data/helpers.go | 53 +++------------- 7 files changed, 181 insertions(+), 90 deletions(-) diff --git a/plc4go/internal/bacnetip/apdu_APCI.go b/plc4go/internal/bacnetip/apdu_APCI.go index aaae43be3af..4fa325a3583 100644 --- a/plc4go/internal/bacnetip/apdu_APCI.go +++ b/plc4go/internal/bacnetip/apdu_APCI.go @@ -54,8 +54,8 @@ type APCI interface { getApduMaxSegs() *uint8 setApduMaxResp(*uint8) getApduMaxResp() *uint8 - setApduService(*readWriteModel.BACnetConfirmedServiceChoice) - getApduService() *readWriteModel.BACnetConfirmedServiceChoice + setApduService(*uint8) + getApduService() *uint8 setApduInvokeID(*uint8) getApduInvokeID() *uint8 setApduAbortRejectReason(*uint8) @@ -76,7 +76,7 @@ type _APCI struct { apduWin *uint8 // actual/proposed window size apduMaxSegs *uint8 // maximum segments accepted (decoded) apduMaxResp *uint8 // max response accepted (decoded) - apduService *readWriteModel.BACnetConfirmedServiceChoice + apduService *uint8 apduInvokeID *uint8 apduAbortRejectReason *uint8 @@ -155,10 +155,10 @@ func (a *_APCI) setApduMaxResp(apduMaxResp *uint8) { func (a *_APCI) getApduMaxResp() *uint8 { return a.apduMaxResp } -func (a *_APCI) setApduService(apduService *readWriteModel.BACnetConfirmedServiceChoice) { +func (a *_APCI) setApduService(apduService *uint8) { a.apduService = apduService } -func (a *_APCI) getApduService() *readWriteModel.BACnetConfirmedServiceChoice { +func (a *_APCI) getApduService() *uint8 { return a.apduService } func (a *_APCI) setApduInvokeID(apduInvokeID *uint8) { diff --git a/plc4go/internal/bacnetip/apdu_APCISequence.go b/plc4go/internal/bacnetip/apdu_APCISequence.go index f875e99d83d..a42c573fef2 100644 --- a/plc4go/internal/bacnetip/apdu_APCISequence.go +++ b/plc4go/internal/bacnetip/apdu_APCISequence.go @@ -21,23 +21,43 @@ package bacnetip import ( "github.com/pkg/errors" - - "github.com/apache/plc4x/plc4go/protocols/bacnetip/readwrite/model" ) +// APCISequenceContract provides a set of functions which can be overwritten by a sub struct +type APCISequenceContract interface { + SequenceContractRequirement +} + +// APCISequenceContractRequirement is needed when one want to extend using SequenceContract +type APCISequenceContractRequirement interface { + APCISequenceContract + // SetAPCISequence callback is needed as we work in the constructor already with the finished object // TODO: maybe we need to return as init again as it might not be finished constructing.... + SetAPCISequence(a *APCISequence) +} + // TODO: implement it... type APCISequence struct { *_APCI *Sequence + _contract APCISequenceContract + tagList *TagList } -func NewAPCISequence() (*APCISequence, error) { +func NewAPCISequence(opts ...func(*APCISequence)) (*APCISequence, error) { a := &APCISequence{} + for _, opt := range opts { + opt(a) + } + if a._contract == nil { + a._contract = a + } else { + a._contract.(APCISequenceContractRequirement).SetAPCISequence(a) + } a._APCI = NewAPCI(nil).(*_APCI) // TODO: what to pass up? var err error - a.Sequence, err = NewSequence(NoKWArgs, WithSequenceContract(a)) + a.Sequence, err = NewSequence(NoKWArgs, WithSequenceExtension(a._contract)) if err != nil { return nil, errors.Wrap(err, "error creating sequence") } @@ -47,6 +67,12 @@ func NewAPCISequence() (*APCISequence, error) { return a, nil } +func WithAPCISequenceExtension(contract APCISequenceContractRequirement) func(*APCISequence) { + return func(a *APCISequence) { + a._contract = contract + } +} + func (a *APCISequence) SetSequence(sequence *Sequence) { a.Sequence = sequence } @@ -78,18 +104,17 @@ func (a *APCISequence) Decode(apdu Arg) error { if err := a.Update(apdu); err != nil { return errors.Wrap(err, "error updating APDU") } - switch pduUserData := apdu.GetRootMessage().(type) { - case model.APDUExactly: - a.tagList = NewTagList(nil) - if err := a.tagList.Decode(apdu); err != nil { - return errors.Wrap(err, "error decoding TagList") - } - // pass the taglist to the Sequence for additional decoding - if err := a.Sequence.Decode(a.tagList); err != nil { - return errors.Wrap(err, "error encoding TagList") - } - - _ = pduUserData + a.tagList = NewTagList(nil) + if err := a.tagList.Decode(apdu); err != nil { + return errors.Wrap(err, "error decoding TagList") + } + // pass the taglist to the Sequence for additional decoding + if err := a.Sequence.Decode(a.tagList); err != nil { + return errors.Wrap(err, "error encoding TagList") + } + + if len(a.tagList.GetTagList()) > 0 { + return errors.New("trailing unmatched tags") } return nil default: diff --git a/plc4go/internal/bacnetip/apdu_ComplexAckSequence.go b/plc4go/internal/bacnetip/apdu_ComplexAckSequence.go index 593c6959e3b..240ae71774b 100644 --- a/plc4go/internal/bacnetip/apdu_ComplexAckSequence.go +++ b/plc4go/internal/bacnetip/apdu_ComplexAckSequence.go @@ -19,14 +19,72 @@ package bacnetip +import ( + "github.com/pkg/errors" + + readWriteModel "github.com/apache/plc4x/plc4go/protocols/bacnetip/readwrite/model" +) + // TODO: implement it... type ComplexAckSequence struct { *APCISequence *ComplexAckPDU } -// TODO: implement it... +// UnconfirmedRequestSequenceContract provides a set of functions which can be overwritten by a sub struct +type UnconfirmedRequestSequenceContract interface { + APCISequenceContractRequirement + GetServiceChoice() *readWriteModel.BACnetUnconfirmedServiceChoice +} + +// UnconfirmedRequestSequenceContractRequirement is needed when one want to extend using SequenceContract +type UnconfirmedRequestSequenceContractRequirement interface { + UnconfirmedRequestSequenceContract + // SetUnconfirmedRequestSequence callback is needed as we work in the constructor already with the finished object // TODO: maybe we need to return as init again as it might not be finished constructing.... + SetUnconfirmedRequestSequence(urs *UnconfirmedRequestSequence) +} + type UnconfirmedRequestSequence struct { *APCISequence *UnconfirmedRequestPDU + + _contract UnconfirmedRequestSequenceContract +} + +func NewUnconfirmedRequestSequence(opts ...func(*UnconfirmedRequestSequence)) (*UnconfirmedRequestSequence, error) { + u := &UnconfirmedRequestSequence{} + for _, opt := range opts { + opt(u) + } + if u._contract == nil { + u._contract = u + } else { + u._contract.(UnconfirmedRequestSequenceContractRequirement).SetUnconfirmedRequestSequence(u) + } + var err error + u.APCISequence, err = NewAPCISequence(WithAPCISequenceExtension(u._contract)) + if err != nil { + return nil, errors.Wrap(err, "error creating _APCISequence") + } + u.UnconfirmedRequestPDU, err = NewUnconfirmedRequestPDU(func(pdu *UnconfirmedRequestPDU) { + pdu.argChoice = u.GetServiceChoice() + }) + if err != nil { + return nil, errors.Wrap(err, "error creating UnconfirmedRequestPDU") + } + return u, nil +} + +func WithUnconfirmedRequestSequenceExtension(contract UnconfirmedRequestSequenceContractRequirement) func(*UnconfirmedRequestSequence) { + return func(a *UnconfirmedRequestSequence) { + a._contract = contract + } +} + +func (u *UnconfirmedRequestSequence) GetServiceChoice() *readWriteModel.BACnetUnconfirmedServiceChoice { + return nil +} + +func (u *UnconfirmedRequestSequence) SetAPCISequence(a *APCISequence) { + u.APCISequence = a } diff --git a/plc4go/internal/bacnetip/apdu_UnconfirmedRequestPDU.go b/plc4go/internal/bacnetip/apdu_UnconfirmedRequestPDU.go index 59eaa7a5d26..2e6c296f8a9 100644 --- a/plc4go/internal/bacnetip/apdu_UnconfirmedRequestPDU.go +++ b/plc4go/internal/bacnetip/apdu_UnconfirmedRequestPDU.go @@ -19,33 +19,42 @@ package bacnetip -import "github.com/pkg/errors" +import ( + "github.com/pkg/errors" + + readWriteModel "github.com/apache/plc4x/plc4go/protocols/bacnetip/readwrite/model" +) -// TODO: implement it... type UnconfirmedRequestPDU struct { *___APDU + + // args + argChoice *readWriteModel.BACnetUnconfirmedServiceChoice + + pduType readWriteModel.ApduType } -func NewUnconfirmedRequestPDU() (*UnconfirmedRequestPDU, error) { - u := &UnconfirmedRequestPDU{} +func NewUnconfirmedRequestPDU(opts ...func(*UnconfirmedRequestPDU)) (*UnconfirmedRequestPDU, error) { + u := &UnconfirmedRequestPDU{ + pduType: readWriteModel.ApduType_UNCONFIRMED_REQUEST_PDU, + } + for _, opt := range opts { + opt(u) + } apdu, err := new_APDU() if err != nil { return nil, errors.Wrap(err, "error creating _APDU") } u.___APDU = apdu.(*___APDU) + + u.apduType = &u.pduType + u.apduService = (*uint8)(u.argChoice) + return u, nil } -func NewUnconfirmedRequestSequence() (*UnconfirmedRequestSequence, error) { - u := &UnconfirmedRequestSequence{} - var err error - u.APCISequence, err = NewAPCISequence() - if err != nil { - return nil, errors.Wrap(err, "error creating _APCISequence") +func WithUnconfirmedRequestPDU(choice readWriteModel.BACnetUnconfirmedServiceChoice) func(*UnconfirmedRequestPDU) { + return func(u *UnconfirmedRequestPDU) { + u.argChoice = &choice } - u.UnconfirmedRequestPDU, err = NewUnconfirmedRequestPDU() - if err != nil { - return nil, errors.Wrap(err, "error creating UnconfirmedRequestPDU") - } - return u, nil } diff --git a/plc4go/internal/bacnetip/apdu_WhoIsRequest.go b/plc4go/internal/bacnetip/apdu_WhoIsRequest.go index c617e4ab6ed..eed494986bb 100644 --- a/plc4go/internal/bacnetip/apdu_WhoIsRequest.go +++ b/plc4go/internal/bacnetip/apdu_WhoIsRequest.go @@ -20,26 +20,42 @@ package bacnetip import ( - "fmt" - "github.com/pkg/errors" + + readWriteModel "github.com/apache/plc4x/plc4go/protocols/bacnetip/readwrite/model" ) -// TODO: implement it... type WhoIsRequest struct { *UnconfirmedRequestSequence + + serviceChoice readWriteModel.BACnetUnconfirmedServiceChoice + sequenceElements []Element } func NewWhoIsRequest() (*WhoIsRequest, error) { - w := &WhoIsRequest{} + w := &WhoIsRequest{ + serviceChoice: readWriteModel.BACnetUnconfirmedServiceChoice_WHO_IS, + sequenceElements: []Element{ + NewElement("deviceInstanceRangeLowLimit", V2E(NewUnsigned), WithElementContext(0), WithElementOptional(true)), + NewElement("deviceInstanceRangeHighLimit", V2E(NewUnsigned), WithElementContext(1), WithElementOptional(true)), + }, + } var err error - w.UnconfirmedRequestSequence, err = NewUnconfirmedRequestSequence() + w.UnconfirmedRequestSequence, err = NewUnconfirmedRequestSequence(WithUnconfirmedRequestSequenceExtension(w)) if err != nil { return nil, errors.Wrap(err, "error creating UnconfirmedRequestSequence") } return w, nil } -func (r *WhoIsRequest) String() string { - return fmt.Sprintf("WhoIsRequest{%s}", r.UnconfirmedRequestSequence) +func (w *WhoIsRequest) GetServiceChoice() *readWriteModel.BACnetUnconfirmedServiceChoice { + return &w.serviceChoice +} + +func (w *WhoIsRequest) GetSequenceElements() []Element { + return w.sequenceElements +} + +func (w *WhoIsRequest) SetUnconfirmedRequestSequence(u *UnconfirmedRequestSequence) { + w.UnconfirmedRequestSequence = u } diff --git a/plc4go/internal/bacnetip/constructeddata.go b/plc4go/internal/bacnetip/constructeddata.go index 32a2d9e243a..96e1919e56c 100644 --- a/plc4go/internal/bacnetip/constructeddata.go +++ b/plc4go/internal/bacnetip/constructeddata.go @@ -48,6 +48,18 @@ type ElementKlass interface { GetAppTag() readWriteModel.BACnetDataType } +// V2E accepts a function which takes an Arg and maps it to a ElementKlass +func V2E[T any](b func(arg Arg) (*T, error)) func(Args, KWArgs) (ElementKlass, error) { + return func(args Args, kwargs KWArgs) (ElementKlass, error) { + var arg any + if len(args) == 1 { + arg = args[0] + } + r, err := b(arg) + return any(r).(ElementKlass), err + } +} + // TODO: finish type _Element struct { Name string @@ -75,6 +87,12 @@ func WithElementOptional(optional bool) func(*_Element) { } } +func WithElementContext(context int) func(*_Element) { + return func(e *_Element) { + e.Context = &context + } +} + func (e *_Element) GetName() string { return e.Name } @@ -105,7 +123,7 @@ type SequenceContract interface { type SequenceContractRequirement interface { SequenceContract // SetSequence callback is needed as we work in the constructor already with the finished object // TODO: maybe we need to return as init again as it might not be finished constructing.... - SetSequence(sequence *Sequence) + SetSequence(s *Sequence) } // TODO: finish @@ -156,7 +174,7 @@ func NewSequence(kwargs KWArgs, opts ...func(*Sequence)) (*Sequence, error) { return s, nil } -func WithSequenceContract(contract SequenceContractRequirement) func(*Sequence) { +func WithSequenceExtension(contract SequenceContractRequirement) func(*Sequence) { return func(s *Sequence) { s._contract = contract } diff --git a/plc4go/internal/bacnetip/tests/test_constructed_data/helpers.go b/plc4go/internal/bacnetip/tests/test_constructed_data/helpers.go index 53f8b541161..0cb0a78fc47 100644 --- a/plc4go/internal/bacnetip/tests/test_constructed_data/helpers.go +++ b/plc4go/internal/bacnetip/tests/test_constructed_data/helpers.go @@ -57,7 +57,7 @@ type EmptySequence struct { func NewEmptySequence(kwargs KWArgs) (*EmptySequence, error) { e := &EmptySequence{} var err error - e.Sequence, err = NewSequence(kwargs, WithSequenceContract(e)) + e.Sequence, err = NewSequence(kwargs, WithSequenceExtension(e)) if err != nil { return nil, errors.Wrap(err, "could not create sequence") } @@ -79,18 +79,11 @@ type SimpleSequence struct { func NewSimpleSequence(kwargs KWArgs) (*SimpleSequence, error) { s := &SimpleSequence{ 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("hydrogen", V2E(NewBoolean)), }, } var err error - s.Sequence, err = NewSequence(kwargs, WithSequenceContract(s)) + s.Sequence, err = NewSequence(kwargs, WithSequenceExtension(s)) if err != nil { return nil, errors.Wrap(err, "could not create sequence") } @@ -116,26 +109,12 @@ type CompoundSequence1 struct { 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 - }), + NewElement("hydrogen", V2E(NewBoolean)), + NewElement("helium", V2E(NewInteger)), }, } var err error - s.Sequence, err = NewSequence(kwargs, WithSequenceContract(s)) + s.Sequence, err = NewSequence(kwargs, WithSequenceExtension(s)) if err != nil { return nil, errors.Wrap(err, "could not create sequence") } @@ -161,26 +140,12 @@ type CompoundSequence2 struct { 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 - }), + NewElement("lithium", V2E(NewBoolean), WithElementOptional(true)), + NewElement("beryllium", V2E(NewInteger)), }, } var err error - s.Sequence, err = NewSequence(kwargs, WithSequenceContract(s)) + s.Sequence, err = NewSequence(kwargs, WithSequenceExtension(s)) if err != nil { return nil, errors.Wrap(err, "could not create sequence") }