[VOL-1349] EPON ONU adapter (package B)

Change-Id: I609ba349c429bc7e87c74b66bb1121841f9caef6
diff --git a/internal/pkg/onuadaptercore/common.go b/internal/pkg/onuadaptercore/common.go
new file mode 100644
index 0000000..3616281
--- /dev/null
+++ b/internal/pkg/onuadaptercore/common.go
@@ -0,0 +1,32 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+var logger log.Logger
+
+func init() {
+	var err error
+	logger, err = log.AddPackage(log.JSON, log.ErrorLevel, log.Fields{"pkg": "adaptercoreonu"})
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
new file mode 100644
index 0000000..9d00d5a
--- /dev/null
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -0,0 +1,1865 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"strconv"
+	"sync"
+	"time"
+
+	"github.com/gogo/protobuf/proto"
+	"github.com/golang/protobuf/ptypes"
+	"github.com/looplab/fsm"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+	"github.com/opencord/voltha-lib-go/v3/pkg/db"
+	flow "github.com/opencord/voltha-lib-go/v3/pkg/flows"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+	vc "github.com/opencord/voltha-protos/v3/go/common"
+	ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+	"github.com/opencord/voltha-protos/v3/go/openflow_13"
+	of "github.com/opencord/voltha-protos/v3/go/openflow_13"
+	ofp "github.com/opencord/voltha-protos/v3/go/openflow_13"
+	oop "github.com/opencord/voltha-protos/v3/go/openolt"
+	"github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+/*
+// Constants for number of retries and for timeout
+const (
+	MaxRetry       = 10
+	MaxTimeOutInMs = 500
+)
+*/
+
+const (
+	devEvDeviceInit       = "devEvDeviceInit"
+	devEvGrpcConnected    = "devEvGrpcConnected"
+	devEvGrpcDisconnected = "devEvGrpcDisconnected"
+	devEvDeviceUpInd      = "devEvDeviceUpInd"
+	devEvDeviceDownInd    = "devEvDeviceDownInd"
+)
+const (
+	devStNull      = "devStNull"
+	devStDown      = "devStDown"
+	devStInit      = "devStInit"
+	devStConnected = "devStConnected"
+	devStUp        = "devStUp"
+)
+
+//Event category and subcategory definitions - same as defiend for OLT in eventmgr.go  - should be done more centrally
+const (
+	pon = voltha.EventSubCategory_PON
+	equipment = voltha.EventCategory_EQUIPMENT
+)
+
+const (
+	cEventObjectType = "ONU"
+)
+const (
+	cOnuActivatedEvent = "ONU_ACTIVATED"
+)
+
+//deviceHandler will interact with the ONU ? device.
+type deviceHandler struct {
+	deviceID         string
+	DeviceType       string
+	adminState       string
+	device           *voltha.Device
+	logicalDeviceID  string
+	ProxyAddressID   string
+	ProxyAddressType string
+	parentID         string
+	ponPortNumber    uint32
+
+	coreProxy    adapterif.CoreProxy
+	AdapterProxy adapterif.AdapterProxy
+	EventProxy   adapterif.EventProxy
+
+	pOpenOnuAc      *OpenONUAC
+	pDeviceStateFsm *fsm.FSM
+	deviceEntrySet  chan bool //channel for DeviceEntry set event
+	pOnuOmciDevice  *OnuDeviceEntry
+	pOnuTP          *onuUniTechProf
+	exitChannel     chan int
+	lockDevice      sync.RWMutex
+	pOnuIndication  *oop.OnuIndication
+	deviceReason    string
+	pLockStateFsm   *lockStateFsm
+	pUnlockStateFsm *lockStateFsm
+
+
+	stopCollector       chan bool
+	stopHeartbeatCheck  chan bool
+	activePorts         sync.Map
+	uniEntityMap        map[uint32]*onuUniPort
+	UniVlanConfigFsmMap map[uint8]*UniVlanConfigFsm
+	reconciling         bool
+}
+
+//newDeviceHandler creates a new device handler
+func newDeviceHandler(cp adapterif.CoreProxy, ap adapterif.AdapterProxy, ep adapterif.EventProxy, device *voltha.Device, adapter *OpenONUAC) *deviceHandler {
+	var dh deviceHandler
+	dh.coreProxy = cp
+	dh.AdapterProxy = ap
+	dh.EventProxy = ep
+	cloned := (proto.Clone(device)).(*voltha.Device)
+	dh.deviceID = cloned.Id
+	dh.DeviceType = cloned.Type
+	dh.adminState = "up"
+	dh.device = cloned
+	dh.pOpenOnuAc = adapter
+	dh.exitChannel = make(chan int, 1)
+	dh.lockDevice = sync.RWMutex{}
+	dh.deviceEntrySet = make(chan bool, 1)
+	dh.stopCollector = make(chan bool, 2)
+	dh.stopHeartbeatCheck = make(chan bool, 2)
+	dh.activePorts = sync.Map{}
+	dh.uniEntityMap = make(map[uint32]*onuUniPort)
+	dh.UniVlanConfigFsmMap = make(map[uint8]*UniVlanConfigFsm)
+	dh.reconciling = false
+
+	dh.pDeviceStateFsm = fsm.NewFSM(
+		devStNull,
+		fsm.Events{
+			{Name: devEvDeviceInit, Src: []string{devStNull, devStDown}, Dst: devStInit},
+			{Name: devEvGrpcConnected, Src: []string{devStInit}, Dst: devStConnected},
+			{Name: devEvGrpcDisconnected, Src: []string{devStConnected, devStDown}, Dst: devStInit},
+			{Name: devEvDeviceUpInd, Src: []string{devStConnected, devStDown}, Dst: devStUp},
+			{Name: devEvDeviceDownInd, Src: []string{devStUp}, Dst: devStDown},
+		},
+		fsm.Callbacks{
+			"before_event":                      func(e *fsm.Event) { dh.logStateChange(e) },
+			("before_" + devEvDeviceInit):       func(e *fsm.Event) { dh.doStateInit(e) },
+			("after_" + devEvDeviceInit):        func(e *fsm.Event) { dh.postInit(e) },
+			("before_" + devEvGrpcConnected):    func(e *fsm.Event) { dh.doStateConnected(e) },
+			("before_" + devEvGrpcDisconnected): func(e *fsm.Event) { dh.doStateInit(e) },
+			("after_" + devEvGrpcDisconnected):  func(e *fsm.Event) { dh.postInit(e) },
+			("before_" + devEvDeviceUpInd):      func(e *fsm.Event) { dh.doStateUp(e) },
+			("before_" + devEvDeviceDownInd):    func(e *fsm.Event) { dh.doStateDown(e) },
+		},
+	)
+
+	return &dh
+}
+
+// start save the device to the data model
+func (dh *deviceHandler) start(ctx context.Context) {
+	logger.Debugw("starting-device-handler", log.Fields{"device": dh.device, "device-id": dh.deviceID})
+	logger.Debug("device-handler-started")
+}
+
+/*
+// stop stops the device dh.  Not much to do for now
+func (dh *deviceHandler) stop(ctx context.Context) {
+	logger.Debug("stopping-device-handler")
+	dh.exitChannel <- 1
+}
+*/
+
+// ##########################################################################################
+// deviceHandler methods that implement the adapters interface requests ##### begin #########
+
+//adoptOrReconcileDevice adopts the OLT device
+func (dh *deviceHandler) adoptOrReconcileDevice(ctx context.Context, device *voltha.Device) {
+	logger.Debugw("Adopt_or_reconcile_device", log.Fields{"device-id": device.Id, "Address": device.GetHostAndPort()})
+
+	logger.Debugw("Device FSM: ", log.Fields{"state": string(dh.pDeviceStateFsm.Current())})
+	if dh.pDeviceStateFsm.Is(devStNull) {
+		if err := dh.pDeviceStateFsm.Event(devEvDeviceInit); err != nil {
+			logger.Errorw("Device FSM: Can't go to state DeviceInit", log.Fields{"err": err})
+		}
+		logger.Debugw("Device FSM: ", log.Fields{"state": string(dh.pDeviceStateFsm.Current())})
+	} else {
+		logger.Debugw("AdoptOrReconcileDevice: Agent/device init already done", log.Fields{"device-id": device.Id})
+	}
+
+}
+
+func (dh *deviceHandler) processInterAdapterOMCIReqMessage(msg *ic.InterAdapterMessage) error {
+	msgBody := msg.GetBody()
+	omciMsg := &ic.InterAdapterOmciMessage{}
+	if err := ptypes.UnmarshalAny(msgBody, omciMsg); err != nil {
+		logger.Warnw("cannot-unmarshal-omci-msg-body", log.Fields{
+			"device-id": dh.deviceID, "error": err})
+		return err
+	}
+
+	logger.Debugw("inter-adapter-recv-omci", log.Fields{
+		"device-id": dh.deviceID, "RxOmciMessage": hex.EncodeToString(omciMsg.Message)})
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry != nil {
+		return pDevEntry.PDevOmciCC.receiveMessage(context.TODO(), omciMsg.Message)
+	}
+	logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+	return errors.New("no valid OnuDevice")
+}
+
+func (dh *deviceHandler) processInterAdapterONUIndReqMessage(msg *ic.InterAdapterMessage) error {
+	msgBody := msg.GetBody()
+	onuIndication := &oop.OnuIndication{}
+	if err := ptypes.UnmarshalAny(msgBody, onuIndication); err != nil {
+		logger.Warnw("cannot-unmarshal-onu-indication-msg-body", log.Fields{
+			"device-id": dh.deviceID, "error": err})
+		return err
+	}
+
+	onuOperstate := onuIndication.GetOperState()
+	logger.Debugw("inter-adapter-recv-onu-ind", log.Fields{"OnuId": onuIndication.GetOnuId(),
+		"AdminState": onuIndication.GetAdminState(), "OperState": onuOperstate,
+		"SNR": onuIndication.GetSerialNumber()})
+
+	if onuOperstate == "up" {
+		_ = dh.createInterface(onuIndication)
+	} else if (onuOperstate == "down") || (onuOperstate == "unreachable") {
+		_ = dh.updateInterface(onuIndication)
+	} else {
+		logger.Errorw("unknown-onu-indication operState", log.Fields{"OnuId": onuIndication.GetOnuId()})
+		return errors.New("invalidOperState")
+	}
+	return nil
+}
+
+func (dh *deviceHandler) processInterAdapterTechProfileDownloadReqMessage(
+	msg *ic.InterAdapterMessage) error {
+	if dh.pOnuTP == nil {
+		//should normally not happen ...
+		logger.Warnw("onuTechProf instance not set up for DLMsg request - ignoring request",
+			log.Fields{"device-id": dh.deviceID})
+		return errors.New("techProfile DLMsg request while onuTechProf instance not setup")
+	}
+	if (dh.deviceReason == "stopping-openomci") || (dh.deviceReason == "omci-admin-lock") {
+		// I've seen cases for this request, where the device was already stopped
+		logger.Warnw("TechProf stopped: device-unreachable", log.Fields{"device-id": dh.deviceID})
+		return errors.New("device-unreachable")
+	}
+
+	msgBody := msg.GetBody()
+	techProfMsg := &ic.InterAdapterTechProfileDownloadMessage{}
+	if err := ptypes.UnmarshalAny(msgBody, techProfMsg); err != nil {
+		logger.Warnw("cannot-unmarshal-techprof-msg-body", log.Fields{
+			"device-id": dh.deviceID, "error": err})
+		return err
+	}
+
+	dh.pOnuTP.lockTpProcMutex()
+	if bTpModify := dh.pOnuTP.updateOnuUniTpPath(techProfMsg.UniId, techProfMsg.Path); bTpModify {
+		//	if there has been some change for some uni TechProfilePath
+		//in order to allow concurrent calls to other dh instances we do not wait for execution here
+		//but doing so we can not indicate problems to the caller (who does what with that then?)
+		//by now we just assume straightforward successful execution
+		//TODO!!! Generally: In this scheme it would be good to have some means to indicate
+		//  possible problems to the caller later autonomously
+
+		// deadline context to ensure completion of background routines waited for
+		//20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
+		deadline := time.Now().Add(30 * time.Second) //allowed run time to finish before execution
+		dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+		dh.pOnuTP.resetProcessingErrorIndication()
+		var wg sync.WaitGroup
+		wg.Add(2) // for the 2 go routines to finish
+		// attention: deadline completion check and wg.Done is to be done in both routines
+		go dh.pOnuTP.configureUniTp(dctx, uint8(techProfMsg.UniId), techProfMsg.Path, &wg)
+		go dh.pOnuTP.updateOnuTpPathKvStore(dctx, &wg)
+		//the wait.. function is responsible for tpProcMutex.Unlock()
+		err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //wait for background process to finish and collect their result
+		return err
+	}
+	dh.pOnuTP.unlockTpProcMutex()
+	return nil
+}
+
+func (dh *deviceHandler) processInterAdapterDeleteGemPortReqMessage(
+	msg *ic.InterAdapterMessage) error {
+
+	if dh.pOnuTP == nil {
+		//should normally not happen ...
+		logger.Warnw("onuTechProf instance not set up for DelGem request - ignoring request",
+			log.Fields{"device-id": dh.deviceID})
+		return errors.New("techProfile DelGem request while onuTechProf instance not setup")
+	}
+
+	msgBody := msg.GetBody()
+	delGemPortMsg := &ic.InterAdapterDeleteGemPortMessage{}
+	if err := ptypes.UnmarshalAny(msgBody, delGemPortMsg); err != nil {
+		logger.Warnw("cannot-unmarshal-delete-gem-msg-body", log.Fields{
+			"device-id": dh.deviceID, "error": err})
+		return err
+	}
+
+	dh.pOnuTP.lockTpProcMutex()
+
+	deadline := time.Now().Add(10 * time.Second) //allowed run time to finish before execution
+	dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+	dh.pOnuTP.resetProcessingErrorIndication()
+	var wg sync.WaitGroup
+	wg.Add(1) // for the 1 go routine to finish
+	go dh.pOnuTP.deleteTpResource(dctx, delGemPortMsg.UniId, delGemPortMsg.TpPath,
+		cResourceGemPort, delGemPortMsg.GemPortId, &wg)
+	err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //let that also run off-line to let the IA messaging return!
+	return err
+}
+
+func (dh *deviceHandler) processInterAdapterDeleteTcontReqMessage(
+	msg *ic.InterAdapterMessage) error {
+	if dh.pOnuTP == nil {
+		//should normally not happen ...
+		logger.Warnw("onuTechProf instance not set up for DelTcont request - ignoring request",
+			log.Fields{"device-id": dh.deviceID})
+		return errors.New("techProfile DelTcont request while onuTechProf instance not setup")
+	}
+
+	msgBody := msg.GetBody()
+	delTcontMsg := &ic.InterAdapterDeleteTcontMessage{}
+	if err := ptypes.UnmarshalAny(msgBody, delTcontMsg); err != nil {
+		logger.Warnw("cannot-unmarshal-delete-tcont-msg-body", log.Fields{
+			"device-id": dh.deviceID, "error": err})
+		return err
+	}
+
+	dh.pOnuTP.lockTpProcMutex()
+	if bTpModify := dh.pOnuTP.updateOnuUniTpPath(delTcontMsg.UniId, ""); bTpModify {
+		// deadline context to ensure completion of background routines waited for
+		deadline := time.Now().Add(10 * time.Second) //allowed run time to finish before execution
+		dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+		dh.pOnuTP.resetProcessingErrorIndication()
+		var wg sync.WaitGroup
+		wg.Add(2) // for the 2 go routines to finish
+		go dh.pOnuTP.deleteTpResource(dctx, delTcontMsg.UniId, delTcontMsg.TpPath,
+			cResourceTcont, delTcontMsg.AllocId, &wg)
+		// Removal of the tcont/alloc id mapping represents the removal of the tech profile
+		go dh.pOnuTP.updateOnuTpPathKvStore(dctx, &wg)
+		//the wait.. function is responsible for tpProcMutex.Unlock()
+		err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //let that also run off-line to let the IA messaging return!
+		return err
+	}
+	dh.pOnuTP.unlockTpProcMutex()
+	return nil
+}
+
+//processInterAdapterMessage sends the proxied messages to the target device
+// If the proxy address is not found in the unmarshalled message, it first fetches the onu device for which the message
+// is meant, and then send the unmarshalled omci message to this onu
+func (dh *deviceHandler) processInterAdapterMessage(msg *ic.InterAdapterMessage) error {
+	msgID := msg.Header.Id
+	msgType := msg.Header.Type
+	fromTopic := msg.Header.FromTopic
+	toTopic := msg.Header.ToTopic
+	toDeviceID := msg.Header.ToDeviceId
+	proxyDeviceID := msg.Header.ProxyDeviceId
+	logger.Debugw("InterAdapter message header", log.Fields{"msgID": msgID, "msgType": msgType,
+		"fromTopic": fromTopic, "toTopic": toTopic, "toDeviceID": toDeviceID, "proxyDeviceID": proxyDeviceID})
+
+	switch msgType {
+	case ic.InterAdapterMessageType_OMCI_REQUEST:
+		{
+			return dh.processInterAdapterOMCIReqMessage(msg)
+		}
+	case ic.InterAdapterMessageType_ONU_IND_REQUEST:
+		{
+			return dh.processInterAdapterONUIndReqMessage(msg)
+		}
+	case ic.InterAdapterMessageType_TECH_PROFILE_DOWNLOAD_REQUEST:
+		{
+			return dh.processInterAdapterTechProfileDownloadReqMessage(msg)
+		}
+	case ic.InterAdapterMessageType_DELETE_GEM_PORT_REQUEST:
+		{
+			return dh.processInterAdapterDeleteGemPortReqMessage(msg)
+
+		}
+	case ic.InterAdapterMessageType_DELETE_TCONT_REQUEST:
+		{
+			return dh.processInterAdapterDeleteTcontReqMessage(msg)
+		}
+	default:
+		{
+			logger.Errorw("inter-adapter-unhandled-type", log.Fields{
+				"device-id": dh.deviceID, "msgType": msg.Header.Type})
+			return errors.New("unimplemented")
+		}
+	}
+}
+
+//FlowUpdateIncremental removes and/or adds the flow changes on a given device
+func (dh *deviceHandler) FlowUpdateIncremental(apOfFlowChanges *openflow_13.FlowChanges,
+	apOfGroupChanges *openflow_13.FlowGroupChanges, apFlowMetaData *voltha.FlowMetadata) error {
+
+	if apOfFlowChanges.ToRemove != nil {
+		for _, flowItem := range apOfFlowChanges.ToRemove.Items {
+			logger.Debugw("incremental flow item remove", log.Fields{"deviceId": dh.deviceID,
+				"Item": flowItem})
+		}
+	}
+	if apOfFlowChanges.ToAdd != nil {
+		for _, flowItem := range apOfFlowChanges.ToAdd.Items {
+			if flowItem.GetCookie() == 0 {
+				logger.Debugw("incremental flow add - no cookie - ignore", log.Fields{
+					"deviceId": dh.deviceID})
+				continue
+			}
+			flowInPort := flow.GetInPort(flowItem)
+			if flowInPort == uint32(of.OfpPortNo_OFPP_INVALID) {
+				logger.Errorw("flow inPort invalid", log.Fields{"deviceID": dh.deviceID})
+				return errors.New("flow inPort invalid")
+			} else if flowInPort == dh.ponPortNumber {
+				//this is some downstream flow
+				logger.Debugw("incremental flow ignore downstream", log.Fields{
+					"deviceId": dh.deviceID, "inPort": flowInPort})
+				continue
+			} else {
+				// this is the relevant upstream flow
+				var loUniPort *onuUniPort
+				if uniPort, exist := dh.uniEntityMap[flowInPort]; exist {
+					loUniPort = uniPort
+				} else {
+					logger.Errorw("flow inPort not found in UniPorts",
+						log.Fields{"deviceID": dh.deviceID, "inPort": flowInPort})
+					return fmt.Errorf("flow-parameter inPort %d not found in internal UniPorts", flowInPort)
+				}
+				flowOutPort := flow.GetOutPort(flowItem)
+				logger.Debugw("incremental flow-add port indications", log.Fields{
+					"deviceId": dh.deviceID, "inPort": flowInPort, "outPort": flowOutPort,
+					"uniPortName": loUniPort.name})
+				err := dh.addFlowItemToUniPort(flowItem, loUniPort)
+				//abort processing in error case
+				if err != nil {
+					return err
+				}
+			}
+		}
+	}
+	return nil
+}
+
+//disableDevice locks the ONU and its UNI/VEIP ports (admin lock via OMCI)
+// TODO!!! Clarify usage of this method, it is for sure not used within ONOS (OLT) device disable
+//         maybe it is obsolete by now
+func (dh *deviceHandler) disableDevice(device *voltha.Device) {
+	logger.Debugw("disable-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+
+	if dh.deviceReason != "omci-admin-lock" {
+		// disable UNI ports/ONU
+		// *** should generate UniAdminStateDone event - unrelated to DeviceProcStatusUpdate!!
+		//     here the result of the processing is not checked (trusted in background) *****
+		if dh.pLockStateFsm == nil {
+			dh.createUniLockFsm(true, UniAdminStateDone)
+		} else { //LockStateFSM already init
+			dh.pLockStateFsm.setSuccessEvent(UniAdminStateDone)
+			dh.runUniLockFsm(true)
+		}
+
+		if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "omci-admin-lock"); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-updating-reason-state", log.Fields{"device-id": dh.deviceID, "error": err})
+		}
+		dh.deviceReason = "omci-admin-lock"
+		//200604: ConnState improved to 'unreachable' (was not set in python-code), OperState 'unknown' seems to be best choice
+		if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID, voltha.ConnectStatus_UNREACHABLE,
+			voltha.OperStatus_UNKNOWN); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+		}
+
+		// update json file
+		// onustat := &OnuStatus{
+		// 	Id:           dh.deviceID,
+		// 	AdminState:   vc.AdminState_Types_name[int32(dh.device.AdminState)],
+		// 	OpeState:     vc.OperStatus_Types_name[int32(dh.device.OperStatus)],
+		// 	ConnectState: vc.ConnectStatus_Types_name[int32(dh.device.ConnectStatus)],
+		// 	MacAddress:   dh.device.MacAddress,
+		// }
+		// logger.Debugw("UpdateOnu", log.Fields{"Id": dh.deviceID})
+		// err := UpdateOnu(onustat)
+		// logger.Debugw("UpdateOnu", log.Fields{"err": err})
+	}
+}
+
+//reEnableDevice unlocks the ONU and its UNI/VEIP ports (admin unlock via OMCI)
+// TODO!!! Clarify usage of this method, compare above DisableDevice, usage may clarify resulting states
+//         maybe it is obsolete by now
+func (dh *deviceHandler) reEnableDevice(device *voltha.Device) {
+	logger.Debugw("reenable-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+
+	if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID, voltha.ConnectStatus_REACHABLE,
+		voltha.OperStatus_ACTIVE); err != nil {
+		//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+		logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+	}
+
+	if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "initial-mib-downloaded"); err != nil {
+		//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+		logger.Errorw("error-updating-reason-state", log.Fields{"device-id": dh.deviceID, "error": err})
+	}
+	dh.deviceReason = "initial-mib-downloaded"
+
+
+	if dh.pUnlockStateFsm == nil {
+		dh.createUniLockFsm(false, UniAdminStateDone)
+	} else { //UnlockStateFSM already init
+		dh.pUnlockStateFsm.setSuccessEvent(UniAdminStateDone)
+		dh.runUniLockFsm(false)
+	}
+}
+
+func (dh *deviceHandler) reconcileDeviceOnuInd() {
+	logger.Debugw("reconciling - simulate onu indication", log.Fields{"device-id": dh.deviceID})
+
+	if err := dh.pOnuTP.restoreFromOnuTpPathKvStore(context.TODO()); err != nil {
+		logger.Errorw("reconciling - restoring OnuTp-data failed - abort", log.Fields{"err": err, "device-id": dh.deviceID})
+		dh.reconciling = false
+		return
+	}
+	var onuIndication oop.OnuIndication
+	onuIndication.IntfId = dh.pOnuTP.sOnuPersistentData.PersIntfID
+	onuIndication.OnuId = dh.pOnuTP.sOnuPersistentData.PersOnuID
+	onuIndication.OperState = dh.pOnuTP.sOnuPersistentData.PersOperState
+	onuIndication.AdminState = dh.pOnuTP.sOnuPersistentData.PersAdminState
+	_ = dh.createInterface(&onuIndication)
+}
+
+func (dh *deviceHandler) reconcileDeviceTechProf() {
+	logger.Debugw("reconciling - trigger tech profile config", log.Fields{"device-id": dh.deviceID})
+
+	dh.pOnuTP.lockTpProcMutex()
+	for _, uniData := range dh.pOnuTP.sOnuPersistentData.PersUniTpPath {
+		//In order to allow concurrent calls to other dh instances we do not wait for execution here
+		//but doing so we can not indicate problems to the caller (who does what with that then?)
+		//by now we just assume straightforward successful execution
+		//TODO!!! Generally: In this scheme it would be good to have some means to indicate
+		//  possible problems to the caller later autonomously
+
+		// deadline context to ensure completion of background routines waited for
+		//20200721: 10s proved to be less in 8*8 ONU test on local vbox machine with debug, might be further adapted
+		deadline := time.Now().Add(30 * time.Second) //allowed run time to finish before execution
+		dctx, cancel := context.WithDeadline(context.Background(), deadline)
+
+		dh.pOnuTP.resetProcessingErrorIndication()
+		var wg sync.WaitGroup
+		wg.Add(1) // for the 1 go routines to finish
+		// attention: deadline completion check and wg.Done is to be done in both routines
+		go dh.pOnuTP.configureUniTp(dctx, uint8(uniData.PersUniID), uniData.PersTpPath, &wg)
+		//the wait.. function is responsible for tpProcMutex.Unlock()
+		_ = dh.pOnuTP.waitForTpCompletion(cancel, &wg) //wait for background process to finish and collect their result
+		return
+	}
+	dh.pOnuTP.unlockTpProcMutex()
+	dh.reconciling = false
+}
+
+func (dh *deviceHandler) deleteDevice(device *voltha.Device) error {
+	logger.Debugw("delete-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+	if err := dh.pOnuTP.deleteOnuTpPathKvStore(context.TODO()); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (dh *deviceHandler) rebootDevice(device *voltha.Device) error {
+	logger.Debugw("reboot-device", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+	if device.ConnectStatus != voltha.ConnectStatus_REACHABLE {
+		logger.Errorw("device-unreachable", log.Fields{"device-id": device.Id, "SerialNumber": device.SerialNumber})
+		return errors.New("device-unreachable")
+	}
+	if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID, voltha.ConnectStatus_UNREACHABLE,
+		voltha.OperStatus_DISCOVERED); err != nil {
+		//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+		logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+		return err
+	}
+	if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "rebooting-onu"); err != nil {
+		//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+		logger.Errorw("error-updating-reason-state", log.Fields{"device-id": dh.deviceID, "error": err})
+		return err
+	}
+	dh.deviceReason = "rebooting-onu"
+
+	if err := dh.sendMsgToOlt(context.TODO(), "reboot", nil); err != nil {
+		logger.Debugw("error", log.Fields{"error": err})
+	}
+
+	return nil
+}
+
+//  deviceHandler methods that implement the adapters interface requests## end #########
+// #####################################################################################
+
+// ################  to be updated acc. needs of ONU Device ########################
+// deviceHandler StateMachine related state transition methods ##### begin #########
+
+func (dh *deviceHandler) logStateChange(e *fsm.Event) {
+	logger.Debugw("Device FSM: ", log.Fields{"event name": string(e.Event), "src state": string(e.Src), "dst state": string(e.Dst), "device-id": dh.deviceID})
+}
+
+// doStateInit provides the device update to the core
+func (dh *deviceHandler) doStateInit(e *fsm.Event) {
+
+	logger.Debug("doStateInit-started")
+	var err error
+
+	dh.device.Root = false
+	dh.device.Vendor = "OpenONU"
+	dh.device.Model = "go"
+	dh.device.Reason = "activating-onu"
+	dh.deviceReason = "activating-onu"
+
+	dh.logicalDeviceID = dh.deviceID // really needed - what for ??? //TODO!!!
+
+	if !dh.reconciling {
+		_ = dh.coreProxy.DeviceUpdate(context.TODO(), dh.device)
+	} else {
+		logger.Debugw("reconciling - don't notify core about DeviceUpdate",
+			log.Fields{"device-id": dh.deviceID})
+	}
+
+	dh.parentID = dh.device.ParentId
+	dh.ponPortNumber = dh.device.ParentPortNo
+
+	dh.ProxyAddressID = dh.device.ProxyAddress.GetDeviceId()
+	dh.ProxyAddressType = dh.device.ProxyAddress.GetDeviceType()
+	logger.Debugw("device-updated", log.Fields{"device-id": dh.deviceID, "proxyAddressID": dh.ProxyAddressID,
+		"proxyAddressType": dh.ProxyAddressType, "SNR": dh.device.SerialNumber,
+		"ParentId": dh.parentID, "ParentPortNo": dh.ponPortNumber})
+
+	/*
+		self._pon = PonPort.create(self, self._pon_port_number)
+		self._pon.add_peer(self.parent_id, self._pon_port_number)
+		self.logger.debug('adding-pon-port-to-agent',
+				   type=self._pon.get_port().type,
+				   admin_state=self._pon.get_port().admin_state,
+				   oper_status=self._pon.get_port().oper_status,
+				   )
+	*/
+	if !dh.reconciling {
+		logger.Debugw("adding-pon-port", log.Fields{"deviceID": dh.deviceID, "ponPortNo": dh.ponPortNumber})
+		var ponPortNo uint32 = 1
+		if dh.ponPortNumber != 0 {
+			ponPortNo = dh.ponPortNumber
+		}
+
+		pPonPort := &voltha.Port{
+			PortNo: ponPortNo,
+			//Label:      fmt.Sprintf("pon-%d", ponPortNo),
+			Label:      "PON port",
+			Type:       voltha.Port_PON_ONU,
+			OperStatus: voltha.OperStatus_ACTIVE,
+			Peers: []*voltha.Port_PeerPort{{DeviceId: dh.parentID, // Peer device  is OLT
+				PortNo: ponPortNo}}, // Peer port is parent's port number
+		}
+		if err = dh.coreProxy.PortCreated(context.TODO(), dh.deviceID, pPonPort); err != nil {
+			logger.Fatalf("Device FSM: PortCreated-failed-%s", err)
+			e.Cancel(err)
+			return
+		}
+	} else {
+		logger.Debugw("reconciling - pon-port already added", log.Fields{"device-id": dh.deviceID})
+	}
+	logger.Debug("doStateInit-done")
+}
+
+// postInit setups the DeviceEntry for the conerned device
+func (dh *deviceHandler) postInit(e *fsm.Event) {
+
+	logger.Debug("postInit-started")
+	var err error
+	/*
+		dh.Client = oop.NewOpenoltClient(dh.clientCon)
+		dh.pTransitionMap.Handle(ctx, GrpcConnected)
+		return nil
+	*/
+	if err = dh.addOnuDeviceEntry(context.TODO()); err != nil {
+		logger.Fatalf("Device FSM: addOnuDeviceEntry-failed-%s", err)
+		e.Cancel(err)
+		return
+	}
+
+	if dh.reconciling {
+		go dh.reconcileDeviceOnuInd()
+		// reconcilement will be continued after mib download is done
+	}
+	/*
+			############################################################################
+			# Setup Alarm handler
+			self.events = AdapterEvents(self.core_proxy, device.id, self.logical_device_id,
+										device.serial_number)
+			############################################################################
+			# Setup PM configuration for this device
+			# Pass in ONU specific options
+			kwargs = {
+				OnuPmMetrics.DEFAULT_FREQUENCY_KEY: OnuPmMetrics.DEFAULT_ONU_COLLECTION_FREQUENCY,
+				'heartbeat': self.heartbeat,
+				OnuOmciPmMetrics.OMCI_DEV_KEY: self._onu_omci_device
+			}
+			self.logger.debug('create-pm-metrics', device_id=device.id, serial_number=device.serial_number)
+			self._pm_metrics = OnuPmMetrics(self.events, self.core_proxy, self.device_id,
+										   self.logical_device_id, device.serial_number,
+										   grouped=True, freq_override=False, **kwargs)
+			pm_config = self._pm_metrics.make_proto()
+			self._onu_omci_device.set_pm_config(self._pm_metrics.omci_pm.openomci_interval_pm)
+			self.logger.info("initial-pm-config", device_id=device.id, serial_number=device.serial_number)
+			yield self.core_proxy.device_pm_config_update(pm_config, init=True)
+
+			# Note, ONU ID and UNI intf set in add_uni_port method
+			self._onu_omci_device.alarm_synchronizer.set_alarm_params(mgr=self.events,
+																	  ani_ports=[self._pon])
+
+			# Code to Run OMCI Test Action
+			kwargs_omci_test_action = {
+				OmciTestRequest.DEFAULT_FREQUENCY_KEY:
+					OmciTestRequest.DEFAULT_COLLECTION_FREQUENCY
+			}
+			serial_number = device.serial_number
+			self._test_request = OmciTestRequest(self.core_proxy,
+										   self.omci_agent, self.device_id,
+										   AniG, serial_number,
+										   self.logical_device_id,
+										   exclusive=False,
+										   **kwargs_omci_test_action)
+
+			self.enabled = True
+		else:
+			self.logger.info('onu-already-activated')
+	*/
+	logger.Debug("postInit-done")
+}
+
+// doStateConnected get the device info and update to voltha core
+// for comparison of the original method (not that easy to uncomment): compare here:
+//  voltha-openolt-adapter/adaptercore/device_handler.go
+//  -> this one obviously initiates all communication interfaces of the device ...?
+func (dh *deviceHandler) doStateConnected(e *fsm.Event) {
+
+	logger.Debug("doStateConnected-started")
+	err := errors.New("device FSM: function not implemented yet")
+	e.Cancel(err)
+	logger.Debug("doStateConnected-done")
+}
+
+// doStateUp handle the onu up indication and update to voltha core
+func (dh *deviceHandler) doStateUp(e *fsm.Event) {
+
+	logger.Debug("doStateUp-started")
+	err := errors.New("device FSM: function not implemented yet")
+	e.Cancel(err)
+	logger.Debug("doStateUp-done")
+
+	/*
+		// Synchronous call to update device state - this method is run in its own go routine
+		if err := dh.coreProxy.DeviceStateUpdate(ctx, dh.device.Id, voltha.ConnectStatus_REACHABLE,
+			voltha.OperStatus_ACTIVE); err != nil {
+			logger.Errorw("Failed to update device with OLT UP indication", log.Fields{"deviceID": dh.device.Id, "error": err})
+			return err
+		}
+		return nil
+	*/
+}
+
+// doStateDown handle the onu down indication
+func (dh *deviceHandler) doStateDown(e *fsm.Event) {
+
+	logger.Debug("doStateDown-started")
+	var err error
+
+	device := dh.device
+	if device == nil {
+		/*TODO: needs to handle error scenarios */
+		logger.Error("Failed to fetch handler device")
+		e.Cancel(err)
+		return
+	}
+
+	cloned := proto.Clone(device).(*voltha.Device)
+	logger.Debugw("do-state-down", log.Fields{"ClonedDeviceID": cloned.Id})
+	/*
+		// Update the all ports state on that device to disable
+		if er := dh.coreProxy.PortsStateUpdate(ctx, cloned.Id, voltha.OperStatus_UNKNOWN); er != nil {
+			logger.Errorw("updating-ports-failed", log.Fields{"deviceID": device.Id, "error": er})
+			return er
+		}
+
+		//Update the device oper state and connection status
+		cloned.OperStatus = voltha.OperStatus_UNKNOWN
+		cloned.ConnectStatus = common.ConnectStatus_UNREACHABLE
+		dh.device = cloned
+
+		if er := dh.coreProxy.DeviceStateUpdate(ctx, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); er != nil {
+			logger.Errorw("error-updating-device-state", log.Fields{"deviceID": device.Id, "error": er})
+			return er
+		}
+
+		//get the child device for the parent device
+		onuDevices, err := dh.coreProxy.GetChildDevices(ctx, dh.device.Id)
+		if err != nil {
+			logger.Errorw("failed to get child devices information", log.Fields{"deviceID": dh.device.Id, "error": err})
+			return err
+		}
+		for _, onuDevice := range onuDevices.Items {
+
+			// Update onu state as down in onu adapter
+			onuInd := oop.OnuIndication{}
+			onuInd.OperState = "down"
+			er := dh.AdapterProxy.SendInterAdapterMessage(ctx, &onuInd, ic.InterAdapterMessageType_ONU_IND_REQUEST,
+				"openolt", onuDevice.Type, onuDevice.Id, onuDevice.ProxyAddress.DeviceId, "")
+			if er != nil {
+				logger.Errorw("Failed to send inter-adapter-message", log.Fields{"OnuInd": onuInd,
+					"From Adapter": "openolt", "DevieType": onuDevice.Type, "DeviceID": onuDevice.Id})
+				//Do not return here and continue to process other ONUs
+			}
+		}
+		// * Discovered ONUs entries need to be cleared , since after OLT
+		//   is up, it starts sending discovery indications again* /
+		dh.discOnus = sync.Map{}
+		logger.Debugw("do-state-down-end", log.Fields{"deviceID": device.Id})
+		return nil
+	*/
+	err = errors.New("device FSM: function not implemented yet")
+	e.Cancel(err)
+	logger.Debug("doStateDown-done")
+}
+
+// deviceHandler StateMachine related state transition methods ##### end #########
+// #################################################################################
+
+// ###################################################
+// deviceHandler utility methods ##### begin #########
+
+//getOnuDeviceEntry getsthe  ONU device entry and may wait until its value is defined
+func (dh *deviceHandler) getOnuDeviceEntry(aWait bool) *OnuDeviceEntry {
+	dh.lockDevice.RLock()
+	pOnuDeviceEntry := dh.pOnuOmciDevice
+	if aWait && pOnuDeviceEntry == nil {
+		//keep the read sema short to allow for subsequent write
+		dh.lockDevice.RUnlock()
+		logger.Debugw("Waiting for DeviceEntry to be set ...", log.Fields{"device-id": dh.deviceID})
+		// based on concurrent processing the deviceEntry setup may not yet be finished at his point
+		// so it might be needed to wait here for that event with some timeout
+		select {
+		case <-time.After(60 * time.Second): //timer may be discussed ...
+			logger.Errorw("No valid DeviceEntry set after maxTime", log.Fields{"device-id": dh.deviceID})
+			return nil
+		case <-dh.deviceEntrySet:
+			logger.Debugw("devicEntry ready now - continue", log.Fields{"device-id": dh.deviceID})
+			// if written now, we can return the written value without sema
+			return dh.pOnuOmciDevice
+		}
+	}
+	dh.lockDevice.RUnlock()
+	return pOnuDeviceEntry
+}
+
+//setOnuDeviceEntry sets the ONU device entry within the handler
+func (dh *deviceHandler) setOnuDeviceEntry(
+	apDeviceEntry *OnuDeviceEntry, apOnuTp *onuUniTechProf) {
+	dh.lockDevice.Lock()
+	defer dh.lockDevice.Unlock()
+	dh.pOnuOmciDevice = apDeviceEntry
+	dh.pOnuTP = apOnuTp
+}
+
+//addOnuDeviceEntry creates a new ONU device or returns the existing
+func (dh *deviceHandler) addOnuDeviceEntry(ctx context.Context) error {
+	logger.Debugw("adding-deviceEntry", log.Fields{"device-id": dh.deviceID})
+
+	deviceEntry := dh.getOnuDeviceEntry(false)
+	if deviceEntry == nil {
+		/* costum_me_map in python code seems always to be None,
+		   we omit that here first (declaration unclear) -> todo at Adapter specialization ...*/
+		/* also no 'clock' argument - usage open ...*/
+		/* and no alarm_db yet (oo.alarm_db)  */
+		deviceEntry = newOnuDeviceEntry(ctx, dh.deviceID, dh.pOpenOnuAc.KVStoreHost,
+			dh.pOpenOnuAc.KVStorePort, dh.pOpenOnuAc.KVStoreType,
+			dh, dh.coreProxy, dh.AdapterProxy,
+			dh.pOpenOnuAc.pSupportedFsms) //nil as FSM pointer would yield deviceEntry internal defaults ...
+		onuTechProfProc := newOnuUniTechProf(ctx, dh.deviceID, dh)
+		//error treatment possible //TODO!!!
+		dh.setOnuDeviceEntry(deviceEntry, onuTechProfProc)
+		// fire deviceEntry ready event to spread to possibly waiting processing
+		dh.deviceEntrySet <- true
+
+		//dh.sendMsgToOlt(ctx, "adopt", nil)
+		// // update json file
+		// onustat := &OnuStatus{
+		// 	Id:           dh.deviceID,
+		// 	AdminState:   vc.AdminState_Types_name[int32(dh.device.AdminState)],
+		// 	OpeState:     vc.OperStatus_Types_name[int32(dh.device.OperStatus)],
+		// 	ConnectState: vc.ConnectStatus_Types_name[int32(dh.device.ConnectStatus)],
+		// 	MacAddress:   dh.device.MacAddress,
+		// }
+		// logger.Debugw("AddOnu", log.Fields{"Id": onustat.Id, "Admin": onustat.AdminState, "Ope": onustat.OpeState, "Con": onustat.ConnectState, "Mac": onustat.MacAddress})
+		// err := AddOnu(onustat)
+		// logger.Debugw("AddOnu", log.Fields{"err": err})
+		// //
+		// go WatchStatus(ctx, dh.coreProxy)
+
+		// dh.device.Root = true
+		// dh.coreProxy.DeviceUpdate(context.TODO(), dh.device)
+
+		logger.Infow("onuDeviceEntry-added", log.Fields{"device-id": dh.deviceID})
+	} else {
+		logger.Infow("onuDeviceEntry-add: Device already exists", log.Fields{"device-id": dh.deviceID})
+	}
+	return nil
+}
+
+// doStateInit provides the device update to the core
+func (dh *deviceHandler) createInterface(onuind *oop.OnuIndication) error {
+	logger.Debugw("create_interface-started", log.Fields{"OnuId": onuind.GetOnuId(),
+		"OnuIntfId": onuind.GetIntfId(), "OnuSerialNumber": onuind.GetSerialNumber()})
+
+	dh.pOnuIndication = onuind // let's revise if storing the pointer is sufficient...
+
+	if !dh.reconciling {
+		if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID,
+			voltha.ConnectStatus_REACHABLE, voltha.OperStatus_ACTIVATING); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+		}
+	} else {
+		logger.Debugw("reconciling - don't notify core about DeviceStateUpdate to ACTIVATING",
+			log.Fields{"device-id": dh.deviceID})
+	}
+	/*
+			device, err := dh.coreProxy.GetDevice(context.TODO(), dh.device.Id, dh.device.Id)
+		    if err != nil || device == nil {
+					//TODO: needs to handle error scenarios
+				logger.Errorw("Failed to fetch device device at creating If", log.Fields{"err": err})
+				return errors.New("Voltha Device not found")
+			}
+	*/
+
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry != nil {
+		if err := pDevEntry.start(context.TODO()); err != nil {
+			return err
+		}
+	} else {
+		logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+		return errors.New("no valid OnuDevice")
+	}
+	if !dh.reconciling {
+		if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "starting-openomci"); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-DeviceReasonUpdate to starting-openomci", log.Fields{"device-id": dh.deviceID, "error": err})
+		}
+	} else {
+		logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to starting-openomci",
+			log.Fields{"device-id": dh.deviceID})
+	}
+	dh.deviceReason = "starting-openomci"
+
+	/* this might be a good time for Omci Verify message?  */
+	verifyExec := make(chan bool)
+	omciVerify := newOmciTestRequest(context.TODO(),
+		dh.device.Id, pDevEntry.PDevOmciCC,
+		true, true) //eclusive and allowFailure (anyway not yet checked)
+	omciVerify.performOmciTest(context.TODO(), verifyExec)
+
+	/* 	give the handler some time here to wait for the OMCi verification result
+	after Timeout start and try MibUpload FSM anyway
+	(to prevent stopping on just not supported OMCI verification from ONU) */
+	select {
+	case <-time.After(2 * time.Second):
+		logger.Warn("omci start-verification timed out (continue normal)")
+	case testresult := <-verifyExec:
+		logger.Infow("Omci start verification done", log.Fields{"result": testresult})
+	}
+
+	/* In py code it looks earlier (on activate ..)
+			# Code to Run OMCI Test Action
+			kwargs_omci_test_action = {
+				OmciTestRequest.DEFAULT_FREQUENCY_KEY:
+					OmciTestRequest.DEFAULT_COLLECTION_FREQUENCY
+			}
+			serial_number = device.serial_number
+			self._test_request = OmciTestRequest(self.core_proxy,
+											self.omci_agent, self.device_id,
+											AniG, serial_number,
+											self.logical_device_id,
+											exclusive=False,
+											**kwargs_omci_test_action)
+	...
+	                    # Start test requests after a brief pause
+	                    if not self._test_request_started:
+	                        self._test_request_started = True
+	                        tststart = _STARTUP_RETRY_WAIT * (random.randint(1, 5))
+	                        reactor.callLater(tststart, self._test_request.start_collector)
+
+	*/
+	/* which is then: in omci_test_request.py : */
+	/*
+	   def start_collector(self, callback=None):
+	       """
+	               Start the collection loop for an adapter if the frequency > 0
+
+	               :param callback: (callable) Function to call to collect PM data
+	       """
+	       self.logger.info("starting-pm-collection", device_name=self.name, default_freq=self.default_freq)
+	       if callback is None:
+	           callback = self.perform_test_omci
+
+	       if self.lc is None:
+	           self.lc = LoopingCall(callback)
+
+	       if self.default_freq > 0:
+	           self.lc.start(interval=self.default_freq / 10)
+
+	   def perform_test_omci(self):
+	       """
+	       Perform the initial test request
+	       """
+	       ani_g_entities = self._device.configuration.ani_g_entities
+	       ani_g_entities_ids = list(ani_g_entities.keys()) if ani_g_entities \
+	                                                     is not None else None
+	       self._entity_id = ani_g_entities_ids[0]
+	       self.logger.info('perform-test', entity_class=self._entity_class,
+	                     entity_id=self._entity_id)
+	       try:
+	           frame = MEFrame(self._entity_class, self._entity_id, []).test()
+	           result = yield self._device.omci_cc.send(frame)
+	           if not result.fields['omci_message'].fields['success_code']:
+	               self.logger.info('Self-Test Submitted Successfully',
+	                             code=result.fields[
+	                                 'omci_message'].fields['success_code'])
+	           else:
+	               raise TestFailure('Test Failure: {}'.format(
+	                   result.fields['omci_message'].fields['success_code']))
+	       except TimeoutError as e:
+	           self.deferred.errback(failure.Failure(e))
+
+	       except Exception as e:
+	           self.logger.exception('perform-test-Error', e=e,
+	                              class_id=self._entity_class,
+	                              entity_id=self._entity_id)
+	           self.deferred.errback(failure.Failure(e))
+
+	*/
+
+
+	/* Note: Even though FSM calls look 'synchronous' here, FSM is running in background with the effect that possible errors
+	 * 	 within the MibUpload are not notified in the OnuIndication response, this might be acceptable here,
+	 *   as further OltAdapter processing may rely on the deviceReason event 'MibUploadDone' as a result of the FSM processing
+	 *   otherwise some processing synchronization would be required - cmp. e.g TechProfile processing
+	 */
+	pMibUlFsm := pDevEntry.pMibUploadFsm.pFsm
+	if pMibUlFsm != nil {
+		if pMibUlFsm.Is(ulStDisabled) {
+			if err := pMibUlFsm.Event(ulEvStart); err != nil {
+				logger.Errorw("MibSyncFsm: Can't go to state starting", log.Fields{"err": err})
+				return errors.New("can't go to state starting")
+			}
+			logger.Debugw("MibSyncFsm", log.Fields{"state": string(pMibUlFsm.Current())})
+			//Determine ONU status and start/re-start MIB Synchronization tasks
+			//Determine if this ONU has ever synchronized
+			if true { //TODO: insert valid check
+				if err := pMibUlFsm.Event(ulEvResetMib); err != nil {
+					logger.Errorw("MibSyncFsm: Can't go to state resetting_mib", log.Fields{"err": err})
+					return errors.New("can't go to state resetting_mib")
+				}
+			} else {
+				if err := pMibUlFsm.Event(ulEvExamineMds); err != nil {
+					logger.Errorw("MibSyncFsm: Can't go to state examine_mds", log.Fields{"err": err})
+					return errors.New("can't go to examine_mds")
+				}
+				logger.Debugw("state of MibSyncFsm", log.Fields{"state": string(pMibUlFsm.Current())})
+				//Examine the MIB Data Sync
+				// callbacks to be handled:
+				// Event(ulEvSuccess)
+				// Event(ulEvTimeout)
+				// Event(ulEvMismatch)
+			}
+		} else {
+			logger.Errorw("wrong state of MibSyncFsm - want: disabled", log.Fields{"have": string(pMibUlFsm.Current())})
+			return errors.New("wrong state of MibSyncFsm")
+		}
+	} else {
+		logger.Errorw("MibSyncFsm invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
+		return errors.New("cannot execut MibSync")
+	}
+	return nil
+}
+
+func (dh *deviceHandler) updateInterface(onuind *oop.OnuIndication) error {
+	if dh.deviceReason != "stopping-openomci" {
+		logger.Debugw("updateInterface-started - stopping-device", log.Fields{"device-id": dh.deviceID})
+		//stop all running SM processing - make use of the DH-state as mirrored in the deviceReason
+		pDevEntry := dh.getOnuDeviceEntry(false)
+		if pDevEntry == nil {
+			logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+			return errors.New("no valid OnuDevice")
+		}
+
+		switch dh.deviceReason {
+		case "starting-openomci":
+			{ //MIBSync FSM may run
+				pMibUlFsm := pDevEntry.pMibUploadFsm.pFsm
+				if pMibUlFsm != nil {
+					_ = pMibUlFsm.Event(ulEvStop) //TODO!! verify if MibSyncFsm stop-processing is sufficient (to allow it again afterwards)
+				}
+			}
+		case "discovery-mibsync-complete":
+			{ //MibDownload may run
+				pMibDlFsm := pDevEntry.pMibDownloadFsm.pFsm
+				if pMibDlFsm != nil {
+					_ = pMibDlFsm.Event(dlEvReset)
+				}
+			}
+		default:
+			{
+				//port lock/unlock FSM's may be active
+				if dh.pUnlockStateFsm != nil {
+					_ = dh.pUnlockStateFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+				}
+				if dh.pLockStateFsm != nil {
+					_ = dh.pLockStateFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+				}
+				//techProfile related PonAniConfigFsm FSM may be active
+				// maybe encapsulated as OnuTP method - perhaps later in context of module splitting
+				if dh.pOnuTP.pAniConfigFsm != nil {
+					_ = dh.pOnuTP.pAniConfigFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+				}
+				for _, uniPort := range dh.uniEntityMap {
+					//reset the TechProfileConfig Done state for all (active) UNI's
+					dh.pOnuTP.setConfigDone(uniPort.uniID, false)
+					// reset the possibly existing VlanConfigFsm
+					if pVlanFilterFsm, exist := dh.UniVlanConfigFsmMap[uniPort.uniID]; exist {
+						//VlanFilterFsm exists and was already started
+						pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
+						if pVlanFilterStatemachine != nil {
+							_ = pVlanFilterStatemachine.Event(vlanEvReset)
+						}
+					}
+				}
+			}
+			//TODO!!! care about PM/Alarm processing once started
+		}
+		//TODO: from here the deviceHandler FSM itself may be stuck in some of the initial states
+		//  (mainly the still separate 'Event states')
+		//  so it is questionable, how this is resolved after some possible re-enable
+		//  assumption there is obviously, that the system may continue with some 'after "mib-download-done" state'
+
+		//stop/remove(?) the device entry
+		_ = pDevEntry.stop(context.TODO()) //maybe some more sophisticated context treatment should be used here?
+
+		//TODO!!! remove existing traffic profiles
+		/* from py code, if TP's exist, remove them - not yet implemented
+		self._tp = dict()
+		# Let TP download happen again
+		for uni_id in self._tp_service_specific_task:
+			self._tp_service_specific_task[uni_id].clear()
+		for uni_id in self._tech_profile_download_done:
+			self._tech_profile_download_done[uni_id].clear()
+		*/
+
+		dh.disableUniPortStateUpdate()
+
+		if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "stopping-openomci"); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-DeviceReasonUpdate to 'stopping-openomci'",
+				log.Fields{"device-id": dh.deviceID, "error": err})
+			// abort: system behavior is just unstable ...
+			return err
+		}
+		dh.deviceReason = "stopping-openomci"
+
+		if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID,
+			voltha.ConnectStatus_UNREACHABLE, voltha.OperStatus_DISCOVERED); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-updating-device-state unreachable-discovered",
+				log.Fields{"device-id": dh.deviceID, "error": err})
+			// abort: system behavior is just unstable ...
+			return err
+		}
+	} else {
+		logger.Debugw("updateInterface - device already stopped", log.Fields{"device-id": dh.deviceID})
+	}
+	return nil
+}
+
+func (dh *deviceHandler) processMibDatabaseSyncEvent(devEvent OnuDeviceEvent) {
+	logger.Debugw("MibInSync event received", log.Fields{"device-id": dh.deviceID})
+	if !dh.reconciling {
+		//initiate DevStateUpdate
+		if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "discovery-mibsync-complete"); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-DeviceReasonUpdate to 'mibsync-complete'", log.Fields{
+				"device-id": dh.deviceID, "error": err})
+		} else {
+			logger.Infow("dev reason updated to 'MibSync complete'", log.Fields{"deviceID": dh.deviceID})
+		}
+	} else {
+		logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to mibsync-complete",
+			log.Fields{"device-id": dh.deviceID})
+	}
+	dh.deviceReason = "discovery-mibsync-complete"
+
+	i := uint8(0) //UNI Port limit: see MaxUnisPerOnu (by now 16) (OMCI supports max 255 p.b.)
+	pDevEntry := dh.getOnuDeviceEntry(false)
+	if unigInstKeys := pDevEntry.pOnuDB.getSortedInstKeys(me.UniGClassID); len(unigInstKeys) > 0 {
+		for _, mgmtEntityID := range unigInstKeys {
+			logger.Debugw("Add UNI port for stored UniG instance:", log.Fields{
+				"device-id": dh.deviceID, "UnigMe EntityID": mgmtEntityID})
+			dh.addUniPort(mgmtEntityID, i, uniPPTP)
+			i++
+		}
+	} else {
+		logger.Debugw("No UniG instances found", log.Fields{"device-id": dh.deviceID})
+	}
+	if veipInstKeys := pDevEntry.pOnuDB.getSortedInstKeys(me.VirtualEthernetInterfacePointClassID); len(veipInstKeys) > 0 {
+		for _, mgmtEntityID := range veipInstKeys {
+			logger.Debugw("Add VEIP acc. to stored VEIP instance:", log.Fields{
+				"device-id": dh.deviceID, "VEIP EntityID": mgmtEntityID})
+			dh.addUniPort(mgmtEntityID, i, uniVEIP)
+			i++
+		}
+	} else {
+		logger.Debugw("No VEIP instances found", log.Fields{"device-id": dh.deviceID})
+	}
+	if i == 0 {
+		logger.Warnw("No PPTP instances found", log.Fields{"device-id": dh.deviceID})
+	}
+
+	/* 200605: lock processing after initial MIBUpload removed now as the ONU should be in the lock state per default here
+	 *  left the code here as comment in case such processing should prove needed unexpectedly
+			// Init Uni Ports to Admin locked state
+			// maybe not really needed here as UNI ports should be locked by default, but still left as available in python code
+			// *** should generate UniLockStateDone event *****
+			if dh.pLockStateFsm == nil {
+				dh.createUniLockFsm(true, UniLockStateDone)
+			} else { //LockStateFSM already init
+				dh.pLockStateFsm.SetSuccessEvent(UniLockStateDone)
+				dh.runUniLockFsm(true)
+			}
+		}
+	case UniLockStateDone:
+		{
+			logger.Infow("UniLockStateDone event: Starting MIB download", log.Fields{"device-id": dh.deviceID})
+	* lockState processing commented out
+	*/
+	/*  Mib download procedure -
+	***** should run over 'downloaded' state and generate MibDownloadDone event *****
+	 */
+	pMibDlFsm := pDevEntry.pMibDownloadFsm.pFsm
+	if pMibDlFsm != nil {
+		if pMibDlFsm.Is(dlStDisabled) {
+			if err := pMibDlFsm.Event(dlEvStart); err != nil {
+				logger.Errorw("MibDownloadFsm: Can't go to state starting", log.Fields{"err": err})
+				// maybe try a FSM reset and then again ... - TODO!!!
+			} else {
+				logger.Debugw("MibDownloadFsm", log.Fields{"state": string(pMibDlFsm.Current())})
+				// maybe use more specific states here for the specific download steps ...
+				if err := pMibDlFsm.Event(dlEvCreateGal); err != nil {
+					logger.Errorw("MibDownloadFsm: Can't start CreateGal", log.Fields{"err": err})
+				} else {
+					logger.Debugw("state of MibDownloadFsm", log.Fields{"state": string(pMibDlFsm.Current())})
+					//Begin MIB data download (running autonomously)
+				}
+			}
+		} else {
+			logger.Errorw("wrong state of MibDownloadFsm - want: disabled", log.Fields{"have": string(pMibDlFsm.Current())})
+			// maybe try a FSM reset and then again ... - TODO!!!
+		}
+		/***** Mib download started */
+	} else {
+		logger.Errorw("MibDownloadFsm invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
+	}
+}
+
+func (dh *deviceHandler) processMibDownloadDoneEvent(devEvent OnuDeviceEvent) {
+	logger.Debugw("MibDownloadDone event received", log.Fields{"device-id": dh.deviceID})
+	if !dh.reconciling {
+		if err := dh.coreProxy.DeviceStateUpdate(context.TODO(), dh.deviceID,
+			voltha.ConnectStatus_REACHABLE, voltha.OperStatus_ACTIVE); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-updating-device-state", log.Fields{"device-id": dh.deviceID, "error": err})
+		} else {
+			logger.Debugw("dev state updated to 'Oper.Active'", log.Fields{"device-id": dh.deviceID})
+		}
+	} else {
+		logger.Debugw("reconciling - don't notify core about DeviceStateUpdate to ACTIVE",
+			log.Fields{"device-id": dh.deviceID})
+	}
+	if !dh.reconciling {
+		if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "initial-mib-downloaded"); err != nil {
+			//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+			logger.Errorw("error-DeviceReasonUpdate to 'initial-mib-downloaded'",
+				log.Fields{"device-id": dh.deviceID, "error": err})
+		} else {
+			logger.Infow("dev reason updated to 'initial-mib-downloaded'", log.Fields{"device-id": dh.deviceID})
+		}
+	} else {
+		logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to initial-mib-downloaded",
+			log.Fields{"device-id": dh.deviceID})
+	}
+	dh.deviceReason = "initial-mib-downloaded"
+	if dh.pUnlockStateFsm == nil {
+		dh.createUniLockFsm(false, UniUnlockStateDone)
+	} else { //UnlockStateFSM already init
+		dh.pUnlockStateFsm.setSuccessEvent(UniUnlockStateDone)
+		dh.runUniLockFsm(false)
+	}
+}
+
+func (dh *deviceHandler) processUniUnlockStateDoneEvent(devEvent OnuDeviceEvent) {
+	go dh.enableUniPortStateUpdate() //cmp python yield self.enable_ports()
+
+	if !dh.reconciling {
+		logger.Infow("UniUnlockStateDone event: Sending OnuUp event", log.Fields{"device-id": dh.deviceID})
+		raisedTs := time.Now().UnixNano()
+		go dh.sendOnuOperStateEvent(voltha.OperStatus_ACTIVE, dh.deviceID, raisedTs) //cmp python onu_active_event
+	} else {
+		logger.Debugw("reconciling - don't notify core that onu went to active but trigger tech profile config",
+			log.Fields{"device-id": dh.deviceID})
+		go dh.reconcileDeviceTechProf()
+		//TODO: further actions e.g. restore flows, metrics, ...
+	}
+}
+
+func (dh *deviceHandler) processOmciAniConfigDoneEvent(devEvent OnuDeviceEvent) {
+	logger.Debugw("OmciAniConfigDone event received", log.Fields{"device-id": dh.deviceID})
+	if dh.deviceReason != "tech-profile-config-download-success" {
+		// which may be the case from some previous actvity on another UNI Port of the ONU
+		if !dh.reconciling {
+			if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "tech-profile-config-download-success"); err != nil {
+				//TODO with VOL-3045/VOL-3046: return the error and stop further processing
+				logger.Errorw("error-DeviceReasonUpdate to 'tech-profile-config-download-success'",
+					log.Fields{"device-id": dh.deviceID, "error": err})
+			} else {
+				logger.Infow("update dev reason to 'tech-profile-config-download-success'",
+					log.Fields{"device-id": dh.deviceID})
+			}
+		} else {
+			logger.Debugw("reconciling - don't notify core about DeviceReasonUpdate to tech-profile-config-download-success",
+				log.Fields{"device-id": dh.deviceID})
+		}
+		//set internal state anyway - as it was done
+		dh.deviceReason = "tech-profile-config-download-success"
+	}
+}
+
+func (dh *deviceHandler) processOmciVlanFilterDoneEvent(devEvent OnuDeviceEvent) {
+	logger.Debugw("OmciVlanFilterDone event received",
+		log.Fields{"device-id": dh.deviceID})
+
+	if dh.deviceReason != "omci-flows-pushed" {
+		// which may be the case from some previous actvity on another UNI Port of the ONU
+		// or even some previous flow add activity on the same port
+		if err := dh.coreProxy.DeviceReasonUpdate(context.TODO(), dh.deviceID, "omci-flows-pushed"); err != nil {
+			logger.Errorw("error-DeviceReasonUpdate to 'omci-flows-pushed'",
+				log.Fields{"device-id": dh.deviceID, "error": err})
+		} else {
+			logger.Infow("updated dev reason to ''omci-flows-pushed'",
+				log.Fields{"device-id": dh.deviceID})
+		}
+		//set internal state anyway - as it was done
+		dh.deviceReason = "omci-flows-pushed"
+	}
+}
+
+//deviceProcStatusUpdate evaluates possible processing events and initiates according next activities
+func (dh *deviceHandler) deviceProcStatusUpdate(devEvent OnuDeviceEvent) {
+	switch devEvent {
+	case MibDatabaseSync:
+		{
+			dh.processMibDatabaseSyncEvent(devEvent)
+		}
+	case MibDownloadDone:
+		{
+			dh.processMibDownloadDoneEvent(devEvent)
+
+		}
+	case UniUnlockStateDone:
+		{
+			dh.processUniUnlockStateDoneEvent(devEvent)
+
+		}
+	case OmciAniConfigDone:
+		{
+			dh.processOmciAniConfigDoneEvent(devEvent)
+
+		}
+	case OmciVlanFilterDone:
+		{
+			dh.processOmciVlanFilterDoneEvent(devEvent)
+
+		}
+	default:
+		{
+			logger.Warnw("unhandled-device-event", log.Fields{"device-id": dh.deviceID, "event": devEvent})
+		}
+	} //switch
+}
+
+func (dh *deviceHandler) addUniPort(aUniInstNo uint16, aUniID uint8, aPortType uniPortType) {
+	uniNo := mkUniPortNum(dh.pOnuIndication.GetIntfId(), dh.pOnuIndication.GetOnuId(),
+		uint32(aUniID))
+	if _, present := dh.uniEntityMap[uniNo]; present {
+		logger.Warnw("onuUniPort-add: Port already exists", log.Fields{"for InstanceId": aUniInstNo})
+	} else {
+		//with arguments aUniID, a_portNo, aPortType
+		pUniPort := newOnuUniPort(aUniID, uniNo, aUniInstNo, aPortType)
+		if pUniPort == nil {
+			logger.Warnw("onuUniPort-add: Could not create Port", log.Fields{"for InstanceId": aUniInstNo})
+		} else {
+			//store UniPort with the System-PortNumber key
+			dh.uniEntityMap[uniNo] = pUniPort
+			if !dh.reconciling {
+				// create announce the UniPort to the core as VOLTHA Port object
+				if err := pUniPort.createVolthaPort(dh); err == nil {
+					logger.Infow("onuUniPort-added", log.Fields{"for PortNo": uniNo})
+				} //error logging already within UniPort method
+			} else {
+				logger.Debugw("reconciling - onuUniPort already added", log.Fields{"for PortNo": uniNo, "device-id": dh.deviceID})
+			}
+		}
+	}
+}
+
+// enableUniPortStateUpdate enables UniPortState and update core port state accordingly
+func (dh *deviceHandler) enableUniPortStateUpdate() {
+
+
+	for uniNo, uniPort := range dh.uniEntityMap {
+		// only if this port is validated for operState transfer
+		if (1<<uniPort.uniID)&activeUniPortStateUpdateMask == (1 << uniPort.uniID) {
+			logger.Infow("onuUniPort-forced-OperState-ACTIVE", log.Fields{"for PortNo": uniNo})
+			uniPort.setOperState(vc.OperStatus_ACTIVE)
+			if !dh.reconciling {
+				//maybe also use getter functions on uniPort - perhaps later ...
+				go dh.coreProxy.PortStateUpdate(context.TODO(), dh.deviceID, voltha.Port_ETHERNET_UNI, uniPort.portNo, uniPort.operState)
+			} else {
+				logger.Debugw("reconciling - don't notify core about PortStateUpdate", log.Fields{"device-id": dh.deviceID})
+			}
+		}
+	}
+}
+
+// Disable UniPortState and update core port state accordingly
+func (dh *deviceHandler) disableUniPortStateUpdate() {
+	for uniNo, uniPort := range dh.uniEntityMap {
+		// only if this port is validated for operState transfer
+		if (1<<uniPort.uniID)&activeUniPortStateUpdateMask == (1 << uniPort.uniID) {
+			logger.Infow("onuUniPort-forced-OperState-UNKNOWN", log.Fields{"for PortNo": uniNo})
+			uniPort.setOperState(vc.OperStatus_UNKNOWN)
+			//maybe also use getter functions on uniPort - perhaps later ...
+			go dh.coreProxy.PortStateUpdate(context.TODO(), dh.deviceID, voltha.Port_ETHERNET_UNI, uniPort.portNo, uniPort.operState)
+		}
+	}
+}
+
+// ONU_Active/Inactive announcement on system KAFKA bus
+// tried to re-use procedure of oltUpDownIndication from openolt_eventmgr.go with used values from Py code
+func (dh *deviceHandler) sendOnuOperStateEvent(aOperState vc.OperStatus_Types, aDeviceID string, raisedTs int64) {
+	var de voltha.DeviceEvent
+	eventContext := make(map[string]string)
+	parentDevice, err := dh.coreProxy.GetDevice(context.TODO(), dh.parentID, dh.parentID)
+	if err != nil || parentDevice == nil {
+		logger.Errorw("Failed to fetch parent device for OnuEvent",
+			log.Fields{"parentID": dh.parentID, "err": err})
+	}
+	oltSerialNumber := parentDevice.SerialNumber
+
+	eventContext["pon-id"] = strconv.FormatUint(uint64(dh.pOnuIndication.IntfId), 10)
+	eventContext["onu-id"] = strconv.FormatUint(uint64(dh.pOnuIndication.OnuId), 10)
+	eventContext["serial-number"] = dh.device.SerialNumber
+	eventContext["olt_serial_number"] = oltSerialNumber
+	eventContext["device_id"] = aDeviceID
+	eventContext["registration_id"] = aDeviceID //py: string(device_id)??
+	logger.Debugw("prepare ONU_ACTIVATED event",
+		log.Fields{"DeviceId": aDeviceID, "EventContext": eventContext})
+
+	/* Populating device event body */
+	de.Context = eventContext
+	de.ResourceId = aDeviceID
+	if aOperState == voltha.OperStatus_ACTIVE {
+		de.DeviceEventName = fmt.Sprintf("%s_%s", cOnuActivatedEvent, "RAISE_EVENT")
+		de.Description = fmt.Sprintf("%s Event - %s - %s",
+			cEventObjectType, cOnuActivatedEvent, "Raised")
+	} else {
+		de.DeviceEventName = fmt.Sprintf("%s_%s", cOnuActivatedEvent, "CLEAR_EVENT")
+		de.Description = fmt.Sprintf("%s Event - %s - %s",
+			cEventObjectType, cOnuActivatedEvent, "Cleared")
+	}
+	/* Send event to KAFKA */
+	if err := dh.EventProxy.SendDeviceEvent(&de, equipment, pon, raisedTs); err != nil {
+		logger.Warnw("could not send ONU_ACTIVATED event",
+			log.Fields{"device-id": aDeviceID, "error": err})
+	}
+	logger.Debugw("ONU_ACTIVATED event sent to KAFKA",
+		log.Fields{"device-id": aDeviceID, "with-EventName": de.DeviceEventName})
+}
+
+// createUniLockFsm initializes and runs the UniLock FSM to transfer the OMCI related commands for port lock/unlock
+func (dh *deviceHandler) createUniLockFsm(aAdminState bool, devEvent OnuDeviceEvent) {
+	chLSFsm := make(chan Message, 2048)
+	var sFsmName string
+	if aAdminState {
+		logger.Infow("createLockStateFSM", log.Fields{"device-id": dh.deviceID})
+		sFsmName = "LockStateFSM"
+	} else {
+		logger.Infow("createUnlockStateFSM", log.Fields{"device-id": dh.deviceID})
+		sFsmName = "UnLockStateFSM"
+	}
+
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+		return
+	}
+	pLSFsm := newLockStateFsm(pDevEntry.PDevOmciCC, aAdminState, devEvent,
+		sFsmName, dh.deviceID, chLSFsm)
+	if pLSFsm != nil {
+		if aAdminState {
+			dh.pLockStateFsm = pLSFsm
+		} else {
+			dh.pUnlockStateFsm = pLSFsm
+		}
+		dh.runUniLockFsm(aAdminState)
+	} else {
+		logger.Errorw("LockStateFSM could not be created - abort!!", log.Fields{"device-id": dh.deviceID})
+	}
+}
+
+// runUniLockFsm starts the UniLock FSM to transfer the OMCI related commands for port lock/unlock
+func (dh *deviceHandler) runUniLockFsm(aAdminState bool) {
+	/*  Uni Port lock/unlock procedure -
+	 ***** should run via 'adminDone' state and generate the argument requested event *****
+	 */
+	var pLSStatemachine *fsm.FSM
+	if aAdminState {
+		pLSStatemachine = dh.pLockStateFsm.pAdaptFsm.pFsm
+		//make sure the opposite FSM is not running and if so, terminate it as not relevant anymore
+		if (dh.pUnlockStateFsm != nil) &&
+			(dh.pUnlockStateFsm.pAdaptFsm.pFsm.Current() != uniStDisabled) {
+			_ = dh.pUnlockStateFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+		}
+	} else {
+		pLSStatemachine = dh.pUnlockStateFsm.pAdaptFsm.pFsm
+		//make sure the opposite FSM is not running and if so, terminate it as not relevant anymore
+		if (dh.pLockStateFsm != nil) &&
+			(dh.pLockStateFsm.pAdaptFsm.pFsm.Current() != uniStDisabled) {
+			_ = dh.pLockStateFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+		}
+	}
+	if pLSStatemachine != nil {
+		if pLSStatemachine.Is(uniStDisabled) {
+			if err := pLSStatemachine.Event(uniEvStart); err != nil {
+				logger.Warnw("LockStateFSM: can't start", log.Fields{"err": err})
+				// maybe try a FSM reset and then again ... - TODO!!!
+			} else {
+				/***** LockStateFSM started */
+				logger.Debugw("LockStateFSM started", log.Fields{
+					"state": pLSStatemachine.Current(), "device-id": dh.deviceID})
+			}
+		} else {
+			logger.Warnw("wrong state of LockStateFSM - want: disabled", log.Fields{
+				"have": pLSStatemachine.Current(), "device-id": dh.deviceID})
+			// maybe try a FSM reset and then again ... - TODO!!!
+		}
+	} else {
+		logger.Errorw("LockStateFSM StateMachine invalid - cannot be executed!!", log.Fields{"device-id": dh.deviceID})
+		// maybe try a FSM reset and then again ... - TODO!!!
+	}
+}
+
+//setBackend provides a DB backend for the specified path on the existing KV client
+func (dh *deviceHandler) setBackend(aBasePathKvStore string) *db.Backend {
+	addr := dh.pOpenOnuAc.KVStoreHost + ":" + strconv.Itoa(dh.pOpenOnuAc.KVStorePort)
+	logger.Debugw("SetKVStoreBackend", log.Fields{"IpTarget": addr,
+		"BasePathKvStore": aBasePathKvStore, "device-id": dh.deviceID})
+	kvbackend := &db.Backend{
+		Client:    dh.pOpenOnuAc.kvClient,
+		StoreType: dh.pOpenOnuAc.KVStoreType,
+		/* address config update acc. to [VOL-2736] */
+		Address:    addr,
+		Timeout:    dh.pOpenOnuAc.KVStoreTimeout,
+		PathPrefix: aBasePathKvStore}
+
+	return kvbackend
+}
+func (dh *deviceHandler) getFlowOfbFields(apFlowItem *ofp.OfpFlowStats, loMatchVlan *uint16,
+	loAddPcp *uint8, loIPProto *uint32) {
+
+	for _, field := range flow.GetOfbFields(apFlowItem) {
+		switch field.Type {
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_ETH_TYPE:
+			{
+				logger.Debugw("FlowAdd type EthType", log.Fields{"device-id": dh.deviceID,
+					"EthType": strconv.FormatInt(int64(field.GetEthType()), 16)})
+			}
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_IP_PROTO:
+			{
+				*loIPProto = field.GetIpProto()
+				logger.Debugw("FlowAdd type IpProto", log.Fields{"device-id": dh.deviceID,
+					"IpProto": strconv.FormatInt(int64(*loIPProto), 16)})
+				if *loIPProto == 2 {
+					// some workaround for TT workflow at proto == 2 (IGMP trap) -> ignore the flow
+					// avoids installing invalid EVTOCD rule
+					logger.Debugw("FlowAdd type IpProto 2: TT workaround: ignore flow",
+						log.Fields{"device-id": dh.deviceID,
+							"IpProto": strconv.FormatInt(int64(*loIPProto), 16)})
+					return
+				}
+			}
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID:
+			{
+				*loMatchVlan = uint16(field.GetVlanVid())
+				loMatchVlanMask := uint16(field.GetVlanVidMask())
+				if !(*loMatchVlan == uint16(of.OfpVlanId_OFPVID_PRESENT) &&
+					loMatchVlanMask == uint16(of.OfpVlanId_OFPVID_PRESENT)) {
+					*loMatchVlan = *loMatchVlan & 0xFFF // not transparent: copy only ID bits
+				}
+				logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+					"VID": strconv.FormatInt(int64(*loMatchVlan), 16)})
+			}
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP:
+			{
+				*loAddPcp = uint8(field.GetVlanPcp())
+				logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+					"PCP": loAddPcp})
+			}
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_UDP_DST:
+			{
+				logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+					"UDP-DST": strconv.FormatInt(int64(field.GetUdpDst()), 16)})
+			}
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC:
+			{
+				logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+					"UDP-SRC": strconv.FormatInt(int64(field.GetUdpSrc()), 16)})
+			}
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST:
+			{
+				logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+					"IPv4-DST": field.GetIpv4Dst()})
+			}
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_SRC:
+			{
+				logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+					"IPv4-SRC": field.GetIpv4Src()})
+			}
+		case of.OxmOfbFieldTypes_OFPXMT_OFB_METADATA:
+			{
+				logger.Debugw("FlowAdd field type", log.Fields{"device-id": dh.deviceID,
+					"Metadata": field.GetTableMetadata()})
+			}
+			/*
+				default:
+					{
+						//all other entires ignored
+					}
+			*/
+		}
+	} //for all OfbFields
+}
+
+func (dh *deviceHandler) getFlowActions(apFlowItem *ofp.OfpFlowStats, loSetPcp *uint8, loSetVlan *uint16) {
+	for _, action := range flow.GetActions(apFlowItem) {
+		switch action.Type {
+		/* not used:
+		case of.OfpActionType_OFPAT_OUTPUT:
+			{
+				logger.Debugw("FlowAdd action type", log.Fields{"device-id": dh.deviceID,
+					"Output": action.GetOutput()})
+			}
+		*/
+		case of.OfpActionType_OFPAT_PUSH_VLAN:
+			{
+				logger.Debugw("FlowAdd action type", log.Fields{"device-id": dh.deviceID,
+					"PushEthType": strconv.FormatInt(int64(action.GetPush().Ethertype), 16)})
+			}
+		case of.OfpActionType_OFPAT_SET_FIELD:
+			{
+				pActionSetField := action.GetSetField()
+				if pActionSetField.Field.OxmClass != of.OfpOxmClass_OFPXMC_OPENFLOW_BASIC {
+					logger.Warnw("FlowAdd action SetField invalid OxmClass (ignored)", log.Fields{"device-id": dh.deviceID,
+						"OxcmClass": pActionSetField.Field.OxmClass})
+				}
+				if pActionSetField.Field.GetOfbField().Type == of.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID {
+					*loSetVlan = uint16(pActionSetField.Field.GetOfbField().GetVlanVid())
+					logger.Debugw("FlowAdd Set VLAN from SetField action", log.Fields{"device-id": dh.deviceID,
+						"SetVlan": strconv.FormatInt(int64(*loSetVlan), 16)})
+				} else if pActionSetField.Field.GetOfbField().Type == of.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP {
+					*loSetPcp = uint8(pActionSetField.Field.GetOfbField().GetVlanPcp())
+					logger.Debugw("FlowAdd Set PCP from SetField action", log.Fields{"device-id": dh.deviceID,
+						"SetPcp": *loSetPcp})
+				} else {
+					logger.Warnw("FlowAdd action SetField invalid FieldType", log.Fields{"device-id": dh.deviceID,
+						"Type": pActionSetField.Field.GetOfbField().Type})
+				}
+			}
+			/*
+				default:
+					{
+						//all other entires ignored
+					}
+			*/
+		}
+	} //for all Actions
+}
+
+//addFlowItemToUniPort parses the actual flow item to add it to the UniPort
+func (dh *deviceHandler) addFlowItemToUniPort(apFlowItem *ofp.OfpFlowStats, apUniPort *onuUniPort) error {
+	var loSetVlan uint16 = uint16(of.OfpVlanId_OFPVID_NONE)      //noValidEntry
+	var loMatchVlan uint16 = uint16(of.OfpVlanId_OFPVID_PRESENT) //reserved VLANID entry
+	var loAddPcp, loSetPcp uint8
+	var loIPProto uint32
+	/* the TechProfileId is part of the flow Metadata - compare also comment within
+	 * OLT-Adapter:openolt_flowmgr.go
+	 *     Metadata 8 bytes:
+	 *	   Most Significant 2 Bytes = Inner VLAN
+	 *	   Next 2 Bytes = Tech Profile ID(TPID)
+	 *	   Least Significant 4 Bytes = Port ID
+	 *     Flow Metadata carries Tech-Profile (TP) ID and is mandatory in all
+	 *     subscriber related flows.
+	 */
+
+	metadata := flow.GetMetadataFromWriteMetadataAction(apFlowItem)
+	if metadata == 0 {
+		logger.Debugw("FlowAdd invalid metadata - abort",
+			log.Fields{"device-id": dh.deviceID})
+		return errors.New("flowAdd invalid metadata")
+	}
+	loTpID := flow.GetTechProfileIDFromWriteMetaData(metadata)
+	logger.Debugw("FlowAdd TechProfileId", log.Fields{"device-id": dh.deviceID, "TP-Id": loTpID})
+
+	dh.getFlowOfbFields(apFlowItem, &loMatchVlan, &loAddPcp, &loIPProto)
+	if loIPProto == 2 {
+		// some workaround for TT workflow at proto == 2 (IGMP trap) -> ignore the flow
+		// avoids installing invalid EVTOCD rule
+		logger.Debugw("FlowAdd type IpProto 2: TT workaround: ignore flow",
+			log.Fields{"device-id": dh.deviceID,
+				"IpProto": strconv.FormatInt(int64(loIPProto), 16)})
+		return nil
+	}
+	dh.getFlowActions(apFlowItem, &loSetPcp, &loSetVlan)
+
+	if loSetVlan == uint16(of.OfpVlanId_OFPVID_NONE) && loMatchVlan != uint16(of.OfpVlanId_OFPVID_PRESENT) {
+		logger.Errorw("FlowAdd aborted - SetVlanId undefined, but MatchVid set", log.Fields{
+			"device-id": dh.deviceID, "UniPort": apUniPort.portNo,
+			"set_vid":   strconv.FormatInt(int64(loSetVlan), 16),
+			"match_vid": strconv.FormatInt(int64(loMatchVlan), 16)})
+		//TODO!!: Use DeviceId within the error response to rwCore
+		//  likewise also in other error response cases to calling components as requested in [VOL-3458]
+		return errors.New("flowAdd Set/Match VlanId inconsistent")
+	}
+	if loSetVlan == uint16(of.OfpVlanId_OFPVID_NONE) && loMatchVlan == uint16(of.OfpVlanId_OFPVID_PRESENT) {
+		logger.Debugw("FlowAdd vlan-any/copy", log.Fields{"device-id": dh.deviceID})
+		loSetVlan = loMatchVlan //both 'transparent' (copy any)
+	} else {
+		//looks like OMCI value 4097 (copyFromOuter - for Uni double tagged) is not supported here
+		if loSetVlan != uint16(of.OfpVlanId_OFPVID_PRESENT) {
+			// not set to transparent
+			loSetVlan &= 0x0FFF //mask VID bits as prerequisite for vlanConfigFsm
+		}
+		logger.Debugw("FlowAdd vlan-set", log.Fields{"device-id": dh.deviceID})
+	}
+	if _, exist := dh.UniVlanConfigFsmMap[apUniPort.uniID]; exist {
+		logger.Errorw("FlowAdd aborted - FSM already running", log.Fields{
+			"device-id": dh.deviceID, "UniPort": apUniPort.portNo})
+		return errors.New("flowAdd FSM already running")
+	}
+	return dh.createVlanFilterFsm(apUniPort,
+		loTpID, loMatchVlan, loSetVlan, loSetPcp, OmciVlanFilterDone)
+}
+
+// createVlanFilterFsm initializes and runs the VlanFilter FSM to transfer OMCI related VLAN config
+func (dh *deviceHandler) createVlanFilterFsm(apUniPort *onuUniPort,
+	aTpID uint16, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8, aDevEvent OnuDeviceEvent) error {
+	chVlanFilterFsm := make(chan Message, 2048)
+
+	pDevEntry := dh.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice -aborting", log.Fields{"device-id": dh.deviceID})
+		return fmt.Errorf("no valid OnuDevice for device-id %x - aborting", dh.deviceID)
+	}
+
+	pVlanFilterFsm := NewUniVlanConfigFsm(dh, pDevEntry.PDevOmciCC, apUniPort, dh.pOnuTP,
+		pDevEntry.pOnuDB, aTpID, aDevEvent, "UniVlanConfigFsm", dh.deviceID, chVlanFilterFsm,
+		dh.pOpenOnuAc.AcceptIncrementalEvto, aMatchVlan, aSetVlan, aSetPcp)
+	if pVlanFilterFsm != nil {
+		dh.UniVlanConfigFsmMap[apUniPort.uniID] = pVlanFilterFsm
+		pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
+		if pVlanFilterStatemachine != nil {
+			if pVlanFilterStatemachine.Is(vlanStDisabled) {
+				if err := pVlanFilterStatemachine.Event(vlanEvStart); err != nil {
+					logger.Warnw("UniVlanConfigFsm: can't start", log.Fields{"err": err})
+					return fmt.Errorf("can't start UniVlanConfigFsm for device-id %x", dh.deviceID)
+				}
+				/***** UniVlanConfigFsm started */
+				logger.Debugw("UniVlanConfigFsm started", log.Fields{
+					"state": pVlanFilterStatemachine.Current(), "device-id": dh.deviceID,
+					"UniPort": apUniPort.portNo})
+			} else {
+				logger.Warnw("wrong state of UniVlanConfigFsm - want: disabled", log.Fields{
+					"have": pVlanFilterStatemachine.Current(), "device-id": dh.deviceID})
+				return fmt.Errorf("uniVlanConfigFsm not in expected disabled state for device-id %x", dh.deviceID)
+			}
+		} else {
+			logger.Errorw("UniVlanConfigFsm StateMachine invalid - cannot be executed!!", log.Fields{
+				"device-id": dh.deviceID})
+			return fmt.Errorf("uniVlanConfigFsm invalid for device-id %x", dh.deviceID)
+		}
+	} else {
+		logger.Errorw("UniVlanConfigFsm could not be created - abort!!", log.Fields{
+			"device-id": dh.deviceID, "UniPort": apUniPort.portNo})
+		return fmt.Errorf("uniVlanConfigFsm could not be created for device-id %x", dh.deviceID)
+	}
+	return nil
+}
+
+//verifyUniVlanConfigRequest checks on existence of flow configuration and starts it accordingly
+func (dh *deviceHandler) verifyUniVlanConfigRequest(apUniPort *onuUniPort) {
+	if pVlanFilterFsm, exist := dh.UniVlanConfigFsmMap[apUniPort.uniID]; exist {
+		//VlanFilterFsm exists and was already started (assumed to wait for TechProfile execution here)
+		pVlanFilterStatemachine := pVlanFilterFsm.pAdaptFsm.pFsm
+		if pVlanFilterStatemachine != nil {
+			if pVlanFilterStatemachine.Is(vlanStWaitingTechProf) {
+				if err := pVlanFilterStatemachine.Event(vlanEvContinueConfig); err != nil {
+					logger.Warnw("UniVlanConfigFsm: can't continue processing", log.Fields{"err": err})
+				} else {
+					/***** UniVlanConfigFsm continued */
+					logger.Debugw("UniVlanConfigFsm continued", log.Fields{
+						"state": pVlanFilterStatemachine.Current(), "device-id": dh.deviceID,
+						"UniPort": apUniPort.portNo})
+				}
+			} else {
+				logger.Debugw("no state of UniVlanConfigFsm to be continued", log.Fields{
+					"have": pVlanFilterStatemachine.Current(), "device-id": dh.deviceID})
+			}
+		} else {
+			logger.Debugw("UniVlanConfigFsm StateMachine does not exist, no flow processing", log.Fields{
+				"device-id": dh.deviceID})
+		}
+
+	} // else: nothing to do
+}
+
+//RemoveVlanFilterFsm deletes the stored pointer to the VlanConfigFsm
+// intention is to provide this method to be called from VlanConfigFsm itself, when resources (and methods!) are cleaned up
+func (dh *deviceHandler) RemoveVlanFilterFsm(apUniPort *onuUniPort) {
+	logger.Debugw("remove UniVlanConfigFsm StateMachine", log.Fields{
+		"device-id": dh.deviceID, "uniPort": apUniPort.portNo})
+	delete(dh.UniVlanConfigFsmMap, apUniPort.uniID)
+}
diff --git a/internal/pkg/onuadaptercore/messageTypes.go b/internal/pkg/onuadaptercore/messageTypes.go
new file mode 100644
index 0000000..892d058
--- /dev/null
+++ b/internal/pkg/onuadaptercore/messageTypes.go
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018-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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	gp "github.com/google/gopacket"
+	"github.com/opencord/omci-lib-go"
+)
+
+// MessageType - Message Protocol Type
+type MessageType uint8
+
+const (
+	TestMsg MessageType = iota
+	OMCI
+)
+
+// String - Return the text representation of the message type based on integer
+func (m MessageType) String() string {
+	names := [...]string{
+		"TestMsg",
+		"OMCI",
+	}
+	return names[m]
+}
+
+// Message - message type and data(OMCI)
+type Message struct {
+	Type MessageType
+	Data interface{}
+}
+
+//TestMessageType - message data for various events
+type TestMessageType uint8
+
+const (
+	LoadMibTemplateOk TestMessageType = iota + 1
+	LoadMibTemplateFailed
+	TimeOutOccurred
+	AbortMessageProcessing
+)
+
+//TestMessage - Struct to hold the message data
+//TODO: place holder to have a second interface variant - to be replaced by real variant later on
+type TestMessage struct {
+	TestMessageVal TestMessageType
+}
+
+//OmciMessage - OMCI protocol messages for managing and monitoring ONUs
+type OmciMessage struct {
+	OmciMsg    *omci.OMCI
+	OmciPacket *gp.Packet
+}
diff --git a/internal/pkg/onuadaptercore/mib_download.go b/internal/pkg/onuadaptercore/mib_download.go
new file mode 100644
index 0000000..baf2709
--- /dev/null
+++ b/internal/pkg/onuadaptercore/mib_download.go
@@ -0,0 +1,285 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"errors"
+	"time"
+
+	"github.com/looplab/fsm"
+
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+func (onuDeviceEntry *OnuDeviceEntry) enterDLStartingState(e *fsm.Event) {
+	logger.Debugw("MibDownload FSM", log.Fields{"Start downloading OMCI MIB in state": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	if onuDeviceEntry.omciMessageReceived == nil {
+		onuDeviceEntry.omciMessageReceived = make(chan bool)
+		logger.Debug("MibDownload FSM - defining the BridgeInit RxChannel")
+	}
+	go onuDeviceEntry.processMibDownloadMessages()
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterCreatingGalState(e *fsm.Event) {
+	logger.Debugw("MibDownload FSM", log.Fields{"Tx create::GAL Ethernet Profile in state": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	meInstance := onuDeviceEntry.PDevOmciCC.sendCreateGalEthernetProfile(context.TODO(), ConstDefaultOmciTimeout, true)
+	onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterSettingOnu2gState(e *fsm.Event) {
+	logger.Debugw("MibDownload FSM", log.Fields{"Tx Set::ONU2-G in state": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	meInstance := onuDeviceEntry.PDevOmciCC.sendSetOnu2g(context.TODO(), ConstDefaultOmciTimeout, true)
+	onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterBridgeInitState(e *fsm.Event) {
+	logger.Infow("MibDownload FSM - starting bridge config port loop", log.Fields{
+		"in state": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	go onuDeviceEntry.performInitialBridgeSetup()
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterDownloadedState(e *fsm.Event) {
+	logger.Debugw("MibDownload FSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	onuDeviceEntry.transferSystemEvent(MibDownloadDone)
+	pMibDlFsm := onuDeviceEntry.pMibDownloadFsm
+	if pMibDlFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				_ = a_pAFsm.pFsm.Event(dlEvReset)
+			}
+		}(pMibDlFsm)
+	}
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterResettingState(e *fsm.Event) {
+	logger.Debugw("MibDownload FSM resetting", log.Fields{"device-id": onuDeviceEntry.deviceID})
+	pMibDlFsm := onuDeviceEntry.pMibDownloadFsm
+	if pMibDlFsm != nil {
+		// abort running message processing
+		fsmAbortMsg := Message{
+			Type: TestMsg,
+			Data: TestMessage{
+				TestMessageVal: AbortMessageProcessing,
+			},
+		}
+		pMibDlFsm.commChan <- fsmAbortMsg
+
+		//try to restart the FSM to 'disabled'
+		// see DownloadedState: decouple event transfer
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				_ = a_pAFsm.pFsm.Event(dlEvRestart)
+			}
+		}(pMibDlFsm)
+	}
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) processMibDownloadMessages( /*ctx context.Context*/ ) {
+	logger.Debugw("Start MibDownload Msg processing", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": onuDeviceEntry.deviceID})
+		// 	break loop
+		// unless multiple channels are not involved, we should not use select
+		message, ok := <-onuDeviceEntry.pMibDownloadFsm.commChan
+		if !ok {
+			logger.Info("MibDownload Rx Msg", log.Fields{"Message couldn't be read from channel for device-id": onuDeviceEntry.deviceID})
+			// but then we have to ensure a restart of the FSM as well - as exceptional procedure
+			_ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvRestart)
+			break loop
+		}
+		logger.Debugw("MibDownload Rx Msg", log.Fields{"Received message for device-id": onuDeviceEntry.deviceID})
+
+		switch message.Type {
+		case TestMsg:
+			msg, _ := message.Data.(TestMessage)
+			if msg.TestMessageVal == AbortMessageProcessing {
+				logger.Infow("MibDownload abort ProcessMsg", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+				break loop
+			}
+			logger.Warnw("MibDownload unknown TestMessage", log.Fields{"device-id": onuDeviceEntry.deviceID, "MessageVal": msg.TestMessageVal})
+		case OMCI:
+			msg, _ := message.Data.(OmciMessage)
+			onuDeviceEntry.handleOmciMibDownloadMessage(msg)
+		default:
+			logger.Warn("MibDownload Rx Msg", log.Fields{"Unknown message type received for device-id": onuDeviceEntry.deviceID,
+				"message.Type": message.Type})
+		}
+
+	}
+	logger.Infow("End MibDownload Msg processing", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMibDownloadMessage(msg OmciMessage) {
+	logger.Debugw("Rx OMCI MibDownload Msg", log.Fields{"device-id": onuDeviceEntry.deviceID,
+		"msgType": msg.OmciMsg.MessageType})
+
+	switch msg.OmciMsg.MessageType {
+	case omci.CreateResponseType:
+		{
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
+			if msgLayer == nil {
+				logger.Error("Omci Msg layer could not be detected for CreateResponse")
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+			if !msgOk {
+				logger.Error("Omci Msg layer could not be assigned for CreateResponse")
+				return
+			}
+			logger.Debugw("CreateResponse Data", log.Fields{"device-id": onuDeviceEntry.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+				// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+				return
+			}
+			// maybe there is a way of pushing the specific create response type generally to the FSM
+			//   and let the FSM verify, if the response was according to current state
+			//   and possibly store the element to DB and progress - maybe some future option ...
+			// but as that is not straightforward to me I insert the type checkes manually here
+			//   and feed the FSM with only 'pre-defined' events ...
+			if msgObj.EntityClass == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetClassID() &&
+				msgObj.EntityInstance == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetEntityID() {
+				//store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+				// if, then something like:
+				//onuDeviceEntry.pOnuDB.StoreMe(msgObj)
+
+				// 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 onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetName() {
+				case "GalEthernetProfile":
+					{ // let the FSM proceed ...
+						_ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvRxGalResp)
+					}
+				case "MacBridgeServiceProfile",
+					"MacBridgePortConfigurationData",
+					"ExtendedVlanTaggingOperationConfigurationData":
+					{ // let bridge init proceed by stopping the wait function
+						onuDeviceEntry.omciMessageReceived <- true
+					}
+				}
+			}
+		} //CreateResponseType
+
+	case omci.SetResponseType:
+		{
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+			if msgLayer == nil {
+				logger.Error("Omci Msg layer could not be detected for SetResponse")
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.SetResponse)
+			if !msgOk {
+				logger.Error("Omci Msg layer could not be assigned for SetResponse")
+				return
+			}
+			logger.Debugw("SetResponse Data", log.Fields{"device-id": onuDeviceEntry.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				logger.Errorw("Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+				// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+				return
+			}
+			// compare comments above for CreateResponse (apply also here ...)
+			if msgObj.EntityClass == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetClassID() &&
+				msgObj.EntityInstance == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetEntityID() {
+				//store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+				// if, then something like:
+				//onuDeviceEntry.pOnuDB.StoreMe(msgObj)
+
+				switch onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetName() {
+				case "Onu2G":
+					{ // let the FSM proceed ...
+						_ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvRxOnu2gResp)
+					}
+					//so far that was the only MibDownlad Set Element ...
+				}
+			}
+		} //SetResponseType
+	default:
+		{
+			logger.Errorw("Rx OMCI MibDownload unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+			return
+		}
+	} // switch msg.OmciMsg.MessageType
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) performInitialBridgeSetup() {
+	for uniNo, uniPort := range onuDeviceEntry.baseDeviceHandler.uniEntityMap {
+		logger.Debugw("Starting IntialBridgeSetup", log.Fields{
+			"device-id": onuDeviceEntry.deviceID, "for PortNo": uniNo})
+
+		//create MBSP
+		meInstance := onuDeviceEntry.PDevOmciCC.sendCreateMBServiceProfile(
+			context.TODO(), uniPort, ConstDefaultOmciTimeout, true)
+		onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+		//verify response
+		err := onuDeviceEntry.waitforOmciResponse(meInstance)
+		if err != nil {
+			logger.Error("InitialBridgeSetup failed at MBSP, aborting MIB Download!")
+			_ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvReset)
+			return
+		}
+
+		//create MBPCD
+		meInstance = onuDeviceEntry.PDevOmciCC.sendCreateMBPConfigData(
+			context.TODO(), uniPort, ConstDefaultOmciTimeout, true)
+		onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+		//verify response
+		err = onuDeviceEntry.waitforOmciResponse(meInstance)
+		if err != nil {
+			logger.Error("InitialBridgeSetup failed at MBPCD, aborting MIB Download!")
+			_ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvReset)
+			return
+		}
+
+		//create EVTOCD
+		meInstance = onuDeviceEntry.PDevOmciCC.sendCreateEVTOConfigData(
+			context.TODO(), uniPort, ConstDefaultOmciTimeout, true)
+		onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+		//verify response
+		err = onuDeviceEntry.waitforOmciResponse(meInstance)
+		if err != nil {
+			logger.Error("InitialBridgeSetup failed at EVTOCD, aborting MIB Download!")
+			_ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvReset)
+			return
+		}
+	}
+	logger.Infow("IntialBridgeSetup finished", log.Fields{"device-id": onuDeviceEntry.deviceID})
+	_ = onuDeviceEntry.pMibDownloadFsm.pFsm.Event(dlEvRxBridgeResp)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) waitforOmciResponse(apMeInstance *me.ManagedEntity) error {
+	select {
+	case <-time.After(30 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+		logger.Warnw("MibDownload-bridge-init timeout", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+		return errors.New("mibDownloadBridgeInit timeout")
+	case success := <-onuDeviceEntry.omciMessageReceived:
+		if success {
+			logger.Debug("MibDownload-bridge-init response received")
+			return nil
+		}
+		// should not happen so far
+		logger.Warnw("MibDownload-bridge-init response error", log.Fields{"for device-id": onuDeviceEntry.deviceID})
+		return errors.New("mibDownloadBridgeInit responseError")
+	}
+}
diff --git a/internal/pkg/onuadaptercore/mib_sync.go b/internal/pkg/onuadaptercore/mib_sync.go
new file mode 100644
index 0000000..a42d3ce
--- /dev/null
+++ b/internal/pkg/onuadaptercore/mib_sync.go
@@ -0,0 +1,533 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"encoding/hex"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/looplab/fsm"
+
+	"time"
+
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+var supportedClassIds = []me.ClassID{
+	me.CardholderClassID,                              // 5
+	me.CircuitPackClassID,                             // 6
+	me.SoftwareImageClassID,                           // 7
+	me.PhysicalPathTerminationPointEthernetUniClassID, // 11
+	me.OltGClassID,                                    // 131
+	me.OnuPowerSheddingClassID,                        // 133
+	me.IpHostConfigDataClassID,                        // 134
+	me.OnuGClassID,                                    // 256
+	me.Onu2GClassID,                                   // 257
+	me.TContClassID,                                   // 262
+	me.AniGClassID,                                    // 263
+	me.UniGClassID,                                    // 264
+	me.PriorityQueueClassID,                           // 277
+	me.TrafficSchedulerClassID,                        // 278
+	me.VirtualEthernetInterfacePointClassID,           // 329
+	me.EnhancedSecurityControlClassID,                 // 332
+	me.OnuDynamicPowerManagementControlClassID,        // 336
+}
+
+var fsmMsg TestMessageType
+
+func (onuDeviceEntry *OnuDeviceEntry) enterStartingState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start processing MibSync-msgs in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	onuDeviceEntry.pOnuDB = newOnuDeviceDB(context.TODO(), onuDeviceEntry)
+	go onuDeviceEntry.processMibSyncMessages()
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterResettingMibState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start MibTemplate processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+
+	logger.Debugw("MibSync FSM", log.Fields{"send mibReset in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	_ = onuDeviceEntry.PDevOmciCC.sendMibReset(context.TODO(), ConstDefaultOmciTimeout, true)
+
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingVendorAndSerialState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start getting VendorId and SerialNumber in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	requestedAttributes := me.AttributeValueMap{"VendorId": "", "SerialNumber": 0}
+	meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.OnuGClassID, onugMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+	onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingEquipmentIDState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start getting EquipmentId in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	requestedAttributes := me.AttributeValueMap{"EquipmentId": ""}
+	meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.Onu2GClassID, onu2gMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+	onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingFirstSwVersionState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start getting IsActive and Version of first SW-image in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	requestedAttributes := me.AttributeValueMap{"IsActive": 0, "Version": ""}
+	meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.SoftwareImageClassID, firstSwImageMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+	onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingSecondSwVersionState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start getting IsActive and Version of second SW-image in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	requestedAttributes := me.AttributeValueMap{"IsActive": 0, "Version": ""}
+	meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.SoftwareImageClassID, secondSwImageMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+	onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingMacAddressState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start getting MacAddress in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	requestedAttributes := me.AttributeValueMap{"MacAddress": ""}
+	meInstance := onuDeviceEntry.PDevOmciCC.sendGetMe(context.TODO(), me.IpHostConfigDataClassID, ipHostConfigDataMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+	onuDeviceEntry.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterGettingMibTemplate(e *fsm.Event) {
+
+	for i := firstSwImageMeID; i <= secondSwImageMeID; i++ {
+		if onuDeviceEntry.swImages[i].isActive > 0 {
+			onuDeviceEntry.activeSwVersion = onuDeviceEntry.swImages[i].version
+		}
+	}
+
+	meStoredFromTemplate := false
+	path := fmt.Sprintf(cSuffixMibTemplateKvStore, onuDeviceEntry.vendorID, onuDeviceEntry.equipmentID, onuDeviceEntry.activeSwVersion)
+	logger.Debugw("MibSync FSM - MibTemplate - etcd search string", log.Fields{"path": path})
+	Value, err := onuDeviceEntry.mibTemplateKVStore.Get(context.TODO(), path)
+	if err == nil {
+		if Value != nil {
+			logger.Debugf("MibSync FSM - MibTemplate read: Key: %s, Value: %s  %s", Value.Key, Value.Value)
+
+			// swap out tokens with specific data
+			mibTmpString, _ := kvstore.ToString(Value.Value)
+			mibTmpString2 := strings.Replace(mibTmpString, "%SERIAL_NUMBER%", onuDeviceEntry.serialNumber, -1)
+			mibTmpString = strings.Replace(mibTmpString2, "%MAC_ADDRESS%", onuDeviceEntry.macAddress, -1)
+			mibTmpBytes := []byte(mibTmpString)
+			logger.Debugf("MibSync FSM - MibTemplate tokens swapped out: %s", mibTmpBytes)
+
+			var fistLevelMap map[string]interface{}
+			if err = json.Unmarshal(mibTmpBytes, &fistLevelMap); err != nil {
+				logger.Error("MibSync FSM - Failed to unmarshal template", log.Fields{"error": err, "device-id": onuDeviceEntry.deviceID})
+			} else {
+				for fistLevelKey, firstLevelValue := range fistLevelMap {
+					logger.Debugw("MibSync FSM - fistLevelKey", log.Fields{"fistLevelKey": fistLevelKey})
+					if uint16ValidNumber, err := strconv.ParseUint(fistLevelKey, 10, 16); err == nil {
+						meClassID := me.ClassID(uint16ValidNumber)
+						logger.Debugw("MibSync FSM - fistLevelKey is a number in uint16-range", log.Fields{"uint16ValidNumber": uint16ValidNumber})
+						if isSupportedClassID(meClassID) {
+							logger.Debugw("MibSync FSM - fistLevelKey is a supported classID", log.Fields{"meClassID": meClassID})
+							secondLevelMap := firstLevelValue.(map[string]interface{})
+							for secondLevelKey, secondLevelValue := range secondLevelMap {
+								logger.Debugw("MibSync FSM - secondLevelKey", log.Fields{"secondLevelKey": secondLevelKey})
+								if uint16ValidNumber, err := strconv.ParseUint(secondLevelKey, 10, 16); err == nil {
+									meEntityID := uint16(uint16ValidNumber)
+									logger.Debugw("MibSync FSM - secondLevelKey is a number and a valid EntityId", log.Fields{"meEntityID": meEntityID})
+									thirdLevelMap := secondLevelValue.(map[string]interface{})
+									for thirdLevelKey, thirdLevelValue := range thirdLevelMap {
+										if thirdLevelKey == "Attributes" {
+											logger.Debugw("MibSync FSM - thirdLevelKey refers to attributes", log.Fields{"thirdLevelKey": thirdLevelKey})
+											attributesMap := thirdLevelValue.(map[string]interface{})
+											logger.Debugw("MibSync FSM - attributesMap", log.Fields{"attributesMap": attributesMap})
+											onuDeviceEntry.pOnuDB.PutMe(meClassID, meEntityID, attributesMap)
+											meStoredFromTemplate = true
+										}
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		} else {
+			logger.Debugw("No MIB template found", log.Fields{"path": path, "device-id": onuDeviceEntry.deviceID})
+		}
+	} else {
+		logger.Errorf("Get from kvstore operation failed for path %s", path)
+	}
+	if meStoredFromTemplate {
+		logger.Debug("MibSync FSM - valid MEs stored from template")
+		onuDeviceEntry.pOnuDB.logMeDb()
+		fsmMsg = LoadMibTemplateOk
+	} else {
+		logger.Debug("MibSync FSM - no valid MEs stored from template - perform MIB-upload!")
+		fsmMsg = LoadMibTemplateFailed
+	}
+
+	mibSyncMsg := Message{
+		Type: TestMsg,
+		Data: TestMessage{
+			TestMessageVal: fsmMsg,
+		},
+	}
+	onuDeviceEntry.pMibUploadFsm.commChan <- mibSyncMsg
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterUploadingState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"send MibUpload in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	_ = onuDeviceEntry.PDevOmciCC.sendMibUpload(context.TODO(), ConstDefaultOmciTimeout, true)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterInSyncState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	onuDeviceEntry.transferSystemEvent(MibDatabaseSync)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterExaminingMdsState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start GetMds processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	logger.Debug("function not implemented yet")
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterResynchronizingState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start MibResync processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	logger.Debug("function not implemented yet")
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterAuditingState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start MibResync processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	logger.Debug("function not implemented yet")
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) enterOutOfSyncState(e *fsm.Event) {
+	logger.Debugw("MibSync FSM", log.Fields{"Start  MibReconcile processing in State": e.FSM.Current(), "device-id": onuDeviceEntry.deviceID})
+	logger.Debug("function not implemented yet")
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) processMibSyncMessages( /*ctx context.Context*/ ) {
+	logger.Debugw("MibSync Msg", log.Fields{"Start routine to process OMCI-messages for device-id": onuDeviceEntry.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": onuDeviceEntry.deviceID})
+		// 	break loop
+		message, ok := <-onuDeviceEntry.pMibUploadFsm.commChan
+		if !ok {
+			logger.Info("MibSync Msg", log.Fields{"Message couldn't be read from channel for device-id": onuDeviceEntry.deviceID})
+			break loop
+		}
+		logger.Debugw("MibSync Msg", log.Fields{"Received message on ONU MibSyncChan for device-id": onuDeviceEntry.deviceID})
+
+		switch message.Type {
+		case TestMsg:
+			msg, _ := message.Data.(TestMessage)
+			onuDeviceEntry.handleTestMsg(msg)
+		case OMCI:
+			msg, _ := message.Data.(OmciMessage)
+			onuDeviceEntry.handleOmciMessage(msg)
+		default:
+			logger.Warn("MibSync Msg", log.Fields{"Unknown message type received for device-id": onuDeviceEntry.deviceID, "message.Type": message.Type})
+		}
+	}
+	logger.Info("MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": onuDeviceEntry.deviceID})
+	_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvStop)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleTestMsg(msg TestMessage) {
+
+	logger.Debugw("MibSync Msg", log.Fields{"TestMessage received for device-id": onuDeviceEntry.deviceID, "msg.TestMessageVal": msg.TestMessageVal})
+
+	switch msg.TestMessageVal {
+	case LoadMibTemplateFailed:
+		_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvUploadMib)
+		logger.Debugw("MibSync Msg", log.Fields{"state": string(onuDeviceEntry.pMibUploadFsm.pFsm.Current())})
+	case LoadMibTemplateOk:
+		_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvSuccess)
+		logger.Debugw("MibSync Msg", log.Fields{"state": string(onuDeviceEntry.pMibUploadFsm.pFsm.Current())})
+	default:
+		logger.Warn("MibSync Msg", log.Fields{"Unknown message type received for device-id": onuDeviceEntry.deviceID, "msg.TestMessageVal": msg.TestMessageVal})
+	}
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMibResetResponseMessage(msg OmciMessage) {
+	if onuDeviceEntry.pMibUploadFsm.pFsm.Is(ulStResettingMib) {
+		msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibResetResponse)
+		if msgLayer != nil {
+			msgObj, msgOk := msgLayer.(*omci.MibResetResponse)
+			if msgOk {
+				logger.Debugw("MibResetResponse Data", log.Fields{"data-fields": msgObj})
+				if msgObj.Result == me.Success {
+					// trigger retrieval of VendorId and SerialNumber
+					_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetVendorAndSerial)
+					return
+				}
+				logger.Errorw("Omci MibResetResponse Error", log.Fields{"Error": msgObj.Result})
+			} else {
+				logger.Error("Omci Msg layer could not be assigned")
+			}
+		} else {
+			logger.Error("Omci Msg layer could not be detected")
+		}
+	} else {
+		logger.Errorw("Omci MibResetResponse received", log.Fields{"in state ": onuDeviceEntry.pMibUploadFsm.pFsm.Current})
+	}
+	logger.Info("MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": onuDeviceEntry.deviceID})
+	_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvStop)
+
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMibUploadResponseMessage(msg OmciMessage) {
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibUploadResponse)
+	if msgLayer == nil {
+		logger.Error("Omci Msg layer could not be detected")
+		return
+	}
+	msgObj, msgOk := msgLayer.(*omci.MibUploadResponse)
+	if !msgOk {
+		logger.Error("Omci Msg layer could not be assigned")
+		return
+	}
+	logger.Debugw("MibUploadResponse Data for:", log.Fields{"deviceId": onuDeviceEntry.deviceID, "data-fields": msgObj})
+	/* to be verified / reworked !!! */
+	onuDeviceEntry.PDevOmciCC.uploadNoOfCmds = msgObj.NumberOfCommands
+	if onuDeviceEntry.PDevOmciCC.uploadSequNo < onuDeviceEntry.PDevOmciCC.uploadNoOfCmds {
+		_ = onuDeviceEntry.PDevOmciCC.sendMibUploadNext(context.TODO(), ConstDefaultOmciTimeout, true)
+	} else {
+		logger.Error("Invalid number of commands received for:", log.Fields{"deviceId": onuDeviceEntry.deviceID, "uploadNoOfCmds": onuDeviceEntry.PDevOmciCC.uploadNoOfCmds})
+		//TODO right action?
+		_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvTimeout)
+	}
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMibUploadNextResponseMessage(msg OmciMessage) {
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeMibUploadNextResponse)
+	if msgLayer == nil {
+		logger.Error("Omci Msg layer could not be detected")
+		return
+	}
+	msgObj, msgOk := msgLayer.(*omci.MibUploadNextResponse)
+	if !msgOk {
+		logger.Error("Omci Msg layer could not be assigned")
+		return
+	}
+	if onuDeviceEntry.mibDebugLevel == "VERBOSE" {
+		logger.Debugw("MibUploadNextResponse Data for:", log.Fields{"deviceId": onuDeviceEntry.deviceID, "data-fields": msgObj})
+	}
+	meClassID := msgObj.ReportedME.GetClassID()
+	meEntityID := msgObj.ReportedME.GetEntityID()
+	meAttributes := msgObj.ReportedME.GetAttributeValueMap()
+
+	onuDeviceEntry.pOnuDB.PutMe(meClassID, meEntityID, meAttributes)
+
+	if onuDeviceEntry.PDevOmciCC.uploadSequNo < onuDeviceEntry.PDevOmciCC.uploadNoOfCmds {
+		_ = onuDeviceEntry.PDevOmciCC.sendMibUploadNext(context.TODO(), ConstDefaultOmciTimeout, true)
+	} else {
+		onuDeviceEntry.pOnuDB.logMeDb()
+		err := onuDeviceEntry.createAndPersistMibTemplate()
+		if err != nil {
+			logger.Errorw("MibSync - MibTemplate - Failed to create and persist the mib template", log.Fields{"error": err, "device-id": onuDeviceEntry.deviceID})
+		}
+
+		_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvSuccess)
+	}
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciGetResponseMessage(msg OmciMessage) {
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeGetResponse)
+	if msgLayer != nil {
+		msgObj, msgOk := msgLayer.(*omci.GetResponse)
+		if msgOk {
+			logger.Debugw("MibSync FSM - GetResponse Data", log.Fields{"deviceId": onuDeviceEntry.deviceID, "data-fields": msgObj})
+			if msgObj.Result == me.Success {
+				entityID := onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetEntityID()
+				if msgObj.EntityClass == onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetClassID() && msgObj.EntityInstance == entityID {
+					meAttributes := msgObj.Attributes
+					meInstance := onuDeviceEntry.PDevOmciCC.pLastTxMeInstance.GetName()
+					if onuDeviceEntry.mibDebugLevel == "VERBOSE" {
+						logger.Debugf("MibSync FSM - GetResponse Data for %s", log.Fields{"deviceId": onuDeviceEntry.deviceID, "data-fields": msgObj}, meInstance)
+					}
+					switch meInstance {
+					case "OnuG":
+						onuDeviceEntry.vendorID = fmt.Sprintf("%s", meAttributes["VendorId"])
+						snBytes, _ := me.InterfaceToOctets(meAttributes["SerialNumber"])
+						if onugSerialNumberLen == len(snBytes) {
+							snVendorPart := fmt.Sprintf("%s", snBytes[:4])
+							snNumberPart := hex.EncodeToString(snBytes[4:])
+							onuDeviceEntry.serialNumber = snVendorPart + snNumberPart
+							logger.Debugw("MibSync FSM - GetResponse Data for Onu-G - VendorId/SerialNumber", log.Fields{"deviceId": onuDeviceEntry.deviceID,
+								"onuDeviceEntry.vendorID": onuDeviceEntry.vendorID, "onuDeviceEntry.serialNumber": onuDeviceEntry.serialNumber})
+						} else {
+							logger.Errorw("MibSync FSM - SerialNumber has wrong length", log.Fields{"deviceId": onuDeviceEntry.deviceID, "length": len(snBytes)})
+						}
+						// trigger retrieval of EquipmentId
+						_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetEquipmentID)
+						return
+					case "Onu2G":
+						onuDeviceEntry.equipmentID = fmt.Sprintf("%s", meAttributes["EquipmentId"])
+						logger.Debugw("MibSync FSM - GetResponse Data for Onu2-G - EquipmentId", log.Fields{"deviceId": onuDeviceEntry.deviceID,
+							"onuDeviceEntry.equipmentID": onuDeviceEntry.equipmentID})
+						// trigger retrieval of 1st SW-image info
+						_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetFirstSwVersion)
+						return
+					case "SoftwareImage":
+						if entityID <= secondSwImageMeID {
+							onuDeviceEntry.swImages[entityID].version = fmt.Sprintf("%s", meAttributes["Version"])
+							onuDeviceEntry.swImages[entityID].isActive = meAttributes["IsActive"].(uint8)
+							logger.Debugw("MibSync FSM - GetResponse Data for SoftwareImage - Version/IsActive",
+								log.Fields{"deviceId": onuDeviceEntry.deviceID, "entityID": entityID,
+									"version": onuDeviceEntry.swImages[entityID].version, "isActive": onuDeviceEntry.swImages[entityID].isActive})
+						} else {
+							//TODO: error handling
+							logger.Errorw("MibSync FSM - Failed to GetResponse Data for SoftwareImage", log.Fields{"deviceId": onuDeviceEntry.deviceID})
+
+						}
+						if firstSwImageMeID == entityID {
+							_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetSecondSwVersion)
+							return
+						} else if secondSwImageMeID == entityID {
+							_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetMacAddress)
+							return
+						}
+					case "IpHostConfigData":
+						macBytes, _ := me.InterfaceToOctets(meAttributes["MacAddress"])
+						if omciMacAddressLen == len(macBytes) {
+							onuDeviceEntry.macAddress = hex.EncodeToString(macBytes[:])
+							logger.Debugw("MibSync FSM - GetResponse Data for IpHostConfigData - MacAddress", log.Fields{"deviceId": onuDeviceEntry.deviceID,
+								"onuDeviceEntry.macAddress": onuDeviceEntry.macAddress})
+						} else {
+							logger.Errorw("MibSync FSM - MacAddress wrong length", log.Fields{"deviceId": onuDeviceEntry.deviceID, "length": len(macBytes)})
+						}
+						// trigger retrieval of mib template
+						_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvGetMibTemplate)
+						return
+					}
+				}
+			} else {
+				logger.Errorw("Omci GetResponse Error", log.Fields{"Error": msgObj.Result})
+			}
+		} else {
+			logger.Error("Omci Msg layer could not be assigned for GetResponse")
+		}
+	} else {
+		logger.Error("Omci Msg layer could not be detected for GetResponse")
+	}
+	logger.Info("MibSync Msg", log.Fields{"Stopped handling of MibSyncChan for device-id": onuDeviceEntry.deviceID})
+	_ = onuDeviceEntry.pMibUploadFsm.pFsm.Event(ulEvStop)
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) handleOmciMessage(msg OmciMessage) {
+	if onuDeviceEntry.mibDebugLevel == "VERBOSE" {
+		logger.Debugw("MibSync Msg", log.Fields{"OmciMessage received for device-id": onuDeviceEntry.deviceID,
+			"msgType": msg.OmciMsg.MessageType})
+	}
+	switch msg.OmciMsg.MessageType {
+	case omci.MibResetResponseType:
+		onuDeviceEntry.handleOmciMibResetResponseMessage(msg)
+
+	case omci.MibUploadResponseType:
+		onuDeviceEntry.handleOmciMibUploadResponseMessage(msg)
+
+	case omci.MibUploadNextResponseType:
+		onuDeviceEntry.handleOmciMibUploadNextResponseMessage(msg)
+
+	case omci.GetResponseType:
+		onuDeviceEntry.handleOmciGetResponseMessage(msg)
+
+	}
+}
+
+func isSupportedClassID(meClassID me.ClassID) bool {
+	for _, v := range supportedClassIds {
+		if v == meClassID {
+			return true
+		}
+	}
+	return false
+}
+
+func (onuDeviceEntry *OnuDeviceEntry) mibDbVolatileDict() error {
+	logger.Debug("MibVolatileDict- running from default Entry code")
+	return errors.New("not_implemented")
+}
+
+// createAndPersistMibTemplate method creates a mib template for the device id when operator enables the ONU device for the first time.
+// We are creating a placeholder for "SerialNumber" for ME Class ID 6 and 256 and "MacAddress" for ME Class ID 134 in the template
+// and then storing the template into etcd "service/voltha/omci_mibs/templates/verdor_id/equipment_id/software_version" path.
+func (onuDeviceEntry *OnuDeviceEntry) createAndPersistMibTemplate() error {
+	path := fmt.Sprintf(cSuffixMibTemplateKvStore, onuDeviceEntry.vendorID, onuDeviceEntry.equipmentID, onuDeviceEntry.activeSwVersion)
+	logger.Debugw("MibSync - MibTemplate - key name", log.Fields{"path": path})
+	currentTime := time.Now()
+
+	templateMap := make(map[string]interface{})
+	templateMap["TemplateName"] = path
+	templateMap["TemplateCreated"] = currentTime.Format("2006-01-02 15:04:05.000000")
+
+	firstLevelMap := onuDeviceEntry.pOnuDB.meDb
+	for firstLevelKey, firstLevelValue := range firstLevelMap {
+		logger.Debugw("MibSync - MibTemplate - firstLevelKey", log.Fields{"firstLevelKey": firstLevelKey})
+		classID := strconv.Itoa(int(firstLevelKey))
+
+		secondLevelMap := make(map[string]interface{})
+		for secondLevelKey, secondLevelValue := range firstLevelValue {
+			thirdLevelMap := make(map[string]interface{})
+			entityID := strconv.Itoa(int(secondLevelKey))
+			thirdLevelMap["Attributes"] = secondLevelValue
+			thirdLevelMap["InstanceId"] = entityID
+			secondLevelMap[entityID] = thirdLevelMap
+			if classID == "6" || classID == "256" {
+				forthLevelMap := map[string]interface{}(thirdLevelMap["Attributes"].(me.AttributeValueMap))
+				delete(forthLevelMap, "SerialNumber")
+				forthLevelMap["SerialNumber"] = "%SERIAL_NUMBER%"
+
+			}
+			if classID == "134" {
+				forthLevelMap := map[string]interface{}(thirdLevelMap["Attributes"].(me.AttributeValueMap))
+				delete(forthLevelMap, "MacAddress")
+				forthLevelMap["MacAddress"] = "%MAC_ADDRESS%"
+			}
+		}
+		secondLevelMap["ClassId"] = classID
+		templateMap[classID] = secondLevelMap
+	}
+	mibTemplate, err := json.Marshal(&templateMap)
+	if err != nil {
+		logger.Errorw("MibSync - MibTemplate - Failed to marshal mibTemplate", log.Fields{"error": err, "device-id": onuDeviceEntry.deviceID})
+		return err
+	}
+	err = onuDeviceEntry.mibTemplateKVStore.Put(context.TODO(), path, string(mibTemplate))
+	if err != nil {
+		logger.Errorw("MibSync - MibTemplate - Failed to store template in etcd", log.Fields{"error": err, "device-id": onuDeviceEntry.deviceID})
+		return err
+	}
+	logger.Debugw("MibSync - MibTemplate - Stored the template to etcd", log.Fields{"device-id": onuDeviceEntry.deviceID})
+	return nil
+}
+
+// func (onuDeviceEntry *OnuDeviceEntry) MibTemplateTask() error {
+// 	return errors.New("not_implemented")
+// }
+// func (onuDeviceEntry *OnuDeviceEntry) MibUploadTask() error {
+// 	return errors.New("not_implemented")
+// }
+// func (onuDeviceEntry *OnuDeviceEntry) GetMdsTask() error {
+// 	return errors.New("not_implemented")
+// }
+// func (onuDeviceEntry *OnuDeviceEntry) MibResyncTask() error {
+// 	return errors.New("not_implemented")
+// }
+// func (onuDeviceEntry *OnuDeviceEntry) MibReconcileTask() error {
+// 	return errors.New("not_implemented")
+// }
diff --git a/internal/pkg/onuadaptercore/olt_onu_msg.go b/internal/pkg/onuadaptercore/olt_onu_msg.go
new file mode 100644
index 0000000..f18910a
--- /dev/null
+++ b/internal/pkg/onuadaptercore/olt_onu_msg.go
@@ -0,0 +1,67 @@
+/*
+ * 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 adaptercoreonu
+
+import (
+	"context"
+	"encoding/json"
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+	ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+	oop "github.com/opencord/voltha-protos/v3/go/openolt"
+)
+
+//AdditionalMessage ...
+type AdditionalMessage struct {
+}
+
+/*
+func (dh *deviceHandler) receivedMsgFromOlt(msg *ic.InterAdapterMessage) error {
+	msgBody := msg.GetBody()
+	ind := &oop.OnuIndication{}
+
+	err := ptypes.UnmarshalAny(msgBody, ind)
+	if err != nil {
+		logger.Debugw("cannot-unmarshal-onu-indication-body", log.Fields{"error": err})
+	} else {
+		var info AdditionalMessage
+		err = json.Unmarshal(ind.SerialNumber.VendorSpecific, &info)
+		if err != nil {
+			logger.Debugw("cannot-unmarshal-additional-message", log.Fields{"error": err})
+		}
+	}
+
+	return err
+}
+*/
+func (dh *deviceHandler) sendMsgToOlt(ctx context.Context, msg string, option interface{}) error {
+	bytes, _ := json.Marshal(option)
+	info := oop.OnuIndication{
+		SerialNumber: &oop.SerialNumber{
+			VendorSpecific: bytes,
+		},
+	}
+
+	err := dh.AdapterProxy.SendInterAdapterMessage(ctx, &info,
+		ic.InterAdapterMessageType_ONU_IND_REQUEST,
+		dh.DeviceType, dh.ProxyAddressType,
+		dh.deviceID, dh.parentID, msg)
+
+	if err != nil {
+		logger.Debugw("err-sending-message", log.Fields{"device": dh})
+	}
+	return err
+}
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
new file mode 100644
index 0000000..d7c496c
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -0,0 +1,782 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"strconv"
+	"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/v3/pkg/log"
+)
+
+const (
+	aniEvStart           = "uniEvStart"
+	aniEvStartConfig     = "aniEvStartConfig"
+	aniEvRxDot1pmapCResp = "aniEvRxDot1pmapCResp"
+	aniEvRxMbpcdResp     = "aniEvRxMbpcdResp"
+	aniEvRxTcontsResp    = "aniEvRxTcontsResp"
+	aniEvRxGemntcpsResp  = "aniEvRxGemntcpsResp"
+	aniEvRxGemiwsResp    = "aniEvRxGemiwsResp"
+	aniEvRxPrioqsResp    = "aniEvRxPrioqsResp"
+	aniEvRxDot1pmapSResp = "aniEvRxDot1pmapSResp"
+	aniEvTimeoutSimple   = "aniEvTimeoutSimple"
+	aniEvTimeoutMids     = "aniEvTimeoutMids"
+	aniEvReset           = "aniEvReset"
+	aniEvRestart         = "aniEvRestart"
+)
+const (
+	aniStDisabled            = "aniStDisabled"
+	aniStStarting            = "aniStStarting"
+	aniStCreatingDot1PMapper = "aniStCreatingDot1PMapper"
+	aniStCreatingMBPCD       = "aniStCreatingMBPCD"
+	aniStSettingTconts       = "aniStSettingTconts"
+	aniStCreatingGemNCTPs    = "aniStCreatingGemNCTPs"
+	aniStCreatingGemIWs      = "aniStCreatingGemIWs"
+	aniStSettingPQs          = "aniStSettingPQs"
+	aniStSettingDot1PMapper  = "aniStSettingDot1PMapper"
+	aniStConfigDone          = "aniStConfigDone"
+	aniStResetting           = "aniStResetting"
+)
+
+type ponAniGemPortAttribs struct {
+	gemPortID   uint16
+	upQueueID   uint16
+	downQueueID uint16
+	direction   uint8
+	qosPolicy   string
+	weight      uint8
+	pbitString  string
+}
+
+//uniPonAniConfigFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+type uniPonAniConfigFsm struct {
+	pOmciCC                  *omciCC
+	pOnuUniPort              *onuUniPort
+	pUniTechProf             *onuUniTechProf
+	pOnuDB                   *onuDeviceDB
+	techProfileID            uint16
+	requestEvent             OnuDeviceEvent
+	omciMIdsResponseReceived chan bool //separate channel needed for checking multiInstance OMCI message responses
+	pAdaptFsm                *AdapterFsm
+	aniConfigCompleted       bool
+	chSuccess                chan<- uint8
+	procStep                 uint8
+	chanSet                  bool
+	mapperSP0ID              uint16
+	macBPCD0ID               uint16
+	tcont0ID                 uint16
+	alloc0ID                 uint16
+	gemPortAttribsSlice      []ponAniGemPortAttribs
+}
+
+//newUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+func newUniPonAniConfigFsm(apDevOmciCC *omciCC, apUniPort *onuUniPort, apUniTechProf *onuUniTechProf,
+	apOnuDB *onuDeviceDB, aTechProfileID uint16, aRequestEvent OnuDeviceEvent, aName string,
+	aDeviceID string, aCommChannel chan Message) *uniPonAniConfigFsm {
+	instFsm := &uniPonAniConfigFsm{
+		pOmciCC:            apDevOmciCC,
+		pOnuUniPort:        apUniPort,
+		pUniTechProf:       apUniTechProf,
+		pOnuDB:             apOnuDB,
+		techProfileID:      aTechProfileID,
+		requestEvent:       aRequestEvent,
+		aniConfigCompleted: false,
+		chanSet:            false,
+	}
+	instFsm.pAdaptFsm = NewAdapterFsm(aName, aDeviceID, aCommChannel)
+	if instFsm.pAdaptFsm == nil {
+		logger.Errorw("uniPonAniConfigFsm's AdapterFsm could not be instantiated!!", log.Fields{
+			"device-id": aDeviceID})
+		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},
+
+			{Name: aniEvTimeoutSimple, Src: []string{
+				aniStCreatingDot1PMapper, aniStCreatingMBPCD, aniStSettingTconts, aniStSettingDot1PMapper}, 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}, Dst: aniStResetting},
+			// the only way to get to resource-cleared disabled state again is via "resseting"
+			{Name: aniEvRestart, Src: []string{aniStResetting}, Dst: aniStDisabled},
+		},
+
+		fsm.Callbacks{
+			"enter_state":                         func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
+			("enter_" + aniStStarting):            func(e *fsm.Event) { instFsm.enterConfigStartingState(e) },
+			("enter_" + aniStCreatingDot1PMapper): func(e *fsm.Event) { instFsm.enterCreatingDot1PMapper(e) },
+			("enter_" + aniStCreatingMBPCD):       func(e *fsm.Event) { instFsm.enterCreatingMBPCD(e) },
+			("enter_" + aniStSettingTconts):       func(e *fsm.Event) { instFsm.enterSettingTconts(e) },
+			("enter_" + aniStCreatingGemNCTPs):    func(e *fsm.Event) { instFsm.enterCreatingGemNCTPs(e) },
+			("enter_" + aniStCreatingGemIWs):      func(e *fsm.Event) { instFsm.enterCreatingGemIWs(e) },
+			("enter_" + aniStSettingPQs):          func(e *fsm.Event) { instFsm.enterSettingPQs(e) },
+			("enter_" + aniStSettingDot1PMapper):  func(e *fsm.Event) { instFsm.enterSettingDot1PMapper(e) },
+			("enter_" + aniStConfigDone):          func(e *fsm.Event) { instFsm.enterAniConfigDone(e) },
+			("enter_" + aniStResetting):           func(e *fsm.Event) { instFsm.enterResettingState(e) },
+			("enter_" + aniStDisabled):            func(e *fsm.Event) { instFsm.enterDisabledState(e) },
+		},
+	)
+	if instFsm.pAdaptFsm.pFsm == nil {
+		logger.Errorw("uniPonAniConfigFsm's Base FSM could not be instantiated!!", log.Fields{
+			"device-id": aDeviceID})
+		return nil
+	}
+
+	logger.Infow("uniPonAniConfigFsm created", log.Fields{"device-id": aDeviceID})
+	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.chanSet = true
+}
+
+func (oFsm *uniPonAniConfigFsm) prepareAndEnterConfigState(aPAFsm *AdapterFsm) {
+	if aPAFsm != nil && aPAFsm.pFsm != nil {
+		//stick to pythonAdapter numbering scheme
+		//index 0 in naming refers to possible usage of multiple instances (later)
+		oFsm.mapperSP0ID = ieeeMapperServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo) + oFsm.techProfileID
+		oFsm.macBPCD0ID = macBridgePortAniEID + uint16(oFsm.pOnuUniPort.entityID) + oFsm.techProfileID
+
+		// For the time being: if there are multiple T-Conts on the ONU the first one from the entityID-ordered list is used
+		// TODO!: if more T-Conts have to be supported (tcontXID!), then use the first instances of the entity-ordered list
+		// or use the py code approach, which might be a bit more complicated, but also more secure, as it
+		// ensures that the selected T-Cont also has queues (which I would assume per definition from ONU, but who knows ...)
+		// so this approach would search the (sorted) upstream PrioQueue list and use the T-Cont (if available) from highest Bytes
+		// or sndHighByte of relatedPort Attribute (T-Cont Reference) and in case of multiple TConts find the next free TContIndex
+		// that way from PrioQueue.relatedPort list
+		if tcontInstKeys := oFsm.pOnuDB.getSortedInstKeys(me.TContClassID); len(tcontInstKeys) > 0 {
+			oFsm.tcont0ID = tcontInstKeys[0]
+			logger.Debugw("Used TcontId:", log.Fields{"TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+				"device-id": oFsm.pAdaptFsm.deviceID})
+		} else {
+			logger.Warnw("No TCont instances found", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+		}
+		oFsm.alloc0ID = (*(oFsm.pUniTechProf.mapPonAniConfig[oFsm.pOnuUniPort.uniID]))[0].tcontParams.allocID
+		loGemPortAttribs := ponAniGemPortAttribs{}
+		//for all TechProfile set GemIndices
+		for _, gemEntry := range (*(oFsm.pUniTechProf.mapPonAniConfig[oFsm.pOnuUniPort.uniID]))[0].mapGemPortParams {
+			//collect all GemConfigData in a separate Fsm related slice (needed also to avoid mix-up with unsorted mapPonAniConfig)
+
+			if queueInstKeys := oFsm.pOnuDB.getSortedInstKeys(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("UpQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+										"upQueueID": strconv.FormatInt(int64(loGemPortAttribs.upQueueID), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+									usQueueFound = true
+								} else if (relatedPort&0xFFFFFF) == dsQrelPortMask && mgmtEntityID < 0x8000 {
+									loGemPortAttribs.downQueueID = mgmtEntityID
+									logger.Debugw("DownQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+										"downQueueID": strconv.FormatInt(int64(loGemPortAttribs.downQueueID), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+									dsQueueFound = true
+								}
+								if usQueueFound && dsQueueFound {
+									break
+								}
+							} else {
+								logger.Warnw("Could not convert attribute value", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+							}
+						} else {
+							logger.Warnw("'RelatedPort' not found in meAttributes:", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+						}
+					} else {
+						logger.Warnw("No attributes available in DB:", log.Fields{"meClassID": me.PriorityQueueClassID,
+							"mgmtEntityID": mgmtEntityID, "device-id": oFsm.pAdaptFsm.deviceID})
+					}
+				}
+			} else {
+				logger.Warnw("No PriorityQueue instances found", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+			}
+			loGemPortAttribs.direction = gemEntry.direction
+			loGemPortAttribs.qosPolicy = gemEntry.queueSchedPolicy
+			loGemPortAttribs.weight = gemEntry.queueWeight
+			loGemPortAttribs.pbitString = gemEntry.pbitString
+
+			logger.Debugw("prio-related GemPort attributes:", log.Fields{
+				"gemPortID":      loGemPortAttribs.gemPortID,
+				"upQueueID":      loGemPortAttribs.upQueueID,
+				"downQueueID":    loGemPortAttribs.downQueueID,
+				"pbitString":     loGemPortAttribs.pbitString,
+				"prioQueueIndex": gemEntry.prioQueueIndex,
+			})
+
+			oFsm.gemPortAttribsSlice = append(oFsm.gemPortAttribsSlice, loGemPortAttribs)
+		}
+		_ = aPAFsm.pFsm.Event(aniEvStartConfig)
+	}
+}
+
+func (oFsm *uniPonAniConfigFsm) enterConfigStartingState(e *fsm.Event) {
+	logger.Debugw("UniPonAniConfigFsm start", log.Fields{"in state": e.FSM.Current(),
+		"device-id": oFsm.pAdaptFsm.deviceID})
+	if oFsm.omciMIdsResponseReceived == nil {
+		oFsm.omciMIdsResponseReceived = make(chan bool)
+		logger.Debug("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
+		}
+	}
+	oFsm.gemPortAttribsSlice = nil
+
+	go oFsm.processOmciAniMessages()
+
+	pConfigAniStateAFsm := oFsm.pAdaptFsm
+	if pConfigAniStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go oFsm.prepareAndEnterConfigState(pConfigAniStateAFsm)
+
+	}
+}
+
+func (oFsm *uniPonAniConfigFsm) enterCreatingDot1PMapper(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm Tx Create::Dot1PMapper", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+		"in state":  e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	meInstance := oFsm.pOmciCC.sendCreateDot1PMapper(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.mapperSP0ID, oFsm.pAdaptFsm.commChan)
+	oFsm.pOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (oFsm *uniPonAniConfigFsm) enterCreatingMBPCD(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm Tx Create::MBPCD", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16),
+		"TPPtr":     strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+		"in state":  e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	bridgePtr := 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,
+		},
+	}
+	meInstance := oFsm.pOmciCC.sendCreateMBPConfigDataVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.pAdaptFsm.commChan, meParams)
+	oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+}
+
+func (oFsm *uniPonAniConfigFsm) enterSettingTconts(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm Tx Set::Tcont", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+		"AllocId":   strconv.FormatInt(int64(oFsm.alloc0ID), 16),
+		"in state":  e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	meParams := me.ParamData{
+		EntityID: oFsm.tcont0ID,
+		Attributes: me.AttributeValueMap{
+			"AllocId": oFsm.alloc0ID,
+		},
+	}
+	meInstance := oFsm.pOmciCC.sendSetTcontVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.pAdaptFsm.commChan, meParams)
+	oFsm.pOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (oFsm *uniPonAniConfigFsm) enterCreatingGemNCTPs(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm - start creating GemNWCtp loop", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	go oFsm.performCreatingGemNCTPs()
+}
+
+func (oFsm *uniPonAniConfigFsm) enterCreatingGemIWs(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm - start creating GemIwTP loop", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	go oFsm.performCreatingGemIWs()
+}
+
+func (oFsm *uniPonAniConfigFsm) enterSettingPQs(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm - start setting PrioQueue loop", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	go oFsm.performSettingPQs()
+}
+
+func (oFsm *uniPonAniConfigFsm) enterSettingDot1PMapper(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm Tx Set::.1pMapper with all PBits set", log.Fields{"EntitytId": 0x8042, /*cmp above*/
+		"toGemIw": 1024 /* cmp above */, "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+
+	logger.Debugw("uniPonAniConfigFsm Tx Set::1pMapper", log.Fields{
+		"EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+		"in state":  e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+
+	meParams := me.ParamData{
+		EntityID:   oFsm.mapperSP0ID,
+		Attributes: make(me.AttributeValueMap),
+	}
+
+	var loPrioGemPortArray [8]uint16
+	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		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("uniPonAniConfigFsm PrioString not unique", log.Fields{
+							"device-id": oFsm.pAdaptFsm.deviceID, "IgnoredGemPort": gemPortAttribs.gemPortID,
+							"SetGemPort": loPrioGemPortArray[i]})
+					}
+				}
+			} else {
+				logger.Warnw("uniPonAniConfigFsm PrioString evaluation error", log.Fields{
+					"device-id": oFsm.pAdaptFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+					"prioString": gemPortAttribs.pbitString, "position": i})
+			}
+
+		}
+	}
+	var foundIwPtr bool = false
+	for index, value := range loPrioGemPortArray {
+		if value != 0 {
+			foundIwPtr = true
+			meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
+			logger.Debugf("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+				"IwPtr for Prio%d": strconv.FormatInt(int64(value), 16), "device-id": oFsm.pAdaptFsm.deviceID}, index)
+			meParams.Attributes[meAttribute] = value
+
+		}
+	}
+
+	if !foundIwPtr {
+		logger.Errorw("UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
+			"device-id": oFsm.pAdaptFsm.deviceID})
+		//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 *AdapterFsm) {
+				if aPAFsm != nil && aPAFsm.pFsm != nil {
+					_ = aPAFsm.pFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+		}
+	}
+
+	meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(context.TODO(), ConstDefaultOmciTimeout, true,
+		oFsm.pAdaptFsm.commChan, meParams)
+	oFsm.pOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (oFsm *uniPonAniConfigFsm) enterAniConfigDone(e *fsm.Event) {
+
+	oFsm.aniConfigCompleted = true
+
+	pConfigAniStateAFsm := oFsm.pAdaptFsm
+	if pConfigAniStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(aPAFsm *AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.pFsm != nil {
+				_ = aPAFsm.pFsm.Event(aniEvReset)
+			}
+		}(pConfigAniStateAFsm)
+	}
+}
+
+func (oFsm *uniPonAniConfigFsm) enterResettingState(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm resetting", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+	pConfigAniStateAFsm := oFsm.pAdaptFsm
+	if pConfigAniStateAFsm != nil {
+		// abort running message processing
+		fsmAbortMsg := Message{
+			Type: TestMsg,
+			Data: TestMessage{
+				TestMessageVal: AbortMessageProcessing,
+			},
+		}
+		pConfigAniStateAFsm.commChan <- fsmAbortMsg
+
+		//try to restart the FSM to 'disabled', decouple event transfer
+		go func(aPAFsm *AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.pFsm != nil {
+				_ = aPAFsm.pFsm.Event(aniEvRestart)
+			}
+		}(pConfigAniStateAFsm)
+	}
+}
+
+func (oFsm *uniPonAniConfigFsm) enterDisabledState(e *fsm.Event) {
+	logger.Debugw("uniPonAniConfigFsm enters disabled state", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+	if oFsm.aniConfigCompleted {
+		logger.Debugw("uniPonAniConfigFsm send dh event notification", log.Fields{
+			"from_State": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+		//use DeviceHandler event notification directly
+		oFsm.pOmciCC.pBaseDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
+		oFsm.aniConfigCompleted = false
+	}
+	oFsm.pUniTechProf.setConfigDone(oFsm.pOnuUniPort.uniID, true)
+	go oFsm.pOmciCC.pBaseDeviceHandler.verifyUniVlanConfigRequest(oFsm.pOnuUniPort)
+
+	if oFsm.chanSet {
+		// indicate processing done to the caller
+		logger.Debugw("uniPonAniConfigFsm processingDone on channel", log.Fields{
+			"ProcessingStep": oFsm.procStep, "from_State": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+		oFsm.chSuccess <- oFsm.procStep
+		oFsm.chanSet = false //reset the internal channel state
+	}
+
+}
+
+func (oFsm *uniPonAniConfigFsm) processOmciAniMessages( /*ctx context.Context*/ ) {
+	logger.Debugw("Start uniPonAniConfigFsm Msg processing", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.pAdaptFsm.deviceID})
+		// 	break loop
+		message, ok := <-oFsm.pAdaptFsm.commChan
+		if !ok {
+			logger.Info("UniPonAniConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.pAdaptFsm.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("UniPonAniConfigFsm Rx Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+		switch message.Type {
+		case TestMsg:
+			msg, _ := message.Data.(TestMessage)
+			if msg.TestMessageVal == AbortMessageProcessing {
+				logger.Infow("UniPonAniConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+				break loop
+			}
+			logger.Warnw("UniPonAniConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "MessageVal": msg.TestMessageVal})
+		case OMCI:
+			msg, _ := message.Data.(OmciMessage)
+			oFsm.handleOmciAniConfigMessage(msg)
+		default:
+			logger.Warn("UniPonAniConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+				"message.Type": message.Type})
+		}
+
+	}
+	logger.Infow("End uniPonAniConfigFsm Msg processing", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigCreateResponseMessage(msg OmciMessage) {
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
+	if msgLayer == nil {
+		logger.Error("Omci Msg layer could not be detected for CreateResponse")
+		return
+	}
+	msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+	if !msgOk {
+		logger.Error("Omci Msg layer could not be assigned for CreateResponse")
+		return
+	}
+	logger.Debugw("CreateResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+	if msgObj.Result != me.Success {
+		logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+		return
+	}
+	if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+		msgObj.EntityInstance == oFsm.pOmciCC.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.pOmciCC.pLastTxMeInstance.GetName() {
+		case "Ieee8021PMapperServiceProfile":
+			{ // let the FSM proceed ...
+				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapCResp)
+			}
+		case "MacBridgePortConfigurationData":
+			{ // let the FSM proceed ...
+				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxMbpcdResp)
+			}
+		case "GemPortNetworkCtp", "GemInterworkingTerminationPoint":
+			{ // let aniConfig Multi-Id processing proceed by stopping the wait function
+				oFsm.omciMIdsResponseReceived <- true
+			}
+		}
+	}
+}
+
+func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigSetResponseMessage(msg OmciMessage) {
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+	if msgLayer == nil {
+		logger.Error("UniPonAniConfigFsm - Omci Msg layer could not be detected for SetResponse")
+		return
+	}
+	msgObj, msgOk := msgLayer.(*omci.SetResponse)
+	if !msgOk {
+		logger.Error("UniPonAniConfigFsm - Omci Msg layer could not be assigned for SetResponse")
+		return
+	}
+	logger.Debugw("UniPonAniConfigFsm SetResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+	if msgObj.Result != me.Success {
+		logger.Errorw("UniPonAniConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+		return
+	}
+	if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+		msgObj.EntityInstance == oFsm.pOmciCC.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.pOmciCC.pLastTxMeInstance.GetName() {
+		case "TCont":
+			{ // let the FSM proceed ...
+				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxTcontsResp)
+			}
+		case "PriorityQueue":
+			{ // let the PrioQueue init proceed by stopping the wait function
+				oFsm.omciMIdsResponseReceived <- true
+			}
+		case "Ieee8021PMapperServiceProfile":
+			{ // let the FSM proceed ...
+				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapSResp)
+			}
+		}
+	}
+}
+
+func (oFsm *uniPonAniConfigFsm) handleOmciAniConfigMessage(msg OmciMessage) {
+	logger.Debugw("Rx OMCI UniPonAniConfigFsm Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+		"msgType": msg.OmciMsg.MessageType})
+
+	switch msg.OmciMsg.MessageType {
+	case omci.CreateResponseType:
+		{
+			oFsm.handleOmciAniConfigCreateResponseMessage(msg)
+
+		} //CreateResponseType
+	case omci.SetResponseType:
+		{
+			oFsm.handleOmciAniConfigSetResponseMessage(msg)
+
+		} //SetResponseType
+	default:
+		{
+			logger.Errorw("uniPonAniConfigFsm - Rx OMCI unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+			return
+		}
+	}
+}
+
+func (oFsm *uniPonAniConfigFsm) performCreatingGemNCTPs() {
+	for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		logger.Debugw("uniPonAniConfigFsm Tx Create::GemNWCtp", log.Fields{
+			"EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
+			"TcontId":   strconv.FormatInt(int64(oFsm.tcont0ID), 16),
+			"device-id": oFsm.pAdaptFsm.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,
+			},
+		}
+		meInstance := oFsm.pOmciCC.sendCreateGemNCTPVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			oFsm.pAdaptFsm.commChan, meParams)
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+		//verify response
+		err := oFsm.waitforOmciResponse()
+		if err != nil {
+			logger.Errorw("GemNWCtp create failed, aborting AniConfig FSM!",
+				log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "GemIndex": gemIndex})
+			_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+			return
+		}
+	} //for all GemPorts of this T-Cont
+
+	logger.Debugw("GemNWCtp create loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+	_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxGemntcpsResp)
+}
+
+func (oFsm *uniPonAniConfigFsm) performCreatingGemIWs() {
+	for gemIndex, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		logger.Debugw("uniPonAniConfigFsm Tx Create::GemIwTp", log.Fields{
+			"EntitytId": strconv.FormatInt(int64(gemPortAttribs.gemPortID), 16),
+			"SPPtr":     strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
+			"device-id": oFsm.pAdaptFsm.deviceID})
+		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":                    galEthernetEID,
+			},
+		}
+		meInstance := oFsm.pOmciCC.sendCreateGemIWTPVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			oFsm.pAdaptFsm.commChan, meParams)
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+		//verify response
+		err := oFsm.waitforOmciResponse()
+		if err != nil {
+			logger.Errorw("GemIwTp create failed, aborting AniConfig FSM!",
+				log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "GemIndex": gemIndex})
+			_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+			return
+		}
+	} //for all GemPort's of this T-Cont
+
+	logger.Debugw("GemIwTp create loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+	_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxGemiwsResp)
+}
+
+func (oFsm *uniPonAniConfigFsm) performSettingPQs() {
+	const cu16StrictPrioWeight uint16 = 0xFFFF
+	loQueueMap := ordered_map.NewOrderedMap()
+	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		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
+		}
+	}
+
+
+	loTrafficSchedulerEID := 0x8000
+	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 (kv.Value).(uint16) == cu16StrictPrioWeight {
+			//StrictPrio indication
+			logger.Debugw("uniPonAniConfigFsm Tx Set::PrioQueue to StrictPrio", log.Fields{
+				"EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+				"device-id": oFsm.pAdaptFsm.deviceID})
+			meParams.Attributes["TrafficSchedulerPointer"] = 0 //ensure T-Cont defined StrictPrio scheduling
+		} else {
+			//WRR indication
+			logger.Debugw("uniPonAniConfigFsm Tx Set::PrioQueue to WRR", log.Fields{
+				"EntitytId": strconv.FormatInt(int64(queueIndex), 16),
+				"Weight":    kv.Value,
+				"device-id": oFsm.pAdaptFsm.deviceID})
+			meParams.Attributes["TrafficSchedulerPointer"] = loTrafficSchedulerEID //ensure assignment of the relevant trafficScheduler
+			meParams.Attributes["Weight"] = uint8(kv.Value.(uint16))
+		}
+		meInstance := oFsm.pOmciCC.sendSetPrioQueueVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			oFsm.pAdaptFsm.commChan, meParams)
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+		//verify response
+		err := oFsm.waitforOmciResponse()
+		if err != nil {
+			logger.Errorw("PrioQueue set failed, aborting AniConfig FSM!",
+				log.Fields{"device-id": oFsm.pAdaptFsm.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
+
+	logger.Debugw("PrioQueue set loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+	_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxPrioqsResp)
+}
+
+func (oFsm *uniPonAniConfigFsm) waitforOmciResponse() error {
+	select {
+	case <-time.After(30 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+		logger.Warnw("UniPonAniConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+		return errors.New("uniPonAniConfigFsm multi entity timeout")
+	case success := <-oFsm.omciMIdsResponseReceived:
+		if success {
+			logger.Debug("uniPonAniConfigFsm multi entity response received")
+			return nil
+		}
+		// should not happen so far
+		logger.Warnw("uniPonAniConfigFsm multi entity response error", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+		return errors.New("uniPonAniConfigFsm multi entity responseError")
+	}
+}
diff --git a/internal/pkg/onuadaptercore/omci_cc.go b/internal/pkg/onuadaptercore/omci_cc.go
new file mode 100644
index 0000000..9cf38ac
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_cc.go
@@ -0,0 +1,1452 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"container/list"
+	"context"
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"strconv"
+	"sync"
+
+
+	"github.com/google/gopacket"
+	gp "github.com/google/gopacket"
+
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+	ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+)
+
+// ### OMCI related definitions - retrieved from Python adapter code/trace ####
+
+//ConstDefaultOmciTimeout - Default OMCI Timeout
+const ConstDefaultOmciTimeout = 10 // ( 3 ?) Seconds
+
+const galEthernetEID = uint16(1)
+const maxGemPayloadSize = uint16(48)
+const connectivityModeValue = uint8(5)
+
+//const defaultTPID = uint16(0x8100)
+//const broadComDefaultVID = uint16(4091)
+const macBridgeServiceProfileEID = uint16(0x201) // TODO: most all these need better definition or tuning
+const ieeeMapperServiceProfileEID = uint16(0x8001)
+const macBridgePortAniEID = uint16(0x2102)
+
+// ### OMCI related definitions - end
+
+//callbackPairEntry to be used for OMCI send/receive correlation
+type callbackPairEntry struct {
+	cbRespChannel chan Message
+	cbFunction    func(*omci.OMCI, *gp.Packet, chan Message) error
+}
+
+//callbackPair to be used for ReceiveCallback init
+type callbackPair struct {
+	cbKey   uint16
+	cbEntry callbackPairEntry
+}
+
+type omciTransferStructure struct {
+	txFrame  []byte
+	timeout  int
+	retry    int
+	highPrio bool
+}
+
+//omciCC structure holds information needed for OMCI communication (to/from OLT Adapter)
+type omciCC struct {
+	enabled            bool
+	pOnuDeviceEntry    *OnuDeviceEntry
+	deviceID           string
+	pBaseDeviceHandler *deviceHandler
+	coreProxy          adapterif.CoreProxy
+	adapterProxy       adapterif.AdapterProxy
+	supportExtMsg      bool
+	txFrames, txOnuFrames                uint32
+	rxFrames, rxOnuFrames, rxOnuDiscards uint32
+
+	mutexTid       sync.Mutex
+	tid            uint16
+	mutexHpTid     sync.Mutex
+	hpTid          uint16
+	uploadSequNo   uint16
+	uploadNoOfCmds uint16
+
+	mutexTxQueue      sync.Mutex
+	txQueue           *list.List
+	mutexRxSchedMap   sync.Mutex
+	rxSchedulerMap    map[uint16]callbackPairEntry
+	pLastTxMeInstance *me.ManagedEntity
+}
+
+//newOmciCC constructor returns a new instance of a OmciCC
+//mib_db (as well as not inluded alarm_db not really used in this code? VERIFY!!)
+func newOmciCC(ctx context.Context, onuDeviceEntry *OnuDeviceEntry,
+	deviceID string, deviceHandler *deviceHandler,
+	coreProxy adapterif.CoreProxy, adapterProxy adapterif.AdapterProxy) *omciCC {
+	logger.Infow("init-omciCC", log.Fields{"device-id": deviceID})
+	var omciCC omciCC
+	omciCC.enabled = false
+	omciCC.pOnuDeviceEntry = onuDeviceEntry
+	omciCC.deviceID = deviceID
+	omciCC.pBaseDeviceHandler = deviceHandler
+	omciCC.coreProxy = coreProxy
+	omciCC.adapterProxy = adapterProxy
+	omciCC.supportExtMsg = false
+	omciCC.txFrames = 0
+	omciCC.txOnuFrames = 0
+	omciCC.rxFrames = 0
+	omciCC.rxOnuFrames = 0
+	omciCC.rxOnuDiscards = 0
+	omciCC.tid = 0x1
+	omciCC.hpTid = 0x8000
+	omciCC.uploadSequNo = 0
+	omciCC.uploadNoOfCmds = 0
+
+	omciCC.txQueue = list.New()
+	omciCC.rxSchedulerMap = make(map[uint16]callbackPairEntry)
+
+	return &omciCC
+}
+
+// Rx handler for omci messages
+func (oo *omciCC) receiveOnuMessage(ctx context.Context, omciMsg *omci.OMCI) error {
+	logger.Debugw("rx-onu-autonomous-message", log.Fields{"omciMsgType": omciMsg.MessageType,
+		"payload": hex.EncodeToString(omciMsg.Payload)})
+	/*
+			msgType = rxFrame.fields["message_type"] //assumed OmciOperationsValue
+			rxOnuFrames++
+
+			switch msgType {
+			case AlarmNotification:
+				{
+					logger.Info("Unhandled: received-onu-alarm-message")
+					// python code was:
+					//if msg_type == EntityOperations.AlarmNotification.value:
+					//	topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.Alarm_Notification)
+					//	self.reactor.callLater(0,  self.event_bus.publish, topic, msg)
+					//
+					return errors.New("RxAlarmNotification unimplemented")
+				}
+			case AttributeValueChange:
+				{
+					logger.Info("Unhandled: received-attribute-value-change")
+					// python code was:
+					//elif msg_type == EntityOperations.AttributeValueChange.value:
+					//	topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.AVC_Notification)
+					//	self.reactor.callLater(0,  self.event_bus.publish, topic, msg)
+					//
+					return errors.New("RxAttributeValueChange unimplemented")
+				}
+			case TestResult:
+				{
+					logger.Info("Unhandled: received-test-result")
+					// python code was:
+					//elif msg_type == EntityOperations.TestResult.value:
+					//	topic = OMCI_CC.event_bus_topic(self._device_id, RxEvent.Test_Result)
+					//	self.reactor.callLater(0,  self.event_bus.publish, topic, msg)
+					//
+					return errors.New("RxTestResult unimplemented")
+				}
+			default:
+				{
+					logger.Errorw("rx-onu-unsupported-autonomous-message", log.Fields{"msgType": msgType})
+					rxOnuDiscards++
+					return errors.New("RxOnuMsgType unimplemented")
+				}
+		    }
+	*/
+	return errors.New("receiveOnuMessage unimplemented")
+}
+
+// Rx handler for onu messages
+//    e.g. would call ReceiveOnuMessage() in case of TID=0 or Action=test ...
+func (oo *omciCC) receiveMessage(ctx context.Context, rxMsg []byte) error {
+	if len(rxMsg) >= 44 { // then it should normally include the BaseFormat trailer Len
+		// NOTE: autocorrection only valid for OmciBaseFormat, which is not specifically verified here!!!
+		//  (am extendedFormat message could be destroyed this way!)
+		trailerLenData := rxMsg[42:44]
+		trailerLen := binary.BigEndian.Uint16(trailerLenData)
+		//logger.Debugw("omci-received-trailer-len", log.Fields{"Length": trailerLen})
+		if trailerLen != 40 { // invalid base Format entry -> autocorrect
+			binary.BigEndian.PutUint16(rxMsg[42:44], 40)
+			logger.Debug("cc-corrected-omci-message: trailer len inserted")
+		}
+	} else {
+		logger.Errorw("received omci-message too small for OmciBaseFormat - abort", log.Fields{"Length": len(rxMsg)})
+		return errors.New("rxOmciMessage too small for BaseFormat")
+	}
+
+	packet := gopacket.NewPacket(rxMsg, omci.LayerTypeOMCI, gopacket.NoCopy)
+	if packet == nil {
+		logger.Error("omci-message could not be decoded")
+		return errors.New("could not decode rxMsg as OMCI")
+	}
+	omciLayer := packet.Layer(omci.LayerTypeOMCI)
+	if omciLayer == nil {
+		logger.Error("omci-message could not decode omci layer")
+		return errors.New("could not decode omci layer")
+	}
+	omciMsg, ok := omciLayer.(*omci.OMCI)
+	if !ok {
+		logger.Error("omci-message could not assign omci layer")
+		return errors.New("could not assign omci layer")
+	}
+	logger.Debugw("omci-message-decoded:", log.Fields{"omciMsgType": omciMsg.MessageType,
+		"transCorrId": strconv.FormatInt(int64(omciMsg.TransactionID), 16), "DeviceIdent": omciMsg.DeviceIdentifier})
+	if byte(omciMsg.MessageType) & ^me.AK == 0 {
+		// Not a response
+		logger.Debug("RxMsg is no Omci Response Message")
+		if omciMsg.TransactionID == 0 {
+			return oo.receiveOnuMessage(ctx, omciMsg)
+		}
+		logger.Errorw("Unexpected TransCorrId != 0  not accepted for autonomous messages",
+			log.Fields{"msgType": omciMsg.MessageType, "payload": hex.EncodeToString(omciMsg.Payload)})
+		return errors.New("autonomous Omci Message with TranSCorrId != 0 not acccepted")
+
+	}
+	oo.mutexRxSchedMap.Lock()
+	rxCallbackEntry, ok := oo.rxSchedulerMap[omciMsg.TransactionID]
+	if ok && rxCallbackEntry.cbFunction != nil {
+		//disadvantage of decoupling: error verification made difficult, but anyway the question is
+		// how to react on erroneous frame reception, maybe can simply be ignored
+		go rxCallbackEntry.cbFunction(omciMsg, &packet, rxCallbackEntry.cbRespChannel)
+		// having posted the response the request is regarded as 'done'
+		delete(oo.rxSchedulerMap, omciMsg.TransactionID)
+		oo.mutexRxSchedMap.Unlock()
+	} else {
+		oo.mutexRxSchedMap.Unlock()
+		logger.Error("omci-message-response for not registered transCorrId")
+		return errors.New("could not find registered response handler tor transCorrId")
+	}
+
+	return nil
+	/* py code was:
+	           Receive and OMCI message from the proxy channel to the OLT.
+
+	           Call this from your ONU Adapter on a new OMCI Rx on the proxy channel
+	           :param msg: (str) OMCI binary message (used as input to Scapy packet decoder)
+	           """
+	           if not self.enabled:
+	               return
+
+	           try:
+	               now = arrow.utcnow()
+	               d = None
+
+	               # NOTE: Since we may need to do an independent ME map on a per-ONU basis
+	               #       save the current value of the entity_id_to_class_map, then
+	               #       replace it with our custom one before decode, and then finally
+	               #       restore it later. Tried other ways but really made the code messy.
+	               saved_me_map = omci_entities.entity_id_to_class_map
+	               omci_entities.entity_id_to_class_map = self._me_map
+
+	               try:
+	                   rx_frame = msg if isinstance(msg, OmciFrame) else OmciFrame(msg)
+	                   self.logger.debug('recv-omci-msg', omci_msg=hexlify(msg))
+	               except KeyError as e:
+	                   # Unknown, Unsupported, or vendor-specific ME. Key is the unknown classID
+	                   self.logger.debug('frame-decode-key-error', omci_msg=hexlify(msg), e=e)
+	                   rx_frame = self._decode_unknown_me(msg)
+	                   self._rx_unknown_me += 1
+
+	               except Exception as e:
+	                   self.logger.exception('frame-decode', omci_msg=hexlify(msg), e=e)
+	                   return
+
+	               finally:
+	                   omci_entities.entity_id_to_class_map = saved_me_map     # Always restore it.
+
+	               rx_tid = rx_frame.fields['transaction_id']
+	               msg_type = rx_frame.fields['message_type']
+	               self.logger.debug('Received message for rx_tid', rx_tid = rx_tid, msg_type = msg_type)
+	               # Filter the Test Result frame and route through receive onu
+	               # message method.
+	               if rx_tid == 0 or msg_type == EntityOperations.TestResult.value:
+	                   self.logger.debug('Receive ONU message', rx_tid=0)
+	                   return self._receive_onu_message(rx_frame)
+
+	               # Previously unreachable if this is the very first round-trip Rx or we
+	               # have been running consecutive errors
+	               if self._rx_frames == 0 or self._consecutive_errors != 0:
+	                   self.logger.debug('Consecutive errors for rx', err = self._consecutive_errors)
+	                   self.reactor.callLater(0, self._publish_connectivity_event, True)
+
+	               self._rx_frames += 1
+	               self._consecutive_errors = 0
+
+	               try:
+	                   high_priority = self._tid_is_high_priority(rx_tid)
+	                   index = self._get_priority_index(high_priority)
+
+	                   # (timestamp, defer, frame, timeout, retry, delayedCall)
+	                   last_tx_tuple = self._tx_request[index]
+
+	                   if last_tx_tuple is None or \
+	                           last_tx_tuple[OMCI_CC.REQUEST_FRAME].fields.get('transaction_id') != rx_tid:
+	                       # Possible late Rx on a message that timed-out
+	                       if last_tx_tuple:
+	                           self.logger.debug('Unknown message', rx_tid=rx_tid,
+	                                          tx_id=last_tx_tuple[OMCI_CC.REQUEST_FRAME].fields.get('transaction_id'))
+	                       self._rx_unknown_tid += 1
+	                       self._rx_late += 1
+	                       return
+
+	                   ts, d, tx_frame, timeout, retry, dc = last_tx_tuple
+	                   if dc is not None and not dc.cancelled and not dc.called:
+	                       dc.cancel()
+
+	                   _secs = self._update_rx_tx_stats(now, ts)
+
+	                   # Late arrival already serviced by a timeout?
+	                   if d.called:
+	                       self._rx_late += 1
+	                       self.logger.debug('Serviced by timeout. Late arrival', rx_late = self._rx_late)
+	                       return
+
+	               except Exception as e:
+	                   self.logger.exception('frame-match', msg=hexlify(msg), e=e)
+	                   if d is not None:
+	                       return d.errback(failure.Failure(e))
+	                   return
+
+	               # Publish Rx event to listeners in a different task
+	               self.logger.debug('Publish rx event', rx_tid = rx_tid,
+	                              tx_tid = tx_frame.fields['transaction_id'])
+	               reactor.callLater(0, self._publish_rx_frame, tx_frame, rx_frame)
+
+	               # begin success callback chain (will cancel timeout and queue next Tx message)
+	               self._rx_response[index] = rx_frame
+	               d.callback(rx_frame)
+
+	           except Exception as e:
+	   			self.logger.exception('rx-msg', e=e)
+	*/
+}
+
+/*
+func (oo *omciCC) publishRxResponseFrame(ctx context.Context, txFrame []byte, rxFrame []byte) error {
+	return errors.New("publishRxResponseFrame unimplemented")
+		//def _publish_rx_frame(self, tx_frame, rx_frame):
+}
+*/
+
+//Queue the OMCI Frame for a transmit to the ONU via the proxy_channel
+func (oo *omciCC) send(ctx context.Context, txFrame []byte, timeout int, retry int, highPrio bool,
+	receiveCallbackPair callbackPair) error {
+
+	logger.Debugw("register-response-callback:", log.Fields{"for TansCorrId": receiveCallbackPair.cbKey})
+	oo.mutexRxSchedMap.Lock()
+	oo.rxSchedulerMap[receiveCallbackPair.cbKey] = receiveCallbackPair.cbEntry
+	oo.mutexRxSchedMap.Unlock()
+
+	omciTxRequest := omciTransferStructure{
+		txFrame,
+		timeout,
+		retry,
+		highPrio,
+	}
+	oo.mutexTxQueue.Lock()
+	oo.txQueue.PushBack(omciTxRequest) // enqueue
+	oo.mutexTxQueue.Unlock()
+
+	go oo.sendNextRequest(ctx)
+	return nil
+}
+
+//Pull next tx request and send it
+func (oo *omciCC) sendNextRequest(ctx context.Context) error {
+
+	oo.mutexTxQueue.Lock()
+	defer oo.mutexTxQueue.Unlock()
+	for oo.txQueue.Len() > 0 {
+		queueElement := oo.txQueue.Front() // First element
+		omciTxRequest := queueElement.Value.(omciTransferStructure)
+		/* compare olt device handler code:
+		func (dh *DeviceHandler) omciIndication(omciInd *oop.OmciIndication) {
+			logger.Debugw("omci indication", log.Fields{"intfID": omciInd.IntfId, "onuID": omciInd.OnuId})
+			var deviceType string
+			var deviceID string
+			var proxyDeviceID string
+
+			onuKey := dh.formOnuKey(omciInd.IntfId, omciInd.OnuId)
+
+			if onuInCache, ok := dh.onus.Load(onuKey); !ok {
+
+				logger.Debugw("omci indication for a device not in cache.", log.Fields{"intfID": omciInd.IntfId, "onuID": omciInd.OnuId})
+				ponPort := IntfIDToPortNo(omciInd.GetIntfId(), voltha.Port_PON_OLT)
+				kwargs := make(map[string]interface{})
+				kwargs["onu_id"] = omciInd.OnuId
+				kwargs["parent_port_no"] = ponPort
+
+				onuDevice, err := dh.coreProxy.GetChildDevice(context.TODO(), dh.device.Id, kwargs)
+				if err != nil {
+					logger.Errorw("onu not found", log.Fields{"intfID": omciInd.IntfId, "onuID": omciInd.OnuId, "error": err})
+					return
+				}
+				deviceType = onuDevice.Type
+				deviceID = onuDevice.Id
+				proxyDeviceID = onuDevice.ProxyAddress.DeviceId
+				//if not exist in cache, then add to cache.
+				dh.onus.Store(onuKey, NewOnuDevice(deviceID, deviceType, onuDevice.SerialNumber, omciInd.OnuId, omciInd.IntfId, proxyDeviceID))
+			} else {
+				//found in cache
+				logger.Debugw("omci indication for a device in cache.", log.Fields{"intfID": omciInd.IntfId, "onuID": omciInd.OnuId})
+				deviceType = onuInCache.(*OnuDevice).deviceType
+				deviceID = onuInCache.(*OnuDevice).deviceID
+				proxyDeviceID = onuInCache.(*OnuDevice).proxyDeviceID
+			}
+		*/
+		/* and compare onu_adapter py code:
+		omci_msg = InterAdapterOmciMessage(
+			message=bytes(frame),
+			proxy_address=self._proxy_address,
+			connect_status=self._device.connect_status)
+
+		self.logger.debug('sent-omci-msg', tid=tx_tid, omci_msg=hexlify(bytes(frame)))
+
+		yield self._adapter_proxy.send_inter_adapter_message(
+			msg=omci_msg,
+			type=InterAdapterMessageType.OMCI_REQUEST,
+			from_adapter=self._device.type,
+			to_adapter=self._proxy_address.device_type,
+			to_device_id=self._device_id,
+			proxy_device_id=self._proxy_address.device_id
+		)
+		*/
+		device, err := oo.coreProxy.GetDevice(ctx,
+			oo.pBaseDeviceHandler.deviceID, oo.deviceID) //parent, child
+		if err != nil || device == nil {
+			/*TODO: needs to handle error scenarios */
+			logger.Errorw("Failed to fetch device", log.Fields{"err": err, "ParentId": oo.pBaseDeviceHandler.deviceID,
+				"ChildId": oo.deviceID})
+			return errors.New("failed to fetch device")
+		}
+
+		logger.Debugw("omci-message-sending", log.Fields{"fromDeviceType": oo.pBaseDeviceHandler.DeviceType,
+			"toDeviceType": oo.pBaseDeviceHandler.ProxyAddressType,
+			"onuDeviceID":  oo.deviceID, "proxyDeviceID": oo.pBaseDeviceHandler.ProxyAddressID})
+		logger.Debugw("omci-message-to-send:",
+			log.Fields{"TxOmciMessage": hex.EncodeToString(omciTxRequest.txFrame)})
+
+		omciMsg := &ic.InterAdapterOmciMessage{Message: omciTxRequest.txFrame}
+		if sendErr := oo.adapterProxy.SendInterAdapterMessage(context.Background(), omciMsg,
+			ic.InterAdapterMessageType_OMCI_REQUEST,
+			//fromType,toType,toDevId, ProxyDevId
+			oo.pBaseDeviceHandler.DeviceType, oo.pBaseDeviceHandler.ProxyAddressType,
+			oo.deviceID, oo.pBaseDeviceHandler.ProxyAddressID, ""); sendErr != nil {
+			logger.Errorw("send omci request error", log.Fields{"error": sendErr})
+			return sendErr
+		}
+		oo.txQueue.Remove(queueElement) // Dequeue
+	}
+	return nil
+}
+
+func (oo *omciCC) getNextTid(highPriority bool) uint16 {
+	var next uint16
+	if highPriority {
+		oo.mutexTid.Lock()
+		next = oo.hpTid
+		oo.hpTid++
+		if oo.hpTid < 0x8000 {
+			oo.hpTid = 0x8000
+		}
+		oo.mutexTid.Unlock()
+	} else {
+		oo.mutexHpTid.Lock()
+		next = oo.tid
+		oo.tid++
+		if oo.tid >= 0x8000 {
+			oo.tid = 1
+		}
+		oo.mutexHpTid.Unlock()
+	}
+	return next
+}
+
+// ###################################################################################
+// # utility methods provided to work on OMCI messages
+func serialize(msgType omci.MessageType, request gopacket.SerializableLayer, tid uint16) ([]byte, error) {
+	omciLayer := &omci.OMCI{
+		TransactionID: tid,
+		MessageType:   msgType,
+	}
+	return serializeOmciLayer(omciLayer, request)
+}
+
+func serializeOmciLayer(aOmciLayer *omci.OMCI, aRequest gopacket.SerializableLayer) ([]byte, error) {
+	var options gopacket.SerializeOptions
+	options.FixLengths = true
+
+	buffer := gopacket.NewSerializeBuffer()
+	err := gopacket.SerializeLayers(buffer, options, aOmciLayer, aRequest)
+	if err != nil {
+		logger.Errorw("Could not create goPacket Omci serial buffer", log.Fields{"Err": err})
+		return nil, err
+	}
+	return buffer.Bytes(), nil
+}
+
+/*
+func hexEncode(omciPkt []byte) ([]byte, error) {
+	dst := make([]byte, hex.EncodedLen(len(omciPkt)))
+	hex.Encode(dst, omciPkt)
+	return dst, nil
+}
+*/
+
+//supply a response handler for omci response messages to be transferred to the requested FSM
+func (oo *omciCC) receiveOmciResponse(omciMsg *omci.OMCI, packet *gp.Packet, respChan chan Message) error {
+
+	logger.Debugw("omci-message-response - transfer on omciRespChannel", log.Fields{"omciMsgType": omciMsg.MessageType,
+		"transCorrId": strconv.FormatInt(int64(omciMsg.TransactionID), 16), "device-id": oo.deviceID})
+
+	if oo.pOnuDeviceEntry == nil {
+		logger.Errorw("Abort receiving OMCI response, DeviceEntryPointer is nil", log.Fields{
+			"device-id": oo.deviceID})
+		return errors.New("deviceEntryPointer is nil")
+	}
+
+	omciRespMsg := Message{
+		Type: OMCI,
+		Data: OmciMessage{
+			OmciMsg:    omciMsg,
+			OmciPacket: packet,
+		},
+	}
+	respChan <- omciRespMsg
+
+	return nil
+}
+
+func (oo *omciCC) sendMibReset(ctx context.Context, timeout int, highPrio bool) error {
+
+	logger.Debugw("send MibReset-msg to:", log.Fields{"device-id": oo.deviceID})
+	request := &omci.MibResetRequest{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass: me.OnuDataClassID,
+		},
+	}
+	tid := oo.getNextTid(highPrio)
+	pkt, err := serialize(omci.MibResetRequestType, request, tid)
+	if err != nil {
+		logger.Errorw("Cannot serialize MibResetRequest", log.Fields{
+			"Err": err, "device-id": oo.deviceID})
+		return err
+	}
+	omciRxCallbackPair := callbackPair{
+		cbKey:   tid,
+		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+	}
+	return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+}
+
+func (oo *omciCC) sendReboot(ctx context.Context, timeout int, highPrio bool, responseChannel chan Message) error {
+	logger.Debugw("send Reboot-msg to:", log.Fields{"device-id": oo.deviceID})
+	request := &omci.RebootRequest{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass: me.OnuGClassID,
+		},
+	}
+	tid := oo.getNextTid(highPrio)
+	pkt, err := serialize(omci.RebootRequestType, request, tid)
+	if err != nil {
+		logger.Errorw("Cannot serialize RebootRequest", log.Fields{
+			"Err": err, "device-id": oo.deviceID})
+		return err
+	}
+	omciRxCallbackPair := callbackPair{
+		cbKey:   tid,
+		cbEntry: callbackPairEntry{oo.pOnuDeviceEntry.omciRebootMessageReceivedChannel, oo.receiveOmciResponse},
+	}
+
+	err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+	if err != nil {
+		logger.Errorw("Cannot send RebootRequest", log.Fields{
+			"Err": err, "device-id": oo.deviceID})
+		return err
+	}
+	err = oo.pOnuDeviceEntry.waitForRebootResponse(responseChannel)
+	if err != nil {
+		logger.Error("aborting ONU Reboot!")
+		_ = oo.pOnuDeviceEntry.pMibDownloadFsm.pFsm.Event("reset")
+		return err
+	}
+	return nil
+}
+
+func (oo *omciCC) sendMibUpload(ctx context.Context, timeout int, highPrio bool) error {
+	logger.Debugw("send MibUpload-msg to:", log.Fields{"device-id": oo.deviceID})
+	request := &omci.MibUploadRequest{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass: me.OnuDataClassID,
+		},
+	}
+	tid := oo.getNextTid(highPrio)
+	pkt, err := serialize(omci.MibUploadRequestType, request, tid)
+	if err != nil {
+		logger.Errorw("Cannot serialize MibUploadRequest", log.Fields{
+			"Err": err, "device-id": oo.deviceID})
+		return err
+	}
+	oo.uploadSequNo = 0
+	oo.uploadNoOfCmds = 0
+
+	omciRxCallbackPair := callbackPair{
+		cbKey:   tid,
+		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+	}
+	return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+}
+
+func (oo *omciCC) sendMibUploadNext(ctx context.Context, timeout int, highPrio bool) error {
+	logger.Debugw("send MibUploadNext-msg to:", log.Fields{"device-id": oo.deviceID, "uploadSequNo": oo.uploadSequNo})
+	request := &omci.MibUploadNextRequest{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass: me.OnuDataClassID,
+		},
+		CommandSequenceNumber: oo.uploadSequNo,
+	}
+	tid := oo.getNextTid(highPrio)
+	pkt, err := serialize(omci.MibUploadNextRequestType, request, tid)
+	if err != nil {
+		logger.Errorw("Cannot serialize MibUploadNextRequest", log.Fields{
+			"Err": err, "device-id": oo.deviceID})
+		return err
+	}
+	oo.uploadSequNo++
+
+	omciRxCallbackPair := callbackPair{
+		cbKey:   tid,
+		cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+	}
+	return oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+}
+
+func (oo *omciCC) sendCreateGalEthernetProfile(ctx context.Context, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send GalEnetProfile-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16)})
+
+	meParams := me.ParamData{
+		EntityID:   galEthernetEID,
+		Attributes: me.AttributeValueMap{"MaximumGemPayloadSize": maxGemPayloadSize},
+	}
+	meInstance, omciErr := me.NewGalEthernetProfile(meParams)
+	if omciErr.GetError() == nil {
+		//all setByCreate parameters already set, no default option required ...
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode GalEnetProfileInstance for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize GalEnetProfile create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send GalEnetProfile create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send GalEnetProfile-Create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate GalEnetProfileInstance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+// might be needed to extend for parameter arguments, here just for setting the ConnectivityMode!!
+func (oo *omciCC) sendSetOnu2g(ctx context.Context, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send ONU2-G-Set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16)})
+
+	meParams := me.ParamData{
+		EntityID:   0,
+		Attributes: me.AttributeValueMap{"CurrentConnectivityMode": connectivityModeValue},
+	}
+	meInstance, omciErr := me.NewOnu2G(meParams)
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode ONU2-G instance for set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize ONU2-G set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send ONU2-G set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send ONU2-G-Set-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate ONU2-G", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendCreateMBServiceProfile(ctx context.Context,
+	aPUniPort *onuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	instID := macBridgeServiceProfileEID + uint16(aPUniPort.macBpNo)
+	logger.Debugw("send MBSP-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16), "InstId": strconv.FormatInt(int64(instID), 16)})
+
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"Priority":     0x8000,
+			"MaxAge":       20 * 256, //20s
+			"HelloTime":    2 * 256,  //2s
+			"ForwardDelay": 15 * 256, //15s
+			//note: DynamicFilteringAgeingTime is taken from omci lib default as
+			//  which is obviously different from default value used in python lib,
+			//  where the value seems to be 0 (ONU defined)  - to be considered in case of test artifacts ...
+		},
+	}
+
+	meInstance, omciErr := me.NewMacBridgeServiceProfile(meParams)
+	if omciErr.GetError() == nil {
+		//obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid), omci.AddDefaults(true))
+		if err != nil {
+			logger.Errorw("Cannot encode MBSP for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize MBSP create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send MBSP create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send MBSP-Create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate MBSP Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendCreateMBPConfigData(ctx context.Context,
+	aPUniPort *onuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	instID := macBridgePortAniEID + aPUniPort.entityID
+	logger.Debugw("send MBPCD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16), "InstId": strconv.FormatInt(int64(instID), 16)})
+
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"BridgeIdPointer": macBridgeServiceProfileEID + uint16(aPUniPort.macBpNo),
+			"PortNum":         aPUniPort.macBpNo,
+			"TpType":          uint8(aPUniPort.portType),
+			"TpPointer":       aPUniPort.entityID,
+		},
+	}
+	meInstance, omciErr := me.NewMacBridgePortConfigurationData(meParams)
+	if omciErr.GetError() == nil {
+		//obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid), omci.AddDefaults(true))
+		if err != nil {
+			logger.Errorw("Cannot encode MBPCD for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize MBPCD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send MBPCD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send MBPCD-Create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate MBPCD Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendCreateEVTOConfigData(ctx context.Context,
+	aPUniPort *onuUniPort, timeout int, highPrio bool) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	instID := macBridgeServiceProfileEID + uint16(aPUniPort.macBpNo)
+	logger.Debugw("send EVTOCD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16), "InstId": strconv.FormatInt(int64(instID), 16)})
+
+	assType := uint8(2) // default AssociationType is PPTPEthUni
+	if aPUniPort.portType == uniVEIP {
+		assType = uint8(10) // for VEIP
+	}
+	meParams := me.ParamData{
+		EntityID: instID,
+		Attributes: me.AttributeValueMap{
+			"AssociationType":     assType,
+			"AssociatedMePointer": aPUniPort.entityID,
+		},
+	}
+	meInstance, omciErr := me.NewExtendedVlanTaggingOperationConfigurationData(meParams)
+	if omciErr.GetError() == nil {
+		//all setByCreate parameters already set, no default option required ...
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode EVTOCD for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize EVTOCD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibDownloadFsm.commChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send EVTOCD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send EVTOCD-Create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate EVTOCD Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendSetOnuGLS(ctx context.Context, timeout int,
+	highPrio bool, requestedAttributes me.AttributeValueMap, rxChan chan Message) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send ONU-G-Set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16)})
+
+	meParams := me.ParamData{
+		EntityID:   0,
+		Attributes: requestedAttributes,
+	}
+	meInstance, omciErr := me.NewOnuG(meParams)
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode ONU-G instance for set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize ONU-G set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send ONU-G set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send ONU-G-Set-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate ONU-G", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendSetUniGLS(ctx context.Context, aInstNo uint16, timeout int,
+	highPrio bool, requestedAttributes me.AttributeValueMap, rxChan chan Message) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send UNI-G-Set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16)})
+
+	meParams := me.ParamData{
+		EntityID:   aInstNo,
+		Attributes: requestedAttributes,
+	}
+	meInstance, omciErr := me.NewUniG(meParams)
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode UNI-G instance for set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize UNI-G-Set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send UNIG-G-Set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send UNI-G-Set-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate UNI-G", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendSetVeipLS(ctx context.Context, aInstNo uint16, timeout int,
+	highPrio bool, requestedAttributes me.AttributeValueMap, rxChan chan Message) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send VEIP-Set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16)})
+
+	meParams := me.ParamData{
+		EntityID:   aInstNo,
+		Attributes: requestedAttributes,
+	}
+	meInstance, omciErr := me.NewVirtualEthernetInterfacePoint(meParams)
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode VEIP instance for set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize VEIP-Set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send VEIP-Set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send VEIP-Set-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate VEIP", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendGetMe(ctx context.Context, classID me.ClassID, entityID uint16, requestedAttributes me.AttributeValueMap,
+	timeout int, highPrio bool) *me.ManagedEntity {
+
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send get-request-msg", log.Fields{"classID": classID, "device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16)})
+
+	meParams := me.ParamData{
+		EntityID:   entityID,
+		Attributes: requestedAttributes,
+	}
+	meInstance, omciErr := me.LoadManagedEntityDefinition(classID, meParams)
+	if omciErr.GetError() == nil {
+		meClassIDName := meInstance.GetName()
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.GetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorf("Cannot encode instance for get-request", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize get-request", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{(*oo.pOnuDeviceEntry).pMibUploadFsm.commChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send get-request-msg", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debugw("send get-request-msg done", log.Fields{"meClassIDName": meClassIDName, "device-id": oo.deviceID})
+		return meInstance
+	}
+	logger.Errorw("Cannot generate meDefinition", log.Fields{"classID": classID, "Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendCreateDot1PMapper(ctx context.Context, timeout int, highPrio bool,
+	aInstID uint16, rxChan chan Message) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send .1pMapper-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16), "InstId": strconv.FormatInt(int64(aInstID), 16)})
+
+	meParams := me.ParamData{
+		EntityID:   aInstID,
+		Attributes: me.AttributeValueMap{},
+	}
+	meInstance, omciErr := me.NewIeee8021PMapperServiceProfile(meParams)
+	if omciErr.GetError() == nil {
+		//we have to set all 'untouched' parameters to default by some additional option parameter!!
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid), omci.AddDefaults(true))
+		if err != nil {
+			logger.Errorw("Cannot encode .1pMapper for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize .1pMapper create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send .1pMapper create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send .1pMapper-create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate .1pMapper", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendCreateMBPConfigDataVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send MBPCD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewMacBridgePortConfigurationData(params[0])
+	if omciErr.GetError() == nil {
+		//obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid), omci.AddDefaults(true))
+		if err != nil {
+			logger.Errorw("Cannot encode MBPCD for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize MBPCD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send MBPCD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send MBPCD-Create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate MBPCD Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendCreateGemNCTPVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send GemNCTP-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewGemPortNetworkCtp(params[0])
+	if omciErr.GetError() == nil {
+		//obviously we have to set all 'untouched' parameters to default by some additional option parameter!!
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid), omci.AddDefaults(true))
+		if err != nil {
+			logger.Errorw("Cannot encode GemNCTP for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize GemNCTP create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send GemNCTP create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send GemNCTP-Create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate GemNCTP Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendCreateGemIWTPVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send GemIwTp-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewGemInterworkingTerminationPoint(params[0])
+	if omciErr.GetError() == nil {
+		//all SetByCreate Parameters (assumed to be) set here, for optimisation no 'AddDefaults'
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode GemIwTp for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize GemIwTp create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send GemIwTp create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send GemIwTp-Create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate GemIwTp Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendSetTcontVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send TCont-Set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewTCont(params[0])
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode TCont for set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize TCont set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send TCont set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send TCont-set msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate TCont Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendSetPrioQueueVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send PrioQueue-Set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewPriorityQueue(params[0])
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode PrioQueue for set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize PrioQueue set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send PrioQueue set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send PrioQueue-set msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate PrioQueue Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendSetDot1PMapperVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send 1PMapper-Set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewIeee8021PMapperServiceProfile(params[0])
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode 1PMapper for set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize 1PMapper set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send 1PMapper set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send 1PMapper-set msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate 1PMapper Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendCreateVtfdVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send VTFD-Create-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewVlanTaggingFilterData(params[0])
+	if omciErr.GetError() == nil {
+		//all SetByCreate Parameters (assumed to be) set here, for optimisation no 'AddDefaults'
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.CreateRequestType,
+			omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode VTFD for create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			//TODO!!: refactoring improvement requested, here as an example for [VOL-3457]:
+			//  return (dual format) error code that can be used at caller for immediate error treatment
+			//  (relevant to all used sendXX() methods and their error conditions)
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize VTFD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send VTFD create", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send VTFD-Create-msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate VTFD Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
+
+func (oo *omciCC) sendSetEvtocdVar(ctx context.Context, timeout int, highPrio bool,
+	rxChan chan Message, params ...me.ParamData) *me.ManagedEntity {
+	tid := oo.getNextTid(highPrio)
+	logger.Debugw("send EVTOCD-Set-msg:", log.Fields{"device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16),
+		"InstId": strconv.FormatInt(int64(params[0].EntityID), 16)})
+
+	meInstance, omciErr := me.NewExtendedVlanTaggingOperationConfigurationData(params[0])
+	if omciErr.GetError() == nil {
+		omciLayer, msgLayer, err := omci.EncodeFrame(meInstance, omci.SetRequestType, omci.TransactionID(tid))
+		if err != nil {
+			logger.Errorw("Cannot encode EVTOCD for set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		pkt, err := serializeOmciLayer(omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw("Cannot serialize EVTOCD set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{rxChan, oo.receiveOmciResponse},
+		}
+		err = oo.send(ctx, pkt, timeout, 0, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw("Cannot send EVTOCD set", log.Fields{
+				"Err": err, "device-id": oo.deviceID})
+			return nil
+		}
+		logger.Debug("send EVTOCD-set msg done")
+		return meInstance
+	}
+	logger.Errorw("Cannot generate EVTOCD Instance", log.Fields{
+		"Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil
+}
diff --git a/internal/pkg/onuadaptercore/omci_test_request.go b/internal/pkg/onuadaptercore/omci_test_request.go
new file mode 100644
index 0000000..d7d967c
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_test_request.go
@@ -0,0 +1,135 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"errors"
+
+
+	gp "github.com/google/gopacket"
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+//omciTestRequest structure holds the information for the OMCI test
+type omciTestRequest struct {
+	deviceID     string
+	pDevOmciCC   *omciCC
+	started      bool
+	result       bool
+	exclusiveCc  bool
+	allowFailure bool
+	txSeqNo      uint16
+	verifyDone   chan<- bool
+}
+
+//newOmciTestRequest returns a new instance of OmciTestRequest
+func newOmciTestRequest(ctx context.Context,
+	deviceID string, omciCc *omciCC,
+	exclusive bool, allowFailure bool) *omciTestRequest {
+	logger.Debug("omciTestRequest-init")
+	var omciTestRequest omciTestRequest
+	omciTestRequest.deviceID = deviceID
+	omciTestRequest.pDevOmciCC = omciCc
+	omciTestRequest.started = false
+	omciTestRequest.result = false
+	omciTestRequest.exclusiveCc = exclusive
+	omciTestRequest.allowFailure = allowFailure
+
+	return &omciTestRequest
+}
+
+//
+func (oo *omciTestRequest) performOmciTest(ctx context.Context, execChannel chan<- bool) {
+	logger.Debug("omciTestRequest-start-test")
+
+	if oo.pDevOmciCC != nil {
+		oo.verifyDone = execChannel
+		// test functionality is limited to ONU-2G get request for the moment
+		// without yet checking the received response automatically here (might be improved ??)
+		tid := oo.pDevOmciCC.getNextTid(false)
+		onu2gBaseGet, _ := oo.createOnu2gBaseGet(tid)
+		omciRxCallbackPair := callbackPair{
+			cbKey:   tid,
+			cbEntry: callbackPairEntry{nil, oo.receiveOmciVerifyResponse},
+		}
+
+		logger.Debugw("performOmciTest-start sending frame", log.Fields{"for device-id": oo.deviceID})
+		// send with default timeout and normal prio
+		go oo.pDevOmciCC.send(ctx, onu2gBaseGet, ConstDefaultOmciTimeout, 0, false, omciRxCallbackPair)
+
+	} else {
+		logger.Errorw("performOmciTest: Device does not exist", log.Fields{"for device-id": oo.deviceID})
+	}
+}
+
+// these are OMCI related functions, could/should be collected in a separate file? TODO!!!
+// for a simple start just included in here
+//basic approach copied from bbsim, cmp /devices/onu.go and /internal/common/omci/mibpackets.go
+func (oo *omciTestRequest) createOnu2gBaseGet(tid uint16) ([]byte, error) {
+
+	request := &omci.GetRequest{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.Onu2GClassID,
+			EntityInstance: 0, //there is only the 0 instance of ONU2-G (still hard-coded - TODO!!!)
+		},
+		AttributeMask: 0xE000, //example hardcoded (TODO!!!) request EquId, OmccVersion, VendorCode
+	}
+
+	oo.txSeqNo = tid
+	pkt, err := serialize(omci.GetRequestType, request, tid)
+	if err != nil {
+		//omciLogger.WithFields(log.Fields{ ...
+		logger.Errorw("Cannot serialize Onu2-G GetRequest", log.Fields{"Err": err})
+		return nil, err
+	}
+	return pkt, nil
+}
+
+//supply a response handler - in this testobject the message is evaluated directly, no response channel used
+func (oo *omciTestRequest) receiveOmciVerifyResponse(omciMsg *omci.OMCI, packet *gp.Packet, respChan chan Message) error {
+
+	logger.Debugw("verify-omci-message-response received:", log.Fields{"omciMsgType": omciMsg.MessageType,
+		"transCorrId": omciMsg.TransactionID, "DeviceIdent": omciMsg.DeviceIdentifier})
+
+	if omciMsg.TransactionID == oo.txSeqNo {
+		logger.Debugw("verify-omci-message-response", log.Fields{"correct TransCorrId": omciMsg.TransactionID})
+	} else {
+		logger.Debugw("verify-omci-message-response error", log.Fields{"incorrect TransCorrId": omciMsg.TransactionID,
+			"expected": oo.txSeqNo})
+		oo.verifyDone <- false
+		return errors.New("unexpected TransCorrId")
+	}
+	if omciMsg.MessageType == omci.GetResponseType {
+		logger.Debugw("verify-omci-message-response", log.Fields{"correct RespType": omciMsg.MessageType})
+	} else {
+		logger.Debugw("verify-omci-message-response error", log.Fields{"incorrect RespType": omciMsg.MessageType,
+			"expected": omci.GetResponseType})
+		oo.verifyDone <- false
+		return errors.New("unexpected MessageType")
+	}
+
+
+	oo.result = true
+	oo.verifyDone <- true
+
+	return nil
+}
diff --git a/internal/pkg/onuadaptercore/omci_vlan_config.go b/internal/pkg/onuadaptercore/omci_vlan_config.go
new file mode 100644
index 0000000..15d16df
--- /dev/null
+++ b/internal/pkg/onuadaptercore/omci_vlan_config.go
@@ -0,0 +1,701 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"encoding/binary"
+	"errors"
+	"strconv"
+	"time"
+
+	"github.com/looplab/fsm"
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+	of "github.com/opencord/voltha-protos/v3/go/openflow_13"
+)
+
+const (
+	cDefaultDownstreamMode = 0
+	cDefaultTpid           = 0x8100
+)
+
+const (
+	cFilterPrioOffset      = 28
+	cFilterVidOffset       = 15
+	cFilterTpidOffset      = 12
+	cFilterEtherTypeOffset = 0
+	cTreatTTROffset        = 30
+	cTreatPrioOffset       = 16
+	cTreatVidOffset        = 3
+	cTreatTpidOffset       = 0
+)
+const (
+	cFilterOuterOffset = 0
+	cFilterInnerOffset = 4
+	cTreatOuterOffset  = 8
+	cTreatInnerOffset  = 12
+)
+const (
+	cPrioIgnoreTag        uint32 = 15
+	cPrioDefaultFilter    uint32 = 14
+	cPrioDoNotFilter      uint32 = 8
+	cDoNotFilterVid       uint32 = 4096
+	cDoNotFilterTPID      uint32 = 0
+	cDoNotFilterEtherType uint32 = 0
+	cDoNotAddPrio         uint32 = 15
+	cCopyPrioFromInner    uint32 = 8
+	cDontCareVid          uint32 = 0
+	cDontCareTpid         uint32 = 0
+	cSetOutputTpidCopyDei uint32 = 4
+)
+
+const (
+	vlanEvStart          = "vlanEvStart"
+	vlanEvWaitTechProf   = "vlanEvWaitTechProf"
+	vlanEvContinueConfig = "vlanEvContinueConfig"
+	vlanEvStartConfig    = "vlanEvStartConfig"
+	vlanEvRxConfigVtfd   = "vlanEvRxConfigVtfd"
+	vlanEvRxConfigEvtocd = "vlanEvRxConfigEvtocd"
+	vlanEvReset   = "vlanEvReset"
+	vlanEvRestart = "vlanEvRestart"
+)
+const (
+	vlanStDisabled        = "vlanStDisabled"
+	vlanStStarting        = "vlanStStarting"
+	vlanStWaitingTechProf = "vlanStWaitingTechProf"
+	vlanStConfigVtfd      = "vlanStConfigVtfd"
+	vlanStConfigEvtocd    = "vlanStConfigEvtocd"
+	vlanStConfigDone      = "vlanStConfigDone"
+	vlanStCleanEvtocd     = "vlanStCleanEvtocd"
+	vlanStCleanVtfd       = "vlanStCleanVtfd"
+	vlanStCleanupDone     = "vlanStCleanupDone"
+	vlanStResetting       = "vlanStResetting"
+)
+
+//UniVlanConfigFsm defines the structure for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+type UniVlanConfigFsm struct {
+	pDeviceHandler              *deviceHandler
+	pOmciCC                     *omciCC
+	pOnuUniPort                 *onuUniPort
+	pUniTechProf                *onuUniTechProf
+	pOnuDB                      *onuDeviceDB
+	techProfileID               uint16
+	requestEvent                OnuDeviceEvent
+	omciMIdsResponseReceived    chan bool //seperate channel needed for checking multiInstance OMCI message responses
+	pAdaptFsm                   *AdapterFsm
+	acceptIncrementalEvtoOption bool
+	matchVid     uint32
+	matchPcp     uint32
+	tagsToRemove uint32
+	setVid       uint32
+	setPcp       uint32
+	vtfdID       uint16
+	evtocdID     uint16
+}
+
+//NewUniVlanConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
+func NewUniVlanConfigFsm(apDeviceHandler *deviceHandler, apDevOmciCC *omciCC, apUniPort *onuUniPort, apUniTechProf *onuUniTechProf,
+	apOnuDB *onuDeviceDB, aTechProfileID uint16, aRequestEvent OnuDeviceEvent, aName string,
+	aDeviceID string, aCommChannel chan Message,
+	aAcceptIncrementalEvto bool, aMatchVlan uint16, aSetVlan uint16, aSetPcp uint8) *UniVlanConfigFsm {
+	instFsm := &UniVlanConfigFsm{
+		pDeviceHandler:              apDeviceHandler,
+		pOmciCC:                     apDevOmciCC,
+		pOnuUniPort:                 apUniPort,
+		pUniTechProf:                apUniTechProf,
+		pOnuDB:                      apOnuDB,
+		techProfileID:               aTechProfileID,
+		requestEvent:                aRequestEvent,
+		acceptIncrementalEvtoOption: aAcceptIncrementalEvto,
+		matchVid:                    uint32(aMatchVlan),
+		setVid:                      uint32(aSetVlan),
+		setPcp:                      uint32(aSetPcp),
+	}
+	instFsm.tagsToRemove = 1            //one tag to remove as default setting
+	instFsm.matchPcp = cPrioDoNotFilter // do not Filter on prio as default
+	if instFsm.matchVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		// no prio/vid filtering requested
+		instFsm.tagsToRemove = 0          //no tag pop action
+		instFsm.matchPcp = cPrioIgnoreTag // no vlan tag filtering
+		if instFsm.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 ..
+			instFsm.setPcp = 0
+		}
+	}
+
+	instFsm.pAdaptFsm = NewAdapterFsm(aName, aDeviceID, aCommChannel)
+	if instFsm.pAdaptFsm == nil {
+		logger.Errorw("UniVlanConfigFsm's AdapterFsm could not be instantiated!!", log.Fields{
+			"device-id": aDeviceID})
+		return nil
+	}
+
+	instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
+		vlanStDisabled,
+		fsm.Events{
+			{Name: vlanEvStart, Src: []string{vlanStDisabled}, Dst: vlanStStarting},
+			{Name: vlanEvWaitTechProf, Src: []string{vlanStStarting}, Dst: vlanStWaitingTechProf},
+			{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}, Dst: vlanStConfigDone},
+			//TODO:!!! Also define state transitions for cleanup states and timeouts
+			/*
+				{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,
+				vlanStCleanEvtocd, vlanStCleanVtfd, vlanStCleanupDone},
+				Dst: vlanStResetting},
+			// the only way to get to resource-cleared disabled state again is via "resseting"
+			{Name: vlanEvRestart, Src: []string{vlanStResetting}, Dst: vlanStDisabled},
+		},
+
+		fsm.Callbacks{
+			"enter_state":                   func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
+			("enter_" + vlanStStarting):     func(e *fsm.Event) { instFsm.enterConfigStarting(e) },
+			("enter_" + vlanStConfigVtfd):   func(e *fsm.Event) { instFsm.enterConfigVtfd(e) },
+			("enter_" + vlanStConfigEvtocd): func(e *fsm.Event) { instFsm.enterConfigEvtocd(e) },
+			("enter_" + vlanStConfigDone):   func(e *fsm.Event) { instFsm.enterVlanConfigDone(e) },
+			("enter_" + vlanStCleanVtfd):    func(e *fsm.Event) { instFsm.enterCleanVtfd(e) },
+			("enter_" + vlanStCleanEvtocd):  func(e *fsm.Event) { instFsm.enterCleanEvtocd(e) },
+			("enter_" + vlanStCleanupDone):  func(e *fsm.Event) { instFsm.enterVlanCleanupDone(e) },
+			("enter_" + vlanStResetting):    func(e *fsm.Event) { instFsm.enterResetting(e) },
+			("enter_" + vlanStDisabled):     func(e *fsm.Event) { instFsm.enterDisabled(e) },
+		},
+	)
+	if instFsm.pAdaptFsm.pFsm == nil {
+		logger.Errorw("UniVlanConfigFsm's Base FSM could not be instantiated!!", log.Fields{
+			"device-id": aDeviceID})
+		return nil
+	}
+
+	logger.Infow("UniVlanConfigFsm created", log.Fields{"device-id": aDeviceID,
+		"accIncrEvto": instFsm.acceptIncrementalEvtoOption,
+		"matchVid":    strconv.FormatInt(int64(instFsm.matchVid), 16),
+		"setVid":      strconv.FormatInt(int64(instFsm.setVid), 16),
+		"setPcp":      instFsm.setPcp})
+	return instFsm
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigStarting(e *fsm.Event) {
+	logger.Debugw("UniVlanConfigFsm start", log.Fields{"in state": e.FSM.Current(),
+		"device-id": oFsm.pAdaptFsm.deviceID})
+
+	oFsm.omciMIdsResponseReceived = make(chan bool)
+	go oFsm.processOmciVlanMessages()
+	pConfigVlanStateAFsm := oFsm.pAdaptFsm
+	if pConfigVlanStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				//stick to pythonAdapter numbering scheme
+				oFsm.vtfdID = macBridgePortAniEID + oFsm.pOnuUniPort.entityID + oFsm.techProfileID
+				//cmp also usage in EVTOCDE create in omci_cc
+				oFsm.evtocdID = macBridgeServiceProfileEID + uint16(oFsm.pOnuUniPort.macBpNo)
+
+				if oFsm.pUniTechProf.getTechProfileDone(oFsm.pOnuUniPort.uniID, oFsm.techProfileID) {
+					// let the vlan processing begin
+					_ = a_pAFsm.pFsm.Event(vlanEvStartConfig)
+				} else {
+					// set to waiting for Techprofile
+					_ = a_pAFsm.pFsm.Event(vlanEvWaitTechProf)
+				}
+			}
+		}(pConfigVlanStateAFsm)
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigVtfd(e *fsm.Event) {
+	if oFsm.setVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		// meaning transparent setup - no specific VTFD setting required
+		logger.Debugw("UniVlanConfigFsm: no VTFD config required", log.Fields{
+			"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+		// let the FSM proceed ... (from within this state all internal pointers may be expected to be correct)
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		pConfigVlanStateAFsm := oFsm.pAdaptFsm
+		go func(a_pAFsm *AdapterFsm) {
+			_ = a_pAFsm.pFsm.Event(vlanEvRxConfigVtfd)
+		}(pConfigVlanStateAFsm)
+	} else {
+		logger.Debugw("UniVlanConfigFsm create VTFD", log.Fields{
+			"EntitytId": strconv.FormatInt(int64(oFsm.vtfdID), 16),
+			"in state":  e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+		vlanFilterList := make([]uint16, 12)
+		vlanFilterList[0] = uint16(oFsm.setVid) // setVid is assumed to be masked already by the caller to 12 bit
+		meParams := me.ParamData{
+			EntityID: oFsm.vtfdID,
+			Attributes: me.AttributeValueMap{
+				"VlanFilterList":   vlanFilterList,
+				"ForwardOperation": uint8(0x10), //VID investigation
+				"NumberOfEntries":  uint8(1),
+			},
+		}
+		meInstance := oFsm.pOmciCC.sendCreateVtfdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			oFsm.pAdaptFsm.commChan, meParams)
+		//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.pOmciCC.pLastTxMeInstance = meInstance
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterConfigEvtocd(e *fsm.Event) {
+	logger.Debugw("UniVlanConfigFsm - start config EVTOCD loop", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	go oFsm.performConfigEvtocdEntries()
+}
+
+func (oFsm *UniVlanConfigFsm) enterVlanConfigDone(e *fsm.Event) {
+	logger.Debugw("UniVlanConfigFsm - VLAN config done: send dh event notification", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	if oFsm.pDeviceHandler != nil {
+		oFsm.pDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterCleanVtfd(e *fsm.Event) {
+	logger.Debugw("UniVlanConfigFsm Tx Delete::VTFD", log.Fields{
+		/*"EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),*/
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *UniVlanConfigFsm) enterCleanEvtocd(e *fsm.Event) {
+	logger.Debugw("UniVlanConfigFsm  cleanup EVTOCD", log.Fields{
+		/*"EntitytId": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16),
+		"TPPtr":     strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),*/
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *UniVlanConfigFsm) enterVlanCleanupDone(e *fsm.Event) {
+	logger.Debugw("UniVlanConfigFsm - VLAN cleanup done", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+
+	pConfigVlanStateAFsm := oFsm.pAdaptFsm
+	if pConfigVlanStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				_ = a_pAFsm.pFsm.Event(vlanEvReset)
+			}
+		}(pConfigVlanStateAFsm)
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterResetting(e *fsm.Event) {
+	logger.Debugw("UniVlanConfigFsm resetting", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+	pConfigVlanStateAFsm := oFsm.pAdaptFsm
+	if pConfigVlanStateAFsm != nil {
+		// abort running message processing
+		fsmAbortMsg := Message{
+			Type: TestMsg,
+			Data: TestMessage{
+				TestMessageVal: AbortMessageProcessing,
+			},
+		}
+		pConfigVlanStateAFsm.commChan <- fsmAbortMsg
+
+		//try to restart the FSM to 'disabled', decouple event transfer
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				_ = a_pAFsm.pFsm.Event(vlanEvRestart)
+			}
+		}(pConfigVlanStateAFsm)
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) enterDisabled(e *fsm.Event) {
+	logger.Debugw("UniVlanConfigFsm enters disabled state", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+	if oFsm.pDeviceHandler != nil {
+		//request removal of 'reference' in the Handler (completely clear the FSM)
+		go oFsm.pDeviceHandler.RemoveVlanFilterFsm(oFsm.pOnuUniPort)
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) processOmciVlanMessages() { //ctx context.Context?
+	logger.Debugw("Start UniVlanConfigFsm Msg processing", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.pAdaptFsm.deviceID})
+		// 	break loop
+		message, ok := <-oFsm.pAdaptFsm.commChan
+		if !ok {
+			logger.Info("UniVlanConfigFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.pAdaptFsm.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("UniVlanConfigFsm Rx Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+		switch message.Type {
+		case TestMsg:
+			msg, _ := message.Data.(TestMessage)
+			if msg.TestMessageVal == AbortMessageProcessing {
+				logger.Infow("UniVlanConfigFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+				break loop
+			}
+			logger.Warnw("UniVlanConfigFsm unknown TestMessage", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "MessageVal": msg.TestMessageVal})
+		case OMCI:
+			msg, _ := message.Data.(OmciMessage)
+			oFsm.handleOmciVlanConfigMessage(msg)
+		default:
+			logger.Warn("UniVlanConfigFsm Rx unknown message", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+				"message.Type": message.Type})
+		}
+	}
+	logger.Infow("End UniVlanConfigFsm Msg processing", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *UniVlanConfigFsm) handleOmciVlanConfigMessage(msg OmciMessage) {
+	logger.Debugw("Rx OMCI UniVlanConfigFsm Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+		"msgType": msg.OmciMsg.MessageType})
+
+	switch msg.OmciMsg.MessageType {
+	case omci.CreateResponseType:
+		{
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeCreateResponse)
+			if msgLayer == nil {
+				logger.Error("Omci Msg layer could not be detected for CreateResponse")
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.CreateResponse)
+			if !msgOk {
+				logger.Error("Omci Msg layer could not be assigned for CreateResponse")
+				return
+			}
+			logger.Debugw("CreateResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				logger.Errorw("Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+				// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+				return
+			}
+			if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+				msgObj.EntityInstance == oFsm.pOmciCC.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.pOmciCC.pLastTxMeInstance.GetName() {
+				case "VlanTaggingFilterData":
+					{ // let the FSM proceed ...
+						_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigVtfd)
+					}
+				}
+			}
+		} //CreateResponseType
+	case omci.SetResponseType:
+		{
+			msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+			if msgLayer == nil {
+				logger.Error("UniVlanConfigFsm - Omci Msg layer could not be detected for SetResponse")
+				return
+			}
+			msgObj, msgOk := msgLayer.(*omci.SetResponse)
+			if !msgOk {
+				logger.Error("UniVlanConfigFsm - Omci Msg layer could not be assigned for SetResponse")
+				return
+			}
+			logger.Debugw("UniVlanConfigFsm SetResponse Data", log.Fields{"deviceId": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+			if msgObj.Result != me.Success {
+				logger.Errorw("UniVlanConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+				// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+				return
+			}
+			if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+				msgObj.EntityInstance == oFsm.pOmciCC.pLastTxMeInstance.GetEntityID() {
+				switch oFsm.pOmciCC.pLastTxMeInstance.GetName() {
+				case "ExtendedVlanTaggingOperationConfigurationData":
+					{ // let the EVTO config proceed by stopping the wait function
+						oFsm.omciMIdsResponseReceived <- true
+					}
+				}
+			}
+		} //SetResponseType
+	default:
+		{
+			logger.Errorw("UniVlanConfigFsm - Rx OMCI unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+			return
+		}
+	}
+}
+
+func (oFsm *UniVlanConfigFsm) performConfigEvtocdEntries() {
+	{ // for local var
+		// 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("UniVlanConfigFsm Tx Set::EVTOCD", log.Fields{
+			"EntitytId":  strconv.FormatInt(int64(oFsm.evtocdID), 16),
+			"i/oEthType": strconv.FormatInt(int64(cDefaultTpid), 16),
+			"device-id":  oFsm.pAdaptFsm.deviceID})
+		meParams := me.ParamData{
+			EntityID: oFsm.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),
+			},
+		}
+		meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			oFsm.pAdaptFsm.commChan, meParams)
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+		//verify response
+		err := oFsm.waitforOmciResponse()
+		if err != nil {
+			logger.Errorw("Evtocd set TPID failed, aborting VlanConfig FSM!",
+				log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+			return
+		}
+	} //for local var
+
+	if oFsm.setVid == uint32(of.OfpVlanId_OFPVID_PRESENT) {
+		//transparent transmission required
+		logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD single tagged transparent rule", log.Fields{
+			"device-id": oFsm.pAdaptFsm.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: oFsm.evtocdID,
+			Attributes: me.AttributeValueMap{
+				"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+			},
+		}
+		meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+			oFsm.pAdaptFsm.commChan, meParams)
+		//accept also nil as (error) return value for writing to LastTx
+		//  - this avoids misinterpretation of new received OMCI messages
+		oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+		//verify response
+		err := oFsm.waitforOmciResponse()
+		if err != nil {
+			logger.Errorw("Evtocd set transparent singletagged rule failed, aborting VlanConfig FSM!",
+				log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+			_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+			return
+		}
+	} else {
+		// according to py-code acceptIncrementalEvto program option decides upon stacking or translation scenario
+		if oFsm.acceptIncrementalEvtoOption {
+			// this defines VID translation scenario: singletagged->singletagged (if not transparent)
+			logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD single tagged translation rule", log.Fields{
+				"device-id": oFsm.pAdaptFsm.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.matchPcp<<cFilterPrioOffset| // either DNFonPrio or ignore tag (default) on innerVLAN
+					oFsm.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.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.setPcp<<cTreatPrioOffset| // as configured in flow
+					oFsm.setVid<<cTreatVidOffset| //as configured in flow
+					cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+			meParams := me.ParamData{
+				EntityID: oFsm.evtocdID,
+				Attributes: me.AttributeValueMap{
+					"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+				},
+			}
+			meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+				oFsm.pAdaptFsm.commChan, meParams)
+			//accept also nil as (error) return value for writing to LastTx
+			//  - this avoids misinterpretation of new received OMCI messages
+			oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+			//verify response
+			err := oFsm.waitforOmciResponse()
+			if err != nil {
+				logger.Errorw("Evtocd set singletagged translation rule failed, aborting VlanConfig FSM!",
+					log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+				_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+				return
+			}
+		} else {
+			//not transparent and not acceptIncrementalEvtoOption untagged/priotagged->singletagged
+			{ // just for local var's
+				// this defines stacking scenario: untagged->singletagged
+				logger.Debugw("UniVlanConfigFsm Tx Set::EVTOCD untagged->singletagged rule", log.Fields{
+					"device-id": oFsm.pAdaptFsm.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.setVid<<cTreatVidOffset| // Outer VID don't care
+						cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+				meParams := me.ParamData{
+					EntityID: oFsm.evtocdID,
+					Attributes: me.AttributeValueMap{
+						"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+					},
+				}
+				meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+					oFsm.pAdaptFsm.commChan, meParams)
+				//accept also nil as (error) return value for writing to LastTx
+				//  - this avoids misinterpretation of new received OMCI messages
+				oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+				//verify response
+				err := oFsm.waitforOmciResponse()
+				if err != nil {
+					logger.Errorw("Evtocd set untagged->singletagged rule failed, aborting VlanConfig FSM!",
+						log.Fields{"device-id": oFsm.pAdaptFsm.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("UniVlanConfigFsm Tx Set::EVTOCD priotagged->singletagged rule", log.Fields{
+					"device-id": oFsm.pAdaptFsm.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
+
+				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.setVid<<cTreatVidOffset| // Outer VID as configured
+						cSetOutputTpidCopyDei<<cTreatTpidOffset) // Set TPID = 0x8100
+
+				meParams := me.ParamData{
+					EntityID: oFsm.evtocdID,
+					Attributes: me.AttributeValueMap{
+						"ReceivedFrameVlanTaggingOperationTable": sliceEvtocdRule,
+					},
+				}
+				meInstance := oFsm.pOmciCC.sendSetEvtocdVar(context.TODO(), ConstDefaultOmciTimeout, true,
+					oFsm.pAdaptFsm.commChan, meParams)
+				//accept also nil as (error) return value for writing to LastTx
+				//  - this avoids misinterpretation of new received OMCI messages
+				oFsm.pOmciCC.pLastTxMeInstance = meInstance
+
+				//verify response
+				err := oFsm.waitforOmciResponse()
+				if err != nil {
+					logger.Errorw("Evtocd set priotagged->singletagged rule failed, aborting VlanConfig FSM!",
+						log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+					_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvReset)
+					return
+				}
+			} //just for local var's
+		}
+	}
+
+	logger.Debugw("EVTOCD set loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+	_ = oFsm.pAdaptFsm.pFsm.Event(vlanEvRxConfigEvtocd)
+}
+
+func (oFsm *UniVlanConfigFsm) waitforOmciResponse() error {
+	select {
+	case <-time.After(30 * time.Second): //AS FOR THE OTHER OMCI FSM's
+		logger.Warnw("UniVlanConfigFsm multi entity timeout", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+		return errors.New("uniVlanConfigFsm multi entity timeout")
+	case success := <-oFsm.omciMIdsResponseReceived:
+		if success {
+			logger.Debug("UniVlanConfigFsm multi entity response received")
+			return nil
+		}
+		// should not happen so far
+		logger.Warnw("UniVlanConfigFsm multi entity response error", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+		return errors.New("uniVlanConfigFsm multi entity responseError")
+	}
+}
diff --git a/internal/pkg/onuadaptercore/onu_device_db.go b/internal/pkg/onuadaptercore/onu_device_db.go
new file mode 100644
index 0000000..ca954d6
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_device_db.go
@@ -0,0 +1,131 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+	"sort"
+
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+type meDbMap map[me.ClassID]map[uint16]me.AttributeValueMap
+
+//onuDeviceDB structure holds information about known ME's
+type onuDeviceDB struct {
+	ctx             context.Context
+	pOnuDeviceEntry *OnuDeviceEntry
+	meDb            meDbMap
+}
+
+//newOnuDeviceDB returns a new instance for a specific ONU_Device_Entry
+func newOnuDeviceDB(ctx context.Context, aPOnuDeviceEntry *OnuDeviceEntry) *onuDeviceDB {
+	logger.Debugw("Init OnuDeviceDB for:", log.Fields{"device-id": aPOnuDeviceEntry.deviceID})
+	var onuDeviceDB onuDeviceDB
+	onuDeviceDB.ctx = ctx
+	onuDeviceDB.pOnuDeviceEntry = aPOnuDeviceEntry
+	onuDeviceDB.meDb = make(meDbMap)
+
+	return &onuDeviceDB
+}
+
+func (onuDeviceDB *onuDeviceDB) PutMe(meClassID me.ClassID, meEntityID uint16, meAttributes me.AttributeValueMap) {
+
+	if me.OnuDataClassID == meClassID {
+		return
+	}
+
+	meInstMap, ok := onuDeviceDB.meDb[meClassID]
+	if !ok {
+		logger.Debugw("meClassID not found - add to db :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+		meInstMap = make(map[uint16]me.AttributeValueMap)
+		onuDeviceDB.meDb[meClassID] = meInstMap
+		onuDeviceDB.meDb[meClassID][meEntityID] = meAttributes
+	} else {
+		meAttribs, ok := meInstMap[meEntityID]
+		if !ok {
+			/* verbose logging, avoid in >= debug level
+			logger.Debugw("meEntityId not found - add to db :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+			*/
+			meInstMap[meEntityID] = meAttributes
+		} else {
+			/* verbose logging, avoid in >= debug level
+			logger.Debugw("ME-Instance exists already: merge attribute data :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID, "meAttribs": meAttribs})
+			*/
+			for k, v := range meAttributes {
+				meAttribs[k] = v
+			}
+			meInstMap[meEntityID] = meAttribs
+			/* verbose logging, avoid in >= debug level
+			logger.Debugw("ME-Instance updated :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID, "meAttribs": meAttribs})
+			*/
+		}
+	}
+}
+
+func (onuDeviceDB *onuDeviceDB) GetMe(meClassID me.ClassID, meEntityID uint16) me.AttributeValueMap {
+
+	if meAttributes, present := onuDeviceDB.meDb[meClassID][meEntityID]; present {
+		/* verbose logging, avoid in >= debug level
+		logger.Debugw("ME found:", log.Fields{"meClassID": meClassID, "meEntityID": meEntityID, "meAttributes": meAttributes,
+			"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+		*/
+		return meAttributes
+	}
+	return nil
+}
+
+func (onuDeviceDB *onuDeviceDB) getUint32Attrib(meAttribute interface{}) (uint32, error) {
+
+	switch reflect.TypeOf(meAttribute).Kind() {
+	case reflect.Float64:
+		//JSON numbers by default are unmarshaled into values of float64 type if type information is not present
+		return uint32(meAttribute.(float64)), nil
+	case reflect.Uint32:
+		return uint32(meAttribute.(uint32)), nil
+	default:
+		return uint32(0), fmt.Errorf(fmt.Sprintf("wrong interface-type received-%s", onuDeviceDB.pOnuDeviceEntry.deviceID))
+	}
+}
+
+func (onuDeviceDB *onuDeviceDB) getSortedInstKeys(meClassID me.ClassID) []uint16 {
+
+	var meInstKeys []uint16
+
+	meInstMap := onuDeviceDB.meDb[meClassID]
+
+	for k := range meInstMap {
+		meInstKeys = append(meInstKeys, k)
+	}
+	logger.Debugw("meInstKeys - input order :", log.Fields{"meInstKeys": meInstKeys}) //TODO: delete the line after test phase!
+	sort.Slice(meInstKeys, func(i, j int) bool { return meInstKeys[i] < meInstKeys[j] })
+	logger.Debugw("meInstKeys - output order :", log.Fields{"meInstKeys": meInstKeys}) //TODO: delete the line after test phase!
+	return meInstKeys
+}
+
+func (onuDeviceDB *onuDeviceDB) logMeDb() {
+	logger.Debugw("ME instances stored for :", log.Fields{"device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+	for meClassID, meInstMap := range onuDeviceDB.meDb {
+		for meEntityID, meAttribs := range meInstMap {
+			logger.Debugw("ME instance: ", log.Fields{"meClassID": meClassID, "meEntityID": meEntityID, "meAttribs": meAttribs, "device-id": onuDeviceDB.pOnuDeviceEntry.deviceID})
+		}
+	}
+}
diff --git a/internal/pkg/onuadaptercore/onu_device_entry.go b/internal/pkg/onuadaptercore/onu_device_entry.go
new file mode 100644
index 0000000..5d888b1
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_device_entry.go
@@ -0,0 +1,436 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"errors"
+	"time"
+
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+
+
+	"github.com/looplab/fsm"
+	"github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+	"github.com/opencord/voltha-lib-go/v3/pkg/db"
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+const (
+	ulEvStart              = "ulEvStart"
+	ulEvResetMib           = "ulEvResetMib"
+	ulEvGetVendorAndSerial = "ulEvGetVendorAndSerial"
+	ulEvGetEquipmentID     = "ulEvGetEquipmentId"
+	ulEvGetFirstSwVersion  = "ulEvGetFirstSwVersion"
+	ulEvGetSecondSwVersion = "ulEvGetSecondSwVersion"
+	ulEvGetMacAddress      = "ulEvGetMacAddress"
+	ulEvGetMibTemplate     = "ulEvGetMibTemplate"
+	ulEvUploadMib          = "ulEvUploadMib"
+	ulEvExamineMds         = "ulEvExamineMds"
+	ulEvSuccess            = "ulEvSuccess"
+	ulEvMismatch           = "ulEvMismatch"
+	ulEvAuditMib           = "ulEvAuditMib"
+	ulEvForceResync        = "ulEvForceResync"
+	ulEvDiffsFound         = "ulEvDiffsFound"
+	ulEvTimeout            = "ulEvTimeout"
+	ulEvStop               = "ulEvStop"
+)
+const (
+	ulStDisabled               = "ulStDisabled"
+	ulStStarting               = "ulStStarting"
+	ulStResettingMib           = "ulStResettingMib"
+	ulStGettingVendorAndSerial = "ulStGettingVendorAndSerial"
+	ulStGettingEquipmentID     = "ulStGettingEquipmentID"
+	ulStGettingFirstSwVersion  = "ulStGettingFirstSwVersion"
+	ulStGettingSecondSwVersion = "ulStGettingSecondSwVersion"
+	ulStGettingMacAddress      = "ulStGettingMacAddress"
+	ulStGettingMibTemplate     = "ulStGettingMibTemplate"
+	ulStUploading              = "ulStUploading"
+	ulStInSync                 = "ulStInSync"
+	ulStExaminingMds           = "ulStExaminingMds"
+	ulStResynchronizing        = "ulStResynchronizing"
+	ulStAuditing               = "ulStAuditing"
+	ulStOutOfSync              = "ulStOutOfSync"
+)
+
+const (
+	dlEvStart         = "dlEvStart"
+	dlEvCreateGal     = "dlEvCreateGal"
+	dlEvRxGalResp     = "dlEvRxGalResp"
+	dlEvRxOnu2gResp   = "dlEvRxOnu2gResp"
+	dlEvRxBridgeResp  = "dlEvRxBridgeResp"
+	dlEvTimeoutSimple = "dlEvTimeoutSimple"
+	dlEvTimeoutBridge = "dlEvTimeoutBridge"
+	dlEvReset         = "dlEvReset"
+	dlEvRestart       = "dlEvRestart"
+)
+const (
+	dlStDisabled     = "dlStDisabled"
+	dlStStarting     = "dlStStarting"
+	dlStCreatingGal  = "dlStCreatingGal"
+	dlStSettingOnu2g = "dlStSettingOnu2g"
+	dlStBridgeInit   = "dlStBridgeInit"
+	dlStDownloaded   = "dlStDownloaded"
+	dlStResetting    = "dlStResetting"
+)
+
+const (
+	cBasePathMibTemplateKvStore = "service/voltha/omci_mibs/go_templates"
+	cSuffixMibTemplateKvStore   = "%s/%s/%s"
+)
+
+// OnuDeviceEvent - event of interest to Device Adapters and OpenOMCI State Machines
+type OnuDeviceEvent int
+
+const (
+
+	DeviceStatusInit OnuDeviceEvent = 0
+	MibDatabaseSync OnuDeviceEvent = 1
+	OmciCapabilitiesDone OnuDeviceEvent = 2
+	MibDownloadDone OnuDeviceEvent = 3
+	UniLockStateDone OnuDeviceEvent = 4
+	UniUnlockStateDone OnuDeviceEvent = 5
+	UniAdminStateDone OnuDeviceEvent = 6
+	PortLinkUp OnuDeviceEvent = 7
+	PortLinkDw OnuDeviceEvent = 8
+	OmciAniConfigDone OnuDeviceEvent = 9
+	OmciVlanFilterDone OnuDeviceEvent = 10
+)
+
+type activityDescr struct {
+	databaseClass func() error
+	auditDelay uint16
+}
+
+// OmciDeviceFsms - FSM event mapping to database class and time to wait between audits
+type OmciDeviceFsms map[string]activityDescr
+
+// AdapterFsm - Adapter FSM details including channel, event and  device
+type AdapterFsm struct {
+	fsmName  string
+	deviceID string
+	commChan chan Message
+	pFsm     *fsm.FSM
+}
+
+//NewAdapterFsm - FSM details including event, device and channel.
+func NewAdapterFsm(aName string, aDeviceID string, aCommChannel chan Message) *AdapterFsm {
+	aFsm := &AdapterFsm{
+		fsmName:  aName,
+		deviceID: aDeviceID,
+		commChan: aCommChannel,
+	}
+	return aFsm
+}
+
+//Start starts (logs) the omci agent
+func (oo *AdapterFsm) logFsmStateChange(e *fsm.Event) {
+	logger.Debugw("FSM state change", log.Fields{"device-id": oo.deviceID, "FSM name": oo.fsmName,
+		"event name": string(e.Event), "src state": string(e.Src), "dst state": string(e.Dst)})
+}
+
+//OntDeviceEntry structure holds information about the attached FSM'as and their communication
+
+const (
+	firstSwImageMeID  = 0
+	secondSwImageMeID = 1
+)
+const onugMeID = 0
+const onu2gMeID = 0
+const ipHostConfigDataMeID = 1
+const onugSerialNumberLen = 8
+const omciMacAddressLen = 6
+
+type swImages struct {
+	version  string
+	isActive uint8
+}
+
+// OnuDeviceEntry - ONU device info and FSM events.
+type OnuDeviceEntry struct {
+	deviceID           string
+	baseDeviceHandler  *deviceHandler
+	coreProxy          adapterif.CoreProxy
+	adapterProxy       adapterif.AdapterProxy
+	started            bool
+	PDevOmciCC         *omciCC
+	pOnuDB             *onuDeviceDB
+	mibTemplateKVStore *db.Backend
+	vendorID           string
+	serialNumber       string
+	equipmentID        string
+	swImages           [secondSwImageMeID + 1]swImages
+	activeSwVersion    string
+	macAddress         string
+	mibDbClass    func() error
+	supportedFsms OmciDeviceFsms
+	devState      OnuDeviceEvent
+	mibAuditDelay uint16
+	mibDebugLevel string
+
+	pMibUploadFsm *AdapterFsm //could be handled dynamically and more general as pAdapterFsm - perhaps later
+	pMibDownloadFsm *AdapterFsm //could be handled dynamically and more general as pAdapterFsm - perhaps later
+	omciMessageReceived              chan bool    //seperate channel needed by DownloadFsm
+	omciRebootMessageReceivedChannel chan Message // channel needed by Reboot request
+}
+
+//newOnuDeviceEntry returns a new instance of a OnuDeviceEntry
+//mib_db (as well as not inluded alarm_db not really used in this code? VERIFY!!)
+func newOnuDeviceEntry(ctx context.Context, deviceID string, kVStoreHost string, kVStorePort int, kvStoreType string, deviceHandler *deviceHandler,
+	coreProxy adapterif.CoreProxy, adapterProxy adapterif.AdapterProxy,
+	supportedFsmsPtr *OmciDeviceFsms) *OnuDeviceEntry {
+	logger.Infow("init-onuDeviceEntry", log.Fields{"device-id": deviceID})
+	var onuDeviceEntry OnuDeviceEntry
+	onuDeviceEntry.started = false
+	onuDeviceEntry.deviceID = deviceID
+	onuDeviceEntry.baseDeviceHandler = deviceHandler
+	onuDeviceEntry.coreProxy = coreProxy
+	onuDeviceEntry.adapterProxy = adapterProxy
+	onuDeviceEntry.devState = DeviceStatusInit
+	onuDeviceEntry.omciRebootMessageReceivedChannel = make(chan Message, 2048)
+	if supportedFsmsPtr != nil {
+		onuDeviceEntry.supportedFsms = *supportedFsmsPtr
+	} else {
+		//var mibSyncFsm = NewMibSynchronizer()
+		// use some internaö defaults, if not defined from outside
+		onuDeviceEntry.supportedFsms = OmciDeviceFsms{
+			"mib-synchronizer": {
+				//mibSyncFsm,        // Implements the MIB synchronization state machine
+				onuDeviceEntry.mibDbVolatileDict, // Implements volatile ME MIB database
+				//true,                             // Advertise events on OpenOMCI event bus
+				60, // Time to wait between MIB audits.  0 to disable audits.
+				// map[string]func() error{
+				// 	"mib-upload":    onuDeviceEntry.MibUploadTask,
+				// 	"mib-template":  onuDeviceEntry.MibTemplateTask,
+				// 	"get-mds":       onuDeviceEntry.GetMdsTask,
+				// 	"mib-audit":     onuDeviceEntry.GetMdsTask,
+				// 	"mib-resync":    onuDeviceEntry.MibResyncTask,
+				// 	"mib-reconcile": onuDeviceEntry.MibReconcileTask,
+				// },
+			},
+		}
+	}
+	onuDeviceEntry.mibDbClass = onuDeviceEntry.supportedFsms["mib-synchronizer"].databaseClass
+	logger.Debug("access2mibDbClass")
+	go onuDeviceEntry.mibDbClass()
+	onuDeviceEntry.mibAuditDelay = onuDeviceEntry.supportedFsms["mib-synchronizer"].auditDelay
+	logger.Debugw("MibAudit is set to", log.Fields{"Delay": onuDeviceEntry.mibAuditDelay})
+
+	onuDeviceEntry.mibDebugLevel = "normal" //set to "verbose" if you want to have all output, possibly later also per config option!
+	mibUploadChan := make(chan Message, 2048)
+	onuDeviceEntry.pMibUploadFsm = NewAdapterFsm("MibUpload", deviceID, mibUploadChan)
+	onuDeviceEntry.pMibUploadFsm.pFsm = fsm.NewFSM(
+		ulStDisabled,
+		fsm.Events{
+
+			{Name: ulEvStart, Src: []string{ulStDisabled}, Dst: ulStStarting},
+
+			{Name: ulEvResetMib, Src: []string{ulStStarting}, Dst: ulStResettingMib},
+			{Name: ulEvGetVendorAndSerial, Src: []string{ulStResettingMib}, Dst: ulStGettingVendorAndSerial},
+			{Name: ulEvGetEquipmentID, Src: []string{ulStGettingVendorAndSerial}, Dst: ulStGettingEquipmentID},
+			{Name: ulEvGetFirstSwVersion, Src: []string{ulStGettingEquipmentID}, Dst: ulStGettingFirstSwVersion},
+			{Name: ulEvGetSecondSwVersion, Src: []string{ulStGettingFirstSwVersion}, Dst: ulStGettingSecondSwVersion},
+			{Name: ulEvGetMacAddress, Src: []string{ulStGettingSecondSwVersion}, Dst: ulStGettingMacAddress},
+			{Name: ulEvGetMibTemplate, Src: []string{ulStGettingMacAddress}, Dst: ulStGettingMibTemplate},
+
+			{Name: ulEvUploadMib, Src: []string{ulStGettingMibTemplate}, Dst: ulStUploading},
+			{Name: ulEvExamineMds, Src: []string{ulStStarting}, Dst: ulStExaminingMds},
+
+			{Name: ulEvSuccess, Src: []string{ulStGettingMibTemplate}, Dst: ulStInSync},
+			{Name: ulEvSuccess, Src: []string{ulStUploading}, Dst: ulStInSync},
+
+			{Name: ulEvSuccess, Src: []string{ulStExaminingMds}, Dst: ulStInSync},
+			{Name: ulEvMismatch, Src: []string{ulStExaminingMds}, Dst: ulStResynchronizing},
+
+			{Name: ulEvAuditMib, Src: []string{ulStInSync}, Dst: ulStAuditing},
+
+			{Name: ulEvSuccess, Src: []string{ulStOutOfSync}, Dst: ulStInSync},
+			{Name: ulEvAuditMib, Src: []string{ulStOutOfSync}, Dst: ulStAuditing},
+
+			{Name: ulEvSuccess, Src: []string{ulStAuditing}, Dst: ulStInSync},
+			{Name: ulEvMismatch, Src: []string{ulStAuditing}, Dst: ulStResynchronizing},
+			{Name: ulEvForceResync, Src: []string{ulStAuditing}, Dst: ulStResynchronizing},
+
+			{Name: ulEvSuccess, Src: []string{ulStResynchronizing}, Dst: ulStInSync},
+			{Name: ulEvDiffsFound, Src: []string{ulStResynchronizing}, Dst: ulStOutOfSync},
+
+			{Name: ulEvTimeout, Src: []string{ulStResettingMib, ulStGettingVendorAndSerial, ulStGettingEquipmentID, ulStGettingFirstSwVersion,
+				ulStGettingSecondSwVersion, ulStGettingMacAddress, ulStGettingMibTemplate, ulStUploading, ulStResynchronizing, ulStExaminingMds,
+				ulStInSync, ulStOutOfSync, ulStAuditing}, Dst: ulStStarting},
+
+			{Name: ulEvStop, Src: []string{ulStStarting, ulStResettingMib, ulStGettingVendorAndSerial, ulStGettingEquipmentID, ulStGettingFirstSwVersion,
+				ulStGettingSecondSwVersion, ulStGettingMacAddress, ulStGettingMibTemplate, ulStUploading, ulStResynchronizing, ulStExaminingMds,
+				ulStInSync, ulStOutOfSync, ulStAuditing}, Dst: ulStDisabled},
+		},
+
+		fsm.Callbacks{
+			"enter_state":                           func(e *fsm.Event) { onuDeviceEntry.pMibUploadFsm.logFsmStateChange(e) },
+			("enter_" + ulStStarting):               func(e *fsm.Event) { onuDeviceEntry.enterStartingState(e) },
+			("enter_" + ulStResettingMib):           func(e *fsm.Event) { onuDeviceEntry.enterResettingMibState(e) },
+			("enter_" + ulStGettingVendorAndSerial): func(e *fsm.Event) { onuDeviceEntry.enterGettingVendorAndSerialState(e) },
+			("enter_" + ulStGettingEquipmentID):     func(e *fsm.Event) { onuDeviceEntry.enterGettingEquipmentIDState(e) },
+			("enter_" + ulStGettingFirstSwVersion):  func(e *fsm.Event) { onuDeviceEntry.enterGettingFirstSwVersionState(e) },
+			("enter_" + ulStGettingSecondSwVersion): func(e *fsm.Event) { onuDeviceEntry.enterGettingSecondSwVersionState(e) },
+			("enter_" + ulStGettingMacAddress):      func(e *fsm.Event) { onuDeviceEntry.enterGettingMacAddressState(e) },
+			("enter_" + ulStGettingMibTemplate):     func(e *fsm.Event) { onuDeviceEntry.enterGettingMibTemplate(e) },
+			("enter_" + ulStUploading):              func(e *fsm.Event) { onuDeviceEntry.enterUploadingState(e) },
+			("enter_" + ulStExaminingMds):           func(e *fsm.Event) { onuDeviceEntry.enterExaminingMdsState(e) },
+			("enter_" + ulStResynchronizing):        func(e *fsm.Event) { onuDeviceEntry.enterResynchronizingState(e) },
+			("enter_" + ulStAuditing):               func(e *fsm.Event) { onuDeviceEntry.enterAuditingState(e) },
+			("enter_" + ulStOutOfSync):              func(e *fsm.Event) { onuDeviceEntry.enterOutOfSyncState(e) },
+			("enter_" + ulStInSync):                 func(e *fsm.Event) { onuDeviceEntry.enterInSyncState(e) },
+		},
+	)
+	mibDownloadChan := make(chan Message, 2048)
+	onuDeviceEntry.pMibDownloadFsm = NewAdapterFsm("MibDownload", deviceID, mibDownloadChan)
+	onuDeviceEntry.pMibDownloadFsm.pFsm = fsm.NewFSM(
+		dlStDisabled,
+		fsm.Events{
+
+			{Name: dlEvStart, Src: []string{dlStDisabled}, Dst: dlStStarting},
+
+			{Name: dlEvCreateGal, Src: []string{dlStStarting}, Dst: dlStCreatingGal},
+			{Name: dlEvRxGalResp, Src: []string{dlStCreatingGal}, Dst: dlStSettingOnu2g},
+			{Name: dlEvRxOnu2gResp, Src: []string{dlStSettingOnu2g}, Dst: dlStBridgeInit},
+			// the bridge state is used for multi ME config for alle UNI related ports
+			// maybe such could be reflected in the state machine as well (port number parametrized)
+			// but that looks not straightforward here - so we keep it simple here for the beginning(?)
+			{Name: dlEvRxBridgeResp, Src: []string{dlStBridgeInit}, Dst: dlStDownloaded},
+
+			{Name: dlEvTimeoutSimple, Src: []string{dlStCreatingGal, dlStSettingOnu2g}, Dst: dlStStarting},
+			{Name: dlEvTimeoutBridge, Src: []string{dlStBridgeInit}, Dst: dlStStarting},
+
+			{Name: dlEvReset, Src: []string{dlStStarting, dlStCreatingGal, dlStSettingOnu2g,
+				dlStBridgeInit, dlStDownloaded}, Dst: dlStResetting},
+			// exceptional treatment for all states except dlStResetting
+			{Name: dlEvRestart, Src: []string{dlStStarting, dlStCreatingGal, dlStSettingOnu2g,
+				dlStBridgeInit, dlStDownloaded, dlStResetting}, Dst: dlStDisabled},
+		},
+
+		fsm.Callbacks{
+			"enter_state":                 func(e *fsm.Event) { onuDeviceEntry.pMibDownloadFsm.logFsmStateChange(e) },
+			("enter_" + dlStStarting):     func(e *fsm.Event) { onuDeviceEntry.enterDLStartingState(e) },
+			("enter_" + dlStCreatingGal):  func(e *fsm.Event) { onuDeviceEntry.enterCreatingGalState(e) },
+			("enter_" + dlStSettingOnu2g): func(e *fsm.Event) { onuDeviceEntry.enterSettingOnu2gState(e) },
+			("enter_" + dlStBridgeInit):   func(e *fsm.Event) { onuDeviceEntry.enterBridgeInitState(e) },
+			("enter_" + dlStDownloaded):   func(e *fsm.Event) { onuDeviceEntry.enterDownloadedState(e) },
+			("enter_" + dlStResetting):    func(e *fsm.Event) { onuDeviceEntry.enterResettingState(e) },
+		},
+	)
+	if onuDeviceEntry.pMibDownloadFsm == nil || onuDeviceEntry.pMibDownloadFsm.pFsm == nil {
+		logger.Error("MibDownloadFsm could not be instantiated!!")
+		// some specifc error treatment - or waiting for crash ???
+	}
+
+	onuDeviceEntry.mibTemplateKVStore = onuDeviceEntry.baseDeviceHandler.setBackend(cBasePathMibTemplateKvStore)
+	if onuDeviceEntry.mibTemplateKVStore == nil {
+		logger.Errorw("Failed to setup mibTemplateKVStore", log.Fields{"device-id": deviceID})
+	}
+
+	return &onuDeviceEntry
+}
+
+//start starts (logs) the omci agent
+func (oo *OnuDeviceEntry) start(ctx context.Context) error {
+	logger.Info("starting-OnuDeviceEntry")
+
+	oo.PDevOmciCC = newOmciCC(ctx, oo, oo.deviceID, oo.baseDeviceHandler,
+		oo.coreProxy, oo.adapterProxy)
+	if oo.PDevOmciCC == nil {
+		logger.Errorw("Could not create devOmciCc - abort", log.Fields{"for device-id": oo.deviceID})
+		return errors.New("could not create devOmciCc")
+	}
+
+	oo.started = true
+	logger.Info("OnuDeviceEntry-started")
+	return nil
+}
+
+//stop terminates the session
+func (oo *OnuDeviceEntry) stop(ctx context.Context) error {
+	logger.Info("stopping-OnuDeviceEntry")
+	oo.started = false
+	logger.Info("OnuDeviceEntry-stopped")
+	return nil
+}
+
+func (oo *OnuDeviceEntry) reboot(ctx context.Context) error {
+	logger.Info("reboot-OnuDeviceEntry")
+	if err := oo.PDevOmciCC.sendReboot(context.TODO(), ConstDefaultOmciTimeout, true, oo.omciRebootMessageReceivedChannel); err != nil {
+		logger.Errorw("onu didn't reboot", log.Fields{"for device-id": oo.deviceID})
+		return err
+	}
+	logger.Info("OnuDeviceEntry-reboot")
+	return nil
+}
+
+func (oo *OnuDeviceEntry) waitForRebootResponse(responseChannel chan Message) error {
+	select {
+	case <-time.After(3 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+		logger.Warnw("Reboot timeout", log.Fields{"for device-id": oo.deviceID})
+		return errors.New("rebootTimeout")
+	case data := <-responseChannel:
+		switch data.Data.(OmciMessage).OmciMsg.MessageType {
+		case omci.RebootResponseType:
+			{
+				msgLayer := (*data.Data.(OmciMessage).OmciPacket).Layer(omci.LayerTypeRebootResponse)
+				if msgLayer == nil {
+					return errors.New("omci Msg layer could not be detected for RebootResponseType")
+				}
+				msgObj, msgOk := msgLayer.(*omci.GetResponse)
+				if !msgOk {
+					return errors.New("omci Msg layer could not be assigned for RebootResponseType")
+				}
+				logger.Debugw("CreateResponse Data", log.Fields{"device-id": oo.deviceID, "data-fields": msgObj})
+				if msgObj.Result != me.Success {
+					logger.Errorw("Omci RebootResponseType Error ", log.Fields{"Error": msgObj.Result})
+					// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+					return errors.New("omci RebootResponse Result Error indication")
+				}
+				return nil
+			}
+		}
+		logger.Warnw("Reboot response error", log.Fields{"for device-id": oo.deviceID})
+		return errors.New("unexpected OmciResponse type received")
+	}
+}
+
+//Relay the InSync message via Handler to Rw core - Status update
+func (oo *OnuDeviceEntry) transferSystemEvent(devEvent OnuDeviceEvent) {
+	logger.Debugw("relaying system-event", log.Fields{"Event": devEvent})
+	if devEvent == MibDatabaseSync {
+		if oo.devState < MibDatabaseSync { //devState has not been synced yet
+			oo.devState = MibDatabaseSync
+			go oo.baseDeviceHandler.deviceProcStatusUpdate(devEvent)
+			//TODO!!! device control: next step: start MIB capability verification from here ?!!!
+		} else {
+			logger.Debugw("mibinsync-event in some already synced state - ignored", log.Fields{"state": oo.devState})
+		}
+	} else if devEvent == MibDownloadDone {
+		if oo.devState < MibDownloadDone { //devState has not been synced yet
+			oo.devState = MibDownloadDone
+			go oo.baseDeviceHandler.deviceProcStatusUpdate(devEvent)
+		} else {
+			logger.Debugw("mibdownloaddone-event was already seen - ignored", log.Fields{"state": oo.devState})
+		}
+	} else {
+		logger.Warnw("device-event not yet handled", log.Fields{"state": devEvent})
+	}
+}
diff --git a/internal/pkg/onuadaptercore/onu_list_access.go b/internal/pkg/onuadaptercore/onu_list_access.go
new file mode 100644
index 0000000..427cf19
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_list_access.go
@@ -0,0 +1,136 @@
+/*
+ * 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 adaptercoreonu
+
+import (
+	"encoding/json"
+	"io/ioutil"
+	"os"
+)
+
+// Jsonファイル名
+const jsonname = "/onu_list.json"
+
+// OnuStatus ONU状態
+type OnuStatus struct {
+	ID           string `json:"id"`
+	AdminState   string `json:"admin_state"`
+	OpeState     string `json:"ope_state"`
+	ConnectState string `json:"con_state"`
+	MacAddress   string `json:"mac_addr"`
+	RebootState  string `json:"reboot_state"`
+}
+
+//ReadOnuStatusList Jsonファイル読込み
+func ReadOnuStatusList() ([]OnuStatus, error) {
+	bytes, err := ioutil.ReadFile(os.Getenv("HOME") + jsonname)
+	if err != nil && os.IsNotExist(err) {
+		return nil, nil
+	}
+	var onuList []OnuStatus
+	if err := json.Unmarshal(bytes, &onuList); err != nil {
+		return nil, err
+	}
+	return onuList, nil
+}
+
+//WriteOnuStatusList Jsonファイル書き込み
+func WriteOnuStatusList(list []OnuStatus) error {
+	bytes, err := json.Marshal(list)
+	if err != nil {
+		return err
+	}
+	return ioutil.WriteFile(os.Getenv("HOME")+jsonname, bytes, 0644)
+}
+
+//AddOnu ONU追加
+func AddOnu(sts *OnuStatus) error {
+	list, err := ReadOnuStatusList()
+	if err != nil {
+		return err
+	}
+	if list == nil {
+		newList := []OnuStatus{*sts}
+		return WriteOnuStatusList(newList)
+	}
+	return WriteOnuStatusList(append(list, *sts))
+}
+
+//UpdateOnu ONU状態更新
+func UpdateOnu(upSts *OnuStatus) error {
+	list, err := ReadOnuStatusList()
+	if (err != nil) || (list == nil) {
+		return err
+	}
+	newList := []OnuStatus{}
+	for _, sts := range list {
+		if sts.ID == upSts.ID {
+			newList = append(newList, *upSts)
+		} else {
+			newList = append(newList, sts)
+		}
+	}
+	return WriteOnuStatusList(newList)
+}
+
+//RemoveOnu ONU削除
+func RemoveOnu(id string) error {
+	list, err := ReadOnuStatusList()
+	if (err != nil) || (list == nil) {
+		return err
+	}
+	newList := []OnuStatus{}
+	for _, sts := range list {
+		if sts.ID != id {
+			newList = append(newList, sts)
+		}
+	}
+	return WriteOnuStatusList(newList)
+}
+
+//GetOnuFromDeviceID ONU状態取得
+func GetOnuFromDeviceID(id string) (*OnuStatus, error) {
+	list, err := ReadOnuStatusList()
+	if err != nil {
+		return nil, err
+	}
+	if list == nil {
+		return nil, nil
+	}
+	for _, sts := range list {
+		if sts.ID != id {
+			return &sts, nil
+		}
+	}
+	return nil, nil
+}
+
+//GetOnuFromMacAddr ONU状態取得
+func GetOnuFromMacAddr(addr string) (*OnuStatus, error) {
+	list, err := ReadOnuStatusList()
+	if err != nil {
+		return nil, err
+	}
+	if list == nil {
+		return nil, nil
+	}
+	for _, sts := range list {
+		if sts.MacAddress == addr {
+			return &sts, nil
+		}
+	}
+	return nil, nil
+}
diff --git a/internal/pkg/onuadaptercore/onu_uni_port.go b/internal/pkg/onuadaptercore/onu_uni_port.go
new file mode 100644
index 0000000..9bfe1e9
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_uni_port.go
@@ -0,0 +1,180 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+	vc "github.com/opencord/voltha-protos/v3/go/common"
+	of "github.com/opencord/voltha-protos/v3/go/openflow_13"
+	"github.com/opencord/voltha-protos/v3/go/voltha"
+)
+
+type uniPortType uint8
+
+// UniPPTP Interface type - re-use values from G.988 TP type definition (directly used in OMCI!)
+const (
+	uniPPTP uniPortType = 1 // relates to PPTP
+	uniVEIP uniPortType = 11 // relates to VEIP
+)
+
+//onuUniPort structure holds information about the ONU attached Uni Ports
+type onuUniPort struct {
+	enabled    bool
+	name       string
+	portNo     uint32
+	portType   uniPortType
+	ofpPortNo  string
+	uniID      uint8
+	macBpNo    uint8
+	entityID   uint16
+	adminState vc.AdminState_Types
+	operState  vc.OperStatus_Types
+	pPort      *voltha.Port
+}
+
+//newOnuUniPort returns a new instance of a OnuUniPort
+func newOnuUniPort(aUniID uint8, aPortNo uint32, aInstNo uint16,
+	aPortType uniPortType) *onuUniPort {
+	logger.Infow("init-onuUniPort", log.Fields{"uniID": aUniID,
+		"portNo": aPortNo, "InstNo": aInstNo, "type": aPortType})
+	var onuUniPort onuUniPort
+	onuUniPort.enabled = false
+	onuUniPort.name = "uni-" + strconv.FormatUint(uint64(aPortNo), 10)
+	onuUniPort.portNo = aPortNo
+	onuUniPort.portType = aPortType
+	onuUniPort.ofpPortNo = onuUniPort.name
+	onuUniPort.uniID = aUniID
+	onuUniPort.macBpNo = aUniID + 1 //ensure >0 instanceNo
+	onuUniPort.entityID = aInstNo
+	onuUniPort.adminState = vc.AdminState_ENABLED //enabled per create
+	onuUniPort.operState = vc.OperStatus_UNKNOWN
+	onuUniPort.pPort = nil // to be set on create
+	return &onuUniPort
+}
+
+//createVolthaPort creates the Voltha port based on ONU UNI Port and informs the core about it
+func (oo *onuUniPort) createVolthaPort(apDeviceHandler *deviceHandler) error {
+	logger.Debugw("creating-voltha-uni-port", log.Fields{
+		"device-id": apDeviceHandler.device.Id, "portNo": oo.portNo})
+	name := apDeviceHandler.device.SerialNumber + "-" + strconv.FormatUint(uint64(oo.macBpNo), 10)
+
+	var macOctets [6]uint8
+	macOctets[5] = 0x08
+	macOctets[4] = uint8(apDeviceHandler.ponPortNumber >> 8)
+	macOctets[3] = uint8(apDeviceHandler.ponPortNumber)
+	macOctets[2] = uint8(oo.portNo >> 16)
+	macOctets[1] = uint8(oo.portNo >> 8)
+	macOctets[0] = uint8(oo.portNo)
+	onuEntry := apDeviceHandler.getOnuDeviceEntry(false)
+	if onuEntry == nil {
+		logger.Debugw("not-found-onu-device-entry", log.Fields{"error": "not found"})
+	} else {
+		if len(onuEntry.macAddress) == 12 {
+			for idx := 0; idx < 6; idx++ {
+				if v, err := strconv.ParseInt(onuEntry.macAddress[idx*2:(idx*2)+2], 16, 8); err == nil {
+					macOctets[5-idx] = uint8(v)
+				}
+			}
+		}
+	}
+	hwAddr := genMacFromOctets(macOctets)
+	ofHwAddr := macAddressToUint32Array(hwAddr)
+	capacity := uint32(of.OfpPortFeatures_OFPPF_1GB_FD | of.OfpPortFeatures_OFPPF_FIBER)
+	ofUniPortState := of.OfpPortState_OFPPS_LINK_DOWN
+	/* as the VOLTHA port create is only called directly after Uni Port create
+	   the OfPortOperState is always Down
+	   Note: this way the OfPortOperState won't ever change (directly in adapter)
+	   maybe that was already always the case, but looks a bit weird - to be kept in mind ...
+		if pUniPort.operState == vc.OperStatus_ACTIVE {
+			ofUniPortState = of.OfpPortState_OFPPS_LIVE
+		}
+	*/
+	logger.Debugw("ofPort values", log.Fields{
+		"forUniPortName": oo.name, "forMacBase": hwAddr,
+		"name": name, "hwAddr": ofHwAddr, "OperState": ofUniPortState})
+
+	pUniPort := &voltha.Port{
+		PortNo:     oo.portNo,
+		Label:      oo.name,
+		Type:       voltha.Port_ETHERNET_UNI,
+		AdminState: oo.adminState,
+		OperStatus: oo.operState,
+		// obviously empty peer setting
+		OfpPort: &of.OfpPort{
+			Name:       name,
+			HwAddr:     ofHwAddr,
+			Config:     0,
+			State:      uint32(ofUniPortState),
+			Curr:       capacity,
+			Advertised: capacity,
+			Peer:       capacity,
+			CurrSpeed:  uint32(of.OfpPortFeatures_OFPPF_1GB_FD),
+			MaxSpeed:   uint32(of.OfpPortFeatures_OFPPF_1GB_FD),
+		},
+	}
+	if pUniPort != nil {
+		if err := apDeviceHandler.coreProxy.PortCreated(context.TODO(),
+			apDeviceHandler.deviceID, pUniPort); err != nil {
+			logger.Fatalf("adding-uni-port: create-VOLTHA-Port-failed-%s", err)
+			return err
+		}
+		logger.Infow("Voltha onuUniPort-added", log.Fields{
+			"device-id": apDeviceHandler.device.Id, "PortNo": oo.portNo})
+		oo.pPort = pUniPort
+		oo.operState = vc.OperStatus_DISCOVERED
+	} else {
+		logger.Warnw("could not create Voltha UniPort", log.Fields{
+			"device-id": apDeviceHandler.device.Id, "PortNo": oo.portNo})
+		return errors.New("create Voltha UniPort failed")
+	}
+	return nil
+}
+
+//setOperState modifies OperState of the the UniPort
+func (oo *onuUniPort) setOperState(aNewOperState vc.OperStatus_Types) {
+	oo.operState = aNewOperState
+}
+
+// uni port related utility functions (so far only used here)
+func genMacFromOctets(aOctets [6]uint8) string {
+	return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
+		aOctets[5], aOctets[4], aOctets[3],
+		aOctets[2], aOctets[1], aOctets[0])
+}
+
+//copied from OLT Adapter: unify centrally ?
+func macAddressToUint32Array(mac string) []uint32 {
+	slist := strings.Split(mac, ":")
+	result := make([]uint32, len(slist))
+	var err error
+	var tmp int64
+	for index, val := range slist {
+		if tmp, err = strconv.ParseInt(val, 16, 32); err != nil {
+			return []uint32{1, 2, 3, 4, 5, 6}
+		}
+		result[index] = uint32(tmp)
+	}
+	return result
+}
diff --git a/internal/pkg/onuadaptercore/onu_uni_tp.go b/internal/pkg/onuadaptercore/onu_uni_tp.go
new file mode 100644
index 0000000..09ae7bf
--- /dev/null
+++ b/internal/pkg/onuadaptercore/onu_uni_tp.go
@@ -0,0 +1,681 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/db"
+	"github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+	tp "github.com/opencord/voltha-lib-go/v3/pkg/techprofile"
+)
+
+const cBasePathTechProfileKVStore = "service/voltha/technology_profiles"
+const cBasePathOnuKVStore = "service/voltha/openonu"
+
+//definitions for TechProfileProcessing - copied from OltAdapter:openolt_flowmgr.go
+//  could perhaps be defined more globally
+const (
+	binaryStringPrefix = "0b"
+)
+
+type resourceEntry int
+
+const (
+	cResourceGemPort resourceEntry = 1
+	cResourceTcont   resourceEntry = 2
+)
+
+type uniPersData struct {
+	PersUniID  uint32 `json:"uni_id"`
+	PersTpPath string `json:"tp_path"`
+}
+
+type onuPersistentData struct {
+	PersOnuID      uint32        `json:"onu_id"`
+	PersIntfID     uint32        `json:"intf_id"`
+	PersSnr        string        `json:"serial_number"`
+	PersAdminState string        `json:"admin_state"`
+	PersOperState  string        `json:"oper_state"`
+	PersUniTpPath  []uniPersData `json:"uni_config"`
+}
+
+type tTechProfileIndication struct {
+	techProfileType       string
+	techProfileID         uint16
+	techProfileConfigDone bool
+}
+
+type tcontParamStruct struct {
+	allocID     uint16
+	schedPolicy uint8
+}
+type gemPortParamStruct struct {
+	gemPortID       uint16
+	direction       uint8
+	gemPortEncState uint8
+	prioQueueIndex  uint8
+	pbitString      string
+	discardPolicy   string
+	queueSchedPolicy string
+	queueWeight      uint8
+}
+
+//refers to one tcont and its properties and all assigned GemPorts and their properties
+type tcontGemList struct {
+	tcontParams      tcontParamStruct
+	mapGemPortParams map[uint16]*gemPortParamStruct
+}
+
+//refers to all tcont and their Tcont/GemPort Parameters
+type tMapPonAniConfig map[uint16]*tcontGemList
+
+//onuUniTechProf structure holds information about the TechProfiles attached to Uni Ports of the ONU
+type onuUniTechProf struct {
+	deviceID                 string
+	baseDeviceHandler        *deviceHandler
+	tpProcMutex              sync.RWMutex
+	mapUniTpPath             map[uint32]string
+	sOnuPersistentData       onuPersistentData
+	techProfileKVStore       *db.Backend
+	onuKVStore               *db.Backend
+	onuKVStorePath           string
+	chTpConfigProcessingStep chan uint8
+	chTpKvProcessingStep     chan uint8
+	mapUniTpIndication       map[uint8]*tTechProfileIndication //use pointer values to ease assignments to the map
+	mapPonAniConfig          map[uint8]*tMapPonAniConfig       //per UNI: use pointer values to ease assignments to the map
+	pAniConfigFsm            *uniPonAniConfigFsm
+	procResult               error //error indication of processing
+	mutexTPState             sync.Mutex
+}
+
+//newOnuUniTechProf returns the instance of a OnuUniTechProf
+//(one instance per ONU/deviceHandler for all possible UNI's)
+func newOnuUniTechProf(ctx context.Context, aDeviceID string, aDeviceHandler *deviceHandler) *onuUniTechProf {
+	logger.Infow("init-OnuUniTechProf", log.Fields{"device-id": aDeviceID})
+	var onuTP onuUniTechProf
+	onuTP.deviceID = aDeviceID
+	onuTP.baseDeviceHandler = aDeviceHandler
+	onuTP.tpProcMutex = sync.RWMutex{}
+	onuTP.mapUniTpPath = make(map[uint32]string)
+	onuTP.sOnuPersistentData.PersUniTpPath = make([]uniPersData, 1)
+	onuTP.chTpConfigProcessingStep = make(chan uint8)
+	onuTP.chTpKvProcessingStep = make(chan uint8)
+	onuTP.mapUniTpIndication = make(map[uint8]*tTechProfileIndication)
+	onuTP.mapPonAniConfig = make(map[uint8]*tMapPonAniConfig)
+	onuTP.procResult = nil //default assumption processing done with success
+
+	onuTP.techProfileKVStore = aDeviceHandler.setBackend(cBasePathTechProfileKVStore)
+	if onuTP.techProfileKVStore == nil {
+		logger.Errorw("Can't access techProfileKVStore - no backend connection to service",
+			log.Fields{"device-id": aDeviceID, "service": cBasePathTechProfileKVStore})
+	}
+
+	onuTP.onuKVStorePath = onuTP.deviceID
+	onuTP.onuKVStore = aDeviceHandler.setBackend(cBasePathOnuKVStore)
+	if onuTP.onuKVStore == nil {
+		logger.Errorw("Can't access onuKVStore - no backend connection to service",
+			log.Fields{"device-id": aDeviceID, "service": cBasePathOnuKVStore})
+	}
+	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()
+}
+
+// resetProcessingErrorIndication resets the internal error indication
+// need to be called before evaluation of any subsequent processing (given by waitForTpCompletion())
+func (onuTP *onuUniTechProf) resetProcessingErrorIndication() {
+	onuTP.procResult = nil
+}
+
+// updateOnuUniTpPath verifies and updates changes in the kvStore onuUniTpPath
+func (onuTP *onuUniTechProf) updateOnuUniTpPath(aUniID uint32, aPathString string) bool {
+	/* within some specific InterAdapter processing request write/read access to data is ensured to be sequentially,
+	   as also the complete sequence is ensured to 'run to  completion' before some new request is accepted
+	   no specific concurrency protection to sOnuPersistentData is required here
+	*/
+	if existingPath, present := onuTP.mapUniTpPath[aUniID]; present {
+		// uni entry already exists
+		//logger.Debugw(" already exists", log.Fields{"for InstanceId": a_uniInstNo})
+		if existingPath != aPathString {
+			if aPathString == "" {
+				//existing entry to be deleted
+				logger.Debugw("UniTp path delete", log.Fields{
+					"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+				delete(onuTP.mapUniTpPath, aUniID)
+			} else {
+				//existing entry to be modified
+				logger.Debugw("UniTp path modify", log.Fields{
+					"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+				onuTP.mapUniTpPath[aUniID] = aPathString
+			}
+			return true
+		}
+		//entry already exists
+		logger.Debugw("UniTp path already exists", log.Fields{
+			"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+		return false
+	}
+	if aPathString == "" {
+		//delete request in non-existing state , accept as no change
+		logger.Debugw("UniTp path already removed", log.Fields{
+			"device-id": onuTP.deviceID, "uniID": aUniID})
+		return false
+	}
+	logger.Debugw("New UniTp path set", log.Fields{
+		"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+	onuTP.mapUniTpPath[aUniID] = aPathString
+	return true
+}
+
+func (onuTP *onuUniTechProf) waitForTpCompletion(cancel context.CancelFunc, wg *sync.WaitGroup) error {
+	defer cancel() //ensure termination of context (may be pro forma)
+	wg.Wait()
+	logger.Debug("some TechProfile Processing completed")
+	onuTP.tpProcMutex.Unlock() //allow further TP related processing
+	return onuTP.procResult
+}
+
+// configureUniTp checks existing tp resources to delete 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, wg *sync.WaitGroup) {
+	defer wg.Done() //always decrement the waitGroup on return
+	logger.Debugw("configure the Uni according to TpPath", log.Fields{
+		"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
+
+	if onuTP.techProfileKVStore == nil {
+		logger.Debug("techProfileKVStore not set - abort")
+		onuTP.procResult = errors.New("techProfile config aborted: techProfileKVStore not set")
+		return
+	}
+
+	var pCurrentUniPort *onuUniPort
+	for _, uniPort := range onuTP.baseDeviceHandler.uniEntityMap {
+		// only if this port is validated for operState transfer
+		if uniPort.uniID == uint8(aUniID) {
+			pCurrentUniPort = uniPort
+			break //found - end search loop
+		}
+	}
+	if pCurrentUniPort == nil {
+		logger.Errorw("TechProfile configuration aborted: requested uniID not found in PortDB",
+			log.Fields{"device-id": onuTP.deviceID, "uniID": aUniID})
+		onuTP.procResult = errors.New("techProfile config aborted: requested uniID not found")
+		return
+	}
+
+	var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
+
+	/* 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, aPathString, processingStep)
+	if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
+		//timeout or error detected
+		logger.Debugw("tech-profile related configuration aborted on read",
+			log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID})
+		onuTP.procResult = errors.New("techProfile config aborted: tech-profile read issue")
+		return
+	}
+
+	processingStep++
+	if valuePA, existPA := onuTP.mapPonAniConfig[aUniID]; existPA {
+		if _, existTG := (*valuePA)[0]; existTG {
+			//Config data for this uni and and at least TCont Index 0 exist
+			go onuTP.setAniSideConfigFromTechProfile(ctx, aUniID, pCurrentUniPort, processingStep)
+			if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpConfigProcessingStep, processingStep) {
+				//timeout or error detected
+				logger.Debugw("tech-profile related configuration aborted on set",
+					log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID})
+				onuTP.procResult = errors.New("techProfile config aborted: Omci AniSideConfig failed")
+				//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)
+				_ = onuTP.pAniConfigFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+				return
+			}
+		} else {
+			// strange: UNI entry exists, but no ANI data, maybe such situation should be cleared up (if observed)
+			logger.Debugw("no Tcont/Gem data for this UNI found - abort", log.Fields{
+				"device-id": onuTP.deviceID, "uniID": aUniID})
+			onuTP.procResult = errors.New("techProfile config aborted: no Tcont/Gem data found for this UNI")
+			return
+		}
+	} else {
+		logger.Debugw("no PonAni data for this UNI found - abort", log.Fields{
+			"device-id": onuTP.deviceID, "uniID": aUniID})
+		onuTP.procResult = errors.New("techProfile config aborted: no AniSide data found for this UNI")
+		return
+	}
+}
+
+func (onuTP *onuUniTechProf) updateOnuTpPathKvStore(ctx context.Context, wg *sync.WaitGroup) {
+	defer wg.Done()
+
+	if onuTP.onuKVStore == nil {
+		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
+		onuTP.procResult = errors.New("onu/tp-data update aborted: onuKVStore not set")
+		return
+	}
+	var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpKvProcessingStep
+	go onuTP.storePersistentData(ctx, processingStep)
+	if !onuTP.waitForTimeoutOrCompletion(ctx, onuTP.chTpKvProcessingStep, processingStep) {
+		//timeout or error detected
+		logger.Debugw("ONU/TP-data not written - abort", log.Fields{"device-id": onuTP.deviceID})
+		onuTP.procResult = errors.New("onu/tp-data update aborted: during writing process")
+		return
+	}
+}
+
+func (onuTP *onuUniTechProf) restoreFromOnuTpPathKvStore(ctx context.Context) error {
+	if onuTP.onuKVStore == nil {
+		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
+		return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
+	}
+	if err := onuTP.restorePersistentData(ctx); err != nil {
+		logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
+		return err
+	}
+	return nil
+}
+
+func (onuTP *onuUniTechProf) deleteOnuTpPathKvStore(ctx context.Context) error {
+	if onuTP.onuKVStore == nil {
+		logger.Debugw("onuKVStore not set - abort", log.Fields{"device-id": onuTP.deviceID})
+		return fmt.Errorf(fmt.Sprintf("onuKVStore-not-set-abort-%s", onuTP.deviceID))
+	}
+	if err := onuTP.deletePersistentData(ctx); err != nil {
+		logger.Debugw("ONU/TP-data not read - abort", log.Fields{"device-id": onuTP.deviceID})
+		return err
+	}
+	return nil
+}
+
+// deleteTpResource removes Resources from the ONU's specified Uni
+func (onuTP *onuUniTechProf) deleteTpResource(ctx context.Context,
+	aUniID uint32, aPathString string, aResource resourceEntry, aEntryID uint32,
+	wg *sync.WaitGroup) {
+	defer wg.Done()
+	logger.Debugw("this would remove TP resources from ONU's UNI", log.Fields{
+		"device-id": onuTP.deviceID, "uniID": aUniID, "path": aPathString, "Resource": aResource})
+	/*
+		var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpConfigProcessingStep
+		go onuTp.deleteAniResource(ctx, processingStep)
+		if !onuTP.waitForTimeoutOrCompletion(ctx, chTpConfigProcessingStep, processingStep) {
+			//timeout or error detected
+			return
+		}
+	*/
+}
+
+/* internal methods *********************/
+
+func (onuTP *onuUniTechProf) storePersistentData(ctx context.Context, aProcessingStep uint8) {
+
+	onuTP.sOnuPersistentData.PersOnuID = onuTP.baseDeviceHandler.pOnuIndication.OnuId
+	onuTP.sOnuPersistentData.PersIntfID = onuTP.baseDeviceHandler.pOnuIndication.IntfId
+	onuTP.sOnuPersistentData.PersSnr = onuTP.baseDeviceHandler.pOnuOmciDevice.serialNumber
+	onuTP.sOnuPersistentData.PersAdminState = "up"
+	onuTP.sOnuPersistentData.PersOperState = "active"
+
+	onuTP.sOnuPersistentData.PersUniTpPath = onuTP.sOnuPersistentData.PersUniTpPath[:0]
+
+	for k, v := range onuTP.mapUniTpPath {
+		onuTP.sOnuPersistentData.PersUniTpPath =
+			append(onuTP.sOnuPersistentData.PersUniTpPath, uniPersData{PersUniID: k, PersTpPath: v})
+	}
+	logger.Debugw("Update ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID, "onuTP.sOnuPersistentData": onuTP.sOnuPersistentData})
+
+	Value, err := json.Marshal(onuTP.sOnuPersistentData)
+	if err != nil {
+		logger.Errorw("unable to marshal ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
+			"device-id": onuTP.deviceID, "err": err})
+		onuTP.chTpKvProcessingStep <- 0 //error indication
+		return
+	}
+	err = onuTP.onuKVStore.Put(ctx, onuTP.onuKVStorePath, Value)
+	if err != nil {
+		logger.Errorw("unable to write ONU/TP-data into KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
+		onuTP.chTpKvProcessingStep <- 0 //error indication
+		return
+	}
+	onuTP.chTpKvProcessingStep <- aProcessingStep //done
+}
+
+func (onuTP *onuUniTechProf) restorePersistentData(ctx context.Context) error {
+
+	onuTP.mapUniTpPath = make(map[uint32]string)
+	onuTP.sOnuPersistentData = onuPersistentData{0, 0, "", "", "", make([]uniPersData, 0)}
+
+	Value, err := onuTP.onuKVStore.Get(ctx, onuTP.onuKVStorePath)
+	if err == nil {
+		if Value != nil {
+			logger.Debugw("ONU/TP-data read",
+				log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
+			tpTmpBytes, _ := kvstore.ToByte(Value.Value)
+
+			if err = json.Unmarshal(tpTmpBytes, &onuTP.sOnuPersistentData); err != nil {
+				logger.Errorw("unable to unmarshal ONU/TP-data", log.Fields{"error": err, "device-id": onuTP.deviceID})
+				return fmt.Errorf(fmt.Sprintf("unable-to-unmarshal-ONU/TP-data-%s", onuTP.deviceID))
+			}
+			logger.Debugw("ONU/TP-data", log.Fields{"onuTP.sOnuPersistentData": onuTP.sOnuPersistentData,
+				"device-id": onuTP.deviceID})
+
+			for _, uniData := range onuTP.sOnuPersistentData.PersUniTpPath {
+				onuTP.mapUniTpPath[uniData.PersUniID] = uniData.PersTpPath
+			}
+			logger.Debugw("TpPath map", log.Fields{"onuTP.mapUniTpPath": onuTP.mapUniTpPath,
+				"device-id": onuTP.deviceID})
+		} else {
+			logger.Errorw("no ONU/TP-data found", log.Fields{"path": onuTP.onuKVStorePath, "device-id": onuTP.deviceID})
+			return fmt.Errorf(fmt.Sprintf("no-ONU/TP-data-found-%s", onuTP.deviceID))
+		}
+	} else {
+		logger.Errorw("unable to read from KVstore", log.Fields{"device-id": onuTP.deviceID})
+		return fmt.Errorf(fmt.Sprintf("unable-to-read-from-KVstore-%s", onuTP.deviceID))
+	}
+	return nil
+}
+
+func (onuTP *onuUniTechProf) deletePersistentData(ctx context.Context) error {
+
+	logger.Debugw("delete ONU/TP-data in KVStore", log.Fields{"device-id": onuTP.deviceID})
+	err := onuTP.onuKVStore.Delete(ctx, onuTP.onuKVStorePath)
+	if err != nil {
+		logger.Errorw("unable to delete in KVstore", log.Fields{"device-id": onuTP.deviceID, "err": err})
+		return fmt.Errorf(fmt.Sprintf("unable-delete-in-KVstore-%s", onuTP.deviceID))
+	}
+	return nil
+}
+
+func (onuTP *onuUniTechProf) readAniSideConfigFromTechProfile(
+	ctx context.Context, aUniID uint8, aPathString string, aProcessingStep uint8) {
+	var tpInst tp.TechProfile
+
+	subStringSlice := strings.Split(aPathString, "/")
+	if len(subStringSlice) <= 2 {
+		logger.Errorw("invalid path name format",
+			log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+		onuTP.chTpConfigProcessingStep <- 0 //error indication
+		return
+	}
+
+	if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
+		logger.Warnw("Some active profile entry at reading new TechProfile",
+			log.Fields{"path": aPathString, "device-id": onuTP.deviceID,
+				"UniId": aUniID, "wrongProfile": onuTP.mapUniTpIndication[aUniID].techProfileID})
+		//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, aUniID)
+	} else {
+		// this is normal processing
+		onuTP.mapUniTpIndication[aUniID] = &tTechProfileIndication{} //need to assign some (empty) struct memory first!
+	}
+
+	onuTP.mapUniTpIndication[aUniID].techProfileType = subStringSlice[0]
+	profID, err := strconv.ParseUint(subStringSlice[1], 10, 32)
+	if err != nil {
+		logger.Errorw("invalid ProfileId from path",
+			log.Fields{"ParseErr": err})
+		onuTP.chTpConfigProcessingStep <- 0 //error indication
+		return
+	}
+
+	onuTP.mapUniTpIndication[aUniID].techProfileID = uint16(profID)
+	logger.Debugw("tech-profile path indications",
+		log.Fields{"device-id": onuTP.deviceID, "UniId": aUniID,
+			"profType": onuTP.mapUniTpIndication[aUniID].techProfileType,
+			"profID":   onuTP.mapUniTpIndication[aUniID].techProfileID})
+
+	Value, err := onuTP.techProfileKVStore.Get(ctx, aPathString)
+	if err == nil {
+		if Value != nil {
+			logger.Debugw("tech-profile read",
+				log.Fields{"Key": Value.Key, "device-id": onuTP.deviceID})
+			tpTmpBytes, _ := kvstore.ToByte(Value.Value)
+
+			if err = json.Unmarshal(tpTmpBytes, &tpInst); err != nil {
+				logger.Errorw("TechProf - Failed to unmarshal tech-profile into tpInst",
+					log.Fields{"error": err, "device-id": onuTP.deviceID})
+				onuTP.chTpConfigProcessingStep <- 0 //error indication
+				return
+			}
+			logger.Debugw("TechProf - tpInst", log.Fields{"tpInst": tpInst})
+			// access examples
+			logger.Debugw("TechProf content", log.Fields{"Name": tpInst.Name,
+				"MaxGemPayloadSize":                tpInst.InstanceCtrl.MaxGemPayloadSize,
+				"DownstreamGemDiscardmaxThreshold": tpInst.DownstreamGemPortAttributeList[0].DiscardConfig.MaxThreshold})
+		} else {
+			logger.Errorw("No tech-profile found",
+				log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+			onuTP.chTpConfigProcessingStep <- 0 //error indication
+			return
+		}
+	} else {
+		logger.Errorw("kvstore-get failed for path",
+			log.Fields{"path": aPathString, "device-id": onuTP.deviceID})
+		onuTP.chTpConfigProcessingStep <- 0 //error indication
+		return
+	}
+
+	localMapGemPortParams := make(map[uint16]*gemPortParamStruct)
+	localMapGemPortParams[0] = &gemPortParamStruct{}
+	localMapPonAniConfig := make(map[uint16]*tcontGemList)
+	localMapPonAniConfig[0] = &tcontGemList{tcontParamStruct{}, localMapGemPortParams}
+	onuTP.mapPonAniConfig[aUniID] = (*tMapPonAniConfig)(&localMapPonAniConfig)
+
+	(*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID = uint16(tpInst.UsScheduler.AllocID)
+	if tpInst.UsScheduler.QSchedPolicy == "StrictPrio" {
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].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[aUniID]))[0].tcontParams.schedPolicy = 2 //for G.988 WRR
+	}
+	loNumGemPorts := tpInst.NumGemPorts
+	loGemPortRead := false
+	for pos, content := range tpInst.UpstreamGemPortAttributeList {
+		if uint32(pos) == loNumGemPorts {
+			logger.Debugw("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
+		} else {
+			//for all further GemPorts we need to extend the mapGemPortParams
+			(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)] = &gemPortParamStruct{}
+		}
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortID =
+			uint16(content.GemportID)
+		//direction can be correlated later with Downstream list,
+		//  for now just assume bidirectional (upstream never exists alone)
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].direction = 3 //as defined in G.988
+		// expected Prio-Queue values 0..7 with 7 for highest PrioQueue, QueueIndex=Prio = 0..7
+		if 7 < content.PriorityQueue {
+			logger.Errorw("PonAniConfig reject on GemPortList - PrioQueue value invalid",
+				log.Fields{"device-id": onuTP.deviceID, "index": pos, "PrioQueue": content.PriorityQueue})
+			//remove PonAniConfig  as done so far, delete map should be safe, even if not existing
+			delete(onuTP.mapPonAniConfig, aUniID)
+			onuTP.chTpConfigProcessingStep <- 0 //error indication
+			return
+		}
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].prioQueueIndex =
+			uint8(content.PriorityQueue)
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].pbitString =
+			strings.TrimPrefix(content.PbitMap, binaryStringPrefix)
+		if content.AesEncryption == "True" {
+			(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortEncState = 1
+		} else {
+			(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].gemPortEncState = 0
+		}
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].discardPolicy =
+			content.DiscardPolicy
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueSchedPolicy =
+			content.SchedulingPolicy
+		//'GemWeight' looks strange in default profile, for now we just copy the weight to first queue
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueWeight =
+			uint8(content.Weight)
+	}
+	if !loGemPortRead {
+		logger.Errorw("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, aUniID)
+		onuTP.chTpConfigProcessingStep <- 0 //error indication
+		return
+	}
+
+	logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
+		"device-id": onuTP.deviceID,
+		"AllocId":   (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID})
+	for gemIndex, gemEntry := range (*(onuTP.mapPonAniConfig[0]))[0].mapGemPortParams {
+		logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
+			"GemIndex":        gemIndex,
+			"GemPort":         gemEntry.gemPortID,
+			"QueueScheduling": gemEntry.queueSchedPolicy})
+	}
+
+	onuTP.chTpConfigProcessingStep <- aProcessingStep //done
+}
+
+func (onuTP *onuUniTechProf) setAniSideConfigFromTechProfile(
+	ctx context.Context, aUniID uint8, apCurrentUniPort *onuUniPort, aProcessingStep uint8) {
+
+	if onuTP.pAniConfigFsm == nil {
+		onuTP.createAniConfigFsm(aUniID, apCurrentUniPort, OmciAniConfigDone, aProcessingStep)
+	} else { //AniConfigFsm already init
+		onuTP.runAniConfigFsm(aProcessingStep)
+	}
+}
+
+func (onuTP *onuUniTechProf) waitForTimeoutOrCompletion(
+	ctx context.Context, aChTpProcessingStep <-chan uint8, aProcessingStep uint8) bool {
+	select {
+	case <-ctx.Done():
+		logger.Warnw("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("Invalid processing step received: abort and force release of TpProcMutex!",
+			log.Fields{"device-id": onuTP.deviceID,
+				"wantedStep": aProcessingStep, "haveStep": rxStep})
+		return false
+	}
+}
+
+// createUniLockFsm initializes and runs the AniConfig FSM to transfer the OMCI related commands for ANI side configuration
+func (onuTP *onuUniTechProf) createAniConfigFsm(aUniID uint8,
+	apCurrentUniPort *onuUniPort, devEvent OnuDeviceEvent, aProcessingStep uint8) {
+	logger.Debugw("createAniConfigFsm", log.Fields{"device-id": onuTP.deviceID})
+	chAniConfigFsm := make(chan Message, 2048)
+	pDevEntry := onuTP.baseDeviceHandler.getOnuDeviceEntry(true)
+	if pDevEntry == nil {
+		logger.Errorw("No valid OnuDevice - aborting", log.Fields{"device-id": onuTP.deviceID})
+		return
+	}
+	pAniCfgFsm := newUniPonAniConfigFsm(pDevEntry.PDevOmciCC, apCurrentUniPort, onuTP,
+		pDevEntry.pOnuDB, onuTP.mapUniTpIndication[aUniID].techProfileID, devEvent,
+		"AniConfigFsm", onuTP.deviceID, chAniConfigFsm)
+	if pAniCfgFsm != nil {
+		onuTP.pAniConfigFsm = pAniCfgFsm
+		onuTP.runAniConfigFsm(aProcessingStep)
+	} else {
+		logger.Errorw("AniConfigFSM could not be created - abort!!", log.Fields{"device-id": onuTP.deviceID})
+	}
+}
+
+// runAniConfigFsm starts the AniConfig FSM to transfer the OMCI related commands for  ANI side configuration
+func (onuTP *onuUniTechProf) runAniConfigFsm(aProcessingStep uint8) {
+	/*  Uni related ANI config procedure -
+	 ***** should run via 'aniConfigDone' state and generate the argument requested event *****
+	 */
+	pACStatemachine := onuTP.pAniConfigFsm.pAdaptFsm.pFsm
+	if pACStatemachine != nil {
+		if pACStatemachine.Is(aniStDisabled) {
+			//FSM init requirement to get informed abou FSM completion! (otherwise timeout of the TechProf config)
+			onuTP.pAniConfigFsm.setFsmCompleteChannel(onuTP.chTpConfigProcessingStep, aProcessingStep)
+			if err := pACStatemachine.Event(aniEvStart); err != nil {
+				logger.Warnw("AniConfigFSM: can't start", log.Fields{"err": err})
+				// maybe try a FSM reset and then again ... - TODO!!!
+			} else {
+				/***** AniConfigFSM started */
+				logger.Debugw("AniConfigFSM started", log.Fields{
+					"state": pACStatemachine.Current(), "device-id": onuTP.deviceID})
+			}
+		} else {
+			logger.Warnw("wrong state of AniConfigFSM - want: disabled", log.Fields{
+				"have": pACStatemachine.Current(), "device-id": onuTP.deviceID})
+			// maybe try a FSM reset and then again ... - TODO!!!
+		}
+	} else {
+		logger.Errorw("AniConfigFSM StateMachine invalid - cannot be executed!!", log.Fields{"device-id": onuTP.deviceID})
+		// maybe try a FSM reset and then again ... - TODO!!!
+	}
+}
+
+// setConfigDone sets the requested techProfile config state (if possible)
+func (onuTP *onuUniTechProf) setConfigDone(aUniID uint8, aState bool) {
+	if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
+		onuTP.mutexTPState.Lock()
+		onuTP.mapUniTpIndication[aUniID].techProfileConfigDone = aState
+		onuTP.mutexTPState.Unlock()
+	} //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(aUniID uint8, aTpID uint16) bool {
+	if _, existTP := onuTP.mapUniTpIndication[aUniID]; existTP {
+		if onuTP.mapUniTpIndication[aUniID].techProfileID == aTpID {
+			onuTP.mutexTPState.Lock()
+			defer onuTP.mutexTPState.Unlock()
+			return onuTP.mapUniTpIndication[aUniID].techProfileConfigDone
+		}
+	}
+	return false
+}
diff --git a/internal/pkg/onuadaptercore/openonu.go b/internal/pkg/onuadaptercore/openonu.go
new file mode 100644
index 0000000..a8a84d4
--- /dev/null
+++ b/internal/pkg/onuadaptercore/openonu.go
@@ -0,0 +1,428 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"sync"
+	"time"
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+	"github.com/opencord/voltha-lib-go/v3/pkg/db/kvstore"
+	"github.com/opencord/voltha-lib-go/v3/pkg/kafka"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+	ic "github.com/opencord/voltha-protos/v3/go/inter_container"
+	"github.com/opencord/voltha-protos/v3/go/openflow_13"
+	"github.com/opencord/voltha-protos/v3/go/voltha"
+
+	"test.internal/openadapter/internal/pkg/config"
+)
+
+//OpenONUAC structure holds the ONU core information
+type OpenONUAC struct {
+	deviceHandlers              map[string]*deviceHandler
+	coreProxy                   adapterif.CoreProxy
+	adapterProxy                adapterif.AdapterProxy
+	eventProxy                  adapterif.EventProxy
+	kafkaICProxy                kafka.InterContainerProxy
+	kvClient                    kvstore.Client
+	config                      *config.AdapterFlags
+	numOnus                     int
+	KVStoreHost                 string
+	KVStorePort                 int
+	KVStoreType                 string
+	KVStoreTimeout              time.Duration
+	exitChannel                 chan int
+	HeartbeatCheckInterval      time.Duration
+	HeartbeatFailReportInterval time.Duration
+	AcceptIncrementalEvto       bool
+	lockDeviceHandlersMap sync.RWMutex
+	pSupportedFsms        *OmciDeviceFsms
+}
+
+//NewOpenONUAC returns a new instance of OpenONU_AC
+func NewOpenONUAC(ctx context.Context, kafkaICProxy kafka.InterContainerProxy,
+	coreProxy adapterif.CoreProxy, adapterProxy adapterif.AdapterProxy,
+	eventProxy adapterif.EventProxy, kvClient kvstore.Client, cfg *config.AdapterFlags) *OpenONUAC {
+	var openOnuAc OpenONUAC
+	openOnuAc.exitChannel = make(chan int, 1)
+	openOnuAc.deviceHandlers = make(map[string]*deviceHandler)
+	openOnuAc.kafkaICProxy = kafkaICProxy
+	openOnuAc.config = cfg
+	openOnuAc.numOnus = cfg.OnuNumber
+	openOnuAc.coreProxy = coreProxy
+	openOnuAc.adapterProxy = adapterProxy
+	openOnuAc.eventProxy = eventProxy
+	openOnuAc.kvClient = kvClient
+	openOnuAc.KVStoreHost = cfg.KVStoreHost
+	openOnuAc.KVStorePort = cfg.KVStorePort
+	openOnuAc.KVStoreType = cfg.KVStoreType
+	openOnuAc.KVStoreTimeout = cfg.KVStoreTimeout
+	openOnuAc.HeartbeatCheckInterval = cfg.HeartbeatCheckInterval
+	openOnuAc.HeartbeatFailReportInterval = cfg.HeartbeatFailReportInterval
+	openOnuAc.AcceptIncrementalEvto = cfg.AccIncrEvto
+	openOnuAc.lockDeviceHandlersMap = sync.RWMutex{}
+
+	openOnuAc.pSupportedFsms = &OmciDeviceFsms{
+		"mib-synchronizer": {
+			//mibSyncFsm,        // Implements the MIB synchronization state machine
+			mibDbVolatileDictImpl, // Implements volatile ME MIB database
+			//true,                  // Advertise events on OpenOMCI event bus
+			cMibAuditDelayImpl, // Time to wait between MIB audits.  0 to disable audits.
+			// map[string]func() error{
+			// 	"mib-upload":    onuDeviceEntry.MibUploadTask,
+			// 	"mib-template":  onuDeviceEntry.MibTemplateTask,
+			// 	"get-mds":       onuDeviceEntry.GetMdsTask,
+			// 	"mib-audit":     onuDeviceEntry.GetMdsTask,
+			// 	"mib-resync":    onuDeviceEntry.MibResyncTask,
+			// 	"mib-reconcile": onuDeviceEntry.MibReconcileTask,
+			// },
+		},
+	}
+
+	return &openOnuAc
+}
+
+//Start starts (logs) the adapter
+func (oo *OpenONUAC) Start(ctx context.Context) error {
+	logger.Info("starting-openonu-adapter")
+	logger.Info("openonu-adapter-started")
+	return nil
+}
+
+/*
+//stop terminates the session
+func (oo *OpenONUAC) stop(ctx context.Context) error {
+	logger.Info("stopping-device-manager")
+	oo.exitChannel <- 1
+	logger.Info("device-manager-stopped")
+	return nil
+}
+*/
+
+func (oo *OpenONUAC) addDeviceHandlerToMap(ctx context.Context, agent *deviceHandler) {
+	oo.lockDeviceHandlersMap.Lock()
+	defer oo.lockDeviceHandlersMap.Unlock()
+	if _, exist := oo.deviceHandlers[agent.deviceID]; !exist {
+		oo.deviceHandlers[agent.deviceID] = agent
+		oo.deviceHandlers[agent.deviceID].start(ctx)
+	}
+}
+
+/*
+func (oo *OpenONUAC) deleteDeviceHandlerToMap(agent *deviceHandler) {
+	oo.lockDeviceHandlersMap.Lock()
+	defer oo.lockDeviceHandlersMap.Unlock()
+	delete(oo.deviceHandlers, agent.deviceID)
+}
+*/
+
+func (oo *OpenONUAC) getDeviceHandler(deviceID string) *deviceHandler {
+	oo.lockDeviceHandlersMap.Lock()
+	defer oo.lockDeviceHandlersMap.Unlock()
+	if agent, ok := oo.deviceHandlers[deviceID]; ok {
+		return agent
+	}
+	return nil
+}
+
+// Adapter interface required methods ############## begin #########
+// #################################################################
+
+// for original content compare: (needs according deviceHandler methods)
+// /voltha-openolt-adapter/adaptercore/openolt.go
+
+// Adopt_device creates a new device handler if not present already and then adopts the device
+func (oo *OpenONUAC) Adopt_device(device *voltha.Device) error {
+	if device == nil {
+		logger.Warn("voltha-device-is-nil")
+		return errors.New("nil-device")
+	}
+	ctx := context.Background()
+	logger.Infow("adopt-device", log.Fields{"device-id": device.Id})
+	var handler *deviceHandler
+	if handler = oo.getDeviceHandler(device.Id); handler == nil {
+		handler := newDeviceHandler(oo.coreProxy, oo.adapterProxy, oo.eventProxy, device, oo)
+		oo.addDeviceHandlerToMap(ctx, handler)
+		go handler.adoptOrReconcileDevice(ctx, device)
+		// Launch the creation of the device topic
+		// go oo.createDeviceTopic(device)
+	}
+	return nil
+}
+
+//Get_ofp_device_info returns OFP information for the given device
+func (oo *OpenONUAC) Get_ofp_device_info(device *voltha.Device) (*ic.SwitchCapability, error) {
+	logger.Errorw("device-handler-not-set", log.Fields{"device-id": device.Id})
+	return nil, errors.New("device-handler-not-set")
+}
+
+//Get_ofp_port_info returns OFP port information for the given device
+//200630: method removed as per [VOL-3202]: OF port info is now to be delivered within UniPort create
+// cmp changes in onu_uni_port.go::CreateVolthaPort()
+
+//Process_inter_adapter_message sends messages to a target device (between adapters)
+func (oo *OpenONUAC) Process_inter_adapter_message(msg *ic.InterAdapterMessage) error {
+	logger.Debugw("Process_inter_adapter_message", log.Fields{"msgId": msg.Header.Id,
+		"msgProxyDeviceId": msg.Header.ProxyDeviceId, "msgToDeviceId": msg.Header.ToDeviceId})
+
+	targetDevice := msg.Header.ToDeviceId
+	if handler := oo.getDeviceHandler(targetDevice); handler != nil {
+		/* 200724: modification towards synchronous implementation - possible errors within processing shall be
+		 * 	 in the accordingly delayed response, some timing effect might result in Techprofile processing for multiple UNI's
+		 */
+		return handler.processInterAdapterMessage(msg)
+		/* so far the processing has been in background with according commented error treatment restrictions:
+		go handler.ProcessInterAdapterMessage(msg)
+		// error treatment might be more sophisticated
+		// by now let's just accept the message on 'communication layer'
+		// message content problems have to be evaluated then in the handler
+		//   and are by now not reported to the calling party (to force what reaction there?)
+		return nil
+		*/
+	}
+	logger.Warnw("no handler found for received Inter-Proxy-message", log.Fields{
+		"msgToDeviceId": targetDevice})
+	return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", targetDevice))
+}
+
+//Adapter_descriptor not implemented
+func (oo *OpenONUAC) Adapter_descriptor() error {
+	return errors.New("unImplemented")
+}
+
+//Device_types unimplemented
+func (oo *OpenONUAC) Device_types() (*voltha.DeviceTypes, error) {
+	return nil, errors.New("unImplemented")
+}
+
+//Health  returns unimplemented
+func (oo *OpenONUAC) Health() (*voltha.HealthStatus, error) {
+	return nil, errors.New("unImplemented")
+}
+
+//Reconcile_device is called once when the adapter needs to re-create device - usually on core restart
+func (oo *OpenONUAC) Reconcile_device(device *voltha.Device) error {
+	if device == nil {
+		logger.Warn("voltha-device-is-nil")
+		return errors.New("nil-device")
+	}
+	ctx := context.Background()
+	logger.Infow("Reconcile_device", log.Fields{"device-id": device.Id})
+	var handler *deviceHandler
+	if handler = oo.getDeviceHandler(device.Id); handler == nil {
+		handler := newDeviceHandler(oo.coreProxy, oo.adapterProxy, oo.eventProxy, device, oo)
+		oo.addDeviceHandlerToMap(ctx, handler)
+		handler.device = device
+		handler.reconciling = true
+		go handler.adoptOrReconcileDevice(ctx, handler.device)
+		// reconcilement will be continued after onu-device entry is added
+	} else {
+		return fmt.Errorf(fmt.Sprintf("device-already-reconciled-or-active-%s", device.Id))
+	}
+	return nil
+}
+
+//Abandon_device unimplemented
+func (oo *OpenONUAC) Abandon_device(device *voltha.Device) error {
+	return errors.New("unImplemented")
+}
+
+var disableOnuFunc bool = true
+
+//Disable_device disables the given device
+func (oo *OpenONUAC) Disable_device(device *voltha.Device) error {
+	logger.Debugw("Disable_device", log.Fields{"device-id": device.Id})
+	if handler := oo.getDeviceHandler(device.Id); handler != nil {
+		if err := handler.sendMsgToOlt(context.TODO(), "disable", nil); err != nil {
+			logger.Debugw("Disable_device", log.Fields{"error": err})
+		}
+		if disableOnuFunc {
+			return nil
+		}
+		//go handler.disableDevice(device)
+		//return nil
+	}
+	logger.Warnw("no handler found for device-disable", log.Fields{"device-id": device.Id})
+	return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+}
+
+//Reenable_device enables the onu device after disable
+func (oo *OpenONUAC) Reenable_device(device *voltha.Device) error {
+	logger.Debugw("Reenable_device", log.Fields{"device-id": device.Id})
+	if handler := oo.getDeviceHandler(device.Id); handler != nil {
+		if err := handler.sendMsgToOlt(context.TODO(), "reenable", nil); err != nil {
+			logger.Debugw("Disable_device", log.Fields{"error": err})
+		}
+		if disableOnuFunc {
+			return nil
+		}
+		go handler.reEnableDevice(device)
+		return nil
+	}
+	logger.Warnw("no handler found for device-reenable", log.Fields{"device-id": device.Id})
+	return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+}
+
+//Reboot_device reboots the given device
+func (oo *OpenONUAC) Reboot_device(device *voltha.Device) error {
+	logger.Debugw("Reboot-device", log.Fields{"device-id": device.Id})
+	if handler := oo.getDeviceHandler(device.Id); handler != nil {
+		go handler.rebootDevice(device)
+		return nil
+	}
+	logger.Warnw("no handler found for device-reboot", log.Fields{"device-id": device.Id})
+	return fmt.Errorf(fmt.Sprintf("handler-not-found-#{device.Id}"))
+}
+
+//Self_test_device unimplemented
+func (oo *OpenONUAC) Self_test_device(device *voltha.Device) error {
+	return errors.New("unImplemented")
+}
+
+// Delete_device deletes the given device
+func (oo *OpenONUAC) Delete_device(device *voltha.Device) error {
+	logger.Debugw("Delete_device", log.Fields{"device-id": device.Id})
+	if handler := oo.getDeviceHandler(device.Id); handler != nil {
+		if err := handler.deleteDevice(device); err != nil {
+			return err
+		}
+	} else {
+		logger.Warnw("no handler found for device-reconcilement", log.Fields{"device-id": device.Id})
+		return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+	}
+	return nil
+}
+
+//Get_device_details unimplemented
+func (oo *OpenONUAC) Get_device_details(device *voltha.Device) error {
+	return errors.New("unImplemented")
+}
+
+//Update_flows_bulk returns
+func (oo *OpenONUAC) Update_flows_bulk(device *voltha.Device, flows *voltha.Flows, groups *voltha.FlowGroups, flowMetadata *voltha.FlowMetadata) error {
+	return errors.New("unImplemented")
+}
+
+var disableUpdateFlows = true
+
+//Update_flows_incrementally updates (add/remove) the flows on a given device
+func (oo *OpenONUAC) Update_flows_incrementally(device *voltha.Device,
+	flows *openflow_13.FlowChanges, groups *openflow_13.FlowGroupChanges, flowMetadata *voltha.FlowMetadata) error {
+	if disableUpdateFlows {
+		return nil
+	}
+	if device.ConnectStatus != voltha.ConnectStatus_REACHABLE ||
+		device.AdminState != voltha.AdminState_ENABLED {
+		logger.Warnw("device disabled or offline - skipping flow-update", log.Fields{"deviceId": device.Id})
+		return errors.New("non-matching device state")
+	}
+
+	if groups.ToAdd != nil && groups.ToAdd.Items != nil {
+		logger.Debugw("Update-flow-incr: group add not supported (ignored)", log.Fields{"deviceId": device.Id})
+	}
+	if groups.ToRemove != nil && groups.ToRemove.Items != nil {
+		logger.Debugw("Update-flow-incr: group remove not supported (ignored)", log.Fields{"deviceId": device.Id})
+	}
+	if groups.ToUpdate != nil && groups.ToUpdate.Items != nil {
+		logger.Debugw("Update-flow-incr: group update not supported (ignored)", log.Fields{"deviceId": device.Id})
+	}
+
+	if handler := oo.getDeviceHandler(device.Id); handler != nil {
+		err := handler.FlowUpdateIncremental(flows, groups, flowMetadata)
+		return err
+	}
+	logger.Warnw("no handler found for incremental flow update", log.Fields{"deviceId": device.Id})
+	return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+}
+
+//Update_pm_config returns PmConfigs nil or error
+func (oo *OpenONUAC) Update_pm_config(device *voltha.Device, pmConfigs *voltha.PmConfigs) error {
+	return errors.New("unImplemented")
+}
+
+//Receive_packet_out sends packet out to the device
+func (oo *OpenONUAC) Receive_packet_out(deviceID string, egressPortNo int, packet *openflow_13.OfpPacketOut) error {
+	return errors.New("unImplemented")
+}
+
+//Suppress_event unimplemented
+func (oo *OpenONUAC) Suppress_event(filter *voltha.EventFilter) error {
+	return errors.New("unImplemented")
+}
+
+//Unsuppress_event  unimplemented
+func (oo *OpenONUAC) Unsuppress_event(filter *voltha.EventFilter) error {
+	return errors.New("unImplemented")
+}
+
+//Download_image unimplemented
+func (oo *OpenONUAC) Download_image(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+	return nil, errors.New("unImplemented")
+}
+
+//Get_image_download_status unimplemented
+func (oo *OpenONUAC) Get_image_download_status(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+	return nil, errors.New("unImplemented")
+}
+
+//Cancel_image_download unimplemented
+func (oo *OpenONUAC) Cancel_image_download(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+	return nil, errors.New("unImplemented")
+}
+
+//Activate_image_update unimplemented
+func (oo *OpenONUAC) Activate_image_update(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+	return nil, errors.New("unImplemented")
+}
+
+//Revert_image_update unimplemented
+func (oo *OpenONUAC) Revert_image_update(device *voltha.Device, request *voltha.ImageDownload) (*voltha.ImageDownload, error) {
+	return nil, errors.New("unImplemented")
+}
+
+// Enable_port to Enable PON/NNI interface - seems not to be used/required according to python code
+func (oo *OpenONUAC) Enable_port(deviceID string, port *voltha.Port) error {
+	return errors.New("unImplemented")
+}
+
+// Disable_port to Disable pon/nni interface  - seems not to be used/required according to python code
+func (oo *OpenONUAC) Disable_port(deviceID string, port *voltha.Port) error {
+	return errors.New("unImplemented")
+}
+
+//Child_device_lost - unimplemented
+//needed for if update >= 3.1.x
+func (oo *OpenONUAC) Child_device_lost(deviceID string, pPortNo uint32, onuID uint32) error {
+	return errors.New("unImplemented")
+}
+
+// Start_omci_test unimplemented
+func (oo *OpenONUAC) Start_omci_test(device *voltha.Device, request *voltha.OmciTestRequest) (*voltha.TestResponse, error) {
+	return nil, errors.New("unImplemented")
+}
+
+// Get_ext_value - unimplemented
+func (oo *OpenONUAC) Get_ext_value(deviceID string, device *voltha.Device, valueparam voltha.ValueType_Type) (*voltha.ReturnValues, error) {
+	return nil, errors.New("unImplemented")
+}
+
+// Adapter interface required methods ################ end #########
+// #################################################################
diff --git a/internal/pkg/onuadaptercore/openonuimpl.go b/internal/pkg/onuadaptercore/openonuimpl.go
new file mode 100644
index 0000000..15109cb
--- /dev/null
+++ b/internal/pkg/onuadaptercore/openonuimpl.go
@@ -0,0 +1,101 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"errors"
+)
+
+/*
+OpenOmciAgentDefaults = {
+    'mib-synchronizer': {
+        'state-machine': MibSynchronizer,  # Implements the MIB synchronization state machine
+        'database': MibDbVolatileDict,     # Implements volatile ME MIB database
+        # 'database': MibDbExternal,         # Implements persistent ME MIB database
+        'advertise-events': True,          # Advertise events on OpenOMCI event bus
+        'audit-delay': 60,                 # Time to wait between MIB audits.  0 to disable audits.
+        'tasks': {
+            'mib-upload': MibUploadTask,
+            'mib-template': MibTemplateTask,
+            'get-mds': GetMdsTask,
+            'mib-audit': GetMdsTask,
+            'mib-resync': MibResyncTask,
+            'mib-reconcile': MibReconcileTask
+        }
+    },
+    'omci-capabilities': {
+        'state-machine': OnuOmciCapabilities,   # Implements OMCI capabilities state machine
+        'advertise-events': False,              # Advertise events on OpenOMCI event bus
+        'tasks': {
+            'get-capabilities': OnuCapabilitiesTask # Get supported ME and Commands
+        }
+    },
+    'performance-intervals': {
+        'state-machine': PerformanceIntervals,  # Implements PM Intervals State machine
+        'advertise-events': False,              # Advertise events on OpenOMCI event bus
+        'tasks': {
+            'sync-time': SyncTimeTask,
+            'collect-data': IntervalDataTask,
+            'create-pm': OmciCreatePMRequest,
+            'delete-pm': OmciDeletePMRequest,
+        },
+    },
+    'alarm-synchronizer': {
+        'state-machine': AlarmSynchronizer,    # Implements the Alarm sync state machine
+        'database': AlarmDbExternal,           # For any State storage needs
+        'advertise-events': True,              # Advertise events on OpenOMCI event bus
+        'tasks': {
+            'alarm-resync': AlarmResyncTask
+        }
+     },
+    'image_downloader': {
+        'state-machine': ImageDownloadeSTM,
+        'advertise-event': True,
+        'tasks': {
+            'download-file': FileDownloadTask
+        }
+    },
+    'image_upgrader': {
+        'state-machine': OmciSoftwareImageDownloadSTM,
+        'advertise-event': True,
+        'tasks': {
+            'omci_upgrade_task': OmciSwImageUpgradeTask
+        }
+    }
+    # 'image_activator': {
+    #     'state-machine': OmciSoftwareImageActivateSTM,
+    #     'advertise-event': True,
+    # }
+}
+*/
+
+// do not use MibAudit
+const cMibAuditDelayImpl = 0
+
+//suppose global methods per adapter ...
+func mibDbVolatileDictImpl() error {
+	logger.Debug("MibVolatileDict-called")
+	return errors.New("not_implemented")
+}
+
+/*
+func alarmDbDictImpl() error {
+	logger.Debug("AlarmDb-called")
+	return errors.New("not_implemented")
+}
+*/
diff --git a/internal/pkg/onuadaptercore/platform.go b/internal/pkg/onuadaptercore/platform.go
new file mode 100644
index 0000000..fa1ff6e
--- /dev/null
+++ b/internal/pkg/onuadaptercore/platform.go
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2018-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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+//Attention: this file is more or less a coopy of file olt_platform.go from the voltha-openolt-adapter
+// which includes system wide definitions and thus normally should be stored more centrally (within some voltha libs)!!
+
+/*=====================================================================
+
+@TODO: Looks like this Flow id concept below is not used anywhere
+       Propose to remove the below documentation of Flow Id on confirmation
+       of the same
+
+Flow id
+
+    Identifies a flow within a single OLT
+    Flow Id is unique per OLT
+    Multiple GEM ports can map to same flow id
+
+     13    11              4      0
+    +--------+--------------+------+
+    | pon id |    onu id    | Flow |
+    |        |              | idx  |
+    +--------+--------------+------+
+
+    14 bits = 16384 flows (per OLT).
+
+    pon id = 4 bits = 16 PON ports
+    onu id = 7 bits = 128 ONUss per PON port
+    Flow index = 3 bits = 4 bi-directional flows per ONU
+                        = 8 uni-directional flows per ONU
+
+
+Logical (OF) UNI port number
+
+    OpenFlow port number corresponding to PON UNI
+
+     20        12              4      0
+    +--+--------+--------------+------+
+    |0 | pon id |    onu id    |uni id|
+    +--+--------+--------------+------+
+
+    pon id = 8 bits = 256 PON ports
+    onu id = 8 bits = 256 ONUs per PON port
+
+Logical (OF) NNI port number
+
+    OpenFlow port number corresponding to PON NNI
+
+     20                             0
+    +--+----------------------------+
+    |1 |                    intf_id |
+    +--+----------------------------+
+
+    No overlap with UNI port number space
+
+
+PON OLT (OF) port number
+
+    OpenFlow port number corresponding to PON OLT ports
+
+     31     28                                 0
+    +--------+------------------------~~~------+
+    |  0x2   |          pon intf id            |
+    +--------+------------------------~~~------+
+*/
+
+const (
+	bitsForUniID = 4
+	bitsForONUID = 8
+	bitsForPONID = 8
+	/*
+		// Number of bits to differentiate between UNI and NNI Logical Port
+		bitsForUNINNIDiff = 1
+	*/
+	maxOnusPerPon = (1 << bitsForONUID)
+	maxPonsPerOlt = (1 << bitsForPONID)
+	maxUnisPerOnu = (1 << bitsForUniID)
+	/*
+		//Bit position where the differentiation bit is located
+		nniUniDiffPos = (bitsForUniID + bitsForONUID + bitsForPONID)
+		//Bit position where the marker for PON port type of OF port is present
+		ponIntfMarkerPos = 28
+		//Value of marker used to distinguish PON port type of OF port
+		ponIntfMarkerValue = 0x2
+		// Number of bits for NNI ID
+		bitsforNNIID = 20
+		// minNniIntPortNum is used to store start range of nni port number (1 << 20) 1048576
+		minNniIntPortNum = (1 << bitsforNNIID)
+		// maxNniPortNum is used to store the maximum range of nni port number ((1 << 21)-1) 2097151
+		maxNniPortNum = ((1 << (bitsforNNIID + 1)) - 1)
+	*/
+)
+
+//Mask to indicate which possibly active ONU UNI state  is really reported to the core
+// compare python code - at the moment restrict active state to the first ONU UNI port
+// check is limited to max 16 uni ports - cmp above UNI limit!!!
+var activeUniPortStateUpdateMask = 0x0001
+
+/*
+//MinUpstreamPortID value
+var minUpstreamPortID = 0xfffd
+
+//MaxUpstreamPortID value
+var maxUpstreamPortID = 0xfffffffd
+
+var controllerPorts = []uint32{0xfffd, 0x7ffffffd, 0xfffffffd}
+*/
+
+//mkUniPortNum returns new UNIportNum based on intfID, inuID and uniID
+func mkUniPortNum(intfID, onuID, uniID uint32) uint32 {
+	var limit = int(intfID)
+	if limit > maxPonsPerOlt {
+		logger.Warn("Warning: exceeded the MAX pons per OLT")
+	}
+	limit = int(onuID)
+	if limit > maxOnusPerPon {
+		logger.Warn("Warning: exceeded the MAX ONUS per PON")
+	}
+	limit = int(uniID)
+	if limit > maxUnisPerOnu {
+		logger.Warn("Warning: exceeded the MAX UNIS per ONU")
+	}
+	return (intfID << (bitsForUniID + bitsForONUID)) | (onuID << bitsForUniID) | uniID
+}
+
+/*
+//onuIDFromPortNum returns ONUID derived from portNumber
+func onuIDFromPortNum(portNum uint32) uint32 {
+	return (portNum >> bitsForUniID) & (maxOnusPerPon - 1)
+}
+
+//intfIDFromUniPortNum returns IntfID derived from portNum
+func intfIDFromUniPortNum(portNum uint32) uint32 {
+	return (portNum >> (bitsForUniID + bitsForONUID)) & (maxPonsPerOlt - 1)
+}
+
+//uniIDFromPortNum return UniID derived from portNum
+func uniIDFromPortNum(portNum uint32) uint32 {
+	return (portNum) & (maxUnisPerOnu - 1)
+}
+
+//intfIDToPortNo returns portId derived from intftype, intfId and portType
+func intfIDToPortNo(intfID uint32, intfType voltha.Port_PortType) uint32 {
+	if (intfType) == voltha.Port_ETHERNET_NNI {
+		return (1 << nniUniDiffPos) | intfID
+	}
+	if (intfType) == voltha.Port_PON_OLT {
+		return (ponIntfMarkerValue << ponIntfMarkerPos) | intfID
+	}
+	return 0
+}
+
+//portNoToIntfID returns portnumber derived from interfaceID
+func portNoToIntfID(portno uint32, intfType voltha.Port_PortType) uint32 {
+	if (intfType) == voltha.Port_ETHERNET_NNI {
+		return (1 << nniUniDiffPos) ^ portno
+	}
+	if (intfType) == voltha.Port_PON_OLT {
+		return (ponIntfMarkerValue << ponIntfMarkerPos) ^ portno
+	}
+	return 0
+}
+
+//intfIDFromNniPortNum returns Intf ID derived from portNum
+func intfIDFromNniPortNum(portNum uint32) (uint32, error) {
+	if portNum < minNniIntPortNum || portNum > maxNniPortNum {
+		logger.Errorw("NNIPortNumber is not in valid range", log.Fields{"portNum": portNum})
+		return uint32(0), errors.New("invalid-port-range") //olterrors.ErrInvalidPortRange
+	}
+	return (portNum & 0xFFFF), nil
+}
+
+//intfIDToPortTypeName returns port type derived from the intfId
+func intfIDToPortTypeName(intfID uint32) voltha.Port_PortType {
+	if ((ponIntfMarkerValue << ponIntfMarkerPos) ^ intfID) < maxPonsPerOlt {
+		return voltha.Port_PON_OLT
+	}
+	if (intfID & (1 << nniUniDiffPos)) == (1 << nniUniDiffPos) {
+		return voltha.Port_ETHERNET_NNI
+	}
+	return voltha.Port_ETHERNET_UNI
+}
+
+//extractAccessFromFlow returns AccessDevice information
+func extractAccessFromFlow(inPort, outPort uint32) (uint32, uint32, uint32, uint32) {
+	if isUpstream(outPort) {
+		return inPort, intfIDFromUniPortNum(inPort), onuIDFromPortNum(inPort), uniIDFromPortNum(inPort)
+	}
+	return outPort, intfIDFromUniPortNum(outPort), onuIDFromPortNum(outPort), uniIDFromPortNum(outPort)
+}
+
+//isUpstream returns true for Upstream and false for downstream
+func isUpstream(outPort uint32) bool {
+	for _, port := range controllerPorts {
+		if port == outPort {
+			return true
+		}
+	}
+	return (outPort & (1 << nniUniDiffPos)) == (1 << nniUniDiffPos)
+}
+
+//isControllerBoundFlow returns true/false
+func isControllerBoundFlow(outPort uint32) bool {
+	for _, port := range controllerPorts {
+		if port == outPort {
+			return true
+		}
+	}
+	return false
+}
+
+//onuIDFromUniPortNum returns onuId from give portNum information.
+func onuIDFromUniPortNum(portNum uint32) uint32 {
+	return (portNum >> bitsForUniID) & (maxOnusPerPon - 1)
+}
+
+//flowExtractInfo fetches uniport from the flow, based on which it gets and returns ponInf, onuID, uniID, inPort and ethType
+func flowExtractInfo(flow *ofp.OfpFlowStats, flowDirection string) (uint32, uint32, uint32, uint32, uint32, uint32, error) {
+	var uniPortNo uint32
+	var ponIntf uint32
+	var onuID uint32
+	var uniID uint32
+	var inPort uint32
+	var ethType uint32
+
+	if flowDirection == "upstream" {
+		if uniPortNo = flows.GetChildPortFromTunnelId(flow); uniPortNo == 0 {
+			for _, field := range flows.GetOfbFields(flow) {
+				if field.GetType() == flows.IN_PORT {
+					uniPortNo = field.GetPort()
+					break
+				}
+			}
+		}
+	} else if flowDirection == "downstream" {
+		if uniPortNo = flows.GetChildPortFromTunnelId(flow); uniPortNo == 0 {
+			for _, field := range flows.GetOfbFields(flow) {
+				if field.GetType() == flows.METADATA {
+					for _, action := range flows.GetActions(flow) {
+						if action.Type == flows.OUTPUT {
+							if out := action.GetOutput(); out != nil {
+								uniPortNo = out.GetPort()
+							}
+							break
+						}
+					}
+				} else if field.GetType() == flows.IN_PORT {
+					inPort = field.GetPort()
+				} else if field.GetType() == flows.ETH_TYPE {
+					ethType = field.GetEthType()
+				}
+			}
+		}
+	}
+
+	if uniPortNo == 0 {
+		return 0, 0, 0, 0, 0, 0, errors.New("notFound: pon-interface (flowDirection)")
+		// olterrors.NewErrNotFound("pon-interface", log.Fields{"flow-direction": flowDirection}, nil)
+	}
+
+	ponIntf = intfIDFromUniPortNum(uniPortNo)
+	onuID = onuIDFromUniPortNum(uniPortNo)
+	uniID = uniIDFromPortNum(uniPortNo)
+
+	logger.Debugw("flow extract info result",
+		log.Fields{"uniPortNo": uniPortNo, "ponIntf": ponIntf,
+			"onuID": onuID, "uniID": uniID, "inPort": inPort, "ethType": ethType})
+
+	return uniPortNo, ponIntf, onuID, uniID, inPort, ethType, nil
+}
+*/
diff --git a/internal/pkg/onuadaptercore/status.go b/internal/pkg/onuadaptercore/status.go
new file mode 100644
index 0000000..d43fd57
--- /dev/null
+++ b/internal/pkg/onuadaptercore/status.go
@@ -0,0 +1,54 @@
+/*
+ * 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 adaptercoreonu
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"github.com/opencord/voltha-lib-go/v3/pkg/adapters/adapterif"
+	"github.com/opencord/voltha-protos/v3/go/common"
+)
+
+var startWatchStatus bool = false
+
+//WatchStatus ...
+func WatchStatus(ctx context.Context, cp adapterif.CoreProxy) {
+	if startWatchStatus {
+		return
+	}
+	startWatchStatus = true
+	logger.Debug(ctx, fmt.Sprintf("Start WatchStatus()"))
+	ticker := time.NewTicker(1000 * time.Millisecond)
+	prevOnuList := make(map[string]OnuStatus)
+	for range ticker.C {
+		onuList, err := ReadOnuStatusList()
+		if err == nil {
+			for _, onu := range onuList {
+				current, ok := prevOnuList[onu.MacAddress]
+				if !ok || onu.OpeState != current.OpeState || onu.ConnectState != current.ConnectState {
+					con := common.ConnectStatus_Types(common.ConnectStatus_Types_value[onu.ConnectState])
+					ope := common.OperStatus_Types(common.OperStatus_Types_value[onu.OpeState])
+					err2 := cp.DeviceStateUpdate(ctx, onu.ID, con, ope)
+					logger.Debug(ctx, fmt.Sprintf("WatchStatus() update: %s, OpeState: %v, ConnectState: %v, error: %v",
+						onu.ID, onu.OpeState, onu.ConnectState, err2))
+					prevOnuList[onu.MacAddress] = onu
+				}
+			}
+		}
+	}
+}
diff --git a/internal/pkg/onuadaptercore/uniportadmin.go b/internal/pkg/onuadaptercore/uniportadmin.go
new file mode 100644
index 0000000..36b7fb7
--- /dev/null
+++ b/internal/pkg/onuadaptercore/uniportadmin.go
@@ -0,0 +1,373 @@
+/*
+ * 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 adaptercoreonu provides the utility for onu devices, flows and statistics
+package adaptercoreonu
+
+import (
+	"context"
+	"errors"
+	"time"
+
+	"github.com/looplab/fsm"
+
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v3/pkg/log"
+)
+
+//lockStateFsm defines the structure for the state machine to lock/unlock the ONU UNI ports via OMCI
+type lockStateFsm struct {
+	pOmciCC                  *omciCC
+	adminState               bool
+	requestEvent             OnuDeviceEvent
+	omciLockResponseReceived chan bool //seperate channel needed for checking UNI port OMCi message responses
+	pAdaptFsm                *AdapterFsm
+}
+
+const (
+	uniEvStart         = "uniEvStart"
+	uniEvStartAdmin    = "uniEvStartAdmin"
+	uniEvRxUnisResp    = "uniEvRxUnisResp"
+	uniEvRxOnugResp    = "uniEvRxOnugResp"
+	uniEvTimeoutSimple = "uniEvTimeoutSimple"
+	uniEvTimeoutUnis   = "uniEvTimeoutUnis"
+	uniEvReset         = "uniEvReset"
+	uniEvRestart       = "uniEvRestart"
+)
+const (
+	uniStDisabled    = "uniStDisabled"
+	uniStStarting    = "uniStStarting"
+	uniStSettingUnis = "uniStSettingUnis"
+	uniStSettingOnuG = "uniStSettingOnuG"
+	uniStAdminDone   = "uniStAdminDone"
+	uniStResetting   = "uniStResetting"
+)
+
+//newLockStateFsm is the 'constructor' for the state machine to lock/unlock the ONU UNI ports via OMCI
+func newLockStateFsm(apDevOmciCC *omciCC, aAdminState bool, aRequestEvent OnuDeviceEvent,
+	aName string, aDeviceID string, aCommChannel chan Message) *lockStateFsm {
+	instFsm := &lockStateFsm{
+		pOmciCC:      apDevOmciCC,
+		adminState:   aAdminState,
+		requestEvent: aRequestEvent,
+	}
+	instFsm.pAdaptFsm = NewAdapterFsm(aName, aDeviceID, aCommChannel)
+	if instFsm.pAdaptFsm == nil {
+		logger.Errorw("LockStateFsm's AdapterFsm could not be instantiated!!", log.Fields{
+			"device-id": aDeviceID})
+		return nil
+	}
+	if aAdminState { //port locking requested
+		instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
+			uniStDisabled,
+			fsm.Events{
+
+				{Name: uniEvStart, Src: []string{uniStDisabled}, Dst: uniStStarting},
+
+				{Name: uniEvStartAdmin, Src: []string{uniStStarting}, Dst: uniStSettingUnis},
+				// the settingUnis state is used for multi ME config for all UNI related ports
+				// maybe such could be reflected in the state machine as well (port number parametrized)
+				// but that looks not straightforward here - so we keep it simple here for the beginning(?)
+				{Name: uniEvRxUnisResp, Src: []string{uniStSettingUnis}, Dst: uniStSettingOnuG},
+				{Name: uniEvRxOnugResp, Src: []string{uniStSettingOnuG}, Dst: uniStAdminDone},
+
+				{Name: uniEvTimeoutSimple, Src: []string{uniStSettingOnuG}, Dst: uniStStarting},
+				{Name: uniEvTimeoutUnis, Src: []string{uniStSettingUnis}, Dst: uniStStarting},
+
+				{Name: uniEvReset, Src: []string{uniStStarting, uniStSettingOnuG, uniStSettingUnis,
+					uniStAdminDone}, Dst: uniStResetting},
+				// exceptional treatment for all states except uniStResetting
+				{Name: uniEvRestart, Src: []string{uniStStarting, uniStSettingOnuG, uniStSettingUnis,
+					uniStAdminDone, uniStResetting}, Dst: uniStDisabled},
+			},
+
+			fsm.Callbacks{
+				"enter_state":                 func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
+				("enter_" + uniStStarting):    func(e *fsm.Event) { instFsm.enterAdminStartingState(e) },
+				("enter_" + uniStSettingOnuG): func(e *fsm.Event) { instFsm.enterSettingOnuGState(e) },
+				("enter_" + uniStSettingUnis): func(e *fsm.Event) { instFsm.enterSettingUnisState(e) },
+				("enter_" + uniStAdminDone):   func(e *fsm.Event) { instFsm.enterAdminDoneState(e) },
+				("enter_" + uniStResetting):   func(e *fsm.Event) { instFsm.enterResettingState(e) },
+			},
+		)
+	} else { //port unlocking requested
+		instFsm.pAdaptFsm.pFsm = fsm.NewFSM(
+			uniStDisabled,
+			fsm.Events{
+
+				{Name: uniEvStart, Src: []string{uniStDisabled}, Dst: uniStStarting},
+
+				{Name: uniEvStartAdmin, Src: []string{uniStStarting}, Dst: uniStSettingOnuG},
+				{Name: uniEvRxOnugResp, Src: []string{uniStSettingOnuG}, Dst: uniStSettingUnis},
+				// the settingUnis state is used for multi ME config for all UNI related ports
+				// maybe such could be reflected in the state machine as well (port number parametrized)
+				// but that looks not straightforward here - so we keep it simple here for the beginning(?)
+				{Name: uniEvRxUnisResp, Src: []string{uniStSettingUnis}, Dst: uniStAdminDone},
+
+				{Name: uniEvTimeoutSimple, Src: []string{uniStSettingOnuG}, Dst: uniStStarting},
+				{Name: uniEvTimeoutUnis, Src: []string{uniStSettingUnis}, Dst: uniStStarting},
+
+				{Name: uniEvReset, Src: []string{uniStStarting, uniStSettingOnuG, uniStSettingUnis,
+					uniStAdminDone}, Dst: uniStResetting},
+				// exceptional treatment for all states except uniStResetting
+				{Name: uniEvRestart, Src: []string{uniStStarting, uniStSettingOnuG, uniStSettingUnis,
+					uniStAdminDone, uniStResetting}, Dst: uniStDisabled},
+			},
+
+			fsm.Callbacks{
+				"enter_state":                 func(e *fsm.Event) { instFsm.pAdaptFsm.logFsmStateChange(e) },
+				("enter_" + uniStStarting):    func(e *fsm.Event) { instFsm.enterAdminStartingState(e) },
+				("enter_" + uniStSettingOnuG): func(e *fsm.Event) { instFsm.enterSettingOnuGState(e) },
+				("enter_" + uniStSettingUnis): func(e *fsm.Event) { instFsm.enterSettingUnisState(e) },
+				("enter_" + uniStAdminDone):   func(e *fsm.Event) { instFsm.enterAdminDoneState(e) },
+				("enter_" + uniStResetting):   func(e *fsm.Event) { instFsm.enterResettingState(e) },
+			},
+		)
+	}
+	if instFsm.pAdaptFsm.pFsm == nil {
+		logger.Errorw("LockStateFsm's Base FSM could not be instantiated!!", log.Fields{
+			"device-id": aDeviceID})
+		return nil
+	}
+
+	logger.Infow("LockStateFsm created", log.Fields{"device-id": aDeviceID})
+	return instFsm
+}
+
+//setSuccessEvent modifies the requested event notified on success
+//assumption is that this is only called in the disabled (idle) state of the FSM, hence no sem protection required
+func (oFsm *lockStateFsm) setSuccessEvent(aEvent OnuDeviceEvent) {
+	oFsm.requestEvent = aEvent
+}
+
+func (oFsm *lockStateFsm) enterAdminStartingState(e *fsm.Event) {
+	logger.Debugw("LockStateFSM start", log.Fields{"in state": e.FSM.Current(),
+		"device-id": oFsm.pAdaptFsm.deviceID})
+	if oFsm.omciLockResponseReceived == nil {
+		oFsm.omciLockResponseReceived = make(chan bool)
+		logger.Debug("LockStateFSM - OMCI UniLock 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.omciLockResponseReceived) > 0 {
+			<-oFsm.omciLockResponseReceived
+		}
+	}
+	go oFsm.processOmciLockMessages()
+
+	pLockStateAFsm := oFsm.pAdaptFsm
+	if pLockStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				_ = a_pAFsm.pFsm.Event(uniEvStartAdmin)
+			}
+		}(pLockStateAFsm)
+	}
+}
+
+func (oFsm *lockStateFsm) enterSettingOnuGState(e *fsm.Event) {
+	var omciAdminState uint8 = 1 //default locked
+	if !oFsm.adminState {
+		omciAdminState = 0
+	}
+	logger.Debugw("LockStateFSM Tx Set::ONU-G:admin", log.Fields{
+		"omciAdmin": omciAdminState, "in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	requestedAttributes := me.AttributeValueMap{"AdministrativeState": omciAdminState}
+	meInstance := oFsm.pOmciCC.sendSetOnuGLS(context.TODO(), ConstDefaultOmciTimeout, true,
+		requestedAttributes, oFsm.pAdaptFsm.commChan)
+	oFsm.pOmciCC.pLastTxMeInstance = meInstance
+}
+
+func (oFsm *lockStateFsm) enterSettingUnisState(e *fsm.Event) {
+	logger.Infow("LockStateFSM - starting PPTP config loop", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID, "LockState": oFsm.adminState})
+	go oFsm.performUniPortAdminSet()
+}
+
+func (oFsm *lockStateFsm) enterAdminDoneState(e *fsm.Event) {
+	logger.Debugw("LockStateFSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": oFsm.pAdaptFsm.deviceID})
+	oFsm.pOmciCC.pBaseDeviceHandler.deviceProcStatusUpdate(oFsm.requestEvent)
+	pLockStateAFsm := oFsm.pAdaptFsm
+	if pLockStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				_ = a_pAFsm.pFsm.Event(uniEvReset)
+			}
+		}(pLockStateAFsm)
+	}
+}
+
+func (oFsm *lockStateFsm) enterResettingState(e *fsm.Event) {
+	logger.Debugw("LockStateFSM resetting", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+	pLockStateAFsm := oFsm.pAdaptFsm
+	if pLockStateAFsm != nil {
+		// abort running message processing
+		fsmAbortMsg := Message{
+			Type: TestMsg,
+			Data: TestMessage{
+				TestMessageVal: AbortMessageProcessing,
+			},
+		}
+		pLockStateAFsm.commChan <- fsmAbortMsg
+
+		//try to restart the FSM to 'disabled'
+		// see DownloadedState: decouple event transfer
+		go func(a_pAFsm *AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+				_ = a_pAFsm.pFsm.Event(uniEvRestart)
+			}
+		}(pLockStateAFsm)
+	}
+}
+
+func (oFsm *lockStateFsm) processOmciLockMessages( /*ctx context.Context*/ ) {
+	logger.Debugw("Start LockStateFsm Msg processing", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info("MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.pAdaptFsm.deviceID})
+		// 	break loop
+		message, ok := <-oFsm.pAdaptFsm.commChan
+		if !ok {
+			logger.Info("LockStateFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+			// but then we have to ensure a restart of the FSM as well - as exceptional procedure
+			_ = oFsm.pAdaptFsm.pFsm.Event(uniEvRestart)
+			break loop
+		}
+		logger.Debugw("LockStateFsm Rx Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+
+		switch message.Type {
+		case TestMsg:
+			msg, _ := message.Data.(TestMessage)
+			if msg.TestMessageVal == AbortMessageProcessing {
+				logger.Infow("LockStateFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+				break loop
+			}
+			logger.Warnw("LockStateFsm unknown TestMessage", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "MessageVal": msg.TestMessageVal})
+		case OMCI:
+			msg, _ := message.Data.(OmciMessage)
+			oFsm.handleOmciLockStateMessage(msg)
+		default:
+			logger.Warn("LockStateFsm Rx unknown message", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+				"message.Type": message.Type})
+		}
+	}
+	logger.Infow("End LockStateFsm Msg processing", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+}
+
+func (oFsm *lockStateFsm) handleOmciLockStateMessage(msg OmciMessage) {
+	logger.Debugw("Rx OMCI LockStateFsm Msg", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID,
+		"msgType": msg.OmciMsg.MessageType})
+
+	if msg.OmciMsg.MessageType == omci.SetResponseType {
+		msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+		if msgLayer == nil {
+			logger.Error("LockStateFsm - Omci Msg layer could not be detected for SetResponse")
+			return
+		}
+		msgObj, msgOk := msgLayer.(*omci.SetResponse)
+		if !msgOk {
+			logger.Error("LockStateFsm - Omci Msg layer could not be assigned for SetResponse")
+			return
+		}
+		logger.Debugw("LockStateFsm SetResponse Data", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "data-fields": msgObj})
+		if msgObj.Result != me.Success {
+			logger.Errorw("LockStateFsm - Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+			// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+			return
+		}
+		// compare comments above for CreateResponse (apply also here ...)
+		if msgObj.EntityClass == oFsm.pOmciCC.pLastTxMeInstance.GetClassID() &&
+			msgObj.EntityInstance == oFsm.pOmciCC.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.pOmciCC.pLastTxMeInstance.GetName() {
+			case "OnuG":
+				{ // let the FSM proceed ...
+					_ = oFsm.pAdaptFsm.pFsm.Event(uniEvRxOnugResp)
+				}
+			case "UniG", "VEIP":
+				{ // let the PPTP init proceed by stopping the wait function
+					oFsm.omciLockResponseReceived <- true
+				}
+			}
+		}
+	} else {
+		logger.Errorw("LockStateFsm - Rx OMCI unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+		return
+	}
+}
+
+func (oFsm *lockStateFsm) performUniPortAdminSet() {
+	var omciAdminState uint8 = 1 //default locked
+	if !oFsm.adminState {
+		omciAdminState = 0
+	}
+	requestedAttributes := me.AttributeValueMap{"AdministrativeState": omciAdminState}
+
+	for uniNo, uniPort := range oFsm.pOmciCC.pBaseDeviceHandler.uniEntityMap {
+		logger.Debugw("Setting PPTP admin state", log.Fields{
+			"device-id": oFsm.pAdaptFsm.deviceID, "for PortNo": uniNo})
+
+		var meInstance *me.ManagedEntity
+		if uniPort.portType == uniPPTP {
+			meInstance = oFsm.pOmciCC.sendSetUniGLS(context.TODO(), uniPort.entityID, ConstDefaultOmciTimeout,
+				true, requestedAttributes, oFsm.pAdaptFsm.commChan)
+			oFsm.pOmciCC.pLastTxMeInstance = meInstance
+		} else if uniPort.portType == uniVEIP {
+			meInstance = oFsm.pOmciCC.sendSetVeipLS(context.TODO(), uniPort.entityID, ConstDefaultOmciTimeout,
+				true, requestedAttributes, oFsm.pAdaptFsm.commChan)
+			oFsm.pOmciCC.pLastTxMeInstance = meInstance
+		} else {
+			logger.Warnw("Unsupported PPTP type - skip",
+				log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "Port": uniNo})
+			continue
+		}
+
+		//verify response
+		err := oFsm.waitforOmciResponse(meInstance)
+		if err != nil {
+			logger.Errorw("PPTP Admin State set failed, aborting LockState set!",
+				log.Fields{"device-id": oFsm.pAdaptFsm.deviceID, "Port": uniNo})
+			_ = oFsm.pAdaptFsm.pFsm.Event(uniEvReset)
+			return
+		}
+	} //for all UNI ports
+	logger.Infow("PPTP config loop finished", log.Fields{"device-id": oFsm.pAdaptFsm.deviceID})
+	_ = oFsm.pAdaptFsm.pFsm.Event(uniEvRxUnisResp)
+}
+
+func (oFsm *lockStateFsm) waitforOmciResponse(apMeInstance *me.ManagedEntity) error {
+	select {
+	case <-time.After(30 * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+		logger.Warnw("LockStateFSM uni-set timeout", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+		return errors.New("lockStateFsm uni-set timeout")
+	case success := <-oFsm.omciLockResponseReceived:
+		if success {
+			logger.Debug("LockStateFSM uni-set response received")
+			return nil
+		}
+		// should not happen so far
+		logger.Warnw("LockStateFSM uni-set response error", log.Fields{"for device-id": oFsm.pAdaptFsm.deviceID})
+		return errors.New("lockStateFsm uni-set responseError")
+	}
+}