VOL-3614  Other multicast ME created after multicast gem detected.

Change-Id: Idc272aa9f6ae3c8da3910031bc226847f253a78f
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
index 70b5f67..2cef989 100644
--- a/internal/pkg/onuadaptercore/omci_ani_config.go
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -307,10 +307,10 @@
 		mapGemPortParams := oFsm.pUniTechProf.mapPonAniConfig[oFsm.uniTpKey].mapGemPortParams
 		oFsm.pUniTechProf.mutexTPState.Unlock()
 
-		loGemPortAttribs := ponAniGemPortAttribs{}
 		//for all TechProfile set GemIndices
-
 		for _, gemEntry := range mapGemPortParams {
+			loGemPortAttribs := ponAniGemPortAttribs{}
+
 			//collect all GemConfigData in a separate Fsm related slice (needed also to avoid mix-up with unsorted mapPonAniConfig)
 
 			if queueInstKeys := oFsm.pOnuDB.getSortedInstKeys(me.PriorityQueueClassID); len(queueInstKeys) > 0 {
@@ -368,23 +368,30 @@
 			loGemPortAttribs.weight = gemEntry.queueWeight
 			loGemPortAttribs.pbitString = gemEntry.pbitString
 			if gemEntry.isMulticast {
+				//TODO this might effectively ignore the for loop starting at line 316
+				loGemPortAttribs.gemPortID = gemEntry.multicastGemPortID
 				loGemPortAttribs.isMulticast = true
 				loGemPortAttribs.multicastGemID = gemEntry.multicastGemPortID
 				loGemPortAttribs.staticACL = gemEntry.staticACL
 				loGemPortAttribs.dynamicACL = gemEntry.dynamicACL
-			}
 
-			logger.Debugw("prio-related GemPort attributes:", log.Fields{
-				"gemPortID":      loGemPortAttribs.gemPortID,
-				"upQueueID":      loGemPortAttribs.upQueueID,
-				"downQueueID":    loGemPortAttribs.downQueueID,
-				"pbitString":     loGemPortAttribs.pbitString,
-				"prioQueueIndex": gemEntry.prioQueueIndex,
-				"isMulticast":    loGemPortAttribs.isMulticast,
-				"multicastGemID": loGemPortAttribs.multicastGemID,
-				"staticACL":      loGemPortAttribs.staticACL,
-				"dynamicACL":     loGemPortAttribs.dynamicACL,
-			})
+				logger.Debugw("Multicast GemPort attributes:", log.Fields{
+					"gemPortID":      loGemPortAttribs.gemPortID,
+					"isMulticast":    loGemPortAttribs.isMulticast,
+					"multicastGemID": loGemPortAttribs.multicastGemID,
+					"staticACL":      loGemPortAttribs.staticACL,
+					"dynamicACL":     loGemPortAttribs.dynamicACL,
+				})
+
+			} else {
+				logger.Debugw("Upstream 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)
 		}
@@ -493,6 +500,7 @@
 }
 
 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 */
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
@@ -509,6 +517,18 @@
 	//assign the GemPorts according to the configured Prio
 	var loPrioGemPortArray [8]uint16
 	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		if gemPortAttribs.isMulticast {
+			logger.Debugw("uniPonAniConfigFsm Port is Multicast, ignoring .1pMapper", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
+		if gemPortAttribs.pbitString == "" {
+			logger.Warnw("uniPonAniConfigFsm PrioString empty string error", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
 		for i := 0; i < 8; i++ {
 			// "lenOfPbitMap(8) - i + 1" will give i-th pbit value from LSB position in the pbit map string
 			if prio, err := strconv.Atoi(string(gemPortAttribs.pbitString[7-i])); err == nil {
@@ -529,39 +549,58 @@
 
 		}
 	}
+
 	var foundIwPtr = false
 	for index, value := range loPrioGemPortArray {
+		meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
 		if value != 0 {
 			foundIwPtr = true
-			meAttribute := fmt.Sprintf("InterworkTpPointerForPBitPriority%d", index)
 			meParams.Attributes[meAttribute] = value
 			logger.Debugw("UniPonAniConfigFsm Set::1pMapper", log.Fields{
 				"for Prio":  index,
 				"IwPtr":     strconv.FormatInt(int64(value), 16),
 				"device-id": oFsm.deviceID})
+		} else {
+			// The null pointer 0xFFFF specifies that frames with the associated priority are to be discarded.
+			meParams.Attributes[meAttribute] = 0xffff
 		}
 	}
+	// The TP type value 0 also indicates bridging mapping, and the TP pointer should be set to 0xFFFF
+	meParams.Attributes["TpPointer"] = 0xffff
 
 	if !foundIwPtr {
-		logger.Errorw("UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
+		logger.Debugw("UniPonAniConfigFsm no GemIwPtr found for .1pMapper - abort", log.Fields{
 			"device-id": oFsm.deviceID})
+		//TODO With multicast is possible that no upstream gem ports are not present in the tech profile,
+		// this reset needs to be performed only if the tech profile provides upstream gem ports but no priority is set
 		//let's reset the state machine in order to release all resources now
+		//pConfigAniStateAFsm := oFsm.pAdaptFsm
+		//if pConfigAniStateAFsm != nil {
+		//	// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		//	go func(aPAFsm *AdapterFsm) {
+		//		if aPAFsm != nil && aPAFsm.pFsm != nil {
+		//			_ = aPAFsm.pFsm.Event(aniEvReset)
+		//		}
+		//	}(pConfigAniStateAFsm)
+		//}
+		//Moving forward the FSM as if the response was received correctly.
 		pConfigAniStateAFsm := oFsm.pAdaptFsm
 		if pConfigAniStateAFsm != nil {
 			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
 			go func(aPAFsm *AdapterFsm) {
 				if aPAFsm != nil && aPAFsm.pFsm != nil {
-					_ = aPAFsm.pFsm.Event(aniEvReset)
+					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapSResp)
 				}
 			}(pConfigAniStateAFsm)
 		}
+	} else {
+		meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(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.pLastTxMeInstance = meInstance
 	}
 
-	meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(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.pLastTxMeInstance = meInstance
 }
 
 func (oFsm *uniPonAniConfigFsm) enterAniConfigDone(e *fsm.Event) {
@@ -775,7 +814,7 @@
 				{ // let the FSM proceed ...
 					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxMbpcdResp)
 				}
-			case "GemPortNetworkCtp", "GemInterworkingTerminationPoint":
+			case "GemPortNetworkCtp", "GemInterworkingTerminationPoint", "MulticastGemInterworkingTerminationPoint":
 				{ // let aniConfig Multi-Id processing proceed by stopping the wait function
 					oFsm.omciMIdsResponseReceived <- true
 				}
@@ -823,7 +862,7 @@
 					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxResetTcontResp)
 				}
 			}
-		case "PriorityQueue":
+		case "PriorityQueue", "MulticastGemInterworkingTerminationPoint":
 			{ // let the PrioQueue init proceed by stopping the wait function
 				oFsm.omciMIdsResponseReceived <- true
 			}
@@ -961,27 +1000,46 @@
 
 		//TODO if the port has only downstream direction the isMulticast flag can be removed.
 		if gemPortAttribs.isMulticast {
-			ipv4MulticastTable := make([]uint8, 12)
-			binary.BigEndian.PutUint16(ipv4MulticastTable[0:], gemPortAttribs.gemPortID)
-			binary.BigEndian.PutUint16(ipv4MulticastTable[2:], 0)
-			// This is the 224.0.0.1 address
-			binary.BigEndian.PutUint32(ipv4MulticastTable[4:], IPToInt32(net.IPv4allsys))
-			// this is the 255.255.255.255 address
-			binary.BigEndian.PutUint32(ipv4MulticastTable[8:], IPToInt32(net.IPv4bcast))
 
 			meParams := me.ParamData{
-				EntityID: gemPortAttribs.gemPortID,
+				EntityID: gemPortAttribs.multicastGemID,
 				Attributes: me.AttributeValueMap{
-					"GemPortNetworkCtpConnectivityPointer": gemPortAttribs.gemPortID,
+					"GemPortNetworkCtpConnectivityPointer": gemPortAttribs.multicastGemID,
 					"InterworkingOption":                   0, // Don't Care
 					"ServiceProfilePointer":                0, // Don't Care
 					"GalProfilePointer":                    galEthernetEID,
-					"Ipv4MulticastAddressTable":            ipv4MulticastTable,
 				},
 			}
 			meInstance := oFsm.pOmciCC.sendCreateMulticastGemIWTPVar(context.TODO(), ConstDefaultOmciTimeout,
 				true, oFsm.pAdaptFsm.commChan, meParams)
 			oFsm.pLastTxMeInstance = meInstance
+			//verify response
+			err := oFsm.waitforOmciResponse()
+			if err != nil {
+				logger.Errorw("GemTP IW multicast create failed, aborting AniConfig FSM!",
+					log.Fields{"device-id": oFsm.deviceID, "GemIndex": gemIndex})
+				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvReset)
+				return
+			}
+			ipv4MulticastTable := make([]uint8, 12)
+			//Gem Port ID
+			binary.BigEndian.PutUint16(ipv4MulticastTable[0:], gemPortAttribs.multicastGemID)
+			//Secondary Key
+			binary.BigEndian.PutUint16(ipv4MulticastTable[2:], 0)
+			// Multicast IP range start This is the 224.0.0.1 address
+			binary.BigEndian.PutUint32(ipv4MulticastTable[4:], IPToInt32(net.IPv4(224, 0, 0, 0)))
+			// MulticastIp range stop
+			binary.BigEndian.PutUint32(ipv4MulticastTable[8:], IPToInt32(net.IPv4(239, 255, 255, 255)))
+
+			meIPV4MCTableParams := me.ParamData{
+				EntityID: gemPortAttribs.multicastGemID,
+				Attributes: me.AttributeValueMap{
+					"Ipv4MulticastAddressTable": ipv4MulticastTable,
+				},
+			}
+			meIPV4MCTableInstance := oFsm.pOmciCC.sendSetMulticastGemIWTPVar(context.TODO(), ConstDefaultOmciTimeout,
+				true, oFsm.pAdaptFsm.commChan, meIPV4MCTableParams)
+			oFsm.pLastTxMeInstance = meIPV4MCTableInstance
 
 		} else {
 			meParams := me.ParamData{
@@ -1020,6 +1078,12 @@
 	//find all upstream PrioQueues related to this T-Cont
 	loQueueMap := ordered_map.NewOrderedMap()
 	for _, gemPortAttribs := range oFsm.gemPortAttribsSlice {
+		if gemPortAttribs.isMulticast {
+			logger.Debugw("uniPonAniConfigFsm Port is Multicast, ignoring PQs", log.Fields{
+				"device-id": oFsm.deviceID, "GemPort": gemPortAttribs.gemPortID,
+				"prioString": gemPortAttribs.pbitString})
+			continue
+		}
 		if gemPortAttribs.qosPolicy == "WRR" {
 			if _, ok := loQueueMap.Get(gemPortAttribs.upQueueID); !ok {
 				//key does not yet exist