[VOL-3542] Support Examine-MDS-handling
- counterpart to verify BBSIM-feature [VOL-3744] BBSIM-ONUs should support MDS-feature according to T-REC-G.988

Change-Id: Iff0579d8b1320beeac570af7e4a84a29f70ab022
diff --git a/VERSION b/VERSION
index a571453..96619c6 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.0-dev154
+1.1.0-dev155
diff --git a/internal/pkg/onuadaptercore/device_handler.go b/internal/pkg/onuadaptercore/device_handler.go
index edcf7ff..de71501 100644
--- a/internal/pkg/onuadaptercore/device_handler.go
+++ b/internal/pkg/onuadaptercore/device_handler.go
@@ -1370,7 +1370,7 @@
 			logger.Debugw(ctx, "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 pDevEntry.isNewOnu() {
 				if err := pMibUlFsm.Event(ulEvResetMib); err != nil {
 					logger.Errorw(ctx, "MibSyncFsm: Can't go to state resetting_mib", log.Fields{"device-id": dh.deviceID, "err": err})
 					return fmt.Errorf("can't go to state resetting_mib: %s", dh.deviceID)
@@ -1381,11 +1381,6 @@
 					return fmt.Errorf("can't go to examine_mds: %s", dh.deviceID)
 				}
 				logger.Debugw(ctx, "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(ctx, "wrong state of MibSyncFsm - want: disabled", log.Fields{"have": string(pMibUlFsm.Current()),
@@ -1765,6 +1760,8 @@
 			// 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
 			_ = dh.deviceReasonUpdate(ctx, drOmciFlowsPushed, !dh.reconciling)
+			// request MDS-value for test and logging purposes
+			dh.pOnuOmciDevice.requestMdsValue(ctx)
 			if dh.reconciling {
 				go dh.reconcileMetrics(ctx)
 			}
diff --git a/internal/pkg/onuadaptercore/mib_sync.go b/internal/pkg/onuadaptercore/mib_sync.go
index dd1d25b..833425c 100644
--- a/internal/pkg/onuadaptercore/mib_sync.go
+++ b/internal/pkg/onuadaptercore/mib_sync.go
@@ -222,13 +222,14 @@
 }
 
 func (oo *OnuDeviceEntry) enterInSyncState(ctx context.Context, e *fsm.Event) {
+	oo.mibLastDbSync = uint32(time.Now().Unix())
 	logger.Debugw(ctx, "MibSync FSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": oo.deviceID})
 	oo.transferSystemEvent(ctx, MibDatabaseSync)
 }
 
 func (oo *OnuDeviceEntry) enterExaminingMdsState(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "MibSync FSM", log.Fields{"Start GetMds processing in State": e.FSM.Current(), "device-id": oo.deviceID})
-	logger.Debug(ctx, "function not implemented yet")
+	oo.requestMdsValue(ctx)
 }
 
 func (oo *OnuDeviceEntry) enterResynchronizingState(ctx context.Context, e *fsm.Event) {
@@ -300,6 +301,7 @@
 			if msgOk {
 				logger.Debugw(ctx, "MibResetResponse Data", log.Fields{"data-fields": msgObj})
 				if msgObj.Result == me.Success {
+					oo.mibDataSyncAdpt = 0
 					// trigger retrieval of VendorId and SerialNumber
 					_ = oo.pMibUploadFsm.pFsm.Event(ulEvGetVendorAndSerial)
 					return
@@ -455,6 +457,19 @@
 				// trigger retrieval of mib template
 				_ = oo.pMibUploadFsm.pFsm.Event(ulEvGetMibTemplate)
 				return nil
+			case "OnuData":
+				mibDataSyncOnu := meAttributes["MibDataSync"].(uint8)
+				logger.Debugw(ctx, "MibSync FSM - GetResponse Data for Onu-Data - MibDataSync", log.Fields{"device-id": oo.deviceID,
+					"mibDataSyncOnu": mibDataSyncOnu, "oo.mibDataSyncAdpt": oo.mibDataSyncAdpt})
+				if oo.pMibUploadFsm.pFsm.Is(ulStExaminingMds) {
+					// Examine MDS value
+					if oo.mibDataSyncAdpt == mibDataSyncOnu {
+						_ = oo.pMibUploadFsm.pFsm.Event(ulEvSuccess)
+					} else {
+						_ = oo.pMibUploadFsm.pFsm.Event(ulEvMismatch)
+					}
+				}
+				return nil
 			}
 		}
 	} else {
@@ -519,6 +534,10 @@
 	return err
 }
 
+func (oo *OnuDeviceEntry) isNewOnu() bool {
+	return oo.mibLastDbSync == 0
+}
+
 func isSupportedClassID(meClassID me.ClassID) bool {
 	for _, v := range supportedClassIds {
 		if v == meClassID {
@@ -614,6 +633,16 @@
 	return nil
 }
 
+func (oo *OnuDeviceEntry) requestMdsValue(ctx context.Context) {
+	logger.Debugw(ctx, "Request MDS value", log.Fields{"device-id": oo.deviceID})
+	requestedAttributes := me.AttributeValueMap{"MibDataSync": ""}
+	meInstance := oo.PDevOmciCC.sendGetMe(log.WithSpanFromContext(context.TODO(), ctx),
+		me.OnuDataClassID, onuDataMeID, requestedAttributes, ConstDefaultOmciTimeout, true)
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oo.PDevOmciCC.pLastTxMeInstance = meInstance
+}
+
 // func (onuDeviceEntry *OnuDeviceEntry) MibTemplateTask() error {
 // 	return errors.New("not_implemented")
 // }
diff --git a/internal/pkg/onuadaptercore/omci_cc.go b/internal/pkg/onuadaptercore/omci_cc.go
index c8ad45f..6d336f4 100644
--- a/internal/pkg/onuadaptercore/omci_cc.go
+++ b/internal/pkg/onuadaptercore/omci_cc.go
@@ -112,6 +112,16 @@
 	pLastTxMeInstance *me.ManagedEntity
 }
 
+var responsesWithMibDataSync = []omci.MessageType{
+	omci.CreateResponseType,
+	omci.DeleteResponseType,
+	omci.SetResponseType,
+	omci.StartSoftwareDownloadResponseType,
+	omci.EndSoftwareDownloadResponseType,
+	omci.ActivateSoftwareResponseType,
+	omci.CommitSoftwareResponseType,
+}
+
 //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,
@@ -278,6 +288,9 @@
 		//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(ctx, omciMsg, &packet, rxCallbackEntry.cbRespChannel)
+		if isResponseWithMibDataSync(omciMsg.MessageType) {
+			oo.pOnuDeviceEntry.incrementMibDataSync(ctx)
+		}
 		// having posted the response the request is regarded as 'done'
 		delete(oo.rxSchedulerMap, omciMsg.TransactionID)
 		oo.mutexRxSchedMap.Unlock()
@@ -2137,3 +2150,12 @@
 		"device-id": oo.deviceID})
 	return nil
 }
+
+func isResponseWithMibDataSync(msgType omci.MessageType) bool {
+	for _, v := range responsesWithMibDataSync {
+		if v == msgType {
+			return true
+		}
+	}
+	return false
+}
diff --git a/internal/pkg/onuadaptercore/onu_device_entry.go b/internal/pkg/onuadaptercore/onu_device_entry.go
index 4873c96..dcc47b0 100644
--- a/internal/pkg/onuadaptercore/onu_device_entry.go
+++ b/internal/pkg/onuadaptercore/onu_device_entry.go
@@ -189,6 +189,7 @@
 	firstSwImageMeID  = 0
 	secondSwImageMeID = 1
 )
+const onuDataMeID = 0
 const onugMeID = 0
 const onu2gMeID = 0
 const ipHostConfigDataMeID = 1
@@ -247,8 +248,10 @@
 	mibDbClass    func(context.Context) error
 	supportedFsms OmciDeviceFsms
 	devState      OnuDeviceEvent
-	// for mibUpload
-	mibAuditDelay uint16
+	// Audit and MDS
+	mibAuditDelay   uint16
+	mibLastDbSync   uint32
+	mibDataSyncAdpt uint8
 
 	// for mibUpload
 	pMibUploadFsm *AdapterFsm //could be handled dynamically and more general as pAdapterFsm - perhaps later
@@ -306,6 +309,8 @@
 	go onuDeviceEntry.mibDbClass(ctx)
 	onuDeviceEntry.mibAuditDelay = onuDeviceEntry.supportedFsms["mib-synchronizer"].auditDelay
 	logger.Debugw(ctx, "MibAudit is set to", log.Fields{"Delay": onuDeviceEntry.mibAuditDelay})
+	onuDeviceEntry.mibLastDbSync = 0
+	onuDeviceEntry.mibDataSyncAdpt = 0
 
 	// Omci related Mib upload sync state machine
 	mibUploadChan := make(chan Message, 2048)
@@ -331,7 +336,10 @@
 			{Name: ulEvSuccess, Src: []string{ulStUploading}, Dst: ulStInSync},
 
 			{Name: ulEvSuccess, Src: []string{ulStExaminingMds}, Dst: ulStInSync},
-			{Name: ulEvMismatch, Src: []string{ulStExaminingMds}, Dst: ulStResynchronizing},
+			// TODO: As long as mib-resynchronizing is not implemented, failed MDS-examination triggers
+			// mib-reset and new provisioning at this point
+			//{Name: ulEvMismatch, Src: []string{ulStExaminingMds}, Dst: ulStResynchronizing},
+			{Name: ulEvMismatch, Src: []string{ulStExaminingMds}, Dst: ulStResettingMib},
 
 			{Name: ulEvAuditMib, Src: []string{ulStInSync}, Dst: ulStAuditing},
 
@@ -754,3 +762,13 @@
 func (oo *OnuDeviceEntry) unlockOnuKVStoreMutex() {
 	oo.onuKVStoreMutex.Unlock()
 }
+
+func (oo *OnuDeviceEntry) incrementMibDataSync(ctx context.Context) {
+	if oo.mibDataSyncAdpt < 255 {
+		oo.mibDataSyncAdpt++
+	} else {
+		// per G.984 and G.988 overflow starts over at 1 given 0 is reserved for reset
+		oo.mibDataSyncAdpt = 1
+	}
+	logger.Debugf(ctx, "mibDataSync updated - mds: %d - device-id: %s", oo.mibDataSyncAdpt, oo.deviceID)
+}