Commit 49be7d1ec3eac3a98dcc7cfd05498cbefcf9957e
1 parent
f3a01f21
Add support ticks to digital gauge
Showing
2 changed files
with
262 additions
and
70 deletions
... | ... | @@ -51,6 +51,10 @@ let defaultDigitalGaugeOptions = Object.assign({}, canvasGauges.GenericOptions, |
51 | 51 | |
52 | 52 | neonGlowBrightness: 0, |
53 | 53 | |
54 | + colorTicks: 'gray', | |
55 | + tickWidth: 4, | |
56 | + ticks: [], | |
57 | + | |
54 | 58 | isMobile: false |
55 | 59 | |
56 | 60 | }); |
... | ... | @@ -133,6 +137,13 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { |
133 | 137 | } |
134 | 138 | } |
135 | 139 | |
140 | + options.ticksValue = []; | |
141 | + for(let i = 0; i < options.ticks.length; i++){ | |
142 | + if(options.ticks[i] !== null){ | |
143 | + options.ticksValue.push(CanvasDigitalGauge.normalizeValue(options.ticks[i], options.minValue, options.maxValue)) | |
144 | + } | |
145 | + } | |
146 | + | |
136 | 147 | if (options.neonGlowBrightness) { |
137 | 148 | options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString(); |
138 | 149 | options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString(); |
... | ... | @@ -729,6 +740,48 @@ function drawBarGlow(context, startX, startY, endX, endY, color, strokeWidth, is |
729 | 740 | context.stroke(); |
730 | 741 | } |
731 | 742 | |
743 | +function drawTickArc(context, tickValues, Cx, Cy, Ri, Rm, Ro, startAngle, endAngle, color, tickWidth) { | |
744 | + if(!tickValues.length) { | |
745 | + return; | |
746 | + } | |
747 | + | |
748 | + const strokeWidth = Ro - Ri; | |
749 | + context.beginPath(); | |
750 | + context.lineWidth = tickWidth; | |
751 | + context.strokeStyle = color; | |
752 | + for (let i = 0; i < tickValues.length; i++) { | |
753 | + var angle = startAngle + tickValues[i] * endAngle; | |
754 | + var x1 = Cx + (Ri + strokeWidth) * Math.cos(angle); | |
755 | + var y1 = Cy + (Ri + strokeWidth) * Math.sin(angle); | |
756 | + var x2 = Cx + Ri * Math.cos(angle); | |
757 | + var y2 = Cy + Ri * Math.sin(angle); | |
758 | + context.moveTo(x1, y1); | |
759 | + context.lineTo(x2, y2); | |
760 | + } | |
761 | + context.stroke(); | |
762 | +} | |
763 | + | |
764 | +function drawTickBar(context, tickValues, startX, startY, distanceBar, strokeWidth, isVertical, color, tickWidth) { | |
765 | + if(!tickValues.length) { | |
766 | + return; | |
767 | + } | |
768 | + | |
769 | + context.beginPath(); | |
770 | + context.lineWidth = tickWidth; | |
771 | + context.strokeStyle = color; | |
772 | + for (let i = 0; i < tickValues.length; i++) { | |
773 | + let tickValue = tickValues[i] * distanceBar; | |
774 | + if (isVertical) { | |
775 | + context.moveTo(startX - strokeWidth / 2, startY + tickValue - distanceBar); | |
776 | + context.lineTo(startX + strokeWidth / 2, startY + tickValue - distanceBar); | |
777 | + } else { | |
778 | + context.moveTo(startX + tickValue, startY); | |
779 | + context.lineTo(startX + tickValue, startY + strokeWidth); | |
780 | + } | |
781 | + } | |
782 | + context.stroke(); | |
783 | +} | |
784 | + | |
732 | 785 | function drawProgress(context, options, progress) { |
733 | 786 | var neonColor; |
734 | 787 | if (options.neonGlowBrightness) { |
... | ... | @@ -759,6 +812,7 @@ function drawProgress(context, options, progress) { |
759 | 812 | if (options.neonGlowBrightness && !options.isMobile) { |
760 | 813 | drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle); |
761 | 814 | } |
815 | + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, options.donutStartAngle, options.donutEndAngle - options.donutStartAngle, options.colorTicks, options.tickWidth); | |
762 | 816 | } else if (options.gaugeType === 'arc') { |
763 | 817 | if (options.neonGlowBrightness) { |
764 | 818 | context.strokeStyle = neonColor; |
... | ... | @@ -769,6 +823,7 @@ function drawProgress(context, options, progress) { |
769 | 823 | if (options.neonGlowBrightness && !options.isMobile) { |
770 | 824 | drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false); |
771 | 825 | } |
826 | + drawTickArc(context, options.ticksValue, Cx, Cy, Ri, Rm, Ro, Math.PI, Math.PI, options.colorTicks, options.tickWidth); | |
772 | 827 | } else if (options.gaugeType === 'horizontalBar') { |
773 | 828 | if (options.neonGlowBrightness) { |
774 | 829 | context.strokeStyle = neonColor; |
... | ... | @@ -781,6 +836,7 @@ function drawProgress(context, options, progress) { |
781 | 836 | drawBarGlow(context, barLeft, barTop + strokeWidth/2, barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2, |
782 | 837 | neonColor, strokeWidth, false); |
783 | 838 | } |
839 | + drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth, false, options.colorTicks, options.tickWidth); | |
784 | 840 | } else if (options.gaugeType === 'verticalBar') { |
785 | 841 | if (options.neonGlowBrightness) { |
786 | 842 | context.strokeStyle = neonColor; |
... | ... | @@ -793,6 +849,7 @@ function drawProgress(context, options, progress) { |
793 | 849 | drawBarGlow(context, baseX + width/2, barBottom, baseX + width/2, barBottom - (barBottom-barTop)*progress, |
794 | 850 | neonColor, strokeWidth, true); |
795 | 851 | } |
852 | + drawTickBar(context, options.ticksValue, baseX + width / 2, barTop, barTop - barBottom, strokeWidth, true, options.colorTicks, options.tickWidth); | |
796 | 853 | } |
797 | 854 | } |
798 | 855 | ... | ... |
... | ... | @@ -62,6 +62,11 @@ export default class TbCanvasDigitalGauge { |
62 | 62 | this.localSettings.fixedLevelColors = settings.fixedLevelColors || []; |
63 | 63 | } |
64 | 64 | |
65 | + this.localSettings.showTicks = settings.showTicks || false; | |
66 | + this.localSettings.ticksValue = settings.ticksValue; | |
67 | + this.localSettings.tickWidth = settings.tickWidth || 4; | |
68 | + this.localSettings.colorTicks = settings.colorTicks || '#666'; | |
69 | + | |
65 | 70 | this.localSettings.decimals = angular.isDefined(dataKey.decimals) ? dataKey.decimals : |
66 | 71 | ((angular.isDefined(settings.decimals) && settings.decimals !== null) |
67 | 72 | ? settings.decimals : ctx.decimals); |
... | ... | @@ -145,6 +150,10 @@ export default class TbCanvasDigitalGauge { |
145 | 150 | gaugeColor: this.localSettings.gaugeColor, |
146 | 151 | levelColors: this.localSettings.levelColors, |
147 | 152 | |
153 | + colorTicks: this.localSettings.colorTicks, | |
154 | + tickWidth: this.localSettings.tickWidth, | |
155 | + ticks: [], | |
156 | + | |
148 | 157 | title: this.localSettings.title, |
149 | 158 | |
150 | 159 | fontTitleSize: this.localSettings.titleFont.size, |
... | ... | @@ -204,9 +213,81 @@ export default class TbCanvasDigitalGauge { |
204 | 213 | if (this.localSettings.useFixedLevelColor) { |
205 | 214 | if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) { |
206 | 215 | this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors); |
207 | - this.updateLevelColors(this.localSettings.levelColors); | |
208 | 216 | } |
209 | 217 | } |
218 | + if (this.localSettings.showTicks) { | |
219 | + if (this.localSettings.ticksValue && this.localSettings.ticksValue.length) { | |
220 | + this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue); | |
221 | + } | |
222 | + } | |
223 | + this.updateSetting(); | |
224 | + } | |
225 | + | |
226 | + static generateDatasorce(ctx, datasources, entityAlias, attribute, settings){ | |
227 | + let entityAliasId = ctx.aliasController.getEntityAliasId(entityAlias); | |
228 | + if (!entityAliasId) { | |
229 | + throw new Error('Not valid entity aliase name ' + entityAlias); | |
230 | + } | |
231 | + | |
232 | + let datasource = datasources.filter((datasource) => { | |
233 | + return datasource.entityAliasId === entityAliasId; | |
234 | + })[0]; | |
235 | + | |
236 | + let dataKey = { | |
237 | + type: ctx.$scope.$injector.get('types').dataKeyType.attribute, | |
238 | + name: attribute, | |
239 | + label: attribute, | |
240 | + settings: [settings], | |
241 | + _hash: Math.random() | |
242 | + }; | |
243 | + | |
244 | + if (datasource) { | |
245 | + let findDataKey = datasource.dataKeys.filter((dataKey) => { | |
246 | + return dataKey.name === attribute; | |
247 | + })[0]; | |
248 | + | |
249 | + if (findDataKey) { | |
250 | + findDataKey.settings.push(settings); | |
251 | + } else { | |
252 | + datasource.dataKeys.push(dataKey) | |
253 | + } | |
254 | + } else { | |
255 | + datasource = { | |
256 | + type: ctx.$scope.$injector.get('types').datasourceType.entity, | |
257 | + name: entityAlias, | |
258 | + aliasName: entityAlias, | |
259 | + entityAliasId: entityAliasId, | |
260 | + dataKeys: [dataKey] | |
261 | + }; | |
262 | + datasources.push(datasource); | |
263 | + } | |
264 | + | |
265 | + return datasources; | |
266 | + } | |
267 | + | |
268 | + settingTicksSubscribe(options) { | |
269 | + let ticksDatasource = []; | |
270 | + let predefineTicks = []; | |
271 | + | |
272 | + for (let i = 0; i < options.length; i++) { | |
273 | + let tick = options[i]; | |
274 | + if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) { | |
275 | + predefineTicks.push(tick.value) | |
276 | + } else if (tick.entityAlias && tick.attribute) { | |
277 | + try { | |
278 | + ticksDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, ticksDatasource, tick.entityAlias, tick.attribute, predefineTicks.length); | |
279 | + } catch (e) { | |
280 | + continue; | |
281 | + } | |
282 | + predefineTicks.push(null); | |
283 | + } | |
284 | + } | |
285 | + | |
286 | + this.subscribeAttributes(ticksDatasource, 'ticks').then((subscription) => { | |
287 | + this.ticksSourcesSubscription = subscription; | |
288 | + }); | |
289 | + | |
290 | + return predefineTicks; | |
210 | 291 | } |
211 | 292 | |
212 | 293 | settingLevelColorsSubscribe(options) { |
... | ... | @@ -220,50 +301,14 @@ export default class TbCanvasDigitalGauge { |
220 | 301 | color: color |
221 | 302 | }) |
222 | 303 | } else if (levelSetting.entityAlias && levelSetting.attribute) { |
223 | - let entityAliasId = this.ctx.aliasController.getEntityAliasId(levelSetting.entityAlias); | |
224 | - if (!entityAliasId) { | |
225 | - return; | |
226 | - } | |
227 | - | |
228 | - let datasource = levelColorsDatasource.filter((datasource) => { | |
229 | - return datasource.entityAliasId === entityAliasId; | |
230 | - })[0]; | |
231 | - | |
232 | - let dataKey = { | |
233 | - type: this.ctx.$scope.$injector.get('types').dataKeyType.attribute, | |
234 | - name: levelSetting.attribute, | |
235 | - label: levelSetting.attribute, | |
236 | - settings: [{ | |
304 | + try { | |
305 | + levelColorsDatasource = TbCanvasDigitalGauge.generateDatasorce(this.ctx, levelColorsDatasource, levelSetting.entityAlias, levelSetting.attribute, { | |
237 | 306 | color: color, |
238 | 307 | index: predefineLevelColors.length |
239 | - }], | |
240 | - _hash: Math.random() | |
241 | - }; | |
242 | - | |
243 | - if (datasource) { | |
244 | - let findDataKey = datasource.dataKeys.filter((dataKey) => { | |
245 | - return dataKey.name === levelSetting.attribute; | |
246 | - })[0]; | |
247 | - | |
248 | - if (findDataKey) { | |
249 | - findDataKey.settings.push({ | |
250 | - color: color, | |
251 | - index: predefineLevelColors.length | |
252 | - }); | |
253 | - } else { | |
254 | - datasource.dataKeys.push(dataKey) | |
255 | - } | |
256 | - } else { | |
257 | - datasource = { | |
258 | - type: this.ctx.$scope.$injector.get('types').datasourceType.entity, | |
259 | - name: levelSetting.entityAlias, | |
260 | - aliasName: levelSetting.entityAlias, | |
261 | - entityAliasId: entityAliasId, | |
262 | - dataKeys: [dataKey] | |
263 | - }; | |
264 | - levelColorsDatasource.push(datasource); | |
308 | + }); | |
309 | + } catch (e) { | |
310 | + return; | |
265 | 311 | } |
266 | - | |
267 | 312 | predefineLevelColors.push(null); |
268 | 313 | } |
269 | 314 | } |
... | ... | @@ -278,49 +323,63 @@ export default class TbCanvasDigitalGauge { |
278 | 323 | } |
279 | 324 | } |
280 | 325 | |
281 | - this.subscribeLevelColorsAttributes(levelColorsDatasource); | |
326 | + this.subscribeAttributes(levelColorsDatasource, 'levelColors').then((subscription) => { | |
327 | + this.levelColorSourcesSubscription = subscription; | |
328 | + }); | |
282 | 329 | |
283 | 330 | return predefineLevelColors; |
284 | 331 | } |
285 | 332 | |
286 | - updateLevelColors(levelColors) { | |
287 | - this.gauge.options.levelColors = levelColors; | |
288 | - this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options); | |
289 | - this.gauge.update(); | |
290 | - } | |
333 | + subscribeAttributes(datasources, typeAttributes) { | |
334 | + if (!datasources.length) { | |
335 | + return this.ctx.$scope.$injector.get('$q').when(null); | |
336 | + } | |
291 | 337 | |
292 | - subscribeLevelColorsAttributes(datasources) { | |
293 | - let TbCanvasDigitalGauge = this; | |
294 | 338 | let levelColorsSourcesSubscriptionOptions = { |
295 | 339 | datasources: datasources, |
296 | 340 | useDashboardTimewindow: false, |
297 | 341 | type: this.ctx.$scope.$injector.get('types').widgetType.latest.value, |
298 | 342 | callbacks: { |
299 | 343 | onDataUpdated: (subscription) => { |
300 | - for (let i = 0; i < subscription.data.length; i++) { | |
301 | - let keyData = subscription.data[i]; | |
302 | - if (keyData && keyData.data && keyData.data[0]) { | |
303 | - let attrValue = keyData.data[0][1]; | |
304 | - if (isFinite(attrValue)) { | |
305 | - for (let i = 0; i < keyData.dataKey.settings.length; i++) { | |
306 | - let setting = keyData.dataKey.settings[i]; | |
307 | - this.localSettings.levelColors[setting.index] = { | |
308 | - value: attrValue, | |
309 | - color: setting.color | |
310 | - }; | |
311 | - } | |
312 | - } | |
313 | - } | |
314 | - } | |
315 | - this.updateLevelColors(this.localSettings.levelColors); | |
344 | + this.updateAttribute(subscription.data, typeAttributes); | |
316 | 345 | } |
317 | 346 | } |
318 | 347 | }; |
319 | - this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true).then( | |
320 | - (subscription) => { | |
321 | - TbCanvasDigitalGauge.levelColorSourcesSubscription = subscription; | |
348 | + | |
349 | + return this.ctx.subscriptionApi.createSubscription(levelColorsSourcesSubscriptionOptions, true); | |
350 | + } | |
351 | + | |
352 | + updateAttribute(data, typeAttributes) { | |
353 | + for (let i = 0; i < data.length; i++) { | |
354 | + let keyData = data[i]; | |
355 | + if (keyData && keyData.data && keyData.data[0]) { | |
356 | + let attrValue = keyData.data[0][1]; | |
357 | + if (isFinite(attrValue)) { | |
358 | + for (let i = 0; i < keyData.dataKey.settings.length; i++) { | |
359 | + let setting = keyData.dataKey.settings[i]; | |
360 | + switch (typeAttributes) { | |
361 | + case 'levelColors': | |
362 | + this.localSettings.levelColors[setting.index] = { | |
363 | + value: attrValue, | |
364 | + color: setting.color | |
365 | + }; | |
366 | + break; | |
367 | + case 'ticks': | |
368 | + this.localSettings.ticks[setting] = attrValue; | |
369 | + break; | |
370 | + } | |
371 | + } | |
372 | + } | |
322 | 373 | } |
323 | - ); | |
374 | + } | |
375 | + this.updateSetting(); | |
376 | + } | |
377 | + | |
378 | + updateSetting() { | |
379 | + this.gauge.options.ticks = this.localSettings.ticks; | |
380 | + this.gauge.options.levelColors = this.localSettings.levelColors; | |
381 | + this.gauge.options = CanvasDigitalGauge.configure(this.gauge.options); | |
382 | + this.gauge.update(); | |
324 | 383 | } |
325 | 384 | |
326 | 385 | update() { |
... | ... | @@ -526,6 +585,48 @@ export default class TbCanvasDigitalGauge { |
526 | 585 | } |
527 | 586 | } |
528 | 587 | }, |
588 | + "showTicks": { | |
589 | + "title": "Show ticks", | |
590 | + "type": "boolean", | |
591 | + "default": false | |
592 | + }, | |
593 | + "tickWidth": { | |
594 | + "title": "Width ticks", | |
595 | + "type": "number", | |
596 | + "default": 4 | |
597 | + }, | |
598 | + "colorTicks": { | |
599 | + "title": "Color ticks", | |
600 | + "type": "string", | |
601 | + "default": "#666" | |
602 | + }, | |
603 | + "ticksValue": { | |
604 | + "title": "The ticks predefined value", | |
605 | + "type": "array", | |
606 | + "items": { | |
607 | + "title": "tickValue", | |
608 | + "type": "object", | |
609 | + "properties": { | |
610 | + "valueSource": { | |
611 | + "title": "Value source", | |
612 | + "type": "string", | |
613 | + "default": "predefinedValue" | |
614 | + }, | |
615 | + "entityAlias": { | |
616 | + "title": "Source entity alias", | |
617 | + "type": "string" | |
618 | + }, | |
619 | + "attribute": { | |
620 | + "title": "Source entity attribute", | |
621 | + "type": "string" | |
622 | + }, | |
623 | + "value": { | |
624 | + "title": "Value (if predefined value is selected)", | |
625 | + "type": "number" | |
626 | + } | |
627 | + } | |
628 | + } | |
629 | + }, | |
529 | 630 | "animation": { |
530 | 631 | "title": "Enable animation", |
531 | 632 | "type": "boolean", |
... | ... | @@ -771,6 +872,40 @@ export default class TbCanvasDigitalGauge { |
771 | 872 | } |
772 | 873 | ] |
773 | 874 | }, |
875 | + "showTicks", | |
876 | + { | |
877 | + "key": "tickWidth", | |
878 | + "condition": "model.showTicks === true" | |
879 | + }, | |
880 | + { | |
881 | + "key": "colorTicks", | |
882 | + "condition": "model.showTicks === true", | |
883 | + "type": "color" | |
884 | + }, | |
885 | + { | |
886 | + "key": "ticksValue", | |
887 | + "condition": "model.showTicks === true", | |
888 | + "items": [ | |
889 | + { | |
890 | + "key": "ticksValue[].valueSource", | |
891 | + "type": "rc-select", | |
892 | + "multiple": false, | |
893 | + "items": [ | |
894 | + { | |
895 | + "value": "predefinedValue", | |
896 | + "label": "Predefined value (Default)" | |
897 | + }, | |
898 | + { | |
899 | + "value": "entityAttribute", | |
900 | + "label": "Value taken from entity attribute" | |
901 | + } | |
902 | + ] | |
903 | + }, | |
904 | + "ticksValue[].value", | |
905 | + "ticksValue[].entityAlias", | |
906 | + "ticksValue[].attribute" | |
907 | + ] | |
908 | + }, | |
774 | 909 | "animation", |
775 | 910 | "animationDuration", |
776 | 911 | { | ... | ... |