@@ -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+
6593const cacheTimeToLiveMax = time .Minute * 10
6694const cacheTimeToLiveMin = cacheTimeToLiveMax / 2
6795const 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
354383type 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