[VOL-3380] Functional area specific logging

Change-Id: I67414da013d8fc82827fcdb69d4f8a34040625d3
diff --git a/internal/pkg/avcfg/common.go b/internal/pkg/avcfg/common.go
new file mode 100755
index 0000000..8e02fe0
--- /dev/null
+++ b/internal/pkg/avcfg/common.go
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed 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
+ *
+ * http://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 avcfg provides anig and vlan configuration functionality
+package avcfg
+
+import (
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+)
+
+var logger log.CLogger
+
+func init() {
+	// Setup this package so that it's log level can be modified at run time
+	var err error
+	logger, err = log.RegisterPackage(log.JSON, log.ErrorLevel, log.Fields{"pkg": "avcfg"})
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/internal/pkg/avcfg/omci_ani_config.go b/internal/pkg/avcfg/omci_ani_config.go
new file mode 100755
index 0000000..6ade746
--- /dev/null
+++ b/internal/pkg/avcfg/omci_ani_config.go
@@ -0,0 +1,1819 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed 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
+ *
+ * http://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 avcfg provides anig and vlan configuration functionality
+package avcfg
+
+import (
+	"context"
+	"encoding/binary"
+	"fmt"
+	"net"
+	"strconv"
+	"sync"
+	"time"
+
+	"github.com/cevaris/ordered_map"
+	"github.com/looplab/fsm"
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+
+	//ic "github.com/opencord/voltha-protos/v5/go/inter_container"
+	//"github.com/opencord/voltha-protos/v5/go/openflow_13"
+	//"github.com/opencord/voltha-protos/v5/go/voltha"
+	cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
+	"github.com/opencord/voltha-openonu-adapter-go/internal/pkg/devdb"
+)
+
+const (
+	// events of config PON ANI port FSM
+	aniEvStart             = "aniEvStart"
+	aniEvStartConfig       = "aniEvStartConfig"
+	aniEvRxDot1pmapCResp   = "aniEvRxDot1pmapCResp"
+	aniEvRxMbpcdResp       = "aniEvRxMbpcdResp"
+	aniEvRxTcontsResp      = "aniEvRxTcontsResp"
+	aniEvRxGemntcpsResp    = "aniEvRxGemntcpsResp"
+	aniEvRxGemiwsResp      = "aniEvRxGemiwsResp"
+	aniEvRxPrioqsResp      = "aniEvRxPrioqsResp"
+	aniEvRxDot1pmapSResp   = "aniEvRxDot1pmapSResp"
+	aniEvRemGemiw          = "aniEvRemGemiw"
+	aniEvWaitFlowRem       = "aniEvWaitFlowRem"
+	aniEvFlowRemDone       = "aniEvFlowRemDone"
+	aniEvRxRemGemiwResp    = "aniEvRxRemGemiwResp"
+	aniEvRxRemGemntpResp   = "aniEvRxRemGemntpResp"
+	aniEvRxRemTdResp       = "aniEvRxRemTdResp"
+	aniEvRemTcontPath      = "aniEvRemTcontPath"
+	aniEvRxResetTcontResp  = "aniEvRxResetTcontResp"
+	aniEvRxRem1pMapperResp = "aniEvRxRem1pMapperResp"
+	aniEvRxRemAniBPCDResp  = "aniEvRxRemAniBPCDResp"
+	aniEvTimeoutSimple     = "aniEvTimeoutSimple"
+	aniEvTimeoutMids       = "aniEvTimeoutMids"
+	aniEvReset             = "aniEvReset"
+	aniEvRestart           = "aniEvRestart"
+	aniEvSkipOmciConfig    = "aniEvSkipOmciConfig"
+	aniEvRemGemDone        = "aniEvRemGemDone"
+)
+const (
+	// states of config PON ANI port FSM
+	aniStDisabled            = "aniStDisabled"
+	aniStStarting            = "aniStStarting"
+	aniStCreatingDot1PMapper = "aniStCreatingDot1PMapper"
+	aniStCreatingMBPCD       = "aniStCreatingMBPCD"
+	aniStSettingTconts       = "aniStSettingTconts"
+	aniStCreatingGemNCTPs    = "aniStCreatingGemNCTPs"
+	aniStCreatingGemIWs      = "aniStCreatingGemIWs"
+	aniStSettingPQs          = "aniStSettingPQs"
+	aniStSettingDot1PMapper  = "aniStSettingDot1PMapper"
+	aniStConfigDone          = "aniStConfigDone"
+	aniStRemovingGemIW       = "aniStRemovingGemIW"
+	aniStWaitingFlowRem      = "aniStWaitingFlowRem"
+	aniStRemovingGemNCTP     = "aniStRemovingGemNCTP"
+	aniStRemovingTD          = "aniStRemovingTD"
+	aniStResetTcont          = "aniStResetTcont"
+	aniStRemDot1PMapper      = "aniStRemDot1PMapper"
+	aniStRemAniBPCD          = "aniStRemAniBPCD"
+	aniStRemoveDone          = "aniStRemoveDone"
+	aniStResetting           = "aniStResetting"
+)
+
+const (
+	bitTrafficSchedulerPtrSetPermitted = 0x0002 // Refer section 9.1.2 ONU-2G, table for "Quality of service (QoS) configuration flexibility" IE
+)
+
+// CAniFsmIdleState - TODO: add comment
+const CAniFsmIdleState = aniStConfigDone
+
+type ponAniGemPortAttribs struct {
+	gemPortID      uint16
+	upQueueID      uint16
+	downQueueID    uint16
+	direction      uint8
+	qosPolicy      string
+	weight         uint8
+	pbitString     string
+	isMulticast    bool
+	multicastGemID uint16
+	staticACL      string
+	dynamicACL     string
+}
+
+//UniPonAniConfigFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+type UniPonAniConfigFsm struct {
+	deviceID                 string
+	pDeviceHandler           cmn.IdeviceHandler
+	pOnuDeviceEntry          cmn.IonuDeviceEntry
+	pOmciCC                  *cmn.OmciCC
+	pOnuUniPort              *cmn.OnuUniPort
+	pUniTechProf             *OnuUniTechProf
+	pOnuDB                   *devdb.OnuDeviceDB
+	techProfileID            uint8
+	uniTpKey                 uniTP
+	requestEvent             cmn.OnuDeviceEvent
+	mutexIsAwaitingResponse  sync.RWMutex
+	isCanceled               bool
+	isAwaitingResponse       bool
+	omciMIdsResponseReceived chan bool //separate channel needed for checking multiInstance OMCI message responses
+	PAdaptFsm                *cmn.AdapterFsm
+	chSuccess                chan<- uint8
+	procStep                 uint8
+	mutexChanSet             sync.RWMutex
+	chanSet                  bool
+	mapperSP0ID              uint16
+	macBPCD0ID               uint16
+	tcont0ID                 uint16
+	alloc0ID                 uint16
+	gemPortAttribsSlice      []ponAniGemPortAttribs
+	mutexPLastTxMeInstance   sync.RWMutex
+	pLastTxMeInstance        *me.ManagedEntity
+	requestEventOffset       uint8 //used to indicate ConfigDone or Removed using successor (enum)
+	isWaitingForFlowDelete   bool
+	waitFlowDeleteChannel    chan bool
+	tcontSetBefore           bool
+}
+
+//NewUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+func NewUniPonAniConfigFsm(ctx context.Context, apDevOmciCC *cmn.OmciCC, apUniPort *cmn.OnuUniPort, apUniTechProf *OnuUniTechProf,
+	apOnuDB *devdb.OnuDeviceDB, aTechProfileID uint8, aRequestEvent cmn.OnuDeviceEvent, aName string,
+	apDeviceHandler cmn.IdeviceHandler, apOnuDeviceEntry cmn.IonuDeviceEntry, aCommChannel chan cmn.Message) *UniPonAniConfigFsm {
+	instFsm := &UniPonAniConfigFsm{
+		pDeviceHandler:  apDeviceHandler,
+		pOnuDeviceEntry: apOnuDeviceEntry,
+		deviceID:        apDeviceHandler.GetDeviceID(),
+		pOmciCC:         apDevOmciCC,
+		pOnuUniPort:     apUniPort,
+		pUniTechProf:    apUniTechProf,
+		pOnuDB:          apOnuDB,
+		techProfileID:   aTechProfileID,
+		requestEvent:    aRequestEvent,
+		chanSet:         false,
+		tcontSetBefore:  false,
+	}
+	instFsm.uniTpKey = uniTP{uniID: apUniPort.UniID, tpID: aTechProfileID}
+	instFsm.waitFlowDeleteChannel = make(chan bool)
+
+	instFsm.PAdaptFsm = cmn.NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
+	if instFsm.PAdaptFsm == nil {
+		logger.Errorw(ctx, "UniPonAniConfigFsm's cmn.AdapterFsm could not be instantiated!!", log.Fields{
+			"device-id": instFsm.deviceID})
+		return nil
+	}
+
+	instFsm.PAdaptFsm.PFsm = fsm.NewFSM(
+		aniStDisabled,
+		fsm.Events{
+
+			{Name: aniEvStart, Src: []string{aniStDisabled}, Dst: aniStStarting},
+
+			//Note: .1p-Mapper and MBPCD might also have multi instances (per T-Cont) - by now only one 1 T-Cont considered!
+			{Name: aniEvStartConfig, Src: []string{aniStStarting}, Dst: aniStCreatingDot1PMapper},
+			{Name: aniEvRxDot1pmapCResp, Src: []string{aniStCreatingDot1PMapper}, Dst: aniStCreatingMBPCD},
+			{Name: aniEvRxMbpcdResp, Src: []string{aniStCreatingMBPCD}, Dst: aniStSettingTconts},
+			{Name: aniEvRxTcontsResp, Src: []string{aniStSettingTconts}, Dst: aniStCreatingGemNCTPs},
+			// the creatingGemNCTPs state is used for multi ME config if required for all configured/available GemPorts
+			{Name: aniEvRxGemntcpsResp, Src: []string{aniStCreatingGemNCTPs}, Dst: aniStCreatingGemIWs},
+			// the creatingGemIWs state is used for multi ME config if required for all configured/available GemPorts
+			{Name: aniEvRxGemiwsResp, Src: []string{aniStCreatingGemIWs}, Dst: aniStSettingPQs},
+			// the settingPQs state is used for multi ME config if required for all configured/available upstream PriorityQueues
+			{Name: aniEvRxPrioqsResp, Src: []string{aniStSettingPQs}, Dst: aniStSettingDot1PMapper},
+			{Name: aniEvRxDot1pmapSResp, Src: []string{aniStSettingDot1PMapper}, Dst: aniStConfigDone},
+
+			//for removing Gem related resources
+			{Name: aniEvRemGemiw, Src: []string{aniStConfigDone}, Dst: aniStRemovingGemIW},
+			{Name: aniEvWaitFlowRem, Src: []string{aniStRemovingGemIW}, Dst: aniStWaitingFlowRem},
+			{Name: aniEvFlowRemDone, Src: []string{aniStWaitingFlowRem}, Dst: aniStRemovingGemIW},
+			{Name: aniEvRxRemGemiwResp, Src: []string{aniStRemovingGemIW}, Dst: aniStRemovingGemNCTP},
+			{Name: aniEvRxRemGemntpResp, Src: []string{aniStRemovingGemNCTP}, Dst: aniStRemovingTD},
+			{Name: aniEvRxRemTdResp, Src: []string{aniStRemovingTD}, Dst: aniStRemDot1PMapper},
+			{Name: aniEvRemGemDone, Src: []string{aniStRemDot1PMapper}, Dst: aniStConfigDone},
+			{Name: aniEvRxRem1pMapperResp, Src: []string{aniStRemDot1PMapper}, Dst: aniStRemAniBPCD},
+			{Name: aniEvRxRemAniBPCDResp, Src: []string{aniStRemAniBPCD}, Dst: aniStRemoveDone},
+
+			//for removing TCONT related resources
+			{Name: aniEvRemTcontPath, Src: []string{aniStConfigDone}, Dst: aniStResetTcont},
+			{Name: aniEvRxResetTcontResp, Src: []string{aniStResetTcont}, Dst: aniStConfigDone},
+
+			{Name: aniEvTimeoutSimple, Src: []string{aniStCreatingDot1PMapper, aniStCreatingMBPCD, aniStSettingTconts, aniStSettingDot1PMapper,
+				aniStRemovingGemIW, aniStRemovingGemNCTP, aniStRemovingTD,
+				aniStResetTcont, aniStRemDot1PMapper, aniStRemAniBPCD, aniStRemoveDone}, Dst: aniStStarting},
+			{Name: aniEvTimeoutMids, Src: []string{
+				aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs}, Dst: aniStStarting},
+
+			// exceptional treatment for all states except aniStResetting
+			{Name: aniEvReset, Src: []string{aniStStarting, aniStCreatingDot1PMapper, aniStCreatingMBPCD,
+				aniStSettingTconts, aniStCreatingGemNCTPs, aniStCreatingGemIWs, aniStSettingPQs, aniStSettingDot1PMapper,
+				aniStConfigDone, aniStRemovingGemIW, aniStWaitingFlowRem, aniStRemovingGemNCTP, aniStRemovingTD,
+				aniStResetTcont, aniStRemDot1PMapper, aniStRemAniBPCD, aniStRemoveDone}, Dst: aniStResetting},
+			// the only way to get to resource-cleared disabled state again is via "resseting"
+			{Name: aniEvRestart, Src: []string{aniStResetting}, Dst: aniStDisabled},
+			{Name: aniEvSkipOmciConfig, Src: []string{aniStStarting}, Dst: aniStConfigDone},
+		},
+
+		fsm.Callbacks{
+			"enter_state":                         func(e *fsm.Event) { instFsm.PAdaptFsm.LogFsmStateChange(ctx, e) },
+			("enter_" + aniStStarting):            func(e *fsm.Event) { instFsm.enterConfigStartingState(ctx, e) },
+			("enter_" + aniStCreatingDot1PMapper): func(e *fsm.Event) { instFsm.enterCreatingDot1PMapper(ctx, e) },
+			("enter_" + aniStCreatingMBPCD):       func(e *fsm.Event) { instFsm.enterCreatingMBPCD(ctx, e) },
+			("enter_" + aniStSettingTconts):       func(e *fsm.Event) { instFsm.enterSettingTconts(ctx, e) },
+			("enter_" + aniStCreatingGemNCTPs):    func(e *fsm.Event) { instFsm.enterCreatingGemNCTPs(ctx, e) },
+			("enter_" + aniStCreatingGemIWs):      func(e *fsm.Event) { instFsm.enterCreatingGemIWs(ctx, e) },
+			("enter_" + aniStSettingPQs):          func(e *fsm.Event) { instFsm.enterSettingPQs(ctx, e) },
+			("enter_" + aniStSettingDot1PMapper):  func(e *fsm.Event) { instFsm.enterSettingDot1PMapper(ctx, e) },
+			("enter_" + aniStConfigDone):          func(e *fsm.Event) { instFsm.enterAniConfigDone(ctx, e) },
+			("enter_" + aniStRemovingGemIW):       func(e *fsm.Event) { instFsm.enterRemovingGemIW(ctx, e) },
+			("enter_" + aniStWaitingFlowRem):      func(e *fsm.Event) { instFsm.enterWaitingFlowRem(ctx, e) },
+			("enter_" + aniStRemovingGemNCTP):     func(e *fsm.Event) { instFsm.enterRemovingGemNCTP(ctx, e) },
+			("enter_" + aniStRemovingTD):          func(e *fsm.Event) { instFsm.enterRemovingTD(ctx, e) },
+			("enter_" + aniStResetTcont):          func(e *fsm.Event) { instFsm.enterResettingTcont(ctx, e) },
+			("enter_" + aniStRemDot1PMapper):      func(e *fsm.Event) { instFsm.enterRemoving1pMapper(ctx, e) },
+			("enter_" + aniStRemAniBPCD):          func(e *fsm.Event) { instFsm.enterRemovingAniBPCD(ctx, e) },
+			("enter_" + aniStRemoveDone):          func(e *fsm.Event) { instFsm.enterAniRemoveDone(ctx, e) },
+			("enter_" + aniStResetting):           func(e *fsm.Event) { instFsm.enterResettingState(ctx, e) },
+			("enter_" + aniStDisabled):            func(e *fsm.Event) { instFsm.enterDisabledState(ctx, e) },
+		},
+	)
+	if instFsm.PAdaptFsm.PFsm == nil {
+		logger.Errorw(ctx, "UniPonAniConfigFsm's Base FSM could not be instantiated!!", log.Fields{
+			"device-id": instFsm.deviceID})
+		return nil
+	}
+
+	logger.Debugw(ctx, "UniPonAniConfigFsm created", log.Fields{"device-id": instFsm.deviceID})
+	return instFsm
+}
+
+//setFsmCompleteChannel sets the requested channel and channel result for transfer on success
+func (oFsm *UniPonAniConfigFsm) setFsmCompleteChannel(aChSuccess chan<- uint8, aProcStep uint8) {
+	oFsm.chSuccess = aChSuccess
+	oFsm.procStep = aProcStep
+	oFsm.setChanSet(true)
+}
+
+//CancelProcessing ensures that suspended processing at waiting on some response is aborted and reset of FSM
+func (oFsm *UniPonAniConfigFsm) CancelProcessing(ctx context.Context) {
+	//early indication about started reset processing
+	oFsm.pUniTechProf.setProfileResetting(ctx, oFsm.pOnuUniPort.UniID, oFsm.techProfileID, true)
+	//mutex protection is required for possible concurrent access to FSM members
+	oFsm.mutexIsAwaitingResponse.Lock()
+	oFsm.isCanceled = true
+	if oFsm.isAwaitingResponse {
+		//attention: for an unbuffered channel the sender is blocked until the value is received (processed)!
+		// accordingly the mutex must be released before sending to channel here (mutex acquired in receiver)
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		//use channel to indicate that the response waiting shall be aborted
+		oFsm.omciMIdsResponseReceived <- false
+	} else {
+		oFsm.mutexIsAwaitingResponse.Unlock()
+	}
+
+	oFsm.mutexIsAwaitingResponse.Lock()
+	if oFsm.isWaitingForFlowDelete {
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		//use channel to indicate that the response waiting shall be aborted
+		oFsm.waitFlowDeleteChannel <- false
+	} else {
+		oFsm.mutexIsAwaitingResponse.Unlock()
+	}
+
+	// in any case (even if it might be automatically requested by above cancellation of waiting) ensure resetting the FSM
+	PAdaptFsm := oFsm.PAdaptFsm
+	if PAdaptFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(aPAFsm *cmn.AdapterFsm) {
+			if aPAFsm.PFsm != nil {
+				_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+			}
+		}(PAdaptFsm)
+	}
+
+	// possible access conflicts on internal data by next needed data clearance
+	//   are avoided by using mutexTPState also from within clearAniSideConfig
+	//   do not try to lock TpProcMutex here as done in previous code version
+	//   as it may result in deadlock situations (as observed at soft-reboot handling where
+	//   TpProcMutex is already locked by some ongoing TechProfile config/removal processing
+	//remove all TechProf related internal data to allow for new configuration
+	oFsm.pUniTechProf.clearAniSideConfig(ctx, oFsm.pOnuUniPort.UniID, oFsm.techProfileID)
+}
+
+//nolint: gocyclo
+//TODO:visit here for refactoring for gocyclo
+func (oFsm *UniPonAniConfigFsm) prepareAndEnterConfigState(ctx context.Context, aPAFsm *cmn.AdapterFsm) {
+	if aPAFsm != nil && aPAFsm.PFsm != nil {
+		var err error
+		oFsm.mapperSP0ID, err = cmn.GenerateIeeMaperServiceProfileEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.techProfileID))
+		if err != nil {
+			logger.Errorw(ctx, "error generating maper id", log.Fields{"device-id": oFsm.deviceID,
+				"techProfileID": oFsm.techProfileID, "error": err})
+			return
+		}
+		oFsm.macBPCD0ID, err = cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.techProfileID))
+		if err != nil {
+			logger.Errorw(ctx, "error generating mbpcd id", log.Fields{"device-id": oFsm.deviceID,
+				"techProfileID": oFsm.techProfileID, "error": err})
+			return
+		}
+		logger.Debugw(ctx, "generated ids for ani config", log.Fields{"mapperSP0ID": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+			"macBPCD0ID": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16), "device-id": oFsm.deviceID,
+			"macBpNo": oFsm.pOnuUniPort.MacBpNo, "techProfileID": oFsm.techProfileID})
+		if oFsm.pOnuDeviceEntry == nil {
+			logger.Errorw(ctx, "No valid OnuDevice - aborting", log.Fields{"device-id": oFsm.deviceID})
+			return
+		}
+		tcontInstID, tcontAlreadyExist, err := oFsm.pOnuDeviceEntry.AllocateFreeTcont(ctx, oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID)
+		if err != nil {
+			logger.Errorw(ctx, "No TCont instances found", log.Fields{"device-id": oFsm.deviceID, "err": err})
+			//reset the state machine to enable usage on subsequent requests
+			_ = aPAFsm.PFsm.Event(aniEvReset)
+			return
+		}
+		oFsm.tcont0ID = tcontInstID
+		oFsm.tcontSetBefore = tcontAlreadyExist
+		logger.Debugw(ctx, "used-tcont-instance-id", log.Fields{"tcont-inst-id": oFsm.tcont0ID,
+			"alloc-id":          oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID,
+			"tcontAlreadyExist": tcontAlreadyExist,
+			"device-id":         oFsm.deviceID})
+
+		// Access critical state with lock
+		oFsm.pUniTechProf.mutexTPState.RLock()
+		oFsm.alloc0ID = oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID
+		mapGemPortParams := oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].mapGemPortParams
+		oFsm.pUniTechProf.mutexTPState.RUnlock()
+
+		//for all TechProfile set GemIndices
+		for _, gemEntry := range mapGemPortParams {
+			loGemPortAttribs := ponAniGemPortAttribs{}
+
+			//collect all GemConfigData in a separate Fsm related slice (needed also to avoid mix-up with unsorted mapPonAniConfig)
+
+			if queueInstKeys := oFsm.pOnuDB.GetSortedInstKeys(ctx, me.PriorityQueueClassID); len(queueInstKeys) > 0 {
+
+				loGemPortAttribs.gemPortID = gemEntry.gemPortID
+				// MibDb usage: upstream PrioQueue.RelatedPort = xxxxyyyy with xxxx=TCont.Entity(incl. slot) and yyyy=prio
+				// i.e.: search PrioQueue list with xxxx=actual T-Cont.Entity,
+				// from that list use the PrioQueue.Entity with  gemEntry.prioQueueIndex == yyyy (expect 0..7)
+				usQrelPortMask := uint32((((uint32)(oFsm.tcont0ID)) << 16) + uint32(gemEntry.prioQueueIndex))
+
+				// MibDb usage: downstream PrioQueue.RelatedPort = xxyyzzzz with xx=slot, yy=UniPort and zzzz=prio
+				// i.e.: search PrioQueue list with yy=actual pOnuUniPort.UniID,
+				// from that list use the PrioQueue.Entity with gemEntry.prioQueueIndex == zzzz (expect 0..7)
+				// Note: As we do not maintain any slot numbering, slot number will be excluded from seatch pattern.
+				//       Furthermore OMCI Onu port-Id is expected to start with 1 (not 0).
+				dsQrelPortMask := uint32((((uint32)(oFsm.pOnuUniPort.UniID + 1)) << 16) + uint32(gemEntry.prioQueueIndex))
+
+				usQueueFound := false
+				dsQueueFound := false
+				for _, mgmtEntityID := range queueInstKeys {
+					if meAttributes := oFsm.pOnuDB.GetMe(me.PriorityQueueClassID, mgmtEntityID); meAttributes != nil {
+						returnVal := meAttributes["RelatedPort"]
+						if returnVal != nil {
+							if relatedPort, err := oFsm.pOnuDB.GetUint32Attrib(returnVal); err == nil {
+								if relatedPort == usQrelPortMask {
+									loGemPortAttribs.upQueueID = mgmtEntityID
+									logger.Debugw(ctx, "UpQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+										"upQueueID": strconv.FormatInt(int64(loGemPortAttribs.upQueueID), 16), "device-id": oFsm.deviceID})
+									usQueueFound = true
+								} else if (relatedPort&0xFFFFFF) == dsQrelPortMask && mgmtEntityID < 0x8000 {
+									loGemPortAttribs.downQueueID = mgmtEntityID
+									logger.Debugw(ctx, "DownQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+										"downQueueID": strconv.FormatInt(int64(loGemPortAttribs.downQueueID), 16), "device-id": oFsm.deviceID})
+									dsQueueFound = true
+								}
+								if usQueueFound && dsQueueFound {
+									break
+								}
+							} else {
+								logger.Warnw(ctx, "Could not convert attribute value", log.Fields{"device-id": oFsm.deviceID})
+							}
+						} else {
+							logger.Warnw(ctx, "'RelatedPort' not found in meAttributes:", log.Fields{"device-id": oFsm.deviceID})
+						}
+					} else {
+						logger.Warnw(ctx, "No attributes available in DB:", log.Fields{"meClassID": me.PriorityQueueClassID,
+							"mgmtEntityID": mgmtEntityID, "device-id": oFsm.deviceID})
+					}
+				}
+			} else {
+				logger.Warnw(ctx, "No PriorityQueue instances found", log.Fields{"device-id": oFsm.deviceID})
+			}
+			loGemPortAttribs.direction = gemEntry.direction
+			loGemPortAttribs.qosPolicy = gemEntry.queueSchedPolicy
+			loGemPortAttribs.weight = gemEntry.queueWeight
+			loGemPortAttribs.pbitString = gemEntry.pbitString
+
+			if gemEntry.isMulticast {
+				//TODO this might effectively ignore the for loop starting at line 316
+				loGemPortAttribs.gemPortID = gemEntry.multicastGemPortID
+				loGemPortAttribs.isMulticast = true
+				loGemPortAttribs.multicastGemID = gemEntry.multicastGemPortID
+				loGemPortAttribs.staticACL = gemEntry.staticACL
+				loGemPortAttribs.dynamicACL = gemEntry.dynamicACL
+
+				logger.Debugw(ctx, "Multicast GemPort attributes:", log.Fields{
+					"gemPortID":      loGemPortAttribs.gemPortID,
+					"isMulticast":    loGemPortAttribs.isMulticast,
+					"multicastGemID": loGemPortAttribs.multicastGemID,
+					"staticACL":      loGemPortAttribs.staticACL,
+					"dynamicACL":     loGemPortAttribs.dynamicACL,
+					"device-id":      oFsm.deviceID,
+				})
+
+			} else {
+				logger.Debugw(ctx, "Upstream GemPort attributes:", log.Fields{
+					"gemPortID":      loGemPortAttribs.gemPortID,
+					"upQueueID":      loGemPortAttribs.upQueueID,
+					"downQueueID":    loGemPortAttribs.downQueueID,
+					"pbitString":     loGemPortAttribs.pbitString,
+					"prioQueueIndex": gemEntry.prioQueueIndex,
+					"device-id":      oFsm.deviceID,
+				})
+			}
+
+			oFsm.gemPortAttribsSlice = append(oFsm.gemPortAttribsSlice, loGemPortAttribs)
+		}
+		if !oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+			_ = aPAFsm.PFsm.Event(aniEvStartConfig)
+		} else {
+			logger.Debugw(ctx, "reconciling - skip omci-config of ANI side ", log.Fields{"device-id": oFsm.deviceID})
+			_ = aPAFsm.PFsm.Event(aniEvSkipOmciConfig)
+		}
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) enterConfigStartingState(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm start", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+	// in case the used channel is not yet defined (can be re-used after restarts)
+	if oFsm.omciMIdsResponseReceived == nil {
+		oFsm.omciMIdsResponseReceived = make(chan bool)
+		logger.Debug(ctx, "UniPonAniConfigFsm - OMCI multiInstance RxChannel defined")
+	} else {
+		// as we may 're-use' this instance of FSM and the connected channel
+		// make sure there is no 'lingering' request in the already existing channel:
+		// (simple loop sufficient as we are the only receiver)
+		for len(oFsm.omciMIdsResponseReceived) > 0 {
+			<-oFsm.omciMIdsResponseReceived
+		}
+	}
+	//ensure internal slices are empty (which might be set from previous run) - release memory
+	oFsm.gemPortAttribsSlice = nil
+	oFsm.mutexIsAwaitingResponse.Lock()
+	//reset the canceled state possibly existing from previous reset
+	oFsm.isCanceled = false
+	oFsm.mutexIsAwaitingResponse.Unlock()
+
+	// start go routine for processing of ANI config messages
+	go oFsm.processOmciAniMessages(ctx)
+
+	//let the state machine run forward from here directly
+	pConfigAniStateAFsm := oFsm.PAdaptFsm
+	if pConfigAniStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go oFsm.prepareAndEnterConfigState(ctx, pConfigAniStateAFsm)
+
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) enterCreatingDot1PMapper(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm Tx Create::Dot1PMapper", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+	oFsm.requestEventOffset = 0 //0 offset for last config request activity
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendCreateDot1PMapper(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.mapperSP0ID, oFsm.PAdaptFsm.CommChan)
+	if err != nil {
+		logger.Errorw(ctx, "Dot1PMapper create failed, aborting UniPonAniConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterCreatingMBPCD(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm Tx Create::MBPCD", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16),
+		"TPPtr":     strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+	bridgePtr := cmn.MacBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.MacBpNo) //cmp also omci_cc.go::sendCreateMBServiceProfile
+	meParams := me.ParamData{
+		EntityID: oFsm.macBPCD0ID,
+		Attributes: me.AttributeValueMap{
+			"BridgeIdPointer": bridgePtr,
+			"PortNum":         0xFF, //fixed unique ANI side indication
+			"TpType":          3,    //for .1PMapper
+			"TpPointer":       oFsm.mapperSP0ID,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendCreateMBPConfigDataVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		logger.Errorw(ctx, "MBPConfigDataVar create failed, aborting UniPonAniConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterSettingTconts(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm Tx Set::Tcont", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+		"AllocId":   strconv.FormatInt(int64(oFsm.alloc0ID), 16),
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+		"tcontExist": oFsm.tcontSetBefore})
+	//If tcont was set before, then no need to set it again. Let state machine to proceed.
+	if oFsm.tcontSetBefore {
+		go func(aPAFsm *cmn.AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.PFsm != nil {
+				_ = aPAFsm.PFsm.Event(aniEvRxTcontsResp)
+			}
+		}(oFsm.PAdaptFsm)
+		return
+	}
+	meParams := me.ParamData{
+		EntityID: oFsm.tcont0ID,
+		Attributes: me.AttributeValueMap{
+			"AllocId": oFsm.alloc0ID,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendSetTcontVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		logger.Errorw(ctx, "TcontVar set failed, aborting UniPonAniConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterCreatingGemNCTPs(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start creating GemNWCtp loop", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+	go oFsm.performCreatingGemNCTPs(ctx)
+}
+
+func (oFsm *UniPonAniConfigFsm) enterCreatingGemIWs(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start creating GemIwTP loop", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+	go oFsm.performCreatingGemIWs(ctx)
+}
+
+func (oFsm *UniPonAniConfigFsm) enterSettingPQs(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start setting PrioQueue loop", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+	go oFsm.performSettingPQs(ctx)
+}
+
+func (oFsm *UniPonAniConfigFsm) enterSettingDot1PMapper(ctx context.Context, e *fsm.Event) {
+
+	logger.Debugw(ctx, "UniPonAniConfigFsm Tx Set::.1pMapper with all PBits set", log.Fields{"EntitytId": 0x8042, /*cmp above*/
+		"toGemIw":   1024, /* cmp above */
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+	logger.Debugw(ctx, "UniPonAniConfigFsm Tx Set::1pMapper", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+		"in state":  e.FSM.Current(), "device-id": oFsm.deviceID})
+
+	meParams := me.ParamData{
+		EntityID:   oFsm.mapperSP0ID,
+		Attributes: make(me.AttributeValueMap),
+	}
+
+	//assign the GemPorts according to the configured Prio
+	var loPrioGemPortArray [8]uint16
+	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		if gemPortAttribs.isMulticast {
+			logger.Debugw(ctx, "UniPonAniConfigFsm Port is Multicast, ignoring .1pMapper", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
+		if gemPortAttribs.pbitString == "" {
+			logger.Warnw(ctx, "UniPonAniConfigFsm PrioString empty string error", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
+		for i := 0; i < 8; i++ {
+			// "lenOfPbitMap(8) - i + 1" will give i-th pbit value from LSB position in the pbit map string
+			if prio, err := strconv.Atoi(string(gemPortAttribs.pbitString[7-i])); err == nil {
+				if prio == 1 { // Check this p-bit is set
+					if loPrioGemPortArray[i] == 0 {
+						loPrioGemPortArray[i] = gemPortAttribs.gemPortID //gemPortId=EntityID and unique
+					} else {
+						logger.Warnw(ctx, "UniPonAniConfigFsm PrioString not unique", log.Fields{
+							"device-id": oFsm.deviceID, "IgnoredGemPort": gemPortAttribs.gemPortID,
+							"SetGemPort": loPrioGemPortArray[i]})
+					}
+				}
+			} else {
+				logger.Warnw(ctx, "UniPonAniConfigFsm PrioString evaluation error", log.Fields{
+					"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+					"prioString": gemPortAttribs.pbitString, "position": i})
+			}
+
+		}
+	}
+
+	var foundIwPtr = false
+	for index, value := range loPrioGemPortArray {
+		meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
+		if value != 0 {
+			foundIwPtr = true
+			meParams.Attributes[meAttribute] = value
+			logger.Debugw(ctx, "UniPonAniConfigFsm Set::1pMapper", log.Fields{
+				"for Prio":  index,
+				"IwPtr":     strconv.FormatInt(int64(value), 16),
+				"device-id": oFsm.deviceID})
+		} else {
+			// The null pointer 0xFFFF specifies that frames with the associated priority are to be discarded.
+			// setting this parameter is not strictly needed anymore with the ensured .1pMapper create default setting
+			// but except for processing effort does not really harm - left to keep changes low
+			meParams.Attributes[meAttribute] = 0xffff
+		}
+	}
+	// The TP type value 0 also indicates bridging mapping, and the TP pointer should be set to 0xFFFF
+	// setting this parameter is not strictly needed anymore with the ensured .1pMapper create default setting
+	// but except for processing effort does not really harm - left to keep changes low
+	meParams.Attributes["TpPointer"] = 0xffff
+
+	if !foundIwPtr {
+		logger.Debugw(ctx, "UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
+			"device-id": oFsm.deviceID})
+		//TODO With multicast is possible that no upstream gem ports are not present in the tech profile,
+		// this reset needs to be performed only if the tech profile provides upstream gem ports but no priority is set
+		//let's reset the state machine in order to release all resources now
+		//pConfigAniStateAFsm := oFsm.PAdaptFsm
+		//if pConfigAniStateAFsm != nil {
+		//	// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		//	go func(aPAFsm *cmn.AdapterFsm) {
+		//		if aPAFsm != nil && aPAFsm.PFsm != nil {
+		//			_ = aPAFsm.PFsm.Event(aniEvReset)
+		//		}
+		//	}(pConfigAniStateAFsm)
+		//}
+		//Moving forward the FSM as if the response was received correctly.
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvRxDot1pmapSResp)
+				}
+			}(pConfigAniStateAFsm)
+		}
+	} else {
+		oFsm.mutexPLastTxMeInstance.Lock()
+		meInstance, err := oFsm.pOmciCC.SendSetDot1PMapperVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+			oFsm.PAdaptFsm.CommChan, meParams)
+		if err != nil {
+			logger.Errorw(ctx, "Dot1PMapperVar set failed, aborting UniPonAniConfigFsm!",
+				log.Fields{"device-id": oFsm.deviceID})
+			pConfigAniStateAFsm := oFsm.PAdaptFsm
+			if pConfigAniStateAFsm != nil {
+				oFsm.mutexPLastTxMeInstance.Unlock()
+				// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+				go func(aPAFsm *cmn.AdapterFsm) {
+					if aPAFsm != nil && aPAFsm.PFsm != nil {
+						_ = aPAFsm.PFsm.Event(aniEvReset)
+					}
+				}(pConfigAniStateAFsm)
+				return
+			}
+		}
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) enterAniConfigDone(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm ani config done", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+	//store that the UNI related techProfile processing is done for the given Profile and Uni
+	oFsm.pUniTechProf.setConfigDone(oFsm.pOnuUniPort.UniID, oFsm.techProfileID, true)
+	if !oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+		//use DeviceHandler event notification directly
+		oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent((uint8(oFsm.requestEvent) + oFsm.requestEventOffset)))
+		//if techProfile processing is done it must be checked, if some prior/parallel flow configuration is pending
+		//  but only in case the techProfile was configured (not deleted)
+		if oFsm.requestEventOffset == 0 {
+			go oFsm.pDeviceHandler.VerifyUniVlanConfigRequest(ctx, oFsm.pOnuUniPort, oFsm.techProfileID)
+		}
+	} else {
+		logger.Debugw(ctx, "reconciling - skip AniConfigDone processing", log.Fields{"device-id": oFsm.deviceID})
+	}
+	if oFsm.isChanSet() {
+		// indicate processing done to the caller
+		logger.Debugw(ctx, "UniPonAniConfigFsm processingDone on channel", log.Fields{
+			"ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.deviceID})
+		oFsm.chSuccess <- oFsm.procStep
+		oFsm.setChanSet(false) //reset the internal channel state
+	}
+
+	//the FSM is left active in this state as long as no specific reset or remove is requested from outside
+}
+
+func (oFsm *UniPonAniConfigFsm) enterRemovingGemIW(ctx context.Context, e *fsm.Event) {
+	// no need to protect access to oFsm.waitFlowDeleteChannel, only used in synchronized state entries
+	//  or CancelProcessing() that uses separate isWaitingForFlowDelete to write to the channel
+	//flush the waitFlowDeleteChannel - possibly already/still set by some previous activity
+	select {
+	case <-oFsm.waitFlowDeleteChannel:
+		logger.Debug(ctx, "flushed waitFlowDeleteChannel")
+	default:
+	}
+
+	uniVlanConfigFsm := oFsm.pDeviceHandler.GetUniVlanConfigFsm(oFsm.pOnuUniPort.UniID)
+	if uniVlanConfigFsm != nil {
+		// ensure mutexTPState not locked before calling some VlanConfigFsm activity (that might already be pending on it)
+		if uniVlanConfigFsm.IsFlowRemovePending(oFsm.waitFlowDeleteChannel) {
+			logger.Debugw(ctx, "flow remove pending - wait before processing gem port delete",
+				log.Fields{"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+			// if flow remove is pending then wait for flow remove to finish first before proceeding with gem port delete
+			pConfigAniStateAFsm := oFsm.PAdaptFsm
+			if pConfigAniStateAFsm != nil {
+				// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+				go func(aPAFsm *cmn.AdapterFsm) {
+					if aPAFsm != nil && aPAFsm.PFsm != nil {
+						_ = aPAFsm.PFsm.Event(aniEvWaitFlowRem)
+					}
+				}(pConfigAniStateAFsm)
+			} else {
+				logger.Errorw(ctx, "pConfigAniStateAFsm is nil", log.Fields{"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+			}
+			return
+		}
+	} else {
+		logger.Debugw(ctx, "uni vlan config doesn't exist - no flow remove could be pending",
+			log.Fields{"device-id": oFsm.deviceID, "techProfile-id": oFsm.techProfileID})
+	}
+
+	oFsm.pUniTechProf.mutexTPState.RLock()
+	// get the related GemPort entity Id from pUniTechProf, OMCI Gem* entityID is set to be equal to GemPortId!
+	loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
+	oFsm.pUniTechProf.mutexTPState.RUnlock()
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start removing one GemIwTP", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+		"GemIwTp-entity-id": loGemPortID})
+	oFsm.requestEventOffset = 1 //offset 1 to indicate last activity = remove
+
+	// this state entry is only expected in a suitable state (checked outside in onu_uni_tp)
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendDeleteGemIWTP(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, loGemPortID)
+	if err != nil {
+		logger.Errorw(ctx, "GemIWTP delete failed, aborting UniPonAniConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+}
+
+func (oFsm *UniPonAniConfigFsm) enterWaitingFlowRem(ctx context.Context, e *fsm.Event) {
+	oFsm.mutexIsAwaitingResponse.Lock()
+	oFsm.isWaitingForFlowDelete = true
+	oFsm.mutexIsAwaitingResponse.Unlock()
+	select {
+	// maybe be also some outside cancel (but no context modeled for the moment ...)
+	// case <-ctx.Done():
+	// 		logger.Infow("LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
+	case <-time.After(2 * oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second): //give flow processing enough time to finish (but try to be less than rwCore flow timeouts)
+		logger.Warnw(ctx, "UniPonAniConfigFsm WaitingFlowRem timeout", log.Fields{
+			"for device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+		oFsm.mutexIsAwaitingResponse.Lock()
+		oFsm.isWaitingForFlowDelete = false
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		//if the flow is not removed as expected we just try to continue with GemPort removal and hope things are clearing up afterwards
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvFlowRemDone)
+				}
+			}(pConfigAniStateAFsm)
+		} else {
+			logger.Errorw(ctx, "pConfigAniStateAFsm is nil", log.Fields{
+				"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+		}
+		return
+
+	case success := <-oFsm.waitFlowDeleteChannel:
+		if success {
+			logger.Debugw(ctx, "UniPonAniConfigFsm flow removed info received", log.Fields{
+				"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+			oFsm.mutexIsAwaitingResponse.Lock()
+			oFsm.isWaitingForFlowDelete = false
+			oFsm.mutexIsAwaitingResponse.Unlock()
+			pConfigAniStateAFsm := oFsm.PAdaptFsm
+			if pConfigAniStateAFsm != nil {
+				// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+				go func(aPAFsm *cmn.AdapterFsm) {
+					if aPAFsm != nil && aPAFsm.PFsm != nil {
+						_ = aPAFsm.PFsm.Event(aniEvFlowRemDone)
+					}
+				}(pConfigAniStateAFsm)
+			} else {
+				logger.Errorw(ctx, "pConfigAniStateAFsm is nil", log.Fields{
+					"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+			}
+			return
+		}
+		// waiting was aborted (probably on external request)
+		logger.Debugw(ctx, "UniPonAniConfigFsm WaitingFlowRem aborted", log.Fields{
+			"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "techProfile-id": oFsm.techProfileID})
+		oFsm.mutexIsAwaitingResponse.Lock()
+		oFsm.isWaitingForFlowDelete = false
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		//to be sure we can just generate the reset-event to ensure leaving this state towards 'reset'
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+		}
+		return
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) enterRemovingGemNCTP(ctx context.Context, e *fsm.Event) {
+	oFsm.pUniTechProf.mutexTPState.RLock()
+	loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
+	oFsm.pUniTechProf.mutexTPState.RUnlock()
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start removing one GemNCTP", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+		"GemNCTP-entity-id": loGemPortID})
+	// this state entry is only expected in a suitable state (checked outside in onu_uni_tp)
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendDeleteGemNCTP(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, loGemPortID)
+	if err != nil {
+		logger.Errorw(ctx, "GemNCTP delete failed, aborting UniPonAniConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+
+	// Mark the gem port to be removed for Performance History monitoring
+	OnuMetricsManager := oFsm.pDeviceHandler.GetOnuMetricsManager()
+	if OnuMetricsManager != nil {
+		OnuMetricsManager.RemoveGemPortForPerfMonitoring(ctx, loGemPortID)
+	}
+}
+func (oFsm *UniPonAniConfigFsm) enterRemovingTD(ctx context.Context, e *fsm.Event) {
+	oFsm.pUniTechProf.mutexTPState.RLock()
+	loGemPortID := (*(oFsm.pUniTechProf.mapRemoveGemEntry[oFsm.uniTpKey])).gemPortID
+	oFsm.pUniTechProf.mutexTPState.RUnlock()
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start removing Traffic Descriptor", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+		"TD-entity-id": loGemPortID})
+
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendDeleteTD(log.WithSpanFromContext(context.TODO(), ctx),
+		oFsm.pDeviceHandler.GetOmciTimeout(), true, oFsm.PAdaptFsm.CommChan, loGemPortID)
+
+	if err != nil {
+		logger.Errorw(ctx, "TD delete failed - proceed fsm",
+			log.Fields{"device-id": oFsm.deviceID, "gemPortID": loGemPortID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+}
+
+func (oFsm *UniPonAniConfigFsm) enterResettingTcont(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start resetting the TCont", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+	oFsm.requestEventOffset = 1 //offset 1 for last remove activity
+	// this state entry is only expected in a suitable state (checked outside in onu_uni_tp)
+	meParams := me.ParamData{
+		EntityID: oFsm.tcont0ID,
+		Attributes: me.AttributeValueMap{
+			"AllocId": cmn.UnusedTcontAllocID,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendSetTcontVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		logger.Errorw(ctx, "TcontVar set failed, aborting UniPonAniConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterRemoving1pMapper(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start deleting the .1pMapper", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+	mapGemPortParams := oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].mapGemPortParams
+	unicastGemCount := 0
+	for _, gemEntry := range mapGemPortParams {
+		if !gemEntry.isMulticast {
+			unicastGemCount++
+		}
+	}
+	if unicastGemCount > 1 {
+		logger.Debugw(ctx, "UniPonAniConfigFsm - Not the last gem in fsm. Skip the rest", log.Fields{
+			"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "unicast-gem-count": unicastGemCount, "gem-count": len(mapGemPortParams)})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvRemGemDone)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	logger.Debugw(ctx, "UniPonAniConfigFsm - Last gem in fsm. Continue with Mapper removal", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID, "unicast-gem-count": unicastGemCount, "gem-count": len(mapGemPortParams)})
+
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendDeleteDot1PMapper(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, oFsm.mapperSP0ID)
+	if err != nil {
+		logger.Errorw(ctx, "Dot1Mapper delete failed, aborting UniPonAniConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+
+}
+
+func (oFsm *UniPonAniConfigFsm) enterRemovingAniBPCD(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm - start deleting the ANI MBCD", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendDeleteMBPConfigData(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, oFsm.macBPCD0ID)
+	if err != nil {
+		logger.Errorw(ctx, "MBPConfigData delete failed, aborting UniPonAniConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		pConfigAniStateAFsm := oFsm.PAdaptFsm
+		if pConfigAniStateAFsm != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(aPAFsm *cmn.AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.PFsm != nil {
+					_ = aPAFsm.PFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+			return
+		}
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+}
+
+func (oFsm *UniPonAniConfigFsm) enterAniRemoveDone(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm ani removal done", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+	//use DeviceHandler event notification directly
+	oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent((uint8(oFsm.requestEvent) + oFsm.requestEventOffset)))
+	if oFsm.isChanSet() {
+		// indicate processing done to the caller
+		logger.Debugw(ctx, "UniPonAniConfigFsm processingDone on channel", log.Fields{
+			"ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.deviceID})
+		oFsm.chSuccess <- oFsm.procStep
+		oFsm.setChanSet(false) //reset the internal channel state
+	}
+
+	//let's reset the state machine in order to release all resources now
+	pConfigAniStateAFsm := oFsm.PAdaptFsm
+	if pConfigAniStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(aPAFsm *cmn.AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.PFsm != nil {
+				_ = aPAFsm.PFsm.Event(aniEvReset)
+			}
+		}(pConfigAniStateAFsm)
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) enterResettingState(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm resetting", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+	if oFsm.isChanSet() {
+		// indicate processing error to the caller (in case there was still some open request)
+		logger.Debugw(ctx, "UniPonAniConfigFsm processingError on channel", log.Fields{
+			"ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.deviceID})
+		//use non-blocking channel send to avoid blocking because of non-existing receiver
+		//  (even though the channel is checked on 'set', the outside receiver channel might (theoretically) already be deleted)
+		select {
+		case oFsm.chSuccess <- 0:
+		default:
+			logger.Debugw(ctx, "UniPonAniConfigFsm processingError not send on channel (no receiver)", log.Fields{
+				"device-id": oFsm.deviceID})
+		}
+		oFsm.setChanSet(false) //reset the internal channel state
+	}
+
+	pConfigAniStateAFsm := oFsm.PAdaptFsm
+	if pConfigAniStateAFsm != nil {
+		// abort running message processing
+		fsmAbortMsg := cmn.Message{
+			Type: cmn.TestMsg,
+			Data: cmn.TestMessage{
+				TestMessageVal: cmn.AbortMessageProcessing,
+			},
+		}
+		pConfigAniStateAFsm.CommChan <- fsmAbortMsg
+
+		//try to restart the FSM to 'disabled', decouple event transfer
+		go func(aPAFsm *cmn.AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.PFsm != nil {
+				_ = aPAFsm.PFsm.Event(aniEvRestart)
+			}
+		}(pConfigAniStateAFsm)
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) enterDisabledState(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniPonAniConfigFsm enters disabled state", log.Fields{
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
+	oFsm.pLastTxMeInstance = nil
+}
+
+func (oFsm *UniPonAniConfigFsm) processOmciAniMessages(ctx context.Context) {
+	logger.Debugw(ctx, "Start UniPonAniConfigFsm Msg processing", log.Fields{"for device-id": oFsm.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.deviceID})
+		// 	break loop
+		message, ok := <-oFsm.PAdaptFsm.CommChan
+		if !ok {
+			logger.Info(ctx, "UniPonAniConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.deviceID})
+			// but then we have to ensure a restart of the FSM as well - as exceptional procedure
+			_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+			break loop
+		}
+		logger.Debugw(ctx, "UniPonAniConfigFsm Rx Msg", log.Fields{"device-id": oFsm.deviceID})
+
+		switch message.Type {
+		case cmn.TestMsg:
+			msg, _ := message.Data.(cmn.TestMessage)
+			if msg.TestMessageVal == cmn.AbortMessageProcessing {
+				logger.Infow(ctx, "UniPonAniConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.deviceID})
+				break loop
+			}
+			logger.Warnw(ctx, "UniPonAniConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.deviceID, "MessageVal": msg.TestMessageVal})
+		case cmn.OMCI:
+			msg, _ := message.Data.(cmn.OmciMessage)
+			oFsm.handleOmciAniConfigMessage(ctx, msg)
+		default:
+			logger.Warn(ctx, "UniPonAniConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.deviceID,
+				"message.Type": message.Type})
+		}
+
+	}
+	logger.Infow(ctx, "End UniPonAniConfigFsm Msg processing", log.Fields{"device-id": oFsm.deviceID})
+}
+
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigCreateResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
+	if msgLayer == nil {
+		logger.Errorw(ctx, "Omci Msg layer could not be detected for CreateResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return
+	}
+	msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+	if !msgOk {
+		logger.Errorw(ctx, "Omci Msg layer could not be assigned for CreateResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return
+	}
+	logger.Debugw(ctx, "CreateResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+	if msgObj.Result == me.Success || msgObj.Result == me.InstanceExists {
+		//if the result is ok or Instance already exists (latest needed at least as long as we do not clear the OMCI techProfile data)
+		oFsm.mutexPLastTxMeInstance.RLock()
+		if oFsm.pLastTxMeInstance != nil {
+			if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+				msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+				// maybe we can use just the same eventName for different state transitions like "forward"
+				//   - might be checked, but so far I go for sure and have to inspect the concrete state events ...
+				switch oFsm.pLastTxMeInstance.GetName() {
+				case "Ieee8021PMapperServiceProfile":
+					{ // let the FSM proceed ...
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxDot1pmapCResp)
+					}
+				case "MacBridgePortConfigurationData":
+					{ // let the FSM proceed ...
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxMbpcdResp)
+					}
+				case "GemPortNetworkCtp", "GemInterworkingTerminationPoint", "MulticastGemInterworkingTerminationPoint":
+					{ // let aniConfig Multi-Id processing proceed by stopping the wait function
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						oFsm.omciMIdsResponseReceived <- true
+					}
+				default:
+					{
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						logger.Warnw(ctx, "Unsupported ME name received!",
+							log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+					}
+				}
+			} else {
+				oFsm.mutexPLastTxMeInstance.RUnlock()
+			}
+		} else {
+			oFsm.mutexPLastTxMeInstance.RUnlock()
+			logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+		}
+	} else {
+		logger.Errorw(ctx, "Omci CreateResponse Error - later: drive FSM to abort state ?",
+			log.Fields{"Error": msgObj.Result, "device-id": oFsm.deviceID})
+		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+		return
+	}
+}
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigSetFailResponseMessage(ctx context.Context, msgObj *omci.SetResponse) {
+	//If TCONT fails, then we need to revert the allocated TCONT in DB.
+	//Because FSMs are running sequentially, we don't expect the same TCONT hit by another tech-profile FSM while this FSM is running.
+	oFsm.mutexPLastTxMeInstance.RLock()
+	defer oFsm.mutexPLastTxMeInstance.RUnlock()
+	if oFsm.pLastTxMeInstance != nil && msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+		msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+		switch oFsm.pLastTxMeInstance.GetName() {
+		case "TCont":
+			//If this is for TCONT creation(requestEventOffset=0) and this is the first allocation of TCONT(so noone else is using the same TCONT)
+			//We should revert DB
+			if oFsm.requestEventOffset == 0 && !oFsm.tcontSetBefore && oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey] != nil {
+				logger.Debugw(ctx, "UniPonAniConfigFsm TCONT creation failed on device. Freeing alloc id", log.Fields{"device-id": oFsm.deviceID,
+					"alloc-id": oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID, "uni-tp": oFsm.uniTpKey})
+				if oFsm.pOnuDeviceEntry != nil {
+					oFsm.pOnuDeviceEntry.FreeTcont(ctx, oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID)
+				} else {
+					logger.Warnw(ctx, "Unable to get device entry! couldn't free tcont",
+						log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+				}
+			}
+		default:
+			logger.Warnw(ctx, "Unsupported ME name received with error!",
+				log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "result": msgObj.Result, "device-id": oFsm.deviceID})
+		}
+	}
+}
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigSetResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+	if msgLayer == nil {
+		logger.Errorw(ctx, "UniPonAniConfigFsm - Omci Msg layer could not be detected for SetResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return
+	}
+	msgObj, msgOk := msgLayer.(*omci.SetResponse)
+	if !msgOk {
+		logger.Errorw(ctx, "UniPonAniConfigFsm - Omci Msg layer could not be assigned for SetResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return
+	}
+	logger.Debugw(ctx, "UniPonAniConfigFsm SetResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+	if msgObj.Result != me.Success {
+		logger.Errorw(ctx, "UniPonAniConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?",
+			log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+
+		oFsm.handleOmciAniConfigSetFailResponseMessage(ctx, msgObj)
+		return
+	}
+	oFsm.mutexPLastTxMeInstance.RLock()
+	if oFsm.pLastTxMeInstance != nil {
+		if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+			msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+			//store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+			// if, then something like:
+			//oFsm.pOnuDB.StoreMe(msgObj)
+
+			switch oFsm.pLastTxMeInstance.GetName() {
+			case "TCont":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					if oFsm.requestEventOffset == 0 { //from TCont config request
+						_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxTcontsResp)
+					} else { // from T-Cont reset request
+						_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxResetTcontResp)
+					}
+				}
+			case "PriorityQueue", "MulticastGemInterworkingTerminationPoint":
+				{ // let the PrioQueue init proceed by stopping the wait function
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					oFsm.omciMIdsResponseReceived <- true
+				}
+			case "Ieee8021PMapperServiceProfile":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxDot1pmapSResp)
+				}
+			default:
+				{
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					logger.Warnw(ctx, "Unsupported ME name received!",
+						log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+				}
+			}
+		} else {
+			oFsm.mutexPLastTxMeInstance.RUnlock()
+		}
+	} else {
+		oFsm.mutexPLastTxMeInstance.RUnlock()
+		logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigDeleteResponseMessage(ctx context.Context, msg cmn.OmciMessage) {
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeDeleteResponse)
+	if msgLayer == nil {
+		logger.Errorw(ctx, "UniPonAniConfigFsm - Omci Msg layer could not be detected for DeleteResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return
+	}
+	msgObj, msgOk := msgLayer.(*omci.DeleteResponse)
+	if !msgOk {
+		logger.Errorw(ctx, "UniPonAniConfigFsm - Omci Msg layer could not be assigned for DeleteResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return
+	}
+	logger.Debugw(ctx, "UniPonAniConfigFsm DeleteResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+	if msgObj.Result != me.Success {
+		logger.Errorw(ctx, "UniPonAniConfigFsm - Omci DeleteResponse Error",
+			log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+		//TODO:  - later: possibly force FSM into abort or ignore some errors for some messages?
+		//         store error for mgmt display?
+		return
+	}
+	oFsm.mutexPLastTxMeInstance.RLock()
+	if oFsm.pLastTxMeInstance != nil {
+		if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+			msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+			//remove ME from DB //TODO??? obviously the Python code does not store/remove the config ...
+			// if, then something like: oFsm.pOnuDB.XyyMe(msgObj)
+
+			switch oFsm.pLastTxMeInstance.GetName() {
+			case "GemInterworkingTerminationPoint":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRemGemiwResp)
+				}
+			case "GemPortNetworkCtp":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRemGemntpResp)
+				}
+			case "TrafficDescriptor":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRemTdResp)
+				}
+			case "Ieee8021PMapperServiceProfile":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRem1pMapperResp)
+				}
+			case "MacBridgePortConfigurationData":
+				{ // this is the last event of the T-Cont cleanup procedure, FSM may be reset here
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxRemAniBPCDResp)
+				}
+			default:
+				{
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					logger.Warnw(ctx, "Unsupported ME name received!",
+						log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+				}
+			}
+		} else {
+			oFsm.mutexPLastTxMeInstance.RUnlock()
+		}
+	} else {
+		oFsm.mutexPLastTxMeInstance.RUnlock()
+		logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) handleOmciAniConfigMessage(ctx context.Context, msg cmn.OmciMessage) {
+	logger.Debugw(ctx, "Rx OMCI UniPonAniConfigFsm Msg", log.Fields{"device-id": oFsm.deviceID,
+		"msgType": msg.OmciMsg.MessageType})
+
+	switch msg.OmciMsg.MessageType {
+	case omci.CreateResponseType:
+		{
+			oFsm.handleOmciAniConfigCreateResponseMessage(ctx, msg)
+
+		} //CreateResponseType
+	case omci.SetResponseType:
+		{
+			oFsm.handleOmciAniConfigSetResponseMessage(ctx, msg)
+
+		} //SetResponseType
+	case omci.DeleteResponseType:
+		{
+			oFsm.handleOmciAniConfigDeleteResponseMessage(ctx, msg)
+
+		} //DeleteResponseType
+	default:
+		{
+			logger.Errorw(ctx, "UniPonAniConfigFsm - Rx OMCI unhandled MsgType",
+				log.Fields{"omciMsgType": msg.OmciMsg.MessageType, "device-id": oFsm.deviceID})
+			return
+		}
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) performCreatingGemNCTPs(ctx context.Context) {
+	// for all GemPorts of this T-Cont as given by the size of set gemPortAttribsSlice
+	for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		logger.Debugw(ctx, "UniPonAniConfigFsm Tx Create::GemNWCtp", log.Fields{
+			"EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
+			"TcontId":   strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+			"device-id": oFsm.deviceID})
+		meParams := me.ParamData{
+			EntityID: gemPortAttribs.gemPortID, //unique, same as PortId
+			Attributes: me.AttributeValueMap{
+				"PortId":       gemPortAttribs.gemPortID,
+				"TContPointer": oFsm.tcont0ID,
+				"Direction":    gemPortAttribs.direction,
+				//ONU-G.TrafficManagementOption dependency ->PrioQueue or TCont
+				//  TODO!! verify dependency and QueueId in case of Multi-GemPort setup!
+				"TrafficManagementPointerForUpstream": gemPortAttribs.upQueueID, //might be different in wrr-only Setup - tcont0ID
+				"PriorityQueuePointerForDownStream":   gemPortAttribs.downQueueID,
+			},
+		}
+		oFsm.mutexPLastTxMeInstance.Lock()
+		meInstance, err := oFsm.pOmciCC.SendCreateGemNCTPVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+			oFsm.PAdaptFsm.CommChan, meParams)
+		if err != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			logger.Errorw(ctx, "GemNCTPVar create failed, aborting UniPonAniConfigFsm!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+			return
+		}
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		//verify response
+		err = oFsm.waitforOmciResponse(ctx)
+		if err != nil {
+			logger.Errorw(ctx, "GemNWCtp create failed, aborting AniConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID, "GemIndex": gemIndex})
+			_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+			return
+		}
+		// Mark the gem port to be added for Performance History monitoring
+		OnuMetricsManager := oFsm.pDeviceHandler.GetOnuMetricsManager()
+		if OnuMetricsManager != nil {
+			OnuMetricsManager.AddGemPortForPerfMonitoring(ctx, gemPortAttribs.gemPortID)
+		}
+	} //for all GemPorts of this T-Cont
+
+	// if Config has been done for all GemPort instances let the FSM proceed
+	logger.Debugw(ctx, "GemNWCtp create loop finished", log.Fields{"device-id": oFsm.deviceID})
+	_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxGemntcpsResp)
+}
+func (oFsm *UniPonAniConfigFsm) hasMulticastGem(ctx context.Context) bool {
+	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		if gemPortAttribs.isMulticast {
+			logger.Debugw(ctx, "Found multicast gem", log.Fields{"device-id": oFsm.deviceID})
+			return true
+		}
+	}
+	return false
+}
+
+func (oFsm *UniPonAniConfigFsm) performCreatingGemIWs(ctx context.Context) {
+	// for all GemPorts of this T-Cont as given by the size of set gemPortAttribsSlice
+	for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		logger.Debugw(ctx, "UniPonAniConfigFsm Tx Create::GemIwTp", log.Fields{
+			"EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
+			"SPPtr":     strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+			"device-id": oFsm.deviceID})
+
+		//TODO if the port has only downstream direction the isMulticast flag can be removed.
+		if gemPortAttribs.isMulticast {
+
+			meParams := me.ParamData{
+				EntityID: gemPortAttribs.multicastGemID,
+				Attributes: me.AttributeValueMap{
+					"GemPortNetworkCtpConnectivityPointer": gemPortAttribs.multicastGemID,
+					"InterworkingOption":                   0, // Don't Care
+					"ServiceProfilePointer":                0, // Don't Care
+					"GalProfilePointer":                    cmn.GalEthernetEID,
+				},
+			}
+			if oFsm.pUniTechProf.multicastConfiguredForOtherUniTps(ctx, oFsm.uniTpKey) {
+				logger.Debugw(ctx, "MulticastGemInterworkingTP already exist", log.Fields{"device-id": oFsm.deviceID, "multicast-gem-id": gemPortAttribs.multicastGemID})
+				continue
+			}
+			oFsm.mutexPLastTxMeInstance.Lock()
+			meInstance, err := oFsm.pOmciCC.SendCreateMulticastGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(),
+				true, oFsm.PAdaptFsm.CommChan, meParams)
+			if err != nil {
+				oFsm.mutexPLastTxMeInstance.Unlock()
+				logger.Errorw(ctx, "MulticastGemIWTPVar create failed, aborting UniPonAniConfigFsm!",
+					log.Fields{"device-id": oFsm.deviceID})
+				_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+				return
+
+			}
+			oFsm.pLastTxMeInstance = meInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			//verify response
+			err = oFsm.waitforOmciResponse(ctx)
+			if err != nil {
+				logger.Errorw(ctx, "MulticastGemIWTP create failed, aborting AniConfig FSM!",
+					log.Fields{"device-id": oFsm.deviceID, "GemIndex": gemIndex})
+				_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+				return
+			}
+			ipv4MulticastTable := make([]uint8, 12)
+			//Gem Port ID
+			binary.BigEndian.PutUint16(ipv4MulticastTable[0:], gemPortAttribs.multicastGemID)
+			//Secondary Key
+			binary.BigEndian.PutUint16(ipv4MulticastTable[2:], 0)
+			// Multicast IP range start This is the 224.0.0.1 address
+			binary.BigEndian.PutUint32(ipv4MulticastTable[4:], cmn.IPToInt32(net.IPv4(224, 0, 0, 0)))
+			// MulticastIp range stop
+			binary.BigEndian.PutUint32(ipv4MulticastTable[8:], cmn.IPToInt32(net.IPv4(239, 255, 255, 255)))
+
+			meIPV4MCTableParams := me.ParamData{
+				EntityID: gemPortAttribs.multicastGemID,
+				Attributes: me.AttributeValueMap{
+					"Ipv4MulticastAddressTable": ipv4MulticastTable,
+				},
+			}
+			oFsm.mutexPLastTxMeInstance.Lock()
+			meIPV4MCTableInstance, err := oFsm.pOmciCC.SendSetMulticastGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(),
+				true, oFsm.PAdaptFsm.CommChan, meIPV4MCTableParams)
+			if err != nil {
+				oFsm.mutexPLastTxMeInstance.Unlock()
+				logger.Errorw(ctx, "MulticastGemIWTPVar set failed, aborting UniPonAniConfigFsm!",
+					log.Fields{"device-id": oFsm.deviceID})
+				_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+				return
+			}
+			oFsm.pLastTxMeInstance = meIPV4MCTableInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
+
+		} else {
+			meParams := me.ParamData{
+				EntityID: gemPortAttribs.gemPortID,
+				Attributes: me.AttributeValueMap{
+					"GemPortNetworkCtpConnectivityPointer": gemPortAttribs.gemPortID, //same as EntityID, see above
+					"InterworkingOption":                   5,                        //fixed model:: G.998 .1pMapper
+					"ServiceProfilePointer":                oFsm.mapperSP0ID,
+					"InterworkingTerminationPointPointer":  0, //not used with .1PMapper Mac bridge
+					"GalProfilePointer":                    cmn.GalEthernetEID,
+				},
+			}
+			oFsm.mutexPLastTxMeInstance.Lock()
+			meInstance, err := oFsm.pOmciCC.SendCreateGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+				oFsm.PAdaptFsm.CommChan, meParams)
+			if err != nil {
+				oFsm.mutexPLastTxMeInstance.Unlock()
+				logger.Errorw(ctx, "GEMIWTPVar create failed, aborting UniPonAniConfigFsm!",
+					log.Fields{"device-id": oFsm.deviceID})
+				_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+				return
+			}
+			//accept also nil as (error) return value for writing to LastTx
+			//  - this avoids misinterpretation of new received OMCI messages
+			oFsm.pLastTxMeInstance = meInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
+		}
+		//verify response
+		err := oFsm.waitforOmciResponse(ctx)
+		if err != nil {
+			logger.Errorw(ctx, "GemTP create failed, aborting AniConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID, "GemIndex": gemIndex})
+			_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+			return
+		}
+	} //for all GemPort's of this T-Cont
+
+	// if Config has been done for all GemPort instances let the FSM proceed
+	logger.Debugw(ctx, "GemIwTp create loop finished", log.Fields{"device-id": oFsm.deviceID})
+	_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxGemiwsResp)
+}
+
+func (oFsm *UniPonAniConfigFsm) performSettingPQs(ctx context.Context) {
+	//If upstream PQs were set before, then no need to set them again. Let state machine to proceed.
+	if oFsm.tcontSetBefore {
+		logger.Debugw(ctx, "No need to set PQs again.", log.Fields{
+			"device-id": oFsm.deviceID, "tcont": oFsm.alloc0ID,
+			"uni-id":         oFsm.pOnuUniPort.UniID,
+			"techProfile-id": oFsm.techProfileID})
+		go func(aPAFsm *cmn.AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.PFsm != nil {
+				_ = aPAFsm.PFsm.Event(aniEvRxPrioqsResp)
+			}
+		}(oFsm.PAdaptFsm)
+		return
+	}
+	const cu16StrictPrioWeight uint16 = 0xFFFF
+	//find all upstream PrioQueues related to this T-Cont
+	loQueueMap := ordered_map.NewOrderedMap()
+	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		if gemPortAttribs.isMulticast {
+			logger.Debugw(ctx, "UniPonAniConfigFsm Port is Multicast, ignoring PQs", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
+		if gemPortAttribs.qosPolicy == "WRR" {
+			if _, ok := loQueueMap.Get(gemPortAttribs.upQueueID); !ok {
+				//key does not yet exist
+				loQueueMap.Set(gemPortAttribs.upQueueID, uint16(gemPortAttribs.weight))
+			}
+		} else {
+			loQueueMap.Set(gemPortAttribs.upQueueID, cu16StrictPrioWeight) //use invalid weight value to indicate SP
+		}
+	}
+
+	trafficSchedPtrSetSupported := false
+	loOnu2g := oFsm.pOnuDB.GetMe(me.Onu2GClassID, cmn.Onu2gMeID)
+	if loOnu2g == nil {
+		logger.Errorw(ctx, "onu2g is nil, cannot read qos configuration flexibility parameter",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+		return
+	}
+	returnVal := loOnu2g["QualityOfServiceQosConfigurationFlexibility"]
+	if returnVal != nil {
+		if qosCfgFlexParam, err := oFsm.pOnuDB.GetUint16Attrib(returnVal); err == nil {
+			trafficSchedPtrSetSupported = qosCfgFlexParam&bitTrafficSchedulerPtrSetPermitted == bitTrafficSchedulerPtrSetPermitted
+			logger.Debugw(ctx, "trafficSchedPtrSetSupported set",
+				log.Fields{"qosCfgFlexParam": qosCfgFlexParam, "trafficSchedPtrSetSupported": trafficSchedPtrSetSupported})
+		} else {
+			logger.Errorw(ctx, "Cannot extract qos configuration flexibility parameter",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+			return
+		}
+	} else {
+		logger.Errorw(ctx, "Cannot read qos configuration flexibility parameter",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+		return
+	}
+
+	//TODO: assumption here is that ONU data uses SP setting in the T-Cont and WRR in the TrafficScheduler
+	//  if that is not the case, the reverse case could be checked and reacted accordingly or if the
+	//  complete chain is not valid, then some error should be thrown and configuration can be aborted
+	//  or even be finished without correct SP/WRR setting
+
+	//TODO: search for the (WRR)trafficScheduler related to the T-Cont of this queue
+	//By now assume fixed value 0x8000, which is the only announce BBSIM TrafficScheduler,
+	//  even though its T-Cont seems to be wrong ...
+	loTrafficSchedulerEID := 0x8000
+	//for all found queues
+	iter := loQueueMap.IterFunc()
+	for kv, ok := iter(); ok; kv, ok = iter() {
+		queueIndex := (kv.Key).(uint16)
+		meParams := me.ParamData{
+			EntityID:   queueIndex,
+			Attributes: make(me.AttributeValueMap),
+		}
+		if trafficSchedPtrSetSupported {
+			if (kv.Value).(uint16) == cu16StrictPrioWeight {
+				//StrictPrio indication
+				logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::PrioQueue to StrictPrio", log.Fields{
+					"EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+					"device-id": oFsm.deviceID})
+				meParams.Attributes["TrafficSchedulerPointer"] = 0 //ensure T-Cont defined StrictPrio scheduling
+			} else {
+				//WRR indication
+				logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::PrioQueue to WRR", log.Fields{
+					"EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+					"Weight":    kv.Value,
+					"device-id": oFsm.deviceID})
+				meParams.Attributes["TrafficSchedulerPointer"] = loTrafficSchedulerEID //ensure assignment of the relevant trafficScheduler
+				meParams.Attributes["Weight"] = uint8(kv.Value.(uint16))
+			}
+		} else {
+			// setting Traffic Scheduler (TS) pointer is not supported unless we point to another TS that points to the same TCONT.
+			// For now lets use TS that is hardwired in the ONU and just update the weight in case of WRR, which in fact is all we need at the moment.
+			// The code could get unnecessarily convoluted if we provide the flexibility try to find and point to another TS that points to the same TCONT.
+			if (kv.Value).(uint16) == cu16StrictPrioWeight { // SP case, nothing to be done. Proceed to the next queue
+				logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::PrioQueue to StrictPrio, traffic sched ptr set unsupported", log.Fields{
+					"EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+					"device-id": oFsm.deviceID})
+				continue
+			}
+			// WRR case, update weight.
+			logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::PrioQueue to WRR, traffic sched ptr set unsupported", log.Fields{
+				"EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+				"Weight":    kv.Value,
+				"device-id": oFsm.deviceID})
+			meParams.Attributes["Weight"] = uint8(kv.Value.(uint16))
+		}
+		oFsm.mutexPLastTxMeInstance.Lock()
+		meInstance, err := oFsm.pOmciCC.SendSetPrioQueueVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+			oFsm.PAdaptFsm.CommChan, meParams)
+		if err != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			logger.Errorw(ctx, "PrioQueueVar set failed, aborting UniPonAniConfigFsm!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+			return
+		}
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
+
+		//verify response
+		err = oFsm.waitforOmciResponse(ctx)
+		if err != nil {
+			logger.Errorw(ctx, "PrioQueue set failed, aborting AniConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID, "QueueId": strconv.FormatInt(int64(queueIndex), 16)})
+			_ = oFsm.PAdaptFsm.PFsm.Event(aniEvReset)
+			return
+		}
+
+		//TODO: In case of WRR setting of the GemPort/PrioQueue it might further be necessary to
+		//  write the assigned trafficScheduler with the requested Prio to be considered in the StrictPrio scheduling
+		//  of the (next upstream) assigned T-Cont, which is f(prioQueue[priority]) - in relation to other SP prioQueues
+		//  not yet done because of BBSIM TrafficScheduler issues (and not done in py code as well)
+
+	} //for all upstream prioQueues
+
+	// if Config has been done for all PrioQueue instances let the FSM proceed
+	logger.Debugw(ctx, "PrioQueue set loop finished", log.Fields{"device-id": oFsm.deviceID})
+	_ = oFsm.PAdaptFsm.PFsm.Event(aniEvRxPrioqsResp)
+}
+
+func (oFsm *UniPonAniConfigFsm) waitforOmciResponse(ctx context.Context) error {
+	oFsm.mutexIsAwaitingResponse.Lock()
+	if oFsm.isCanceled {
+		// FSM already canceled before entering wait
+		logger.Debugw(ctx, "UniPonAniConfigFsm wait-for-multi-entity-response aborted (on enter)", log.Fields{"for device-id": oFsm.deviceID})
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		return fmt.Errorf(cmn.CErrWaitAborted)
+	}
+	oFsm.isAwaitingResponse = true
+	oFsm.mutexIsAwaitingResponse.Unlock()
+	select {
+	// maybe be also some outside cancel (but no context modeled for the moment ...)
+	// case <-ctx.Done():
+	// 		logger.Infow("LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
+	case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+		logger.Warnw(ctx, "UniPonAniConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.deviceID})
+		oFsm.mutexIsAwaitingResponse.Lock()
+		oFsm.isAwaitingResponse = false
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		return fmt.Errorf("uniPonAniConfigFsm multi entity timeout %s", oFsm.deviceID)
+	case success := <-oFsm.omciMIdsResponseReceived:
+		if success {
+			logger.Debugw(ctx, "UniPonAniConfigFsm multi entity response received", log.Fields{"for device-id": oFsm.deviceID})
+			oFsm.mutexIsAwaitingResponse.Lock()
+			oFsm.isAwaitingResponse = false
+			oFsm.mutexIsAwaitingResponse.Unlock()
+			return nil
+		}
+		// waiting was aborted (probably on external request)
+		logger.Debugw(ctx, "UniPonAniConfigFsm wait-for-multi-entity-response aborted", log.Fields{"for device-id": oFsm.deviceID})
+		oFsm.mutexIsAwaitingResponse.Lock()
+		oFsm.isAwaitingResponse = false
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		return fmt.Errorf(cmn.CErrWaitAborted)
+	}
+}
+
+func (oFsm *UniPonAniConfigFsm) setChanSet(flagValue bool) {
+	oFsm.mutexChanSet.Lock()
+	oFsm.chanSet = flagValue
+	oFsm.mutexChanSet.Unlock()
+}
+
+func (oFsm *UniPonAniConfigFsm) isChanSet() bool {
+	oFsm.mutexChanSet.RLock()
+	flagValue := oFsm.chanSet
+	oFsm.mutexChanSet.RUnlock()
+	return flagValue
+}
diff --git a/internal/pkg/avcfg/omci_vlan_config.go b/internal/pkg/avcfg/omci_vlan_config.go
new file mode 100755
index 0000000..4a690f7
--- /dev/null
+++ b/internal/pkg/avcfg/omci_vlan_config.go
@@ -0,0 +1,3123 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed 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
+ *
+ * http://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 avcfg provides anig and vlan configuration functionality
+package avcfg
+
+import (
+	"context"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"net"
+	"strconv"
+	"sync"
+	"time"
+
+	meters "github.com/opencord/voltha-lib-go/v7/pkg/meters"
+	"github.com/opencord/voltha-protos/v5/go/voltha"
+
+	gp "github.com/google/gopacket"
+	"github.com/looplab/fsm"
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+	cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
+	"github.com/opencord/voltha-openonu-adapter-go/internal/pkg/devdb"
+	of "github.com/opencord/voltha-protos/v5/go/openflow_13"
+)
+
+const (
+	// internal predefined values
+	cDefaultDownstreamMode = 0
+	cDefaultTpid           = 0x8100
+	cVtfdTableSize         = 12             //as per G.988
+	cMaxAllowedFlows       = cVtfdTableSize //which might be under discussion, for the moment connected to limit of VLAN's within VTFD
+)
+
+const (
+	//  internal offsets for requestEvent according to definition in onu_device_entry::cmn.OnuDeviceEvent
+	cDeviceEventOffsetAddWithKvStore    = 0 //OmciVlanFilterAddDone - OmciVlanFilterAddDone cannot use because of lint
+	cDeviceEventOffsetAddNoKvStore      = cmn.OmciVlanFilterAddDoneNoKvStore - cmn.OmciVlanFilterAddDone
+	cDeviceEventOffsetRemoveWithKvStore = cmn.OmciVlanFilterRemDone - cmn.OmciVlanFilterAddDone
+	cDeviceEventOffsetRemoveNoKvStore   = cmn.OmciVlanFilterRemDoneNoKvStore - cmn.OmciVlanFilterAddDone
+)
+
+const (
+	// bit mask offsets for EVTOCD VlanTaggingOperationTable related to 32 bits (4 bytes)
+	cFilterPrioOffset      = 28
+	cFilterVidOffset       = 15
+	cFilterTpidOffset      = 12
+	cFilterEtherTypeOffset = 0
+	cTreatTTROffset        = 30
+	cTreatPrioOffset       = 16
+	cTreatVidOffset        = 3
+	cTreatTpidOffset       = 0
+)
+const (
+	// byte offsets for EVTOCD VlanTaggingOperationTable related to overall 16 byte size with slice byte 0 as first Byte (MSB)
+	cFilterOuterOffset = 0
+	cFilterInnerOffset = 4
+	cTreatOuterOffset  = 8
+	cTreatInnerOffset  = 12
+)
+const (
+	// basic values used within EVTOCD VlanTaggingOperationTable in respect to their bitfields
+	cPrioIgnoreTag        uint32 = 15
+	cPrioDefaultFilter    uint32 = 14
+	cPrioDoNotFilter      uint32 = 8
+	cDoNotFilterVid       uint32 = 4096
+	cDoNotFilterTPID      uint32 = 0
+	cDoNotFilterEtherType uint32 = 0
+	cDoNotAddPrio         uint32 = 15
+	cCopyPrioFromInner    uint32 = 8
+	//cDontCarePrio         uint32 = 0
+	cDontCareVid          uint32 = 0
+	cDontCareTpid         uint32 = 0
+	cSetOutputTpidCopyDei uint32 = 4
+)
+
+// events of config UNI port VLAN FSM
+const (
+	VlanEvStart                   = "VlanEvStart"
+	VlanEvPrepareDone             = "VlanEvPrepareDone"
+	VlanEvWaitTechProf            = "VlanEvWaitTechProf"
+	VlanEvCancelOutstandingConfig = "VlanEvCancelOutstandingConfig"
+	VlanEvContinueConfig          = "VlanEvContinueConfig"
+	VlanEvStartConfig             = "VlanEvStartConfig"
+	VlanEvRxConfigVtfd            = "VlanEvRxConfigVtfd"
+	VlanEvRxConfigEvtocd          = "VlanEvRxConfigEvtocd"
+	VlanEvWaitTPIncr              = "VlanEvWaitTPIncr"
+	VlanEvIncrFlowConfig          = "VlanEvIncrFlowConfig"
+	VlanEvRenew                   = "VlanEvRenew"
+	VlanEvRemFlowConfig           = "VlanEvRemFlowConfig"
+	VlanEvRemFlowDone             = "VlanEvRemFlowDone"
+	VlanEvFlowDataRemoved         = "VlanEvFlowDataRemoved"
+	//VlanEvTimeoutSimple  = "VlanEvTimeoutSimple"
+	//VlanEvTimeoutMids    = "VlanEvTimeoutMids"
+	VlanEvReset             = "VlanEvReset"
+	VlanEvRestart           = "VlanEvRestart"
+	VlanEvSkipOmciConfig    = "VlanEvSkipOmciConfig"
+	VlanEvSkipIncFlowConfig = "VlanEvSkipIncFlowConfig"
+)
+
+// states of config UNI port VLAN FSM
+const (
+	VlanStDisabled        = "VlanStDisabled"
+	VlanStPreparing       = "VlanStPreparing"
+	VlanStStarting        = "VlanStStarting"
+	VlanStWaitingTechProf = "VlanStWaitingTechProf"
+	VlanStConfigVtfd      = "VlanStConfigVtfd"
+	VlanStConfigEvtocd    = "VlanStConfigEvtocd"
+	VlanStConfigDone      = "VlanStConfigDone"
+	VlanStIncrFlowWaitTP  = "VlanStIncrFlowWaitTP"
+	VlanStConfigIncrFlow  = "VlanStConfigIncrFlow"
+	VlanStRemoveFlow      = "VlanStRemoveFlow"
+	VlanStCleanupDone     = "VlanStCleanupDone"
+	VlanStResetting       = "VlanStResetting"
+)
+
+// CVlanFsmIdleState - TODO: add comment
+const CVlanFsmIdleState = VlanStConfigDone // state where no OMCI activity is done (for a longer time)
+// CVlanFsmConfiguredState - TODO: add comment
+const CVlanFsmConfiguredState = VlanStConfigDone // state that indicates that at least some valid user related VLAN configuration should exist
+
+type uniRemoveVlanFlowParams struct {
+	isSuspendedOnAdd bool
+	removeChannel    chan bool
+	cookie           uint64 //just the last cookie valid for removal
+	vlanRuleParams   cmn.UniVlanRuleParams
+}
+
+//UniVlanConfigFsm defines the structure for the state machine for configuration of the VLAN related setting via OMCI
+//  builds upon 'VLAN rules' that are derived from multiple flows
+type UniVlanConfigFsm struct {
+	pDeviceHandler              cmn.IdeviceHandler
+	pOnuDeviceEntry             cmn.IonuDeviceEntry
+	deviceID                    string
+	pOmciCC                     *cmn.OmciCC
+	pOnuUniPort                 *cmn.OnuUniPort
+	pUniTechProf                *OnuUniTechProf
+	pOnuDB                      *devdb.OnuDeviceDB
+	requestEvent                cmn.OnuDeviceEvent
+	omciMIdsResponseReceived    chan bool //seperate channel needed for checking multiInstance OMCI message responses
+	PAdaptFsm                   *cmn.AdapterFsm
+	acceptIncrementalEvtoOption bool
+	clearPersistency            bool
+	isCanceled                  bool
+	isAwaitingResponse          bool
+	mutexIsAwaitingResponse     sync.RWMutex
+	mutexFlowParams             sync.RWMutex
+	chCookieDeleted             chan bool //channel to indicate that a specific cookie (related to the active rule) was deleted
+	actualUniVlanConfigRule     cmn.UniVlanRuleParams
+	actualUniVlanConfigMeter    *voltha.OfpMeterConfig
+	uniVlanFlowParamsSlice      []cmn.UniVlanFlowParams
+	uniRemoveFlowsSlice         []uniRemoveVlanFlowParams
+	NumUniFlows                 uint8 // expected number of flows should be less than 12
+	ConfiguredUniFlow           uint8
+	numRemoveFlows              uint8
+	numVlanFilterEntries        uint8
+	vlanFilterList              [cVtfdTableSize]uint16
+	evtocdID                    uint16
+	mutexPLastTxMeInstance      sync.RWMutex
+	pLastTxMeInstance           *me.ManagedEntity
+	requestEventOffset          uint8
+	TpIDWaitingFor              uint8
+	signalOnFlowDelete          bool
+	flowDeleteChannel           chan<- bool
+	//cookie value that indicates that a rule to add is delayed by waiting for deletion of some other existing rule with the same cookie
+	delayNewRuleCookie uint64
+	// Used to indicate if the FSM is for a reconciling flow and if it's the last flow to be reconciled
+	// thus notification needs to be sent on chan.
+	lastFlowToReconcile bool
+}
+
+//NewUniVlanConfigFsm is the 'constructor' for the state machine to config the PON ANI ports
+//  of ONU UNI ports via OMCI
+func NewUniVlanConfigFsm(ctx context.Context, apDeviceHandler cmn.IdeviceHandler, apOnuDeviceEntry cmn.IonuDeviceEntry, apDevOmciCC *cmn.OmciCC, apUniPort *cmn.OnuUniPort,
+	apUniTechProf *OnuUniTechProf, apOnuDB *devdb.OnuDeviceDB, aTechProfileID uint8,
+	aRequestEvent cmn.OnuDeviceEvent, aName string, aCommChannel chan cmn.Message, aAcceptIncrementalEvto bool,
+	aCookieSlice []uint64, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, lastFlowToRec bool, aMeter *voltha.OfpMeterConfig) *UniVlanConfigFsm {
+	instFsm := &UniVlanConfigFsm{
+		pDeviceHandler:              apDeviceHandler,
+		pOnuDeviceEntry:             apOnuDeviceEntry,
+		deviceID:                    apDeviceHandler.GetDeviceID(),
+		pOmciCC:                     apDevOmciCC,
+		pOnuUniPort:                 apUniPort,
+		pUniTechProf:                apUniTechProf,
+		pOnuDB:                      apOnuDB,
+		requestEvent:                aRequestEvent,
+		acceptIncrementalEvtoOption: aAcceptIncrementalEvto,
+		NumUniFlows:                 0,
+		ConfiguredUniFlow:           0,
+		numRemoveFlows:              0,
+		clearPersistency:            true,
+		lastFlowToReconcile:         lastFlowToRec,
+	}
+
+	instFsm.PAdaptFsm = cmn.NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
+	if instFsm.PAdaptFsm == nil {
+		logger.Errorw(ctx, "UniVlanConfigFsm's cmn.AdapterFsm could not be instantiated!!", log.Fields{
+			"device-id": instFsm.deviceID})
+		return nil
+	}
+	instFsm.PAdaptFsm.PFsm = fsm.NewFSM(
+		VlanStDisabled,
+		fsm.Events{
+			{Name: VlanEvStart, Src: []string{VlanStDisabled}, Dst: VlanStPreparing},
+			{Name: VlanEvPrepareDone, Src: []string{VlanStPreparing}, Dst: VlanStStarting},
+			{Name: VlanEvWaitTechProf, Src: []string{VlanStStarting}, Dst: VlanStWaitingTechProf},
+			{Name: VlanEvCancelOutstandingConfig, Src: []string{VlanStWaitingTechProf}, Dst: VlanStConfigDone},
+			{Name: VlanEvContinueConfig, Src: []string{VlanStWaitingTechProf}, Dst: VlanStConfigVtfd},
+			{Name: VlanEvStartConfig, Src: []string{VlanStStarting}, Dst: VlanStConfigVtfd},
+			{Name: VlanEvRxConfigVtfd, Src: []string{VlanStConfigVtfd}, Dst: VlanStConfigEvtocd},
+			{Name: VlanEvRxConfigEvtocd, Src: []string{VlanStConfigEvtocd, VlanStConfigIncrFlow},
+				Dst: VlanStConfigDone},
+			{Name: VlanEvRenew, Src: []string{VlanStConfigDone}, Dst: VlanStStarting},
+			{Name: VlanEvWaitTPIncr, Src: []string{VlanStConfigDone}, Dst: VlanStIncrFlowWaitTP},
+			{Name: VlanEvIncrFlowConfig, Src: []string{VlanStConfigDone, VlanStIncrFlowWaitTP},
+				Dst: VlanStConfigIncrFlow},
+			{Name: VlanEvRemFlowConfig, Src: []string{VlanStConfigDone}, Dst: VlanStRemoveFlow},
+			{Name: VlanEvRemFlowDone, Src: []string{VlanStRemoveFlow}, Dst: VlanStCleanupDone},
+			{Name: VlanEvFlowDataRemoved, Src: []string{VlanStCleanupDone}, Dst: VlanStConfigDone},
+			/*
+				{Name: VlanEvTimeoutSimple, Src: []string{
+					VlanStCreatingDot1PMapper, VlanStCreatingMBPCD, VlanStSettingTconts, VlanStSettingDot1PMapper}, Dst: VlanStStarting},
+				{Name: VlanEvTimeoutMids, Src: []string{
+					VlanStCreatingGemNCTPs, VlanStCreatingGemIWs, VlanStSettingPQs}, Dst: VlanStStarting},
+			*/
+			// exceptional treatment for all states except VlanStResetting
+			{Name: VlanEvReset, Src: []string{VlanStStarting, VlanStWaitingTechProf,
+				VlanStConfigVtfd, VlanStConfigEvtocd, VlanStConfigDone, VlanStConfigIncrFlow,
+				VlanStRemoveFlow, VlanStCleanupDone},
+				Dst: VlanStResetting},
+			// the only way to get to resource-cleared disabled state again is via "resseting"
+			{Name: VlanEvRestart, Src: []string{VlanStResetting}, Dst: VlanStDisabled},
+			// transitions for reconcile handling according to VOL-3834
+			{Name: VlanEvSkipOmciConfig, Src: []string{VlanStPreparing}, Dst: VlanStConfigDone},
+			{Name: VlanEvSkipOmciConfig, Src: []string{VlanStConfigDone}, Dst: VlanStConfigIncrFlow},
+			{Name: VlanEvSkipIncFlowConfig, Src: []string{VlanStConfigIncrFlow}, Dst: VlanStConfigDone},
+		},
+		fsm.Callbacks{
+			"enter_state":                   func(e *fsm.Event) { instFsm.PAdaptFsm.LogFsmStateChange(ctx, e) },
+			"enter_" + VlanStPreparing:      func(e *fsm.Event) { instFsm.enterPreparing(ctx, e) },
+			"enter_" + VlanStStarting:       func(e *fsm.Event) { instFsm.enterConfigStarting(ctx, e) },
+			"enter_" + VlanStConfigVtfd:     func(e *fsm.Event) { instFsm.enterConfigVtfd(ctx, e) },
+			"enter_" + VlanStConfigEvtocd:   func(e *fsm.Event) { instFsm.enterConfigEvtocd(ctx, e) },
+			"enter_" + VlanStConfigDone:     func(e *fsm.Event) { instFsm.enterVlanConfigDone(ctx, e) },
+			"enter_" + VlanStConfigIncrFlow: func(e *fsm.Event) { instFsm.enterConfigIncrFlow(ctx, e) },
+			"enter_" + VlanStRemoveFlow:     func(e *fsm.Event) { instFsm.enterRemoveFlow(ctx, e) },
+			"enter_" + VlanStCleanupDone:    func(e *fsm.Event) { instFsm.enterVlanCleanupDone(ctx, e) },
+			"enter_" + VlanStResetting:      func(e *fsm.Event) { instFsm.enterResetting(ctx, e) },
+			"enter_" + VlanStDisabled:       func(e *fsm.Event) { instFsm.enterDisabled(ctx, e) },
+		},
+	)
+	if instFsm.PAdaptFsm.PFsm == nil {
+		logger.Errorw(ctx, "UniVlanConfigFsm's Base FSM could not be instantiated!!", log.Fields{
+			"device-id": instFsm.deviceID})
+		return nil
+	}
+
+	_ = instFsm.initUniFlowParams(ctx, aTechProfileID, aCookieSlice, aMatchVlan, aSetVlan, aSetPcp, aMeter)
+
+	logger.Debugw(ctx, "UniVlanConfigFsm created", log.Fields{"device-id": instFsm.deviceID,
+		"accIncrEvto": instFsm.acceptIncrementalEvtoOption})
+	return instFsm
+}
+
+//initUniFlowParams is a simplified form of SetUniFlowParams() used for first flow parameters configuration
+func (oFsm *UniVlanConfigFsm) initUniFlowParams(ctx context.Context, aTpID uint8, aCookieSlice []uint64,
+	aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, aMeter *voltha.OfpMeterConfig) error {
+	loRuleParams := cmn.UniVlanRuleParams{
+		TpID:     aTpID,
+		MatchVid: uint32(aMatchVlan),
+		SetVid:   uint32(aSetVlan),
+		SetPcp:   uint32(aSetPcp),
+	}
+	// some automatic adjustments on the filter/treat parameters as not specifically configured/ensured by flow configuration parameters
+	loRuleParams.TagsToRemove = 1            //one tag to remove as default setting
+	loRuleParams.MatchPcp = cPrioDoNotFilter // do not Filter on prio as default
+
+	if loRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		//then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
+		loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+		//TODO!!: maybe be needed to be re-checked at flow deletion (but assume all flows are always deleted togehther)
+	} else {
+		if !oFsm.acceptIncrementalEvtoOption {
+			//then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
+			loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+		}
+	}
+
+	if loRuleParams.MatchVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		// no prio/vid filtering requested
+		loRuleParams.TagsToRemove = 0          //no tag pop action
+		loRuleParams.MatchPcp = cPrioIgnoreTag // no vlan tag filtering
+		if loRuleParams.SetPcp == cCopyPrioFromInner {
+			//in case of no filtering and configured PrioCopy ensure default prio setting to 0
+			// which is required for stacking of untagged, but obviously also ensures prio setting for prio/singletagged
+			// might collide with NoMatchVid/CopyPrio(/setVid) setting
+			// this was some precondition setting taken over from py adapter ..
+			loRuleParams.SetPcp = 0
+		}
+	}
+
+	loFlowParams := cmn.UniVlanFlowParams{VlanRuleParams: loRuleParams}
+	loFlowParams.CookieSlice = make([]uint64, 0)
+	loFlowParams.CookieSlice = append(loFlowParams.CookieSlice, aCookieSlice...)
+	if aMeter != nil {
+		loFlowParams.Meter = aMeter
+	}
+
+	//no mutex protection is required for initial access and adding the first flow is always possible
+	oFsm.uniVlanFlowParamsSlice = make([]cmn.UniVlanFlowParams, 0)
+	oFsm.uniVlanFlowParamsSlice = append(oFsm.uniVlanFlowParamsSlice, loFlowParams)
+	logger.Debugw(ctx, "first UniVlanConfigFsm flow added", log.Fields{
+		"Cookies":   oFsm.uniVlanFlowParamsSlice[0].CookieSlice,
+		"MatchVid":  strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
+		"SetVid":    strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+		"SetPcp":    loRuleParams.SetPcp,
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+	if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+		oFsm.reconcileVlanFilterList(ctx, uint16(loRuleParams.SetVid))
+	}
+	oFsm.NumUniFlows = 1
+	oFsm.uniRemoveFlowsSlice = make([]uniRemoveVlanFlowParams, 0) //initially nothing to remove
+
+	//permanently store flow config for reconcile case
+	if err := oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID,
+		&oFsm.uniVlanFlowParamsSlice, true); err != nil {
+		logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+
+	return nil
+}
+
+//CancelProcessing ensures that suspended processing at waiting on some response is aborted and reset of FSM
+func (oFsm *UniVlanConfigFsm) CancelProcessing(ctx context.Context) {
+	//mutex protection is required for possible concurrent access to FSM members
+	oFsm.mutexIsAwaitingResponse.Lock()
+	oFsm.isCanceled = true
+	if oFsm.isAwaitingResponse {
+		//attention: for an unbuffered channel the sender is blocked until the value is received (processed)!
+		// accordingly the mutex must be released before sending to channel here (mutex acquired in receiver)
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		//use channel to indicate that the response waiting shall be aborted
+		oFsm.omciMIdsResponseReceived <- false
+	} else {
+		oFsm.mutexIsAwaitingResponse.Unlock()
+	}
+
+	// in any case (even if it might be automatically requested by above cancellation of waiting) ensure resetting the FSM
+	PAdaptFsm := oFsm.PAdaptFsm
+	if PAdaptFsm != nil {
+		if fsmErr := PAdaptFsm.PFsm.Event(VlanEvReset); fsmErr != nil {
+			logger.Errorw(ctx, "reset-event failed in UniVlanConfigFsm!",
+				log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+		}
+	}
+}
+
+//GetWaitingTpID returns the TpId that the FSM might be waiting for continuation (0 if none)
+func (oFsm *UniVlanConfigFsm) GetWaitingTpID() uint8 {
+	//mutex protection is required for possible concurrent access to FSM members
+	oFsm.mutexFlowParams.RLock()
+	defer oFsm.mutexFlowParams.RUnlock()
+	return oFsm.TpIDWaitingFor
+}
+
+//RequestClearPersistency sets the internal flag to not clear persistency data (false=NoClear)
+func (oFsm *UniVlanConfigFsm) RequestClearPersistency(aClear bool) {
+	//mutex protection is required for possible concurrent access to FSM members
+	oFsm.mutexFlowParams.Lock()
+	defer oFsm.mutexFlowParams.Unlock()
+	oFsm.clearPersistency = aClear
+}
+
+//SetUniFlowParams verifies on existence of flow parameters to be configured,
+// optionally udates the cookie list or appends a new flow if there is space
+// if possible the FSM is trigggerd to start with the processing
+// ignore complexity by now
+// nolint: gocyclo
+func (oFsm *UniVlanConfigFsm) SetUniFlowParams(ctx context.Context, aTpID uint8, aCookieSlice []uint64,
+	aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, lastFlowToReconcile bool, aMeter *voltha.OfpMeterConfig) error {
+	loRuleParams := cmn.UniVlanRuleParams{
+		TpID:     aTpID,
+		MatchVid: uint32(aMatchVlan),
+		SetVid:   uint32(aSetVlan),
+		SetPcp:   uint32(aSetPcp),
+	}
+	// some automatic adjustments on the filter/treat parameters as not specifically configured/ensured by flow configuration parameters
+	loRuleParams.TagsToRemove = 1            //one tag to remove as default setting
+	loRuleParams.MatchPcp = cPrioDoNotFilter // do not Filter on prio as default
+	if loRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		//then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
+		loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+		//TODO!!: maybe be needed to be re-checked at flow deletion (but assume all flows are always deleted togehther)
+	} else {
+		if !oFsm.acceptIncrementalEvtoOption {
+			//then matchVlan is don't care and should be overwritten to 'transparent' here to avoid unneeded multiple flow entries
+			loRuleParams.MatchVid = uint32(of.OfpVlanId_OFPVID_PRESENT)
+		}
+	}
+
+	if loRuleParams.MatchVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		// no prio/vid filtering requested
+		loRuleParams.TagsToRemove = 0          //no tag pop action
+		loRuleParams.MatchPcp = cPrioIgnoreTag // no vlan tag filtering
+		if loRuleParams.SetPcp == cCopyPrioFromInner {
+			//in case of no filtering and configured PrioCopy ensure default prio setting to 0
+			// which is required for stacking of untagged, but obviously also ensures prio setting for prio/singletagged
+			// might collide with NoMatchVid/CopyPrio(/setVid) setting
+			// this was some precondition setting taken over from py adapter ..
+			loRuleParams.SetPcp = 0
+		}
+	}
+
+	//check if there is some ongoing delete-request running for this flow. If so, block here until this is finished.
+	//  might be accordingly rwCore processing runs into timeout in specific situations - needs to be observed ...
+	//  this is to protect uniVlanFlowParams from adding new or re-writing the same cookie to the rule currently under deletion
+	oFsm.mutexFlowParams.RLock()
+	if len(oFsm.uniRemoveFlowsSlice) > 0 {
+		for flow, removeUniFlowParams := range oFsm.uniRemoveFlowsSlice {
+			if removeUniFlowParams.vlanRuleParams == loRuleParams {
+				// the flow to add is the same as the one already in progress of deleting
+				logger.Infow(ctx, "UniVlanConfigFsm flow setting - suspending rule-add due to ongoing removal", log.Fields{
+					"device-id": oFsm.deviceID, "cookie": removeUniFlowParams.cookie, "remove-index": flow})
+				if flow >= len(oFsm.uniRemoveFlowsSlice) {
+					logger.Errorw(ctx, "abort UniVlanConfigFsm flow add - inconsistent RemoveFlowsSlice", log.Fields{
+						"device-id": oFsm.deviceID, "slice length": len(oFsm.uniRemoveFlowsSlice)})
+					oFsm.mutexFlowParams.RUnlock()
+					return fmt.Errorf("abort UniVlanConfigFsm flow add - inconsistent RemoveFlowsSlice %s", oFsm.deviceID)
+				}
+				pRemoveParams := &oFsm.uniRemoveFlowsSlice[flow] //wants to modify the uniRemoveFlowsSlice element directly!
+				oFsm.mutexFlowParams.RUnlock()
+				if err := oFsm.suspendAddRule(ctx, pRemoveParams); err != nil {
+					logger.Errorw(ctx, "UniVlanConfigFsm suspension on add aborted - abort complete add-request", log.Fields{
+						"device-id": oFsm.deviceID, "cookie": removeUniFlowParams.cookie})
+					return fmt.Errorf("abort UniVlanConfigFsm suspension on add %s", oFsm.deviceID)
+				}
+				oFsm.mutexFlowParams.RLock()
+				break //this specific rule should only exist once per uniRemoveFlowsSlice
+			}
+		}
+	}
+	oFsm.mutexFlowParams.RUnlock()
+
+	flowEntryMatch := false
+	flowCookieModify := false
+	requestAppendRule := false
+	oFsm.lastFlowToReconcile = lastFlowToReconcile
+	//mutex protection is required for possible concurrent access to FSM members
+	oFsm.mutexFlowParams.Lock()
+	for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+		//TODO: Verify if using e.g. hashes for the structures here for comparison may generate
+		//  countable run time optimization (perhaps with including the hash in kvStore storage?)
+		if storedUniFlowParams.VlanRuleParams == loRuleParams {
+			flowEntryMatch = true
+			logger.Debugw(ctx, "UniVlanConfigFsm flow setting - rule already exists", log.Fields{
+				"MatchVid":  strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
+				"SetVid":    strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+				"SetPcp":    loRuleParams.SetPcp,
+				"device-id": oFsm.deviceID, " uni-id": oFsm.pOnuUniPort.UniID})
+			var cookieMatch bool
+			for _, newCookie := range aCookieSlice { // for all cookies available in the arguments
+				cookieMatch = false
+				for _, cookie := range storedUniFlowParams.CookieSlice {
+					if cookie == newCookie {
+						logger.Debugw(ctx, "UniVlanConfigFsm flow setting - and cookie already exists", log.Fields{
+							"device-id": oFsm.deviceID, "cookie": cookie})
+						cookieMatch = true
+						break //found new cookie - no further search for this requested cookie
+					}
+				}
+				if !cookieMatch {
+					delayedCookie := oFsm.delayNewRuleForCookie(ctx, aCookieSlice)
+					if delayedCookie != 0 {
+						//a delay for adding the cookie to this rule is requested
+						// take care of the mutex which is already locked here, need to unlock/lock accordingly to prevent deadlock in suspension
+						oFsm.mutexFlowParams.Unlock()
+						if deleteSuccess := oFsm.suspendNewRule(ctx); !deleteSuccess {
+							logger.Errorw(ctx, "UniVlanConfigFsm suspended add-cookie-to-rule aborted", log.Fields{
+								"device-id": oFsm.deviceID, "cookie": delayedCookie})
+							return fmt.Errorf(" UniVlanConfigFsm suspended add-cookie-to-rule aborted %s", oFsm.deviceID)
+						}
+						flowCookieModify, requestAppendRule = oFsm.reviseFlowConstellation(ctx, delayedCookie, loRuleParams)
+						oFsm.mutexFlowParams.Lock()
+					} else {
+						logger.Debugw(ctx, "UniVlanConfigFsm flow setting -adding new cookie", log.Fields{
+							"device-id": oFsm.deviceID, "cookie": newCookie})
+						//as range works with copies of the slice we have to write to the original slice!!
+						oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(oFsm.uniVlanFlowParamsSlice[flow].CookieSlice,
+							newCookie)
+						flowCookieModify = true
+					}
+				}
+			} //for all new cookies
+			break // found rule - no further rule search
+		}
+	}
+	oFsm.mutexFlowParams.Unlock()
+
+	if !flowEntryMatch { //it is (was) a new rule
+		delayedCookie, deleteSuccess := oFsm.suspendIfRequiredNewRule(ctx, aCookieSlice)
+		if !deleteSuccess {
+			logger.Errorw(ctx, "UniVlanConfigFsm suspended add-new-rule aborted", log.Fields{
+				"device-id": oFsm.deviceID, "cookie": delayedCookie})
+			return fmt.Errorf(" UniVlanConfigFsm suspended add-new-rule aborted %s", oFsm.deviceID)
+		}
+		requestAppendRule = true //default assumption here is that rule is to be appended
+		flowCookieModify = true  //and that the the flow data base is to be updated
+		if delayedCookie != 0 {  //it was suspended
+			flowCookieModify, requestAppendRule = oFsm.reviseFlowConstellation(ctx, delayedCookie, loRuleParams)
+		}
+	}
+	kvStoreWrite := false //default setting is to not write to kvStore immediately - will be done on FSM execution finally
+	if requestAppendRule {
+		oFsm.mutexFlowParams.Lock()
+		if oFsm.NumUniFlows < cMaxAllowedFlows {
+			loFlowParams := cmn.UniVlanFlowParams{VlanRuleParams: loRuleParams}
+			loFlowParams.CookieSlice = make([]uint64, 0)
+			loFlowParams.CookieSlice = append(loFlowParams.CookieSlice, aCookieSlice...)
+			if aMeter != nil {
+				loFlowParams.Meter = aMeter
+			}
+			oFsm.uniVlanFlowParamsSlice = append(oFsm.uniVlanFlowParamsSlice, loFlowParams)
+			logger.Debugw(ctx, "UniVlanConfigFsm flow add", log.Fields{
+				"Cookies":  oFsm.uniVlanFlowParamsSlice[oFsm.NumUniFlows].CookieSlice,
+				"MatchVid": strconv.FormatInt(int64(loRuleParams.MatchVid), 16),
+				"SetVid":   strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+				"SetPcp":   loRuleParams.SetPcp, "numberofFlows": oFsm.NumUniFlows + 1,
+				"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID})
+
+			if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+				oFsm.reconcileVlanFilterList(ctx, uint16(loRuleParams.SetVid))
+			}
+			oFsm.NumUniFlows++
+			pConfigVlanStateBaseFsm := oFsm.PAdaptFsm.PFsm
+
+			if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+				logger.Debugw(ctx, "reconciling - skip omci-config of additional vlan rule",
+					log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+				//attention: take care to release the mutexFlowParams when calling the FSM directly -
+				//  synchronous FSM 'event/state' functions may rely on this mutex
+				oFsm.mutexFlowParams.Unlock()
+				if pConfigVlanStateBaseFsm.Is(VlanStConfigDone) {
+					if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvSkipOmciConfig); fsmErr != nil {
+						logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+							log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+					}
+				}
+				return nil
+			}
+			// note: theoretical it would be possible to clear the same rule from the remove slice
+			//  (for entries that have not yet been started with removal)
+			//  but that is getting quite complicated - maybe a future optimization in case it should prove reasonable
+			// anyway the precondition here is that the FSM checks for rules to delete first and adds new rules afterwards
+
+			if pConfigVlanStateBaseFsm.Is(VlanStConfigDone) {
+				//have to re-trigger the FSM to proceed with outstanding incremental flow configuration
+				if oFsm.ConfiguredUniFlow == 0 {
+					// this is a restart with a complete new flow, we can re-use the initial flow config control
+					// including the check, if the related techProfile is (still) available (probably also removed in between)
+					//attention: take care to release the mutexFlowParams when calling the FSM directly -
+					//  synchronous FSM 'event/state' functions may rely on this mutex
+					oFsm.mutexFlowParams.Unlock()
+					if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvRenew); fsmErr != nil {
+						logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+							log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+					}
+				} else {
+					//some further flows are to be configured
+					//store the actual rule that shall be worked upon in the following transient states
+					if len(oFsm.uniVlanFlowParamsSlice) < int(oFsm.ConfiguredUniFlow) {
+						//check introduced after having observed some panic here
+						logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm - inconsistent counter",
+							log.Fields{"ConfiguredUniFlow": oFsm.ConfiguredUniFlow,
+								"sliceLen": len(oFsm.uniVlanFlowParamsSlice), "device-id": oFsm.deviceID})
+						oFsm.mutexFlowParams.Unlock()
+						return fmt.Errorf("abort UniVlanConfigFsm on add due to internal counter mismatch %s", oFsm.deviceID)
+					}
+					oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[oFsm.ConfiguredUniFlow].VlanRuleParams
+					oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[oFsm.ConfiguredUniFlow].Meter
+					//tpId of the next rule to be configured
+					tpID := oFsm.actualUniVlanConfigRule.TpID
+					oFsm.TpIDWaitingFor = tpID
+					loSetVlan := oFsm.actualUniVlanConfigRule.SetVid
+					//attention: take care to release the mutexFlowParams when calling the FSM directly -
+					//  synchronous FSM 'event/state' functions may rely on this mutex
+					//  but it must be released already before calling getTechProfileDone() as it may already be locked
+					//  by the techProfile processing call to VlanFsm.IsFlowRemovePending() (see VOL-4207)
+					oFsm.mutexFlowParams.Unlock()
+					loTechProfDone := oFsm.pUniTechProf.getTechProfileDone(ctx, oFsm.pOnuUniPort.UniID, tpID)
+					logger.Debugw(ctx, "UniVlanConfigFsm - incremental config request (on setConfig)", log.Fields{
+						"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+						"set-Vlan": loSetVlan, "tp-id": tpID, "ProfDone": loTechProfDone})
+
+					var fsmErr error
+					if loTechProfDone {
+						// let the vlan processing continue with next rule
+						fsmErr = pConfigVlanStateBaseFsm.Event(VlanEvIncrFlowConfig)
+					} else {
+						// set to waiting for Techprofile
+						fsmErr = pConfigVlanStateBaseFsm.Event(VlanEvWaitTPIncr)
+					}
+					if fsmErr != nil {
+						logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+							log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+					}
+				}
+			} else {
+				// if not in the appropriate state a new entry will be automatically considered later
+				//   when the configDone state is reached
+				oFsm.mutexFlowParams.Unlock()
+			}
+		} else {
+			logger.Errorw(ctx, "UniVlanConfigFsm flow limit exceeded", log.Fields{
+				"device-id": oFsm.deviceID, "flow-number": oFsm.NumUniFlows})
+			oFsm.mutexFlowParams.Unlock()
+			return fmt.Errorf(" UniVlanConfigFsm flow limit exceeded %s", oFsm.deviceID)
+		}
+	} else {
+		// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+		kvStoreWrite = true // ensure actual data write to kvStore immediately (no FSM activity)
+		oFsm.mutexFlowParams.RLock()
+		if oFsm.NumUniFlows == oFsm.ConfiguredUniFlow {
+			//all requested rules really have been configured
+			// state transition notification is checked in deviceHandler
+			oFsm.mutexFlowParams.RUnlock()
+			if oFsm.pDeviceHandler != nil {
+				//also the related TechProfile was already configured
+				logger.Debugw(ctx, "UniVlanConfigFsm rule already set - send immediate add-success event for reason update", log.Fields{
+					"device-id": oFsm.deviceID})
+				// success indication without the need to write to kvStore (done already below with updated data from StorePersUniFlowConfig())
+				go oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetAddNoKvStore))
+			}
+		} else {
+			//  avoid device reason update as the rule config connected to this flow may still be in progress
+			//  and the device reason should only be updated on success of rule config
+			logger.Debugw(ctx, "UniVlanConfigFsm rule already set but configuration ongoing, suppress early add-success event for reason update",
+				log.Fields{"device-id": oFsm.deviceID,
+					"NumberofRules": oFsm.NumUniFlows, "Configured rules": oFsm.ConfiguredUniFlow})
+			oFsm.mutexFlowParams.RUnlock()
+		}
+	}
+
+	if flowCookieModify { // some change was done to the flow entries
+		//permanently store flow config for reconcile case
+		oFsm.mutexFlowParams.RLock()
+		if err := oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID,
+			&oFsm.uniVlanFlowParamsSlice, kvStoreWrite); err != nil {
+			oFsm.mutexFlowParams.RUnlock()
+			logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
+			return err
+		}
+		oFsm.mutexFlowParams.RUnlock()
+	}
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) suspendAddRule(ctx context.Context, apRemoveFlowParams *uniRemoveVlanFlowParams) error {
+	oFsm.mutexFlowParams.Lock()
+	deleteChannel := apRemoveFlowParams.removeChannel
+	apRemoveFlowParams.isSuspendedOnAdd = true
+	oFsm.mutexFlowParams.Unlock()
+
+	// isSuspendedOnAdd is not reset here-after as the assumption is, that after
+	select {
+	case success := <-deleteChannel:
+		//no need to reset isSuspendedOnAdd as in this case the removeElement will be deleted completely
+		if success {
+			logger.Infow(ctx, "resume adding this rule after having completed deletion", log.Fields{
+				"device-id": oFsm.deviceID})
+			return nil
+		}
+		return fmt.Errorf("suspend aborted, also aborting add-activity: %s", oFsm.deviceID)
+	case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second):
+		oFsm.mutexFlowParams.Lock()
+		if apRemoveFlowParams != nil {
+			apRemoveFlowParams.isSuspendedOnAdd = false
+		}
+		oFsm.mutexFlowParams.Unlock()
+		logger.Errorw(ctx, "timeout waiting for deletion of rule, also aborting add-activity", log.Fields{
+			"device-id": oFsm.deviceID})
+		return fmt.Errorf("suspend aborted on timeout, also aborting add-activity: %s", oFsm.deviceID)
+	}
+}
+
+// VOL-3828 flow config sequence workaround ###########  start ##########
+func (oFsm *UniVlanConfigFsm) delayNewRuleForCookie(ctx context.Context, aCookieSlice []uint64) uint64 {
+	//assumes mutexFlowParams.Lock() protection from caller!
+	if oFsm.delayNewRuleCookie == 0 && len(aCookieSlice) == 1 {
+		// if not already waiting, limitation for this workaround is to just have one overlapping cookie/rule
+		// suspend check is done only if there is only one cookie in the request
+		//  background: more elements only expected in reconcile use case, where no conflicting sequence is to be expected
+		newCookie := aCookieSlice[0]
+		for _, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+			for _, cookie := range storedUniFlowParams.CookieSlice {
+				if cookie == newCookie {
+					logger.Debugw(ctx, "UniVlanConfigFsm flow setting - new cookie still exists for some rule", log.Fields{
+						"device-id": oFsm.deviceID, "cookie": cookie, "exists with SetVlan": storedUniFlowParams.VlanRuleParams.SetVid})
+					oFsm.delayNewRuleCookie = newCookie
+					return newCookie //found new cookie in some existing rule
+				}
+			} // for all stored cookies of the actual inspected rule
+		} //for all rules
+	}
+	return 0 //no delay requested
+}
+func (oFsm *UniVlanConfigFsm) suspendNewRule(ctx context.Context) bool {
+	oFsm.mutexFlowParams.RLock()
+	logger.Infow(ctx, "Need to suspend adding this rule as long as the cookie is still connected to some other rule", log.Fields{
+		"device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+	oFsm.mutexFlowParams.RUnlock()
+	cookieDeleted := true //default assumption also for timeout (just try to continue as if removed)
+	select {
+	case cookieDeleted = <-oFsm.chCookieDeleted:
+		logger.Infow(ctx, "resume adding this rule after having deleted cookie in some other rule or abort", log.Fields{
+			"device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie, "deleted": cookieDeleted})
+	case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second):
+		logger.Errorw(ctx, "timeout waiting for deletion of cookie in some other rule, just try to continue", log.Fields{
+			"device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+	}
+	oFsm.mutexFlowParams.Lock()
+	oFsm.delayNewRuleCookie = 0
+	oFsm.mutexFlowParams.Unlock()
+	return cookieDeleted
+}
+func (oFsm *UniVlanConfigFsm) suspendIfRequiredNewRule(ctx context.Context, aCookieSlice []uint64) (uint64, bool) {
+	oFsm.mutexFlowParams.Lock()
+	delayedCookie := oFsm.delayNewRuleForCookie(ctx, aCookieSlice)
+	oFsm.mutexFlowParams.Unlock()
+
+	deleteSuccess := true
+	if delayedCookie != 0 {
+		deleteSuccess = oFsm.suspendNewRule(ctx)
+	}
+	return delayedCookie, deleteSuccess
+}
+
+//returns flowModified, RuleAppendRequest
+func (oFsm *UniVlanConfigFsm) reviseFlowConstellation(ctx context.Context, aCookie uint64, aUniVlanRuleParams cmn.UniVlanRuleParams) (bool, bool) {
+	flowEntryMatch := false
+	oFsm.mutexFlowParams.Lock()
+	defer oFsm.mutexFlowParams.Unlock()
+	for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+		if storedUniFlowParams.VlanRuleParams == aUniVlanRuleParams {
+			flowEntryMatch = true
+			logger.Debugw(ctx, "UniVlanConfigFsm flow revise - rule already exists", log.Fields{
+				"device-id": oFsm.deviceID})
+			cookieMatch := false
+			for _, cookie := range storedUniFlowParams.CookieSlice {
+				if cookie == aCookie {
+					logger.Debugw(ctx, "UniVlanConfigFsm flow revise - and cookie already exists", log.Fields{
+						"device-id": oFsm.deviceID, "cookie": cookie})
+					cookieMatch = true
+					break //found new cookie - no further search for this requested cookie
+				}
+			}
+			if !cookieMatch {
+				logger.Debugw(ctx, "UniVlanConfigFsm flow revise -adding new cookie", log.Fields{
+					"device-id": oFsm.deviceID, "cookie": aCookie})
+				//as range works with copies of the slice we have to write to the original slice!!
+				oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(oFsm.uniVlanFlowParamsSlice[flow].CookieSlice,
+					aCookie)
+				return true, false //flowModified, NoRuleAppend
+			}
+			break // found rule - no further rule search
+		}
+	}
+	if !flowEntryMatch { //it is a new rule
+		return true, true //flowModified, RuleAppend
+	}
+	return false, false //flowNotModified, NoRuleAppend
+}
+
+// VOL-3828 flow config sequence workaround ###########  end ##########
+
+//RemoveUniFlowParams verifies on existence of flow cookie,
+// if found removes cookie from flow cookie list and if this is empty
+// initiates removal of the flow related configuration from the ONU (via OMCI)
+func (oFsm *UniVlanConfigFsm) RemoveUniFlowParams(ctx context.Context, aCookie uint64) error {
+	var deletedCookie uint64
+	flowCookieMatch := false
+	//mutex protection is required for possible concurrent access to FSM members
+	oFsm.mutexFlowParams.Lock()
+	defer oFsm.mutexFlowParams.Unlock()
+remove_loop:
+	for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+		for i, cookie := range storedUniFlowParams.CookieSlice {
+			if cookie == aCookie {
+				logger.Debugw(ctx, "UniVlanConfigFsm flow removal - cookie found", log.Fields{
+					"device-id": oFsm.deviceID, "cookie": cookie})
+				deletedCookie = aCookie
+				//remove the cookie from the cookie slice and verify it is getting empty
+				if len(storedUniFlowParams.CookieSlice) == 1 {
+					// had to shift content to function due to sca complexity
+					flowCookieMatch = oFsm.removeRuleComplete(ctx, storedUniFlowParams, aCookie)
+					//persistencyData write is now part of removeRuleComplete() (on success)
+				} else {
+					flowCookieMatch = true
+					//cut off the requested cookie by slicing out this element
+					oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = append(
+						oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[:i],
+						oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[i+1:]...)
+					// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+					// state transition notification is checked in deviceHandler
+					if oFsm.pDeviceHandler != nil {
+						// success indication without the need to write to kvStore (done already below with updated data from StorePersUniFlowConfig())
+						go oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetRemoveNoKvStore))
+					}
+					logger.Debugw(ctx, "UniVlanConfigFsm flow removal - rule persists with still valid cookies", log.Fields{
+						"device-id": oFsm.deviceID, "cookies": oFsm.uniVlanFlowParamsSlice[flow].CookieSlice})
+					if deletedCookie == oFsm.delayNewRuleCookie {
+						//the delayedNewCookie is the one that is currently deleted, but the rule still exist with other cookies
+						//as long as there are further cookies for this rule indicate there is still some cookie to be deleted
+						//simply use the first one
+						oFsm.delayNewRuleCookie = oFsm.uniVlanFlowParamsSlice[flow].CookieSlice[0]
+						logger.Debugw(ctx, "UniVlanConfigFsm remaining cookie awaited for deletion before new rule add", log.Fields{
+							"device-id": oFsm.deviceID, "cookie": oFsm.delayNewRuleCookie})
+					}
+					//permanently store the modified flow config for reconcile case and immediately write to KvStore
+					if oFsm.pDeviceHandler != nil {
+						if err := oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID,
+							&oFsm.uniVlanFlowParamsSlice, true); err != nil {
+							logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
+							return err
+						}
+					}
+				}
+				break remove_loop //found the cookie - no further search for this requested cookie
+			}
+		}
+	} //search all flows
+	if !flowCookieMatch { //some cookie remove-request for a cookie that does not exist in the FSM data
+		logger.Warnw(ctx, "UniVlanConfigFsm flow removal - remove-cookie not found", log.Fields{
+			"device-id": oFsm.deviceID, "remove-cookie": aCookie})
+		// but accept the request with success as no such cookie (flow) does exist
+		// no activity within the FSM for OMCI processing, the deviceReason may be updated immediately
+		// state transition notification is checked in deviceHandler
+		if oFsm.pDeviceHandler != nil {
+			// success indication without the need to write to kvStore (no change)
+			go oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent(oFsm.requestEvent+cDeviceEventOffsetRemoveNoKvStore))
+		}
+		return nil
+	} //unknown cookie
+
+	return nil
+}
+
+// removeRuleComplete initiates the complete removal of a VLAN rule (from single cookie element)
+// requires mutexFlowParams to be locked at call
+func (oFsm *UniVlanConfigFsm) removeRuleComplete(ctx context.Context,
+	aUniFlowParams cmn.UniVlanFlowParams, aCookie uint64) bool {
+	pConfigVlanStateBaseFsm := oFsm.PAdaptFsm.PFsm
+	var cancelPendingConfig bool = false
+	var loRemoveParams uniRemoveVlanFlowParams = uniRemoveVlanFlowParams{}
+	logger.Debugw(ctx, "UniVlanConfigFsm flow removal - full flow removal", log.Fields{
+		"device-id": oFsm.deviceID})
+	//rwCore flow recovery may be the reason for this delete, in which case the flowToBeDeleted may be the same
+	//  as the one still waiting in the FSM as toAdd but waiting for TechProfileConfig
+	//  so we have to check if we have to abort the outstanding AddRequest and regard the current DelRequest as done
+	//  if the Fsm is in some other transient (config) state, we will reach the DelRequest later and correctly process it then
+	if pConfigVlanStateBaseFsm.Is(VlanStWaitingTechProf) {
+		logger.Debugw(ctx, "UniVlanConfigFsm was waiting for TechProf config with add-request, just aborting the outstanding add",
+			log.Fields{"device-id": oFsm.deviceID})
+		cancelPendingConfig = true
+	} else {
+		//create a new element for the removeVlanFlow slice
+		loRemoveParams = uniRemoveVlanFlowParams{
+			vlanRuleParams: aUniFlowParams.VlanRuleParams,
+			cookie:         aCookie,
+		}
+		loRemoveParams.removeChannel = make(chan bool)
+		oFsm.uniRemoveFlowsSlice = append(oFsm.uniRemoveFlowsSlice, loRemoveParams)
+	}
+
+	usedTpID := aUniFlowParams.VlanRuleParams.TpID
+	if len(oFsm.uniVlanFlowParamsSlice) <= 1 {
+		//at this point it is evident that no flow anymore will refer to a still possibly active Techprofile
+		//request that this profile gets deleted before a new flow add is allowed (except for some aborted add)
+		if !cancelPendingConfig {
+			// ensure mutexFlowParams not locked before calling some TPProcessing activity (that might already be pending on it)
+			oFsm.mutexFlowParams.Unlock()
+			logger.Debugw(ctx, "UniVlanConfigFsm flow removal requested - set TechProfile to-delete", log.Fields{
+				"device-id": oFsm.deviceID})
+			if oFsm.pUniTechProf != nil {
+				oFsm.pUniTechProf.SetProfileToDelete(oFsm.pOnuUniPort.UniID, usedTpID, true)
+			}
+			oFsm.mutexFlowParams.Lock()
+		}
+	} else {
+		if !cancelPendingConfig {
+			oFsm.updateTechProfileToDelete(ctx, usedTpID)
+		}
+	}
+	//trigger the FSM to remove the relevant rule
+	if cancelPendingConfig {
+		//as the uniFlow parameters are already stored (for add) but no explicit removal is done anymore
+		//  the paramSlice has to be updated with rule-removal, which also then updates NumUniFlows
+		//call from 'non-configured' state of the rules
+		if err := oFsm.removeFlowFromParamsSlice(ctx, aCookie, false); err != nil {
+			//something quite inconsistent detected, perhaps just try to recover with FSM reset
+			oFsm.mutexFlowParams.Unlock()
+			if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvReset); fsmErr != nil {
+				logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+					log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+			}
+			return false //data base update could not be done, return like cookie not found
+		}
+
+		oFsm.requestEventOffset = uint8(cDeviceEventOffsetRemoveWithKvStore) //offset for last flow-remove activity (with kvStore request)
+		//attention: take care to release and re-take the mutexFlowParams when calling the FSM directly -
+		//  synchronous FSM 'event/state' functions may rely on this mutex
+		oFsm.mutexFlowParams.Unlock()
+		if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvCancelOutstandingConfig); fsmErr != nil {
+			logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+				log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+		}
+		oFsm.mutexFlowParams.Lock()
+		return true
+	}
+	if pConfigVlanStateBaseFsm.Is(VlanStConfigDone) {
+		logger.Debugw(ctx, "UniVlanConfigFsm rule removal request", log.Fields{
+			"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+			"tp-id":    loRemoveParams.vlanRuleParams.TpID,
+			"set-Vlan": loRemoveParams.vlanRuleParams.SetVid})
+		//have to re-trigger the FSM to proceed with outstanding incremental flow configuration
+		//attention: take care to release and re-take the mutexFlowParams when calling the FSM directly -
+		//  synchronous FSM 'event/state' functions may rely on this mutex
+		oFsm.mutexFlowParams.Unlock()
+		if fsmErr := pConfigVlanStateBaseFsm.Event(VlanEvRemFlowConfig); fsmErr != nil {
+			logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm!",
+				log.Fields{"fsmState": pConfigVlanStateBaseFsm.Current(), "error": fsmErr, "device-id": oFsm.deviceID})
+		}
+		oFsm.mutexFlowParams.Lock()
+	} // if not in the appropriate state a new entry will be automatically considered later
+	//   when the configDone state is reached
+	return true
+}
+
+//removeFlowFromParamsSlice removes a flow from stored  uniVlanFlowParamsSlice based on the cookie
+//  it assumes that adding cookies for this flow (including the actual one to delete) was prevented
+//  from the start of the deletion request to avoid to much interference
+//  so when called, there can only be one cookie active for this flow
+// requires mutexFlowParams to be locked at call
+func (oFsm *UniVlanConfigFsm) removeFlowFromParamsSlice(ctx context.Context, aCookie uint64, aWasConfigured bool) error {
+	logger.Debugw(ctx, "UniVlanConfigFsm flow removal from ParamsSlice", log.Fields{
+		"device-id": oFsm.deviceID, "cookie": aCookie})
+	cookieFound := false
+removeFromSlice_loop:
+	for flow, storedUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+		// if UniFlowParams exists, cookieSlice should always have at least one element
+		cookieSliceLen := len(storedUniFlowParams.CookieSlice)
+		if cookieSliceLen == 1 {
+			if storedUniFlowParams.CookieSlice[0] == aCookie {
+				cookieFound = true
+			}
+		} else if cookieSliceLen == 0 {
+			errStr := "UniVlanConfigFsm unexpected cookie slice length 0  - removal in uniVlanFlowParamsSlice aborted"
+			logger.Errorw(ctx, errStr, log.Fields{"device-id": oFsm.deviceID})
+			return errors.New(errStr)
+		} else {
+			errStr := "UniVlanConfigFsm flow removal unexpected cookie slice length, but rule removal continued"
+			logger.Errorw(ctx, errStr, log.Fields{
+				"cookieSliceLen": len(oFsm.uniVlanFlowParamsSlice), "device-id": oFsm.deviceID})
+			for _, cookie := range storedUniFlowParams.CookieSlice {
+				if cookie == aCookie {
+					cookieFound = true
+					break
+				}
+			}
+		}
+		if cookieFound {
+			logger.Debugw(ctx, "UniVlanConfigFsm flow removal from ParamsSlice - cookie found", log.Fields{
+				"device-id": oFsm.deviceID, "cookie": aCookie})
+			//remove the actual element from the addVlanFlow slice
+			// oFsm.uniVlanFlowParamsSlice[flow].CookieSlice = nil //automatically done by garbage collector
+			if len(oFsm.uniVlanFlowParamsSlice) <= 1 {
+				oFsm.NumUniFlows = 0              //no more flows
+				oFsm.ConfiguredUniFlow = 0        //no more flows configured
+				oFsm.uniVlanFlowParamsSlice = nil //reset the slice
+				//at this point it is evident that no flow anymore refers to a still possibly active Techprofile
+				//request that this profile gets deleted before a new flow add is allowed
+				logger.Debugw(ctx, "UniVlanConfigFsm flow removal from ParamsSlice - no more flows", log.Fields{
+					"device-id": oFsm.deviceID})
+			} else {
+				oFsm.NumUniFlows--
+				if aWasConfigured && oFsm.ConfiguredUniFlow > 0 {
+					oFsm.ConfiguredUniFlow--
+				}
+				//cut off the requested flow by slicing out this element
+				oFsm.uniVlanFlowParamsSlice = append(
+					oFsm.uniVlanFlowParamsSlice[:flow], oFsm.uniVlanFlowParamsSlice[flow+1:]...)
+				logger.Debugw(ctx, "UniVlanConfigFsm flow removal - specific flow removed from data", log.Fields{
+					"device-id": oFsm.deviceID})
+			}
+			break removeFromSlice_loop //found the cookie - no further search for this requested cookie
+		}
+	} //search all flows
+	if !cookieFound {
+		errStr := "UniVlanConfigFsm cookie for removal not found, internal counter not updated"
+		logger.Errorw(ctx, errStr, log.Fields{"device-id": oFsm.deviceID})
+		return errors.New(errStr)
+	}
+	//if the cookie was found and removed from uniVlanFlowParamsSlice above now write the modified persistency data
+	//  KVStore update will be done after reaching the requested FSM end state (not immediately here)
+	if err := oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID,
+		&oFsm.uniVlanFlowParamsSlice, false); err != nil {
+		logger.Errorw(ctx, err.Error(), log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	return nil
+}
+
+// requires mutexFlowParams to be locked at call
+func (oFsm *UniVlanConfigFsm) updateTechProfileToDelete(ctx context.Context, usedTpID uint8) {
+	//here we have to check, if there are still other flows referencing to the actual ProfileId
+	//  before we can request that this profile gets deleted before a new flow add is allowed
+	tpIDInOtherFlows := false
+	for _, tpUniFlowParams := range oFsm.uniVlanFlowParamsSlice {
+		if tpUniFlowParams.VlanRuleParams.TpID == usedTpID {
+			tpIDInOtherFlows = true
+			break // search loop can be left
+		}
+	}
+	if tpIDInOtherFlows {
+		logger.Debugw(ctx, "UniVlanConfigFsm tp-id used in deleted flow is still used in other flows", log.Fields{
+			"device-id": oFsm.deviceID, "tp-id": usedTpID})
+	} else {
+		logger.Debugw(ctx, "UniVlanConfigFsm tp-id used in deleted flow is not used anymore - set TechProfile to-delete", log.Fields{
+			"device-id": oFsm.deviceID, "tp-id": usedTpID})
+		// ensure mutexFlowParams not locked before calling some TPProcessing activity (that might already be pending on it)
+		oFsm.mutexFlowParams.Unlock()
+		if oFsm.pUniTechProf != nil {
+			//request that this profile gets deleted before a new flow add is allowed
+			oFsm.pUniTechProf.SetProfileToDelete(oFsm.pOnuUniPort.UniID, usedTpID, true)
+		}
+		oFsm.mutexFlowParams.Lock()
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterPreparing(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniVlanConfigFsm preparing", log.Fields{"device-id": oFsm.deviceID})
+
+	// this FSM is not intended for re-start, needs always new creation for a new run
+	// (self-destroying - compare enterDisabled())
+	oFsm.omciMIdsResponseReceived = make(chan bool)
+	oFsm.chCookieDeleted = make(chan bool)
+	// start go routine for processing of LockState messages
+	go oFsm.processOmciVlanMessages(ctx)
+	//let the state machine run forward from here directly
+	pConfigVlanStateAFsm := oFsm.PAdaptFsm
+	if pConfigVlanStateAFsm != nil {
+		if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+			logger.Debugw(ctx, "reconciling - skip omci-config of vlan rule",
+				log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+			// Can't call FSM Event directly, decoupling it
+			go func(a_pAFsm *cmn.AdapterFsm) {
+				_ = a_pAFsm.PFsm.Event(VlanEvSkipOmciConfig)
+			}(pConfigVlanStateAFsm)
+			return
+		}
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *cmn.AdapterFsm) {
+			_ = a_pAFsm.PFsm.Event(VlanEvPrepareDone)
+		}(pConfigVlanStateAFsm)
+		return
+	}
+	logger.Errorw(ctx, "UniVlanConfigFsm abort: invalid FSM pointer", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+	//should never happen, else: recovery would be needed from outside the FSM
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigStarting(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniVlanConfigFsm start vlan configuration", log.Fields{"device-id": oFsm.deviceID})
+	pConfigVlanStateAFsm := oFsm.PAdaptFsm
+	if pConfigVlanStateAFsm != nil {
+		oFsm.mutexFlowParams.Lock()
+		//possibly the entry is not valid anymore based on intermediate delete requests
+		//just a basic protection ...
+		if len(oFsm.uniVlanFlowParamsSlice) == 0 {
+			oFsm.mutexFlowParams.Unlock()
+			logger.Debugw(ctx, "UniVlanConfigFsm start: no rule entry anymore available", log.Fields{
+				"device-id": oFsm.deviceID})
+			// Can't call FSM Event directly, decoupling it
+			go func(a_pAFsm *cmn.AdapterFsm) {
+				_ = a_pAFsm.PFsm.Event(VlanEvReset)
+			}(pConfigVlanStateAFsm)
+			return
+		}
+		//access to uniVlanFlowParamsSlice is done on first element only here per definition
+		//store the actual rule that shall be worked upon in the following transient states
+		oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[0].VlanRuleParams
+		oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[0].Meter
+		tpID := oFsm.actualUniVlanConfigRule.TpID
+		oFsm.TpIDWaitingFor = tpID
+		//cmp also usage in EVTOCDE create in omci_cc
+		oFsm.evtocdID = cmn.MacBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.MacBpNo)
+		loSetVlan := oFsm.actualUniVlanConfigRule.SetVid
+		//attention: take care to release the mutexFlowParams when calling the FSM directly -
+		//  synchronous FSM 'event/state' functions may rely on this mutex
+		//  but it must be released already before calling getTechProfileDone() as it may already be locked
+		//  by the techProfile processing call to VlanFsm.IsFlowRemovePending() (see VOL-4207)
+		oFsm.mutexFlowParams.Unlock()
+		loTechProfDone := oFsm.pUniTechProf.getTechProfileDone(ctx, oFsm.pOnuUniPort.UniID, uint8(tpID))
+		logger.Debugw(ctx, "UniVlanConfigFsm - start with first rule", log.Fields{
+			"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+			"set-Vlan": loSetVlan, "tp-id": tpID, "ProfDone": loTechProfDone})
+
+		// Can't call FSM Event directly, decoupling it
+		go func(aPAFsm *cmn.AdapterFsm, aTechProfDone bool) {
+			if aPAFsm != nil && aPAFsm.PFsm != nil {
+				if aTechProfDone {
+					// let the vlan processing begin
+					_ = aPAFsm.PFsm.Event(VlanEvStartConfig)
+				} else {
+					// set to waiting for Techprofile
+					_ = aPAFsm.PFsm.Event(VlanEvWaitTechProf)
+				}
+			}
+		}(pConfigVlanStateAFsm, loTechProfDone)
+	} else {
+		logger.Errorw(ctx, "UniVlanConfigFsm abort: invalid FSM pointer", log.Fields{
+			"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+		//should never happen, else: recovery would be needed from outside the FSM
+		return
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigVtfd(ctx context.Context, e *fsm.Event) {
+	//mutex protection is required for possible concurrent access to FSM members
+	oFsm.mutexFlowParams.Lock()
+	oFsm.TpIDWaitingFor = 0 //reset indication to avoid misinterpretation
+	if oFsm.actualUniVlanConfigRule.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		// meaning transparent setup - no specific VTFD setting required
+		oFsm.mutexFlowParams.Unlock()
+		logger.Debugw(ctx, "UniVlanConfigFsm: no VTFD config required", log.Fields{
+			"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+		// let the FSM proceed ... (from within this state all internal pointers may be expected to be correct)
+		pConfigVlanStateAFsm := oFsm.PAdaptFsm
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *cmn.AdapterFsm) {
+			_ = a_pAFsm.PFsm.Event(VlanEvRxConfigVtfd)
+		}(pConfigVlanStateAFsm)
+	} else {
+		// This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+		// this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+		vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.actualUniVlanConfigRule.TpID))
+		logger.Debugw(ctx, "UniVlanConfigFsm create VTFD", log.Fields{
+			"EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+			"in state":  e.FSM.Current(), "device-id": oFsm.deviceID,
+			"macBpNo": oFsm.pOnuUniPort.MacBpNo, "TpID": oFsm.actualUniVlanConfigRule.TpID})
+		// setVid is assumed to be masked already by the caller to 12 bit
+		oFsm.vlanFilterList[0] = uint16(oFsm.actualUniVlanConfigRule.SetVid)
+		oFsm.mutexFlowParams.Unlock()
+		vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+		vtfdFilterList[0] = oFsm.vlanFilterList[0]
+		oFsm.numVlanFilterEntries = 1
+		meParams := me.ParamData{
+			EntityID: vtfdID,
+			Attributes: me.AttributeValueMap{
+				"VlanFilterList":   vtfdFilterList, //omci lib wants a slice for serialization
+				"ForwardOperation": uint8(0x10),    //VID investigation
+				"NumberOfEntries":  oFsm.numVlanFilterEntries,
+			},
+		}
+		logger.Debugw(ctx, "UniVlanConfigFsm sendcreate VTFD", log.Fields{
+			"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+		oFsm.mutexPLastTxMeInstance.Lock()
+		meInstance, err := oFsm.pOmciCC.SendCreateVtfdVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+			oFsm.PAdaptFsm.CommChan, meParams)
+		if err != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			logger.Errorw(ctx, "VTFD create failed, aborting UniVlanConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID})
+			pConfigVlanStateAFsm := oFsm.PAdaptFsm
+			if pConfigVlanStateAFsm != nil {
+				go func(a_pAFsm *cmn.AdapterFsm) {
+					_ = a_pAFsm.PFsm.Event(VlanEvReset)
+				}(pConfigVlanStateAFsm)
+			}
+			return
+		}
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		//TODO!!: refactoring improvement requested, here as an example for [VOL-3457]:
+		//  send shall return (dual format) error code that can be used here for immediate error treatment
+		//  (relevant to all used sendXX() methods in this (and other) FSM's)
+		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigEvtocd(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniVlanConfigFsm - start config EVTOCD loop", log.Fields{
+		"device-id": oFsm.deviceID})
+	oFsm.requestEventOffset = uint8(cDeviceEventOffsetAddWithKvStore) //0 offset for last flow-add activity
+	go func() {
+		//using the first element in the slice because it's the first flow per definition here
+		errEvto := oFsm.performConfigEvtocdEntries(ctx, 0)
+		//This is correct passing scenario
+		if errEvto == nil {
+			oFsm.mutexFlowParams.RLock()
+			tpID := oFsm.actualUniVlanConfigRule.TpID
+			vlanID := oFsm.actualUniVlanConfigRule.SetVid
+			configuredUniFlows := oFsm.ConfiguredUniFlow
+			// ensure mutexFlowParams not locked before calling some TPProcessing activity (that might already be pending on it)
+			oFsm.mutexFlowParams.RUnlock()
+			for _, gemPort := range oFsm.pUniTechProf.getMulticastGemPorts(ctx, oFsm.pOnuUniPort.UniID, uint8(tpID)) {
+				logger.Infow(ctx, "Setting multicast MEs, with first flow", log.Fields{"deviceID": oFsm.deviceID,
+					"techProfile": tpID, "gemPort": gemPort, "vlanID": vlanID, "ConfiguredUniFlow": configuredUniFlows})
+				errCreateAllMulticastME := oFsm.performSettingMulticastME(ctx, tpID, gemPort,
+					vlanID)
+				if errCreateAllMulticastME != nil {
+					logger.Errorw(ctx, "Multicast ME create failed, aborting AniConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+				}
+			}
+			//If this first flow contains a meter, then create TD for related gems.
+			if oFsm.actualUniVlanConfigMeter != nil {
+				logger.Debugw(ctx, "Creating Traffic Descriptor", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter})
+				for _, gemPort := range oFsm.pUniTechProf.getBidirectionalGemPortIDsForTP(ctx, oFsm.pOnuUniPort.UniID, tpID) {
+					logger.Debugw(ctx, "Creating Traffic Descriptor for gem", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter, "gem": gemPort})
+					errCreateTrafficDescriptor := oFsm.createTrafficDescriptor(ctx, oFsm.actualUniVlanConfigMeter, tpID,
+						oFsm.pOnuUniPort.UniID, gemPort)
+					if errCreateTrafficDescriptor != nil {
+						logger.Errorw(ctx, "Create Traffic Descriptor create failed, aborting Ani Config FSM!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					}
+				}
+			}
+
+			//TODO Possibly insert new state for multicast --> possibly another jira/later time.
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvRxConfigEvtocd)
+		}
+	}()
+}
+
+func (oFsm *UniVlanConfigFsm) enterVlanConfigDone(ctx context.Context, e *fsm.Event) {
+
+	oFsm.mutexFlowParams.Lock()
+
+	logger.Infow(ctx, "UniVlanConfigFsm config done - checking on more flows", log.Fields{
+		"device-id":         oFsm.deviceID,
+		"overall-uni-rules": oFsm.NumUniFlows, "configured-uni-rules": oFsm.ConfiguredUniFlow})
+	pConfigVlanStateAFsm := oFsm.PAdaptFsm
+	if pConfigVlanStateAFsm == nil {
+		oFsm.mutexFlowParams.Unlock()
+		logger.Errorw(ctx, "UniVlanConfigFsm abort: invalid FSM pointer", log.Fields{
+			"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+		//should never happen, else: recovery would be needed from outside the FSM
+		return
+	}
+	pConfigVlanStateBaseFsm := pConfigVlanStateAFsm.PFsm
+	if len(oFsm.uniRemoveFlowsSlice) > 0 {
+		//some further flows are to be removed, removal always starts with the first element
+		logger.Debugw(ctx, "UniVlanConfigFsm rule removal from ConfigDone", log.Fields{
+			"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+			"tp-id":    oFsm.uniRemoveFlowsSlice[0].vlanRuleParams.TpID,
+			"set-Vlan": oFsm.uniRemoveFlowsSlice[0].vlanRuleParams.SetVid})
+		oFsm.mutexFlowParams.Unlock()
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pBaseFsm *fsm.FSM) {
+			_ = a_pBaseFsm.Event(VlanEvRemFlowConfig)
+		}(pConfigVlanStateBaseFsm)
+		return
+	}
+	if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+		oFsm.ConfiguredUniFlow = oFsm.NumUniFlows
+		if oFsm.lastFlowToReconcile {
+			logger.Debugw(ctx, "reconciling - flow processing finished", log.Fields{"device-id": oFsm.deviceID})
+			oFsm.pOnuDeviceEntry.SetReconcilingFlows(false)
+			oFsm.pOnuDeviceEntry.SetChReconcilingFlowsFinished(true)
+		}
+		logger.Debugw(ctx, "reconciling - skip enterVlanConfigDone processing",
+			log.Fields{"NumUniFlows": oFsm.NumUniFlows, "ConfiguredUniFlow": oFsm.ConfiguredUniFlow, "device-id": oFsm.deviceID})
+		oFsm.mutexFlowParams.Unlock()
+		return
+	}
+	if oFsm.NumUniFlows > oFsm.ConfiguredUniFlow {
+		if oFsm.ConfiguredUniFlow == 0 {
+			oFsm.mutexFlowParams.Unlock()
+			// this is a restart with a complete new flow, we can re-use the initial flow config control
+			// including the check, if the related techProfile is (still) available (probably also removed in between)
+			// Can't call FSM Event directly, decoupling it
+			go func(a_pBaseFsm *fsm.FSM) {
+				_ = a_pBaseFsm.Event(VlanEvRenew)
+			}(pConfigVlanStateBaseFsm)
+			return
+		}
+
+		//some further flows are to be configured
+		//store the actual rule that shall be worked upon in the following transient states
+		if len(oFsm.uniVlanFlowParamsSlice) < int(oFsm.ConfiguredUniFlow) {
+			//check introduced after having observed some panic in this processing
+			logger.Errorw(ctx, "error in FsmEvent handling UniVlanConfigFsm in ConfigDone - inconsistent counter",
+				log.Fields{"ConfiguredUniFlow": oFsm.ConfiguredUniFlow,
+					"sliceLen": len(oFsm.uniVlanFlowParamsSlice), "device-id": oFsm.deviceID})
+			oFsm.mutexFlowParams.Unlock()
+			go func(a_pAFsm *cmn.AdapterFsm) {
+				_ = a_pAFsm.PFsm.Event(VlanEvReset)
+			}(pConfigVlanStateAFsm)
+			return
+		}
+		oFsm.actualUniVlanConfigRule = oFsm.uniVlanFlowParamsSlice[oFsm.ConfiguredUniFlow].VlanRuleParams
+		oFsm.actualUniVlanConfigMeter = oFsm.uniVlanFlowParamsSlice[oFsm.ConfiguredUniFlow].Meter
+		//tpId of the next rule to be configured
+		tpID := oFsm.actualUniVlanConfigRule.TpID
+		oFsm.TpIDWaitingFor = tpID
+		loSetVlan := oFsm.actualUniVlanConfigRule.SetVid
+		//attention: take care to release the mutexFlowParams when calling the FSM directly -
+		//  synchronous FSM 'event/state' functions may rely on this mutex
+		//  but it must be released already before calling getTechProfileDone() as it may already be locked
+		//  by the techProfile processing call to VlanFsm.IsFlowRemovePending() (see VOL-4207)
+		oFsm.mutexFlowParams.Unlock()
+		loTechProfDone := oFsm.pUniTechProf.getTechProfileDone(ctx, oFsm.pOnuUniPort.UniID, tpID)
+		logger.Debugw(ctx, "UniVlanConfigFsm - incremental config request", log.Fields{
+			"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.UniID,
+			"set-Vlan": loSetVlan, "tp-id": tpID, "ProfDone": loTechProfDone})
+
+		// Can't call FSM Event directly, decoupling it
+		go func(aPBaseFsm *fsm.FSM, aTechProfDone bool) {
+			if aTechProfDone {
+				// let the vlan processing continue with next rule
+				_ = aPBaseFsm.Event(VlanEvIncrFlowConfig)
+			} else {
+				// set to waiting for Techprofile
+				_ = aPBaseFsm.Event(VlanEvWaitTPIncr)
+			}
+		}(pConfigVlanStateBaseFsm, loTechProfDone)
+		return
+	}
+	oFsm.mutexFlowParams.Unlock()
+	logger.Debugw(ctx, "UniVlanConfigFsm - VLAN config done: send dh event notification", log.Fields{
+		"device-id": oFsm.deviceID})
+	// it might appear that some flows are requested also after 'flowPushed' event has been generated ...
+	// state transition notification is checked in deviceHandler
+	// note: 'flowPushed' event is only generated if all 'pending' rules are configured
+	if oFsm.pDeviceHandler != nil {
+		//making use of the add->remove successor enum assumption/definition
+		go oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OnuDeviceEvent(uint8(oFsm.requestEvent)+oFsm.requestEventOffset))
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigIncrFlow(ctx context.Context, e *fsm.Event) {
+
+	if oFsm.pDeviceHandler.IsSkipOnuConfigReconciling() {
+		logger.Debugw(ctx, "reconciling - skip further processing for incremental flow",
+			log.Fields{"fsmState": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+		go func(a_pBaseFsm *fsm.FSM) {
+			_ = a_pBaseFsm.Event(VlanEvSkipIncFlowConfig)
+		}(oFsm.PAdaptFsm.PFsm)
+		return
+	}
+	oFsm.mutexFlowParams.Lock()
+	logger.Debugw(ctx, "UniVlanConfigFsm - start config further incremental flow", log.Fields{
+		"recent flow-number": oFsm.ConfiguredUniFlow,
+		"device-id":          oFsm.deviceID})
+	oFsm.TpIDWaitingFor = 0 //reset indication to avoid misinterpretation
+
+	if oFsm.actualUniVlanConfigRule.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		// meaning transparent setup - no specific VTFD setting required
+		logger.Debugw(ctx, "UniVlanConfigFsm: no VTFD config required", log.Fields{
+			"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+	} else {
+		//TODO!!!: it was not really intended to keep this enter* FSM method waiting on OMCI response (preventing other state transitions)
+		// so it would be conceptually better to wait for the response in background like for the other multi-entity processing
+		// but as the OMCI sequence must be ensured, a separate new state would be required - perhaps later
+		// in practice should have no influence by now as no other state transition is currently accepted (while cancel() is ensured)
+		if oFsm.numVlanFilterEntries == 0 {
+			// This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+			// this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+			vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.actualUniVlanConfigRule.TpID))
+			//no VTFD yet created
+			logger.Debugw(ctx, "UniVlanConfigFsm create VTFD", log.Fields{
+				"EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+				"device-id": oFsm.deviceID,
+				"macBpNo":   oFsm.pOnuUniPort.MacBpNo, "TpID": oFsm.actualUniVlanConfigRule.TpID})
+			// 'SetVid' below is assumed to be masked already by the caller to 12 bit
+			oFsm.vlanFilterList[0] = uint16(oFsm.actualUniVlanConfigRule.SetVid)
+
+			vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+			vtfdFilterList[0] = oFsm.vlanFilterList[0]
+			oFsm.numVlanFilterEntries = 1
+			meParams := me.ParamData{
+				EntityID: vtfdID,
+				Attributes: me.AttributeValueMap{
+					"VlanFilterList":   vtfdFilterList,
+					"ForwardOperation": uint8(0x10), //VID investigation
+					"NumberOfEntries":  oFsm.numVlanFilterEntries,
+				},
+			}
+			oFsm.mutexPLastTxMeInstance.Lock()
+			meInstance, err := oFsm.pOmciCC.SendCreateVtfdVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+				oFsm.PAdaptFsm.CommChan, meParams)
+			if err != nil {
+				oFsm.mutexPLastTxMeInstance.Unlock()
+				oFsm.mutexFlowParams.Unlock()
+				logger.Errorw(ctx, "VTFD create failed, aborting UniVlanConfig FSM!",
+					log.Fields{"device-id": oFsm.deviceID})
+				pConfigVlanStateAFsm := oFsm.PAdaptFsm
+				if pConfigVlanStateAFsm != nil {
+					go func(a_pAFsm *cmn.AdapterFsm) {
+						_ = a_pAFsm.PFsm.Event(VlanEvReset)
+					}(pConfigVlanStateAFsm)
+				}
+				return
+			}
+			//accept also nil as (error) return value for writing to LastTx
+			//  - this avoids misinterpretation of new received OMCI messages
+			//TODO!!: refactoring improvement requested, here as an example for [VOL-3457]:
+			//  send shall return (dual format) error code that can be used here for immediate error treatment
+			//  (relevant to all used sendXX() methods in this (and other) FSM's)
+			oFsm.pLastTxMeInstance = meInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
+		} else {
+			// This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+			// this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+			vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(oFsm.actualUniVlanConfigRule.TpID))
+
+			logger.Debugw(ctx, "UniVlanConfigFsm set VTFD", log.Fields{
+				"EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+				"device-id": oFsm.deviceID,
+				"macBpNo":   oFsm.pOnuUniPort.MacBpNo, "TpID": oFsm.actualUniVlanConfigRule.TpID})
+			// setVid is assumed to be masked already by the caller to 12 bit
+			oFsm.vlanFilterList[oFsm.numVlanFilterEntries] =
+				uint16(oFsm.actualUniVlanConfigRule.SetVid)
+			vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+
+			// FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+			// VTFD has to be created afresh with a new entity ID that has the same entity ID as the MBPCD ME for every
+			// new vlan associated with a different TP.
+			vtfdFilterList[0] = uint16(oFsm.actualUniVlanConfigRule.SetVid)
+
+			oFsm.numVlanFilterEntries++
+			meParams := me.ParamData{
+				EntityID: vtfdID,
+				Attributes: me.AttributeValueMap{
+					"VlanFilterList":   vtfdFilterList,
+					"ForwardOperation": uint8(0x10), //VID investigation
+					"NumberOfEntries":  oFsm.numVlanFilterEntries,
+				},
+			}
+			oFsm.mutexPLastTxMeInstance.Lock()
+			meInstance, err := oFsm.pOmciCC.SendCreateVtfdVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+				oFsm.PAdaptFsm.CommChan, meParams)
+			if err != nil {
+				oFsm.mutexPLastTxMeInstance.Unlock()
+				oFsm.mutexFlowParams.Unlock()
+				logger.Errorw(ctx, "UniVlanFsm create Vlan Tagging Filter ME result error",
+					log.Fields{"device-id": oFsm.deviceID, "Error": err})
+				_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+				return
+			}
+			//accept also nil as (error) return value for writing to LastTx
+			//  - this avoids misinterpretation of new received OMCI messages
+			//TODO!!: refactoring improvement requested, here as an example for [VOL-3457]:
+			//  send shall return (dual format) error code that can be used here for immediate error treatment
+			//  (relevant to all used sendXX() methods in this (and other) FSM's)
+			oFsm.pLastTxMeInstance = meInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
+		}
+		//verify response
+		err := oFsm.waitforOmciResponse(ctx)
+		if err != nil {
+			oFsm.mutexFlowParams.Unlock()
+			logger.Errorw(ctx, "VTFD create/set failed, aborting VlanConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID})
+			pConfigVlanStateBaseFsm := oFsm.PAdaptFsm.PFsm
+			// Can't call FSM Event directly, decoupling it
+			go func(a_pBaseFsm *fsm.FSM) {
+				_ = a_pBaseFsm.Event(VlanEvReset)
+			}(pConfigVlanStateBaseFsm)
+			return
+		}
+	}
+
+	oFsm.requestEventOffset = uint8(cDeviceEventOffsetAddWithKvStore) //0 offset for last flow-add activity
+	oFsm.mutexFlowParams.Unlock()
+	go func() {
+		oFsm.mutexFlowParams.RLock()
+		tpID := oFsm.actualUniVlanConfigRule.TpID
+		ConfiguredUniFlow := oFsm.ConfiguredUniFlow
+		// ensure mutexFlowParams not locked before calling some TPProcessing activity (that might already be pending on it)
+		oFsm.mutexFlowParams.RUnlock()
+		errEvto := oFsm.performConfigEvtocdEntries(ctx, ConfiguredUniFlow)
+		//This is correct passing scenario
+		if errEvto == nil {
+			//TODO Possibly insert new state for multicast --> possibly another jira/later time.
+			for _, gemPort := range oFsm.pUniTechProf.getMulticastGemPorts(ctx, oFsm.pOnuUniPort.UniID, uint8(tpID)) {
+				oFsm.mutexFlowParams.RLock()
+				vlanID := oFsm.actualUniVlanConfigRule.SetVid
+				logger.Infow(ctx, "Setting multicast MEs for additional flows", log.Fields{"deviceID": oFsm.deviceID,
+					"techProfile": tpID, "gemPort": gemPort,
+					"vlanID": vlanID, "ConfiguredUniFlow": ConfiguredUniFlow})
+				oFsm.mutexFlowParams.RUnlock()
+				errCreateAllMulticastME := oFsm.performSettingMulticastME(ctx, tpID, gemPort, vlanID)
+				if errCreateAllMulticastME != nil {
+					logger.Errorw(ctx, "Multicast ME create failed, aborting AniConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+				}
+			}
+			//If this incremental flow contains a meter, then create TD for related gems.
+			if oFsm.actualUniVlanConfigMeter != nil {
+				for _, gemPort := range oFsm.pUniTechProf.getBidirectionalGemPortIDsForTP(ctx, oFsm.pOnuUniPort.UniID, tpID) {
+					logger.Debugw(ctx, "Creating Traffic Descriptor for gem", log.Fields{"device-id": oFsm.deviceID, "meter": oFsm.actualUniVlanConfigMeter, "gem": gemPort})
+					errCreateTrafficDescriptor := oFsm.createTrafficDescriptor(ctx, oFsm.actualUniVlanConfigMeter, tpID,
+						oFsm.pOnuUniPort.UniID, gemPort)
+					if errCreateTrafficDescriptor != nil {
+						logger.Errorw(ctx, "Create Traffic Descriptor create failed, aborting Ani Config FSM!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					}
+				}
+			}
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvRxConfigEvtocd)
+		}
+	}()
+}
+
+func (oFsm *UniVlanConfigFsm) enterRemoveFlow(ctx context.Context, e *fsm.Event) {
+	oFsm.mutexFlowParams.RLock()
+	logger.Debugw(ctx, "UniVlanConfigFsm - start removing the top remove-flow", log.Fields{
+		"with last cookie": oFsm.uniRemoveFlowsSlice[0].cookie,
+		"device-id":        oFsm.deviceID})
+
+	pConfigVlanStateBaseFsm := oFsm.PAdaptFsm.PFsm
+	loAllowSpecificOmciConfig := oFsm.pDeviceHandler.IsReadyForOmciConfig()
+	loVlanEntryClear := uint8(0)
+	loVlanEntryRmPos := uint8(0x80) //with indication 'invalid' in bit 7
+	//shallow copy is sufficient as no reference variables are used within struct
+	loRuleParams := oFsm.uniRemoveFlowsSlice[0].vlanRuleParams
+	oFsm.mutexFlowParams.RUnlock()
+	logger.Debugw(ctx, "UniVlanConfigFsm - remove-flow parameters are", log.Fields{
+		"match vid": loRuleParams.MatchVid, "match Pcp": loRuleParams.MatchPcp,
+		"set vid":   strconv.FormatInt(int64(loRuleParams.SetVid), 16),
+		"device-id": oFsm.deviceID})
+
+	if loRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		// meaning transparent setup - no specific VTFD setting required
+		logger.Debugw(ctx, "UniVlanConfigFsm: no VTFD removal required for transparent flow", log.Fields{
+			"in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+	} else {
+		vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization and 're-copy'
+		if oFsm.numVlanFilterEntries == 1 {
+			vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(loRuleParams.TpID))
+			//only one active VLAN entry (hopefully the SetVID we want to remove - should be, but not verified ..)
+			//  so we can just delete the VTFD entry
+			logger.Debugw(ctx, "UniVlanConfigFsm: VTFD delete (no more vlan filters)",
+				log.Fields{"current vlan list": oFsm.vlanFilterList, "EntitytId": strconv.FormatInt(int64(vtfdID), 16),
+					"device-id": oFsm.deviceID,
+					"macBpNo":   oFsm.pOnuUniPort.MacBpNo, "TpID": loRuleParams.TpID})
+			loVlanEntryClear = 1           //full VlanFilter clear request
+			if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+				oFsm.mutexPLastTxMeInstance.Lock()
+				meInstance, err := oFsm.pOmciCC.SendDeleteVtfd(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+					oFsm.PAdaptFsm.CommChan, vtfdID)
+				if err != nil {
+					oFsm.mutexPLastTxMeInstance.Unlock()
+					logger.Errorw(ctx, "UniVlanFsm delete Vlan Tagging Filter ME result error",
+						log.Fields{"device-id": oFsm.deviceID, "Error": err})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					return
+				}
+				oFsm.pLastTxMeInstance = meInstance
+				oFsm.mutexPLastTxMeInstance.Unlock()
+			} else {
+				logger.Debugw(ctx, "UniVlanConfigFsm delete VTFD OMCI handling skipped based on device state", log.Fields{
+					"device-id": oFsm.deviceID, "device-state": oFsm.pDeviceHandler.GetDeviceReasonString()})
+			}
+		} else {
+			//many VTFD already should exists - find and remove the one concerned by the actual remove rule
+			//  by updating the VTFD per set command with new valid list
+			logger.Debugw(ctx, "UniVlanConfigFsm: VTFD removal of requested VLAN from the list on OMCI",
+				log.Fields{"current vlan list": oFsm.vlanFilterList,
+					"set-vlan": loRuleParams.SetVid, "device-id": oFsm.deviceID})
+			for i := uint8(0); i < oFsm.numVlanFilterEntries; i++ {
+				if loRuleParams.SetVid == uint32(oFsm.vlanFilterList[i]) {
+					loVlanEntryRmPos = i
+					break //abort search
+				}
+			}
+			if loVlanEntryRmPos < cVtfdTableSize {
+				vtfdID, _ := cmn.GenerateANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo), uint16(loRuleParams.TpID))
+				//valid entry was found - to be eclipsed
+				loVlanEntryClear = 2 //VlanFilter remove request for a specific entry
+				for i := uint8(0); i < oFsm.numVlanFilterEntries; i++ {
+					if i < loVlanEntryRmPos {
+						vtfdFilterList[i] = oFsm.vlanFilterList[i] //copy original
+					} else if i < (cVtfdTableSize - 1) {
+						vtfdFilterList[i] = oFsm.vlanFilterList[i+1] //copy successor (including 0 elements)
+					} else {
+						vtfdFilterList[i] = 0 //set last byte if needed
+					}
+				}
+				logger.Debugw(ctx, "UniVlanConfigFsm set VTFD", log.Fields{
+					"EntitytId":     strconv.FormatInt(int64(vtfdID), 16),
+					"new vlan list": vtfdFilterList, "device-id": oFsm.deviceID,
+					"macBpNo": oFsm.pOnuUniPort.MacBpNo, "TpID": loRuleParams.TpID})
+
+				if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+					// FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+					oFsm.mutexPLastTxMeInstance.Lock()
+					meInstance, err := oFsm.pOmciCC.SendDeleteVtfd(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+						oFsm.PAdaptFsm.CommChan, vtfdID)
+					if err != nil {
+						oFsm.mutexPLastTxMeInstance.Unlock()
+						logger.Errorw(ctx, "UniVlanFsm delete Vlan Tagging Filter ME result error",
+							log.Fields{"device-id": oFsm.deviceID, "Error": err})
+						_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+						return
+					}
+					oFsm.pLastTxMeInstance = meInstance
+					oFsm.mutexPLastTxMeInstance.Unlock()
+				} else {
+					logger.Debugw(ctx, "UniVlanConfigFsm set VTFD OMCI handling skipped based on device state", log.Fields{
+						"device-id": oFsm.deviceID, "device-state": oFsm.pDeviceHandler.GetDeviceReasonString()})
+				}
+			} else {
+				logger.Warnw(ctx, "UniVlanConfigFsm: requested VLAN for removal not found in list - ignore and continue (no VTFD set)",
+					log.Fields{"device-id": oFsm.deviceID})
+			}
+		}
+		if loVlanEntryClear > 0 {
+			if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+				//waiting on response
+				err := oFsm.waitforOmciResponse(ctx)
+				if err != nil {
+					logger.Errorw(ctx, "VTFD delete/reset failed, aborting VlanConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					// Can't call FSM Event directly, decoupling it
+					go func(a_pBaseFsm *fsm.FSM) {
+						_ = a_pBaseFsm.Event(VlanEvReset)
+					}(pConfigVlanStateBaseFsm)
+					return
+				}
+			}
+
+			oFsm.mutexFlowParams.Lock()
+			if loVlanEntryClear == 1 {
+				oFsm.vlanFilterList[0] = 0 //first entry is the only that can contain the previous only-one element
+				oFsm.numVlanFilterEntries = 0
+			} else if loVlanEntryClear == 2 {
+				// new VlanFilterList should be one entry smaller now - copy from last configured entry
+				// this loop now includes the 0 element on previous last valid entry
+				for i := uint8(0); i <= oFsm.numVlanFilterEntries; i++ {
+					oFsm.vlanFilterList[i] = vtfdFilterList[i]
+				}
+				oFsm.numVlanFilterEntries--
+			}
+			oFsm.mutexFlowParams.Unlock()
+		}
+	}
+
+	if loAllowSpecificOmciConfig { //specific OMCI config is expected to work acc. to the device state
+		go oFsm.removeEvtocdEntries(ctx, loRuleParams)
+	} else {
+		// OMCI processing is not done, expectation is to have the ONU in some basic config state accordingly
+		logger.Debugw(ctx, "UniVlanConfigFsm remove EVTOCD OMCI handling skipped based on device state", log.Fields{
+			"device-id": oFsm.deviceID})
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pBaseFsm *fsm.FSM) {
+			_ = a_pBaseFsm.Event(VlanEvRemFlowDone, loRuleParams.TpID)
+		}(pConfigVlanStateBaseFsm)
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterVlanCleanupDone(ctx context.Context, e *fsm.Event) {
+	var tpID uint8
+	// Extract the tpID
+	if len(e.Args) > 0 {
+		tpID = e.Args[0].(uint8)
+		logger.Debugw(ctx, "UniVlanConfigFsm - flow removed for tp id", log.Fields{"device-id": oFsm.deviceID, "tpID": e.Args[0].(uint8)})
+	} else {
+		logger.Warnw(ctx, "UniVlanConfigFsm - tp id not available", log.Fields{"device-id": oFsm.deviceID})
+	}
+	oFsm.mutexFlowParams.Lock()
+	deletedCookie := oFsm.uniRemoveFlowsSlice[0].cookie
+
+	pConfigVlanStateAFsm := oFsm.PAdaptFsm
+	if pConfigVlanStateAFsm == nil {
+		logger.Errorw(ctx, "invalid Fsm pointer - unresolvable - abort",
+			log.Fields{"device-id": oFsm.deviceID})
+		//would have to be fixed from outside somehow
+		return
+	}
+
+	// here we need o finally remove the removed data also from uniVlanFlowParamsSlice and possibly have to
+	//  stop the suspension of a add-activity waiting for the end of removal
+	//call from 'configured' state of the rule
+	if err := oFsm.removeFlowFromParamsSlice(ctx, deletedCookie, true); err != nil {
+		//something quite inconsistent detected, perhaps just try to recover with FSM reset
+		oFsm.mutexFlowParams.Unlock()
+		logger.Errorw(ctx, "UniVlanConfigFsm - could not clear database - abort", log.Fields{"device-id": oFsm.deviceID})
+		go func(a_pAFsm *cmn.AdapterFsm) {
+			_ = a_pAFsm.PFsm.Event(VlanEvReset)
+		}(pConfigVlanStateAFsm)
+		return
+	}
+	if oFsm.uniRemoveFlowsSlice[0].isSuspendedOnAdd {
+		removeChannel := oFsm.uniRemoveFlowsSlice[0].removeChannel
+		oFsm.mutexFlowParams.Unlock()
+		removeChannel <- true
+		oFsm.mutexFlowParams.Lock()
+	}
+
+	logger.Debugw(ctx, "UniVlanConfigFsm - removing the removal data", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.deviceID,
+		"removed cookie": deletedCookie, "waitForDeleteCookie": oFsm.delayNewRuleCookie})
+
+	if len(oFsm.uniRemoveFlowsSlice) <= 1 {
+		oFsm.uniRemoveFlowsSlice = nil //reset the slice
+		logger.Debugw(ctx, "UniVlanConfigFsm flow removal - last remove-flow deleted", log.Fields{
+			"device-id": oFsm.deviceID})
+	} else {
+		//cut off the actual flow by slicing out the first element
+		oFsm.uniRemoveFlowsSlice = append(
+			oFsm.uniRemoveFlowsSlice[:0],
+			oFsm.uniRemoveFlowsSlice[1:]...)
+		logger.Debugw(ctx, "UniVlanConfigFsm flow removal - specific flow deleted from data", log.Fields{
+			"device-id": oFsm.deviceID})
+	}
+	oFsm.mutexFlowParams.Unlock()
+
+	oFsm.requestEventOffset = uint8(cDeviceEventOffsetRemoveWithKvStore) //offset for last flow-remove activity (with kvStore request)
+	//return to the basic config verification state
+	// Can't call FSM Event directly, decoupling it
+	go func(a_pAFsm *cmn.AdapterFsm) {
+		_ = a_pAFsm.PFsm.Event(VlanEvFlowDataRemoved)
+	}(pConfigVlanStateAFsm)
+
+	oFsm.mutexFlowParams.Lock()
+	noOfFlowRem := len(oFsm.uniRemoveFlowsSlice)
+	if deletedCookie == oFsm.delayNewRuleCookie {
+		// flush the channel CookieDeleted to ensure it is not lingering from some previous (aborted) activity
+		select {
+		case <-oFsm.chCookieDeleted:
+			logger.Debug(ctx, "flushed CookieDeleted")
+		default:
+		}
+		oFsm.chCookieDeleted <- true // let the waiting AddFlow thread continue
+	}
+	// If all pending flow-removes are completed and TP ID is valid go on processing any pending TP delete
+	if oFsm.signalOnFlowDelete && noOfFlowRem == 0 && tpID > 0 {
+		logger.Debugw(ctx, "signal flow removal for pending TP delete", log.Fields{"device-id": oFsm.deviceID, "tpID": tpID})
+		// If we are here then all flows are removed.
+		if len(oFsm.flowDeleteChannel) == 0 { //channel not yet in use
+			oFsm.flowDeleteChannel <- true
+			oFsm.signalOnFlowDelete = false
+		}
+	}
+	oFsm.mutexFlowParams.Unlock()
+}
+
+func (oFsm *UniVlanConfigFsm) enterResetting(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniVlanConfigFsm resetting", log.Fields{"device-id": oFsm.deviceID})
+
+	pConfigVlanStateAFsm := oFsm.PAdaptFsm
+	if pConfigVlanStateAFsm != nil {
+		// abort running message processing
+		fsmAbortMsg := cmn.Message{
+			Type: cmn.TestMsg,
+			Data: cmn.TestMessage{
+				TestMessageVal: cmn.AbortMessageProcessing,
+			},
+		}
+		pConfigVlanStateAFsm.CommChan <- fsmAbortMsg
+
+		//try to restart the FSM to 'disabled'
+		// Can't call FSM Event directly, decoupling it
+		go func(a_pAFsm *cmn.AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.PFsm != nil {
+				_ = a_pAFsm.PFsm.Event(VlanEvRestart)
+			}
+		}(pConfigVlanStateAFsm)
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterDisabled(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "UniVlanConfigFsm enters disabled state", log.Fields{"device-id": oFsm.deviceID})
+	oFsm.mutexPLastTxMeInstance.Lock()
+	oFsm.pLastTxMeInstance = nil
+	oFsm.mutexPLastTxMeInstance.Unlock()
+
+	oFsm.mutexFlowParams.RLock()
+	if oFsm.delayNewRuleCookie != 0 {
+		// looks like the waiting AddFlow is stuck
+		oFsm.mutexFlowParams.RUnlock()
+		oFsm.chCookieDeleted <- false // let the waiting AddFlow thread terminate
+		oFsm.mutexFlowParams.RLock()
+	}
+	if len(oFsm.uniRemoveFlowsSlice) > 0 {
+		for _, removeUniFlowParams := range oFsm.uniRemoveFlowsSlice {
+			if removeUniFlowParams.isSuspendedOnAdd {
+				removeChannel := removeUniFlowParams.removeChannel
+				logger.Debugw(ctx, "UniVlanConfigFsm flow clear-up - abort suspended rule-add", log.Fields{
+					"device-id": oFsm.deviceID, "cookie": removeUniFlowParams.cookie})
+				oFsm.mutexFlowParams.RUnlock()
+				removeChannel <- false
+				oFsm.mutexFlowParams.RLock()
+			}
+		}
+	}
+
+	if oFsm.pDeviceHandler != nil {
+		//TODO: to clarify with improved error treatment for VlanConfigFsm (timeout,reception) errors
+		//  current code removes the complete FSM including all flow/rule configuration done so far
+		//  this might be a bit to much, it would require fully new flow config from rwCore (at least on OnuDown/up)
+		//  maybe a more sophisticated approach is possible without clearing the data
+		if oFsm.clearPersistency {
+			//permanently remove possibly stored persistent data
+			if len(oFsm.uniVlanFlowParamsSlice) > 0 {
+				var emptySlice = make([]cmn.UniVlanFlowParams, 0)
+				_ = oFsm.pDeviceHandler.StorePersUniFlowConfig(ctx, oFsm.pOnuUniPort.UniID, &emptySlice, true) //ignore errors
+			}
+		} else {
+			logger.Debugw(ctx, "UniVlanConfigFsm persistency data not cleared", log.Fields{"device-id": oFsm.deviceID})
+		}
+		oFsm.mutexFlowParams.RUnlock()
+		//request removal of 'reference' in the Handler (completely clear the FSM and its data)
+		go oFsm.pDeviceHandler.RemoveVlanFilterFsm(ctx, oFsm.pOnuUniPort)
+		return
+	}
+	oFsm.mutexFlowParams.RUnlock()
+}
+
+func (oFsm *UniVlanConfigFsm) processOmciVlanMessages(ctx context.Context) { //ctx context.Context?
+	logger.Debugw(ctx, "Start UniVlanConfigFsm Msg processing", log.Fields{"for device-id": oFsm.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info(ctx,"MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.deviceID})
+		// 	break loop
+		message, ok := <-oFsm.PAdaptFsm.CommChan
+		if !ok {
+			logger.Info(ctx, "UniVlanConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.deviceID})
+			// but then we have to ensure a restart of the FSM as well - as exceptional procedure
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			break loop
+		}
+		logger.Debugw(ctx, "UniVlanConfigFsm Rx Msg", log.Fields{"device-id": oFsm.deviceID})
+
+		switch message.Type {
+		case cmn.TestMsg:
+			msg, _ := message.Data.(cmn.TestMessage)
+			if msg.TestMessageVal == cmn.AbortMessageProcessing {
+				logger.Infow(ctx, "UniVlanConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.deviceID})
+				break loop
+			}
+			logger.Warnw(ctx, "UniVlanConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.deviceID, "MessageVal": msg.TestMessageVal})
+		case cmn.OMCI:
+			msg, _ := message.Data.(cmn.OmciMessage)
+			oFsm.handleOmciVlanConfigMessage(ctx, msg)
+		default:
+			logger.Warn(ctx, "UniVlanConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.deviceID,
+				"message.Type": message.Type})
+		}
+	}
+	logger.Infow(ctx, "End UniVlanConfigFsm Msg processing", log.Fields{"device-id": oFsm.deviceID})
+}
+
+func (oFsm *UniVlanConfigFsm) handleOmciVlanConfigMessage(ctx context.Context, msg cmn.OmciMessage) {
+	logger.Debugw(ctx, "Rx OMCI UniVlanConfigFsm Msg", log.Fields{"device-id": oFsm.deviceID,
+		"msgType": msg.OmciMsg.MessageType})
+
+	switch msg.OmciMsg.MessageType {
+	case omci.CreateResponseType:
+		{ // had to shift that to a method to cope with StaticCodeAnalysis restrictions :-(
+			if err := oFsm.handleOmciCreateResponseMessage(ctx, msg.OmciPacket); err != nil {
+				logger.Warnw(ctx, "CreateResponse handling aborted", log.Fields{"err": err})
+				return
+			}
+		} //CreateResponseType
+	case omci.SetResponseType:
+		{ //leave that here as direct code as most often used
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+			if msgLayer == nil {
+				logger.Errorw(ctx, "Omci Msg layer could not be detected for SetResponse",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.SetResponse)
+			if !msgOk {
+				logger.Errorw(ctx, "Omci Msg layer could not be assigned for SetResponse",
+					log.Fields{"device-id": oFsm.deviceID})
+				return
+			}
+			logger.Debugw(ctx, "UniVlanConfigFsm SetResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				logger.Errorw(ctx, "UniVlanConfigFsm Omci SetResponse Error - later: drive FSM to abort state ?",
+					log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+				// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+				return
+			}
+			oFsm.mutexPLastTxMeInstance.RLock()
+			if oFsm.pLastTxMeInstance != nil {
+				if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+					msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+					switch oFsm.pLastTxMeInstance.GetName() {
+					case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData", "MulticastOperationsProfile", "GemPortNetworkCtp":
+						{ // let the MultiEntity config proceed by stopping the wait function
+							oFsm.mutexPLastTxMeInstance.RUnlock()
+							oFsm.omciMIdsResponseReceived <- true
+							return
+						}
+					default:
+						{
+							logger.Warnw(ctx, "Unsupported ME name received!",
+								log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+						}
+					}
+				}
+			} else {
+				logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+			}
+			oFsm.mutexPLastTxMeInstance.RUnlock()
+		} //SetResponseType
+	case omci.DeleteResponseType:
+		{ // had to shift that to a method to cope with StaticCodeAnalysis restrictions :-(
+			if err := oFsm.handleOmciDeleteResponseMessage(ctx, msg.OmciPacket); err != nil {
+				logger.Warnw(ctx, "DeleteResponse handling aborted", log.Fields{"err": err})
+				return
+			}
+		} //DeleteResponseType
+	default:
+		{
+			logger.Errorw(ctx, "Rx OMCI unhandled MsgType",
+				log.Fields{"omciMsgType": msg.OmciMsg.MessageType, "device-id": oFsm.deviceID})
+			return
+		}
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) handleOmciCreateResponseMessage(ctx context.Context, apOmciPacket *gp.Packet) error {
+	msgLayer := (*apOmciPacket).Layer(omci.LayerTypeCreateResponse)
+	if msgLayer == nil {
+		logger.Errorw(ctx, "Omci Msg layer could not be detected for CreateResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return fmt.Errorf("omci msg layer could not be detected for CreateResponse for device-id %x",
+			oFsm.deviceID)
+	}
+	msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+	if !msgOk {
+		logger.Errorw(ctx, "Omci Msg layer could not be assigned for CreateResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return fmt.Errorf("omci msg layer could not be assigned for CreateResponse for device-id %x",
+			oFsm.deviceID)
+	}
+	logger.Debugw(ctx, "UniVlanConfigFsm CreateResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+	if msgObj.Result != me.Success && msgObj.Result != me.InstanceExists {
+		logger.Errorw(ctx, "Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"device-id": oFsm.deviceID,
+			"Error": msgObj.Result})
+		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+		return fmt.Errorf("omci CreateResponse Error for device-id %x",
+			oFsm.deviceID)
+	}
+	oFsm.mutexPLastTxMeInstance.RLock()
+	if oFsm.pLastTxMeInstance != nil {
+		if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+			msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+			// to satisfy StaticCodeAnalysis I had to move the small processing into a separate method :-(
+			switch oFsm.pLastTxMeInstance.GetName() {
+			case "VlanTaggingFilterData", "MulticastOperationsProfile",
+				"MulticastSubscriberConfigInfo", "MacBridgePortConfigurationData",
+				"ExtendedVlanTaggingOperationConfigurationData", "TrafficDescriptor":
+				{
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					if oFsm.PAdaptFsm.PFsm.Current() == VlanStConfigVtfd {
+						// Only if CreateResponse is received from first flow entry - let the FSM proceed ...
+						_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvRxConfigVtfd)
+					} else { // let the MultiEntity config proceed by stopping the wait function
+						oFsm.omciMIdsResponseReceived <- true
+					}
+					return nil
+				}
+			default:
+				{
+					logger.Warnw(ctx, "Unsupported ME name received!",
+						log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+				}
+			}
+		}
+	} else {
+		logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+	}
+	oFsm.mutexPLastTxMeInstance.RUnlock()
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) handleOmciDeleteResponseMessage(ctx context.Context, apOmciPacket *gp.Packet) error {
+	msgLayer := (*apOmciPacket).Layer(omci.LayerTypeDeleteResponse)
+	if msgLayer == nil {
+		logger.Errorw(ctx, "UniVlanConfigFsm - Omci Msg layer could not be detected for DeleteResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return fmt.Errorf("omci msg layer could not be detected for DeleteResponse for device-id %x",
+			oFsm.deviceID)
+	}
+	msgObj, msgOk := msgLayer.(*omci.DeleteResponse)
+	if !msgOk {
+		logger.Errorw(ctx, "UniVlanConfigFsm - Omci Msg layer could not be assigned for DeleteResponse",
+			log.Fields{"device-id": oFsm.deviceID})
+		return fmt.Errorf("omci msg layer could not be assigned for DeleteResponse for device-id %x",
+			oFsm.deviceID)
+	}
+	logger.Debugw(ctx, "UniVlanConfigFsm DeleteResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+	if msgObj.Result != me.Success {
+		logger.Errorw(ctx, "UniVlanConfigFsm - Omci DeleteResponse Error - later: drive FSM to abort state ?",
+			log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
+		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+		return fmt.Errorf("omci DeleteResponse Error for device-id %x",
+			oFsm.deviceID)
+	}
+	oFsm.mutexPLastTxMeInstance.RLock()
+	if oFsm.pLastTxMeInstance != nil {
+		if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+			msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+			switch oFsm.pLastTxMeInstance.GetName() {
+			case "VlanTaggingFilterData", "ExtendedVlanTaggingOperationConfigurationData", "TrafficDescriptor":
+				{ // let the MultiEntity config proceed by stopping the wait function
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					oFsm.omciMIdsResponseReceived <- true
+					return nil
+				}
+			default:
+				{
+					logger.Warnw(ctx, "Unsupported ME name received!",
+						log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+				}
+			}
+		}
+	} else {
+		logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
+	}
+	oFsm.mutexPLastTxMeInstance.RUnlock()
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performConfigEvtocdEntries(ctx context.Context, aFlowEntryNo uint8) error {
+	oFsm.mutexFlowParams.RLock()
+	evtocdID := oFsm.evtocdID
+	oFsm.mutexFlowParams.RUnlock()
+
+	if aFlowEntryNo == 0 {
+		// EthType set only at first flow element
+		// EVTOCD ME is expected to exist at this point already from MIB-Download (with AssociationType/Pointer)
+		// we need to extend the configuration by EthType definition and, to be sure, downstream 'inverse' mode
+		logger.Debugw(ctx, "UniVlanConfigFsm Tx Create::EVTOCD", log.Fields{
+			"EntitytId":  strconv.FormatInt(int64(evtocdID), 16),
+			"i/oEthType": strconv.FormatInt(int64(cDefaultTpid), 16),
+			"device-id":  oFsm.deviceID})
+		associationType := 2 // default to UniPPTP
+		if oFsm.pOnuUniPort.PortType == cmn.UniVEIP {
+			associationType = 10
+		}
+		// Create the EVTOCD ME
+		meParams := me.ParamData{
+			EntityID: evtocdID,
+			Attributes: me.AttributeValueMap{
+				"AssociationType":     uint8(associationType),
+				"AssociatedMePointer": oFsm.pOnuUniPort.EntityID,
+			},
+		}
+		oFsm.mutexPLastTxMeInstance.Lock()
+		meInstance, err := oFsm.pOmciCC.SendCreateEvtocdVar(context.TODO(), oFsm.pDeviceHandler.GetOmciTimeout(),
+			true, oFsm.PAdaptFsm.CommChan, meParams)
+		if err != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			logger.Errorw(ctx, "CreateEvtocdVar create failed, aborting UniVlanConfigFsm!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			return fmt.Errorf("evtocd instance create failed %s, error %s", oFsm.deviceID, err)
+		}
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
+
+		//verify response
+		err = oFsm.waitforOmciResponse(ctx)
+		if err != nil {
+			logger.Errorw(ctx, "Evtocd create failed, aborting VlanConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			return fmt.Errorf("evtocd create failed %s, error %s", oFsm.deviceID, err)
+		}
+
+		// Set the EVTOCD ME default params
+		meParams = me.ParamData{
+			EntityID: evtocdID,
+			Attributes: me.AttributeValueMap{
+				"InputTpid":      uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
+				"OutputTpid":     uint16(cDefaultTpid), //could be possibly retrieved from flow config one day, by now just like py-code base
+				"DownstreamMode": uint8(cDefaultDownstreamMode),
+			},
+		}
+		oFsm.mutexPLastTxMeInstance.Lock()
+		meInstance, err = oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+			oFsm.pDeviceHandler.GetOmciTimeout(), true,
+			oFsm.PAdaptFsm.CommChan, meParams)
+		if err != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+		}
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
+
+		//verify response
+		err = oFsm.waitforOmciResponse(ctx)
+		if err != nil {
+			logger.Errorw(ctx, "Evtocd set TPID failed, aborting VlanConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			return fmt.Errorf("evtocd set TPID failed %s, error %s", oFsm.deviceID, err)
+		}
+	} //first flow element
+
+	oFsm.mutexFlowParams.RLock()
+	if oFsm.actualUniVlanConfigRule.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		//transparent transmission required
+		oFsm.mutexFlowParams.RUnlock()
+		logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD single tagged transparent rule", log.Fields{
+			"device-id": oFsm.deviceID})
+		sliceEvtocdRule := make([]uint8, 16)
+		// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+		binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+			cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+				cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+				cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+		binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+			cPrioDefaultFilter<<cFilterPrioOffset| // default inner-tag rule
+				cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+				cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+				cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+		binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+			0<<cTreatTTROffset| // Do not pop any tags
+				cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+				cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+				cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+		binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+			cDoNotAddPrio<<cTreatPrioOffset| // do not add inner tag
+				cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+				cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+		meParams := me.ParamData{
+			EntityID: evtocdID,
+			Attributes: me.AttributeValueMap{
+				"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+			},
+		}
+		oFsm.mutexPLastTxMeInstance.Lock()
+		meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+			oFsm.pDeviceHandler.GetOmciTimeout(), true,
+			oFsm.PAdaptFsm.CommChan, meParams)
+		if err != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+		}
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
+
+		//verify response
+		err = oFsm.waitforOmciResponse(ctx)
+		if err != nil {
+			logger.Errorw(ctx, "Evtocd set transparent singletagged rule failed, aborting VlanConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			return fmt.Errorf("evtocd set transparent singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
+		}
+	} else {
+		// according to py-code acceptIncrementalEvto program option decides upon stacking or translation scenario
+		if oFsm.acceptIncrementalEvtoOption {
+			matchPcp := oFsm.actualUniVlanConfigRule.MatchPcp
+			matchVid := oFsm.actualUniVlanConfigRule.MatchVid
+			setPcp := oFsm.actualUniVlanConfigRule.SetPcp
+			setVid := oFsm.actualUniVlanConfigRule.SetVid
+			// this defines VID translation scenario: singletagged->singletagged (if not transparent)
+			logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD single tagged translation rule", log.Fields{
+				"match-pcp": matchPcp, "match-vid": matchVid, "set-pcp": setPcp, "set-vid:": setVid, "device-id": oFsm.deviceID})
+			sliceEvtocdRule := make([]uint8, 16)
+			// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+			binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+				cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+					cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+					cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+			binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+				oFsm.actualUniVlanConfigRule.MatchPcp<<cFilterPrioOffset| // either DNFonPrio or ignore tag (default) on innerVLAN
+					oFsm.actualUniVlanConfigRule.MatchVid<<cFilterVidOffset| // either DNFonVid or real filter VID
+					cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+					cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+			binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+				oFsm.actualUniVlanConfigRule.TagsToRemove<<cTreatTTROffset| // either 1 or 0
+					cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+					cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+					cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+			binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+				oFsm.actualUniVlanConfigRule.SetPcp<<cTreatPrioOffset| // as configured in flow
+					oFsm.actualUniVlanConfigRule.SetVid<<cTreatVidOffset| //as configured in flow
+					cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+			oFsm.mutexFlowParams.RUnlock()
+
+			meParams := me.ParamData{
+				EntityID: evtocdID,
+				Attributes: me.AttributeValueMap{
+					"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+				},
+			}
+			oFsm.mutexPLastTxMeInstance.Lock()
+			meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+				oFsm.pDeviceHandler.GetOmciTimeout(), true,
+				oFsm.PAdaptFsm.CommChan, meParams)
+			if err != nil {
+				oFsm.mutexPLastTxMeInstance.Unlock()
+				logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+					log.Fields{"device-id": oFsm.deviceID})
+				_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+				return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+			}
+			//accept also nil as (error) return value for writing to LastTx
+			//  - this avoids misinterpretation of new received OMCI messages
+			oFsm.pLastTxMeInstance = meInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
+
+			//verify response
+			err = oFsm.waitforOmciResponse(ctx)
+			if err != nil {
+				logger.Errorw(ctx, "Evtocd set singletagged translation rule failed, aborting VlanConfig FSM!",
+					log.Fields{"device-id": oFsm.deviceID})
+				_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+				return fmt.Errorf("evtocd set singletagged translation rule failed %s, error %s", oFsm.deviceID, err)
+			}
+		} else {
+			//not transparent and not acceptIncrementalEvtoOption untagged/priotagged->singletagged
+			{ // just for local var's
+				// this defines stacking scenario: untagged->singletagged
+				logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD untagged->singletagged rule", log.Fields{
+					"device-id": oFsm.deviceID})
+				sliceEvtocdRule := make([]uint8, 16)
+				// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+				binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+					cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+						cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+						cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+				binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+					cPrioIgnoreTag<<cFilterPrioOffset| // Not an inner-tag rule
+						cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+						cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+						cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+					0<<cTreatTTROffset| // Do not pop any tags
+						cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+						cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+						cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+					0<<cTreatPrioOffset| // vlan prio set to 0
+						//   (as done in Py code, maybe better option would be setPcp here, which still could be 0?)
+						oFsm.actualUniVlanConfigRule.SetVid<<cTreatVidOffset| // Outer VID don't care
+						cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+				oFsm.mutexFlowParams.RUnlock()
+				meParams := me.ParamData{
+					EntityID: evtocdID,
+					Attributes: me.AttributeValueMap{
+						"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+					},
+				}
+				oFsm.mutexPLastTxMeInstance.Lock()
+				meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+					oFsm.pDeviceHandler.GetOmciTimeout(), true,
+					oFsm.PAdaptFsm.CommChan, meParams)
+				if err != nil {
+					oFsm.mutexPLastTxMeInstance.Unlock()
+					logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+				}
+				//accept also nil as (error) return value for writing to LastTx
+				//  - this avoids misinterpretation of new received OMCI messages
+				oFsm.pLastTxMeInstance = meInstance
+				oFsm.mutexPLastTxMeInstance.Unlock()
+
+				//verify response
+				err = oFsm.waitforOmciResponse(ctx)
+				if err != nil {
+					logger.Errorw(ctx, "Evtocd set untagged->singletagged rule failed, aborting VlanConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					return fmt.Errorf("evtocd set untagged->singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
+				}
+			} // just for local var's
+			{ // just for local var's
+				// this defines 'stacking' scenario: priotagged->singletagged
+				logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD priotagged->singletagged rule", log.Fields{
+					"device-id": oFsm.deviceID})
+				sliceEvtocdRule := make([]uint8, 16)
+				// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+				binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+					cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+						cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+						cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+				binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+					cPrioDoNotFilter<<cFilterPrioOffset| // Do not Filter on innerprio
+						0<<cFilterVidOffset| // filter on inner vid 0 (prioTagged)
+						cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+						cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+					1<<cTreatTTROffset| // pop the prio-tag
+						cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+						cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+						cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+				oFsm.mutexFlowParams.RLock()
+				binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+					cCopyPrioFromInner<<cTreatPrioOffset| // vlan copy from PrioTag
+						//   (as done in Py code, maybe better option would be setPcp here, which still could be PrioCopy?)
+						oFsm.actualUniVlanConfigRule.SetVid<<cTreatVidOffset| // Outer VID as configured
+						cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+				oFsm.mutexFlowParams.RUnlock()
+
+				meParams := me.ParamData{
+					EntityID: evtocdID,
+					Attributes: me.AttributeValueMap{
+						"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+					},
+				}
+				oFsm.mutexPLastTxMeInstance.Lock()
+				meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+					oFsm.pDeviceHandler.GetOmciTimeout(), true,
+					oFsm.PAdaptFsm.CommChan, meParams)
+				if err != nil {
+					oFsm.mutexPLastTxMeInstance.Unlock()
+					logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					return fmt.Errorf("evtocd instance set failed %s, error %s", oFsm.deviceID, err)
+				}
+				//accept also nil as (error) return value for writing to LastTx
+				//  - this avoids misinterpretation of new received OMCI messages
+				oFsm.pLastTxMeInstance = meInstance
+				oFsm.mutexPLastTxMeInstance.Unlock()
+
+				//verify response
+				err = oFsm.waitforOmciResponse(ctx)
+				if err != nil {
+					logger.Errorw(ctx, "Evtocd set priotagged->singletagged rule failed, aborting VlanConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					return fmt.Errorf("evtocd set priotagged->singletagged rule failed %s, error %s", oFsm.deviceID, err)
+
+				}
+			} //just for local var's
+		}
+	}
+
+	// if Config has been done for all EVTOCD entries let the FSM proceed
+	logger.Debugw(ctx, "EVTOCD set loop finished", log.Fields{"device-id": oFsm.deviceID})
+	oFsm.mutexFlowParams.Lock()
+	oFsm.ConfiguredUniFlow++ // one (more) flow configured
+	oFsm.mutexFlowParams.Unlock()
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) removeEvtocdEntries(ctx context.Context, aRuleParams cmn.UniVlanRuleParams) {
+	oFsm.mutexFlowParams.RLock()
+	evtocdID := oFsm.evtocdID
+	oFsm.mutexFlowParams.RUnlock()
+
+	// configured Input/Output TPID is not modified again - no influence if no filter is applied
+	if aRuleParams.SetVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		//transparent transmission was set
+		//perhaps the config is not needed for removal,
+		//  but the specific InnerTpid setting is removed in favor of the real default forwarding rule
+		logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD reset to default single tagged rule", log.Fields{
+			"device-id": oFsm.deviceID})
+		sliceEvtocdRule := make([]uint8, 16)
+		// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+		binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+			cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+				cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+				cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+		binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+			cPrioDefaultFilter<<cFilterPrioOffset| // default inner-tag rule
+				cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+				cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+				cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+		binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+			0<<cTreatTTROffset| // Do not pop any tags
+				cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+				cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+				cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+		binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+			cDoNotAddPrio<<cTreatPrioOffset| // do not add inner tag
+				cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+				cDontCareTpid<<cTreatTpidOffset) // copy TPID and DEI
+
+		meParams := me.ParamData{
+			EntityID: evtocdID,
+			Attributes: me.AttributeValueMap{
+				"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+			},
+		}
+		oFsm.mutexPLastTxMeInstance.Lock()
+		meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+			oFsm.pDeviceHandler.GetOmciTimeout(), true,
+			oFsm.PAdaptFsm.CommChan, meParams)
+		if err != nil {
+			oFsm.mutexPLastTxMeInstance.Unlock()
+			logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			return
+		}
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
+
+		//verify response
+		err = oFsm.waitforOmciResponse(ctx)
+		if err != nil {
+			logger.Errorw(ctx, "Evtocd reset singletagged rule failed, aborting VlanConfig FSM!",
+				log.Fields{"device-id": oFsm.deviceID})
+			_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+			return
+		}
+	} else {
+		// according to py-code acceptIncrementalEvto program option decides upon stacking or translation scenario
+		oFsm.mutexFlowParams.RLock()
+		if oFsm.acceptIncrementalEvtoOption {
+			oFsm.mutexFlowParams.RUnlock()
+			// this defines VID translation scenario: singletagged->singletagged (if not transparent)
+			logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD clear single tagged translation rule", log.Fields{
+				"device-id": oFsm.deviceID, "match-vlan": aRuleParams.MatchVid})
+			sliceEvtocdRule := make([]uint8, 16)
+			// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+			binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+				cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+					cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+					cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+			binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+				aRuleParams.MatchPcp<<cFilterPrioOffset| // either DNFonPrio or ignore tag (default) on innerVLAN
+					aRuleParams.MatchVid<<cFilterVidOffset| // either DNFonVid or real filter VID
+					cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+					cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+			// delete indication for the indicated Filter
+			binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:], 0xFFFFFFFF)
+			binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:], 0xFFFFFFFF)
+
+			meParams := me.ParamData{
+				EntityID: evtocdID,
+				Attributes: me.AttributeValueMap{
+					"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+				},
+			}
+			oFsm.mutexPLastTxMeInstance.Lock()
+			meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+				oFsm.pDeviceHandler.GetOmciTimeout(), true,
+				oFsm.PAdaptFsm.CommChan, meParams)
+			if err != nil {
+				oFsm.mutexPLastTxMeInstance.Unlock()
+				logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+					log.Fields{"device-id": oFsm.deviceID})
+				_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+				return
+			}
+			//accept also nil as (error) return value for writing to LastTx
+			//  - this avoids misinterpretation of new received OMCI messages
+			oFsm.pLastTxMeInstance = meInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
+
+			//verify response
+			err = oFsm.waitforOmciResponse(ctx)
+			if err != nil {
+				logger.Errorw(ctx, "Evtocd clear singletagged translation rule failed, aborting VlanConfig FSM!",
+					log.Fields{"device-id": oFsm.deviceID, "match-vlan": aRuleParams.MatchVid})
+				_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+				return
+			}
+		} else {
+			// VOL-3685
+			// NOTE: With ALPHA ONUs it was seen that just resetting a particular entry in the EVTOCD table
+			// and re-configuring a new entry would not work. The old entry is removed and new entry is created
+			// indeed, but the traffic landing upstream would carry old vlan sometimes.
+			// This is only a WORKAROUND which basically deletes the entire EVTOCD ME and re-creates it again
+			// later when the flow is being re-installed.
+			// Of course this is applicable to case only where single service (or single tcont) is in use and
+			// there is only one service vlan (oFsm.acceptIncrementalEvtoOption is false in this case).
+			// Interstingly this problem has not been observed in multi-tcont (or multi-service) scenario (in
+			// which case the oFsm.acceptIncrementalEvtoOption is set to true).
+			if oFsm.ConfiguredUniFlow == 1 && !oFsm.acceptIncrementalEvtoOption {
+				oFsm.mutexFlowParams.RUnlock()
+				logger.Debugw(ctx, "UniVlanConfigFsm Tx Remove::EVTOCD", log.Fields{"device-id": oFsm.deviceID})
+				// When there are no more EVTOCD vlan configurations on the ONU and acceptIncrementalEvtoOption
+				// is not enabled, delete the EVTOCD ME.
+				meParams := me.ParamData{
+					EntityID: evtocdID,
+				}
+				oFsm.mutexPLastTxMeInstance.Lock()
+				meInstance, err := oFsm.pOmciCC.SendDeleteEvtocd(log.WithSpanFromContext(context.TODO(), ctx),
+					oFsm.pDeviceHandler.GetOmciTimeout(), true,
+					oFsm.PAdaptFsm.CommChan, meParams)
+				if err != nil {
+					oFsm.mutexPLastTxMeInstance.Unlock()
+					logger.Errorw(ctx, "DeleteEvtocdVar delete failed, aborting UniVlanConfigFsm!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					return
+				}
+				//accept also nil as (error) return value for writing to LastTx
+				//  - this avoids misinterpretation of new received OMCI messages
+				oFsm.pLastTxMeInstance = meInstance
+				oFsm.mutexPLastTxMeInstance.Unlock()
+
+				//verify response
+				err = oFsm.waitforOmciResponse(ctx)
+				if err != nil {
+					logger.Errorw(ctx, "Evtocd delete rule failed, aborting VlanConfig FSM!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+					return
+				}
+			} else {
+				// NOTE : We should ideally never ether this section when oFsm.acceptIncrementalEvtoOption is set to false
+				// This is true for only ATT/DT workflow
+				logger.Debugw(ctx, "UniVlanConfigFsm: Remove EVTOCD set operation",
+					log.Fields{"configured-flow": oFsm.ConfiguredUniFlow, "incremental-evto": oFsm.acceptIncrementalEvtoOption})
+				oFsm.mutexFlowParams.RUnlock()
+				//not transparent and not acceptIncrementalEvtoOption: untagged/priotagged->singletagged
+				{ // just for local var's
+					// this defines stacking scenario: untagged->singletagged
+					//TODO!! in theory there could be different rules running in setting different PCP/VID'S
+					//  for untagged/priotagged, last rule wins (and remains the only one), maybe that should be
+					//  checked already at flow-add (and rejected) - to be observed if such is possible in Voltha
+					//  delete now assumes there is only one such rule!
+					logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD reset untagged rule to default", log.Fields{
+						"device-id": oFsm.deviceID})
+					sliceEvtocdRule := make([]uint8, 16)
+					// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+						cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+							cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+							cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+						cPrioIgnoreTag<<cFilterPrioOffset| // Not an inner-tag rule
+							cDoNotFilterVid<<cFilterVidOffset| // Do not filter on inner vid
+							cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+							cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:],
+						0<<cTreatTTROffset| // Do not pop any tags
+							cDoNotAddPrio<<cTreatPrioOffset| // do not add outer tag
+							cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+							cDontCareTpid<<cTreatTpidOffset) // Outer TPID field don't care
+
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:],
+						cDoNotAddPrio<<cTreatPrioOffset| // do not add inner tag
+							cDontCareVid<<cTreatVidOffset| // Outer VID don't care
+							cDontCareTpid<<cTreatTpidOffset) // copy TPID and DEI
+
+					meParams := me.ParamData{
+						EntityID: evtocdID,
+						Attributes: me.AttributeValueMap{
+							"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+						},
+					}
+					oFsm.mutexPLastTxMeInstance.Lock()
+					meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(context.TODO(),
+						oFsm.pDeviceHandler.GetOmciTimeout(), true,
+						oFsm.PAdaptFsm.CommChan, meParams)
+					if err != nil {
+						oFsm.mutexPLastTxMeInstance.Unlock()
+						logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+						return
+					}
+					//accept also nil as (error) return value for writing to LastTx
+					//  - this avoids misinterpretation of new received OMCI messages
+					oFsm.pLastTxMeInstance = meInstance
+					oFsm.mutexPLastTxMeInstance.Unlock()
+
+					//verify response
+					err = oFsm.waitforOmciResponse(ctx)
+					if err != nil {
+						logger.Errorw(ctx, "Evtocd reset untagged rule to default failed, aborting VlanConfig FSM!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+						return
+					}
+				} // just for local var's
+				{ // just for local var's
+					// this defines 'stacking' scenario: priotagged->singletagged
+					logger.Debugw(ctx, "UniVlanConfigFsm Tx Set::EVTOCD delete priotagged rule", log.Fields{
+						"device-id": oFsm.deviceID})
+					sliceEvtocdRule := make([]uint8, 16)
+					// fill vlan tagging operation table bit fields using network=bigEndian order and using slice offset 0 as highest 'word'
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterOuterOffset:],
+						cPrioIgnoreTag<<cFilterPrioOffset| // Not an outer-tag rule
+							cDoNotFilterVid<<cFilterVidOffset| // Do not filter on outer vid
+							cDoNotFilterTPID<<cFilterTpidOffset) // Do not filter on outer TPID field
+
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cFilterInnerOffset:],
+						cPrioDoNotFilter<<cFilterPrioOffset| // Do not Filter on innerprio
+							0<<cFilterVidOffset| // filter on inner vid 0 (prioTagged)
+							cDoNotFilterTPID<<cFilterTpidOffset| // Do not filter on inner TPID field
+							cDoNotFilterEtherType<<cFilterEtherTypeOffset) // Do not filter of EtherType
+
+					// delete indication for the indicated Filter
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatOuterOffset:], 0xFFFFFFFF)
+					binary.BigEndian.PutUint32(sliceEvtocdRule[cTreatInnerOffset:], 0xFFFFFFFF)
+
+					meParams := me.ParamData{
+						EntityID: evtocdID,
+						Attributes: me.AttributeValueMap{
+							"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+						},
+					}
+					oFsm.mutexPLastTxMeInstance.Lock()
+					meInstance, err := oFsm.pOmciCC.SendSetEvtocdVar(log.WithSpanFromContext(context.TODO(), ctx),
+						oFsm.pDeviceHandler.GetOmciTimeout(), true,
+						oFsm.PAdaptFsm.CommChan, meParams)
+					if err != nil {
+						oFsm.mutexPLastTxMeInstance.Unlock()
+						logger.Errorw(ctx, "SetEvtocdVar set failed, aborting UniVlanConfigFsm!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+						return
+					}
+					//accept also nil as (error) return value for writing to LastTx
+					//  - this avoids misinterpretation of new received OMCI messages
+					oFsm.pLastTxMeInstance = meInstance
+					oFsm.mutexPLastTxMeInstance.Unlock()
+
+					//verify response
+					err = oFsm.waitforOmciResponse(ctx)
+					if err != nil {
+						logger.Errorw(ctx, "Evtocd delete priotagged rule failed, aborting VlanConfig FSM!",
+							log.Fields{"device-id": oFsm.deviceID})
+						_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+						return
+					}
+				}
+			} //just for local var's
+		}
+	}
+	// if Config has been done for all EVTOCD entries let the FSM proceed
+	logger.Debugw(ctx, "EVTOCD filter remove loop finished", log.Fields{"device-id": oFsm.deviceID})
+	_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvRemFlowDone, aRuleParams.TpID)
+}
+
+func (oFsm *UniVlanConfigFsm) waitforOmciResponse(ctx context.Context) error {
+	oFsm.mutexIsAwaitingResponse.Lock()
+	if oFsm.isCanceled {
+		// FSM already canceled before entering wait
+		logger.Debugw(ctx, "UniVlanConfigFsm wait-for-multi-entity-response aborted (on enter)", log.Fields{"for device-id": oFsm.deviceID})
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		return fmt.Errorf(cmn.CErrWaitAborted)
+	}
+	oFsm.isAwaitingResponse = true
+	oFsm.mutexIsAwaitingResponse.Unlock()
+	select {
+	// maybe be also some outside cancel (but no context modeled for the moment ...)
+	// case <-ctx.Done():
+	// 		logger.Infow(ctx,"LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
+	case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second): //AS FOR THE OTHER OMCI FSM's
+		logger.Warnw(ctx, "UniVlanConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.deviceID})
+		oFsm.mutexIsAwaitingResponse.Lock()
+		oFsm.isAwaitingResponse = false
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		return fmt.Errorf("uniVlanConfigFsm multi entity timeout %s", oFsm.deviceID)
+	case success := <-oFsm.omciMIdsResponseReceived:
+		if success {
+			logger.Debugw(ctx, "UniVlanConfigFsm multi entity response received", log.Fields{"for device-id": oFsm.deviceID})
+			oFsm.mutexIsAwaitingResponse.Lock()
+			oFsm.isAwaitingResponse = false
+			oFsm.mutexIsAwaitingResponse.Unlock()
+			return nil
+		}
+		// waiting was aborted (probably on external request)
+		logger.Debugw(ctx, "UniVlanConfigFsm wait-for-multi-entity-response aborted", log.Fields{"for device-id": oFsm.deviceID})
+		oFsm.mutexIsAwaitingResponse.Lock()
+		oFsm.isAwaitingResponse = false
+		oFsm.mutexIsAwaitingResponse.Unlock()
+		return fmt.Errorf(cmn.CErrWaitAborted)
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) performSettingMulticastME(ctx context.Context, tpID uint8, multicastGemPortID uint16, vlanID uint32) error {
+	logger.Debugw(ctx, "Setting Multicast MEs", log.Fields{"device-id": oFsm.deviceID, "tpID": tpID,
+		"multicastGemPortID": multicastGemPortID, "vlanID": vlanID})
+	errCreateMOP := oFsm.performCreatingMulticastOperationProfile(ctx)
+	if errCreateMOP != nil {
+		logger.Errorw(ctx, "MulticastOperationProfile create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errCreateMOP)
+	}
+
+	errSettingMOP := oFsm.performSettingMulticastOperationProfile(ctx, multicastGemPortID, vlanID)
+	if errSettingMOP != nil {
+		logger.Errorw(ctx, "MulticastOperationProfile setting failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errSettingMOP)
+	}
+
+	errCreateMSCI := oFsm.performCreatingMulticastSubscriberConfigInfo(ctx)
+	if errCreateMSCI != nil {
+		logger.Errorw(ctx, "MulticastOperationProfile setting failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, errCreateMSCI)
+	}
+	macBpCdEID, errMacBpCdEID := cmn.GenerateMcastANISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo))
+	if errMacBpCdEID != nil {
+		logger.Errorw(ctx, "MulticastMacBridgePortConfigData entity id generation failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("generateMcastANISideMBPCDEID responseError %s, error %s", oFsm.deviceID, errMacBpCdEID)
+
+	}
+	logger.Debugw(ctx, "UniVlanConfigFsm set macBpCdEID for mcast", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(macBpCdEID), 16), "macBpNo": oFsm.pOnuUniPort.MacBpNo,
+		"in state": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+	meParams := me.ParamData{
+		EntityID: macBpCdEID,
+		Attributes: me.AttributeValueMap{
+			"BridgeIdPointer": cmn.MacBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.MacBpNo),
+			"PortNum":         0xf0, //fixed unique ANI side indication
+			"TpType":          6,    //MCGemIWTP
+			"TpPointer":       multicastGemPortID,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendCreateMBPConfigDataVar(context.TODO(),
+		oFsm.pDeviceHandler.GetOmciTimeout(), true, oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "MBPConfigDataVar create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo createError #{oFsm.deviceID}, error #{err}")
+	}
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "CreateMBPConfigData failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "MBPConfigDataID": cmn.MacBridgeServiceProfileEID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s, error %s", oFsm.deviceID, err)
+	}
+
+	// ==> Start creating VTFD for mcast vlan
+
+	// This attribute uniquely identifies each instance of this managed entity. Through an identical ID,
+	// this managed entity is implicitly linked to an instance of the MAC bridge port configuration data ME.
+	mcastVtfdID := macBpCdEID
+
+	logger.Debugw(ctx, "UniVlanConfigFsm set VTFD for mcast", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(mcastVtfdID), 16), "mcastVlanID": vlanID,
+		"in state": oFsm.PAdaptFsm.PFsm.Current(), "device-id": oFsm.deviceID})
+	vtfdFilterList := make([]uint16, cVtfdTableSize) //needed for parameter serialization
+
+	// FIXME: VOL-3685: Issues with resetting a table entry in EVTOCD ME
+	// VTFD has to be created afresh with a new entity ID that has the same entity ID as the MBPCD ME for every
+	// new vlan associated with a different TP.
+	vtfdFilterList[0] = uint16(vlanID)
+
+	meParams = me.ParamData{
+		EntityID: mcastVtfdID,
+		Attributes: me.AttributeValueMap{
+			"VlanFilterList":   vtfdFilterList,
+			"ForwardOperation": uint8(0x10), //VID investigation
+			"NumberOfEntries":  oFsm.numVlanFilterEntries,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err = oFsm.pOmciCC.SendCreateVtfdVar(context.TODO(),
+		oFsm.pDeviceHandler.GetOmciTimeout(), true, oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "CreateVtfdVar create failed, aborting UniVlanConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("createMcastVlanFilterData creationError %s, error %s", oFsm.deviceID, err)
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "CreateMcastVlanFilterData failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "mcastVtfdID": mcastVtfdID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("createMcastVlanFilterData responseError %s, error %s", oFsm.deviceID, err)
+	}
+
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performCreatingMulticastSubscriberConfigInfo(ctx context.Context) error {
+	instID, err := cmn.GenerateUNISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo))
+	if err != nil {
+		logger.Errorw(ctx, "error generrating me instance id",
+			log.Fields{"device-id": oFsm.deviceID, "error": err})
+		return err
+	}
+	logger.Debugw(ctx, "UniVlanConfigFsm create MulticastSubscriberConfigInfo",
+		log.Fields{"device-id": oFsm.deviceID, "EntityId": instID})
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"MeType": 0,
+			//Direct reference to the Operation profile
+			//TODO ANI side used on UNI side, not the clearest option.
+			"MulticastOperationsProfilePointer": instID,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendCreateMulticastSubConfigInfoVar(context.TODO(),
+		oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "CreateMulticastSubConfigInfoVar create failed, aborting UniVlanConfigFSM!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo interface creationError %s, error %s",
+			oFsm.deviceID, err)
+	}
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+	//verify response
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "CreateMulticastSubConfigInfo create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "MulticastSubConfigInfo": instID})
+		return fmt.Errorf("creatingMulticastSubscriberConfigInfo responseError %s", oFsm.deviceID)
+	}
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performCreatingMulticastOperationProfile(ctx context.Context) error {
+	instID, err := cmn.GenerateUNISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo))
+	if err != nil {
+		logger.Errorw(ctx, "error generating me instance id",
+			log.Fields{"device-id": oFsm.deviceID, "error": err})
+		return err
+	}
+	logger.Debugw(ctx, "UniVlanConfigFsm create MulticastOperationProfile",
+		log.Fields{"device-id": oFsm.deviceID, "EntityId": instID})
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"IgmpVersion":  2,
+			"IgmpFunction": 0,
+			//0 means false
+			"ImmediateLeave":         0,
+			"Robustness":             2,
+			"QuerierIp":              0,
+			"QueryInterval":          125,
+			"QuerierMaxResponseTime": 100,
+			"LastMemberResponseTime": 10,
+			//0 means false
+			"UnauthorizedJoinBehaviour": 0,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendCreateMulticastOperationProfileVar(context.TODO(),
+		oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "CreateMulticastOperationProfileVar create failed, aborting UniVlanConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("createMulticastOperationProfileVar responseError %s, error %s", oFsm.deviceID, err)
+	}
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+	//verify response
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "CreateMulticastOperationProfile create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "MulticastOperationProfileID": instID})
+		return fmt.Errorf("createMulticastOperationProfile responseError %s, error %s", oFsm.deviceID, err)
+	}
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) performSettingMulticastOperationProfile(ctx context.Context, multicastGemPortID uint16, vlanID uint32) error {
+	instID, err := cmn.GenerateUNISideMBPCDEID(uint16(oFsm.pOnuUniPort.MacBpNo))
+	if err != nil {
+		logger.Errorw(ctx, "error generating me instance id",
+			log.Fields{"device-id": oFsm.deviceID, "error": err})
+		return err
+	}
+	logger.Debugw(ctx, "UniVlanConfigFsm set MulticastOperationProfile",
+		log.Fields{"device-id": oFsm.deviceID, "EntityId": instID})
+	//TODO check that this is correct
+	// Table control
+	//setCtrl = 1
+	//rowPartId = 0
+	//test = 0
+	//rowKey = 0
+	tableCtrlStr := "0100000000000000"
+	tableCtrl := cmn.AsByteSlice(tableCtrlStr)
+	dynamicAccessCL := make([]uint8, 24)
+	copy(dynamicAccessCL, tableCtrl)
+	//Multicast GemPortId
+	binary.BigEndian.PutUint16(dynamicAccessCL[2:], multicastGemPortID)
+	// python version waits for installation of flows, see line 723 onward of
+	// brcm_openomci_onu_handler.py
+	binary.BigEndian.PutUint16(dynamicAccessCL[4:], uint16(vlanID))
+	//Source IP all to 0
+	binary.BigEndian.PutUint32(dynamicAccessCL[6:], cmn.IPToInt32(net.IPv4(0, 0, 0, 0)))
+	//TODO start and end are hardcoded, get from TP
+	// Destination IP address start of range
+	binary.BigEndian.PutUint32(dynamicAccessCL[10:], cmn.IPToInt32(net.IPv4(225, 0, 0, 0)))
+	// Destination IP address end of range
+	binary.BigEndian.PutUint32(dynamicAccessCL[14:], cmn.IPToInt32(net.IPv4(239, 255, 255, 255)))
+	//imputed group bandwidth
+	binary.BigEndian.PutUint16(dynamicAccessCL[18:], 0)
+
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"DynamicAccessControlListTable": dynamicAccessCL,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendSetMulticastOperationProfileVar(context.TODO(),
+		oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "SetMulticastOperationProfileVar set failed, aborting UniVlanConfigFsm!",
+			log.Fields{"device-id": oFsm.deviceID})
+		_ = oFsm.PAdaptFsm.PFsm.Event(VlanEvReset)
+		return fmt.Errorf("setMulticastOperationProfile responseError %s, error %s", oFsm.deviceID, err)
+	}
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+	//verify response
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "CreateMulticastOperationProfile create failed, aborting AniConfig FSM!",
+			log.Fields{"device-id": oFsm.deviceID, "MulticastOperationProfileID": instID})
+		return fmt.Errorf("createMulticastOperationProfile responseError %s, error %s", oFsm.deviceID, err)
+	}
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) createTrafficDescriptor(ctx context.Context, aMeter *voltha.OfpMeterConfig,
+	tpID uint8, uniID uint8, gemPortID uint16) error {
+	logger.Infow(ctx, "Starting create traffic descriptor", log.Fields{"device-id": oFsm.deviceID, "uniID": uniID, "tpID": tpID})
+	// uniTPKey  generate id to Traffic Descriptor ME. We need to create two of them. They should be unique. Because of that
+	// I created unique TD ID by flow direction.
+	// TODO! Traffic descriptor ME ID will check
+	trafficDescriptorID := gemPortID
+	if aMeter == nil {
+		return fmt.Errorf("meter not found %s", oFsm.deviceID)
+	}
+	trafficShapingInfo, err := meters.GetTrafficShapingInfo(ctx, aMeter)
+	if err != nil {
+		logger.Errorw(ctx, "Traffic Shaping Info get failed", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	cir := trafficShapingInfo.Cir + trafficShapingInfo.Gir
+	cbs := trafficShapingInfo.Cbs
+	pir := trafficShapingInfo.Pir
+	pbs := trafficShapingInfo.Pbs
+
+	logger.Infow(ctx, "cir-pir-cbs-pbs", log.Fields{"device-id": oFsm.deviceID, "cir": cir, "pir": pir, "cbs": cbs, "pbs": pbs})
+	meParams := me.ParamData{
+		EntityID: trafficDescriptorID,
+		Attributes: me.AttributeValueMap{
+			"Cir":                  cir,
+			"Pir":                  pir,
+			"Cbs":                  cbs,
+			"Pbs":                  pbs,
+			"ColourMode":           1,
+			"IngressColourMarking": 3,
+			"EgressColourMarking":  3,
+			"MeterType":            1,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, errCreateTD := oFsm.pOmciCC.SendCreateTDVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(),
+		true, oFsm.PAdaptFsm.CommChan, meParams)
+	if errCreateTD != nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "Traffic Descriptor create failed", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "Traffic Descriptor create failed, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+
+	// Note: in the below request the gemport entity id is same as the gemport id and the traffic descriptor entity id is also same as gemport id
+	err = oFsm.setTrafficDescriptorToGemPortNWCTP(ctx, gemPortID, gemPortID)
+	if err != nil {
+		logger.Errorw(ctx, "Traffic Descriptor set failed to Gem Port Network CTP, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	logger.Infow(ctx, "Set TD Info to GemPortNWCTP successfully", log.Fields{"device-id": oFsm.deviceID, "gem-port-id": gemPortID, "td-id": trafficDescriptorID})
+
+	return nil
+}
+
+func (oFsm *UniVlanConfigFsm) setTrafficDescriptorToGemPortNWCTP(ctx context.Context, gemPortEntityID uint16, trafficDescriptorEntityID uint16) error {
+	logger.Debugw(ctx, "Starting Set Traffic Descriptor to GemPortNWCTP",
+		log.Fields{"device-id": oFsm.deviceID, "gem-port-entity-id": gemPortEntityID, "traffic-descriptor-entity-id": trafficDescriptorEntityID})
+	meParams := me.ParamData{
+		EntityID: gemPortEntityID,
+		Attributes: me.AttributeValueMap{
+			"TrafficDescriptorProfilePointerForUpstream": trafficDescriptorEntityID,
+		},
+	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendSetGemNCTPVar(log.WithSpanFromContext(context.TODO(), ctx),
+		oFsm.pDeviceHandler.GetOmciTimeout(), true, oFsm.PAdaptFsm.CommChan, meParams)
+	if err != nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "GemNCTP set failed", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
+	err = oFsm.waitforOmciResponse(ctx)
+	if err != nil {
+		logger.Errorw(ctx, "Upstream Traffic Descriptor set failed, aborting VlanConfig FSM!", log.Fields{"device-id": oFsm.deviceID})
+		return err
+	}
+	return nil
+}
+
+// IsFlowRemovePending returns true if there are pending flows to remove, else false.
+func (oFsm *UniVlanConfigFsm) IsFlowRemovePending(aFlowDeleteChannel chan<- bool) bool {
+	oFsm.mutexFlowParams.Lock()
+	defer oFsm.mutexFlowParams.Unlock()
+	if len(oFsm.uniRemoveFlowsSlice) > 0 {
+		//flow removal is still ongoing/pending
+		oFsm.signalOnFlowDelete = true
+		oFsm.flowDeleteChannel = aFlowDeleteChannel
+		return true
+	}
+	return false
+}
+
+func (oFsm *UniVlanConfigFsm) reconcileVlanFilterList(ctx context.Context, aSetVid uint16) {
+	// VOL-4342 - reconcile vlanFilterList[] for possible later flow removal
+	if aSetVid == uint16(of.OfpVlanId_OFPVID_PRESENT) {
+		logger.Debugw(ctx, "reconciling - transparent setup: no VTFD config was required",
+			log.Fields{"device-id": oFsm.deviceID})
+	} else {
+		oFsm.vlanFilterList[oFsm.numVlanFilterEntries] = aSetVid
+		logger.Debugw(ctx, "reconciling - Vid of VTFD stored in list", log.Fields{
+			"index":     oFsm.numVlanFilterEntries,
+			"vid":       strconv.FormatInt(int64(oFsm.vlanFilterList[oFsm.numVlanFilterEntries]), 16),
+			"device-id": oFsm.deviceID})
+		oFsm.numVlanFilterEntries++
+	}
+}
diff --git a/internal/pkg/avcfg/onu_uni_tp.go b/internal/pkg/avcfg/onu_uni_tp.go
new file mode 100755
index 0000000..efb69be
--- /dev/null
+++ b/internal/pkg/avcfg/onu_uni_tp.go
@@ -0,0 +1,1011 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed 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
+ *
+ * http://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 avcfg provides anig and vlan configuration functionality
+package avcfg
+
+import (
+	"context"
+	"fmt"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+	cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
+	"github.com/opencord/voltha-protos/v5/go/tech_profile"
+)
+
+//definitions for TechProfileProcessing - copied from OltAdapter:openolt_flowmgr.go
+//  could perhaps be defined more globally
+const (
+	// binaryStringPrefix is binary string prefix
+	binaryStringPrefix = "0b"
+	// binaryBit1 is binary bit 1 expressed as a character
+	//binaryBit1 = '1'
+)
+
+// ResourceEntry - TODO: add comment
+type ResourceEntry int
+
+// TODO: add comment
+const (
+	CResourceGemPort ResourceEntry = 1
+	CResourceTcont   ResourceEntry = 2
+)
+
+type tTechProfileIndication struct {
+	techProfileType       string
+	techProfileID         uint8
+	techProfileConfigDone bool
+	techProfileToDelete   bool
+}
+
+type tcontParamStruct struct {
+	allocID     uint16
+	schedPolicy uint8
+}
+type gemPortParamStruct struct {
+	//ponOmciCC       bool
+	gemPortID       uint16
+	direction       uint8
+	gemPortEncState uint8
+	prioQueueIndex  uint8
+	pbitString      string
+	discardPolicy   string
+	//could also be a queue specific parameter, not used that way here
+	//maxQueueSize     uint16
+	queueSchedPolicy string
+	queueWeight      uint8
+	removeGemID      uint16
+	isMulticast      bool
+	//TODO check if this has any value/difference from gemPortId
+	multicastGemPortID uint16
+	staticACL          string
+	dynamicACL         string
+}
+
+//refers to one tcont and its properties and all assigned GemPorts and their properties
+type tcontGemList struct {
+	tcontParams      tcontParamStruct
+	mapGemPortParams map[uint16]*gemPortParamStruct
+}
+
+// refers a unique combination of uniID and tpID for a given ONU.
+type uniTP struct {
+	uniID uint8
+	tpID  uint8
+}
+
+//OnuUniTechProf structure holds information about the TechProfiles attached to Uni Ports of the ONU
+type OnuUniTechProf struct {
+	deviceID                 string
+	baseDeviceHandler        cmn.IdeviceHandler
+	onuDevice                cmn.IonuDeviceEntry
+	tpProcMutex              sync.RWMutex
+	chTpConfigProcessingStep chan uint8
+	mapUniTpIndication       map[uniTP]*tTechProfileIndication //use pointer values to ease assignments to the map
+	mapPonAniConfig          map[uniTP]*tcontGemList           //per UNI: use pointer values to ease assignments to the map
+	PAniConfigFsm            map[uniTP]*UniPonAniConfigFsm
+	procResult               map[uniTP]error //error indication of processing
+	mutexTPState             sync.RWMutex
+	tpProfileExists          map[uniTP]bool
+	tpProfileResetting       map[uniTP]bool
+	mapRemoveGemEntry        map[uniTP]*gemPortParamStruct //per UNI: pointer to GemEntry to be removed
+}
+
+func (onuTP *OnuUniTechProf) multicastConfiguredForOtherUniTps(ctx context.Context, uniTpKey uniTP) bool {
+	for _, aniFsm := range onuTP.PAniConfigFsm {
+		if aniFsm.uniTpKey.uniID == uniTpKey.uniID && aniFsm.uniTpKey.tpID == uniTpKey.tpID {
+			continue
+		}
+		if aniFsm.hasMulticastGem(ctx) {
+			return true
+		}
+	}
+	return false
+}
+
+//NewOnuUniTechProf returns the instance of a OnuUniTechProf
+//(one instance per ONU/deviceHandler for all possible UNI's)
+func NewOnuUniTechProf(ctx context.Context, aDeviceHandler cmn.IdeviceHandler, aOnuDev cmn.IonuDeviceEntry) *OnuUniTechProf {
+
+	var onuTP OnuUniTechProf
+	onuTP.deviceID = aDeviceHandler.GetDeviceID()
+	logger.Debugw(ctx, "init-OnuUniTechProf", log.Fields{"device-id": onuTP.deviceID})
+	onuTP.baseDeviceHandler = aDeviceHandler
+	onuTP.onuDevice = aOnuDev
+	onuTP.chTpConfigProcessingStep = make(chan uint8)
+	onuTP.mapUniTpIndication = make(map[uniTP]*tTechProfileIndication)
+	onuTP.mapPonAniConfig = make(map[uniTP]*tcontGemList)
+	onuTP.procResult = make(map[uniTP]error)
+	onuTP.tpProfileExists = make(map[uniTP]bool)
+	onuTP.tpProfileResetting = make(map[uniTP]bool)
+	onuTP.mapRemoveGemEntry = make(map[uniTP]*gemPortParamStruct)
+
+	return &onuTP
+}
+
+// LockTpProcMutex locks OnuUniTechProf processing mutex
+func (onuTP *OnuUniTechProf) LockTpProcMutex() {
+	onuTP.tpProcMutex.Lock()
+}
+
+// UnlockTpProcMutex unlocks OnuUniTechProf processing mutex
+func (onuTP *OnuUniTechProf) UnlockTpProcMutex() {
+	onuTP.tpProcMutex.Unlock()
+}
+
+// ResetTpProcessingErrorIndication resets the internal error indication
+// need to be called before evaluation of any subsequent processing (given by waitForTpCompletion())
+func (onuTP *OnuUniTechProf) ResetTpProcessingErrorIndication(aUniID uint8, aTpID uint8) {
+	onuTP.mutexTPState.Lock()
+	defer onuTP.mutexTPState.Unlock()
+	onuTP.procResult[uniTP{uniID: aUniID, tpID: aTpID}] = nil
+}
+
+// GetTpProcessingErrorIndication - TODO: add comment
+func (onuTP *OnuUniTechProf) GetTpProcessingErrorIndication(aUniID uint8, aTpID uint8) error {
+	onuTP.mutexTPState.RLock()
+	defer onuTP.mutexTPState.RUnlock()
+	return onuTP.procResult[uniTP{uniID: aUniID, tpID: aTpID}]
+}
+
+// ConfigureUniTp checks existing tp resources to configure and starts the corresponding OMCI configuation of the UNI port
+// all possibly blocking processing must be run in background to allow for deadline supervision!
+// but take care on sequential background processing when needed (logical dependencies)
+//   use waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) for internal synchronization
+func (onuTP *OnuUniTechProf) ConfigureUniTp(ctx context.Context,
+	aUniID uint8, aPathString string, tpInst tech_profile.TechProfileInstance, wg *sync.WaitGroup) {
+	defer wg.Done() //always decrement the waitGroup on return
+	logger.Debugw(ctx, "configure the Uni according to TpPath", log.Fields{
+		"device-id": onuTP.deviceID, "uni-id": aUniID, "path": aPathString})
+	tpID, err := cmn.GetTpIDFromTpPath(aPathString)
+	uniTpKey := uniTP{uniID: aUniID, tpID: tpID}
+	if err != nil {
+		logger.Errorw(ctx, "error-extracting-tp-id-from-tp-path", log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID, "path": aPathString})
+		return
+	}
+
+	//ensure that the given uniID is available (configured) in the UniPort class (used for OMCI entities)
+	var pCurrentUniPort *cmn.OnuUniPort
+	for _, uniPort := range *onuTP.baseDeviceHandler.GetUniEntityMap() {
+		// only if this port is validated for operState transfer
+		if uniPort.UniID == aUniID {
+			pCurrentUniPort = uniPort
+			break //found - end search loop
+		}
+	}
+	if pCurrentUniPort == nil {
+		logger.Errorw(ctx, "TechProfile configuration aborted: requested uniID not found in PortDB",
+			log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": uniTpKey.tpID})
+		onuTP.mutexTPState.Lock()
+		defer onuTP.mutexTPState.Unlock()
+		onuTP.procResult[uniTpKey] = fmt.Errorf("techProfile config aborted: requested uniID not found %d on %s",
+			aUniID, onuTP.deviceID)
+		return
+	}
+
+	if onuTP.getProfileResetting(uniTpKey) {
+		logger.Debugw(ctx, "aborting TP configuration, reset requested in parallel", log.Fields{
+			"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": uniTpKey.tpID})
+		onuTP.mutexTPState.Lock()
+		defer onuTP.mutexTPState.Unlock()
+		onuTP.procResult[uniTpKey] = fmt.Errorf(
+			"techProfile config aborted - reset requested in parallel - for uniID %d on %s",
+			aUniID, onuTP.deviceID)
+		return
+	}
+	var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
+
+	//according to UpdateOnuUniTpPath() logic the assumption here is, that this configuration is only called
+	//  in case the KVPath has changed for the given UNI,
+	//  as T-Cont and Gem-Id's are dependent on TechProfile-Id this means, that possibly previously existing
+	//  (ANI) configuration of this port has to be removed first
+	//  (moreover in this case a possibly existing flow configuration is also not valid anymore and needs clean-up as well)
+	//  existence of configuration can be detected based on tp stored TCONT's
+	//TODO:
+	/* if tcontMap  not empty {
+		go onuTP.deleteAniSideConfig(ctx, aUniID, processingStep)
+		if !onuTP.waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) {
+			//timeout or error detected
+			return
+		}
+		clear tcontMap
+	}
+
+	processingStep++
+	*/
+	go onuTP.readAniSideConfigFromTechProfile(ctx, aUniID, tpID, aPathString, tpInst, processingStep)
+	if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
+		//timeout or error detected
+		onuTP.mutexTPState.RLock()
+		ok := onuTP.tpProfileExists[uniTpKey]
+		onuTP.mutexTPState.RUnlock()
+		if ok {
+			//ignore the internal error in case the new profile is already configured
+			// and abort the processing here
+			return
+		}
+		logger.Errorw(ctx, "tech-profile related configuration aborted on read",
+			log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID})
+		onuTP.mutexTPState.Lock()
+		defer onuTP.mutexTPState.Unlock()
+		onuTP.procResult[uniTpKey] = fmt.Errorf("techProfile config aborted: tech-profile read issue for %d on %s",
+			aUniID, onuTP.deviceID)
+		return
+	}
+	if onuTP.getProfileResetting(uniTpKey) {
+		logger.Debugw(ctx, "aborting TP configuration, reset requested in parallel", log.Fields{
+			"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": uniTpKey.tpID})
+		onuTP.mutexTPState.Lock()
+		defer onuTP.mutexTPState.Unlock()
+		onuTP.procResult[uniTpKey] = fmt.Errorf(
+			"techProfile config aborted - reset requested in parallel - for uniID %d on %s",
+			aUniID, onuTP.deviceID)
+		return
+	}
+	processingStep++
+
+	//ensure read protection for access to mapPonAniConfig
+	onuTP.mutexTPState.RLock()
+	valuePA, existPA := onuTP.mapPonAniConfig[uniTpKey]
+	onuTP.mutexTPState.RUnlock()
+	if existPA {
+		if valuePA != nil {
+			//Config data for this uni and and at least TCont Index 0 exist
+			if err := onuTP.setAniSideConfigFromTechProfile(ctx, aUniID, tpID, pCurrentUniPort, processingStep); err != nil {
+				logger.Errorw(ctx, "tech-profile related FSM could not be started",
+					log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID})
+				onuTP.mutexTPState.Lock()
+				defer onuTP.mutexTPState.Unlock()
+				onuTP.procResult[uniTpKey] = err
+				return
+			}
+			if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
+				//timeout or error detected (included wanted cancellation after e.g. disable device (FsmReset))
+				logger.Warnw(ctx, "tech-profile related configuration aborted on set",
+					log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID})
+
+				onuTP.mutexTPState.Lock()
+				defer onuTP.mutexTPState.Unlock()
+				onuTP.procResult[uniTpKey] = fmt.Errorf("techProfile config aborted: Omci AniSideConfig failed %d on %s",
+					aUniID, onuTP.deviceID)
+				//this issue here means that the AniConfigFsm has not finished successfully
+				//which requires to reset it to allow for new usage, e.g. also on a different UNI
+				//(without that it would be reset on device down indication latest)
+				if _, ok := onuTP.PAniConfigFsm[uniTpKey]; ok {
+					_ = onuTP.PAniConfigFsm[uniTpKey].PAdaptFsm.PFsm.Event(aniEvReset)
+				}
+				return
+			}
+		} else {
+			// strange: UNI entry exists, but no ANI data, maybe such situation should be cleared up (if observed)
+			logger.Errorw(ctx, "no Tcont/Gem data for this UNI found - abort", log.Fields{
+				"device-id": onuTP.deviceID, "uni-id": aUniID})
+			onuTP.mutexTPState.Lock()
+			defer onuTP.mutexTPState.Unlock()
+			onuTP.procResult[uniTpKey] = fmt.Errorf("techProfile config aborted: no Tcont/Gem data found for this UNI %d on %s",
+				aUniID, onuTP.deviceID)
+			return
+		}
+	} else {
+		logger.Errorw(ctx, "no PonAni data for this UNI found - abort", log.Fields{
+			"device-id": onuTP.deviceID, "uni-id": aUniID})
+
+		onuTP.mutexTPState.Lock()
+		defer onuTP.mutexTPState.Unlock()
+		onuTP.procResult[uniTpKey] = fmt.Errorf("techProfile config aborted: no AniSide data found for this UNI %d on %s",
+			aUniID, onuTP.deviceID)
+		return
+	}
+}
+
+/* internal methods *********************/
+// nolint: gocyclo
+func (onuTP *OnuUniTechProf) readAniSideConfigFromTechProfile(
+	ctx context.Context, aUniID uint8, aTpID uint8, aPathString string, tpInst tech_profile.TechProfileInstance, aProcessingStep uint8) {
+	var err error
+	//store profile type and identifier for later usage within the OMCI identifier and possibly ME setup
+	//pathstring is defined to be in the form of <ProfType>/<profID>/<Interface/../Identifier>
+	subStringSlice := strings.Split(aPathString, "/")
+	if len(subStringSlice) <= 2 {
+		logger.Errorw(ctx, "invalid path name format",
+			log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+		onuTP.chTpConfigProcessingStep <- 0 //error indication
+		return
+	}
+
+	//ensure write protection for access to used maps
+	onuTP.mutexTPState.Lock()
+	defer onuTP.mutexTPState.Unlock()
+
+	uniTPKey := uniTP{uniID: aUniID, tpID: aTpID}
+	onuTP.tpProfileExists[uniTP{uniID: aUniID, tpID: aTpID}] = false
+
+	//at this point it is assumed that a new TechProfile is assigned to the UNI
+	//expectation is that no TPIndication entry exists here, if exists and with the same TPId
+	//  then we throw a warning, set an internal error and abort with error,
+	//  which is later re-defined to success response to OLT adapter
+	//  if TPId has changed, current data is removed (note that the ONU config state may be
+	// 	  ambivalent in such a case)
+	if _, existTP := onuTP.mapUniTpIndication[uniTPKey]; existTP {
+		logger.Warnw(ctx, "Some active profile entry at reading new TechProfile",
+			log.Fields{"path": aPathString, "device-id": onuTP.deviceID,
+				"uni-id": aUniID, "wrongProfile": onuTP.mapUniTpIndication[uniTPKey].techProfileID})
+		if aTpID == onuTP.mapUniTpIndication[uniTPKey].techProfileID {
+			// ProfId not changed - assume profile to be still the same
+			// anyway this should not appear after full support of profile (Gem/TCont) removal
+			logger.Warnw(ctx, "New TechProfile already exists - aborting configuration",
+				log.Fields{"device-id": onuTP.deviceID})
+			onuTP.tpProfileExists[uniTPKey] = true
+			onuTP.chTpConfigProcessingStep <- 0 //error indication
+			return
+		}
+		//delete on the mapUniTpIndication map not needed, just overwritten later
+		//delete on the PonAniConfig map should be safe, even if not existing
+		delete(onuTP.mapPonAniConfig, uniTPKey)
+	} else {
+		// this is normal processing
+		onuTP.mapUniTpIndication[uniTPKey] = &tTechProfileIndication{} //need to assign some (empty) struct memory first!
+	}
+
+	onuTP.mapUniTpIndication[uniTPKey].techProfileType = subStringSlice[0]
+	//note the limitation on ID range (probably even more limited) - based on usage within OMCI EntityID
+	onuTP.mapUniTpIndication[uniTPKey].techProfileID = aTpID
+	onuTP.mapUniTpIndication[uniTPKey].techProfileConfigDone = false
+	onuTP.mapUniTpIndication[uniTPKey].techProfileToDelete = false
+	logger.Debugw(ctx, "tech-profile path indications",
+		log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID,
+			"profType": onuTP.mapUniTpIndication[uniTPKey].techProfileType,
+			"profID":   onuTP.mapUniTpIndication[uniTPKey].techProfileID})
+
+	//default start with 1Tcont profile, later perhaps extend to MultiTcontMultiGem
+	localMapGemPortParams := make(map[uint16]*gemPortParamStruct)
+	onuTP.mapPonAniConfig[uniTPKey] = &tcontGemList{tcontParamStruct{}, localMapGemPortParams}
+
+	//note: the code is currently restricted to one TCcont per Onu (index [0])
+	//get the relevant values from the profile and store to mapPonAniConfig
+	onuTP.mapPonAniConfig[uniTPKey].tcontParams.allocID = uint16(tpInst.UsScheduler.AllocId)
+	//maybe tCont scheduling not (yet) needed - just to basically have it for future
+	//  (would only be relevant in case of ONU-2G QOS configuration flexibility)
+	if tpInst.UsScheduler.QSchedPolicy == tech_profile.SchedulingPolicy_StrictPriority {
+		onuTP.mapPonAniConfig[uniTPKey].tcontParams.schedPolicy = 1 //for the moment fixed value acc. G.988 //TODO: defines!
+	} else {
+		//default profile defines "Hybrid" - which probably comes down to WRR with some weigthts for SP
+		onuTP.mapPonAniConfig[uniTPKey].tcontParams.schedPolicy = 2 //for G.988 WRR
+	}
+	loNumGemPorts := tpInst.NumGemPorts
+	loGemPortRead := false
+	for pos, content := range tpInst.UpstreamGemPortAttributeList {
+		if uint32(pos) == loNumGemPorts {
+			logger.Debugw(ctx, "PonAniConfig abort GemPortList - GemList exceeds set NumberOfGemPorts",
+				log.Fields{"device-id": onuTP.deviceID, "index": pos, "NumGem": loNumGemPorts})
+			break
+		}
+		if pos == 0 {
+			//at least one upstream GemPort should always exist (else traffic profile makes no sense)
+			loGemPortRead = true
+		}
+		//for all GemPorts we need to extend the mapGemPortParams
+		onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)] = &gemPortParamStruct{}
+
+		onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].gemPortID =
+			uint16(content.GemportId)
+		//direction can be correlated later with Downstream list,
+		//  for now just assume bidirectional (upstream never exists alone)
+		onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].direction = 3 //as defined in G.988
+		// expected Prio-Queue values 0..7 with 7 for highest PrioQueue, QueueIndex=Prio = 0..7
+		if content.PriorityQ > 7 {
+			logger.Errorw(ctx, "PonAniConfig reject on GemPortList - PrioQueue value invalid",
+				log.Fields{"device-id": onuTP.deviceID, "index": pos, "PrioQueue": content.PriorityQ})
+			//remove PonAniConfig  as done so far, delete map should be safe, even if not existing
+			delete(onuTP.mapPonAniConfig, uniTPKey)
+			onuTP.chTpConfigProcessingStep <- 0 //error indication
+			return
+		}
+		onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].prioQueueIndex =
+			uint8(content.PriorityQ)
+		onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].pbitString =
+			strings.TrimPrefix(content.PbitMap, binaryStringPrefix)
+		if content.AesEncryption == "True" {
+			onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].gemPortEncState = 1
+		} else {
+			onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].gemPortEncState = 0
+		}
+		onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].discardPolicy =
+			content.DiscardPolicy.String()
+		onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].queueSchedPolicy =
+			content.SchedulingPolicy.String()
+		//'GemWeight' looks strange in default profile, for now we just copy the weight to first queue
+		onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[uint16(content.GemportId)].queueWeight =
+			uint8(content.Weight)
+	}
+
+	for _, downstreamContent := range tpInst.DownstreamGemPortAttributeList {
+		logger.Debugw(ctx, "Operating on Downstream Gem Port", log.Fields{"downstream-gem": downstreamContent})
+		//Commenting this out due to faliure, needs investigation
+		//if uint32(pos) == loNumGemPorts {
+		//	logger.Debugw("PonAniConfig abort GemPortList - GemList exceeds set NumberOfGemPorts",
+		//		log.Fields{"device-id": onuTP.deviceID, "index": pos, "NumGem": loNumGemPorts})
+		//	break
+		//}
+		isMulticast := false
+		//Flag is defined as string in the TP in voltha-lib-go, parsing it from string
+		if downstreamContent.IsMulticast != "" {
+			isMulticast, err = strconv.ParseBool(downstreamContent.IsMulticast)
+			if err != nil {
+				logger.Errorw(ctx, "multicast-error-config-unknown-flag-in-technology-profile",
+					log.Fields{"UniTpKey": uniTPKey, "downstream-gem": downstreamContent, "error": err})
+				continue
+			}
+		}
+		logger.Infow(ctx, "Gem Port is multicast", log.Fields{"isMulticast": isMulticast})
+		if isMulticast {
+			mcastGemID := uint16(downstreamContent.MulticastGemId)
+			_, existing := onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID]
+			if existing {
+				//GEM port was previously configured, avoid setting multicast attributes
+				logger.Errorw(ctx, "multicast-error-config-existing-gem-port-config", log.Fields{"UniTpKey": uniTPKey,
+					"downstream-gem": downstreamContent, "key": mcastGemID})
+				continue
+			} else {
+				//GEM port is not configured, setting multicast attributes
+				logger.Infow(ctx, "creating-multicast-gem-port", log.Fields{"uniTpKey": uniTPKey,
+					"gemPortId": mcastGemID, "key": mcastGemID})
+
+				//for all further GemPorts we need to extend the mapGemPortParams
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID] = &gemPortParamStruct{}
+
+				//Separate McastGemId is derived from OMCI-lib-go, if not needed first needs to be removed there.
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].gemPortID = mcastGemID
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].direction = 2 // for ANI to UNI as defined in G.988
+
+				if downstreamContent.AesEncryption == "True" {
+					onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].gemPortEncState = 1
+				} else {
+					onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].gemPortEncState = 0
+				}
+
+				// expected Prio-Queue values 0..7 with 7 for highest PrioQueue, QueueIndex=Prio = 0..7
+				if downstreamContent.PriorityQ > 7 {
+					logger.Errorw(ctx, "PonAniConfig reject on GemPortList - PrioQueue value invalid",
+						log.Fields{"device-id": onuTP.deviceID, "index": mcastGemID, "PrioQueue": downstreamContent.PriorityQ})
+					//remove PonAniConfig  as done so far, delete map should be safe, even if not existing
+					delete(onuTP.mapPonAniConfig, uniTPKey)
+					onuTP.chTpConfigProcessingStep <- 0 //error indication
+					return
+				}
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].prioQueueIndex =
+					uint8(downstreamContent.PriorityQ)
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].pbitString =
+					strings.TrimPrefix(downstreamContent.PbitMap, binaryStringPrefix)
+
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].discardPolicy =
+					downstreamContent.DiscardPolicy.String()
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].queueSchedPolicy =
+					downstreamContent.SchedulingPolicy.String()
+				//'GemWeight' looks strange in default profile, for now we just copy the weight to first queue
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].queueWeight =
+					uint8(downstreamContent.Weight)
+
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].isMulticast = isMulticast
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].multicastGemPortID =
+					uint16(downstreamContent.MulticastGemId)
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].staticACL = downstreamContent.StaticAccessControlList
+				onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams[mcastGemID].dynamicACL = downstreamContent.DynamicAccessControlList
+			}
+		}
+	}
+
+	if !loGemPortRead {
+		logger.Errorw(ctx, "PonAniConfig reject - no GemPort could be read from TechProfile",
+			log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+		//remove PonAniConfig  as done so far, delete map should be safe, even if not existing
+		delete(onuTP.mapPonAniConfig, uniTPKey)
+		onuTP.chTpConfigProcessingStep <- 0 //error indication
+		return
+	}
+	//logger does not simply output the given structures, just give some example debug values
+	logger.Debugw(ctx, "PonAniConfig read from TechProfile", log.Fields{
+		"device-id": onuTP.deviceID, "uni-id": aUniID,
+		"AllocId": onuTP.mapPonAniConfig[uniTPKey].tcontParams.allocID})
+	for gemPortID, gemEntry := range onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams {
+		logger.Debugw(ctx, "PonAniConfig read from TechProfile", log.Fields{
+			"GemPort":         gemPortID,
+			"QueueScheduling": gemEntry.queueSchedPolicy})
+	}
+
+	onuTP.chTpConfigProcessingStep <- aProcessingStep //done
+}
+
+func (onuTP *OnuUniTechProf) setAniSideConfigFromTechProfile(
+	ctx context.Context, aUniID uint8, aTpID uint8, apCurrentUniPort *cmn.OnuUniPort, aProcessingStep uint8) error {
+
+	//OMCI transfer of ANI data acc. to mapPonAniConfig
+	// also the FSM's are running in background,
+	//   hence we have to make sure they indicate 'success' on chTpConfigProcessingStep with aProcessingStep
+	uniTPKey := uniTP{uniID: aUniID, tpID: aTpID}
+	if onuTP.PAniConfigFsm == nil {
+		return onuTP.createAniConfigFsm(ctx, aUniID, aTpID, apCurrentUniPort, cmn.OmciAniConfigDone, aProcessingStep)
+	} else if _, ok := onuTP.PAniConfigFsm[uniTPKey]; !ok {
+		return onuTP.createAniConfigFsm(ctx, aUniID, aTpID, apCurrentUniPort, cmn.OmciAniConfigDone, aProcessingStep)
+	}
+	//AniConfigFsm already init
+	return onuTP.runAniConfigFsm(ctx, aniEvStart, aProcessingStep, aUniID, aTpID)
+}
+
+// DeleteTpResource removes Resources from the ONU's specified Uni
+// nolint: gocyclo
+func (onuTP *OnuUniTechProf) DeleteTpResource(ctx context.Context,
+	aUniID uint8, aTpID uint8, aPathString string, aResource ResourceEntry, aEntryID uint32,
+	wg *sync.WaitGroup) {
+	defer wg.Done()
+	logger.Debugw(ctx, "will remove TP resources from ONU's UNI", log.Fields{
+		"device-id": onuTP.deviceID, "uni-id": aUniID, "path": aPathString, "Resource": aResource})
+	uniTPKey := uniTP{uniID: aUniID, tpID: aTpID}
+
+	if CResourceGemPort == aResource {
+		logger.Debugw(ctx, "remove GemPort from the list of existing ones of the TP", log.Fields{
+			"device-id": onuTP.deviceID, "uni-id": aUniID, "path": aPathString, "GemPort": aEntryID})
+
+		//ensure read protection for access to mapPonAniConfig
+		onuTP.mutexTPState.RLock()
+		// check if the requested GemPort exists in the DB, indicate it to the FSM
+		// store locally to remove it from DB later on success
+		pLocAniConfigOnUni := onuTP.mapPonAniConfig[uniTPKey]
+		if pLocAniConfigOnUni == nil {
+			onuTP.mutexTPState.RUnlock()
+			// No relevant entry exists anymore - acknowledge success
+			logger.Debugw(ctx, "AniConfig or GemEntry do not exists in DB", log.Fields{
+				"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID})
+			return
+		}
+		onuTP.mutexTPState.RUnlock()
+
+		for gemPortID, gemEntry := range pLocAniConfigOnUni.mapGemPortParams {
+			if gemPortID == uint16(aEntryID) {
+				//GemEntry to be deleted found
+				gemEntry.removeGemID = gemPortID //store the index for later removal
+				onuTP.mapRemoveGemEntry[uniTPKey] = pLocAniConfigOnUni.mapGemPortParams[gemPortID]
+				logger.Debugw(ctx, "Remove-GemEntry stored", log.Fields{
+					"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID, "GemPort": aEntryID})
+				break //abort loop, always only one GemPort to remove
+			}
+		}
+		if onuTP.mapRemoveGemEntry[uniTPKey] == nil {
+			logger.Errorw(ctx, "GemPort removal aborted - GemPort not found",
+				log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID, "GemPort": aEntryID})
+			/* Do not set some error indication to the outside system interface on delete
+			   assume there is nothing to be deleted internally and hope a new config request will recover the situation
+			 onuTP.procResult[uniTpKey] = fmt.Errorf("GemPort removal aborted: GemPort not found %d for %d on %s",
+				aEntryID, aUniID, onuTP.deviceID)
+			*/
+			return
+		}
+		if onuTP.baseDeviceHandler.IsReadyForOmciConfig() {
+			// check that the TpConfigRequest was done before
+			//   -> that is implicitly done using the AniConfigFsm,
+			//      which must be in the according state to remove something
+			if onuTP.PAniConfigFsm == nil {
+				logger.Errorw(ctx, "abort GemPort removal - no AniConfigFsm available",
+					log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID})
+				/* Do not set some error indication to the outside system interface on delete (see above)
+				onuTP.procResult[uniTpKey] = fmt.Errorf("GemPort removal aborted: no AniConfigFsm available %d on %s",
+					aUniID, onuTP.deviceID)
+				*/
+				//if the FSM is not valid, also TP related remove data should not be valid:
+				// remove GemPort from config DB
+				//ensure write protection for access to mapPonAniConfig
+				onuTP.mutexTPState.Lock()
+				delete(onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams, onuTP.mapRemoveGemEntry[uniTPKey].removeGemID)
+				// remove the removeEntry
+				delete(onuTP.mapRemoveGemEntry, uniTPKey)
+				onuTP.mutexTPState.Unlock()
+				return
+			}
+			if _, ok := onuTP.PAniConfigFsm[uniTPKey]; !ok {
+				logger.Errorw(ctx, "abort GemPort removal - no AniConfigFsm available for this uni/tp",
+					log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID})
+				/* Do not set some error indication to the outside system interface on delete (see above)
+				onuTP.procResult[uniTpKey] = fmt.Errorf("GemPort removal aborted: no AniConfigFsm available %d on %s for tpid",
+					aUniID, onuTP.deviceID, aTpID)
+				*/
+				//if the FSM is not valid, also TP related remove data should not be valid:
+				// remove GemPort from config DB
+				//ensure write protection for access to mapPonAniConfig
+				onuTP.mutexTPState.Lock()
+				delete(onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams, onuTP.mapRemoveGemEntry[uniTPKey].removeGemID)
+				// remove the removeEntry
+				delete(onuTP.mapRemoveGemEntry, uniTPKey)
+				onuTP.mutexTPState.Unlock()
+				return
+			}
+			if onuTP.getProfileResetting(uniTPKey) {
+				logger.Debugw(ctx, "aborting GemRemoval on FSM, reset requested in parallel", log.Fields{
+					"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID})
+				//ensure write protection for access to mapPonAniConfig
+				onuTP.mutexTPState.Lock()
+				delete(onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams, onuTP.mapRemoveGemEntry[uniTPKey].removeGemID)
+				// remove the removeEntry
+				delete(onuTP.mapRemoveGemEntry, uniTPKey)
+				onuTP.mutexTPState.Unlock()
+				return
+			}
+			// initiate OMCI GemPort related removal
+			var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
+			//   hence we have to make sure they indicate 'success' on chTpConfigProcessingStep with aProcessingStep
+			if nil != onuTP.runAniConfigFsm(ctx, aniEvRemGemiw, processingStep, aUniID, aTpID) {
+				//even if the FSM invocation did not work we don't indicate a problem within procResult
+				//errors could exist also because there was nothing to delete - so we just accept that as 'deleted'
+				//TP related data cleared by FSM error treatment or re-used by FSM error-recovery (if implemented)
+				return
+			}
+			if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
+				//timeout or error detected (included wanted cancellation after e.g. disable device (FsmReset))
+				logger.Warnw(ctx, "GemPort removal aborted - Omci AniSideConfig failed",
+					log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID})
+				//even if the FSM delete execution did not work we don't indicate a problem within procResult
+				//we should never respond to delete with error ...
+				//this issue here means that the AniConfigFsm has not finished successfully
+				//which requires to reset it to allow for new usage, e.g. also on a different UNI
+				//(without that it would be reset on device down indication latest)
+				if _, ok := onuTP.PAniConfigFsm[uniTPKey]; ok {
+					_ = onuTP.PAniConfigFsm[uniTPKey].PAdaptFsm.PFsm.Event(aniEvReset)
+				}
+				//TP related data cleared by FSM error treatment or re-used by FSM error-recovery (if implemented)
+				return
+			}
+		} else {
+			//if we can't do the OMCI processing we also suppress the ProcStatusUpdate
+			//this is needed as in the device-down case where all FSM's are getting reset and internal data gets cleared
+			//as a consequence a possible remove-flow does not see any dependency on the TechProfile anymore and is executed (pro forma) directly
+			//a later TechProfile removal would cause the device-reason to be updated to 'techProfile-delete-success' which is not the expected state
+			// and anyway is no real useful information at that stage
+			logger.Debugw(ctx, "UniPonAniConfigFsm delete Gem on OMCI skipped based on device state", log.Fields{
+				"device-id": onuTP.deviceID, "device-state": onuTP.baseDeviceHandler.GetDeviceReasonString()})
+		}
+		// remove GemPort from config DB
+		//ensure write protection for access to mapPonAniConfig
+		logger.Debugw(ctx, "UniPonAniConfigFsm removing gem from config data and clearing ani FSM", log.Fields{
+			"device-id": onuTP.deviceID, "gem-id": onuTP.mapRemoveGemEntry[uniTPKey].removeGemID, "uniTPKey": uniTPKey})
+		onuTP.mutexTPState.Lock()
+		delete(onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams, onuTP.mapRemoveGemEntry[uniTPKey].removeGemID)
+		// remove the removeEntry
+		delete(onuTP.mapRemoveGemEntry, uniTPKey)
+		onuTP.mutexTPState.Unlock()
+	} else { //if CResourceTcont == aResource {
+		logger.Debugw(ctx, "reset TCont with AllocId", log.Fields{
+			"device-id": onuTP.deviceID, "uni-id": aUniID, "path": aPathString, "allocId": aEntryID})
+
+		//ensure read protection for access to mapPonAniConfig
+		onuTP.mutexTPState.RLock()
+		// check if the TCont with the indicated AllocId exists in the DB, indicate its EntityId to the FSM
+		pLocAniConfigOnUni := onuTP.mapPonAniConfig[uniTPKey]
+		if pLocAniConfigOnUni == nil {
+			// No relevant entry exists anymore - acknowledge success
+			onuTP.mutexTPState.RUnlock()
+			logger.Debugw(ctx, "AniConfig or TCont entry do not exists in DB", log.Fields{
+				"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID})
+			return
+		}
+		onuTP.mutexTPState.RUnlock()
+
+		if pLocAniConfigOnUni.tcontParams.allocID != uint16(aEntryID) {
+			logger.Errorw(ctx, "TCont removal aborted - indicated AllocId not found",
+				log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID, "AllocId": aEntryID})
+			/* Do not set some error indication to the outside system interface on delete
+			   assume there is nothing to be deleted internally and hope a new config request will recover the situation
+			 onuTP.procResult[uniTpKey] = fmt.Errorf("TCont removal aborted: AllocId not found %d for %d on %s",
+				aEntryID, aUniID, onuTP.deviceID)
+			*/
+			return
+		}
+		//T-Cont to be reset found
+		logger.Debugw(ctx, "Reset-T-Cont AllocId found - valid", log.Fields{
+			"device-id": onuTP.deviceID, "uni-id": aUniID, "AllocId": aEntryID})
+		if onuTP.PAniConfigFsm == nil {
+			logger.Errorw(ctx, "no TCont removal on OMCI - no AniConfigFsm available",
+				log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID})
+			/* Do not set some error indication to the outside system interface on delete (see above)
+			onuTP.procResult[uniTpKey] = fmt.Errorf("TCont cleanup aborted: no AniConfigFsm available %d on %s",
+				aUniID, onuTP.deviceID)
+			*/
+			return
+		}
+		if _, ok := onuTP.PAniConfigFsm[uniTPKey]; !ok {
+			logger.Errorw(ctx, "no TCont removal on OMCI - no AniConfigFsm available for this uni/tp",
+				log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID})
+			//even if the FSM invocation did not work we don't indicate a problem within procResult
+			//errors could exist also because there was nothing to delete - so we just accept that as 'deleted'
+			//if the FSM is not valid, also TP related data should not be valid - clear the internal store profile data
+			return
+		}
+		if onuTP.baseDeviceHandler.IsReadyForOmciConfig() {
+			// check that the TpConfigRequest was done before
+			//   -> that is implicitly done using the AniConfigFsm,
+			//      which must be in the according state to remove something
+			if onuTP.getProfileResetting(uniTPKey) {
+				logger.Debugw(ctx, "aborting TCont removal on FSM, reset requested in parallel", log.Fields{
+					"device-id": onuTP.deviceID, "uni-id": aUniID, "tp-id": aTpID})
+				return
+			}
+			// initiate OMCI TCont related cleanup
+			var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
+			//   hence we have to make sure they indicate 'success' on chTpConfigProcessingStep with aProcessingStep
+			if nil != onuTP.runAniConfigFsm(ctx, aniEvRemTcontPath, processingStep, aUniID, aTpID) {
+				//even if the FSM invocation did not work we don't indicate a problem within procResult
+				//errors could exist also because there was nothing to delete - so we just accept that as 'deleted'
+				//TP related data cleared by FSM error treatment or re-used by FSM error-recovery (if implemented)
+				return
+			}
+			if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
+				//timeout or error detected (included wanted cancellation after e.g. disable device (FsmReset))
+				logger.Warnw(ctx, "TCont cleanup aborted - Omci AniSideConfig failed",
+					log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID})
+				//even if the FSM delete execution did not work we don't indicate a problem within procResult
+				//we should never respond to delete with error ...
+				//this issue here means that the AniConfigFsm has not finished successfully
+				//which requires to reset it to allow for new usage, e.g. also on a different UNI
+				//(without that it would be reset on device down indication latest)
+				if _, ok := onuTP.PAniConfigFsm[uniTPKey]; ok {
+					_ = onuTP.PAniConfigFsm[uniTPKey].PAdaptFsm.PFsm.Event(aniEvReset)
+				}
+				//TP related data cleared by FSM error treatment or re-used by FSM error-recovery (if implemented)
+				return
+			}
+		} else {
+			//see gemPort comments
+			logger.Debugw(ctx, "UniPonAniConfigFsm TCont cleanup on OMCI skipped based on device state", log.Fields{
+				"device-id": onuTP.deviceID, "device-state": onuTP.baseDeviceHandler.GetDeviceReasonString()})
+		}
+	}
+
+}
+
+// IsTechProfileConfigCleared - TODO: add comment
+func (onuTP *OnuUniTechProf) IsTechProfileConfigCleared(ctx context.Context, uniID uint8, tpID uint8) bool {
+	uniTPKey := uniTP{uniID: uniID, tpID: tpID}
+	logger.Debugw(ctx, "IsTechProfileConfigCleared", log.Fields{"device-id": onuTP.deviceID})
+	if onuTP.mapPonAniConfig[uniTPKey] != nil {
+		mapGemPortParams := onuTP.mapPonAniConfig[uniTPKey].mapGemPortParams
+		unicastGemCount := 0
+		for _, gemEntry := range mapGemPortParams {
+			if !gemEntry.isMulticast {
+				unicastGemCount++
+			}
+		}
+		if unicastGemCount == 0 || onuTP.mapPonAniConfig[uniTPKey].tcontParams.allocID == 0 {
+			logger.Debugw(ctx, "clearing-ani-side-config", log.Fields{
+				"device-id": onuTP.deviceID, "uniTpKey": uniTPKey})
+			onuTP.clearAniSideConfig(ctx, uniID, tpID)
+			if _, ok := onuTP.PAniConfigFsm[uniTPKey]; ok {
+				_ = onuTP.PAniConfigFsm[uniTPKey].PAdaptFsm.PFsm.Event(aniEvReset)
+			}
+			go onuTP.baseDeviceHandler.DeviceProcStatusUpdate(ctx, cmn.OmciAniResourceRemoved)
+			return true
+		}
+	}
+	return false
+}
+
+func (onuTP *OnuUniTechProf) waitForTimeoutOrCompletion(
+	ctx context.Context, aChTpProcessingStep <-chan uint8, aProcessingStep uint8) bool {
+	select {
+	case <-ctx.Done():
+		logger.Warnw(ctx, "processing not completed in-time: force release of TpProcMutex!",
+			log.Fields{"device-id": onuTP.deviceID, "error": ctx.Err()})
+		return false
+	case rxStep := <-aChTpProcessingStep:
+		if rxStep == aProcessingStep {
+			return true
+		}
+		//all other values are not accepted - including 0 for error indication
+		logger.Warnw(ctx, "Invalid processing step received: abort and force release of TpProcMutex!",
+			log.Fields{"device-id": onuTP.deviceID,
+				"wantedStep": aProcessingStep, "haveStep": rxStep})
+		return false
+	}
+}
+
+// createAniConfigFsm initializes and runs the AniConfig FSM to transfer the OMCI related commands for ANI side configuration
+func (onuTP *OnuUniTechProf) createAniConfigFsm(ctx context.Context, aUniID uint8, aTpID uint8,
+	apCurrentUniPort *cmn.OnuUniPort, devEvent cmn.OnuDeviceEvent, aProcessingStep uint8) error {
+	logger.Debugw(ctx, "createAniConfigFsm", log.Fields{"device-id": onuTP.deviceID})
+	chAniConfigFsm := make(chan cmn.Message, 2048)
+	uniTPKey := uniTP{uniID: aUniID, tpID: aTpID}
+	if onuTP.onuDevice == nil {
+		logger.Errorw(ctx, "No valid OnuDevice - aborting", log.Fields{"device-id": onuTP.deviceID})
+		return fmt.Errorf("no valid OnuDevice: %s", onuTP.deviceID)
+	}
+	pAniCfgFsm := NewUniPonAniConfigFsm(ctx, onuTP.onuDevice.GetDevOmciCC(), apCurrentUniPort, onuTP,
+		onuTP.onuDevice.GetOnuDB(), aTpID, devEvent,
+		"AniConfigFsm", onuTP.baseDeviceHandler, onuTP.onuDevice, chAniConfigFsm)
+	if pAniCfgFsm == nil {
+		logger.Errorw(ctx, "AniConfigFSM could not be created - abort!!", log.Fields{"device-id": onuTP.deviceID})
+		return fmt.Errorf("could not create AniConfigFSM: %s", onuTP.deviceID)
+	}
+	if onuTP.PAniConfigFsm == nil {
+		onuTP.PAniConfigFsm = make(map[uniTP]*UniPonAniConfigFsm)
+	}
+	onuTP.PAniConfigFsm[uniTPKey] = pAniCfgFsm
+	return onuTP.runAniConfigFsm(ctx, aniEvStart, aProcessingStep, aUniID, aTpID)
+}
+
+// runAniConfigFsm starts the AniConfig FSM to transfer the OMCI related commands for  ANI side configuration
+func (onuTP *OnuUniTechProf) runAniConfigFsm(ctx context.Context, aEvent string, aProcessingStep uint8, aUniID uint8, aTpID uint8) error {
+	/*  Uni related ANI config procedure -
+	 ***** should run via 'aniConfigDone' state and generate the argument requested event *****
+	 */
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+
+	pACStatemachine := onuTP.PAniConfigFsm[uniTpKey].PAdaptFsm.PFsm
+	if pACStatemachine != nil {
+		if aEvent == aniEvStart {
+			if !pACStatemachine.Is(aniStDisabled) {
+				logger.Errorw(ctx, "wrong state of AniConfigFSM to start - want: Disabled", log.Fields{
+					"have": pACStatemachine.Current(), "device-id": onuTP.deviceID})
+				// maybe try a FSM reset and then again ... - TODO: add comment!!!
+				return fmt.Errorf("wrong state of AniConfigFSM to start: %s", onuTP.deviceID)
+			}
+		} else if !pACStatemachine.Is(aniStConfigDone) {
+			logger.Errorw(ctx, "wrong state of AniConfigFSM to remove - want: ConfigDone", log.Fields{
+				"have": pACStatemachine.Current(), "device-id": onuTP.deviceID})
+			return fmt.Errorf("wrong state of AniConfigFSM to remove: %s", onuTP.deviceID)
+		}
+		//FSM init requirement to get informed about FSM completion! (otherwise timeout of the TechProf config)
+		onuTP.PAniConfigFsm[uniTpKey].setFsmCompleteChannel(onuTP.chTpConfigProcessingStep, aProcessingStep)
+		if err := pACStatemachine.Event(aEvent); err != nil {
+			logger.Errorw(ctx, "AniConfigFSM: can't trigger event", log.Fields{"err": err})
+			return fmt.Errorf("can't trigger event in AniConfigFSM: %s", onuTP.deviceID)
+		}
+		/***** AniConfigFSM event notified */
+		logger.Debugw(ctx, "AniConfigFSM event notified", log.Fields{
+			"state": pACStatemachine.Current(), "device-id": onuTP.deviceID, "event": aEvent})
+		return nil
+	}
+	logger.Errorw(ctx, "AniConfigFSM StateMachine invalid - cannot be executed!!", log.Fields{"device-id": onuTP.deviceID})
+	// maybe try a FSM reset and then again ... - TODO: add comment!!!
+	return fmt.Errorf("stateMachine AniConfigFSM invalid: %s", onuTP.deviceID)
+}
+
+// clearAniSideConfig deletes internal TechProfile related data connected to the requested UniPort and TpID
+func (onuTP *OnuUniTechProf) clearAniSideConfig(ctx context.Context, aUniID uint8, aTpID uint8) {
+	logger.Debugw(ctx, "removing TpIndication and PonAniConfig data", log.Fields{
+		"device-id": onuTP.deviceID, "uni-id": aUniID})
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+
+	onuTP.mutexTPState.Lock()
+	defer onuTP.mutexTPState.Unlock()
+	//deleting a map entry should be safe, even if not existing
+	delete(onuTP.mapUniTpIndication, uniTpKey)
+	delete(onuTP.mapPonAniConfig, uniTpKey)
+	delete(onuTP.procResult, uniTpKey)
+	delete(onuTP.tpProfileExists, uniTpKey)
+	delete(onuTP.tpProfileResetting, uniTpKey)
+}
+
+// setConfigDone sets the requested techProfile config state (if possible)
+func (onuTP *OnuUniTechProf) setConfigDone(aUniID uint8, aTpID uint8, aState bool) {
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+	onuTP.mutexTPState.Lock()
+	defer onuTP.mutexTPState.Unlock()
+	if _, existTP := onuTP.mapUniTpIndication[uniTpKey]; existTP {
+		onuTP.mapUniTpIndication[uniTpKey].techProfileConfigDone = aState
+	} //else: the state is just ignored (does not exist)
+}
+
+// getTechProfileDone checks if the Techprofile processing with the requested TechProfile ID was done
+func (onuTP *OnuUniTechProf) getTechProfileDone(ctx context.Context, aUniID uint8, aTpID uint8) bool {
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+	onuTP.mutexTPState.RLock()
+	defer onuTP.mutexTPState.RUnlock()
+	if _, existTP := onuTP.mapUniTpIndication[uniTpKey]; existTP {
+		if onuTP.mapUniTpIndication[uniTpKey].techProfileID == aTpID {
+			if onuTP.mapUniTpIndication[uniTpKey].techProfileToDelete {
+				logger.Debugw(ctx, "TechProfile not relevant for requested flow config - waiting on delete",
+					log.Fields{"device-id": onuTP.deviceID, "uni-id": aUniID})
+				return false //still waiting for removal of this techProfile first
+			}
+			return onuTP.mapUniTpIndication[uniTpKey].techProfileConfigDone
+		}
+	}
+	//for all other constellations indicate false = Config not done
+	return false
+}
+
+// SetProfileToDelete sets the requested techProfile toDelete state (if possible)
+func (onuTP *OnuUniTechProf) SetProfileToDelete(aUniID uint8, aTpID uint8, aState bool) {
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+	onuTP.mutexTPState.Lock()
+	defer onuTP.mutexTPState.Unlock()
+	if _, existTP := onuTP.mapUniTpIndication[uniTpKey]; existTP {
+		onuTP.mapUniTpIndication[uniTpKey].techProfileToDelete = aState
+	} //else: the state is just ignored (does not exist)
+}
+
+func (onuTP *OnuUniTechProf) getMulticastGemPorts(ctx context.Context, aUniID uint8, aTpID uint8) []uint16 {
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+	onuTP.mutexTPState.RLock()
+	defer onuTP.mutexTPState.RUnlock()
+	gemPortIds := make([]uint16, 0)
+	if techProfile, existTP := onuTP.mapPonAniConfig[uniTpKey]; existTP {
+		for _, gemPortParam := range techProfile.mapGemPortParams {
+			if gemPortParam.isMulticast {
+				logger.Debugw(ctx, "Detected multicast gemPort", log.Fields{"device-id": onuTP.deviceID,
+					"aUniID": aUniID, "aTPID": aTpID, "uniTPKey": uniTpKey,
+					"mcastGemId": gemPortParam.multicastGemPortID})
+				gemPortIds = append(gemPortIds, gemPortParam.multicastGemPortID)
+			}
+		}
+	} //else: the state is just ignored (does not exist)
+	return gemPortIds
+}
+
+func (onuTP *OnuUniTechProf) getBidirectionalGemPortIDsForTP(ctx context.Context, aUniID uint8, aTpID uint8) []uint16 {
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+	onuTP.mutexTPState.RLock()
+	defer onuTP.mutexTPState.RUnlock()
+	gemPortIds := make([]uint16, 0)
+	if techProfile, existTP := onuTP.mapPonAniConfig[uniTpKey]; existTP {
+		logger.Debugw(ctx, "TechProfile exist", log.Fields{"device-id": onuTP.deviceID})
+		for _, gemPortParam := range techProfile.mapGemPortParams {
+			if !gemPortParam.isMulticast {
+				logger.Debugw(ctx, "Detected unicast gemPort", log.Fields{"device-id": onuTP.deviceID,
+					"aUniID": aUniID, "aTPID": aTpID, "uniTPKey": uniTpKey,
+					"GemId": gemPortParam.multicastGemPortID})
+				gemPortIds = append(gemPortIds, gemPortParam.gemPortID)
+			}
+		}
+	} else {
+		logger.Debugw(ctx, "TechProfile doesn't exist", log.Fields{"device-id": onuTP.deviceID})
+	} //else: the state is just ignored (does not exist)
+	logger.Debugw(ctx, "Gem PortID list", log.Fields{"device-id": onuTP.deviceID, "gemportList": gemPortIds})
+	return gemPortIds
+}
+
+// GetAllBidirectionalGemPortIDsForOnu - TODO: add comment
+func (onuTP *OnuUniTechProf) GetAllBidirectionalGemPortIDsForOnu() []uint16 {
+	var gemPortInstIDs []uint16
+	onuTP.mutexTPState.RLock()
+	defer onuTP.mutexTPState.RUnlock()
+	for _, tcontGemList := range onuTP.mapPonAniConfig {
+		for gemPortID, gemPortData := range tcontGemList.mapGemPortParams {
+			if gemPortData != nil && !gemPortData.isMulticast { // only if not multicast gem port
+				gemPortInstIDs = append(gemPortInstIDs, gemPortID)
+			}
+		}
+	}
+	return gemPortInstIDs
+}
+
+// setProfileResetting sets/resets the indication, that a reset of the TechProfileConfig/Removal is ongoing
+func (onuTP *OnuUniTechProf) setProfileResetting(ctx context.Context, aUniID uint8, aTpID uint8, aState bool) {
+	uniTpKey := uniTP{uniID: aUniID, tpID: aTpID}
+	onuTP.mutexTPState.Lock()
+	defer onuTP.mutexTPState.Unlock()
+	onuTP.tpProfileResetting[uniTpKey] = aState
+}
+
+// getProfileResetting returns true, if the the according indication for started reset procedure is set
+func (onuTP *OnuUniTechProf) getProfileResetting(aUniTpKey uniTP) bool {
+	onuTP.mutexTPState.RLock()
+	defer onuTP.mutexTPState.RUnlock()
+	if isResetting, exist := onuTP.tpProfileResetting[aUniTpKey]; exist {
+		return isResetting
+	}
+	return false
+}