VOL-4028: Add support for ANI-G Test Response and Result

Change-Id: Iae61554c426937a4ce5e78a0499195f6c3975b78
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index 0f04039..549b663 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -705,31 +705,6 @@
 	}
 }
 
-// Create a TestResponse packet and send it
-func (o *Onu) sendTestResult(msg bbsim.OmciMessage, stream openolt.Openolt_EnableIndicationServer) error {
-	resp, err := omcilib.BuildTestResult(msg.OmciPkt.Data())
-	if err != nil {
-		return err
-	}
-
-	var omciInd openolt.OmciIndication
-	omciInd.IntfId = o.PonPortID
-	omciInd.OnuId = o.ID
-	omciInd.Pkt = resp
-
-	omci := &openolt.Indication_OmciInd{OmciInd: &omciInd}
-	if err := stream.Send(&openolt.Indication{Data: omci}); err != nil {
-		return err
-	}
-	onuLogger.WithFields(log.Fields{
-		"IntfId":       o.PonPortID,
-		"SerialNumber": o.Sn(),
-		"omciPacket":   omciInd.Pkt,
-	}).Tracef("Sent TestResult OMCI message")
-
-	return nil
-}
-
 // handleOmciRequest is responsible to parse the OMCI packets received from the openolt adapter
 // and generate the appropriate response to it
 func (o *Onu) handleOmciRequest(msg bbsim.OmciMessage, stream openolt.Openolt_EnableIndicationServer) error {
@@ -944,23 +919,29 @@
 			}
 		}()
 	case omci.TestRequestType:
-
-		// Test message is special, it requires sending two packets:
-		//     first packet: TestResponse, says whether test was started successully, handled by omci-sim
-		//     second packet, TestResult, reports the result of running the self-test
-		// TestResult can come some time after a TestResponse
-		//     TODO: Implement some delay between the TestResponse and the TestResult
-		isTest, err := omcilib.IsTestRequest(msg.OmciPkt.Data())
-		if (err == nil) && (isTest) {
-			if sendErr := o.sendTestResult(msg, stream); sendErr != nil {
-				onuLogger.WithFields(log.Fields{
-					"IntfId":       o.PonPortID,
-					"OnuId":        o.ID,
-					"SerialNumber": o.Sn(),
-					"omciPacket":   msg.OmciPkt.Data(),
-					"msg":          msg,
-					"err":          sendErr,
-				}).Error("send-TestResult-indication-failed")
+		var classID me.ClassID
+		var omciResult me.Results
+		var instID uint16
+		responsePkt, errResp, classID, instID, omciResult = omcilib.CreateTestResponse(msg.OmciPkt, msg.OmciMsg)
+		// Send TestResult only in case the TestResponse omci result code is me.Success
+		if responsePkt != nil && errResp == nil && omciResult == me.Success {
+			if testResultPkt, err := omcilib.CreateTestResult(classID, instID, msg.OmciMsg.TransactionID); err == nil {
+				// send test results asynchronously
+				go func() {
+					// Send test results after a second to emulate async behavior
+					time.Sleep(1 * time.Second)
+					if testResultPkt != nil {
+						if err := o.sendOmciIndication(testResultPkt, msg.OmciMsg.TransactionID, stream); err != nil {
+							onuLogger.WithFields(log.Fields{
+								"IntfId":          o.PonPortID,
+								"SerialNumber":    o.Sn(),
+								"omciPacket":      testResultPkt,
+								"msg.OmciMsgType": msg.OmciMsg.MessageType,
+								"transCorrId":     msg.OmciMsg.TransactionID,
+							}).Errorf("failed-to-send-omci-message: %v", err)
+						}
+					}
+				}()
 			}
 		}
 	case omci.SynchronizeTimeRequestType:
diff --git a/internal/common/omci/test.go b/internal/common/omci/test.go
index 40398ba..1285203 100644
--- a/internal/common/omci/test.go
+++ b/internal/common/omci/test.go
@@ -18,18 +18,22 @@
 
 import (
 	"errors"
+	"fmt"
 	"github.com/google/gopacket"
 	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	log "github.com/sirupsen/logrus"
+	"math/rand"
 )
 
-func ParseTestRequest(omciPkt gopacket.Packet) (*omci.TestRequest, error) {
-	msgLayer := omciPkt.Layer(omci.LayerTypeGetRequest)
+func ParseOpticalLineSupervisionRequest(omciPkt gopacket.Packet) (*omci.OpticalLineSupervisionTestRequest, error) {
+	msgLayer := omciPkt.Layer(omci.LayerTypeTestRequest)
 	if msgLayer == nil {
 		err := "omci Msg layer could not be detected for LayerTypeTestRequest"
 		omciLogger.Error(err)
 		return nil, errors.New(err)
 	}
-	msgObj, msgOk := msgLayer.(*omci.TestRequest)
+	msgObj, msgOk := msgLayer.(*omci.OpticalLineSupervisionTestRequest)
 	if !msgOk {
 		err := "omci Msg layer could not be assigned for LayerTypeTestRequest"
 		omciLogger.Error(err)
@@ -38,58 +42,71 @@
 	return msgObj, nil
 }
 
-// Return true if msg is an Omci Test Request
-func IsTestRequest(payload []byte) (bool, error) {
-	_, omciMsg, err := ParseOpenOltOmciPacket(payload)
+func CreateTestResponse(omciPkt gopacket.Packet, omciMsg *omci.OMCI) ([]byte, error, me.ClassID, uint16, me.Results) {
+	// TODO: currently supports only OpticalLineSupervisionRequest
+	optSupReq, err := ParseOpticalLineSupervisionRequest(omciPkt)
+
 	if err != nil {
-		return false, err
+		return nil, err, 0, 0, me.ParameterError
 	}
 
-	return omciMsg.MessageType == omci.TestRequestType, nil
+	omciLogger.WithFields(log.Fields{
+		"EntityClass":    optSupReq.EntityClass,
+		"EntityInstance": optSupReq.EntityInstance,
+	}).Trace("received-test-request")
+
+	omciResult := me.Success
+	response := &omci.TestResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    optSupReq.EntityClass,
+			EntityInstance: optSupReq.EntityInstance,
+		},
+		Result: omciResult,
+	}
+
+	pkt, err := Serialize(omci.TestResponseType, response, omciMsg.TransactionID)
+	if err != nil {
+		omciLogger.WithFields(log.Fields{
+			"Err": err,
+		}).Error("cannot-Serialize-test-response")
+		return nil, err, 0, 0, me.ParameterError
+	}
+
+	return pkt, nil, optSupReq.EntityClass, optSupReq.EntityInstance, omciResult
+
 }
 
-func BuildTestResult(payload []byte) ([]byte, error) {
-
-	omciPkt, omciMsg, err := ParseOpenOltOmciPacket(payload)
-
-	//transactionId, deviceId, _, class, instance, _, err := omcisim.ParsePkt(payload)
-
-	if err != nil {
-		return []byte{}, err
+func CreateTestResult(classID me.ClassID, instID uint16, tid uint16) ([]byte, error) {
+	var result gopacket.SerializableLayer
+	switch classID {
+	case me.AniGClassID:
+		result = &omci.OpticalLineSupervisionTestResult{
+			MeBasePacket: omci.MeBasePacket{
+				EntityClass:    classID,
+				EntityInstance: instID,
+			},
+			PowerFeedVoltageType:     uint8(1),
+			PowerFeedVoltage:         uint16(163),
+			ReceivedOpticalPowerType: uint8(3),
+			ReceivedOpticalPower:     uint16(rand.Intn(65000)),
+			MeanOpticalLaunchType:    uint8(5),
+			MeanOpticalLaunch:        uint16(rand.Intn(3000)),
+			LaserBiasCurrentType:     uint8(9),
+			LaserBiasCurrent:         uint16(rand.Intn(10000)),
+			TemperatureType:          uint8(12),
+			Temperature:              uint16(rand.Intn(20000)),
+			GeneralPurposeBuffer:     uint16(0),
+		}
+	default:
+		return nil, fmt.Errorf("unsupported-class-id-for-test-result--class-id-%v", classID)
 	}
 
-	testRequest, err := ParseTestRequest(omciPkt)
+	pkt, err := Serialize(omci.TestResultType, result, tid)
 	if err != nil {
-		return []byte{}, err
+		omciLogger.WithFields(log.Fields{
+			"Err": err,
+		}).Error("cannot-Serialize-test-result")
+		return nil, err
 	}
-
-	// TODO create a TestResponse using omci-lib-go
-	resp := make([]byte, 48)
-	resp[0] = byte(omciMsg.TransactionID >> 8)
-	resp[1] = byte(omciMsg.TransactionID & 0xFF)
-	resp[2] = 27 // Upper nibble 0x0 is fixed (0000), Lower nibbles defines msg type (TestResult=27)
-	resp[3] = byte(omciMsg.DeviceIdentifier)
-	resp[4] = byte(omciMsg.MessageType)
-	resp[5] = byte(omciMsg.MessageType & 0xFF)
-	resp[6] = byte(testRequest.EntityInstance >> 8)
-	resp[7] = byte(testRequest.EntityInstance & 0xFF)
-	// Each of these is a 1-byte code
-	// follow by a 2-byte (high, low) value
-	resp[8] = 1 // power feed voltage
-	resp[9] = 0
-	resp[10] = 123 // 123 mV, 20 mv res --> 6mv
-	resp[11] = 3   // received optical power
-	resp[12] = 1
-	resp[13] = 200 // 456 decibel-microwatts, 0.002 dB res --> 0.912 db-mw
-	resp[14] = 5   // mean optical launch power
-	resp[15] = 3
-	resp[16] = 21 // 789 uA, 0.002 dB res --> 1.578 db-mw
-	resp[17] = 9  // laser bias current
-	resp[18] = 3
-	resp[19] = 244 // 1012 uA, 2uA res --> 505 ua
-	resp[20] = 12  // temperature
-	resp[21] = 38
-	resp[22] = 148 // 9876 deg C, 1/256 resolution --> 38.57 Deg C
-
-	return resp, nil
+	return pkt, nil
 }