Code changes for onu stats fetched from olt VOL-4029

Change-Id: If3838e24a5eefad19a98dd7824cd66653555506f
diff --git a/internal/pkg/commands/devices.go b/internal/pkg/commands/devices.go
index b0f0ab9..e3c1e92 100644
--- a/internal/pkg/commands/devices.go
+++ b/internal/pkg/commands/devices.go
@@ -341,6 +341,16 @@
 		Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
 	} `positional-args:"yes"`
 }
+
+type GetOnuStats struct {
+	ListOutputOptions
+	Args struct {
+		OltId  DeviceId `positional-arg-name:"OLT_DEVICE_ID" required:"yes"`
+		IntfId uint32   `positional-arg-name:"PON_INTF_ID" required:"yes"`
+		OnuId  uint32   `positional-arg-name:"ONU_ID" required:"yes"`
+	} `positional-args:"yes"`
+}
+
 type DeviceOpts struct {
 	List    DeviceList     `command:"list"`
 	Create  DeviceCreate   `command:"create"`
@@ -398,6 +408,7 @@
 		Stats       DeviceGetPortStats `command:"portstats"`
 		UniStatus   UniStatus          `command:"unistatus"`
 		OpticalInfo OnuPonOpticalInfo  `command:"onu_pon_optical_info"`
+		OnuStats    GetOnuStats        `command:"onu_stats"`
 	} `command:"getextval"`
 }
 
@@ -1879,6 +1890,52 @@
 	return nil
 }
 
+func (options *GetOnuStats) Execute(args []string) error {
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+	client := extension.NewExtensionClient(conn)
+
+	singleGetValReq := extension.SingleGetValueRequest{
+		TargetId: string(options.Args.OltId),
+		Request: &extension.GetValueRequest{
+			Request: &extension.GetValueRequest_OnuPonInfo{
+				OnuPonInfo: &extension.GetOnuCountersRequest{
+					IntfId: options.Args.IntfId,
+					OnuId:  options.Args.OnuId,
+				},
+			},
+		},
+	}
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().Grpc.Timeout)
+	defer cancel()
+	rv, err := client.GetExtValue(ctx, &singleGetValReq)
+	if err != nil {
+		Error.Printf("Error getting value on device Id %s,err=%s\n", options.Args.OltId, ErrorToString(err))
+		return err
+	}
+
+	if rv.Response.Status != extension.GetValueResponse_OK {
+		return fmt.Errorf("failed to get onu stats %v", rv.Response.ErrReason.String())
+	}
+	outputFormat := CharReplacer.Replace(options.Format)
+	data, formatStr := buildOnuStatsOutputFormat(rv.GetResponse().GetOnuPonCounters())
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("device-get-onu-status", "format", formatStr)
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      data,
+	}
+	GenerateOutput(&result)
+	return nil
+}
+
 func (options *UniStatus) Execute(args []string) error {
 	conn, err := NewConnection()
 	if err != nil {
diff --git a/internal/pkg/commands/stats.go b/internal/pkg/commands/stats.go
new file mode 100644
index 0000000..e77aa75
--- /dev/null
+++ b/internal/pkg/commands/stats.go
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2021-present Ciena Corporation
+ *
+ * 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 commands
+
+import (
+	"github.com/opencord/voltctl/pkg/model"
+	"github.com/opencord/voltha-protos/v4/go/extension"
+	"strings"
+)
+
+type tagBuilder struct {
+	firstField bool
+	tag        strings.Builder
+}
+
+func NewTagBuilder() tagBuilder {
+	return tagBuilder{
+		firstField: true,
+		tag:        strings.Builder{},
+	}
+}
+func (t *tagBuilder) buildOutputString() string {
+	return t.tag.String()
+}
+func (t *tagBuilder) addFieldInFormat(name string) {
+	if !t.firstField {
+		t.tag.WriteString("\t")
+	}
+	t.firstField = false
+	t.tag.WriteString("{{.")
+	t.tag.WriteString(name)
+	t.tag.WriteString("}}")
+}
+func (t *tagBuilder) addTableInFormat() {
+	t.tag.WriteString("table")
+}
+
+/*
+ * Construct a template format string based on the fields required by the
+ * results.
+ */
+func buildOnuStatsOutputFormat(counters *extension.GetOnuCountersResponse) (model.OnuStats, string) {
+	onuStats := model.OnuStats{}
+	tagBuilder := NewTagBuilder()
+	tagBuilder.addTableInFormat()
+	if counters.IsIntfId != nil {
+		intfId := counters.GetIntfId()
+		onuStats.IntfId = &intfId
+		tagBuilder.addFieldInFormat("IntfId")
+	}
+	if counters.IsOnuId != nil {
+		onuId := counters.GetOnuId()
+		onuStats.OnuId = &onuId
+		tagBuilder.addFieldInFormat("OnuId")
+	}
+	if counters.IsPositiveDrift != nil {
+		positiveDrift := counters.GetPositiveDrift()
+		onuStats.PositiveDrift = &positiveDrift
+		tagBuilder.addFieldInFormat("PositiveDrift")
+	}
+	if counters.IsNegativeDrift != nil {
+		negativeDrift := counters.GetNegativeDrift()
+		onuStats.NegativeDrift = &negativeDrift
+		tagBuilder.addFieldInFormat("NegativeDrift")
+	}
+	if counters.IsDelimiterMissDetection != nil {
+		delimiterMissDet := counters.GetDelimiterMissDetection()
+		onuStats.DelimiterMissDetection = &delimiterMissDet
+		tagBuilder.addFieldInFormat("DelimiterMissDetection")
+	}
+	if counters.IsBipErrors != nil {
+		bipErrors := counters.GetBipErrors()
+		onuStats.BipErrors = &bipErrors
+		tagBuilder.addFieldInFormat("BipErrors")
+	}
+	if counters.IsBipUnits != nil {
+		bipUnits := counters.GetBipUnits()
+		onuStats.BipUnits = &bipUnits
+		tagBuilder.addFieldInFormat("BipUnits")
+	}
+	if counters.IsFecCorrectedSymbols != nil {
+		fecCorrectedSymbols := counters.GetFecCorrectedSymbols()
+		onuStats.FecCorrectedSymbols = &fecCorrectedSymbols
+		tagBuilder.addFieldInFormat("FecCorrectedSymbols")
+	}
+	if counters.IsFecCodewordsCorrected != nil {
+		fecCodewordsCorrected := counters.GetFecCodewordsCorrected()
+		onuStats.FecCodewordsCorrected = &fecCodewordsCorrected
+		tagBuilder.addFieldInFormat("FecCodewordsCorrected")
+	}
+	if counters.IsFecCodewordsUncorrectable != nil {
+		fecCodewordsUncorrectable := counters.GetFecCodewordsUncorrectable()
+		onuStats.FecCodewordsUncorrectable = &fecCodewordsUncorrectable
+		tagBuilder.addFieldInFormat("FecCodewordsUncorrectable")
+	}
+	if counters.IsFecCodewords != nil {
+		fecCodewords := counters.GetFecCodewords()
+		onuStats.FecCodewords = &fecCodewords
+		tagBuilder.addFieldInFormat("FecCodewords")
+	}
+	if counters.IsFecCorrectedUnits != nil {
+		fecCorrectedUnits := counters.GetFecCorrectedUnits()
+		onuStats.FecCorrectedUnits = &fecCorrectedUnits
+		tagBuilder.addFieldInFormat("FecCorrectedUnits")
+	}
+	if counters.IsXgemKeyErrors != nil {
+		xgemKeyErrors := counters.GetXgemKeyErrors()
+		onuStats.XgemKeyErrors = &xgemKeyErrors
+		tagBuilder.addFieldInFormat("XgemKeyErrors")
+	}
+	if counters.IsXgemLoss != nil {
+		xgemLoss := counters.GetXgemLoss()
+		onuStats.XgemLoss = &xgemLoss
+		tagBuilder.addFieldInFormat("XgemLoss")
+	}
+	if counters.IsRxPloamsError != nil {
+		rxPloamsError := counters.GetRxPloamsError()
+		onuStats.RxPloamsError = &rxPloamsError
+		tagBuilder.addFieldInFormat("RxPloamsError")
+	}
+	if counters.IsRxPloamsNonIdle != nil {
+		rxPloamsNonIdle := counters.GetRxPloamsNonIdle()
+		onuStats.RxPloamsNonIdle = &rxPloamsNonIdle
+		tagBuilder.addFieldInFormat("RxPloamsNonIdle")
+	}
+	if counters.IsRxOmci != nil {
+		rxOmci := counters.GetRxOmci()
+		onuStats.RxOmci = &rxOmci
+		tagBuilder.addFieldInFormat("RxOmci")
+	}
+	if counters.IsTxOmci != nil {
+		txOmci := counters.GetTxOmci()
+		onuStats.TxOmci = &txOmci
+		tagBuilder.addFieldInFormat("TxOmci")
+	}
+	if counters.IsRxOmciPacketsCrcError != nil {
+		rxOmciPacketsCrcError := counters.GetRxOmciPacketsCrcError()
+		onuStats.RxOmciPacketsCrcError = &rxOmciPacketsCrcError
+		tagBuilder.addFieldInFormat("RxOmciPacketsCrcError")
+	}
+	if counters.IsRxBytes != nil {
+		rxBytes := counters.GetRxBytes()
+		onuStats.RxBytes = &rxBytes
+		tagBuilder.addFieldInFormat("RxBytes")
+	}
+	if counters.IsRxPackets != nil {
+		rxPackets := counters.GetRxPackets()
+		onuStats.RxPackets = &rxPackets
+		tagBuilder.addFieldInFormat("RxPackets")
+	}
+	if counters.IsTxBytes != nil {
+		txBytes := counters.GetTxBytes()
+		onuStats.TxBytes = &txBytes
+		tagBuilder.addFieldInFormat("TxBytes")
+	}
+	if counters.IsTxPackets != nil {
+		txPackets := counters.GetTxPackets()
+		onuStats.TxPackets = &txPackets
+		tagBuilder.addFieldInFormat("TxPackets")
+	}
+	if counters.IsBerReported != nil {
+		berReported := counters.GetBerReported()
+		onuStats.BerReported = &berReported
+		tagBuilder.addFieldInFormat("BerReported")
+	}
+	if counters.IsLcdgErrors != nil {
+		lcdgErrors := counters.GetLcdgErrors()
+		onuStats.LcdgErrors = &lcdgErrors
+		tagBuilder.addFieldInFormat("LcdgErrors")
+	}
+	if counters.IsRdiErrors != nil {
+		rdiErrors := counters.GetRdiErrors()
+		onuStats.RdiErrors = &rdiErrors
+		tagBuilder.addFieldInFormat("RdiErrors")
+	}
+	if counters.IsTimestamp != nil {
+		timestamp := counters.GetTimestamp()
+		onuStats.Timestamp = &timestamp
+		tagBuilder.addFieldInFormat("Timestamp")
+	}
+	return onuStats, tagBuilder.buildOutputString()
+}