VOL-4058 - TCONT allocation changes in ani configuration

Change-Id: If2e9c6b58ad62b5c1aa3b0771282058f2f2f5067
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
index ae70353..5701985 100644
--- a/internal/pkg/onuadaptercore/omci_ani_config.go
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -85,10 +85,6 @@
 )
 const cAniFsmIdleState = aniStConfigDone
 
-const (
-	tpIDOffset = 64
-)
-
 type ponAniGemPortAttribs struct {
 	gemPortID      uint16
 	upQueueID      uint16
@@ -133,6 +129,7 @@
 	requestEventOffset       uint8 //used to indicate ConfigDone or Removed using successor (enum)
 	isWaitingForFlowDelete   bool
 	waitFlowDeleteChannel    chan bool
+	tcontSetBefore           bool
 }
 
 //newUniPonAniConfigFsm is the 'constructor' for the state machine to config the PON ANI ports of ONU UNI ports via OMCI
@@ -149,6 +146,7 @@
 		techProfileID:  aTechProfileID,
 		requestEvent:   aRequestEvent,
 		chanSet:        false,
+		tcontSetBefore: false,
 	}
 	instFsm.uniTpKey = uniTP{uniID: apUniPort.uniID, tpID: aTechProfileID}
 	instFsm.waitFlowDeleteChannel = make(chan bool)
@@ -298,8 +296,6 @@
 //TODO:visit here for refactoring for gocyclo
 func (oFsm *uniPonAniConfigFsm) prepareAndEnterConfigState(ctx context.Context, 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)
 		var err error
 		oFsm.mapperSP0ID, err = generateIeeMaperServiceProfileEID(uint16(oFsm.pOnuUniPort.macBpNo), uint16(oFsm.techProfileID))
 		if err != nil {
@@ -316,66 +312,29 @@
 		logger.Debugw(ctx, "generated ids for ani config", log.Fields{"mapperSP0ID": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
 			"macBPCD0ID": strconv.FormatInt(int64(oFsm.macBPCD0ID), 16), "device-id": oFsm.deviceID,
 			"macBpNo": oFsm.pOnuUniPort.macBpNo, "techProfileID": oFsm.techProfileID})
-		if tcontInstKeys := oFsm.pOnuDB.getSortedInstKeys(ctx, me.TContClassID); len(tcontInstKeys) > 0 {
-
-			// FIXME: Ideally the ME configurations on the ONU should constantly be MIB Synced back to the ONU DB
-			// So, as soon as we use up a TCONT Entity on the ONU, the DB at ONU adapter should know that the TCONT
-			// entity is used up via MIB Sync procedure and it will not use it for subsequent TCONT on that ONU.
-			// But, it seems, due to the absence of the constant mib-sync procedure, the TCONT Entities show up as
-			// free even though they are already reserved on the ONU. It seems the mib is synced only once, initially
-			// when the ONU is discovered.
-			/*
-				for _, tcontInstID := range tcontInstKeys {
-					tconInst := oFsm.pOnuDB.GetMe(me.TContClassID, tcontInstID)
-					returnVal := tconInst["AllocId"]
-					if returnVal != nil {
-						if allocID, err := oFsm.pOnuDB.getUint16Attrib(returnVal); err == nil {
-							// If the TCONT Instance ID is set to 0xff or 0xffff, it means it is free to use.
-							if allocID == 0xff || allocID == 0xffff {
-								foundFreeTcontInstID = true
-								oFsm.tcont0ID = uint16(tcontInstID)
-								logger.Debugw("Used TcontId:", log.Fields{"TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
-									"device-id": oFsm.deviceID})
-								break
-							}
-						} else {
-							logger.Errorw("error-converting-alloc-id-to-uint16", log.Fields{"device-id": oFsm.deviceID, "tcont-inst": tcontInstID})
-						}
-					} else {
-						logger.Errorw("error-extracting-alloc-id-attribute", log.Fields{"device-id": oFsm.deviceID, "tcont-inst": tcontInstID})
-					}
-				}
-			*/
-
-			// Ensure that the techProfileID is in a valid range so that we can allocate a free Tcont for it.
-			if oFsm.techProfileID >= tpIDOffset && oFsm.techProfileID < uint8(tpIDOffset+len(tcontInstKeys)) {
-				// For now, as a dirty workaround, use the tpIDOffset to index the TcontEntityID to be used.
-				// The first TP ID for the ONU will get the first TcontEntityID, the next will get second and so on.
-				// Here the assumption is TP ID will always start from 64 (this is also true to Technology Profile Specification) and the
-				// TP ID will increment in single digit
-				oFsm.tcont0ID = tcontInstKeys[oFsm.techProfileID-tpIDOffset]
-				logger.Debugw(ctx, "Used TcontId:", log.Fields{"TcontId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
-					"device-id": oFsm.deviceID})
-			} else {
-				logger.Errorw(ctx, "tech profile id not in valid range", log.Fields{"device-id": oFsm.deviceID, "tp-id": oFsm.techProfileID, "num-tcont": len(tcontInstKeys)})
-				if oFsm.isChanSet() {
-					// indicate processing error/abort to the caller
-					oFsm.chSuccess <- 0
-					oFsm.setChanSet(false) //reset the internal channel state
-				}
-				//reset the state machine to enable usage on subsequent requests
-				_ = aPAFsm.pFsm.Event(aniEvReset)
-				return
-			}
-		} else {
-			logger.Errorw(ctx, "No TCont instances found", log.Fields{"device-id": oFsm.deviceID})
+		pDevEntry := oFsm.pDeviceHandler.getOnuDeviceEntry(ctx, false)
+		if pDevEntry == nil {
+			logger.Errorw(ctx, "No valid OnuDevice - aborting", log.Fields{"device-id": oFsm.deviceID})
 			return
 		}
-		/*
-			if !foundFreeTcontInstID {
-				// This should never happen. If it does, the behavior is unpredictable.
-				logger.Warnw("No free TCONT instances found", log.Fields{"device-id": oFsm.deviceID})
-			}*/
+		tcontInstID, tcontAlreadyExist, err := pDevEntry.allocateFreeTcont(ctx, oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID)
+		if err != nil {
+			logger.Errorw(ctx, "No TCont instances found", log.Fields{"device-id": oFsm.deviceID, "err": err})
+			if oFsm.chanSet {
+				// indicate processing error/abort to the caller
+				oFsm.chSuccess <- 0
+				oFsm.chanSet = false //reset the internal channel state
+			}
+			//reset the state machine to enable usage on subsequent requests
+			_ = aPAFsm.pFsm.Event(aniEvReset)
+			return
+		}
+		oFsm.tcont0ID = tcontInstID
+		oFsm.tcontSetBefore = tcontAlreadyExist
+		logger.Debugw(ctx, "used-tcont-instance-id", log.Fields{"tcont-inst-id": oFsm.tcont0ID,
+			"alloc-id":          oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].tcontParams.allocID,
+			"tcontAlreadyExist": tcontAlreadyExist,
+			"device-id":         oFsm.deviceID})
 
 		// Access critical state with lock
 		oFsm.pUniTechProf.mutexTPState.Lock()
@@ -590,7 +549,17 @@
 	logger.Debugw(ctx, "uniPonAniConfigFsm Tx Set::Tcont", log.Fields{
 		"EntitytId": strconv.FormatInt(int64(oFsm.tcont0ID), 16),
 		"AllocId":   strconv.FormatInt(int64(oFsm.alloc0ID), 16),
-		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
+		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID,
+		"tcontExist": oFsm.tcontSetBefore})
+	//If tcont was set before, then no need to set it again. Let state machine to proceed.
+	if oFsm.tcontSetBefore {
+		go func(aPAFsm *AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.pFsm != nil {
+				_ = aPAFsm.pFsm.Event(aniEvRxTcontsResp)
+			}
+		}(oFsm.pAdaptFsm)
+		return
+	}
 	meParams := me.ParamData{
 		EntityID: oFsm.tcont0ID,
 		Attributes: me.AttributeValueMap{
@@ -1219,6 +1188,11 @@
 		logger.Errorw(ctx, "UniPonAniConfigFsm - Omci SetResponse Error - later: drive FSM to abort state ?",
 			log.Fields{"device-id": oFsm.deviceID, "Error": msgObj.Result})
 		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+
+		//FIXME: If setting TCONT fails we need to revert the DB back. Because of the concurency,
+		//doing it here may cause a data inconsistency. To fix this problem we need to think on running
+		//the FSMs of different UNIs sequentially instead of running them concurrently.
+
 		return
 	}
 	oFsm.mutexPLastTxMeInstance.RLock()
@@ -1523,6 +1497,19 @@
 }
 
 func (oFsm *uniPonAniConfigFsm) performSettingPQs(ctx context.Context) {
+	//If upstream PQs were set before, then no need to set them again. Let state machine to proceed.
+	if oFsm.tcontSetBefore {
+		logger.Debugw(ctx, "No need to set PQs again.", log.Fields{
+			"device-id": oFsm.deviceID, "tcont": oFsm.alloc0ID,
+			"uni-id":         oFsm.pOnuUniPort.uniID,
+			"techProfile-id": oFsm.techProfileID})
+		go func(aPAFsm *AdapterFsm) {
+			if aPAFsm != nil && aPAFsm.pFsm != nil {
+				_ = aPAFsm.pFsm.Event(aniEvRxPrioqsResp)
+			}
+		}(oFsm.pAdaptFsm)
+		return
+	}
 	const cu16StrictPrioWeight uint16 = 0xFFFF
 	//find all upstream PrioQueues related to this T-Cont
 	loQueueMap := ordered_map.NewOrderedMap()