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))
}