VOL-3835: Add support for reporting L2 PM counters

Change-Id: Ibe64b94d442b7ce83965824beb18d0a281c64a6b
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index 86c70ba..465f2cd 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -728,6 +728,9 @@
 				}).Error("send-TestResult-indication-failed")
 			}
 		}
+	case omci.SynchronizeTimeRequestType:
+		// MDS counter increment is not required for this message type
+		responsePkt, _ = omcilib.CreateSyncTimeResponse(omciPkt, omciMsg)
 	default:
 		log.WithFields(log.Fields{
 			"omciMsgType":  omciMsg.MessageType,
diff --git a/internal/common/omci/get.go b/internal/common/omci/get.go
index 25ba8ed..f6f32df 100644
--- a/internal/common/omci/get.go
+++ b/internal/common/omci/get.go
@@ -25,6 +25,7 @@
 	me "github.com/opencord/omci-lib-go/generated"
 	"github.com/opencord/voltha-protos/v4/go/openolt"
 	log "github.com/sirupsen/logrus"
+	"math/rand"
 	"strconv"
 )
 
@@ -76,6 +77,12 @@
 		response = createAnigResponse(msgObj.AttributeMask, msgObj.EntityInstance)
 	case me.OnuDataClassID:
 		response = createOnuDataResponse(msgObj.AttributeMask, msgObj.EntityInstance, mds)
+	case me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID:
+		response = createEthernetFramePerformanceMonitoringHistoryDataUpstreamResponse(msgObj.AttributeMask, msgObj.EntityInstance)
+	case me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID:
+		response = createEthernetFramePerformanceMonitoringHistoryDataDownstreamResponse(msgObj.AttributeMask, msgObj.EntityInstance)
+	case me.EthernetPerformanceMonitoringHistoryDataClassID:
+		response = createEthernetPerformanceMonitoringHistoryDataResponse(msgObj.AttributeMask, msgObj.EntityInstance)
 	default:
 		omciLogger.WithFields(log.Fields{
 			"EntityClass":    msgObj.EntityClass,
@@ -291,38 +298,167 @@
 	}
 }
 
-func createAnigResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
-	managedEntity, meErr := me.NewAniG(me.ParamData{
+func createEthernetFramePerformanceMonitoringHistoryDataUpstreamResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
+	fmt.Printf("createEthernetFramePerformanceMonitoringHistoryDataUpstreamResponse attribute mask = %v", attributeMask)
+	managedEntity, meErr := me.NewEthernetFramePerformanceMonitoringHistoryDataUpstream(me.ParamData{
 		EntityID: entityID,
 		Attributes: me.AttributeValueMap{
-			"ManagedEntityId":             entityID,
-			"SrIndication":                0,
-			"TotalTcontNumber":            0,
-			"GemBlockLength":              0,
-			"PiggybackDbaReporting":       0,
-			"Deprecated":                  0,
-			"SignalFailThreshold":         0,
-			"SignalDegradeThreshold":      0,
-			"Arc":                         0,
-			"ArcInterval":                 0,
-			"OpticalSignalLevel":          0,
-			"LowerOpticalThreshold":       0,
-			"UpperOpticalThreshold":       0,
-			"OnuResponseTime":             0,
-			"TransmitOpticalLevel":        0,
-			"LowerTransmitPowerThreshold": 0,
-			"UpperTransmitPowerThreshold": 0,
+			"ManagedEntityId":         entityID,
+			"IntervalEndTime":         0, // This ideally should increment by 1 every collection interval, but staying 0 for simulation is Ok for now.
+			"ThresholdData12Id":       0,
+			"DropEvents":              rand.Intn(100),
+			"Octets":                  rand.Intn(100),
+			"Packets":                 rand.Intn(100),
+			"BroadcastPackets":        rand.Intn(100),
+			"MulticastPackets":        rand.Intn(100),
+			"CrcErroredPackets":       rand.Intn(100),
+			"UndersizePackets":        rand.Intn(100),
+			"OversizePackets":         rand.Intn(100),
+			"Packets64Octets":         rand.Intn(100),
+			"Packets65To127Octets":    rand.Intn(100),
+			"Packets128To255Octets":   rand.Intn(100),
+			"Packets256To511Octets":   rand.Intn(100),
+			"Packets512To1023Octets":  rand.Intn(100),
+			"Packets1024To1518Octets": rand.Intn(100),
 		},
 	})
 
 	if meErr.GetError() != nil {
-		omciLogger.Errorf("NewAniG %v", meErr.Error())
+		omciLogger.Errorf("NewEthernetFramePerformanceMonitoringHistoryDataUpstream %v", meErr.Error())
 		return nil
 	}
 
+	// L2 PM counters MEs exceed max allowed OMCI payload size.
+	// So the request/responses are always multipart.
+	// First identify the attributes that are not requested in the current GET request.
+	// Then filter out those attributes from the responses in the current GET response.
+	unwantedAttributeMask := ^attributeMask
+	var i uint16
+	for i = 1; i <= 16; i++ { // 1 and 16 because they are allowed valid min and max index keys in AttributeValueMap.
+		// We leave out 0 because that is ManagedEntity and that is a default IE in the map
+		if (1<<(16-i))&unwantedAttributeMask > 0 {
+			if err := managedEntity.DeleteAttributeByIndex(uint(i)); err != nil {
+				omciLogger.Errorf("error deleting attribute at index=%v, err=%v", i, err)
+			}
+		}
+	}
+
 	return &omci.GetResponse{
 		MeBasePacket: omci.MeBasePacket{
-			EntityClass: me.AniGClassID,
+			EntityClass:    me.EthernetFramePerformanceMonitoringHistoryDataUpstreamClassID,
+			EntityInstance: entityID,
+		},
+		Attributes:    managedEntity.GetAttributeValueMap(),
+		AttributeMask: attributeMask,
+		Result:        me.Success,
+	}
+}
+
+func createEthernetFramePerformanceMonitoringHistoryDataDownstreamResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
+	fmt.Printf("createEthernetFramePerformanceMonitoringHistoryDataDownstreamResponse attribute mask = %v", attributeMask)
+	managedEntity, meErr := me.NewEthernetFramePerformanceMonitoringHistoryDataDownstream(me.ParamData{
+		EntityID: entityID,
+		Attributes: me.AttributeValueMap{
+			"ManagedEntityId":         entityID,
+			"IntervalEndTime":         0, // This ideally should increment by 1 every collection interval, but staying 0 for simulation is Ok for now.
+			"ThresholdData12Id":       0,
+			"DropEvents":              rand.Intn(100),
+			"Octets":                  rand.Intn(100),
+			"Packets":                 rand.Intn(100),
+			"BroadcastPackets":        rand.Intn(100),
+			"MulticastPackets":        rand.Intn(100),
+			"CrcErroredPackets":       rand.Intn(100),
+			"UndersizePackets":        rand.Intn(100),
+			"OversizePackets":         rand.Intn(100),
+			"Packets64Octets":         rand.Intn(100),
+			"Packets65To127Octets":    rand.Intn(100),
+			"Packets128To255Octets":   rand.Intn(100),
+			"Packets256To511Octets":   rand.Intn(100),
+			"Packets512To1023Octets":  rand.Intn(100),
+			"Packets1024To1518Octets": rand.Intn(100),
+		},
+	})
+
+	if meErr.GetError() != nil {
+		omciLogger.Errorf("NewEthernetFramePerformanceMonitoringHistoryDataDownstream %v", meErr.Error())
+		return nil
+	}
+
+	// L2 PM counters MEs exceed max allowed OMCI payload size.
+	// So the request/responses are always multipart.
+	// First identify the attributes that are not requested in the current GET request.
+	// Then filter out those attributes from the responses in the current GET response.
+	unwantedAttributeMask := ^attributeMask
+	var i uint16
+	for i = 1; i <= 16; i++ { // 1 and 16 because they are allowed valid min and max index keys in AttributeValueMap.
+		// We leave out 0 because that is ManagedEntity and that is a default IE in the map
+		if (1<<(16-i))&unwantedAttributeMask > 0 {
+			if err := managedEntity.DeleteAttributeByIndex(uint(i)); err != nil {
+				omciLogger.Errorf("error deleting attribute at index=%v, err=%v", i, err)
+			}
+		}
+	}
+
+	return &omci.GetResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.EthernetFramePerformanceMonitoringHistoryDataDownstreamClassID,
+			EntityInstance: entityID,
+		},
+		Attributes:    managedEntity.GetAttributeValueMap(),
+		AttributeMask: attributeMask,
+		Result:        me.Success,
+	}
+}
+
+func createEthernetPerformanceMonitoringHistoryDataResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
+	fmt.Printf("createEthernetPerformanceMonitoringHistoryDataResponse attribute mask = %v", attributeMask)
+	managedEntity, meErr := me.NewEthernetPerformanceMonitoringHistoryData(me.ParamData{
+		EntityID: entityID,
+		Attributes: me.AttributeValueMap{
+			"ManagedEntityId":                 entityID,
+			"IntervalEndTime":                 0, // This ideally should increment by 1 every collection interval, but staying 0 for simulation is Ok for now.
+			"ThresholdData12Id":               0,
+			"FcsErrors":                       rand.Intn(100),
+			"ExcessiveCollisionCounter":       rand.Intn(100),
+			"LateCollisionCounter":            rand.Intn(100),
+			"FramesTooLong":                   rand.Intn(100),
+			"BufferOverflowsOnReceive":        rand.Intn(100),
+			"BufferOverflowsOnTransmit":       rand.Intn(100),
+			"SingleCollisionFrameCounter":     rand.Intn(100),
+			"MultipleCollisionsFrameCounter":  rand.Intn(100),
+			"SqeCounter":                      rand.Intn(100),
+			"DeferredTransmissionCounter":     rand.Intn(100),
+			"InternalMacTransmitErrorCounter": rand.Intn(100),
+			"CarrierSenseErrorCounter":        rand.Intn(100),
+			"AlignmentErrorCounter":           rand.Intn(100),
+			"InternalMacReceiveErrorCounter":  rand.Intn(100),
+		},
+	})
+
+	if meErr.GetError() != nil {
+		omciLogger.Errorf("NewEthernetPerformanceMonitoringHistoryData %v", meErr.Error())
+		return nil
+	}
+
+	// L2 PM counters MEs exceed max allowed OMCI payload size.
+	// So the request/responses are always multipart.
+	// First identify the attributes that are not requested in the current GET request.
+	// Then filter out those attributes from the responses in the current GET response.
+	unwantedAttributeMask := ^attributeMask
+	var i uint16
+	for i = 1; i <= 16; i++ { // 1 and 16 because they are allowed valid min and max index keys in AttributeValueMap.
+		// We leave out 0 because that is ManagedEntity and that is a default IE in the map
+		if (1<<(16-i))&unwantedAttributeMask > 0 {
+			if err := managedEntity.DeleteAttributeByIndex(uint(i)); err != nil {
+				omciLogger.Errorf("error deleting attribute at index=%v, err=%v", i, err)
+			}
+		}
+	}
+
+	return &omci.GetResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.EthernetPerformanceMonitoringHistoryDataClassID,
+			EntityInstance: entityID,
 		},
 		Attributes:    managedEntity.GetAttributeValueMap(),
 		AttributeMask: attributeMask,
@@ -346,7 +482,48 @@
 
 	return &omci.GetResponse{
 		MeBasePacket: omci.MeBasePacket{
-			EntityClass: me.OnuDataClassID,
+			EntityClass:    me.OnuDataClassID,
+			EntityInstance: entityID,
+		},
+		Attributes:    managedEntity.GetAttributeValueMap(),
+		AttributeMask: attributeMask,
+		Result:        me.Success,
+	}
+}
+
+func createAnigResponse(attributeMask uint16, entityID uint16) *omci.GetResponse {
+	managedEntity, meErr := me.NewAniG(me.ParamData{
+		EntityID: entityID,
+		Attributes: me.AttributeValueMap{
+			"ManagedEntityId":             entityID,
+			"SrIndication":                0,
+			"TotalTcontNumber":            0,
+			"GemBlockLength":              0,
+			"PiggybackDbaReporting":       0,
+			"Deprecated":                  0,
+			"SignalFailThreshold":         0,
+			"SignalDegradeThreshold":      0,
+			"Arc":                         0,
+			"ArcInterval":                 0,
+			"OpticalSignalLevel":          rand.Intn(16000), // generate some random power level than defaulting to 0
+			"LowerOpticalThreshold":       0,
+			"UpperOpticalThreshold":       0,
+			"OnuResponseTime":             0,
+			"TransmitOpticalLevel":        rand.Intn(16000), // generate some random power level than defaulting to 0
+			"LowerTransmitPowerThreshold": 0,
+			"UpperTransmitPowerThreshold": 0,
+		},
+	})
+
+	if meErr.GetError() != nil {
+		omciLogger.Errorf("NewAniG %v", meErr.Error())
+		return nil
+	}
+
+	return &omci.GetResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.AniGClassID,
+			EntityInstance: entityID,
 		},
 		Attributes:    managedEntity.GetAttributeValueMap(),
 		AttributeMask: attributeMask,
diff --git a/internal/common/omci/sync_time.go b/internal/common/omci/sync_time.go
new file mode 100644
index 0000000..a5e6474
--- /dev/null
+++ b/internal/common/omci/sync_time.go
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package omci
+
+import (
+	"errors"
+	"github.com/google/gopacket"
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	log "github.com/sirupsen/logrus"
+)
+
+func ParseSyncTimeRequest(omciPkt gopacket.Packet) (*omci.SynchronizeTimeRequest, error) {
+	msgLayer := omciPkt.Layer(omci.LayerTypeSynchronizeTimeRequest)
+	if msgLayer == nil {
+		err := "omci Msg layer could not be detected for LayerTypeSynchronizeTimeRequest"
+		omciLogger.Error(err)
+		return nil, errors.New(err)
+	}
+	msgObj, msgOk := msgLayer.(*omci.SynchronizeTimeRequest)
+	if !msgOk {
+		err := "omci Msg layer could not be assigned for LayerTypeSynchronizeTimeRequest"
+		omciLogger.Error(err)
+		return nil, errors.New(err)
+	}
+	return msgObj, nil
+}
+
+func CreateSyncTimeResponse(omciPkt gopacket.Packet, omciMsg *omci.OMCI) ([]byte, error) {
+
+	msgObj, err := ParseSyncTimeRequest(omciPkt)
+
+	if err != nil {
+		return nil, err
+	}
+
+	omciLogger.WithFields(log.Fields{
+		"EntityClass":    msgObj.EntityClass,
+		"EntityInstance": msgObj.EntityInstance,
+	}).Trace("received-omci-sync-time-request")
+
+	response := &omci.SynchronizeTimeResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    msgObj.EntityClass,
+			EntityInstance: msgObj.EntityInstance,
+		},
+		Result: me.Success,
+	}
+
+	pkt, err := Serialize(omci.SynchronizeTimeResponseType, response, omciMsg.TransactionID)
+	if err != nil {
+		omciLogger.WithFields(log.Fields{
+			"Err": err,
+		}).Error("cannot-Serialize-SyncTimeResponse")
+		return nil, err
+	}
+
+	return pkt, nil
+
+}