[VOL-4472] BBSim receives two "EndSoftwareDownloadRequest" when you abort the download

Change-Id: I0bd99832ccf466699691c7ffe8fc687291f62a59
diff --git a/internal/pkg/onuadaptercore/omci_onu_upgrade.go b/internal/pkg/onuadaptercore/omci_onu_upgrade.go
index 736e5cd..dbdad2b 100644
--- a/internal/pkg/onuadaptercore/omci_onu_upgrade.go
+++ b/internal/pkg/onuadaptercore/omci_onu_upgrade.go
@@ -171,6 +171,7 @@
 	imageIdentifier                  string       //name of the image as used in the adapter
 	mutexIsAwaitingAdapterDlResponse sync.RWMutex
 	chAdapterDlReady                 chan bool
+	chAbortDelayEndSwDl              chan struct{}
 	isWaitingForAdapterDlResponse    bool
 	chOnuDlReady                     chan bool
 	activateImage                    bool
@@ -211,6 +212,7 @@
 	}
 	instFsm.chReceiveExpectedResponse = make(chan bool)
 	instFsm.chAdapterDlReady = make(chan bool)
+	instFsm.chAbortDelayEndSwDl = make(chan struct{})
 	instFsm.chOnuDlReady = make(chan bool)
 	instFsm.chReceiveAbortEndSwDlResponse = make(chan tEndSwDlResponseResult)
 
@@ -547,6 +549,12 @@
 	} else {
 		oFsm.mutexIsAwaitingAdapterDlResponse.RUnlock()
 	}
+	//abort a possible delaying of sending EndSwDl
+	//use asynchronous channel sending to avoid blocking here in case no receiver is waiting
+	select {
+	case oFsm.chAbortDelayEndSwDl <- struct{}{}:
+	default:
+	}
 	//chOnuDlReady is cleared as part of the FSM reset processing (from enterResetting())
 
 	// in any case (even if it might be automatically requested by above cancellation of waiting) ensure resetting the FSM
@@ -700,7 +708,7 @@
 	logger.Debugw(ctx, "OnuUpgradeFsm start downloading sections", log.Fields{
 		"device-id": oFsm.deviceID, "absolute window": oFsm.nextDownloadWindow})
 	//use a background routine to send the multiple download sections frames in a loop
-	//  in order to avoid blocking on synchrounous event calls for the entire (long) processing time
+	//  in order to avoid blocking on synchronous event calls for the entire (long) processing time
 	go oFsm.runSwDlSectionWindow(ctx)
 }
 
@@ -813,17 +821,33 @@
 func (oFsm *OnuUpgradeFsm) enterFinalizeDL(ctx context.Context, e *fsm.Event) {
 	logger.Infow(ctx, "OnuUpgradeFsm finalize DL", log.Fields{
 		"device-id": oFsm.deviceID, "crc": strconv.FormatInt(int64(oFsm.imageCRC), 16), "delay": oFsm.delayEndSwDl})
+	//use a background routine to wait EndSwDlDelay and then send the EndSwDl request
+	//  in order to avoid blocking on synchronous event calls for the complete wait time
+	//  and to allow for state transition before sending the EndSwDl request
+	go oFsm.delayAndSendEndSwDl(ctx)
+}
 
+//delayAndSendEndSwDl ensures a delay before sending the EndSwDl request
+//  may also be aborted by parallel channel reception on chAbortEndSwDl
+func (oFsm *OnuUpgradeFsm) delayAndSendEndSwDl(ctx context.Context) {
 	oFsm.mutexUpgradeParams.RLock()
 	if oFsm.delayEndSwDl {
 		oFsm.mutexUpgradeParams.RUnlock()
-		//give the ONU some time for image evaluation (hoping it does not base that on first EndSwDl itself)
-		// should not be set in case this state is used for real download abort (not yet implemented)
-		time.Sleep(cOmciEndSwDlDelaySeconds * time.Second)
+		//give the ONU some time for image evaluation (hoping it does not only start this evaluation on first EndSwDl itself)
+		logger.Debugw(ctx, "OnuUpgradeFsm delay EndSwDl", log.Fields{"device-id": oFsm.deviceID,
+			"duration_s": cOmciEndSwDlDelaySeconds})
+		select {
+		case <-time.After(cOmciEndSwDlDelaySeconds * time.Second):
+			logger.Warnw(ctx, "OnuUpgradeFsm start sending EndSwDl", log.Fields{
+				"for device-id": oFsm.deviceID, "image-id": oFsm.imageIdentifier})
+		case <-oFsm.chAbortDelayEndSwDl:
+			logger.Debugw(ctx, "OnuUpgradeFsm abort request to send EndSwDl", log.Fields{"device-id": oFsm.deviceID})
+			//according further state transition is ensured by the entity that sent the abort
+			return
+		}
 	} else {
 		oFsm.mutexUpgradeParams.RUnlock()
 	}
-
 	pBaseFsm := oFsm.pAdaptFsm
 	if pBaseFsm == nil {
 		logger.Errorw(ctx, "EndSwDl abort: BaseFsm invalid", log.Fields{"device-id": oFsm.deviceID})
@@ -831,25 +855,25 @@
 		oFsm.volthaDownloadReason = voltha.ImageState_UNKNOWN_ERROR
 		oFsm.mutexUpgradeParams.Unlock()
 		// Can't call FSM Event directly, decoupling it
-		go func(a_pAFsm *AdapterFsm) {
-			_ = a_pAFsm.pFsm.Event(upgradeEvAbort)
-		}(pBaseFsm)
+		_ = pBaseFsm.pFsm.Event(upgradeEvAbort)
 		return
 	}
-	err := oFsm.pOmciCC.sendEndSoftwareDownload(log.WithSpanFromContext(context.Background(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, false,
-		oFsm.pAdaptFsm.commChan, oFsm.inactiveImageMeID, oFsm.origImageLength, oFsm.imageCRC)
+	err := oFsm.pOmciCC.sendEndSoftwareDownload(log.WithSpanFromContext(context.Background(), ctx),
+		oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, false,
+		oFsm.pAdaptFsm.commChan, oFsm.inactiveImageMeID, 0, 0xFFFFFFFF)
 	if err != nil {
 		logger.Errorw(ctx, "EndSwDl abort: error sending EndSwDl", log.Fields{
 			"device-id": oFsm.deviceID, "error": err})
 		oFsm.abortOnOmciError(ctx, true)
 		return
 	}
-	// go waiting for the EndSwDLResponse and check, if the ONU is ready for activation
-	// Can't call FSM Event directly, decoupling it
-	go func(a_pAFsm *AdapterFsm) {
-		_ = a_pAFsm.pFsm.Event(upgradeEvWaitEndDownload)
-	}(pBaseFsm)
-}
+	//from here on sending of EndSwDl(Abort) is not needed anymore (even though response is not yet received)
+	//  this avoids sending of both EndSwDl(Success) and EndSwDl(Abort) when cancellation falls just into this window
+	//  the ONU must theoretically be prepared to receive none of them (in case of OMCI transfer issues) e.g. by timeout
+	oFsm.isEndSwDlOpen = false
+	// wait for the EndSwDLResponse and check, if the ONU is ready for activation
+	_ = pBaseFsm.pFsm.Event(upgradeEvWaitEndDownload)
+} //delayAndSendEndSwDl
 
 func (oFsm *OnuUpgradeFsm) enterWaitEndDL(ctx context.Context, e *fsm.Event) {
 	logger.Infow(ctx, "OnuUpgradeFsm WaitEndDl", log.Fields{
@@ -864,7 +888,6 @@
 			return
 		}
 		oFsm.mutexUpgradeParams.Lock()
-		oFsm.isEndSwDlOpen = false                                         //no need to additionally request abort of download (already finished)
 		oFsm.volthaDownloadReason = voltha.ImageState_IMAGE_REFUSED_BY_ONU //something like 'END_DOWNLOAD_TIMEOUT' would be better (proto)
 		oFsm.mutexUpgradeParams.Unlock()
 		go func(a_pAFsm *AdapterFsm) {