[VOL-3322] Support 1t4gem tech profile
[VOL-3323] Support 1t8gem tech profile
[VOL-3381] Support device restart w/o OLT-trigger: save/restore/delete ONU/TP-data in kv-store
[VOL-3402] Support usage of MIB-upload data during tech profile configuration

Signed-off-by: Holger Hildebrandt <holger.hildebrandt@adtran.com>
Change-Id: If1fb83fe509e8cb0c7300146111bf83d6e458fe5
diff --git a/VERSION b/VERSION
index 984c086..bb32cc1 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.11-dev125
+0.1.11-dev126
diff --git a/go.mod b/go.mod
index 618af4a..9eee99b 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@
 go 1.13
 
 require (
+	github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73
 	github.com/gogo/protobuf v1.3.1
 	github.com/golang/protobuf v1.4.2
 	github.com/google/gopacket v1.1.17
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index a580530..d1cb6f3 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -325,7 +325,7 @@
 				go dh.pOnuTP.configureUniTp(dctx, 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 collet their result
+				err := dh.pOnuTP.waitForTpCompletion(cancel, &wg) //wait for background process to finish and collect their result
 				return err
 			}
 			// no change, nothing really to do
@@ -475,6 +475,24 @@
 	}
 }
 
+func (dh *DeviceHandler) ReconcileDevice(device *voltha.Device) error {
+	logger.Debugw("reconcile-device", log.Fields{"DeviceId": device.Id, "SerialNumber": device.SerialNumber})
+	if err := dh.pOnuTP.restoreFromOnuTpPathKvStore(context.TODO()); err != nil {
+		return err
+	}
+	// TODO: further actions - init PON, metrics, reload DB ...
+	return nil
+}
+
+func (dh *DeviceHandler) DeleteDevice(device *voltha.Device) error {
+	logger.Debugw("delete-device", log.Fields{"DeviceId": device.Id, "SerialNumber": device.SerialNumber})
+	if err := dh.pOnuTP.deleteOnuTpPathKvStore(context.TODO()); err != nil {
+		return err
+	}
+	// TODO: further actions - stop metrics and FSMs, remove device ...
+	return nil
+}
+
 //  DeviceHandler methods that implement the adapters interface requests## end #########
 // #####################################################################################
 
@@ -1039,11 +1057,9 @@
 			//set internal state anyway - as it was done
 			dh.deviceReason = "discovery-mibsync-complete"
 
-			pDevEntry := dh.GetOnuDeviceEntry(false)
-			unigMap, ok := pDevEntry.pOnuDB.meDb[me.UniGClassID]
-			unigInstKeys := pDevEntry.pOnuDB.GetSortedInstKeys(unigMap)
 			i := uint8(0) //UNI Port limit: see MaxUnisPerOnu (by now 16) (OMCI supports max 255 p.b.)
-			if ok {
+			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{
 						"deviceId": dh.deviceID, "UnigMe EntityID": mgmtEntityId})
@@ -1053,9 +1069,7 @@
 			} else {
 				logger.Debugw("No UniG instances found", log.Fields{"deviceId": dh.deviceID})
 			}
-			veipMap, ok := pDevEntry.pOnuDB.meDb[me.VirtualEthernetInterfacePointClassID]
-			veipInstKeys := pDevEntry.pOnuDB.GetSortedInstKeys(veipMap)
-			if ok {
+			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{
 						"deviceId": dh.deviceID, "VEIP EntityID": mgmtEntityId})
diff --git a/internal/pkg/onuadaptercore/mib_sync.go b/internal/pkg/onuadaptercore/mib_sync.go
index abb80f1..cc589b8 100644
--- a/internal/pkg/onuadaptercore/mib_sync.go
+++ b/internal/pkg/onuadaptercore/mib_sync.go
@@ -170,7 +170,7 @@
 											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.StoreMe(meClassId, meEntityId, attributesMap)
+											onuDeviceEntry.pOnuDB.PutMe(meClassId, meEntityId, attributesMap)
 											meStoredFromTemplate = true
 										}
 									}
@@ -354,7 +354,7 @@
 		meEntityId := msgObj.ReportedME.GetEntityID()
 		meAttributes := msgObj.ReportedME.GetAttributeValueMap()
 
-		onuDeviceEntry.pOnuDB.StoreMe(meClassId, meEntityId, meAttributes)
+		onuDeviceEntry.pOnuDB.PutMe(meClassId, meEntityId, meAttributes)
 
 		if onuDeviceEntry.PDevOmciCC.uploadSequNo < onuDeviceEntry.PDevOmciCC.uploadNoOfCmds {
 			onuDeviceEntry.PDevOmciCC.sendMibUploadNext(context.TODO(), ConstDefaultOmciTimeout, true)
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
index 54e8a50..c4bc235 100644
--- a/internal/pkg/onuadaptercore/omci_ani_config.go
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -23,8 +23,8 @@
 	"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"
@@ -64,6 +64,16 @@
 	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
@@ -82,9 +92,7 @@
 	macBPCD0ID               uint16
 	tcont0ID                 uint16
 	alloc0ID                 uint16
-	gemPortXID               []uint16
-	upQueueXID               []uint16
-	downQueueXID             []uint16
+	gemPortAttribsSlice      []ponAniGemPortAttribs
 }
 
 //NewUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
@@ -187,6 +195,9 @@
 			<-oFsm.omciMIdsResponseReceived
 		}
 	}
+	//ensure internal slices are empty (which might be set from previous run) - release memory
+	oFsm.gemPortAttribsSlice = nil
+
 	// start go routine for processing of LockState messages
 	go oFsm.ProcessOmciAniMessages()
 
@@ -200,15 +211,88 @@
 				//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
-				oFsm.tcont0ID = 0x8001 //TODO!: for now fixed, but target is to use value from MibUpload (mibDB)
-				oFsm.alloc0ID = (*(oFsm.pUniTechProf.mapPonAniConfig[uint32(oFsm.pOnuUniPort.uniId)]))[0].tcontParams.allocID
-				//TODO!! this is just for the first GemPort right now - needs update
-				oFsm.gemPortXID = append(oFsm.gemPortXID,
-					(*(oFsm.pUniTechProf.mapPonAniConfig[uint32(oFsm.pOnuUniPort.uniId)]))[0].mapGemPortParams[0].gemPortID)
-				oFsm.upQueueXID = append(oFsm.upQueueXID, 0x8001) //TODO!: for now fixed, but target is to use value from MibUpload (mibDB)
-				//TODO!: for now fixed, but target is to use value from MibUpload (mibDB), also TechProf setting dependency may exist!
-				oFsm.downQueueXID = append(oFsm.downQueueXID, 1)
 
+				// For the time beeing: 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{"deviceId": oFsm.pAdaptFsm.deviceID})
+				}
+				oFsm.alloc0ID = (*(oFsm.pUniTechProf.mapPonAniConfig[uint32(oFsm.pOnuUniPort.uniId)]))[0].tcontParams.allocID
+				loGemPortAttribs := ponAniGemPortAttribs{}
+				//for all TechProfile set GemIndices
+				for _, gemEntry := range (*(oFsm.pUniTechProf.mapPonAniConfig[uint32(oFsm.pOnuUniPort.uniId)]))[0].mapGemPortParams {
+					//collect all GemConfigData in a seperate 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 {
+									relatedPort := returnVal.(uint32)
+									if relatedPort == usQrelPortMask {
+										loGemPortAttribs.upQueueID = mgmtEntityId
+										logger.Debugw("UpQueue for GemPort found:", log.Fields{"gemPortID": loGemPortAttribs.gemPortID,
+											"upQueueID": strconv.FormatInt(int64(loGemPortAttribs.upQueueID), 16), "deviceId": 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), "deviceId": oFsm.pAdaptFsm.deviceID})
+										dsQueueFound = true
+									}
+									if usQueueFound && dsQueueFound {
+										break
+									}
+								} else {
+									logger.Warnw("'relatedPort' not found in meAttributes:", log.Fields{"deviceId": oFsm.pAdaptFsm.deviceID})
+								}
+							} else {
+								logger.Warnw("No attributes available in DB:", log.Fields{"meClassID": me.PriorityQueueClassID,
+									"mgmtEntityId": mgmtEntityId, "deviceId": oFsm.pAdaptFsm.deviceID})
+							}
+						}
+					} else {
+						logger.Warnw("No PriorityQueue instances found", log.Fields{"deviceId": 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 assigned:", log.Fields{
+						"gemPortID":   loGemPortAttribs.gemPortID,
+						"upQueueID":   loGemPortAttribs.upQueueID,
+						"downQueueID": loGemPortAttribs.downQueueID,
+						"pbitString":  loGemPortAttribs.pbitString,
+					})
+
+					oFsm.gemPortAttribsSlice = append(oFsm.gemPortAttribsSlice, loGemPortAttribs)
+				}
 				a_pAFsm.pFsm.Event(aniEvStartConfig)
 			}
 		}(pConfigAniStateAFsm)
@@ -290,24 +374,102 @@
 	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})
 
-	//TODO!! in MultiGemPort constellation the IwTpPtr setting will get variable -f(Prio) based on pUniTechProf
-	logger.Debugw("UniPonAniConfigFsm Tx Set::1pMapper SingleGem", log.Fields{
-		"EntitytId":  strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
-		"GemIwTpPtr": strconv.FormatInt(int64(oFsm.gemPortXID[0]), 16),
-		"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: me.AttributeValueMap{
-			"InterworkTpPointerForPBitPriority0": oFsm.gemPortXID[0],
-			"InterworkTpPointerForPBitPriority1": oFsm.gemPortXID[0],
-			"InterworkTpPointerForPBitPriority2": oFsm.gemPortXID[0],
-			"InterworkTpPointerForPBitPriority3": oFsm.gemPortXID[0],
-			"InterworkTpPointerForPBitPriority4": oFsm.gemPortXID[0],
-			"InterworkTpPointerForPBitPriority5": oFsm.gemPortXID[0],
-			"InterworkTpPointerForPBitPriority6": oFsm.gemPortXID[0],
-			"InterworkTpPointerForPBitPriority7": oFsm.gemPortXID[0],
-		},
+		EntityID:   oFsm.mapperSP0ID,
+		Attributes: make(me.AttributeValueMap, 0),
 	}
+
+	//assign the GemPorts according to the configured Prio
+	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
+	if loPrioGemPortArray[0] != 0 {
+		foundIwPtr = true
+		logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+			"IwPtr for Prio0": strconv.FormatInt(int64(loPrioGemPortArray[0]), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+		meParams.Attributes["InterworkTpPointerForPBitPriority0"] = loPrioGemPortArray[0]
+	}
+	if loPrioGemPortArray[1] != 0 {
+		foundIwPtr = true
+		logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+			"IwPtr for Prio1": strconv.FormatInt(int64(loPrioGemPortArray[1]), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+		meParams.Attributes["InterworkTpPointerForPBitPriority1"] = loPrioGemPortArray[1]
+	}
+	if loPrioGemPortArray[2] != 0 {
+		foundIwPtr = true
+		logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+			"IwPtr for Prio2": strconv.FormatInt(int64(loPrioGemPortArray[2]), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+		meParams.Attributes["InterworkTpPointerForPBitPriority2"] = loPrioGemPortArray[2]
+	}
+	if loPrioGemPortArray[3] != 0 {
+		foundIwPtr = true
+		logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+			"IwPtr for Prio3": strconv.FormatInt(int64(loPrioGemPortArray[3]), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+		meParams.Attributes["InterworkTpPointerForPBitPriority3"] = loPrioGemPortArray[3]
+	}
+	if loPrioGemPortArray[4] != 0 {
+		foundIwPtr = true
+		logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+			"IwPtr for Prio4": strconv.FormatInt(int64(loPrioGemPortArray[4]), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+		meParams.Attributes["InterworkTpPointerForPBitPriority4"] = loPrioGemPortArray[4]
+	}
+	if loPrioGemPortArray[5] != 0 {
+		foundIwPtr = true
+		logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+			"IwPtr for Prio5": strconv.FormatInt(int64(loPrioGemPortArray[5]), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+		meParams.Attributes["InterworkTpPointerForPBitPriority5"] = loPrioGemPortArray[5]
+	}
+	if loPrioGemPortArray[6] != 0 {
+		foundIwPtr = true
+		logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+			"IwPtr for Prio6": strconv.FormatInt(int64(loPrioGemPortArray[6]), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+		meParams.Attributes["InterworkTpPointerForPBitPriority6"] = loPrioGemPortArray[6]
+	}
+	if loPrioGemPortArray[7] != 0 {
+		foundIwPtr = true
+		logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
+			"IwPtr for Prio7": strconv.FormatInt(int64(loPrioGemPortArray[7]), 16), "device-id": oFsm.pAdaptFsm.deviceID})
+		meParams.Attributes["InterworkTpPointerForPBitPriority7"] = loPrioGemPortArray[7]
+	}
+	if foundIwPtr == false {
+		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(a_pAFsm *AdapterFsm) {
+				if a_pAFsm != nil && a_pAFsm.pFsm != nil {
+					a_pAFsm.pFsm.Event(aniEvReset)
+				}
+			}(pConfigAniStateAFsm)
+		}
+	}
+
 	meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(context.TODO(), ConstDefaultOmciTimeout, true,
 		oFsm.pAdaptFsm.commChan, meParams)
 	//accept also nil as (error) return value for writing to LastTx
@@ -333,6 +495,7 @@
 
 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
@@ -507,39 +670,39 @@
 }
 
 func (oFsm *UniPonAniConfigFsm) performCreatingGemNCTPs() {
-	//TODO!! this is just for the first GemPort right now - needs update
-	//   .. for gemPort in range gemPortXID
-	logger.Infow("UniPonAniConfigFsm Tx Create::GemNWCtp", log.Fields{
-		"EntitytId": strconv.FormatInt(int64(oFsm.gemPortXID[0]), 16),
-		"TcontId":   strconv.FormatInt(int64(oFsm.tcont0ID), 16),
-		"device-id": oFsm.pAdaptFsm.deviceID})
-	meParams := me.ParamData{
-		EntityID: oFsm.gemPortXID[0],
-		Attributes: me.AttributeValueMap{
-			"PortId":       oFsm.gemPortXID[0], //same as EntityID
-			"TContPointer": oFsm.tcont0ID,
-			"Direction":    (*(oFsm.pUniTechProf.mapPonAniConfig[uint32(oFsm.pOnuUniPort.uniId)]))[0].mapGemPortParams[0].direction,
-			//ONU-G.TrafficManagementOption dependency ->PrioQueue or TCont
-			//  TODO!! verify dependency and QueueId in case of Multi-GemPort setup!
-			"TrafficManagementPointerForUpstream": oFsm.upQueueXID[0], //might be different in wrr-only Setup - tcont0ID
-			"PriorityQueuePointerForDownStream":   oFsm.downQueueXID[0],
-		},
-	}
-	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
+	// for all GemPorts of this T-Cont as given by the size of set gemPortAttribsSlice
+	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{"deviceId": oFsm.pAdaptFsm.deviceID, "GemIndex": 0}) //running index in loop later!
-		oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
-		return
-	}
-	//for all GemPortID's ports - later
+		//verify response
+		err := oFsm.waitforOmciResponse()
+		if err != nil {
+			logger.Errorw("GemNWCtp create failed, aborting AniConfig FSM!",
+				log.Fields{"deviceId": oFsm.pAdaptFsm.deviceID, "GemIndex": gemIndex})
+			oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+			return
+		}
+	} //for all GemPorts of this T-Cont
 
 	// if Config has been done for all GemPort instances let the FSM proceed
 	logger.Debugw("GemNWCtp create loop finished", log.Fields{"deviceId": oFsm.pAdaptFsm.deviceID})
@@ -548,37 +711,37 @@
 }
 
 func (oFsm *UniPonAniConfigFsm) performCreatingGemIWs() {
-	//TODO!! this is just for the first GemPort right now - needs update
-	//   .. for gemPort in range gemPortXID
-	logger.Infow("UniPonAniConfigFsm Tx Create::GemIwTp", log.Fields{
-		"EntitytId": strconv.FormatInt(int64(oFsm.gemPortXID[0]), 16),
-		"SPPtr":     strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
-		"device-id": oFsm.pAdaptFsm.deviceID})
-	meParams := me.ParamData{
-		EntityID: oFsm.gemPortXID[0],
-		Attributes: me.AttributeValueMap{
-			"GemPortNetworkCtpConnectivityPointer": oFsm.gemPortXID[0], //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
+	// for all GemPorts of this T-Cont as given by the size of set gemPortAttribsSlice
+	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{"deviceId": oFsm.pAdaptFsm.deviceID, "GemIndex": 0}) //running index in loop later!
-		oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
-		return
-	}
-	//for all GemPortID's ports - later
+		//verify response
+		err := oFsm.waitforOmciResponse()
+		if err != nil {
+			logger.Errorw("GemIwTp create failed, aborting AniConfig FSM!",
+				log.Fields{"deviceId": oFsm.pAdaptFsm.deviceID, "GemIndex": gemIndex})
+			oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+			return
+		}
+	} //for all GemPort's of this T-Cont
 
 	// if Config has been done for all GemPort instances let the FSM proceed
 	logger.Debugw("GemIwTp create loop finished", log.Fields{"deviceId": oFsm.pAdaptFsm.deviceID})
@@ -587,37 +750,73 @@
 }
 
 func (oFsm *UniPonAniConfigFsm) performSettingPQs() {
-	//TODO!! this is just for the first upstream PrioQueue right now - needs update
-	//TODO!! implementation is restricted to WRR setting on the TrafficScheduler/Tcont
-	//  SP setting would allow relatedPort(Prio) setting in case ONU supports config (ONU-2G QOS)
-
-	//   .. for prioQueu in range upQueueXID
-	weight := (*(oFsm.pUniTechProf.mapPonAniConfig[uint32(oFsm.pOnuUniPort.uniId)]))[0].mapGemPortParams[0].queueWeight
-	logger.Infow("UniPonAniConfigFsm Tx Set::PrioQueue", log.Fields{
-		"EntitytId": strconv.FormatInt(int64(oFsm.upQueueXID[0]), 16),
-		"Weight":    weight,
-		"device-id": oFsm.pAdaptFsm.deviceID})
-	meParams := me.ParamData{
-		EntityID: oFsm.upQueueXID[0],
-		Attributes: me.AttributeValueMap{
-			"Weight": weight,
-		},
+	const cu16StrictPrioWeight uint16 = 0xFFFF
+	//find all upstream PrioQueues related to this T-Cont
+	loQueueMap := ordered_map.NewOrderedMap()
+	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		if gemPortAttribs.qosPolicy == "WRR" {
+			if _, ok := loQueueMap.Get(gemPortAttribs.upQueueID); ok == false {
+				//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
+		}
 	}
-	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{"deviceId": oFsm.pAdaptFsm.deviceID, "QueueIndex": 0}) //running index in loop later!
-		oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
-		return
-	}
-	//for all upstream prioQueus - later
+	//TODO: assumption here is that ONU data uses SP setting in the T-Cont and WRR in the TrafficScheduler
+	//  if that is not the case, the reverse case could be checked and reacted accordingly or if the
+	//  complete chain is not valid, then some error should be thrown and configuration can be aborted
+	//  or even be finished without correct SP/WRR setting
+
+	//TODO: search for the (WRR)trafficScheduler related to the T-Cont of this queue
+	//By now assume fixed value 0x8000, which is the only announce BBSIM TrafficScheduler,
+	//  even though its T-Cont seems to be wrong ...
+	loTrafficSchedulerEID := 0x8000
+	//for all found queues
+	iter := loQueueMap.IterFunc()
+	for kv, ok := iter(); ok; kv, ok = iter() {
+		queueIndex := (kv.Key).(uint16)
+		meParams := me.ParamData{
+			EntityID:   queueIndex,
+			Attributes: make(me.AttributeValueMap, 0),
+		}
+		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{"deviceId": 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
 
 	// if Config has been done for all PrioQueue instances let the FSM proceed
 	logger.Debugw("PrioQueue set loop finished", log.Fields{"deviceId": oFsm.pAdaptFsm.deviceID})
diff --git a/internal/pkg/onuadaptercore/onu_device_db.go b/internal/pkg/onuadaptercore/onu_device_db.go
index 3395495..6f38584 100644
--- a/internal/pkg/onuadaptercore/onu_device_db.go
+++ b/internal/pkg/onuadaptercore/onu_device_db.go
@@ -45,7 +45,7 @@
 	return &onuDeviceDB
 }
 
-func (onuDeviceDB *OnuDeviceDB) StoreMe(meClassId me.ClassID, meEntityId uint16, meAttributes me.AttributeValueMap) {
+func (onuDeviceDB *OnuDeviceDB) PutMe(meClassId me.ClassID, meEntityId uint16, meAttributes me.AttributeValueMap) {
 
 	//filter out the OnuData
 	if me.OnuDataClassID == meClassId {
@@ -76,10 +76,23 @@
 	}
 }
 
-func (onuDeviceDB *OnuDeviceDB) GetSortedInstKeys(meInstMap map[uint16]me.AttributeValueMap) []uint16 {
+func (onuDeviceDB *OnuDeviceDB) GetMe(meClassId me.ClassID, meEntityId uint16) me.AttributeValueMap {
+
+	if meAttributes, present := onuDeviceDB.meDb[meClassId][meEntityId]; present {
+		logger.Debugw("ME found:", log.Fields{"meClassId": meClassId, "meEntityId": meEntityId, "meAttributes": meAttributes,
+			"deviceId": onuDeviceDB.pOnuDeviceEntry.deviceID})
+		return meAttributes
+	} else {
+		return nil
+	}
+}
+
+func (onuDeviceDB *OnuDeviceDB) GetSortedInstKeys(meClassID me.ClassID) []uint16 {
 
 	var meInstKeys []uint16
 
+	meInstMap := onuDeviceDB.meDb[meClassID]
+
 	for k := range meInstMap {
 		meInstKeys = append(meInstKeys, k)
 	}
diff --git a/internal/pkg/onuadaptercore/onu_uni_tp.go b/internal/pkg/onuadaptercore/onu_uni_tp.go
index d6950ff..ec94e37 100644
--- a/internal/pkg/onuadaptercore/onu_uni_tp.go
+++ b/internal/pkg/onuadaptercore/onu_uni_tp.go
@@ -21,6 +21,7 @@
 	"context"
 	"encoding/json"
 	"errors"
+	"fmt"
 	"strconv"
 	"strings"
 	"sync"
@@ -33,6 +34,16 @@
 )
 
 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 is binary string prefix
+	BinaryStringPrefix = "0b"
+	// BinaryBit1 is binary bit 1 expressed as a character
+	BinaryBit1 = '1'
+)
 
 type resourceEntry int
 
@@ -41,18 +52,18 @@
 	cResourceTcont   resourceEntry = 2
 )
 
-type onuSerialNumber struct {
-	sliceVendorID       []byte
-	sliceVendorSpecific []byte
+type uniPersData struct {
+	PersUniId  uint32 `json:"uni_id"`
+	PersTpPath string `json:"tp_path"`
 }
 
 type onuPersistentData struct {
-	persOnuID      uint32
-	persIntfID     uint32
-	persSnr        onuSerialNumber
-	persAdminState string
-	persOperState  string
-	persUniTpPath  map[uint32]string
+	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 {
@@ -65,11 +76,12 @@
 	schedPolicy uint8
 }
 type gemPortParamStruct struct {
+	ponOmciCC       bool
 	gemPortID       uint16
 	direction       uint8
 	gemPortEncState uint8
-	usedPbitMap     uint8
-	ponOmciCC       bool
+	prioQueueIndex  uint8
+	pbitString      string
 	discardPolicy   string
 	//could also be a queue specific paramter, not used that way here
 	maxQueueSize     uint16
@@ -91,8 +103,11 @@
 	deviceID           string
 	baseDeviceHandler  *DeviceHandler
 	tpProcMutex        sync.RWMutex
+	mapUniTpPath       map[uint32]string
 	sOnuPersistentData onuPersistentData
 	techProfileKVStore *db.Backend
+	onuKVStore         *db.Backend
+	onuKVStorePath     string
 	chTpProcessingStep chan uint8
 	mapUniTpIndication map[uint32]*tTechProfileIndication //use pointer values to ease assignments to the map
 	mapPonAniConfig    map[uint32]*tMapPonAniConfig       //per UNI: use pointer values to ease assignments to the map
@@ -108,7 +123,8 @@
 	onuTP.deviceID = aDeviceID
 	onuTP.baseDeviceHandler = aDeviceHandler
 	onuTP.tpProcMutex = sync.RWMutex{}
-	onuTP.sOnuPersistentData.persUniTpPath = make(map[uint32]string)
+	onuTP.mapUniTpPath = make(map[uint32]string)
+	onuTP.sOnuPersistentData.PersUniTpPath = make([]uniPersData, 1)
 	onuTP.chTpProcessingStep = make(chan uint8)
 	onuTP.mapUniTpIndication = make(map[uint32]*tTechProfileIndication)
 	onuTP.mapPonAniConfig = make(map[uint32]*tMapPonAniConfig)
@@ -119,6 +135,13 @@
 		logger.Errorw("Can't access techProfileKVStore - no backend connection to service",
 			log.Fields{"deviceID": 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{"deviceID": aDeviceID, "service": cBasePathOnuKVStore})
+	}
 	return &onuTP
 }
 
@@ -144,7 +167,7 @@
 	   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.sOnuPersistentData.persUniTpPath[aUniID]; present {
+	if existingPath, present := onuTP.mapUniTpPath[aUniID]; present {
 		// uni entry already exists
 		//logger.Debugw(" already exists", log.Fields{"for InstanceId": a_uniInstNo})
 		if existingPath != aPathString {
@@ -152,12 +175,12 @@
 				//existing entry to be deleted
 				logger.Debugw("UniTp path delete", log.Fields{
 					"deviceID": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
-				delete(onuTP.sOnuPersistentData.persUniTpPath, aUniID)
+				delete(onuTP.mapUniTpPath, aUniID)
 			} else {
 				//existing entry to be modified
 				logger.Debugw("UniTp path modify", log.Fields{
 					"deviceID": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
-				onuTP.sOnuPersistentData.persUniTpPath[aUniID] = aPathString
+				onuTP.mapUniTpPath[aUniID] = aPathString
 			}
 			return true
 		}
@@ -176,7 +199,7 @@
 	//new entry to be set
 	logger.Debugw("New UniTp path set", log.Fields{
 		"deviceID": onuTP.deviceID, "uniID": aUniID, "path": aPathString})
-	onuTP.sOnuPersistentData.persUniTpPath[aUniID] = aPathString
+	onuTP.mapUniTpPath[aUniID] = aPathString
 	return true
 }
 
@@ -282,19 +305,44 @@
 
 func (onuTP *OnuUniTechProf) updateOnuTpPathKvStore(ctx context.Context, wg *sync.WaitGroup) {
 	defer wg.Done()
-	logger.Debugw("this would update the ONU's TpPath in KVStore", log.Fields{
-		"deviceID": onuTP.deviceID})
-	//TODO!!!
-	// set onuTP.procResult in case of errors!! cmp. configureUniTp
-	//make use of onuTP.sOnuPersistentData to store the TpPath to KVStore - as background routine
-	/*
-		var processingStep uint8 = 1 // used to synchronize the different processing steps with chTpProcessingStep
-		go onuTp.storePersistentData(ctx, processingStep)
-		if !onuTP.waitForTimeoutOrCompletion(ctx, processingStep) {
-			//timeout or error detected
-			return
-		}
-	*/
+
+	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 chTpProcessingStep
+	go onuTP.storePersistentData(ctx, processingStep)
+	if !onuTP.waitForTimeoutOrCompletion(ctx, 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
@@ -317,6 +365,86 @@
 }
 
 /* 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
+	//TODO: verify usage of these values during restart UC
+	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{"deviceID": 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.chTpProcessingStep <- 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.chTpProcessingStep <- 0 //error indication
+		return
+	}
+	onuTP.chTpProcessingStep <- 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{"deviceID": 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 uint32, aPathString string, aProcessingStep uint8) {
 	var tpInst tp.TechProfile
@@ -366,7 +494,7 @@
 			"profType": onuTP.mapUniTpIndication[aUniID].techProfileType,
 			"profID":   onuTP.mapUniTpIndication[aUniID].techProfileID})
 
-	Value, err := onuTP.techProfileKVStore.Get(context.TODO(), aPathString)
+	Value, err := onuTP.techProfileKVStore.Get(ctx, aPathString)
 	if err == nil {
 		if Value != nil {
 			logger.Debugw("tech-profile read",
@@ -397,13 +525,14 @@
 		return
 	}
 
-	//for first start assume a 1Tcont1Gem profile, later extend for multi GemPerTcont and MultiTcontMultiGem
+	//default start with 1Tcont1Gem profile, later extend for multi GemPerTcont and perhaps even  MultiTcontMultiGem
 	localMapGemPortParams := make(map[uint16]*gemPortParamStruct)
 	localMapGemPortParams[0] = &gemPortParamStruct{}
 	localMapPonAniConfig := make(map[uint16]*tcontGemList)
 	localMapPonAniConfig[0] = &tcontGemList{tcontParamStruct{}, localMapGemPortParams}
 	onuTP.mapPonAniConfig[aUniID] = (*tMapPonAniConfig)(&localMapPonAniConfig)
 
+	//note: the code is currently restricted to one TCcont per Onu (index [0])
 	//get the relevant values from the profile and store to mapPonAniConfig
 	(*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID = uint16(tpInst.UsScheduler.AllocID)
 	//maybe tCont scheduling not (yet) needed - just to basicaly have it for future
@@ -411,38 +540,60 @@
 	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
+		//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 pos == 0 {
-			loGemPortRead = true
-		}
 		if uint32(pos) == loNumGemPorts {
 			logger.Debugw("PonAniConfig abort GemPortList - GemList exceeds set NumberOfGemPorts",
 				log.Fields{"device-id": onuTP.deviceID, "index": pos, "NumGem": loNumGemPorts})
 			break
 		}
-		// a downstream GemPort should always exist (only downstream for MC)
-		(*(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)
+		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.chTpProcessingStep <- 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
+		(*(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)
+		(*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[uint16(pos)].queueWeight =
+			uint8(content.Weight)
 	}
 	if loGemPortRead == false {
-		logger.Errorw("no GemPort could be read from TechProfile",
+		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.chTpProcessingStep <- 0 //error indication
 		return
 	}
@@ -450,10 +601,14 @@
 
 	//logger does not simply output the given structures, just give some example debug values
 	logger.Debugw("PonAniConfig read from TechProfile", log.Fields{
-		"device-id":       onuTP.deviceID,
-		"AllocId":         (*(onuTP.mapPonAniConfig[aUniID]))[0].tcontParams.allocID,
-		"GemPort":         (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[0].gemPortID,
-		"QueueScheduling": (*(onuTP.mapPonAniConfig[aUniID]))[0].mapGemPortParams[0].queueSchedPolicy})
+		"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.chTpProcessingStep <- aProcessingStep //done
 }
diff --git a/internal/pkg/onuadaptercore/openonu.go b/internal/pkg/onuadaptercore/openonu.go
index 1590f60..06790bc 100644
--- a/internal/pkg/onuadaptercore/openonu.go
+++ b/internal/pkg/onuadaptercore/openonu.go
@@ -215,9 +215,18 @@
 	return nil, errors.New("unImplemented")
 }
 
-//Reconcile_device 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 {
-	return errors.New("unImplemented")
+	logger.Debugw("Reconcile_device", log.Fields{"deviceId": device.Id})
+	if handler := oo.getDeviceHandler(device.Id); handler != nil {
+		if err := handler.ReconcileDevice(device); err != nil {
+			return err
+		}
+	} else {
+		logger.Warnw("no handler found for device-reconcilement", log.Fields{"deviceId": device.Id})
+		return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+	}
+	return nil
 }
 
 //Abandon_device unimplemented
@@ -257,9 +266,17 @@
 	return errors.New("unImplemented")
 }
 
-//Delete_device unimplemented
 func (oo *OpenONUAC) Delete_device(device *voltha.Device) error {
-	return errors.New("unImplemented")
+	logger.Debugw("Delete_device", log.Fields{"deviceId": 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{"deviceId": device.Id})
+		return fmt.Errorf(fmt.Sprintf("handler-not-found-%s", device.Id))
+	}
+	return nil
 }
 
 //Get_device_details unimplemented