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 a86fe67ed6 feat(plc4go/bacnetip): ForwardedNPDU
a86fe67ed6 is described below

commit a86fe67ed69b69c2a06819966d825a0bde7b4852
Author: Sebastian Rühl <[email protected]>
AuthorDate: Thu Aug 22 20:34:21 2024 +0200

    feat(plc4go/bacnetip): ForwardedNPDU
---
 plc4go/internal/bacnetip/CommunicationsModule.go   |   9 +-
 plc4go/internal/bacnetip/PDU.go                    |  16 ++++
 plc4go/internal/bacnetip/bvll.go                   | 103 +++++++++++++++++++--
 plc4go/internal/bacnetip/comp.go                   |  10 +-
 plc4go/internal/bacnetip/tests/state_machine.go    |   6 +-
 .../bacnetip/tests/test_bvll/test_codec_test.go    |  44 +++++++++
 6 files changed, 175 insertions(+), 13 deletions(-)

diff --git a/plc4go/internal/bacnetip/CommunicationsModule.go 
b/plc4go/internal/bacnetip/CommunicationsModule.go
index 2b13a79352..b3e0c6ba0e 100644
--- a/plc4go/internal/bacnetip/CommunicationsModule.go
+++ b/plc4go/internal/bacnetip/CommunicationsModule.go
@@ -25,6 +25,7 @@ import (
        "strconv"
        "strings"
 
+       "github.com/apache/plc4x/plc4go/internal/bacnetip/globals"
        "github.com/apache/plc4x/plc4go/spi"
        "github.com/apache/plc4x/plc4go/spi/utils"
 
@@ -151,12 +152,18 @@ func (p *__PCI) GetLengthInBits(ctx context.Context) 
uint16 {
 
 func (p *__PCI) String() string {
        pduUserDataString := ""
-       if p.pduUserData != nil {
+       if p.pduUserData != nil && globals.ExtendedPDUOutput {
                pduUserDataString = p.pduUserData.String()
                if strings.Contains(pduUserDataString, "\n") {
                        pduUserDataString = "\n" + pduUserDataString + "\n"
                }
                pduUserDataString = "pduUserData: " + pduUserDataString + " ,"
+       } else if p.pduUserData != nil {
+               if bytes, err := p.pduUserData.Serialize(); err != nil {
+                       pduUserDataString = "pduUserData: " + err.Error() + " ,"
+               } else {
+                       pduUserDataString = "pduUserData: " + Btox(bytes, ".") 
+ " ,"
+               }
        }
        return fmt.Sprintf("__PCI{%spduSource: %s, pduDestination: %s}", 
pduUserDataString, p.pduSource, p.pduDestination)
 }
diff --git a/plc4go/internal/bacnetip/PDU.go b/plc4go/internal/bacnetip/PDU.go
index cb3551d3b7..4b1e4eb0c0 100644
--- a/plc4go/internal/bacnetip/PDU.go
+++ b/plc4go/internal/bacnetip/PDU.go
@@ -799,6 +799,22 @@ func uint32ToIpv4(number uint32) net.IP {
        return ipv4
 }
 
+// PackIpAddr Given an IP address tuple like ('1.2.3.4', 47808) return the 
six-octet string
+// useful for a BACnet address.
+func PackIpAddr(addrTuple *AddressTuple[string, uint16]) (octetString []byte) {
+       addr, port := addrTuple.Left, addrTuple.Right
+       octetString = append(net.ParseIP(addr).To4(), uint16ToPort(port)...)
+       return
+}
+
+// UnpackIpAddr Given a six-octet BACnet address, return an IP address tuple.
+func UnpackIpAddr(addr []byte) (addrTuple *AddressTuple[string, uint16]) {
+       ip := ipv4ToUint32(addr[:4])
+       port := portToUint16(addr[4:])
+
+       return &AddressTuple[string, uint16]{uint32ToIpv4(ip).String(), port}
+}
+
 func NewLocalStation(localLog zerolog.Logger, addr any, route *Address) 
(*Address, error) {
        l := &Address{
                log: localLog,
diff --git a/plc4go/internal/bacnetip/bvll.go b/plc4go/internal/bacnetip/bvll.go
index b664af2067..bac477bcf6 100644
--- a/plc4go/internal/bacnetip/bvll.go
+++ b/plc4go/internal/bacnetip/bvll.go
@@ -325,6 +325,7 @@ func (w *WriteBroadcastDistributionTable) 
produceBvlciBDT(entries []readWriteMod
        }
        return
 }
+
 func (w *WriteBroadcastDistributionTable) Encode(bvlpdu Arg) error {
        switch bvlpdu := bvlpdu.(type) {
        case BVLPDU:
@@ -419,6 +420,7 @@ func (w *ReadBroadcastDistributionTable) String() string {
 
 type ReadBroadcastDistributionTableAck struct {
        *_BVLPDU
+
        bvlciBDT []*Address
 }
 
@@ -514,27 +516,108 @@ func (w *ReadBroadcastDistributionTableAck) String() 
string {
        return fmt.Sprintf("ReadBroadcastDistributionTableAck{%v, bvlciBDT: 
%v}", w._BVLPDU, w.bvlciBDT)
 }
 
-// TODO: finish
 type ForwardedNPDU struct {
        *_BVLPDU
+
+       bvlciAddress *Address
 }
 
 var _ BVLPDU = (*ForwardedNPDU)(nil)
 
-func NewForwardedNPDU() (BVLPDU, error) {
+func NewForwardedNPDU(pdu PDU, opts ...func(*ForwardedNPDU)) (*ForwardedNPDU, 
error) {
        b := &ForwardedNPDU{}
-       b._BVLPDU = NewBVLPDU(nil).(*_BVLPDU)
+       for _, opt := range opts {
+               opt(b)
+       }
+       switch npdu := pdu.(type) {
+       case readWriteModel.NPDUExactly:
+               b._BVLPDU = 
NewBVLPDU(readWriteModel.NewBVLCForwardedNPDU(b.produceInnerNPDU(npdu))).(*_BVLPDU)
+       case nil:
+               b._BVLPDU = NewBVLPDU(nil).(*_BVLPDU)
+       default:
+               // TODO: re-encode seems expensive... check if there is a 
better option (e.g. only do it on the message bridge)
+               data := pdu.GetPduData()
+               parse, err := readWriteModel.NPDUParse(context.Background(), 
data, uint16(len(data)))
+               if err != nil {
+                       return nil, errors.Wrap(err, "error re-encoding")
+               }
+               b._BVLPDU = 
NewBVLPDU(readWriteModel.NewBVLCForwardedNPDU(b.produceInnerNPDU(parse))).(*_BVLPDU)
+       }
        return b, nil
 }
 
-func (b *ForwardedNPDU) Encode(pdu Arg) error {
-       // TODO: finish
-       return nil
+func WithForwardedNPDUAddress(addr *Address) func(*ForwardedNPDU) {
+       return func(b *ForwardedNPDU) {
+               b.bvlciAddress = addr
+       }
 }
 
-func (b *ForwardedNPDU) Decode(pdu Arg) error {
-       // TODO: finish
-       return nil
+func (w *ForwardedNPDU) GetBvlciAddress() *Address {
+       return w.bvlciAddress
+}
+
+func (w *ForwardedNPDU) produceInnerNPDU(inNpdu readWriteModel.NPDU) (ip 
[]uint8, port uint16, npdu readWriteModel.NPDU, bvlcPayloadLength uint16) {
+       ip = w.bvlciAddress.AddrAddress[:4]
+       port = uint16(47808)
+       if w.bvlciAddress.AddrPort != nil {
+               port = *w.bvlciAddress.AddrPort
+       }
+       npdu = inNpdu
+       return
+}
+
+func (w *ForwardedNPDU) Encode(bvlpdu Arg) error {
+       switch bvlpdu := bvlpdu.(type) {
+       case BVLPDU:
+               if err := bvlpdu.Update(w); err != nil {
+                       return errors.Wrap(err, "error updating BVLPDU")
+               }
+
+               // encode the addrress
+               bvlpdu.PutData(w.bvlciAddress.AddrAddress...)
+
+               // encode the rest of the data
+               bvlpdu.PutData(w.GetPduData()...)
+
+               bvlpdu.setBVLC(w.bvlc)
+               return nil
+       default:
+               return errors.Errorf("invalid BVLPDU type %T", bvlpdu)
+       }
+}
+
+func (w *ForwardedNPDU) Decode(bvlpdu Arg) error {
+       switch bvlpdu := bvlpdu.(type) {
+       case BVLPDU:
+               if err := w.Update(bvlpdu); err != nil {
+                       return errors.Wrap(err, "error updating BVLPDU")
+               }
+               switch pduUserData := bvlpdu.GetPDUUserData().(type) {
+               case readWriteModel.BVLCForwardedNPDUExactly:
+                       switch bvlc := pduUserData.(type) {
+                       case readWriteModel.BVLCForwardedNPDU:
+                               addr := bvlc.GetIp()
+                               port := bvlc.GetPort()
+                               var portArray = make([]byte, 2)
+                               binary.BigEndian.PutUint16(portArray, port)
+                               var err error
+                               address, err := NewAddress(zerolog.Nop(), 
append(addr, portArray...))
+                               if err != nil {
+                                       return errors.Wrap(err, "error creating 
address")
+                               }
+                               w.bvlciAddress = address
+
+                               w.setBVLC(bvlc)
+                       }
+               }
+               return nil
+       default:
+               return errors.Errorf("invalid BVLPDU type %T", bvlpdu)
+       }
+}
+
+func (w *ForwardedNPDU) String() string {
+       return fmt.Sprintf("ForwardedNPDU{%v, bvlciAddress: %v}", w._BVLPDU, 
w.bvlciAddress)
 }
 
 // TODO: finish
@@ -844,7 +927,7 @@ func init() {
                        return v
                },
                0x04: func() interface{ Decode(Arg) error } {
-                       v, _ := NewForwardedNPDU()
+                       v, _ := NewForwardedNPDU(nil)
                        return v
                },
                0x05: func() interface{ Decode(Arg) error } {
diff --git a/plc4go/internal/bacnetip/comp.go b/plc4go/internal/bacnetip/comp.go
index fd37ac093f..a35d64cc02 100644
--- a/plc4go/internal/bacnetip/comp.go
+++ b/plc4go/internal/bacnetip/comp.go
@@ -64,6 +64,10 @@ func (a Args) Get0MultiplexServer() *_MultiplexServer {
 func (a Args) String() string {
        r := ""
        for i, ea := range a {
+               switch tea := ea.(type) {
+               case []byte:
+                       ea = Btox(tea, ".")
+               }
                r += fmt.Sprintf("%d: %v, ", i, ea)
        }
        if r != "" {
@@ -93,12 +97,16 @@ func NewKWArgs(kw ...any) KWArgs {
 func (k KWArgs) String() string {
        r := ""
        for kk, ea := range k {
+               switch tea := ea.(type) {
+               case []byte:
+                       ea = Btox(tea, ".")
+               }
                r += fmt.Sprintf("%s=%v, ", kk, ea)
        }
        if r != "" {
                r = r[:len(r)-2]
        }
-       return r
+       return "{" + r + "}"
 }
 
 type KnownKey string
diff --git a/plc4go/internal/bacnetip/tests/state_machine.go 
b/plc4go/internal/bacnetip/tests/state_machine.go
index 8fe2c04cda..77b5b93d69 100644
--- a/plc4go/internal/bacnetip/tests/state_machine.go
+++ b/plc4go/internal/bacnetip/tests/state_machine.go
@@ -299,7 +299,11 @@ func MatchPdu(localLog zerolog.Logger, pdu bacnetip.PDU, 
pduType any, pduAttrs m
                                return a.Equals(b)
                        })
                case bacnetip.KWBvlciAddress:
-                       panic("implement me")
+                       nni, ok := pdu.(*bacnetip.ForwardedNPDU)
+                       if !ok {
+                               return false
+                       }
+                       return nni.GetBvlciAddress().Equals(attrValue)
                case bacnetip.KWFdAddress:
                        panic("implement me")
                case bacnetip.KWFdTTL:
diff --git a/plc4go/internal/bacnetip/tests/test_bvll/test_codec_test.go 
b/plc4go/internal/bacnetip/tests/test_bvll/test_codec_test.go
index a937a00ede..e82f94e584 100644
--- a/plc4go/internal/bacnetip/tests/test_bvll/test_codec_test.go
+++ b/plc4go/internal/bacnetip/tests/test_bvll/test_codec_test.go
@@ -63,6 +63,14 @@ func ReadBroadcastDistributionTableAck(bdt 
...*bacnetip.Address) *bacnetip.ReadB
        return readBroadcastDistributionTable
 }
 
+func ForwardNPDU(addr *bacnetip.Address, pduBytes []byte) 
*bacnetip.ForwardedNPDU {
+       npdu, err := 
bacnetip.NewForwardedNPDU(bacnetip.NewPDU(&bacnetip.MessageBridge{Bytes: 
pduBytes}), bacnetip.WithForwardedNPDUAddress(addr))
+       if err != nil {
+               panic(err)
+       }
+       return npdu
+}
+
 type TestAnnexJCodecSuite struct {
        suite.Suite
 
@@ -279,6 +287,42 @@ func (suite *TestAnnexJCodecSuite) 
TestReadBroadcastDistributionTableAck() {
        err = 
suite.Confirmation(bacnetip.NewArgs((*bacnetip.ReadBroadcastDistributionTableAck)(nil)),
 bacnetip.NewKWArgs(bacnetip.KWBvlciBDT, []*bacnetip.Address{addr}))
 }
 
+func (suite *TestAnnexJCodecSuite) TestForwardNPDU() {
+       // Read an empty TableAck
+       addr, err := bacnetip.NewAddress(zerolog.Nop(), "192.168.0.1")
+       xpdu, err := bacnetip.Xtob(
+               // "deadbeef", // forwarded PDU // TODO: this is not a ndpu so 
we just exploded with that. We use the iartn for that for now
+               // TODO: this below is from us as upstream message is not 
parsable
+               "01.80" + // version, network layer message
+                       "01 0001 0002 0003", // message type and network list
+       )
+       suite.Require().NoError(err)
+       pduBytes, err := bacnetip.Xtob("81.04.0013" + //   bvlci // TODO: 
length was 0e before
+               "c0.a8.00.01.ba.c0" + // original source address
+               // "deadbeef", // forwarded PDU // TODO: this is not a ndpu so 
we just exploded with that. We use the iartn for that for now
+               // TODO: this below is from us as upstream message is not 
parsable
+               "01.80" + // version, network layer message
+               "01 0001 0002 0003", // message type and network list
+       )
+       suite.Require().NoError(err)
+       { // Parse with plc4x parser to validate
+               parse, err := 
readWriteModel.BVLCParse(testutils.TestContext(suite.T()), pduBytes)
+               suite.Assert().NoError(err)
+               if parse != nil {
+                       suite.T().Log("\n" + parse.String())
+               }
+       }
+
+       err = suite.Request(bacnetip.NewArgs(ForwardNPDU(addr, xpdu)), 
bacnetip.NoKWArgs)
+       suite.Assert().NoError(err)
+       err = suite.Indication(bacnetip.NoArgs, 
bacnetip.NewKWArgs(bacnetip.KWPDUData, pduBytes))
+       suite.Assert().NoError(err)
+
+       err = 
suite.Response(bacnetip.NewArgs(bacnetip.NewPDU(&bacnetip.MessageBridge{Bytes: 
pduBytes})), bacnetip.NoKWArgs)
+       suite.Assert().NoError(err)
+       err = 
suite.Confirmation(bacnetip.NewArgs((*bacnetip.ForwardedNPDU)(nil)), 
bacnetip.NewKWArgs(bacnetip.KWBvlciAddress, addr, bacnetip.KWPDUData, xpdu))
+}
+
 func TestAnnexJCodec(t *testing.T) {
        suite.Run(t, new(TestAnnexJCodecSuite))
 }

Reply via email to