Skip to content

Commit 668c63f

Browse files
authored
Feat: Add Combine by Channel Name (#64)
* working * remove channel_id when combining assets * add datatype * handle multiple types. change option name * add tests * switch back to labels * tooltip change
1 parent 239e1c1 commit 668c63f

4 files changed

Lines changed: 206 additions & 41 deletions

File tree

pkg/plugin/datasource.go

Lines changed: 68 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,34 @@ var ValidSiftGrafanaDataTypes = []string{
6262
// Note: No bytes
6363
}
6464

65+
// Provides a more concise data_type label
66+
func dataTypeLabel(dataType string) string {
67+
switch dataType {
68+
case "CHANNEL_DATA_TYPE_DOUBLE":
69+
return "double"
70+
case "CHANNEL_DATA_TYPE_FLOAT":
71+
return "float"
72+
case "CHANNEL_DATA_TYPE_INT_32":
73+
return "int32"
74+
case "CHANNEL_DATA_TYPE_INT_64":
75+
return "int64"
76+
case "CHANNEL_DATA_TYPE_UINT_32":
77+
return "uint32"
78+
case "CHANNEL_DATA_TYPE_UINT_64":
79+
return "uint64"
80+
case "CHANNEL_DATA_TYPE_BOOL":
81+
return "bool"
82+
case "CHANNEL_DATA_TYPE_STRING":
83+
return "string"
84+
case "CHANNEL_DATA_TYPE_ENUM":
85+
return "enum"
86+
case "CHANNEL_DATA_TYPE_BIT_FIELD":
87+
return "bitfield"
88+
default:
89+
return dataType
90+
}
91+
}
92+
6593
const cacheTimeToLiveMax = time.Minute * 10
6694
const cacheTimeToLiveMin = cacheTimeToLiveMax / 2
6795
const cachePurgeTime = time.Minute * 5
@@ -250,6 +278,7 @@ type queryModel struct {
250278
commonQueryProperties
251279
ChannelDataQueries []channelDataQuery `json:"channelDataQueries"`
252280
CombineRuns bool `json:"combineRuns"`
281+
GroupByChannelName bool `json:"groupByChannelName"`
253282
EnumDisplay string `json:"enumDisplay"`
254283
QueryVersion string `json:"queryVersion"`
255284
AnnotationType string `json:"annotationType"`
@@ -352,7 +381,11 @@ type bitFieldElementValues struct {
352381
}
353382

354383
type frameKey struct {
355-
channelId string
384+
// channelId when groupByChannelName=false, channelName when groupByChannelName=true
385+
channelIdentifier string
386+
// dataType is used to differentiate channels in the event groupByChannelName=true
387+
// and two or more channels share a name, but not a type
388+
dataType string
356389
runId string
357390
bitFieldElementName string
358391
isEnumString bool
@@ -425,9 +458,9 @@ func (d *SiftDatasource) query(ctx context.Context, pCtx backend.PluginContext,
425458
var frame *data.Frame
426459
if fqm.AnnotationType != "" {
427460
// Any annotationType set means we want the flat annotation frame format
428-
frame, err = generateAnnotationFrame(responseData, calculatedChannelKeys, fqm.CombineRuns, fqm.EnumDisplay)
461+
frame, err = generateAnnotationFrame(responseData, calculatedChannelKeys, fqm.CombineRuns, fqm.GroupByChannelName, fqm.EnumDisplay)
429462
} else {
430-
frame, err = generateDataFrame(responseData, calculatedChannelKeys, fqm.CombineRuns, fqm.EnumDisplay)
463+
frame, err = generateDataFrame(responseData, calculatedChannelKeys, fqm.CombineRuns, fqm.GroupByChannelName, fqm.EnumDisplay)
431464
}
432465
if err != nil {
433466
return backend.ErrDataResponse(backend.StatusBadRequest, fmt.Sprintf("error generating data frame: %v", err.Error()))
@@ -583,7 +616,7 @@ func runDataQueries(ctx context.Context, pCtx backend.PluginContext, queries []s
583616
return allData, nil
584617
}
585618

586-
func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys map[string]calculatedChannelKey, combineRuns bool, enumDisplay string) (*data.Frame, error) {
619+
func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys map[string]calculatedChannelKey, combineRuns bool, groupByChannelName bool, enumDisplay string) (*data.Frame, error) {
587620
// create data frame response.
588621
// For an overview on data frames and how grafana handles them:
589622
// https://grafana.com/developers/plugin-tools/introduction/data-frames
@@ -592,11 +625,15 @@ func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys m
592625
allData := map[frameKey]map[int64]any{}
593626

594627
for _, d := range responseData {
628+
channelIdentifier := d.Metadata.Channel.ChannelId
629+
if groupByChannelName {
630+
channelIdentifier = d.Metadata.Channel.Name
631+
}
595632
switch d.Metadata.DataType {
596633
case "CHANNEL_DATA_TYPE_BIT_FIELD":
597634
for _, bitFieldElement := range d.Metadata.Channel.BitFieldElements {
598635
key := frameKey{
599-
channelId: d.Metadata.Channel.ChannelId,
636+
channelIdentifier: channelIdentifier,
600637
bitFieldElementName: bitFieldElement.Name,
601638
}
602639
if !combineRuns {
@@ -610,8 +647,8 @@ func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys m
610647
case "CHANNEL_DATA_TYPE_ENUM":
611648
if enumDisplay == EnumDisplayCombined {
612649
key := frameKey{
613-
channelId: d.Metadata.Channel.ChannelId,
614-
isEnumCombined: true,
650+
channelIdentifier: channelIdentifier,
651+
isEnumCombined: true,
615652
}
616653
if !combineRuns {
617654
key.runId = d.Metadata.Run.RunId
@@ -623,8 +660,8 @@ func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys m
623660
} else {
624661
for _, v := range []bool{true, false} {
625662
key := frameKey{
626-
channelId: d.Metadata.Channel.ChannelId,
627-
isEnumString: v,
663+
channelIdentifier: channelIdentifier,
664+
isEnumString: v,
628665
}
629666
if !combineRuns {
630667
key.runId = d.Metadata.Run.RunId
@@ -637,7 +674,8 @@ func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys m
637674
}
638675
default:
639676
key := frameKey{
640-
channelId: d.Metadata.Channel.ChannelId,
677+
channelIdentifier: channelIdentifier,
678+
dataType: d.Metadata.DataType,
641679
}
642680
if !combineRuns {
643681
key.runId = d.Metadata.Run.RunId
@@ -832,7 +870,7 @@ func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys m
832870
allDataKeys = append(allDataKeys, k)
833871
}
834872
sort.SliceStable(allDataKeys, func(i, j int) bool {
835-
return allDataKeys[i].runId < allDataKeys[j].runId && allDataKeys[i].channelId < allDataKeys[j].channelId
873+
return allDataKeys[i].runId < allDataKeys[j].runId && allDataKeys[i].channelIdentifier < allDataKeys[j].channelIdentifier
836874
})
837875

838876
// Track enum field base names for filtering later
@@ -859,23 +897,31 @@ func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys m
859897
if m.Run.RunId != "" && !combineRuns {
860898
labels["run_id"] = m.Run.RunId
861899
}
862-
if m.Asset.Name != "" {
900+
groupedAcrossAssets := false
901+
if groupByChannelName {
902+
firstAssetId := dataMap[key][0].Metadata.Asset.AssetId
903+
for _, d := range dataMap[key][1:] {
904+
if d.Metadata.Asset.AssetId != firstAssetId {
905+
groupedAcrossAssets = true
906+
break
907+
}
908+
}
909+
}
910+
if m.Asset.Name != "" && !groupedAcrossAssets {
863911
labels["asset"] = m.Asset.Name
864912
}
865-
if m.Asset.AssetId != "" {
913+
if m.Asset.AssetId != "" && !groupedAcrossAssets {
866914
labels["asset_id"] = m.Asset.AssetId
867915
}
868-
if len(m.Channel.BitFieldElements) > 0 {
869-
for _, bitFieldElement := range m.Channel.BitFieldElements {
870-
if key.bitFieldElementName == bitFieldElement.Name {
871-
labels["bitfield_element"] = bitFieldElement.Name
872-
}
873-
}
916+
if key.bitFieldElementName != "" {
917+
labels["bitfield_element"] = key.bitFieldElementName
874918
}
875-
if include_channel_id {
919+
if include_channel_id && !groupedAcrossAssets {
876920
labels["channel_id"] = m.Channel.ChannelId
877921
}
878922

923+
labels["data_type"] = dataTypeLabel(m.DataType)
924+
879925
switch m.DataType {
880926
default:
881927
return nil, fmt.Errorf("unknown data type: %v", m.DataType)
@@ -1006,13 +1052,13 @@ func generateDataFrame(responseData []queryResponseData, calculatedChannelKeys m
10061052

10071053
// generateAnnotationFrame creates an annotation-compatible data frame by reusing generateDataFrame
10081054
// and converting it to a flat row-per-event format with metadata columns.
1009-
func generateAnnotationFrame(responseData []queryResponseData, calculatedChannelKeys map[string]calculatedChannelKey, combineRuns bool, enumDisplay string) (*data.Frame, error) {
1055+
func generateAnnotationFrame(responseData []queryResponseData, calculatedChannelKeys map[string]calculatedChannelKey, combineRuns bool, groupByChannelName bool, enumDisplay string) (*data.Frame, error) {
10101056
// Use combined mode for enums so we get a single "string (number)" field
10111057
annotationEnumDisplay := enumDisplay
10121058
if annotationEnumDisplay == "" || annotationEnumDisplay == EnumDisplayBoth {
10131059
annotationEnumDisplay = EnumDisplayCombined
10141060
}
1015-
sourceFrame, err := generateDataFrame(responseData, calculatedChannelKeys, combineRuns, annotationEnumDisplay)
1061+
sourceFrame, err := generateDataFrame(responseData, calculatedChannelKeys, combineRuns, groupByChannelName, annotationEnumDisplay)
10161062
if err != nil {
10171063
return nil, err
10181064
}

0 commit comments

Comments
 (0)