diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44ea487..56f2cde 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,12 @@ Adds support for grafana variables in the SVG URL. Before this was only availabl
URLs. It also corrects the variable change detection logic for all url variables to ensure we
get a fresh fetch when relevant variables change.
+String Data
+-----------
+This adds support for string timeseries for label drives. Color can still be driven by having
+number timeseries for colors and string timeseries for the label text. This is also compatible
+with value mappings.
+
## 1.11.0
Flow Animations
---------------
diff --git a/provisioning/dashboardData/stringData.yaml b/provisioning/dashboardData/stringData.yaml
new file mode 100644
index 0000000..0d0a5f0
--- /dev/null
+++ b/provisioning/dashboardData/stringData.yaml
@@ -0,0 +1,84 @@
+---
+
+#------------------------------------------------------------------------------
+# YAML Aliases to simplify maintenance
+
+anchorLinks:
+ - link: &grafana-home
+ url: "https://grafana.com/"
+ params: "time"
+
+#------------------------------------------------------------------------------
+# Panel Config
+
+test:
+ testDataExtendedZero: true
+ testDataStringData: true
+
+cellIdPreamble: "cell-"
+gradientMode: "hue"
+tagConfig:
+ condensed: false
+cells:
+ drawio:
+ link:
+ url: "https://app.diagrams.net/?p=svgdata"
+ docs:
+ link:
+ url: "https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md"
+ inbox_depth:
+ tags: ['depth']
+ dataRef: "test-data-large-sin"
+ label:
+ dataRef: "test-data-string"
+ valueMappings:
+ - {value: "*0*", text: "*mapped to 0*"}
+ - {value: "*29*", text: "*mapped to 29*"}
+ - {valueMin: 300, valueMax: 600, text: "will never get here as data not a number"}
+ separator: "cr"
+ units: "none"
+ labelColor:
+ gradientMode: "hue"
+ thresholds:
+ - {color: "green", level: 0}
+ - {color: "orange", level: 500}
+ - {color: "red", level: 1000}
+ link: *grafana-home
+ db_transactions:
+ dataRef: "test-data-large-cos"
+ label:
+ separator: "cr"
+ units: "ops"
+ fillColor:
+ thresholds:
+ - {color: "semi-dark-green", level: 0}
+ - {color: "orange", level: 400}
+ - {color: "red", level: 800}
+ link: *grafana-home
+ inbox_transactions:
+ tags: ['db']
+ dataRef: "test-data-large-cos"
+ inbox_workers:
+ tags: ['worker']
+ dataRef: "test-data-small-sin"
+ workers_reads:
+ dataRef: "test-data-small-sin"
+ start_rate:
+ dataRef: "test-data-small-sin"
+ label:
+ separator: "colon"
+ units: "pps"
+ labelColor:
+ thresholds:
+ - {color: "green", level: 0}
+ - {color: "orange", level: 100}
+ link: *grafana-home
+ active_workers:
+ dataRef: "test-data-string"
+ label:
+ separator: "cr"
+ units: "none"
+ labelColor:
+ thresholds:
+ - {color: "#888888", level: 0}
+ - {color: "light-blue", level: 100}
diff --git a/provisioning/dashboardData/stringDataSparse.yaml b/provisioning/dashboardData/stringDataSparse.yaml
new file mode 100644
index 0000000..425a09a
--- /dev/null
+++ b/provisioning/dashboardData/stringDataSparse.yaml
@@ -0,0 +1,85 @@
+---
+
+#------------------------------------------------------------------------------
+# YAML Aliases to simplify maintenance
+
+anchorLinks:
+ - link: &grafana-home
+ url: "https://grafana.com/"
+ params: "time"
+
+#------------------------------------------------------------------------------
+# Panel Config
+
+test:
+ testDataExtendedZero: true
+ testDataStringData: true
+ testDataSparse: true
+
+cellIdPreamble: "cell-"
+gradientMode: "hue"
+tagConfig:
+ condensed: false
+cells:
+ drawio:
+ link:
+ url: "https://app.diagrams.net/?p=svgdata"
+ docs:
+ link:
+ url: "https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md"
+ inbox_depth:
+ tags: ['depth']
+ dataRef: "test-data-large-sin"
+ label:
+ dataRef: "test-data-string"
+ valueMappings:
+ - {value: "*0*", text: "*mapped to 0*"}
+ - {value: "*29*", text: "*mapped to 29*"}
+ - {valueMin: 300, valueMax: 600, text: "will never get here as data not a number"}
+ separator: "cr"
+ units: "none"
+ labelColor:
+ gradientMode: "hue"
+ thresholds:
+ - {color: "green", level: 0}
+ - {color: "orange", level: 500}
+ - {color: "red", level: 1000}
+ link: *grafana-home
+ db_transactions:
+ dataRef: "test-data-large-cos"
+ label:
+ separator: "cr"
+ units: "ops"
+ fillColor:
+ thresholds:
+ - {color: "semi-dark-green", level: 0}
+ - {color: "orange", level: 400}
+ - {color: "red", level: 800}
+ link: *grafana-home
+ inbox_transactions:
+ tags: ['db']
+ dataRef: "test-data-large-cos"
+ inbox_workers:
+ tags: ['worker']
+ dataRef: "test-data-small-sin"
+ workers_reads:
+ dataRef: "test-data-small-sin"
+ start_rate:
+ dataRef: "test-data-small-sin"
+ label:
+ separator: "colon"
+ units: "pps"
+ labelColor:
+ thresholds:
+ - {color: "green", level: 0}
+ - {color: "orange", level: 100}
+ link: *grafana-home
+ active_workers:
+ dataRef: "test-data-string"
+ label:
+ separator: "cr"
+ units: "none"
+ labelColor:
+ thresholds:
+ - {color: "#888888", level: 0}
+ - {color: "light-blue", level: 100}
diff --git a/provisioning/dashboards/stringData.json b/provisioning/dashboards/stringData.json
new file mode 100644
index 0000000..723f279
--- /dev/null
+++ b/provisioning/dashboards/stringData.json
@@ -0,0 +1,130 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "graphTooltip": 0,
+ "id": 34,
+ "links": [],
+ "liveNow": false,
+ "panels": [
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 6,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 5,
+ "options": {
+ "code": {
+ "language": "plaintext",
+ "showLineNumbers": false,
+ "showMiniMap": false
+ },
+ "content": "# String Data \n\nThis shows labels being driven off of string timeseries in all the potential ways:\n- 'Inbox Depth' cell-label has dateRef override for string data so color drive\n also possible.\n- 'Active Workers' cell level dataRef is string data so color drive not possible\neven though defined\n- Value mappings: 'Inbox Depth' has a zero mapping and a 29 mapping defined.\nIt also has a range mapping that can't work because source data is a string.\n- Sparse data: The string data has a different timestamp cadence to the number\ndata and so comes and goes at a different frequency. This results in a a complete\nintermix of no-data / color-data with no label-data / label-data with no color-data / all-data",
+ "mode": "markdown"
+ },
+ "pluginVersion": "10.0.0",
+ "type": "text"
+ },
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 6
+ },
+ "id": 9,
+ "options": {
+ "animationsEnabled": false,
+ "debuggingCtr": {
+ "colorsCtr": 0,
+ "dataCtr": 1,
+ "displaySvgCtr": 2,
+ "mappingsCtr": 0,
+ "timingsCtr": 0
+ },
+ "highlighterEnabled": true,
+ "panZoomEnabled": true,
+ "panelConfig": "---\n\n#------------------------------------------------------------------------------\n# YAML Aliases to simplify maintenance\n\nanchorLinks:\n - link: &grafana-home\n url: \"https://grafana.com/\"\n params: \"time\"\n\n#------------------------------------------------------------------------------\n# Panel Config\n\ntest:\n testDataExtendedZero: true\n testDataStringData: true\n\ncellIdPreamble: \"cell-\"\ngradientMode: \"hue\"\ntagConfig:\n condensed: false\ncells: \n drawio:\n link:\n url: \"https://app.diagrams.net/?p=svgdata\"\n docs:\n link:\n url: \"https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md\"\n inbox_depth:\n tags: ['depth']\n dataRef: \"test-data-large-sin\"\n label:\n dataRef: \"test-data-string\"\n valueMappings:\n - {value: \"*0*\", text: \"*mapped to 0*\"}\n - {value: \"*29*\", text: \"*mapped to 29*\"}\n - {valueMin: 300, valueMax: 600, text: \"will never get here as data not a number\"}\n separator: \"cr\"\n units: \"none\"\n labelColor:\n gradientMode: \"hue\"\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 500}\n - {color: \"red\", level: 1000}\n link: *grafana-home\n db_transactions:\n dataRef: \"test-data-large-cos\"\n label:\n separator: \"cr\"\n units: \"ops\"\n fillColor:\n thresholds:\n - {color: \"semi-dark-green\", level: 0}\n - {color: \"orange\", level: 400}\n - {color: \"red\", level: 800}\n link: *grafana-home\n inbox_transactions:\n tags: ['db']\n dataRef: \"test-data-large-cos\"\n inbox_workers:\n tags: ['worker']\n dataRef: \"test-data-small-sin\"\n workers_reads:\n dataRef: \"test-data-small-sin\"\n start_rate:\n dataRef: \"test-data-small-sin\"\n label:\n separator: \"colon\"\n units: \"pps\"\n labelColor:\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 100}\n link: *grafana-home\n active_workers:\n dataRef: \"test-data-string\"\n label:\n separator: \"cr\"\n units: \"none\"\n labelColor:\n thresholds:\n - {color: \"#888888\", level: 0}\n - {color: \"light-blue\", level: 100}",
+ "siteConfig": "",
+ "svg": "\n\n\n",
+ "testDataEnabled": true,
+ "timeSliderEnabled": true
+ },
+ "title": "Continuous Data",
+ "type": "andrewbmchugh-flow-panel"
+ },
+ {
+ "datasource": {
+ "type": "datasource",
+ "uid": "grafana"
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 12,
+ "y": 6
+ },
+ "id": 10,
+ "options": {
+ "animationsEnabled": false,
+ "debuggingCtr": {
+ "colorsCtr": 0,
+ "dataCtr": 1,
+ "displaySvgCtr": 2,
+ "mappingsCtr": 0,
+ "timingsCtr": 0
+ },
+ "highlighterEnabled": true,
+ "panZoomEnabled": true,
+ "panelConfig": "---\n\n#------------------------------------------------------------------------------\n# YAML Aliases to simplify maintenance\n\nanchorLinks:\n - link: &grafana-home\n url: \"https://grafana.com/\"\n params: \"time\"\n\n#------------------------------------------------------------------------------\n# Panel Config\n\ntest:\n testDataExtendedZero: true\n testDataStringData: true\n testDataSparse: true\n\ncellIdPreamble: \"cell-\"\ngradientMode: \"hue\"\ntagConfig:\n condensed: false\ncells: \n drawio:\n link:\n url: \"https://app.diagrams.net/?p=svgdata\"\n docs:\n link:\n url: \"https://github.com/andymchugh/andrewbmchugh-flow-panel/blob/main/src/README.md\"\n inbox_depth:\n tags: ['depth']\n dataRef: \"test-data-large-sin\"\n label:\n dataRef: \"test-data-string\"\n valueMappings:\n - {value: \"*0*\", text: \"*mapped to 0*\"}\n - {value: \"*29*\", text: \"*mapped to 29*\"}\n - {valueMin: 300, valueMax: 600, text: \"will never get here as data not a number\"}\n separator: \"cr\"\n units: \"none\"\n labelColor:\n gradientMode: \"hue\"\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 500}\n - {color: \"red\", level: 1000}\n link: *grafana-home\n db_transactions:\n dataRef: \"test-data-large-cos\"\n label:\n separator: \"cr\"\n units: \"ops\"\n fillColor:\n thresholds:\n - {color: \"semi-dark-green\", level: 0}\n - {color: \"orange\", level: 400}\n - {color: \"red\", level: 800}\n link: *grafana-home\n inbox_transactions:\n tags: ['db']\n dataRef: \"test-data-large-cos\"\n inbox_workers:\n tags: ['worker']\n dataRef: \"test-data-small-sin\"\n workers_reads:\n dataRef: \"test-data-small-sin\"\n start_rate:\n dataRef: \"test-data-small-sin\"\n label:\n separator: \"colon\"\n units: \"pps\"\n labelColor:\n thresholds:\n - {color: \"green\", level: 0}\n - {color: \"orange\", level: 100}\n link: *grafana-home\n active_workers:\n dataRef: \"test-data-string\"\n label:\n separator: \"cr\"\n units: \"none\"\n labelColor:\n thresholds:\n - {color: \"#888888\", level: 0}\n - {color: \"light-blue\", level: 100}",
+ "siteConfig": "",
+ "svg": "\n\n\n",
+ "testDataEnabled": true,
+ "timeSliderEnabled": true
+ },
+ "title": "Sparse Data",
+ "type": "andrewbmchugh-flow-panel"
+ }
+ ],
+ "refresh": "",
+ "schemaVersion": 38,
+ "style": "dark",
+ "tags": [],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-6h",
+ "to": "now"
+ },
+ "timepicker": {},
+ "timezone": "",
+ "title": "String Data",
+ "version": 1,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/src/components/Config.tsx b/src/components/Config.tsx
index 2408daa..37a3387 100644
--- a/src/components/Config.tsx
+++ b/src/components/Config.tsx
@@ -28,10 +28,11 @@ export type TestConfig = {
testDataSparse: boolean | undefined;
testDataBaseOffset: number | undefined;
testDataExtendedZero: boolean | undefined;
+ testDataStringData: boolean | undefined;
};
export type FlowValueMapping = {
- value: number | null | undefined;
+ value: string | number | null | undefined;
valueMin: number | undefined;
valueMax: number | undefined;
text: string;
diff --git a/src/components/SvgUpdater.tsx b/src/components/SvgUpdater.tsx
index 8e34816..9f024fe 100644
--- a/src/components/SvgUpdater.tsx
+++ b/src/components/SvgUpdater.tsx
@@ -210,7 +210,7 @@ function getCellValue(datapoint: DatapointMode | undefined, tsName: string, tsDa
return value;
}
-export function valueMapping(valueMappings: FlowValueMapping[], value: number | null) {
+export function valueMapping(valueMappings: FlowValueMapping[], value: number | string | null) {
for (const mapping of valueMappings) {
if (mapping.valid) {
let match = false;
@@ -242,7 +242,7 @@ function formatCellValue(cellLabelData: PanelConfigCellLabel, value: number) {
return res;
}
-export function getFlowAnimationState(config: PanelConfigCellFlowAnimation, cellValue: number | null) {
+export function getFlowAnimationState(config: PanelConfigCellFlowAnimation, cellValue: number | string | null) {
if (typeof cellValue === 'number' && config.dataCoherent) {
const absValue = Math.abs(cellValue);
let durationSecs = 0;
@@ -294,7 +294,7 @@ export function svgUpdate(svgHolder: SvgHolder, tsData: TimeSeriesData, highligh
// This function sources the dataRef from the inner paramData and scales it using
// the variables to a threshold seed. If it doesn't exist it returns the passed in
// default.
- function thresholdSeed(datapoint: DatapointMode | undefined, paramData: PanelConfigCellColor | PanelConfigCellFlowAnimation | undefined, defaultSeed: number | null) {
+ function thresholdSeed(datapoint: DatapointMode | undefined, paramData: PanelConfigCellColor | PanelConfigCellFlowAnimation | undefined, defaultSeed: number | string | null) {
if (paramData?.dataRef) {
const cellValue = getCellValue(datapoint, paramData.dataRef, tsData);
return variableThresholdScaleValue(variableValues, cellData, cellValue);
@@ -312,7 +312,7 @@ export function svgUpdate(svgHolder: SvgHolder, tsData: TimeSeriesData, highligh
const cellLabelDatapoint = cellLabelData?.datapoint;
const cellLabelValue = cellLabelData?.dataRef ? getCellValue(cellLabelDatapoint, cellLabelData.dataRef, tsData) : cellValue;
const cellLabelMappedValue = cellLabelData?.valueMappings ? valueMapping(cellLabelData.valueMappings, cellLabelValue) : null;
- const cellLabel = cellLabelMappedValue || (cellLabelData && (typeof cellLabelValue === 'number') ? formatCellValue(cellLabelData, cellLabelValue) : null);
+ const cellLabel = cellLabelMappedValue || (cellLabelData && (typeof cellLabelValue === 'number') ? formatCellValue(cellLabelData, cellLabelValue) : cellLabelValue);
const cellFillColorData = cellData.cellProps.fillColor;
const cellFillColorDatapoint = cellFillColorData?.datapoint;
diff --git a/src/components/TimeSeries.tsx b/src/components/TimeSeries.tsx
index 29d1ac4..3876506 100644
--- a/src/components/TimeSeries.tsx
+++ b/src/components/TimeSeries.tsx
@@ -7,7 +7,7 @@ export type TimeSeries = {
valuesIndex?: number | null;
values: number[];
}
- values: Array;
+ values: Array;
};
export type TimeSeriesData = {
@@ -21,7 +21,7 @@ export function seriesExtend(tsData: TimeSeriesData, timeMin: number, timeMax: n
const dataSparse = testConfig?.testDataSparse;
const dataExtendedZero = testConfig?.testDataExtendedZero;
const baseOffset = typeof testConfig?.testDataBaseOffset === 'number' ? testConfig.testDataBaseOffset : 1;
- const create = function(datapoints: number, scalar: number, fn: (inp: number) => number) {
+ const create = function(datapoints: number, scalar: number, fn: (inp: number) => number, asString: boolean) {
const intervalTime = Math.ceil((timeMax - timeMin) / datapoints);
const intervalValue = 2 * Math.PI / datapoints;
let timeValues = [];
@@ -30,7 +30,9 @@ export function seriesExtend(tsData: TimeSeriesData, timeMin: number, timeMax: n
timeValues.push(timeMin + (i * intervalTime));
const dv = scalar * (baseOffset + fn(i * intervalValue));
- dataValues.push(dataSparse && ((i % 10) > 5) ? null : dataExtendedZero && Math.abs(dv) < 20 ? 0 : dv);
+ const val1 = dataSparse && ((i % 10) > 5) ? null : dataExtendedZero && Math.abs(dv) < 20 ? 0 : dv;
+ const val2 = asString && (typeof val1 === 'number') ? '*' + Math.ceil(val1).toString() + '*' : val1;
+ dataValues.push(val2);
}
return {
time: {values: timeValues},
@@ -38,16 +40,20 @@ export function seriesExtend(tsData: TimeSeriesData, timeMin: number, timeMax: n
};
}
- const dataSets = [
- {name: 'test-data-small-sin', datapoints: 75, scalar: 100, fn: Math.sin},
- {name: 'test-data-large-sin', datapoints: 50, scalar: 500, fn: Math.sin},
- {name: 'test-data-small-cos', datapoints: 60, scalar: 100, fn: Math.cos},
- {name: 'test-data-large-cos', datapoints: 88, scalar: 500, fn: Math.cos},
+ let dataSets = [
+ {name: 'test-data-small-sin', datapoints: 75, scalar: 100, fn: Math.sin, asString: false},
+ {name: 'test-data-large-sin', datapoints: 50, scalar: 500, fn: Math.sin, asString: false},
+ {name: 'test-data-small-cos', datapoints: 60, scalar: 100, fn: Math.cos, asString: false},
+ {name: 'test-data-large-cos', datapoints: 88, scalar: 500, fn: Math.cos, asString: false},
];
+ if (testConfig?.testDataStringData) {
+ dataSets.push({name: 'test-data-string', datapoints: 65, scalar: 500, fn: Math.cos, asString: true});
+ }
+
dataSets.forEach((ds) => {
if (!tsData.ts.get(ds.name)) {
- tsData.ts.set(ds.name, create(ds.datapoints, ds.scalar, ds.fn));
+ tsData.ts.set(ds.name, create(ds.datapoints, ds.scalar, ds.fn, ds.asString));
}
});
}
diff --git a/src/components/Utils.tsx b/src/components/Utils.tsx
index ae1eb86..635fcdd 100644
--- a/src/components/Utils.tsx
+++ b/src/components/Utils.tsx
@@ -193,9 +193,9 @@ export function variableThresholdScalarsInit(
}
}
-export function variableThresholdScaleValue(variableValues: Map, cellData: SvgCell, value: number | null) {
+export function variableThresholdScaleValue(variableValues: Map, cellData: SvgCell, value: number | string | null) {
if (typeof value !== 'number') {
- return null;
+ return value;
}
let scalar = 1.0;
cellData.variableThresholdScalars.forEach((rules, variableName) => {
diff --git a/yaml_defs/panelConfig.yaml b/yaml_defs/panelConfig.yaml
index 2def1c0..a6a1062 100644
--- a/yaml_defs/panelConfig.yaml
+++ b/yaml_defs/panelConfig.yaml
@@ -28,6 +28,9 @@ test:
# testing in this region.
testDataExtendedZero: false
+ # Version 1.12.0 onwards: When set test data values are extended with a string-data timeseries.
+ testDataStringData: false
+
#------------------------------------------------------------------------------
# Grafana Variable Scalars