diff --git a/plc4go/internal/bacnetip/bvll.go b/plc4go/internal/bacnetip/bvll.go index feb97caa786..b664af20677 100644 --- a/plc4go/internal/bacnetip/bvll.go +++ b/plc4go/internal/bacnetip/bvll.go @@ -286,7 +286,7 @@ func NewWriteBroadcastDistributionTable(opts ...func(*WriteBroadcastDistribution return b, nil } -func WithWriteBroadcastDistributionTable(bdt ...*Address) func(*WriteBroadcastDistributionTable) { +func WithWriteBroadcastDistributionTableBDT(bdt ...*Address) func(*WriteBroadcastDistributionTable) { return func(b *WriteBroadcastDistributionTable) { b.bvlciBDT = bdt } @@ -417,27 +417,101 @@ func (w *ReadBroadcastDistributionTable) String() string { return fmt.Sprintf("ReadBroadcastDistributionTable{%v}", w._BVLPDU) } -// TODO: finish type ReadBroadcastDistributionTableAck struct { *_BVLPDU + bvlciBDT []*Address } var _ BVLPDU = (*ReadBroadcastDistributionTableAck)(nil) -func NewReadBroadcastDistributionTableAck() (BVLPDU, error) { +func NewReadBroadcastDistributionTableAck(opts ...func(*ReadBroadcastDistributionTableAck)) (*ReadBroadcastDistributionTableAck, error) { b := &ReadBroadcastDistributionTableAck{} - b._BVLPDU = NewBVLPDU(nil).(*_BVLPDU) + for _, opt := range opts { + opt(b) + } + b._BVLPDU = NewBVLPDU(readWriteModel.NewBVLCReadBroadcastDistributionTableAck(b.produceBroadcastDistributionTable(), 0)).(*_BVLPDU) return b, nil } -func (b *ReadBroadcastDistributionTableAck) Encode(pdu Arg) error { - // TODO: finish - return nil +func WithReadBroadcastDistributionTableAckBDT(bdt ...*Address) func(*ReadBroadcastDistributionTableAck) { + return func(b *ReadBroadcastDistributionTableAck) { + b.bvlciBDT = bdt + } } -func (b *ReadBroadcastDistributionTableAck) Decode(pdu Arg) error { - // TODO: finish - return nil +func (w *ReadBroadcastDistributionTableAck) GetBvlciBDT() []*Address { + return w.bvlciBDT +} + +func (w *ReadBroadcastDistributionTableAck) produceBroadcastDistributionTable() (entries []readWriteModel.BVLCBroadcastDistributionTableEntry) { + for _, address := range w.bvlciBDT { + addr := address.AddrAddress[:4] + port := uint16(47808) + if address.AddrPort != nil { + port = *address.AddrPort + } + mask := make([]byte, 4) + if address.AddrMask != nil { + binary.BigEndian.PutUint32(mask, *address.AddrMask) + } + entries = append(entries, readWriteModel.NewBVLCBroadcastDistributionTableEntry(addr, port, mask)) + } + return +} + +func (w *ReadBroadcastDistributionTableAck) produceBvlciBDT(entries []readWriteModel.BVLCBroadcastDistributionTableEntry) (bvlciBDT []*Address) { + for _, entry := range entries { + addr := entry.GetIp() + port := entry.GetPort() + var portArray = make([]byte, 2) + binary.BigEndian.PutUint16(portArray, port) + address, _ := NewAddress(zerolog.Nop(), append(addr, portArray...)) + mask := binary.BigEndian.Uint32(entry.GetBroadcastDistributionMap()) + address.AddrMask = &mask + bvlciBDT = append(bvlciBDT, address) + } + return +} + +func (w *ReadBroadcastDistributionTableAck) Encode(bvlpdu Arg) error { + switch bvlpdu := bvlpdu.(type) { + case BVLPDU: + if err := bvlpdu.Update(w); err != nil { + return errors.Wrap(err, "error updating BVLPDU") + } + for _, bdte := range w.bvlciBDT { + bvlpdu.PutData(bdte.AddrAddress...) + bvlpdu.PutLong(*bdte.AddrMask) + } + bvlpdu.setBVLC(w.bvlc) + return nil + default: + return errors.Errorf("invalid BVLPDU type %T", bvlpdu) + } +} + +func (w *ReadBroadcastDistributionTableAck) 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.BVLCReadBroadcastDistributionTableAckExactly: + switch bvlc := pduUserData.(type) { + case readWriteModel.BVLCReadBroadcastDistributionTableAck: + w.setBVLC(bvlc) + w.bvlciBDT = w.produceBvlciBDT(bvlc.GetTable()) + } + } + return nil + default: + return errors.Errorf("invalid BVLPDU type %T", bvlpdu) + } +} + +func (w *ReadBroadcastDistributionTableAck) String() string { + return fmt.Sprintf("ReadBroadcastDistributionTableAck{%v, bvlciBDT: %v}", w._BVLPDU, w.bvlciBDT) } // TODO: finish diff --git a/plc4go/internal/bacnetip/tests/state_machine.go b/plc4go/internal/bacnetip/tests/state_machine.go index dae5d478bc4..8fe2c04cda2 100644 --- a/plc4go/internal/bacnetip/tests/state_machine.go +++ b/plc4go/internal/bacnetip/tests/state_machine.go @@ -282,11 +282,15 @@ func MatchPdu(localLog zerolog.Logger, pdu bacnetip.PDU, pduType any, pduAttrs m } return r.GetBvlciResultCode() == attrValue case bacnetip.KWBvlciBDT: - wbdt, ok := pdu.(*bacnetip.WriteBroadcastDistributionTable) - if !ok { + var iwbdt []*bacnetip.Address + switch pdu := pdu.(type) { + case *bacnetip.WriteBroadcastDistributionTable: + iwbdt = pdu.GetBvlciBDT() + case *bacnetip.ReadBroadcastDistributionTableAck: + iwbdt = pdu.GetBvlciBDT() + default: return false } - iwbdt := wbdt.GetBvlciBDT() owbdt, ok := attrValue.([]*bacnetip.Address) if !ok { return false 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 e876ea0538f..a937a00ede0 100644 --- a/plc4go/internal/bacnetip/tests/test_bvll/test_codec_test.go +++ b/plc4go/internal/bacnetip/tests/test_bvll/test_codec_test.go @@ -40,7 +40,7 @@ func Result(i uint16) *bacnetip.Result { } func WriteBroadcastDistributionTable(bdt ...*bacnetip.Address) *bacnetip.WriteBroadcastDistributionTable { - writeBroadcastDistributionTable, err := bacnetip.NewWriteBroadcastDistributionTable(bacnetip.WithWriteBroadcastDistributionTable(bdt...)) + writeBroadcastDistributionTable, err := bacnetip.NewWriteBroadcastDistributionTable(bacnetip.WithWriteBroadcastDistributionTableBDT(bdt...)) if err != nil { panic(err) } @@ -55,6 +55,14 @@ func ReadBroadcastDistributionTable() *bacnetip.ReadBroadcastDistributionTable { return readBroadcastDistributionTable } +func ReadBroadcastDistributionTableAck(bdt ...*bacnetip.Address) *bacnetip.ReadBroadcastDistributionTableAck { + readBroadcastDistributionTable, err := bacnetip.NewReadBroadcastDistributionTableAck(bacnetip.WithReadBroadcastDistributionTableAckBDT(bdt...)) + if err != nil { + panic(err) + } + return readBroadcastDistributionTable +} + type TestAnnexJCodecSuite struct { suite.Suite @@ -227,6 +235,50 @@ func (suite *TestAnnexJCodecSuite) TestReadBroadcastDistributionTable() { err = suite.Confirmation(bacnetip.NewArgs((*bacnetip.ReadBroadcastDistributionTable)(nil)), bacnetip.NoKWArgs) } +func (suite *TestAnnexJCodecSuite) TestReadBroadcastDistributionTableAck() { + // Read an empty TableAck + pduBytes, err := bacnetip.Xtob("81.03.0004") + 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(ReadBroadcastDistributionTableAck()), 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.ReadBroadcastDistributionTableAck)(nil)), bacnetip.NewKWArgs(bacnetip.KWBvlciBDT, []*bacnetip.Address{})) + + // Read TableAck with an element + addr, _ := bacnetip.NewAddress(zerolog.Nop(), "192.168.0.254/24") + pduBytes, err = bacnetip.Xtob("81.03.000e" + //bvlci + "c0.a8.00.fe.ba.c0 ff.ff.ff.00") // address and mask + 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(ReadBroadcastDistributionTableAck(addr)), 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.ReadBroadcastDistributionTableAck)(nil)), bacnetip.NewKWArgs(bacnetip.KWBvlciBDT, []*bacnetip.Address{addr})) +} + func TestAnnexJCodec(t *testing.T) { suite.Run(t, new(TestAnnexJCodecSuite)) }