You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@plc4x.apache.org by sr...@apache.org on 2022/11/09 15:40:27 UTC

[plc4x] branch develop updated (e6de5d560a -> 863eb4e22a)

This is an automated email from the ASF dual-hosted git repository.

sruehl pushed a change to branch develop
in repository https://gitbox.apache.org/repos/asf/plc4x.git


    from e6de5d560a added include at proper location
     new ba86633aab fix(bacnet): use BACnetConfirmedServiceChoice where appropriate
     new 863eb4e22a feat(plc4go/bacnet): partial transaction state machine

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../protocols/bacnet/ParserSerializerTestsuite.xml | 100 ++++-
 plc4go/internal/bacnetip/DeviceInventory.go        |  26 +-
 plc4go/internal/bacnetip/MessageCodec.go           |   5 +-
 .../internal/bacnetip/TransactionStateMachine.go   | 375 +++++++++++++++++-
 .../bacnetip/TransactionStateMachine_test.go       | 429 +++++++++++++++++++++
 .../bacnetip/readwrite/model/APDUComplexAck.go     |  28 +-
 .../readwrite/model/APDUConfirmedRequest.go        |  28 +-
 .../bacnetip/readwrite/model/APDUSimpleAck.go      |  25 +-
 .../bacnetip/readwrite/model/StaticHelper.go       |   7 +-
 .../resources/protocols/bacnetip/bacnetip.mspec    |   9 +-
 .../protocols/bacnet/ParserSerializerTestsuite.xml | 100 ++++-
 11 files changed, 1062 insertions(+), 70 deletions(-)
 create mode 100644 plc4go/internal/bacnetip/TransactionStateMachine_test.go


[plc4x] 02/02: feat(plc4go/bacnet): partial transaction state machine

Posted by sr...@apache.org.
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

commit 863eb4e22a6e960c630ce144232f80786b773696
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed Nov 9 16:40:12 2022 +0100

    feat(plc4go/bacnet): partial transaction state machine
    
    + ported code from bacpypes
---
 plc4go/internal/bacnetip/DeviceInventory.go        |  26 +-
 plc4go/internal/bacnetip/MessageCodec.go           |   5 +-
 .../internal/bacnetip/TransactionStateMachine.go   | 375 +++++++++++++++++-
 .../bacnetip/TransactionStateMachine_test.go       | 429 +++++++++++++++++++++
 4 files changed, 803 insertions(+), 32 deletions(-)

diff --git a/plc4go/internal/bacnetip/DeviceInventory.go b/plc4go/internal/bacnetip/DeviceInventory.go
index a067017775..c2beee7b7c 100644
--- a/plc4go/internal/bacnetip/DeviceInventory.go
+++ b/plc4go/internal/bacnetip/DeviceInventory.go
@@ -22,7 +22,6 @@ package bacnetip
 import (
 	readWriteModel "github.com/apache/plc4x/plc4go/protocols/bacnetip/readwrite/model"
 	"github.com/pkg/errors"
-	"math"
 	"sync"
 	"time"
 )
@@ -44,18 +43,25 @@ func (d *DeviceInventory) getEntryForDestination(destination []uint8) (DeviceEnt
 }
 
 var NoDeviceEntry = DeviceEntry{
-	MaximumApduLengthAcceptedLength: readWriteModel.NewBACnetTagPayloadUnsignedInteger(nil, nil, nil, nil, nil, nil, nil, func() *uint64 {
-		var maxUint64 uint64 = math.MaxUint64
-		return &maxUint64
-	}(), 4),
+	DeviceIdentifier:          nil,
+	MaximumApduLengthAccepted: readWriteModel.MaxApduLengthAccepted_NUM_OCTETS_1024,
+	SegmentationSupported:     readWriteModel.BACnetSegmentation_SEGMENTED_BOTH,
+	MaxSegmentsAccepted:       16,
+	APDUSegmentTimeout:        5000,
+	APDUTimeout:               3000,
+	NumberOfAPDURetries:       3,
 }
 
 type DeviceEntry struct {
-	DeviceIdentifier                readWriteModel.BACnetTagPayloadObjectIdentifier
-	MaximumApduLengthAcceptedLength readWriteModel.BACnetTagPayloadUnsignedInteger
-	SegmentationSupported           bool
-	VendorId                        readWriteModel.BACnetVendorId
-	DeviceObjects                   []DeviceObject
+	DeviceIdentifier          readWriteModel.BACnetTagPayloadObjectIdentifier
+	MaximumApduLengthAccepted readWriteModel.MaxApduLengthAccepted
+	SegmentationSupported     readWriteModel.BACnetSegmentation
+	MaxSegmentsAccepted       readWriteModel.MaxSegmentsAccepted
+	APDUSegmentTimeout        uint
+	APDUTimeout               uint
+	NumberOfAPDURetries       uint
+	VendorId                  readWriteModel.BACnetVendorId
+	DeviceObjects             []DeviceObject
 }
 
 func (d DeviceEntry) GetDeviceObjects(filter ...DeviceObjectFilter) []DeviceObject {
diff --git a/plc4go/internal/bacnetip/MessageCodec.go b/plc4go/internal/bacnetip/MessageCodec.go
index 8675796d87..528884a7e8 100644
--- a/plc4go/internal/bacnetip/MessageCodec.go
+++ b/plc4go/internal/bacnetip/MessageCodec.go
@@ -35,10 +35,7 @@ type ApplicationLayerMessageCodec struct {
 
 func NewApplicationLayerMessageCodec(transportInstance transports.TransportInstance, deviceInventory *DeviceInventory) *ApplicationLayerMessageCodec {
 	return &ApplicationLayerMessageCodec{
-		TransactionStateMachine{
-			MessageCodec:    NewMessageCodec(transportInstance),
-			deviceInventory: deviceInventory,
-		},
+		NewTransactionStateMachine(NewMessageCodec(transportInstance), deviceInventory),
 	}
 }
 
diff --git a/plc4go/internal/bacnetip/TransactionStateMachine.go b/plc4go/internal/bacnetip/TransactionStateMachine.go
index 21b0c3147b..68cc443ef2 100644
--- a/plc4go/internal/bacnetip/TransactionStateMachine.go
+++ b/plc4go/internal/bacnetip/TransactionStateMachine.go
@@ -44,6 +44,23 @@ type TransactionStateMachine struct {
 	RequestTimer          int
 }
 
+func NewTransactionStateMachine(messageCodec *MessageCodec, deviceInventory *DeviceInventory) TransactionStateMachine {
+	return TransactionStateMachine{
+		MessageCodec:          messageCodec,
+		deviceInventory:       deviceInventory,
+		retryCount:            3,
+		segmentRetryCount:     3,
+		duplicateCount:        0,
+		sentAllSegments:       false,
+		lastSequenceNumber:    0,
+		initialSequenceNumber: 0,
+		actualWindowSize:      0,
+		proposeWindowSize:     2,
+		segmentTimer:          1500,
+		RequestTimer:          3000,
+	}
+}
+
 func (t *TransactionStateMachine) GetCodec() spi.MessageCodec {
 	return t
 }
@@ -89,7 +106,7 @@ func (t *TransactionStateMachine) handleOutboundMessage(message spi.Message) (ha
 			return false, nil
 		}
 		if npdu.GetControl().GetMessageTypeFieldPresent() {
-			log.Trace().Msg("No message type field present")
+			log.Trace().Msg("Message type field present")
 			return false, nil
 		}
 		var entryForDestination = NoDeviceEntry
@@ -106,27 +123,12 @@ func (t *TransactionStateMachine) handleOutboundMessage(message spi.Message) (ha
 		apdu := npdu.GetApdu()
 		switch apdu := apdu.(type) {
 		case readWriteModel.APDUConfirmedRequestExactly:
+			// TODO: this is a "client" request
 			// TODO: check if adpu length is the magic number (it should be "unencoded")
-			maximumApduLengthForDevice := entryForDestination.MaximumApduLengthAcceptedLength.GetActualValue()
-			apduLengthDoesOverflow := uint64(apdu.GetLengthInBytes()) > maximumApduLengthForDevice
-			if apduLengthDoesOverflow && !entryForDestination.SegmentationSupported {
-				return false, errors.Errorf("We have a overflow. We need %d bytes, but device only supports a max of %d and no segmentation", apdu.GetLengthInBytes(), maximumApduLengthForDevice)
-			}
-			// TODO: handle potential retry
-			if apduLengthDoesOverflow {
-				// TODO: handle potential segmentation
-			}
 			return false, nil
 		case readWriteModel.APDUComplexAckExactly:
+			// TODO: this is a "server" response
 			// TODO: check if adpu length is the magic number (it should be "unencoded")
-			maximumApduLengthForDevice := entryForDestination.MaximumApduLengthAcceptedLength.GetActualValue()
-			apduLengthDoesOverflow := uint64(apdu.GetLengthInBytes()) > maximumApduLengthForDevice
-			if apduLengthDoesOverflow && !entryForDestination.SegmentationSupported {
-				return false, errors.Errorf("We have a overflow. We need %d bytes, but device only supports a max of %d and no segmentation", apdu.GetLengthInBytes(), maximumApduLengthForDevice)
-			}
-			if apduLengthDoesOverflow {
-				// TODO: handle potential segmentation
-			}
 			return false, nil
 		default:
 			log.Trace().Msgf("APDU type not relevant %T present", apdu)
@@ -137,3 +139,340 @@ func (t *TransactionStateMachine) handleOutboundMessage(message spi.Message) (ha
 		return false, nil
 	}
 }
+
+// TODO: this is a placeholder for a tasking framework
+type _Task struct {
+	taskTime    time.Time
+	isScheduled bool
+}
+
+func (t *_Task) installTask(when *time.Time, delta *time.Duration) {
+	// TODO: schedule task
+}
+
+func (t *_Task) suspendTask() {
+	// TODO: suspend task
+}
+
+func (t *_Task) resume() {
+	// TODO: resume task
+}
+
+type OneShotTask struct {
+	_Task
+}
+
+// TODO: this is the interface to the outside for the SSM
+type ServiceAccessPoint interface {
+	GetDeviceInventory() *DeviceInventory
+	GetLocalDevice() DeviceEntry
+	GetProposedWindowSize() uint8
+	Request(apdu readWriteModel.APDU)
+	// TODO: wrap that properly
+	GetClientTransactions() []interface{}
+}
+
+type SSMState uint8
+
+const (
+	IDLE SSMState = iota
+	SEGMENTED_REQUEST
+	AWAIT_CONFIRMATION
+	AWAIT_RESPONSE
+	SEGMENTED_RESPONSE
+	SEGMENTED_CONFIRMATION
+	COMPLETED
+	ABORTED
+)
+
+func (s SSMState) String() string {
+	switch s {
+	case IDLE:
+		return "IDLE"
+	case SEGMENTED_REQUEST:
+		return "SEGMENTED_REQUEST"
+	case AWAIT_CONFIRMATION:
+		return "AWAIT_CONFIRMATION"
+	case AWAIT_RESPONSE:
+		return "AWAIT_RESPONSE"
+	case SEGMENTED_RESPONSE:
+		return "SEGMENTED_RESPONSE"
+	case SEGMENTED_CONFIRMATION:
+		return "SEGMENTED_CONFIRMATION"
+	case COMPLETED:
+		return "COMPLETED"
+	case ABORTED:
+		return "ABORTED"
+	default:
+		return "Unknown"
+	}
+}
+
+type segmentAPDU struct {
+	originalApdu     readWriteModel.APDU
+	originalInvokeId uint8
+	serviceBytes     []byte
+	serviceChoice    readWriteModel.BACnetConfirmedServiceChoice
+	isAck            bool
+}
+
+// SSM - Segmentation State Machine
+type SSM struct {
+	OneShotTask
+
+	ssmSAP ServiceAccessPoint
+
+	pduAddress  []byte
+	deviceEntry DeviceEntry
+
+	invokeId uint8
+
+	state        SSMState
+	segmentAPDU  *segmentAPDU // TODO: rename that to segmentAPDUSource or something
+	segmentSize  uint
+	segmentCount uint8
+
+	retryCount            uint
+	segmentRetryCount     uint
+	sentAllSegments       bool
+	lastSequenceNumber    uint8
+	initialSequenceNumber uint8
+	actualWindowSize      uint8
+
+	numberOfApduRetries   uint
+	apduTimeout           uint
+	segmentationSupported readWriteModel.BACnetSegmentation
+	segmentTimeout        uint
+	maxSegmentsAccepted   readWriteModel.MaxSegmentsAccepted
+	maxApduLengthAccepted readWriteModel.MaxApduLengthAccepted
+}
+
+func NewSSM(sap ServiceAccessPoint, pduAddress []byte) (SSM, error) {
+	deviceEntry, err := sap.GetDeviceInventory().getEntryForDestination(pduAddress)
+	if err != nil {
+		return SSM{}, errors.Wrap(err, "Can't create SSM")
+	}
+	localDevice := sap.GetLocalDevice()
+	return SSM{
+		ssmSAP:                sap,
+		pduAddress:            pduAddress,
+		deviceEntry:           deviceEntry,
+		state:                 IDLE,
+		numberOfApduRetries:   localDevice.NumberOfAPDURetries,
+		apduTimeout:           localDevice.APDUTimeout,
+		segmentationSupported: localDevice.SegmentationSupported,
+		segmentTimeout:        localDevice.APDUSegmentTimeout,
+		maxSegmentsAccepted:   localDevice.MaxSegmentsAccepted,
+		maxApduLengthAccepted: localDevice.MaximumApduLengthAccepted,
+	}, nil
+}
+
+func (s *SSM) startTimer(millis int64) {
+	s.restartTimer(millis)
+}
+
+func (s *SSM) stopTimer() {
+	if s.isScheduled {
+		s.suspendTask()
+	}
+}
+
+func (s *SSM) restartTimer(millis int64) {
+	if s.isScheduled {
+		s.suspendTask()
+	}
+
+	delta := time.Millisecond * time.Duration(millis)
+	s.installTask(nil, &delta)
+}
+
+func (s *SSM) setState(newState SSMState, timer *int64) error {
+	if s.state == COMPLETED || s.state == ABORTED {
+		return errors.Errorf("Invalid state transition from %s to %s", s.state, newState)
+	}
+
+	s.stopTimer()
+
+	s.state = newState
+
+	if timer != nil {
+		s.startTimer(*timer)
+	}
+	return nil
+}
+
+func (s *SSM) setSegmentationContext(apdu readWriteModel.APDU) error {
+	switch apdu := apdu.(type) {
+	case readWriteModel.APDUConfirmedRequestExactly:
+		if apdu.GetSegmentedMessage() || apdu.GetMoreFollows() {
+			return errors.New("Can't handle already segmented message")
+		}
+		bytes, err := apdu.GetServiceRequest().Serialize()
+		if err != nil {
+			return errors.Wrap(err, "Can serialize service request")
+		}
+		segmentAPDU := segmentAPDU{
+			originalApdu:     apdu,
+			originalInvokeId: apdu.GetInvokeId(),
+			serviceBytes:     bytes,
+			serviceChoice:    apdu.GetServiceRequest().GetServiceChoice(),
+		}
+		s.segmentAPDU = &segmentAPDU
+	case readWriteModel.APDUComplexAckExactly:
+		if apdu.GetSegmentedMessage() || apdu.GetMoreFollows() {
+			return errors.New("Can't handle already segmented message")
+		}
+		bytes, err := apdu.GetServiceAck().Serialize()
+		if err != nil {
+			return errors.Wrap(err, "Can serialize service request")
+		}
+		segmentAPDU := segmentAPDU{
+			originalApdu:  apdu,
+			serviceBytes:  bytes,
+			serviceChoice: apdu.GetServiceAck().GetServiceChoice(),
+			isAck:         true,
+		}
+		s.segmentAPDU = &segmentAPDU
+	default:
+		return errors.Errorf("invalid APDU type %T", apdu)
+	}
+	return nil
+}
+
+func (s *SSM) getSegment(index uint8) (segmentAPDU readWriteModel.APDU, moreFollows bool, err error) {
+	if s.segmentAPDU == nil {
+		return nil, false, errors.New("No segment apdu set")
+	}
+
+	if index > s.segmentCount {
+		return nil, false, errors.Errorf("Invalid segment number %d, APDU has %d segments", index, s.segmentCount)
+	}
+
+	// TODO: the original code does here something funky but it seems it is best to just return the original apdu
+	if s.segmentCount == 1 {
+		return s.segmentAPDU.originalApdu, false, nil
+	}
+
+	moreFollows = index < s.segmentCount-1
+	sequenceNumber := index % 255
+	proposedWindowSize := s.actualWindowSize
+	if index == 0 {
+		proposedWindowSize = s.ssmSAP.GetProposedWindowSize()
+	}
+	serviceChoice := &s.segmentAPDU.serviceChoice
+	offset := uint(index) * s.segmentSize
+	segmentBytes := s.segmentAPDU.serviceBytes[offset : offset+s.segmentSize]
+	if !s.segmentAPDU.isAck {
+		segmentAPDU = readWriteModel.NewAPDUConfirmedRequest(
+			true,
+			moreFollows,
+			s.segmentationSupported == readWriteModel.BACnetSegmentation_SEGMENTED_RECEIVE || s.segmentationSupported == readWriteModel.BACnetSegmentation_SEGMENTED_BOTH,
+			s.maxSegmentsAccepted,
+			s.maxApduLengthAccepted,
+			s.segmentAPDU.originalInvokeId,
+			&sequenceNumber,
+			&proposedWindowSize,
+			nil,
+			serviceChoice,
+			segmentBytes,
+			0,
+		)
+	} else {
+		segmentAPDU = readWriteModel.NewAPDUComplexAck(
+			true,
+			moreFollows,
+			s.segmentAPDU.originalInvokeId,
+			&sequenceNumber,
+			&proposedWindowSize,
+			nil,
+			serviceChoice,
+			segmentBytes,
+			0,
+		)
+	}
+	return segmentAPDU, moreFollows, nil
+}
+
+// TODO: check that function. looks a bit wonky to just append the payloads like that
+func (s *SSM) appendSegment(apdu readWriteModel.APDU) error {
+	switch apdu := apdu.(type) {
+	case readWriteModel.APDUConfirmedRequestExactly:
+		if apdu.GetSegmentedMessage() || apdu.GetMoreFollows() {
+			return errors.New("Can't handle already segmented message")
+		}
+		bytes, err := apdu.GetServiceRequest().Serialize()
+		if err != nil {
+			return errors.Wrap(err, "Can serialize service request")
+		}
+		s.segmentAPDU.serviceBytes = append(s.segmentAPDU.serviceBytes, bytes...)
+	case readWriteModel.APDUComplexAckExactly:
+		if apdu.GetSegmentedMessage() || apdu.GetMoreFollows() {
+			return errors.New("Can't handle already segmented message")
+		}
+		bytes, err := apdu.GetServiceAck().Serialize()
+		if err != nil {
+			return errors.Wrap(err, "Can serialize service request")
+		}
+		s.segmentAPDU.serviceBytes = append(s.segmentAPDU.serviceBytes, bytes...)
+	default:
+		return errors.Errorf("invalid APDU type %T", apdu)
+	}
+	return nil
+}
+
+func (s *SSM) inWindow(sequenceA, sequenceB uint) bool {
+	return (sequenceA-sequenceB-256)%256 < uint(s.actualWindowSize)
+}
+
+func (s *SSM) fillWindow(sequenceNumber uint8) error {
+	for i := uint8(0); i < s.actualWindowSize; i++ {
+		apdu, moreFollows, err := s.getSegment(sequenceNumber + i)
+		if err != nil {
+			return errors.Wrapf(err, "Error sending out segment %d", i)
+		}
+		s.ssmSAP.Request(apdu)
+		if moreFollows {
+			s.sentAllSegments = true
+		}
+	}
+	return nil
+}
+
+type ClientSSM struct {
+	SSM
+}
+
+func NewClientSSM(sap ServiceAccessPoint, pduAddress []byte) (ClientSSM, error) {
+	ssm, err := NewSSM(sap, pduAddress)
+	if err != nil {
+		return ClientSSM{}, err
+	}
+	// TODO: if deviceEntry is not there get it now...
+	if &ssm.deviceEntry == &NoDeviceEntry {
+		// TODO: get entry for device, store it in inventory
+	}
+	return ClientSSM{
+		SSM: ssm,
+	}, nil
+}
+
+func (s *ClientSSM) setState(newState SSMState, timer *int64) error {
+	// do the regular state change
+	if err := s.SSM.setState(newState, timer); err != nil {
+		return errors.Wrap(err, "error during SSM state transition")
+	}
+
+	if s.state == COMPLETED || s.state == ABORTED {
+		s.ssmSAP.GetClientTransactions() // TODO remove this
+		if &s.deviceEntry != &NoDeviceEntry {
+			// TODO: release device entry
+		}
+	}
+	return nil
+}
+
+func (s *ClientSSM) request(apdu readWriteModel.APDU) {
+	// TODO: ensure apdu has destination, otherwise
+	// TODO: we would need a BVLC to send something or not... maybe the todo above is nonsense, as we are in a connection context
+	s.ssmSAP.Request(apdu)
+}
diff --git a/plc4go/internal/bacnetip/TransactionStateMachine_test.go b/plc4go/internal/bacnetip/TransactionStateMachine_test.go
new file mode 100644
index 0000000000..71944a3517
--- /dev/null
+++ b/plc4go/internal/bacnetip/TransactionStateMachine_test.go
@@ -0,0 +1,429 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package bacnetip
+
+import (
+	"context"
+	readWriteModel "github.com/apache/plc4x/plc4go/protocols/bacnetip/readwrite/model"
+	"github.com/apache/plc4x/plc4go/spi"
+	"github.com/apache/plc4x/plc4go/spi/transports/test"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestTransactionStateMachine_Expect(t1 *testing.T) {
+	type fields struct {
+		MessageCodec          *MessageCodec
+		deviceInventory       *DeviceInventory
+		retryCount            int
+		segmentRetryCount     int
+		duplicateCount        int
+		sentAllSegments       bool
+		lastSequenceNumber    int
+		initialSequenceNumber int
+		actualWindowSize      int
+		proposeWindowSize     int
+		segmentTimer          int
+		RequestTimer          int
+	}
+	type args struct {
+		ctx            context.Context
+		acceptsMessage spi.AcceptsMessage
+		handleMessage  spi.HandleMessage
+		handleError    spi.HandleError
+		ttl            time.Duration
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+	}{
+		// TODO: Add test cases.
+	}
+	for _, tt := range tests {
+		t1.Run(tt.name, func(t1 *testing.T) {
+			t := &TransactionStateMachine{
+				MessageCodec:          tt.fields.MessageCodec,
+				deviceInventory:       tt.fields.deviceInventory,
+				retryCount:            tt.fields.retryCount,
+				segmentRetryCount:     tt.fields.segmentRetryCount,
+				duplicateCount:        tt.fields.duplicateCount,
+				sentAllSegments:       tt.fields.sentAllSegments,
+				lastSequenceNumber:    tt.fields.lastSequenceNumber,
+				initialSequenceNumber: tt.fields.initialSequenceNumber,
+				actualWindowSize:      tt.fields.actualWindowSize,
+				proposeWindowSize:     tt.fields.proposeWindowSize,
+				segmentTimer:          tt.fields.segmentTimer,
+				RequestTimer:          tt.fields.RequestTimer,
+			}
+			if err := t.Expect(tt.args.ctx, tt.args.acceptsMessage, tt.args.handleMessage, tt.args.handleError, tt.args.ttl); (err != nil) != tt.wantErr {
+				t1.Errorf("Expect() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestTransactionStateMachine_GetCodec(t1 *testing.T) {
+	type fields struct {
+		MessageCodec          *MessageCodec
+		deviceInventory       *DeviceInventory
+		retryCount            int
+		segmentRetryCount     int
+		duplicateCount        int
+		sentAllSegments       bool
+		lastSequenceNumber    int
+		initialSequenceNumber int
+		actualWindowSize      int
+		proposeWindowSize     int
+		segmentTimer          int
+		RequestTimer          int
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   spi.MessageCodec
+	}{
+		// TODO: Add test cases.
+	}
+	for _, tt := range tests {
+		t1.Run(tt.name, func(t1 *testing.T) {
+			t := &TransactionStateMachine{
+				MessageCodec:          tt.fields.MessageCodec,
+				deviceInventory:       tt.fields.deviceInventory,
+				retryCount:            tt.fields.retryCount,
+				segmentRetryCount:     tt.fields.segmentRetryCount,
+				duplicateCount:        tt.fields.duplicateCount,
+				sentAllSegments:       tt.fields.sentAllSegments,
+				lastSequenceNumber:    tt.fields.lastSequenceNumber,
+				initialSequenceNumber: tt.fields.initialSequenceNumber,
+				actualWindowSize:      tt.fields.actualWindowSize,
+				proposeWindowSize:     tt.fields.proposeWindowSize,
+				segmentTimer:          tt.fields.segmentTimer,
+				RequestTimer:          tt.fields.RequestTimer,
+			}
+			if got := t.GetCodec(); !reflect.DeepEqual(got, tt.want) {
+				t1.Errorf("GetCodec() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestTransactionStateMachine_Send(t1 *testing.T) {
+	type fields struct {
+		MessageCodec          *MessageCodec
+		deviceInventory       *DeviceInventory
+		retryCount            int
+		segmentRetryCount     int
+		duplicateCount        int
+		sentAllSegments       bool
+		lastSequenceNumber    int
+		initialSequenceNumber int
+		actualWindowSize      int
+		proposeWindowSize     int
+		segmentTimer          int
+		RequestTimer          int
+	}
+	type args struct {
+		message spi.Message
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+	}{
+		// TODO: Add test cases.
+	}
+	for _, tt := range tests {
+		t1.Run(tt.name, func(t1 *testing.T) {
+			t := &TransactionStateMachine{
+				MessageCodec:          tt.fields.MessageCodec,
+				deviceInventory:       tt.fields.deviceInventory,
+				retryCount:            tt.fields.retryCount,
+				segmentRetryCount:     tt.fields.segmentRetryCount,
+				duplicateCount:        tt.fields.duplicateCount,
+				sentAllSegments:       tt.fields.sentAllSegments,
+				lastSequenceNumber:    tt.fields.lastSequenceNumber,
+				initialSequenceNumber: tt.fields.initialSequenceNumber,
+				actualWindowSize:      tt.fields.actualWindowSize,
+				proposeWindowSize:     tt.fields.proposeWindowSize,
+				segmentTimer:          tt.fields.segmentTimer,
+				RequestTimer:          tt.fields.RequestTimer,
+			}
+			if err := t.Send(tt.args.message); (err != nil) != tt.wantErr {
+				t1.Errorf("Send() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestTransactionStateMachine_SendRequest(t1 *testing.T) {
+	type fields struct {
+		MessageCodec          *MessageCodec
+		deviceInventory       *DeviceInventory
+		retryCount            int
+		segmentRetryCount     int
+		duplicateCount        int
+		sentAllSegments       bool
+		lastSequenceNumber    int
+		initialSequenceNumber int
+		actualWindowSize      int
+		proposeWindowSize     int
+		segmentTimer          int
+		RequestTimer          int
+	}
+	type args struct {
+		ctx            context.Context
+		message        spi.Message
+		acceptsMessage spi.AcceptsMessage
+		handleMessage  spi.HandleMessage
+		handleError    spi.HandleError
+		ttl            time.Duration
+	}
+	tests := []struct {
+		name    string
+		fields  fields
+		args    args
+		wantErr bool
+	}{
+		// TODO: Add test cases.
+	}
+	for _, tt := range tests {
+		t1.Run(tt.name, func(t1 *testing.T) {
+			t := &TransactionStateMachine{
+				MessageCodec:          tt.fields.MessageCodec,
+				deviceInventory:       tt.fields.deviceInventory,
+				retryCount:            tt.fields.retryCount,
+				segmentRetryCount:     tt.fields.segmentRetryCount,
+				duplicateCount:        tt.fields.duplicateCount,
+				sentAllSegments:       tt.fields.sentAllSegments,
+				lastSequenceNumber:    tt.fields.lastSequenceNumber,
+				initialSequenceNumber: tt.fields.initialSequenceNumber,
+				actualWindowSize:      tt.fields.actualWindowSize,
+				proposeWindowSize:     tt.fields.proposeWindowSize,
+				segmentTimer:          tt.fields.segmentTimer,
+				RequestTimer:          tt.fields.RequestTimer,
+			}
+			if err := t.SendRequest(tt.args.ctx, tt.args.message, tt.args.acceptsMessage, tt.args.handleMessage, tt.args.handleError, tt.args.ttl); (err != nil) != tt.wantErr {
+				t1.Errorf("SendRequest() error = %v, wantErr %v", err, tt.wantErr)
+			}
+		})
+	}
+}
+
+func TestTransactionStateMachine_handleOutboundMessage(t1 *testing.T) {
+	type fields struct {
+		MessageCodec          *MessageCodec
+		deviceInventory       *DeviceInventory
+		retryCount            int
+		segmentRetryCount     int
+		duplicateCount        int
+		sentAllSegments       bool
+		lastSequenceNumber    int
+		initialSequenceNumber int
+		actualWindowSize      int
+		proposeWindowSize     int
+		segmentTimer          int
+		RequestTimer          int
+	}
+	type args struct {
+		message spi.Message
+	}
+	tests := []struct {
+		name        string
+		fields      fields
+		args        args
+		wantHandled bool
+		wantErr     bool
+	}{
+		{
+			name: "message not relevant",
+		},
+		{
+			name: "Normal unsgemented message",
+			fields: fields{
+				MessageCodec:          NewMessageCodec(test.NewTransportInstance(test.NewTransport())),
+				deviceInventory:       nil,
+				retryCount:            0,
+				segmentRetryCount:     0,
+				duplicateCount:        0,
+				sentAllSegments:       false,
+				lastSequenceNumber:    0,
+				initialSequenceNumber: 0,
+				actualWindowSize:      0,
+				proposeWindowSize:     0,
+				segmentTimer:          0,
+				RequestTimer:          0,
+			},
+			args: args{
+				message: readWriteModel.NewBVLCOriginalUnicastNPDU(
+					readWriteModel.NewNPDU(
+						1,
+						readWriteModel.NewNPDUControl(false, false, false, false, readWriteModel.NPDUNetworkPriority_NORMAL_MESSAGE),
+						nil,
+						nil,
+						nil,
+						nil,
+						nil,
+						nil,
+						nil,
+						nil,
+						readWriteModel.NewAPDUComplexAck(
+							false,
+							false,
+							13,
+							nil,
+							nil,
+							readWriteModel.NewBACnetServiceAckReadProperty(
+								readWriteModel.CreateBACnetContextTagObjectIdentifier(0, 2, 1),
+								readWriteModel.CreateBACnetPropertyIdentifierTagged(1, 85),
+								nil,
+								readWriteModel.NewBACnetConstructedDataAnalogValuePresentValue(
+									readWriteModel.CreateBACnetApplicationTagReal(101),
+									readWriteModel.CreateBACnetOpeningTag(3),
+									readWriteModel.CreateBACnetTagHeaderBalanced(true, 3, 3),
+									readWriteModel.CreateBACnetClosingTag(3),
+									3,
+									nil,
+								),
+								0,
+							),
+							nil,
+							nil,
+							0,
+						),
+						0,
+					),
+					0,
+				),
+			},
+		},
+		{
+			name: "Normal segmented message",
+			fields: fields{
+				MessageCodec: NewMessageCodec(test.NewTransportInstance(test.NewTransport())),
+				deviceInventory: func() *DeviceInventory {
+					var deviceInventory = DeviceInventory{
+						devices: map[string]DeviceEntry{
+							"123": {
+								DeviceIdentifier:          nil,
+								MaximumApduLengthAccepted: readWriteModel.MaxApduLengthAccepted_NUM_OCTETS_206,
+								SegmentationSupported:     readWriteModel.BACnetSegmentation_SEGMENTED_BOTH,
+								VendorId:                  0,
+								DeviceObjects:             nil,
+							},
+						},
+					}
+					return &deviceInventory
+				}(),
+				retryCount:            0,
+				segmentRetryCount:     0,
+				duplicateCount:        0,
+				sentAllSegments:       false,
+				lastSequenceNumber:    0,
+				initialSequenceNumber: 0,
+				actualWindowSize:      0,
+				proposeWindowSize:     0,
+				segmentTimer:          0,
+				RequestTimer:          0,
+			},
+			args: args{
+				message: readWriteModel.NewBVLCOriginalUnicastNPDU(
+					readWriteModel.NewNPDU(
+						1,
+						readWriteModel.NewNPDUControl(false, true, false, false, readWriteModel.NPDUNetworkPriority_NORMAL_MESSAGE),
+						nil,
+						func() *uint8 {
+							var elements uint8 = 3
+							return &elements
+						}(),
+						[]uint8{0x31, 0x32, 0x33},
+						nil,
+						nil,
+						nil,
+						nil,
+						nil,
+						readWriteModel.NewAPDUComplexAck(
+							false,
+							false,
+							13,
+							nil,
+							nil,
+							readWriteModel.NewBACnetServiceAckReadProperty(
+								readWriteModel.CreateBACnetContextTagObjectIdentifier(0, 2, 1),
+								readWriteModel.CreateBACnetPropertyIdentifierTagged(1, 85),
+								nil,
+								readWriteModel.NewBACnetConstructedDataActionText(
+									readWriteModel.CreateBACnetApplicationTagUnsignedInteger(100),
+									func() []readWriteModel.BACnetApplicationTagCharacterString {
+										var characterStrings []readWriteModel.BACnetApplicationTagCharacterString
+										for i := 0; i < 100; i++ {
+											characterStrings = append(characterStrings, readWriteModel.CreateBACnetApplicationTagCharacterString(readWriteModel.BACnetCharacterEncoding_ISO_10646, "ALAAARM!!"))
+										}
+										return characterStrings
+									}(),
+									readWriteModel.CreateBACnetOpeningTag(3),
+									readWriteModel.CreateBACnetTagHeaderBalanced(true, 3, 3),
+									readWriteModel.CreateBACnetClosingTag(3),
+									3,
+									nil,
+								),
+								0,
+							),
+							nil,
+							nil,
+							0,
+						),
+						0,
+					),
+					0,
+				),
+			},
+		},
+	}
+	for _, tt := range tests {
+		t1.Run(tt.name, func(t1 *testing.T) {
+			t := &TransactionStateMachine{
+				MessageCodec:          tt.fields.MessageCodec,
+				deviceInventory:       tt.fields.deviceInventory,
+				retryCount:            tt.fields.retryCount,
+				segmentRetryCount:     tt.fields.segmentRetryCount,
+				duplicateCount:        tt.fields.duplicateCount,
+				sentAllSegments:       tt.fields.sentAllSegments,
+				lastSequenceNumber:    tt.fields.lastSequenceNumber,
+				initialSequenceNumber: tt.fields.initialSequenceNumber,
+				actualWindowSize:      tt.fields.actualWindowSize,
+				proposeWindowSize:     tt.fields.proposeWindowSize,
+				segmentTimer:          tt.fields.segmentTimer,
+				RequestTimer:          tt.fields.RequestTimer,
+			}
+			gotHandled, err := t.handleOutboundMessage(tt.args.message)
+			if (err != nil) != tt.wantErr {
+				t1.Errorf("handleOutboundMessage() error = %v, wantErr %v", err, tt.wantErr)
+				return
+			}
+			if gotHandled != tt.wantHandled {
+				t1.Errorf("handleOutboundMessage() gotHandled = %v, want %v", gotHandled, tt.wantHandled)
+			}
+		})
+	}
+}


[plc4x] 01/02: fix(bacnet): use BACnetConfirmedServiceChoice where appropriate

Posted by sr...@apache.org.
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

commit ba86633aab7bb7f56473fe583c1d4954766cc83d
Author: Sebastian Rühl <sr...@apache.org>
AuthorDate: Wed Nov 9 15:24:11 2022 +0100

    fix(bacnet): use BACnetConfirmedServiceChoice where appropriate
---
 .../protocols/bacnet/ParserSerializerTestsuite.xml | 100 ++++++++++++++++++++-
 .../bacnetip/readwrite/model/APDUComplexAck.go     |  28 ++++--
 .../readwrite/model/APDUConfirmedRequest.go        |  28 ++++--
 .../bacnetip/readwrite/model/APDUSimpleAck.go      |  25 ++++--
 .../bacnetip/readwrite/model/StaticHelper.go       |   7 +-
 .../resources/protocols/bacnetip/bacnetip.mspec    |   9 +-
 .../protocols/bacnet/ParserSerializerTestsuite.xml | 100 ++++++++++++++++++++-
 7 files changed, 259 insertions(+), 38 deletions(-)

diff --git a/plc4go/assets/testing/protocols/bacnet/ParserSerializerTestsuite.xml b/plc4go/assets/testing/protocols/bacnet/ParserSerializerTestsuite.xml
index 008a6588f3..f0dc20dea9 100644
--- a/plc4go/assets/testing/protocols/bacnet/ParserSerializerTestsuite.xml
+++ b/plc4go/assets/testing/protocols/bacnet/ParserSerializerTestsuite.xml
@@ -63,7 +63,9 @@
                   <APDUSimpleAck>
                     <reserved dataType="uint" bitLength="4">0</reserved>
                     <originalInvokeId dataType="uint" bitLength="8">2</originalInvokeId>
-                    <serviceChoice dataType="uint" bitLength="8">0</serviceChoice>
+                    <serviceChoice>
+                      <BACnetConfirmedServiceChoice dataType="uint" bitLength="8" stringRepresentation="ACKNOWLEDGE_ALARM">0</BACnetConfirmedServiceChoice>
+                    </serviceChoice>
                   </APDUSimpleAck>
                 </APDU>
               </apdu>
@@ -983,6 +985,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -1066,6 +1070,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -1226,6 +1232,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -1309,6 +1317,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -1469,6 +1479,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -1552,6 +1564,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -1712,6 +1726,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -1795,6 +1811,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -1955,6 +1973,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -2038,6 +2058,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -2198,6 +2220,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -2281,6 +2305,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -2441,6 +2467,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -2524,6 +2552,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -2684,6 +2714,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -2767,6 +2799,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -3106,6 +3140,8 @@
                         </BACnetConfirmedServiceRequestReinitializeDevice>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -3534,6 +3570,8 @@
                         </BACnetConfirmedServiceRequestReadProperty>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -3962,6 +4000,8 @@
                         </BACnetConfirmedServiceRequestReadProperty>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -4390,6 +4430,8 @@
                         </BACnetConfirmedServiceRequestReadProperty>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -4830,6 +4872,8 @@
                         </BACnetServiceAckReadProperty>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -5466,6 +5510,8 @@
                         </BACnetConfirmedServiceRequestWriteProperty>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -5897,6 +5943,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -5949,7 +5997,9 @@
                   <APDUSimpleAck>
                     <reserved dataType="uint" bitLength="4">0</reserved>
                     <originalInvokeId dataType="uint" bitLength="8">1</originalInvokeId>
-                    <serviceChoice dataType="uint" bitLength="8">17</serviceChoice>
+                    <serviceChoice>
+                      <BACnetConfirmedServiceChoice dataType="uint" bitLength="8" stringRepresentation="DEVICE_COMMUNICATION_CONTROL">17</BACnetConfirmedServiceChoice>
+                    </serviceChoice>
                   </APDUSimpleAck>
                 </APDU>
               </apdu>
@@ -6288,6 +6338,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -6616,6 +6668,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -6724,6 +6778,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -6832,6 +6888,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -6940,6 +6998,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -7176,6 +7236,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -7377,6 +7439,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -7430,7 +7494,9 @@
                   <APDUSimpleAck>
                     <reserved dataType="uint" bitLength="4">0</reserved>
                     <originalInvokeId dataType="uint" bitLength="8">51</originalInvokeId>
-                    <serviceChoice dataType="uint" bitLength="8">17</serviceChoice>
+                    <serviceChoice>
+                      <BACnetConfirmedServiceChoice dataType="uint" bitLength="8" stringRepresentation="DEVICE_COMMUNICATION_CONTROL">17</BACnetConfirmedServiceChoice>
+                    </serviceChoice>
                   </APDUSimpleAck>
                 </APDU>
               </apdu>
@@ -7988,6 +8054,8 @@
                         </BACnetConfirmedServiceRequestReinitializeDevice>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -8189,6 +8257,8 @@
                         </BACnetConfirmedServiceRequestReinitializeDevice>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -8242,7 +8312,9 @@
                   <APDUSimpleAck>
                     <reserved dataType="uint" bitLength="4">0</reserved>
                     <originalInvokeId dataType="uint" bitLength="8">53</originalInvokeId>
-                    <serviceChoice dataType="uint" bitLength="8">20</serviceChoice>
+                    <serviceChoice>
+                      <BACnetConfirmedServiceChoice dataType="uint" bitLength="8" stringRepresentation="REINITIALIZE_DEVICE">20</BACnetConfirmedServiceChoice>
+                    </serviceChoice>
                   </APDUSimpleAck>
                 </APDU>
               </apdu>
@@ -8405,6 +8477,8 @@
                         </BACnetConfirmedServiceRequestAtomicReadFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -8562,6 +8636,8 @@
                         </BACnetServiceAckAtomicReadFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -8725,6 +8801,8 @@
                         </BACnetConfirmedServiceRequestAtomicReadFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -8882,6 +8960,8 @@
                         </BACnetServiceAckAtomicReadFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -9045,6 +9125,8 @@
                         </BACnetConfirmedServiceRequestAtomicReadFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -9202,6 +9284,8 @@
                         </BACnetServiceAckAtomicReadFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -9365,6 +9449,8 @@
                         </BACnetConfirmedServiceRequestAtomicReadFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -9522,6 +9608,8 @@
                         </BACnetServiceAckAtomicReadFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -9681,6 +9769,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -9877,6 +9967,8 @@
                         </BACnetConfirmedServiceRequestReadPropertyMultiple>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
diff --git a/plc4go/protocols/bacnetip/readwrite/model/APDUComplexAck.go b/plc4go/protocols/bacnetip/readwrite/model/APDUComplexAck.go
index e873de4179..8187af5fe5 100644
--- a/plc4go/protocols/bacnetip/readwrite/model/APDUComplexAck.go
+++ b/plc4go/protocols/bacnetip/readwrite/model/APDUComplexAck.go
@@ -46,7 +46,7 @@ type APDUComplexAck interface {
 	// GetServiceAck returns ServiceAck (property field)
 	GetServiceAck() BACnetServiceAck
 	// GetSegmentServiceChoice returns SegmentServiceChoice (property field)
-	GetSegmentServiceChoice() *uint8
+	GetSegmentServiceChoice() *BACnetConfirmedServiceChoice
 	// GetSegment returns Segment (property field)
 	GetSegment() []byte
 	// GetApduHeaderReduction returns ApduHeaderReduction (virtual field)
@@ -71,7 +71,7 @@ type _APDUComplexAck struct {
 	SequenceNumber       *uint8
 	ProposedWindowSize   *uint8
 	ServiceAck           BACnetServiceAck
-	SegmentServiceChoice *uint8
+	SegmentServiceChoice *BACnetConfirmedServiceChoice
 	Segment              []byte
 	// Reserved Fields
 	reservedField0 *uint8
@@ -126,7 +126,7 @@ func (m *_APDUComplexAck) GetServiceAck() BACnetServiceAck {
 	return m.ServiceAck
 }
 
-func (m *_APDUComplexAck) GetSegmentServiceChoice() *uint8 {
+func (m *_APDUComplexAck) GetSegmentServiceChoice() *BACnetConfirmedServiceChoice {
 	return m.SegmentServiceChoice
 }
 
@@ -173,7 +173,7 @@ func (m *_APDUComplexAck) GetSegmentReduction() uint16 {
 ///////////////////////////////////////////////////////////
 
 // NewAPDUComplexAck factory function for _APDUComplexAck
-func NewAPDUComplexAck(segmentedMessage bool, moreFollows bool, originalInvokeId uint8, sequenceNumber *uint8, proposedWindowSize *uint8, serviceAck BACnetServiceAck, segmentServiceChoice *uint8, segment []byte, apduLength uint16) *_APDUComplexAck {
+func NewAPDUComplexAck(segmentedMessage bool, moreFollows bool, originalInvokeId uint8, sequenceNumber *uint8, proposedWindowSize *uint8, serviceAck BACnetServiceAck, segmentServiceChoice *BACnetConfirmedServiceChoice, segment []byte, apduLength uint16) *_APDUComplexAck {
 	_result := &_APDUComplexAck{
 		SegmentedMessage:     segmentedMessage,
 		MoreFollows:          moreFollows,
@@ -363,13 +363,19 @@ func APDUComplexAckParseWithBuffer(readBuffer utils.ReadBuffer, apduLength uint1
 	}
 
 	// Optional Field (segmentServiceChoice) (Can be skipped, if a given expression evaluates to false)
-	var segmentServiceChoice *uint8 = nil
+	var segmentServiceChoice *BACnetConfirmedServiceChoice = nil
 	if bool(segmentedMessage) && bool(bool((*sequenceNumber) != (0))) {
-		_val, _err := readBuffer.ReadUint8("segmentServiceChoice", 8)
+		if pullErr := readBuffer.PullContext("segmentServiceChoice"); pullErr != nil {
+			return nil, errors.Wrap(pullErr, "Error pulling for segmentServiceChoice")
+		}
+		_val, _err := BACnetConfirmedServiceChoiceParseWithBuffer(readBuffer)
 		if _err != nil {
 			return nil, errors.Wrap(_err, "Error parsing 'segmentServiceChoice' field of APDUComplexAck")
 		}
 		segmentServiceChoice = &_val
+		if closeErr := readBuffer.CloseContext("segmentServiceChoice"); closeErr != nil {
+			return nil, errors.Wrap(closeErr, "Error closing for segmentServiceChoice")
+		}
 	}
 
 	// Virtual field
@@ -502,10 +508,16 @@ func (m *_APDUComplexAck) SerializeWithWriteBuffer(writeBuffer utils.WriteBuffer
 		}
 
 		// Optional Field (segmentServiceChoice) (Can be skipped, if the value is null)
-		var segmentServiceChoice *uint8 = nil
+		var segmentServiceChoice *BACnetConfirmedServiceChoice = nil
 		if m.GetSegmentServiceChoice() != nil {
+			if pushErr := writeBuffer.PushContext("segmentServiceChoice"); pushErr != nil {
+				return errors.Wrap(pushErr, "Error pushing for segmentServiceChoice")
+			}
 			segmentServiceChoice = m.GetSegmentServiceChoice()
-			_segmentServiceChoiceErr := writeBuffer.WriteUint8("segmentServiceChoice", 8, *(segmentServiceChoice))
+			_segmentServiceChoiceErr := writeBuffer.WriteSerializable(segmentServiceChoice)
+			if popErr := writeBuffer.PopContext("segmentServiceChoice"); popErr != nil {
+				return errors.Wrap(popErr, "Error popping for segmentServiceChoice")
+			}
 			if _segmentServiceChoiceErr != nil {
 				return errors.Wrap(_segmentServiceChoiceErr, "Error serializing 'segmentServiceChoice' field")
 			}
diff --git a/plc4go/protocols/bacnetip/readwrite/model/APDUConfirmedRequest.go b/plc4go/protocols/bacnetip/readwrite/model/APDUConfirmedRequest.go
index 7d3d2eb385..7c2dba2914 100644
--- a/plc4go/protocols/bacnetip/readwrite/model/APDUConfirmedRequest.go
+++ b/plc4go/protocols/bacnetip/readwrite/model/APDUConfirmedRequest.go
@@ -52,7 +52,7 @@ type APDUConfirmedRequest interface {
 	// GetServiceRequest returns ServiceRequest (property field)
 	GetServiceRequest() BACnetConfirmedServiceRequest
 	// GetSegmentServiceChoice returns SegmentServiceChoice (property field)
-	GetSegmentServiceChoice() *uint8
+	GetSegmentServiceChoice() *BACnetConfirmedServiceChoice
 	// GetSegment returns Segment (property field)
 	GetSegment() []byte
 	// GetApduHeaderReduction returns ApduHeaderReduction (virtual field)
@@ -80,7 +80,7 @@ type _APDUConfirmedRequest struct {
 	SequenceNumber            *uint8
 	ProposedWindowSize        *uint8
 	ServiceRequest            BACnetConfirmedServiceRequest
-	SegmentServiceChoice      *uint8
+	SegmentServiceChoice      *BACnetConfirmedServiceChoice
 	Segment                   []byte
 	// Reserved Fields
 	reservedField0 *uint8
@@ -147,7 +147,7 @@ func (m *_APDUConfirmedRequest) GetServiceRequest() BACnetConfirmedServiceReques
 	return m.ServiceRequest
 }
 
-func (m *_APDUConfirmedRequest) GetSegmentServiceChoice() *uint8 {
+func (m *_APDUConfirmedRequest) GetSegmentServiceChoice() *BACnetConfirmedServiceChoice {
 	return m.SegmentServiceChoice
 }
 
@@ -194,7 +194,7 @@ func (m *_APDUConfirmedRequest) GetSegmentReduction() uint16 {
 ///////////////////////////////////////////////////////////
 
 // NewAPDUConfirmedRequest factory function for _APDUConfirmedRequest
-func NewAPDUConfirmedRequest(segmentedMessage bool, moreFollows bool, segmentedResponseAccepted bool, maxSegmentsAccepted MaxSegmentsAccepted, maxApduLengthAccepted MaxApduLengthAccepted, invokeId uint8, sequenceNumber *uint8, proposedWindowSize *uint8, serviceRequest BACnetConfirmedServiceRequest, segmentServiceChoice *uint8, segment []byte, apduLength uint16) *_APDUConfirmedRequest {
+func NewAPDUConfirmedRequest(segmentedMessage bool, moreFollows bool, segmentedResponseAccepted bool, maxSegmentsAccepted MaxSegmentsAccepted, maxApduLengthAccepted MaxApduLengthAccepted, invokeId uint8, sequenceNumber *uint8, proposedWindowSize *uint8, serviceRequest BACnetConfirmedServiceRequest, segmentServiceChoice *BACnetConfirmedServiceChoice, segment []byte, apduLength uint16) *_APDUConfirmedRequest {
 	_result := &_APDUConfirmedRequest{
 		SegmentedMessage:          segmentedMessage,
 		MoreFollows:               moreFollows,
@@ -429,13 +429,19 @@ func APDUConfirmedRequestParseWithBuffer(readBuffer utils.ReadBuffer, apduLength
 	}
 
 	// Optional Field (segmentServiceChoice) (Can be skipped, if a given expression evaluates to false)
-	var segmentServiceChoice *uint8 = nil
+	var segmentServiceChoice *BACnetConfirmedServiceChoice = nil
 	if bool(segmentedMessage) && bool(bool((*sequenceNumber) != (0))) {
-		_val, _err := readBuffer.ReadUint8("segmentServiceChoice", 8)
+		if pullErr := readBuffer.PullContext("segmentServiceChoice"); pullErr != nil {
+			return nil, errors.Wrap(pullErr, "Error pulling for segmentServiceChoice")
+		}
+		_val, _err := BACnetConfirmedServiceChoiceParseWithBuffer(readBuffer)
 		if _err != nil {
 			return nil, errors.Wrap(_err, "Error parsing 'segmentServiceChoice' field of APDUConfirmedRequest")
 		}
 		segmentServiceChoice = &_val
+		if closeErr := readBuffer.CloseContext("segmentServiceChoice"); closeErr != nil {
+			return nil, errors.Wrap(closeErr, "Error closing for segmentServiceChoice")
+		}
 	}
 
 	// Virtual field
@@ -602,10 +608,16 @@ func (m *_APDUConfirmedRequest) SerializeWithWriteBuffer(writeBuffer utils.Write
 		}
 
 		// Optional Field (segmentServiceChoice) (Can be skipped, if the value is null)
-		var segmentServiceChoice *uint8 = nil
+		var segmentServiceChoice *BACnetConfirmedServiceChoice = nil
 		if m.GetSegmentServiceChoice() != nil {
+			if pushErr := writeBuffer.PushContext("segmentServiceChoice"); pushErr != nil {
+				return errors.Wrap(pushErr, "Error pushing for segmentServiceChoice")
+			}
 			segmentServiceChoice = m.GetSegmentServiceChoice()
-			_segmentServiceChoiceErr := writeBuffer.WriteUint8("segmentServiceChoice", 8, *(segmentServiceChoice))
+			_segmentServiceChoiceErr := writeBuffer.WriteSerializable(segmentServiceChoice)
+			if popErr := writeBuffer.PopContext("segmentServiceChoice"); popErr != nil {
+				return errors.Wrap(popErr, "Error popping for segmentServiceChoice")
+			}
 			if _segmentServiceChoiceErr != nil {
 				return errors.Wrap(_segmentServiceChoiceErr, "Error serializing 'segmentServiceChoice' field")
 			}
diff --git a/plc4go/protocols/bacnetip/readwrite/model/APDUSimpleAck.go b/plc4go/protocols/bacnetip/readwrite/model/APDUSimpleAck.go
index 8126a5aeb8..b7bd105ae8 100644
--- a/plc4go/protocols/bacnetip/readwrite/model/APDUSimpleAck.go
+++ b/plc4go/protocols/bacnetip/readwrite/model/APDUSimpleAck.go
@@ -35,7 +35,7 @@ type APDUSimpleAck interface {
 	// GetOriginalInvokeId returns OriginalInvokeId (property field)
 	GetOriginalInvokeId() uint8
 	// GetServiceChoice returns ServiceChoice (property field)
-	GetServiceChoice() uint8
+	GetServiceChoice() BACnetConfirmedServiceChoice
 }
 
 // APDUSimpleAckExactly can be used when we want exactly this type and not a type which fulfills APDUSimpleAck.
@@ -49,7 +49,7 @@ type APDUSimpleAckExactly interface {
 type _APDUSimpleAck struct {
 	*_APDU
 	OriginalInvokeId uint8
-	ServiceChoice    uint8
+	ServiceChoice    BACnetConfirmedServiceChoice
 	// Reserved Fields
 	reservedField0 *uint8
 }
@@ -83,7 +83,7 @@ func (m *_APDUSimpleAck) GetOriginalInvokeId() uint8 {
 	return m.OriginalInvokeId
 }
 
-func (m *_APDUSimpleAck) GetServiceChoice() uint8 {
+func (m *_APDUSimpleAck) GetServiceChoice() BACnetConfirmedServiceChoice {
 	return m.ServiceChoice
 }
 
@@ -93,7 +93,7 @@ func (m *_APDUSimpleAck) GetServiceChoice() uint8 {
 ///////////////////////////////////////////////////////////
 
 // NewAPDUSimpleAck factory function for _APDUSimpleAck
-func NewAPDUSimpleAck(originalInvokeId uint8, serviceChoice uint8, apduLength uint16) *_APDUSimpleAck {
+func NewAPDUSimpleAck(originalInvokeId uint8, serviceChoice BACnetConfirmedServiceChoice, apduLength uint16) *_APDUSimpleAck {
 	_result := &_APDUSimpleAck{
 		OriginalInvokeId: originalInvokeId,
 		ServiceChoice:    serviceChoice,
@@ -179,11 +179,17 @@ func APDUSimpleAckParseWithBuffer(readBuffer utils.ReadBuffer, apduLength uint16
 	originalInvokeId := _originalInvokeId
 
 	// Simple Field (serviceChoice)
-	_serviceChoice, _serviceChoiceErr := readBuffer.ReadUint8("serviceChoice", 8)
+	if pullErr := readBuffer.PullContext("serviceChoice"); pullErr != nil {
+		return nil, errors.Wrap(pullErr, "Error pulling for serviceChoice")
+	}
+	_serviceChoice, _serviceChoiceErr := BACnetConfirmedServiceChoiceParseWithBuffer(readBuffer)
 	if _serviceChoiceErr != nil {
 		return nil, errors.Wrap(_serviceChoiceErr, "Error parsing 'serviceChoice' field of APDUSimpleAck")
 	}
 	serviceChoice := _serviceChoice
+	if closeErr := readBuffer.CloseContext("serviceChoice"); closeErr != nil {
+		return nil, errors.Wrap(closeErr, "Error closing for serviceChoice")
+	}
 
 	if closeErr := readBuffer.CloseContext("APDUSimpleAck"); closeErr != nil {
 		return nil, errors.Wrap(closeErr, "Error closing for APDUSimpleAck")
@@ -242,8 +248,13 @@ func (m *_APDUSimpleAck) SerializeWithWriteBuffer(writeBuffer utils.WriteBuffer)
 		}
 
 		// Simple Field (serviceChoice)
-		serviceChoice := uint8(m.GetServiceChoice())
-		_serviceChoiceErr := writeBuffer.WriteUint8("serviceChoice", 8, (serviceChoice))
+		if pushErr := writeBuffer.PushContext("serviceChoice"); pushErr != nil {
+			return errors.Wrap(pushErr, "Error pushing for serviceChoice")
+		}
+		_serviceChoiceErr := writeBuffer.WriteSerializable(m.GetServiceChoice())
+		if popErr := writeBuffer.PopContext("serviceChoice"); popErr != nil {
+			return errors.Wrap(popErr, "Error popping for serviceChoice")
+		}
 		if _serviceChoiceErr != nil {
 			return errors.Wrap(_serviceChoiceErr, "Error serializing 'serviceChoice' field")
 		}
diff --git a/plc4go/protocols/bacnetip/readwrite/model/StaticHelper.go b/plc4go/protocols/bacnetip/readwrite/model/StaticHelper.go
index 04cb39aba2..559c8da8cf 100644
--- a/plc4go/protocols/bacnetip/readwrite/model/StaticHelper.go
+++ b/plc4go/protocols/bacnetip/readwrite/model/StaticHelper.go
@@ -798,8 +798,7 @@ func CreateBACnetContextTagUnsignedInteger(tagNumber uint8, value uint) BACnetCo
 	return NewBACnetContextTagUnsignedInteger(payload, header, tagNumber)
 }
 
-func CreateUnsignedPayload(value uint) (uint32, BACnetTagPayloadUnsignedInteger) {
-	var length uint32
+func CreateUnsignedPayload(value uint) (length uint32, payload BACnetTagPayloadUnsignedInteger) {
 	var valueUint8 *uint8
 	var valueUint16 *uint16
 	var valueUint24 *uint32
@@ -827,8 +826,8 @@ func CreateUnsignedPayload(value uint) (uint32, BACnetTagPayloadUnsignedInteger)
 		valueUint32_ := uint32(value)
 		valueUint32 = &valueUint32_
 	}
-	payload := NewBACnetTagPayloadUnsignedInteger(valueUint8, valueUint16, valueUint24, valueUint32, valueUint40, valueUint48, valueUint56, valueUint64, length)
-	return length, payload
+	payload = NewBACnetTagPayloadUnsignedInteger(valueUint8, valueUint16, valueUint24, valueUint32, valueUint40, valueUint48, valueUint56, valueUint64, length)
+	return
 }
 
 func CreateBACnetApplicationTagSignedInteger(value int) BACnetApplicationTagSignedInteger {
diff --git a/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec b/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec
index 0af5248f08..348ebe93e2 100644
--- a/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec
+++ b/protocols/bacnetip/src/main/resources/protocols/bacnetip/bacnetip.mspec
@@ -297,7 +297,8 @@
             [validation '(!segmentedMessage && serviceRequest != null) || segmentedMessage'
                         "service request should be set"                     ]
             // When we read the first segment we want the service choice to be part of the bytes so we only read it > 0
-            [optional uint 8    segmentServiceChoice 'segmentedMessage && sequenceNumber != 0']
+            [optional BACnetConfirmedServiceChoice
+                                segmentServiceChoice 'segmentedMessage && sequenceNumber != 0']
             [virtual  uint 16   segmentReduction
                                     '(segmentServiceChoice != null)?(apduHeaderReduction+1):apduHeaderReduction'       ]
             [array    byte      segment
@@ -312,7 +313,8 @@
         ['SIMPLE_ACK_PDU' *SimpleAck
             [reserved uint 4    '0'                                     ]
             [simple   uint 8    originalInvokeId                        ]
-            [simple   uint 8    serviceChoice                           ]
+            [simple   BACnetConfirmedServiceChoice
+                                serviceChoice                           ]
         ]
         ['COMPLEX_ACK_PDU' *ComplexAck
             [simple   bit       segmentedMessage                        ]
@@ -329,7 +331,8 @@
             [validation '(!segmentedMessage && serviceAck != null) || segmentedMessage'
                         "service ack should be set"                     ]
             // When we read the first segment we want the service choice to be part of the bytes so we only read it > 0
-            [optional uint 8    segmentServiceChoice 'segmentedMessage && sequenceNumber != 0']
+            [optional BACnetConfirmedServiceChoice
+                                segmentServiceChoice 'segmentedMessage && sequenceNumber != 0']
             [virtual  uint 16   segmentReduction
                                     '(segmentServiceChoice != null)?(apduHeaderReduction+1):apduHeaderReduction'
                                                                         ]
diff --git a/protocols/bacnetip/src/test/resources/protocols/bacnet/ParserSerializerTestsuite.xml b/protocols/bacnetip/src/test/resources/protocols/bacnet/ParserSerializerTestsuite.xml
index 008a6588f3..f0dc20dea9 100644
--- a/protocols/bacnetip/src/test/resources/protocols/bacnet/ParserSerializerTestsuite.xml
+++ b/protocols/bacnetip/src/test/resources/protocols/bacnet/ParserSerializerTestsuite.xml
@@ -63,7 +63,9 @@
                   <APDUSimpleAck>
                     <reserved dataType="uint" bitLength="4">0</reserved>
                     <originalInvokeId dataType="uint" bitLength="8">2</originalInvokeId>
-                    <serviceChoice dataType="uint" bitLength="8">0</serviceChoice>
+                    <serviceChoice>
+                      <BACnetConfirmedServiceChoice dataType="uint" bitLength="8" stringRepresentation="ACKNOWLEDGE_ALARM">0</BACnetConfirmedServiceChoice>
+                    </serviceChoice>
                   </APDUSimpleAck>
                 </APDU>
               </apdu>
@@ -983,6 +985,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -1066,6 +1070,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -1226,6 +1232,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -1309,6 +1317,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -1469,6 +1479,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -1552,6 +1564,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -1712,6 +1726,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -1795,6 +1811,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -1955,6 +1973,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -2038,6 +2058,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -2198,6 +2220,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -2281,6 +2305,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -2441,6 +2467,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -2524,6 +2552,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -2684,6 +2714,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -2767,6 +2799,8 @@
                         </BACnetServiceAckAtomicWriteFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -3106,6 +3140,8 @@
                         </BACnetConfirmedServiceRequestReinitializeDevice>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -3534,6 +3570,8 @@
                         </BACnetConfirmedServiceRequestReadProperty>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -3962,6 +4000,8 @@
                         </BACnetConfirmedServiceRequestReadProperty>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -4390,6 +4430,8 @@
                         </BACnetConfirmedServiceRequestReadProperty>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -4830,6 +4872,8 @@
                         </BACnetServiceAckReadProperty>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -5466,6 +5510,8 @@
                         </BACnetConfirmedServiceRequestWriteProperty>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -5897,6 +5943,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -5949,7 +5997,9 @@
                   <APDUSimpleAck>
                     <reserved dataType="uint" bitLength="4">0</reserved>
                     <originalInvokeId dataType="uint" bitLength="8">1</originalInvokeId>
-                    <serviceChoice dataType="uint" bitLength="8">17</serviceChoice>
+                    <serviceChoice>
+                      <BACnetConfirmedServiceChoice dataType="uint" bitLength="8" stringRepresentation="DEVICE_COMMUNICATION_CONTROL">17</BACnetConfirmedServiceChoice>
+                    </serviceChoice>
                   </APDUSimpleAck>
                 </APDU>
               </apdu>
@@ -6288,6 +6338,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -6616,6 +6668,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -6724,6 +6778,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -6832,6 +6888,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -6940,6 +6998,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -7176,6 +7236,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -7377,6 +7439,8 @@
                         </BACnetConfirmedServiceRequestDeviceCommunicationControl>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -7430,7 +7494,9 @@
                   <APDUSimpleAck>
                     <reserved dataType="uint" bitLength="4">0</reserved>
                     <originalInvokeId dataType="uint" bitLength="8">51</originalInvokeId>
-                    <serviceChoice dataType="uint" bitLength="8">17</serviceChoice>
+                    <serviceChoice>
+                      <BACnetConfirmedServiceChoice dataType="uint" bitLength="8" stringRepresentation="DEVICE_COMMUNICATION_CONTROL">17</BACnetConfirmedServiceChoice>
+                    </serviceChoice>
                   </APDUSimpleAck>
                 </APDU>
               </apdu>
@@ -7988,6 +8054,8 @@
                         </BACnetConfirmedServiceRequestReinitializeDevice>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -8189,6 +8257,8 @@
                         </BACnetConfirmedServiceRequestReinitializeDevice>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -8242,7 +8312,9 @@
                   <APDUSimpleAck>
                     <reserved dataType="uint" bitLength="4">0</reserved>
                     <originalInvokeId dataType="uint" bitLength="8">53</originalInvokeId>
-                    <serviceChoice dataType="uint" bitLength="8">20</serviceChoice>
+                    <serviceChoice>
+                      <BACnetConfirmedServiceChoice dataType="uint" bitLength="8" stringRepresentation="REINITIALIZE_DEVICE">20</BACnetConfirmedServiceChoice>
+                    </serviceChoice>
                   </APDUSimpleAck>
                 </APDU>
               </apdu>
@@ -8405,6 +8477,8 @@
                         </BACnetConfirmedServiceRequestAtomicReadFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -8562,6 +8636,8 @@
                         </BACnetServiceAckAtomicReadFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -8725,6 +8801,8 @@
                         </BACnetConfirmedServiceRequestAtomicReadFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -8882,6 +8960,8 @@
                         </BACnetServiceAckAtomicReadFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -9045,6 +9125,8 @@
                         </BACnetConfirmedServiceRequestAtomicReadFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -9202,6 +9284,8 @@
                         </BACnetServiceAckAtomicReadFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -9365,6 +9449,8 @@
                         </BACnetConfirmedServiceRequestAtomicReadFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -9522,6 +9608,8 @@
                         </BACnetServiceAckAtomicReadFile>
                       </BACnetServiceAck>
                     </serviceAck>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUComplexAck>
                 </APDU>
@@ -9681,6 +9769,8 @@
                         </BACnetConfirmedServiceRequestAtomicWriteFile>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>
@@ -9877,6 +9967,8 @@
                         </BACnetConfirmedServiceRequestReadPropertyMultiple>
                       </BACnetConfirmedServiceRequest>
                     </serviceRequest>
+                    <segmentServiceChoice>
+                    </segmentServiceChoice>
                     <segment dataType="byte" bitLength="0">0x</segment>
                   </APDUConfirmedRequest>
                 </APDU>