[VOL-5483] Add support at openonu adapter to fetch the current PM data from the supported ONT devices.

Change-Id: I205b4a59b2a4eacff71e2114de8ceb07d7d131e5
Signed-off-by: pnalmas <praneeth.nalmas@radisys.com>
diff --git a/VERSION b/VERSION
index 9359b93..b6ddb73 100755
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.12.33
+2.12.34
diff --git a/go.mod b/go.mod
index 4593cd9..31054fd 100644
--- a/go.mod
+++ b/go.mod
@@ -16,7 +16,7 @@
 	github.com/google/gopacket v1.1.17
 	github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
 	github.com/looplab/fsm v0.2.0
-	github.com/opencord/omci-lib-go/v2 v2.2.3
+	github.com/opencord/omci-lib-go/v2 v2.2.4
 	github.com/opencord/voltha-lib-go/v7 v7.6.6
 	github.com/opencord/voltha-protos/v5 v5.6.5
 	github.com/stretchr/testify v1.8.1
diff --git a/go.sum b/go.sum
index bd8d79f..3d367de 100644
--- a/go.sum
+++ b/go.sum
@@ -928,8 +928,8 @@
 github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
 github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI=
 github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
-github.com/opencord/omci-lib-go/v2 v2.2.3 h1:cE9+67m8HmZmlET57eyQmqOOAJlfvvKrLgiJMiwhSMg=
-github.com/opencord/omci-lib-go/v2 v2.2.3/go.mod h1:o1S/jhDLHNikFU7uG2TR5UOM5KmKlqwLlVncXi0FBYQ=
+github.com/opencord/omci-lib-go/v2 v2.2.4 h1:IwRO6AHkhwETNxyiZmqT+7NzDTco3BmUvbsXrtKCoew=
+github.com/opencord/omci-lib-go/v2 v2.2.4/go.mod h1:o1S/jhDLHNikFU7uG2TR5UOM5KmKlqwLlVncXi0FBYQ=
 github.com/opencord/voltha-lib-go/v7 v7.6.6 h1:TrLo0nykH2MXPngKzTtmL9/u2gjxs98m1/F0m3FWY2U=
 github.com/opencord/voltha-lib-go/v7 v7.6.6/go.mod h1:uGmArLg+nSZd49YXv7ZaD48FA5c+siEFxnyRuldwv6Y=
 github.com/opencord/voltha-protos/v5 v5.6.2/go.mod h1:E/Jn3DNu8VGRBCgIWSSg4sWtTBiNuQGSFvHyNH1XlyM=
diff --git a/internal/pkg/common/omci_cc.go b/internal/pkg/common/omci_cc.go
index 89d1afb..5fbe0e7 100755
--- a/internal/pkg/common/omci_cc.go
+++ b/internal/pkg/common/omci_cc.go
@@ -1647,6 +1647,51 @@
 	return nil
 }
 
+// SendGetCurrentDataME gets current performance monitoring data from ME instance
+func (oo *OmciCC) SendGetCurrentDataME(ctx context.Context, classID me.ClassID, entityID uint16, requestedAttributes me.AttributeValueMap,
+	timeout int, highPrio bool, rxChan chan Message, isExtendedOmci bool) (*me.ManagedEntity, error) {
+
+	tid := oo.GetNextTid(highPrio)
+	logger.Debugw(ctx, "send get-current-data-request-msg", log.Fields{"classID": classID, "device-id": oo.deviceID,
+		"SequNo": strconv.FormatInt(int64(tid), 16)})
+
+	meParams := me.ParamData{
+		EntityID:   entityID,
+		Attributes: requestedAttributes,
+	}
+	var messageSet = omci.BaselineIdent
+	if isExtendedOmci {
+		messageSet = omci.ExtendedIdent
+	}
+	meInstance, omciErr := me.LoadManagedEntityDefinition(classID, meParams)
+	if omciErr.GetError() == nil {
+		meClassIDName := meInstance.GetName()
+		omciLayer, msgLayer, err := oframe.EncodeFrame(meInstance, omci.GetCurrentDataRequestType, oframe.TransactionID(tid), oframe.FrameFormat(messageSet))
+		if err != nil {
+			logger.Errorf(ctx, "Cannot encode instance for get-current-data-request", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+			return nil, err
+		}
+		pkt, err := SerializeOmciLayer(ctx, omciLayer, msgLayer)
+		if err != nil {
+			logger.Errorw(ctx, "Cannot serialize get-current-data-request", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+			return nil, err
+		}
+		omciRxCallbackPair := CallbackPair{
+			CbKey:   tid,
+			CbEntry: CallbackPairEntry{rxChan, oo.receiveOmciResponse, true},
+		}
+		err = oo.Send(ctx, pkt, timeout, CDefaultRetries, highPrio, omciRxCallbackPair)
+		if err != nil {
+			logger.Errorw(ctx, "Cannot send get-current-data-request-msg", log.Fields{"meClassIDName": meClassIDName, "Err": err, "device-id": oo.deviceID})
+			return nil, err
+		}
+		logger.Debugw(ctx, "send get-current-data-request-msg done", log.Fields{"meClassIDName": meClassIDName, "device-id": oo.deviceID})
+		return meInstance, nil
+	}
+	logger.Errorw(ctx, "Cannot generate meDefinition", log.Fields{"classID": classID, "Err": omciErr.GetError(), "device-id": oo.deviceID})
+	return nil, omciErr.GetError()
+}
+
 // SendCreateDot1PMapper creates Ieee8021PMapperServiceProfile ME instance
 func (oo *OmciCC) SendCreateDot1PMapper(ctx context.Context, timeout int, highPrio bool,
 	aInstID uint16, rxChan chan Message) (*me.ManagedEntity, error) {
diff --git a/internal/pkg/core/device_handler.go b/internal/pkg/core/device_handler.go
index 337a97b..1bdeab0 100755
--- a/internal/pkg/core/device_handler.go
+++ b/internal/pkg/core/device_handler.go
@@ -5197,7 +5197,14 @@
 // getONUGEMStatsInfo - Get the GEM PM history data of the request ONT device
 func (dh *deviceHandler) getONUGEMStatsInfo(ctx context.Context) *extension.SingleGetValueResponse {
 	resp := dh.pOnuMetricsMgr.GetONUGEMCounters(ctx)
-	logger.Debugw(ctx, "Received response from AlarmManager for Active Alarms for DeviceEntry", log.Fields{"device-id": dh.DeviceID})
+	logger.Debugw(ctx, "Received response from ONU Metrics Manager for GEM Stats", log.Fields{"device-id": dh.DeviceID})
+	return resp
+}
+
+// getOnuFECStats - Get the GEM PM history data of the request ONT device
+func (dh *deviceHandler) getOnuFECStats(ctx context.Context) *extension.SingleGetValueResponse {
+	resp := dh.pOnuMetricsMgr.GetONUFECCounters(ctx)
+	logger.Debugw(ctx, "Received response from ONU Metrics Manager for FEC Stats", log.Fields{"device-id": dh.DeviceID})
 	return resp
 }
 
diff --git a/internal/pkg/core/openonu.go b/internal/pkg/core/openonu.go
index 0f6a887..e5e31fc 100755
--- a/internal/pkg/core/openonu.go
+++ b/internal/pkg/core/openonu.go
@@ -527,8 +527,11 @@
 			return resp, nil
 		case *extension.GetValueRequest_OnuAllocGemStats:
 			resp := handler.getONUGEMStatsInfo(ctx)
-			logger.Infow(ctx, "Received response for on demand active alarms request ", log.Fields{"response": resp})
+			logger.Infow(ctx, "Received response for on demand GEM counters ", log.Fields{"response": resp})
 			return resp, nil
+		case *extension.GetValueRequest_FecHistory:
+			return handler.getOnuFECStats(ctx), nil
+
 		default:
 			return uniprt.PostUniStatusErrResponse(extension.GetValueResponse_UNSUPPORTED), nil
 		}
diff --git a/internal/pkg/devdb/onu_device_db.go b/internal/pkg/devdb/onu_device_db.go
index d299789..96b8eb9 100755
--- a/internal/pkg/devdb/onu_device_db.go
+++ b/internal/pkg/devdb/onu_device_db.go
@@ -191,12 +191,12 @@
 	}
 }
 
-// GetSortedInstKeys returns a sorted list of all instances of an ME
+// // GetSortedInstKeys returns a sorted list of all instances of an ME from both CommonMeDb and OnuSpecificMeDb
 func (OnuDeviceDB *OnuDeviceDB) GetSortedInstKeys(ctx context.Context, meClassID me.ClassID) []uint16 {
 
 	var meInstKeys []uint16
+	// Check CommonMeDb
 	OnuDeviceDB.CommonMeDb.MeDbLock.RLock()
-	defer OnuDeviceDB.CommonMeDb.MeDbLock.RUnlock()
 	meDb := OnuDeviceDB.CommonMeDb.MeDb
 
 	// Check if the class ID exists in the MeDb map
@@ -206,6 +206,18 @@
 		}
 		logger.Debugw(ctx, "meInstKeys - input order :", log.Fields{"meInstKeys": meInstKeys}) //TODO: delete the line after test phase!
 	}
+	OnuDeviceDB.CommonMeDb.MeDbLock.RUnlock()
+
+	// Check OnuSpecificMeDb
+	OnuDeviceDB.OnuSpecificMeDbLock.RLock()
+	if onuSpecificMap, found := OnuDeviceDB.OnuSpecificMeDb[meClassID]; found {
+		for k := range onuSpecificMap {
+			meInstKeys = append(meInstKeys, k)
+		}
+	}
+	OnuDeviceDB.OnuSpecificMeDbLock.RUnlock()
+
+	// Sort all instance keys
 	sort.Slice(meInstKeys, func(i, j int) bool { return meInstKeys[i] < meInstKeys[j] })
 	logger.Debugw(ctx, "meInstKeys - output order :", log.Fields{"meInstKeys": meInstKeys}) //TODO: delete the line after test phase!
 
diff --git a/internal/pkg/pmmgr/onu_metrics_manager.go b/internal/pkg/pmmgr/onu_metrics_manager.go
index e6eda1c..c333ba4 100755
--- a/internal/pkg/pmmgr/onu_metrics_manager.go
+++ b/internal/pkg/pmmgr/onu_metrics_manager.go
@@ -259,7 +259,7 @@
 )
 
 // Defines the type for generic metric population function
-type groupMetricPopulateFunc func(context.Context, me.ClassID, uint16, me.AttributeValueMap, me.AttributeValueMap, map[string]float32, *int) error
+type groupMetricPopulateFunc func(context.Context, me.ClassID, uint16, me.AttributeValueMap, me.AttributeValueMap, map[string]float32, *int, bool) error
 
 // *** Classical L2 PM Counters end   ***
 
@@ -1233,6 +1233,12 @@
 			me.EthernetFrameExtendedPm64BitClassID:
 			mm.extendedPmMeChan <- meAttributes
 			return nil
+		case me.GemPortNetworkCtpPerformanceMonitoringHistoryDataClassID:
+			mm.l2PmChan <- meAttributes
+			return nil
+		case me.FecPerformanceMonitoringHistoryDataClassID:
+			mm.opticalMetricsChan <- meAttributes
+			return nil
 		default:
 			logger.Errorw(ctx, "unhandled omci get current data response message",
 				log.Fields{"device-id": mm.deviceID, "class-id": msgObj.EntityClass})
@@ -1246,6 +1252,15 @@
 			// AttributeFailure error code, while correctly populating other counters it supports
 			mm.extendedPmMeChan <- meAttributes
 			return nil
+		case me.GemPortNetworkCtpPerformanceMonitoringHistoryDataClassID:
+			// Handle attribute failure for GemPort data similar to Extended PM
+			mm.l2PmChan <- meAttributes
+			return nil
+		case me.FecPerformanceMonitoringHistoryDataClassID:
+			// Handle attribute failure for FEC data similar to Extended PM
+			mm.opticalMetricsChan <- meAttributes
+			return nil
+
 		default:
 			logger.Errorw(ctx, "unhandled omci get current data response message",
 				log.Fields{"device-id": mm.deviceID, "class-id": msgObj.EntityClass})
@@ -1541,13 +1556,13 @@
 
 			case FecHistoryName:
 				for _, entityID := range copyOfEntityIDs {
-					if metricInfo := mm.collectFecHistoryData(ctx, entityID); metricInfo != nil { // upstream
+					if metricInfo := mm.collectFecHistoryData(ctx, entityID, false); metricInfo != nil { // upstream
 						metricInfoSlice = append(metricInfoSlice, metricInfo)
 					}
 				}
 			case GemPortHistoryName:
 				for _, entityID := range copyOfEntityIDs {
-					if metricInfo := mm.collectGemHistoryData(ctx, entityID); metricInfo != nil { // upstream
+					if metricInfo := mm.collectGemHistoryData(ctx, entityID, false); metricInfo != nil { // upstream
 						metricInfoSlice = append(metricInfoSlice, metricInfo)
 					}
 				}
@@ -2037,7 +2052,7 @@
 
 	intervalEndTime := -1
 	ethPMHistData := make(map[string]float32)
-	if err := mm.populateGroupSpecificMetrics(ctx, mEnt, classID, entityID, meAttributes, ethPMHistData, &intervalEndTime); err != nil {
+	if err := mm.populateGroupSpecificMetrics(ctx, mEnt, classID, entityID, meAttributes, ethPMHistData, &intervalEndTime, false); err != nil {
 		return nil
 	}
 
@@ -2074,7 +2089,7 @@
 
 	intervalEndTime := -1
 	ethUniHistData := make(map[string]float32)
-	if err := mm.populateGroupSpecificMetrics(ctx, mEnt, classID, entityID, meAttributes, ethUniHistData, &intervalEndTime); err != nil {
+	if err := mm.populateGroupSpecificMetrics(ctx, mEnt, classID, entityID, meAttributes, ethUniHistData, &intervalEndTime, false); err != nil {
 		return nil
 	}
 
@@ -2089,7 +2104,7 @@
 	return &metricInfo
 }
 
-func (mm *OnuMetricsManager) collectFecHistoryData(ctx context.Context, entityID uint16) *voltha.MetricInformation {
+func (mm *OnuMetricsManager) collectFecHistoryData(ctx context.Context, entityID uint16, isCurrent bool) *voltha.MetricInformation {
 	var mEnt *me.ManagedEntity
 	var omciErr me.OmciErrors
 	var classID me.ClassID
@@ -2104,7 +2119,7 @@
 
 	intervalEndTime := -1
 	fecHistData := make(map[string]float32)
-	if err := mm.populateGroupSpecificMetrics(ctx, mEnt, classID, entityID, meAttributes, fecHistData, &intervalEndTime); err != nil {
+	if err := mm.populateGroupSpecificMetrics(ctx, mEnt, classID, entityID, meAttributes, fecHistData, &intervalEndTime, isCurrent); err != nil {
 		return nil
 	}
 
@@ -2119,7 +2134,7 @@
 	return &metricInfo
 }
 
-func (mm *OnuMetricsManager) collectGemHistoryData(ctx context.Context, entityID uint16) *voltha.MetricInformation {
+func (mm *OnuMetricsManager) collectGemHistoryData(ctx context.Context, entityID uint16, isCurrent bool) *voltha.MetricInformation {
 	var mEnt *me.ManagedEntity
 	var omciErr me.OmciErrors
 	var classID me.ClassID
@@ -2134,7 +2149,7 @@
 
 	intervalEndTime := -1
 	gemHistData := make(map[string]float32)
-	if err := mm.populateGroupSpecificMetrics(ctx, mEnt, classID, entityID, meAttributes, gemHistData, &intervalEndTime); err != nil {
+	if err := mm.populateGroupSpecificMetrics(ctx, mEnt, classID, entityID, meAttributes, gemHistData, &intervalEndTime, isCurrent); err != nil {
 		return nil
 	}
 
@@ -2151,16 +2166,22 @@
 
 // nolint: gocyclo
 func (mm *OnuMetricsManager) populateEthernetBridgeHistoryMetrics(ctx context.Context, classID me.ClassID, entityID uint16,
-	meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, ethPMHistData map[string]float32, intervalEndTime *int) error {
-	//nolint:staticcheck
-	upstream := false
-	if classID == me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID {
-		upstream = true
-	}
+	meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, ethPMHistData map[string]float32, intervalEndTime *int, isCurrent bool) error {
+	upstream := classID == me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID
 	// Insert "IntervalEndTime" as part of the requested attributes as we need this to compare the get responses when get request is multipart
 	requestedAttributes[me.EthernetFramePerformanceMonitoringHistoryDataUpstream_IntervalEndTime] = 0
-	meInstance, err := mm.GetMeInstance(ctx, classID, entityID, requestedAttributes,
-		mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	var meInstance *me.ManagedEntity
+	var err error
+
+	// Use GetCurrentDataMEInstance if isCurrent is true, otherwise use GetMeInstance
+	if isCurrent {
+		meInstance, err = mm.GetCurrentDataMEInstance(ctx, classID, entityID, requestedAttributes,
+			mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	} else {
+		meInstance, err = mm.GetMeInstance(ctx, classID, entityID, requestedAttributes,
+			mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	}
+
 	if err != nil {
 		if CheckMeInstanceStatusCode(err) {
 			return err // Device is being deleted, so we stop processing
@@ -2269,13 +2290,23 @@
 
 // nolint: gocyclo
 func (mm *OnuMetricsManager) populateEthernetUniHistoryMetrics(ctx context.Context, classID me.ClassID, entityID uint16,
-	meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, ethPMUniHistData map[string]float32, intervalEndTime *int) error {
+	meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, ethPMUniHistData map[string]float32, intervalEndTime *int, isCurrent bool) error {
 	// Insert "IntervalEndTime" as part of the requested attributes as we need this to compare the get responses when get request is multipart
 	if _, ok := requestedAttributes["IntervalEndTime"]; !ok {
 		requestedAttributes["IntervalEndTime"] = 0
 	}
-	meInstance, err := mm.GetMeInstance(ctx, classID, entityID, requestedAttributes,
-		mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	var meInstance *me.ManagedEntity
+	var err error
+
+	// Use GetCurrentDataMEInstance if isCurrent is true, otherwise use GetMeInstance
+	if isCurrent {
+		meInstance, err = mm.GetCurrentDataMEInstance(ctx, classID, entityID, requestedAttributes,
+			mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	} else {
+		meInstance, err = mm.GetMeInstance(ctx, classID, entityID, requestedAttributes,
+			mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	}
+
 	if err != nil {
 		if CheckMeInstanceStatusCode(err) {
 			return err // Device is being deleted, so we stop processing
@@ -2378,13 +2409,23 @@
 
 // nolint: gocyclo
 func (mm *OnuMetricsManager) populateFecHistoryMetrics(ctx context.Context, classID me.ClassID, entityID uint16,
-	meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, fecHistData map[string]float32, intervalEndTime *int) error {
+	meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, fecHistData map[string]float32, intervalEndTime *int, isCurrent bool) error {
 	// Insert "IntervalEndTime" as part of the requested attributes as we need this to compare the get responses when get request is multipart
 	if _, ok := requestedAttributes[me.FecPerformanceMonitoringHistoryData_IntervalEndTime]; !ok {
 		requestedAttributes[me.FecPerformanceMonitoringHistoryData_IntervalEndTime] = 0
 	}
-	meInstance, err := mm.GetMeInstance(ctx, classID, entityID, requestedAttributes,
-		mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	var meInstance *me.ManagedEntity
+	var err error
+
+	// Use GetCurrentDataMEInstance if isCurrent is true, otherwise use GetMeInstance
+	if isCurrent {
+		meInstance, err = mm.GetCurrentDataMEInstance(ctx, classID, entityID, requestedAttributes,
+			mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	} else {
+		meInstance, err = mm.GetMeInstance(ctx, classID, entityID, requestedAttributes,
+			mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	}
+
 	if err != nil {
 		if CheckMeInstanceStatusCode(err) {
 			return err // Device is being deleted, so we stop processing
@@ -2400,9 +2441,14 @@
 				logger.Warnw(ctx, "Deleting the device, stopping FEC history metrics collection for the device ", log.Fields{"device-id": mm.deviceID})
 				return fmt.Errorf("deleting the device, stopping FEC history metrics collection for the device %v", mm.deviceID)
 			}
-		case meAttributes = <-mm.l2PmChan:
+		case meAttributes = <-func() chan me.AttributeValueMap {
+			if isCurrent {
+				return mm.opticalMetricsChan
+			}
+			return mm.l2PmChan
+		}():
 			logger.Debugw(ctx, "received fec history data metrics",
-				log.Fields{"device-id": mm.deviceID, "entityID": entityID})
+				log.Fields{"device-id": mm.deviceID, "entityID": entityID, "isCurrent": isCurrent})
 		case <-time.After(mm.pOnuDeviceEntry.GetDevOmciCC().GetMaxOmciTimeoutWithRetries() * time.Second):
 			logger.Errorw(ctx, "timeout waiting for omci-get response for fec history data",
 				log.Fields{"device-id": mm.deviceID, "entityID": entityID})
@@ -2469,6 +2515,25 @@
 	return nil, nil
 }
 
+// GetCurrentDataMEInstance gets current performance monitoring data from ME instance
+func (mm *OnuMetricsManager) GetCurrentDataMEInstance(ctx context.Context, classID me.ClassID, entityID uint16, requestedAttributes me.AttributeValueMap,
+	timeout int, highPrio bool, rxChan chan cmn.Message, isExtendedOmci bool) (*me.ManagedEntity, error) {
+
+	select {
+	case <-mm.pDeviceHandler.GetDeviceDeleteCommChan(ctx):
+		errMsg := "deleting the device, stopping GetCurrentDataMEInstance for the device " + mm.deviceID
+		logger.Warn(ctx, errMsg)
+		return nil, status.Error(codes.NotFound, errMsg)
+	default:
+		if mm.pOnuDeviceEntry.GetDevOmciCC() != nil {
+			meInstance, err := mm.pOnuDeviceEntry.GetDevOmciCC().SendGetCurrentDataME(ctx, classID, entityID, requestedAttributes,
+				mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+			return meInstance, err
+		}
+	}
+	return nil, nil
+}
+
 // CheckMeInstanceStatusCode checked status code if not found returns true
 func CheckMeInstanceStatusCode(err error) bool {
 	if err != nil {
@@ -2482,13 +2547,23 @@
 
 // nolint: gocyclo
 func (mm *OnuMetricsManager) populateGemPortMetrics(ctx context.Context, classID me.ClassID, entityID uint16,
-	meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, gemPortHistData map[string]float32, intervalEndTime *int) error {
+	meAttributes me.AttributeValueMap, requestedAttributes me.AttributeValueMap, gemPortHistData map[string]float32, intervalEndTime *int, isCurrent bool) error {
 	// Insert "IntervalEndTime" is part of the requested attributes as we need this to compare the get responses when get request is multipart
 	if _, ok := requestedAttributes[me.GemPortNetworkCtpPerformanceMonitoringHistoryData_IntervalEndTime]; !ok {
 		requestedAttributes[me.GemPortNetworkCtpPerformanceMonitoringHistoryData_IntervalEndTime] = 0
 	}
-	meInstance, err := mm.GetMeInstance(ctx, classID, entityID, requestedAttributes,
-		mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	var meInstance *me.ManagedEntity
+	var err error
+
+	// Use GetCurrentDataMEInstance if isCurrent is true, otherwise use GetMeInstance
+	if isCurrent {
+		meInstance, err = mm.GetCurrentDataMEInstance(ctx, classID, entityID, requestedAttributes,
+			mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	} else {
+		meInstance, err = mm.GetMeInstance(ctx, classID, entityID, requestedAttributes,
+			mm.pDeviceHandler.GetOmciTimeout(), true, mm.PAdaptFsm.CommChan, mm.isExtendedOmci)
+	}
+
 	if err != nil {
 		if CheckMeInstanceStatusCode(err) {
 			return err // Device is being deleted, so we stop processing
@@ -2661,7 +2736,7 @@
 
 // nolint: unparam
 func (mm *OnuMetricsManager) populateGroupSpecificMetrics(ctx context.Context, mEnt *me.ManagedEntity, classID me.ClassID, entityID uint16,
-	meAttributes me.AttributeValueMap, data map[string]float32, intervalEndTime *int) error {
+	meAttributes me.AttributeValueMap, data map[string]float32, intervalEndTime *int, isCurrent bool) error {
 	var grpFunc groupMetricPopulateFunc
 	switch classID {
 	case me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID, me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID:
@@ -2690,7 +2765,7 @@
 			size = v.Size + size
 		} else { // We exceeded the allow omci get size
 			// Let's collect the attributes via get now and collect remaining in the next iteration
-			if err := grpFunc(ctx, classID, entityID, meAttributes, requestedAttributes, data, intervalEndTime); err != nil {
+			if err := grpFunc(ctx, classID, entityID, meAttributes, requestedAttributes, data, intervalEndTime, isCurrent); err != nil {
 				logger.Errorw(ctx, "error during metric collection",
 					log.Fields{"device-id": mm.deviceID, "entityID": entityID, "err": err})
 				return err
@@ -2701,7 +2776,7 @@
 		}
 	}
 	// Collect the omci get attributes for the last bunch of attributes.
-	if err := grpFunc(ctx, classID, entityID, meAttributes, requestedAttributes, data, intervalEndTime); err != nil {
+	if err := grpFunc(ctx, classID, entityID, meAttributes, requestedAttributes, data, intervalEndTime, isCurrent); err != nil {
 		logger.Errorw(ctx, "error during metric collection",
 			log.Fields{"device-id": mm.deviceID, "entityID": entityID, "err": err})
 		return err
@@ -4130,6 +4205,29 @@
 	mm.deviceDeletionInProgress = true
 }
 
+// safeGetMetricValue safely extracts a metric value from the metrics map
+// Returns 0 if the map is nil, key doesn't exist, or value is 0
+// Logs warnings for missing keys to aid in debugging
+func (mm *OnuMetricsManager) safeGetMetricValue(ctx context.Context, metrics map[string]float32, key string, deviceID string) uint32 {
+	if metrics == nil {
+		logger.Warnw(ctx, "Metrics map is nil", log.Fields{"device-id": deviceID, "key": key})
+		return 0
+	}
+
+	value, exists := metrics[key]
+	if !exists {
+		logger.Warnw(ctx, "Metric key not found in metrics map", log.Fields{"device-id": deviceID, "key": key})
+		return 0
+	}
+
+	if value < 0 {
+		logger.Warnw(ctx, "Negative metric value, setting to 0", log.Fields{"device-id": deviceID, "key": key, "value": value})
+		return 0
+	}
+
+	return uint32(value)
+}
+
 // Obtain the ONU GEM counters for the ONU device
 func (mm *OnuMetricsManager) GetONUGEMCounters(ctx context.Context) *extension.SingleGetValueResponse {
 
@@ -4167,15 +4265,16 @@
 		// Loop through each element in the slice
 		for _, gem := range gemSlice {
 			logger.Debugw(ctx, "Collecting stats for Gem: ", log.Fields{"GEMID": gem})
-			if metricInfo := mm.collectGemHistoryData(ctx, gem); metricInfo != nil {
+			if metricInfo := mm.collectGemHistoryData(ctx, gem, true); metricInfo != nil {
 				logger.Infow(ctx, "Metricinfo for GEM", log.Fields{"GEMID": gem, "metricInfo": metricInfo})
 				gemHistoryData := extension.OnuGemPortHistoryData{}
 				gemHistoryData.GemId = uint32(gem)
-				gemHistoryData.TransmittedGEMFrames = uint32(metricInfo.GetMetrics()["transmitted_gem_frames"])
-				gemHistoryData.ReceivedGEMFrames = uint32(metricInfo.GetMetrics()["received_gem_frames"])
-				gemHistoryData.ReceivedPayloadBytes = uint32(metricInfo.GetMetrics()["received_payload_bytes"])
-				gemHistoryData.TransmittedPayloadBytes = uint32(metricInfo.GetMetrics()["transmitted_payload_bytes"])
-				gemHistoryData.EncryptionKeyErrors = uint32(metricInfo.GetMetrics()["encryption_key_errors"])
+				metrics := metricInfo.GetMetrics()
+				gemHistoryData.TransmittedGEMFrames = mm.safeGetMetricValue(ctx, metrics, "transmitted_gem_frames", mm.deviceID)
+				gemHistoryData.ReceivedGEMFrames = mm.safeGetMetricValue(ctx, metrics, "received_gem_frames", mm.deviceID)
+				gemHistoryData.ReceivedPayloadBytes = mm.safeGetMetricValue(ctx, metrics, "received_payload_bytes", mm.deviceID)
+				gemHistoryData.TransmittedPayloadBytes = mm.safeGetMetricValue(ctx, metrics, "transmitted_payload_bytes", mm.deviceID)
+				gemHistoryData.EncryptionKeyErrors = mm.safeGetMetricValue(ctx, metrics, "encryption_key_errors", mm.deviceID)
 				allocIdGemData.GemPortInfo = append(allocIdGemData.GemPortInfo, &gemHistoryData)
 				logger.Debugw(ctx, " allocIdGemData value ", log.Fields{"AllocIDGemData": allocIdGemData})
 
@@ -4188,3 +4287,54 @@
 	return &resp
 
 }
+
+// Obtain the ONU FEC  counters for the ONU device
+func (mm *OnuMetricsManager) GetONUFECCounters(ctx context.Context) *extension.SingleGetValueResponse {
+
+	resp := extension.SingleGetValueResponse{
+		Response: &extension.GetValueResponse{
+			Status: extension.GetValueResponse_OK,
+			Response: &extension.GetValueResponse_FecHistory{
+				FecHistory: &extension.GetOnuFecHistoryResponse{},
+			},
+		},
+	}
+
+	if mm.GetdeviceDeletionInProgress() {
+		logger.Infow(ctx, "device already deleted, return", log.Fields{"curr-state": mm.PAdaptFsm.PFsm.Current, "deviceID": mm.deviceID})
+		return nil
+	}
+
+	mm.OnuMetricsManagerLock.RLock()
+	defer mm.OnuMetricsManagerLock.RUnlock()
+
+	FecHistoryInstKeys := mm.pOnuDeviceEntry.GetOnuDB().GetSortedInstKeys(ctx, me.AniGClassID)
+	if len(FecHistoryInstKeys) > 0 {
+		firstInstanceKey := FecHistoryInstKeys[0]
+		logger.Debugw(ctx, "First FEC History Instance Key and Length", log.Fields{"InstanceKey": firstInstanceKey, "FecHistoryInstKeys": FecHistoryInstKeys, "Length": len(FecHistoryInstKeys)})
+
+		logger.Debugw(ctx, "Collecting FEC stats for ONU", log.Fields{"EntityID": firstInstanceKey})
+		if metricInfo := mm.collectFecHistoryData(ctx, firstInstanceKey, true); metricInfo != nil {
+			// Process FEC metrics data here
+			logger.Debugw(ctx, "FEC metrics collected successfully", log.Fields{"metricInfo": metricInfo})
+
+			// Populate the response with the collected FEC metrics directly
+			fecResponse := resp.Response.GetFecHistory()
+			metrics := metricInfo.GetMetrics()
+			fecResponse.CorrectedBytes = mm.safeGetMetricValue(ctx, metrics, "corrected_bytes", mm.deviceID)
+			fecResponse.CorrectedCodeWords = mm.safeGetMetricValue(ctx, metrics, "corrected_code_words", mm.deviceID)
+			fecResponse.UncorrectableCodeWords = mm.safeGetMetricValue(ctx, metrics, "uncorrectable_code_words", mm.deviceID)
+			fecResponse.TotalCodeWords = mm.safeGetMetricValue(ctx, metrics, "total_code_words", mm.deviceID)
+			fecResponse.FecSeconds = mm.safeGetMetricValue(ctx, metrics, "fec_seconds", mm.deviceID)
+
+			logger.Debugw(ctx, "FEC response populated successfully",
+				log.Fields{"device-id": mm.deviceID, "correctedBytes": fecResponse.CorrectedBytes,
+					"correctedCodeWords": fecResponse.CorrectedCodeWords, "fecSeconds": fecResponse.FecSeconds})
+		} else {
+			logger.Warnw(ctx, "Failed to collect FEC metrics", log.Fields{"device-id": mm.deviceID, "EntityID": firstInstanceKey})
+		}
+	}
+
+	logger.Debugw(ctx, "FEC History collection completed", log.Fields{"device-id": mm.deviceID})
+	return &resp
+}
diff --git a/vendor/github.com/opencord/omci-lib-go/v2/VERSION b/vendor/github.com/opencord/omci-lib-go/v2/VERSION
index 5859406..530cdd9 100644
--- a/vendor/github.com/opencord/omci-lib-go/v2/VERSION
+++ b/vendor/github.com/opencord/omci-lib-go/v2/VERSION
@@ -1 +1 @@
-2.2.3
+2.2.4
diff --git a/vendor/github.com/opencord/omci-lib-go/v2/meframe/me_getcurrent.go b/vendor/github.com/opencord/omci-lib-go/v2/meframe/me_getcurrent.go
index 2589bbb..de9343d 100644
--- a/vendor/github.com/opencord/omci-lib-go/v2/meframe/me_getcurrent.go
+++ b/vendor/github.com/opencord/omci-lib-go/v2/meframe/me_getcurrent.go
@@ -20,6 +20,7 @@
 import (
 	"errors"
 	"fmt"
+
 	"github.com/google/gopacket"
 	. "github.com/opencord/omci-lib-go/v2"
 	me "github.com/opencord/omci-lib-go/v2/generated"
@@ -29,10 +30,24 @@
 	if opt.frameFormat == ExtendedIdent {
 		return nil, errors.New("extended message set for this message type is not supported")
 	}
-	mask, err := checkAttributeMask(m, opt.attributeMask)
+	// Given mask sent in (could be default of 0xFFFF) get what is allowable.
+	// This will be all allowed if 0xFFFF is passed in, or a subset if a fixed
+	// number of items.
+	maxMask, err := checkAttributeMask(m, opt.attributeMask)
 	if err != nil {
 		return nil, err
 	}
+	// Now scan attributes and reduce mask to only those requested
+	var mask uint16
+	mask, err = calculateAttributeMask(m, maxMask)
+	if err != nil {
+		return nil, err
+	}
+	if mask == 0 {
+		// TODO: Is a GetCurrentData request with no attributes valid?
+		return nil, errors.New("no attributes requested for GetCurrentDataRequest")
+	}
+
 	// Common for all MEs
 	meLayer := &GetCurrentDataRequest{
 		MeBasePacket: MeBasePacket{
@@ -40,14 +55,10 @@
 			EntityInstance: m.GetEntityID(),
 			Extended:       opt.frameFormat == ExtendedIdent,
 		},
+		AttributeMask: mask,
 	}
-	// Get payload space available
-	maxPayload := maxPacketAvailable(m, opt)
 
-	// TODO: Lots of work to do
-
-	fmt.Println(mask, maxPayload)
-	return meLayer, errors.New("todo: Not implemented")
+	return meLayer, nil
 }
 
 func GetCurrentDataResponseFrame(m *me.ManagedEntity, opt options) (gopacket.SerializableLayer, error) {
@@ -58,6 +69,11 @@
 	if err != nil {
 		return nil, err
 	}
+	mask, err = calculateAttributeMask(m, mask)
+	if err != nil {
+		return nil, err
+	}
+
 	// Common for all MEs
 	meLayer := &GetCurrentDataResponse{
 		MeBasePacket: MeBasePacket{
@@ -65,12 +81,66 @@
 			EntityInstance: m.GetEntityID(),
 			Extended:       opt.frameFormat == ExtendedIdent,
 		},
+		Result:        opt.result,
+		AttributeMask: 0,
+		Attributes:    make(me.AttributeValueMap),
 	}
-	// Get payload space available
-	maxPayload := maxPacketAvailable(m, opt)
 
-	// TODO: Lots of work to do
+	if meLayer.Result == me.AttributeFailure {
+		meLayer.UnsupportedAttributeMask = opt.unsupportedMask
+		meLayer.FailedAttributeMask = opt.attrExecutionMask
+	}
 
-	fmt.Println(mask, maxPayload)
-	return meLayer, errors.New("todo: Not implemented")
+	// Encode whatever we can
+	if meLayer.Result == me.Success || meLayer.Result == me.AttributeFailure {
+		// Get payload space available
+		maxPayload := maxPacketAvailable(m, opt)
+		payloadAvailable := int(maxPayload) - 2 - 4 // Less attribute mask and attribute error encoding
+		meDefinition := m.GetManagedEntityDefinition()
+		attrDefs := meDefinition.GetAttributeDefinitions()
+		attrMap := m.GetAttributeValueMap()
+
+		if mask != 0 {
+			// Iterate down the attributes (Attribute 0 is the ManagedEntity ID)
+			var attrIndex uint
+			for attrIndex = 1; attrIndex <= 16; attrIndex++ {
+				// Is this attribute requested
+				if mask&(1<<(16-attrIndex)) != 0 {
+					// Get definitions since we need the name
+					attrDef, ok := attrDefs[attrIndex]
+					if !ok {
+						msg := fmt.Sprintf("Unexpected error, index %v not valued for ME %v",
+							attrIndex, meDefinition.GetName())
+						return nil, errors.New(msg)
+					}
+					var attrValue interface{}
+					attrValue, ok = attrMap[attrDef.Name]
+					if !ok {
+						msg := fmt.Sprintf("Unexpected error, attribute %v not provided in ME %v: %v",
+							attrDef.GetName(), meDefinition.GetName(), m)
+						return nil, errors.New(msg)
+					}
+					// Is space available?
+					if attrDef.Size <= payloadAvailable {
+						// Mark bit handled
+						mask &= ^attrDef.Mask
+						meLayer.AttributeMask |= attrDef.Mask
+						meLayer.Attributes[attrDef.Name] = attrValue
+						payloadAvailable -= attrDef.Size
+
+					} else if opt.failIfTruncated {
+						msg := fmt.Sprintf("out-of-space. Cannot fit attribute %v into GetCurrentDataResponse message",
+							attrDef.GetName())
+						return nil, me.NewMessageTruncatedError(msg)
+					} else {
+						// Add to existing 'failed' mask and update result
+						meLayer.FailedAttributeMask |= attrDef.Mask
+						meLayer.Result = me.AttributeFailure
+					}
+				}
+			}
+		}
+	}
+
+	return meLayer, nil
 }
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 7aa67c6..b6dc866 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -176,7 +176,7 @@
 # github.com/looplab/fsm v0.2.0
 ## explicit; go 1.13
 github.com/looplab/fsm
-# github.com/opencord/omci-lib-go/v2 v2.2.3
+# github.com/opencord/omci-lib-go/v2 v2.2.4
 ## explicit; go 1.16
 github.com/opencord/omci-lib-go/v2
 github.com/opencord/omci-lib-go/v2/generated