Skip to content

Commit

Permalink
Merge pull request #177 from sasaki77/enum
Browse files Browse the repository at this point in the history
ENH: add enum response to the fieldName function for SEVR and STAT
  • Loading branch information
sasaki77 authored Nov 22, 2024
2 parents 5fdb34b + 704b5f5 commit cf13ea7
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 8 deletions.
34 changes: 31 additions & 3 deletions pkg/archiverappliance/pbparse.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
MessageType_Numeric MessageType = 0
MessageType_String MessageType = 1
MessageType_Array MessageType = 2
MessageType_Enum MessageType = 3
)

type EPICSSeverity int
Expand Down Expand Up @@ -95,6 +96,15 @@ func archiverPBSingleQueryParser(in io.Reader, field models.FieldName, initialCa
values = models.NewStrings(initialCapacity)
case MessageType_Array:
values = models.NewArrays(initialCapacity)
case MessageType_Enum:
switch field {
case models.FIELD_NAME_SEVR_AS_ENUM:
values = models.NewSevirityEnums(initialCapacity)
case models.FIELD_NAME_STAT_AS_ENUM:
values = models.NewStatusEnums(initialCapacity)
default:
return sD, errIllegalFieldName
}
}

continue
Expand Down Expand Up @@ -132,6 +142,17 @@ func archiverPBSingleQueryParser(in io.Reader, field models.FieldName, initialCa
}
t := time.Date(int(year), 1, 1, 0, 0, int(sec), int(nano), time.UTC)
v.Append(value, t)
case *models.Enums:
value, sec, nano, err := getMetaValue(escapedLine, dataType, field)

if err != nil {
return sD, errFailedToParsePBFormat
}
if value == nil {
continue
}
t := time.Date(int(year), 1, 1, 0, 0, int(sec), int(nano), time.UTC)
v.Append(int16(*value), t)
default:
return sD, errIllegalPayloadType
}
Expand Down Expand Up @@ -200,9 +221,11 @@ func getMetaValue(line []byte, dataType pb.PayloadType, field models.FieldName)

var v float64
switch field {
case models.FIELD_NAME_SEVR:
case models.FIELD_NAME_SEVR,
models.FIELD_NAME_SEVR_AS_ENUM:
v = float64(sample.GetSeverity())
case models.FIELD_NAME_STAT:
case models.FIELD_NAME_STAT,
models.FIELD_NAME_STAT_AS_ENUM:
v = float64(sample.GetStatus())
default:
return nil, 0, 0, errIllegalFieldName
Expand Down Expand Up @@ -328,7 +351,12 @@ func initPBMessage(dataType pb.PayloadType) *proto.Message {
}

func getMessageType(dataType pb.PayloadType, field models.FieldName) (MessageType, error) {
if field != models.FIELD_NAME_VAL {
switch field {
case models.FIELD_NAME_SEVR_AS_ENUM,
models.FIELD_NAME_STAT_AS_ENUM:
return MessageType_Enum, nil
case models.FIELD_NAME_SEVR,
models.FIELD_NAME_STAT:
return MessageType_Numeric, nil
}

Expand Down
87 changes: 86 additions & 1 deletion pkg/archiverappliance/pbparse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"
"time"

"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/sasaki77/archiverappliance-datasource/pkg/models"
)

Expand Down Expand Up @@ -416,12 +417,12 @@ func TestParseDataWithSeverity(t *testing.T) {
if math.Abs(*v.Values[0]-testCase.firstVal) > ARCHIVER_FLOAT_PRECISION {
t.Fatalf("First values differ - Wanted: %v Got: %v", testCase.firstVal, v.Values[0])
}

if !v.Times[0].Equal(testCase.firstDate) {
t.Fatalf("Fisrt date differ - Wanted: %v Got: %v", v.Times[0], testCase.firstDate)
}

lastIndex := resultLength - 1

if math.Abs(*v.Values[lastIndex]-testCase.lastVal) > ARCHIVER_FLOAT_PRECISION {
t.Fatalf("last values differ - Wanted: %v Got: %v", testCase.lastVal, v.Values[lastIndex])
}
Expand All @@ -433,6 +434,90 @@ func TestParseDataWithSeverity(t *testing.T) {
}
}

func TestParseDataWithSeverityEnum(t *testing.T) {
var tests = []struct {
name string
field models.FieldName
length int
firstVal data.EnumItemIndex
firstDate time.Time
lastVal data.EnumItemIndex
lastDate time.Time
}{
{
name: "SCALAR_BYTE_sampledata",
field: models.FIELD_NAME_SEVR_AS_ENUM,
length: 366,
firstVal: 0,
lastVal: 0,
firstDate: time.Date(2012, 1, 1, 9, 43, 37, 0, time.UTC),
lastDate: time.Date(2012, 12, 31, 9, 43, 37, 0, time.UTC),
},
{
name: "SCALAR_BYTE_sampledata",
field: models.FIELD_NAME_STAT_AS_ENUM,
length: 366,
firstVal: 0,
lastVal: 0,
firstDate: time.Date(2012, 1, 1, 9, 43, 37, 0, time.UTC),
lastDate: time.Date(2012, 12, 31, 9, 43, 37, 0, time.UTC),
},
}

for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
f, err := os.Open("../test_data/pb/" + testCase.name)

if err != nil {
t.Fatalf("Failed to load test data: %v", err)
}

defer f.Close()

sD, err := archiverPBSingleQueryParser(f, testCase.field, 1000, false)
if err != nil {
t.Fatalf("Failed to parse the data: %v", err)
}

if sD.Name != testCase.name {
t.Fatalf("Names differ - Wanted: %v Got: %v", testCase.name, sD.Name)
}

if sD.PVname != testCase.name {
t.Fatalf("Names differ - Wanted: %v Got: %v", testCase.name, sD.Name)
}

v, ok := sD.Values.(*models.Enums)

if !ok {
t.Fatalf("Single data type is diffrent")
}

resultLength := len(v.Times)
if resultLength != testCase.length {
t.Fatalf("Lengths differ - Wanted: %v Got: %v", testCase.length, resultLength)
}

if v.Values[0] != testCase.firstVal {
t.Fatalf("First values differ - Wanted: %v Got: %v", testCase.firstVal, v.Values[0])
}

if !v.Times[0].Equal(testCase.firstDate) {
t.Fatalf("Fisrt date differ - Wanted: %v Got: %v", v.Times[0], testCase.firstDate)
}

lastIndex := resultLength - 1
if v.Values[lastIndex] != testCase.lastVal {
t.Fatalf("last values differ - Wanted: %v Got: %v", testCase.lastVal, v.Values[lastIndex])
}

if !v.Times[lastIndex].Equal(testCase.lastDate) {
t.Fatalf("Last date differ - Wanted: %v Got: %v", v.Times[lastIndex], testCase.lastDate)
}
})
}
}

func TestParseSevInvalidData(t *testing.T) {
var tests = []struct {
name string
Expand Down
78 changes: 78 additions & 0 deletions pkg/models/enums.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package models

import (
"time"

"github.com/grafana/grafana-plugin-sdk-go/data"
)

type Enums struct {
Times []time.Time
Values []data.EnumItemIndex
EnumConfig data.EnumFieldConfig
}

func NewEnums(length int) *Enums {
return &Enums{
Times: make([]time.Time, 0, length),
Values: make([]data.EnumItemIndex, 0, length),
}
}

func NewSevirityEnums(length int) *Enums {
c := data.EnumFieldConfig{
Text: []string{"NO_ALARM", "MINOR", "MAJOR", "INVALID"},
Color: []string{"rgb(86, 166, 75)", "rgb(255, 120, 10)", "rgb(224, 47, 68)", "rgb(163, 82, 204)"},
}

return &Enums{
Times: make([]time.Time, 0, length),
Values: make([]data.EnumItemIndex, 0, length),
EnumConfig: c,
}
}

func NewStatusEnums(length int) *Enums {
c := data.EnumFieldConfig{
Text: []string{"NO_ALARM", "READ", "WRITE", "HIHI", "HIGH", "LOLO", "LOW", "STATE", "COS", "COMM", "TIMEOUT", "HWLIMIT", "CALC", "SCAN", "LINK", "SOFT", "BAD_SUB", "UDF", "DISABLE", "SIMM", "READ_ACCESS", "WRITE_ACCESS"},
}

return &Enums{
Times: make([]time.Time, 0, length),
Values: make([]data.EnumItemIndex, 0, length),
EnumConfig: c,
}
}

func (v *Enums) Append(val int16, t time.Time) {
v.Values = append(v.Values, data.EnumItemIndex(val))
v.Times = append(v.Times, t)
}

func (v *Enums) ToFields(pvname string, name string, format FormatOption) []*data.Field {
// ToFields doesn't use FormatOption in Enums for now

var fields []*data.Field

//add the time dimension
fields = append(fields, data.NewField("time", nil, v.Times))

// add values
labels := make(data.Labels, 1)
labels["pvname"] = pvname

valueField := data.NewField(name, labels, v.Values)
tc := &data.FieldTypeConfig{Enum: &v.EnumConfig}
valueField.Config = &data.FieldConfig{DisplayName: name, TypeConfig: tc}
fields = append(fields, valueField)

return fields
}

func (v *Enums) Extrapolation(t time.Time) {
if len(v.Values) == 0 {
return
}
v.Values = append(v.Values, v.Values[len(v.Values)-1])
v.Times = append(v.Times, t)
}
8 changes: 5 additions & 3 deletions pkg/models/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ const (
type FieldName string

const (
FIELD_NAME_VAL FieldName = "VAL"
FIELD_NAME_SEVR FieldName = "SEVR"
FIELD_NAME_STAT FieldName = "STAT"
FIELD_NAME_VAL FieldName = "VAL"
FIELD_NAME_SEVR FieldName = "SEVR"
FIELD_NAME_STAT FieldName = "STAT"
FIELD_NAME_SEVR_AS_ENUM FieldName = "SEVR as Enum"
FIELD_NAME_STAT_AS_ENUM FieldName = "STAT as Enum"
)

func (qm ArchiverQueryModel) PickFuncsByCategories(categories []FunctionCategory) []FunctionDescriptorQueryModel {
Expand Down
81 changes: 81 additions & 0 deletions pkg/models/singledata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"testing"
"time"

"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/sasaki77/archiverappliance-datasource/pkg/testhelper"
)

Expand Down Expand Up @@ -442,6 +443,70 @@ func TestToFrameIndexArrayInJST(t *testing.T) {
}
}

func TestToFrameEnum(t *testing.T) {
var tests = []struct {
sD SingleData
name string
pvname string
values []data.EnumItemIndex
dataSize int
}{
{
sD: SingleData{
Name: "testing_name",
PVname: "pvname",
Values: &Enums{
Times: []time.Time{testhelper.TimeHelper(0), testhelper.TimeHelper(1), testhelper.TimeHelper(2)},
Values: []data.EnumItemIndex{1, 2, 3},
EnumConfig: data.EnumFieldConfig{
Text: []string{"NO_ALARM", "MINOR"},
Color: []string{"rgb(86, 166, 75)", "rgb(255, 120, 10)"},
},
},
},
name: "testing_name",
pvname: "pvname",
values: []data.EnumItemIndex{1, 2, 3},
dataSize: 3,
},
}
for idx, testCase := range tests {
testName := fmt.Sprintf("%d: %s", idx, testCase.name)
t.Run(testName, func(t *testing.T) {
result := testCase.sD.ToFrame(FormatOption(FORMAT_TIMESERIES))
if testCase.name != result.Name {
t.Errorf("got %v, want %v", result.Name, testCase.name)
}
if result.Fields[0].Name != "time" {
t.Errorf("got %v, want time", result.Fields[0].Name)
}
if result.Fields[0].Len() != testCase.dataSize {
t.Errorf("got %d, want %d", result.Fields[0].Len(), testCase.dataSize)
}
if testCase.name != result.Fields[1].Config.DisplayName {
t.Errorf("got %v, want %v", result.Fields[1].Config.DisplayName, testCase.name)
}
if testCase.pvname != result.Fields[1].Labels["pvname"] {
t.Errorf("got %v, want %v", result.Fields[1].Labels["pvname"], testCase.pvname)
}
if testCase.name != result.Fields[1].Name {
t.Errorf("got %v, want %v", result.Fields[1].Name, testCase.name)
}
for i := 0; i < result.Fields[1].Len(); i++ {
if testCase.values[i] != result.Fields[1].CopyAt(i) {
t.Errorf("got %v, want %v", result.Fields[1].CopyAt(i), testCase.values[i])
}
}
if result.Fields[1].Config.TypeConfig.Enum.Text[0] != "NO_ALARM" {
t.Errorf("got %v, want NO_ALARM", result.Fields[1].Config.TypeConfig.Enum.Text[0])
}
if result.Fields[1].Config.TypeConfig.Enum.Color[1] != "rgb(255, 120, 10)" {
t.Errorf("got %v, want rgb(255, 120, 10)", result.Fields[1].Config.TypeConfig.Enum.Color[1])
}
})
}
}

func TestExtrapolation(t *testing.T) {
var tests = []struct {
sDIn SingleData
Expand Down Expand Up @@ -513,6 +578,22 @@ func TestExtrapolation(t *testing.T) {
},
},
},
{
sDIn: SingleData{
Values: &Enums{
Times: []time.Time{testhelper.TimeHelper(0)},
Values: []data.EnumItemIndex{1},
},
},
name: "enums extrapolation",
t: testhelper.TimeHelper(5),
sDOut: SingleData{
Values: &Enums{
Times: []time.Time{testhelper.TimeHelper(0), testhelper.TimeHelper(5)},
Values: []data.EnumItemIndex{1, 1},
},
},
},
}
for _, testCase := range tests {
t.Run(testCase.name, func(t *testing.T) {
Expand Down
Loading

0 comments on commit cf13ea7

Please sign in to comment.