Showing
18 changed files
with
1996 additions
and
64 deletions
... | ... | @@ -43,7 +43,7 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid |
43 | 43 | constructor(@Inject(RafService) public raf: RafService, |
44 | 44 | @Inject(Store) protected store: Store<AppState>, |
45 | 45 | @Inject(FormBuilder) public fb: FormBuilder, |
46 | - @Inject(Injector) private $injector: Injector, | |
46 | + @Inject(Injector) public readonly $injector: Injector, | |
47 | 47 | @Inject('widgetContext') public readonly ctx: WidgetContext, |
48 | 48 | @Inject('errorMessages') public readonly errorMessages: string[]) { |
49 | 49 | super(store); | ... | ... |
... | ... | @@ -14,7 +14,7 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | - | |
17 | +import * as CanvasGauges from 'canvas-gauges'; | |
18 | 18 | import { FontSettings, getFontFamily } from '@home/components/widget/lib/settings.models'; |
19 | 19 | import { JsonSettingsSchema } from '@shared/models/widget.models'; |
20 | 20 | import { WidgetContext } from '@home/models/widget-component.models'; | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import * as CanvasGauges from 'canvas-gauges'; | |
18 | +import GenericOptions = CanvasGauges.GenericOptions; | |
19 | +import BaseGauge = CanvasGauges.BaseGauge; | |
20 | +import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models'; | |
21 | +import * as tinycolor_ from 'tinycolor2'; | |
22 | +import { ColorFormats } from 'tinycolor2'; | |
23 | +import { isDefined, isUndefined } from '@core/utils'; | |
24 | + | |
25 | +const tinycolor = tinycolor_; | |
26 | + | |
27 | +export type GaugeType = 'arc' | 'donut' | 'horizontalBar' | 'verticalBar'; | |
28 | + | |
29 | +export interface DigitalGaugeColorRange { | |
30 | + pct: number; | |
31 | + color: ColorFormats.RGBA; | |
32 | + rgbString: string; | |
33 | +} | |
34 | + | |
35 | +export interface CanvasDigitalGaugeOptions extends GenericOptions { | |
36 | + gaugeType?: GaugeType; | |
37 | + gaugeWithScale?: number; | |
38 | + dashThickness?: number; | |
39 | + roundedLineCap?: boolean; | |
40 | + gaugeColor?: string; | |
41 | + levelColors?: string[]; | |
42 | + symbol?: string; | |
43 | + label?: string; | |
44 | + hideValue?: boolean; | |
45 | + hideMinMax?: boolean; | |
46 | + fontTitle?: string; | |
47 | + fontValue?: string; | |
48 | + fontMinMaxSize?: number; | |
49 | + fontMinMaxStyle?: FontStyle; | |
50 | + fontMinMaxWeight?: FontWeight; | |
51 | + colorMinMax?: string; | |
52 | + fontMinMax?: string; | |
53 | + fontLabelSize?: number; | |
54 | + fontLabelStyle?: FontStyle; | |
55 | + fontLabelWeight?: FontWeight; | |
56 | + colorLabel?: string; | |
57 | + colorValue?: string; | |
58 | + fontLabel?: string; | |
59 | + neonGlowBrightness?: number; | |
60 | + isMobile?: boolean; | |
61 | + donutStartAngle?: number; | |
62 | + donutEndAngle?: number; | |
63 | + | |
64 | + colorsRange?: DigitalGaugeColorRange[]; | |
65 | + neonColorsRange?: DigitalGaugeColorRange[]; | |
66 | + neonColorTitle?: string; | |
67 | + neonColorLabel?: string; | |
68 | + neonColorValue?: string; | |
69 | + neonColorMinMax?: string; | |
70 | + timestamp?: number; | |
71 | + gaugeWidthScale?: number; | |
72 | + fontTitleHeight?: FontHeightInfo; | |
73 | + fontLabelHeight?: FontHeightInfo; | |
74 | + fontValueHeight?: FontHeightInfo; | |
75 | + fontMinMaxHeight?: FontHeightInfo; | |
76 | + | |
77 | + showTimestamp?: boolean; | |
78 | +} | |
79 | + | |
80 | +const defaultDigitalGaugeOptions: CanvasDigitalGaugeOptions = { ...GenericOptions, | |
81 | + ...{ | |
82 | + gaugeType: 'arc', | |
83 | + gaugeWithScale: 0.75, | |
84 | + dashThickness: 0, | |
85 | + roundedLineCap: false, | |
86 | + | |
87 | + gaugeColor: '#777', | |
88 | + levelColors: ['blue'], | |
89 | + | |
90 | + symbol: '', | |
91 | + label: '', | |
92 | + hideValue: false, | |
93 | + hideMinMax: false, | |
94 | + | |
95 | + fontTitle: 'Roboto', | |
96 | + | |
97 | + fontValue: 'Roboto', | |
98 | + | |
99 | + fontMinMaxSize: 10, | |
100 | + fontMinMaxStyle: 'normal', | |
101 | + fontMinMaxWeight: '500', | |
102 | + colorMinMax: '#eee', | |
103 | + fontMinMax: 'Roboto', | |
104 | + | |
105 | + fontLabelSize: 8, | |
106 | + fontLabelStyle: 'normal', | |
107 | + fontLabelWeight: '500', | |
108 | + colorLabel: '#eee', | |
109 | + fontLabel: 'Roboto', | |
110 | + | |
111 | + neonGlowBrightness: 0, | |
112 | + | |
113 | + isMobile: false | |
114 | + } | |
115 | +}; | |
116 | + | |
117 | +BaseGauge.initialize('CanvasDigitalGauge', defaultDigitalGaugeOptions); | |
118 | + | |
119 | +interface HTMLCanvasElementClone extends HTMLCanvasElement { | |
120 | + initialized?: boolean; | |
121 | + renderedTimestamp?: number; | |
122 | + renderedValue?: number; | |
123 | + renderedProgress?: string; | |
124 | +} | |
125 | + | |
126 | +interface DigitalGaugeCanvasRenderingContext2D extends CanvasRenderingContext2D { | |
127 | + barDimensions?: BarDimensions; | |
128 | + currentColor?: string; | |
129 | +} | |
130 | + | |
131 | +interface BarDimensions { | |
132 | + baseX: number; | |
133 | + baseY: number; | |
134 | + width: number; | |
135 | + height: number; | |
136 | + origBaseX?: number; | |
137 | + origBaseY?: number; | |
138 | + fontSizeFactor?: number; | |
139 | + Ro?: number; | |
140 | + Cy?: number; | |
141 | + titleY?: number; | |
142 | + titleBottom?: number; | |
143 | + Ri?: number; | |
144 | + Cx?: number; | |
145 | + strokeWidth?: number; | |
146 | + Rm?: number; | |
147 | + fontValueBaseline?: CanvasTextBaseline; | |
148 | + fontMinMaxBaseline?: CanvasTextBaseline; | |
149 | + fontMinMaxAlign?: CanvasTextAlign; | |
150 | + labelY?: number; | |
151 | + valueY?: number; | |
152 | + minY?: number; | |
153 | + maxY?: number; | |
154 | + minX?: number; | |
155 | + maxX?: number; | |
156 | + barTop?: number; | |
157 | + barBottom?: number; | |
158 | + barLeft?: number; | |
159 | + barRight?: number; | |
160 | + dashLength?: number; | |
161 | +} | |
162 | + | |
163 | +interface FontHeightInfo { | |
164 | + ascent?: number; | |
165 | + height?: number; | |
166 | + descent?: number; | |
167 | +} | |
168 | + | |
169 | +export class CanvasDigitalGauge extends BaseGauge { | |
170 | + | |
171 | + static heightCache: {[key: string]: FontHeightInfo} = {}; | |
172 | + | |
173 | + private elementValueClone: HTMLCanvasElementClone; | |
174 | + private contextValueClone: DigitalGaugeCanvasRenderingContext2D; | |
175 | + | |
176 | + private elementProgressClone: HTMLCanvasElementClone; | |
177 | + private contextProgressClone: DigitalGaugeCanvasRenderingContext2D; | |
178 | + | |
179 | + public _value: number; | |
180 | + | |
181 | + constructor(options: CanvasDigitalGaugeOptions) { | |
182 | + options = {...defaultDigitalGaugeOptions,...(options || {})}; | |
183 | + super(CanvasDigitalGauge.configure(options)); | |
184 | + this.initValueClone(); | |
185 | + } | |
186 | + | |
187 | + static configure(options: CanvasDigitalGaugeOptions): CanvasDigitalGaugeOptions { | |
188 | + | |
189 | + if (options.value > options.maxValue) { | |
190 | + options.value = options.maxValue; | |
191 | + } | |
192 | + | |
193 | + if (options.value < options.minValue) { | |
194 | + options.value = options.minValue; | |
195 | + } | |
196 | + | |
197 | + if (options.gaugeType === 'donut') { | |
198 | + if (!options.donutStartAngle) { | |
199 | + options.donutStartAngle = 1.5 * Math.PI; | |
200 | + } | |
201 | + if (!options.donutEndAngle) { | |
202 | + options.donutEndAngle = options.donutStartAngle + 2 * Math.PI; | |
203 | + } | |
204 | + } | |
205 | + | |
206 | + const colorsCount = options.levelColors.length; | |
207 | + const inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1; | |
208 | + options.colorsRange = []; | |
209 | + if (options.neonGlowBrightness) { | |
210 | + options.neonColorsRange = []; | |
211 | + } | |
212 | + for (let i = 0; i < options.levelColors.length; i++) { | |
213 | + const percentage = inc * i; | |
214 | + let tColor = tinycolor(options.levelColors[i]); | |
215 | + options.colorsRange[i] = { | |
216 | + pct: percentage, | |
217 | + color: tColor.toRgb(), | |
218 | + rgbString: tColor.toRgbString() | |
219 | + }; | |
220 | + if (options.neonGlowBrightness) { | |
221 | + tColor = tinycolor(options.levelColors[i]).brighten(options.neonGlowBrightness); | |
222 | + options.neonColorsRange[i] = { | |
223 | + pct: percentage, | |
224 | + color: tColor.toRgb(), | |
225 | + rgbString: tColor.toRgbString() | |
226 | + }; | |
227 | + } | |
228 | + } | |
229 | + | |
230 | + if (options.neonGlowBrightness) { | |
231 | + options.neonColorTitle = tinycolor(options.colorTitle).brighten(options.neonGlowBrightness).toHexString(); | |
232 | + options.neonColorLabel = tinycolor(options.colorLabel).brighten(options.neonGlowBrightness).toHexString(); | |
233 | + options.neonColorValue = tinycolor(options.colorValue).brighten(options.neonGlowBrightness).toHexString(); | |
234 | + options.neonColorMinMax = tinycolor(options.colorMinMax).brighten(options.neonGlowBrightness).toHexString(); | |
235 | + } | |
236 | + | |
237 | + return options; | |
238 | + } | |
239 | + | |
240 | + private initValueClone() { | |
241 | + const canvas = this.canvas; | |
242 | + this.elementValueClone = canvas.element.cloneNode(true) as HTMLCanvasElementClone; | |
243 | + this.contextValueClone = this.elementValueClone.getContext('2d'); | |
244 | + this.elementValueClone.initialized = false; | |
245 | + | |
246 | + this.contextValueClone.translate(canvas.drawX, canvas.drawY); | |
247 | + this.contextValueClone.save(); | |
248 | + | |
249 | + this.elementProgressClone = canvas.element.cloneNode(true) as HTMLCanvasElementClone; | |
250 | + this.contextProgressClone = this.elementProgressClone.getContext('2d'); | |
251 | + this.elementProgressClone.initialized = false; | |
252 | + | |
253 | + this.contextProgressClone.translate(canvas.drawX, canvas.drawY); | |
254 | + this.contextProgressClone.save(); | |
255 | + } | |
256 | + | |
257 | + destroy() { | |
258 | + this.contextValueClone = null; | |
259 | + this.elementValueClone = null; | |
260 | + this.contextProgressClone = null; | |
261 | + this.elementProgressClone = null; | |
262 | + super.destroy(); | |
263 | + } | |
264 | + | |
265 | + update(options: GenericOptions): BaseGauge { | |
266 | + this.canvas.onRedraw = null; | |
267 | + const result = super.update(options); | |
268 | + this.initValueClone(); | |
269 | + this.canvas.onRedraw = this.draw.bind(this); | |
270 | + this.draw(); | |
271 | + return result; | |
272 | + } | |
273 | + | |
274 | + set timestamp(timestamp: number) { | |
275 | + (this.options as CanvasDigitalGaugeOptions).timestamp = timestamp; | |
276 | + this.draw(); | |
277 | + } | |
278 | + | |
279 | + get timestamp(): number { | |
280 | + return (this.options as CanvasDigitalGaugeOptions).timestamp; | |
281 | + } | |
282 | + | |
283 | + draw(): CanvasDigitalGauge { | |
284 | + try { | |
285 | + const canvas = this.canvas; | |
286 | + if (!canvas.drawWidth || !canvas.drawHeight) { | |
287 | + return this; | |
288 | + } | |
289 | + const [x, y, w, h] = [ | |
290 | + -canvas.drawX, | |
291 | + -canvas.drawY, | |
292 | + canvas.drawWidth, | |
293 | + canvas.drawHeight | |
294 | + ]; | |
295 | + const options = this.options as CanvasDigitalGaugeOptions; | |
296 | + const elementClone = canvas.elementClone as HTMLCanvasElementClone; | |
297 | + if (!elementClone.initialized) { | |
298 | + const context = canvas.contextClone; | |
299 | + | |
300 | + // clear the cache | |
301 | + context.clearRect(x, y, w, h); | |
302 | + context.save(); | |
303 | + | |
304 | + const canvasContext = canvas.context as DigitalGaugeCanvasRenderingContext2D; | |
305 | + | |
306 | + canvasContext.barDimensions = barDimensions(context, options, x, y, w, h); | |
307 | + this.contextValueClone.barDimensions = canvasContext.barDimensions; | |
308 | + this.contextProgressClone.barDimensions = canvasContext.barDimensions; | |
309 | + | |
310 | + drawBackground(context, options); | |
311 | + | |
312 | + drawDigitalTitle(context, options); | |
313 | + | |
314 | + if (!options.showTimestamp) { | |
315 | + drawDigitalLabel(context, options); | |
316 | + } | |
317 | + | |
318 | + drawDigitalMinMax(context, options); | |
319 | + | |
320 | + elementClone.initialized = true; | |
321 | + } | |
322 | + | |
323 | + let valueChanged = false; | |
324 | + if (!this.elementValueClone.initialized || | |
325 | + isDefined(this._value) && this.elementValueClone.renderedValue !== this._value || | |
326 | + (options.showTimestamp && this.elementValueClone.renderedTimestamp !== this.timestamp)) { | |
327 | + if (isDefined(this._value)) { | |
328 | + this.elementValueClone.renderedValue = this._value; | |
329 | + } | |
330 | + if (isUndefined(this.elementValueClone.renderedValue)) { | |
331 | + this.elementValueClone.renderedValue = this.value; | |
332 | + } | |
333 | + const context = this.contextValueClone; | |
334 | + // clear the cache | |
335 | + context.clearRect(x, y, w, h); | |
336 | + context.save(); | |
337 | + | |
338 | + context.drawImage(canvas.elementClone, x, y, w, h); | |
339 | + context.save(); | |
340 | + | |
341 | + drawDigitalValue(context, options, this.elementValueClone.renderedValue); | |
342 | + | |
343 | + if (options.showTimestamp) { | |
344 | + drawDigitalLabel(context, options); | |
345 | + this.elementValueClone.renderedTimestamp = this.timestamp; | |
346 | + } | |
347 | + | |
348 | + this.elementValueClone.initialized = true; | |
349 | + | |
350 | + valueChanged = true; | |
351 | + } | |
352 | + | |
353 | + const progress = (CanvasGauges.drawings.normalizedValue(options).normal - options.minValue) / | |
354 | + (options.maxValue - options.minValue); | |
355 | + | |
356 | + const fixedProgress = progress.toFixed(3); | |
357 | + | |
358 | + if (!this.elementProgressClone.initialized || this.elementProgressClone.renderedProgress !== fixedProgress || valueChanged) { | |
359 | + const context = this.contextProgressClone; | |
360 | + // clear the cache | |
361 | + context.clearRect(x, y, w, h); | |
362 | + context.save(); | |
363 | + | |
364 | + context.drawImage(this.elementValueClone, x, y, w, h); | |
365 | + context.save(); | |
366 | + | |
367 | + if (Number(fixedProgress) > 0) { | |
368 | + drawProgress(context, options, progress); | |
369 | + } | |
370 | + | |
371 | + this.elementProgressClone.initialized = true; | |
372 | + this.elementProgressClone.renderedProgress = fixedProgress; | |
373 | + } | |
374 | + | |
375 | + this.canvas.commit(); | |
376 | + | |
377 | + // clear the canvas | |
378 | + canvas.context.clearRect(x, y, w, h); | |
379 | + canvas.context.save(); | |
380 | + | |
381 | + canvas.context.drawImage(this.elementProgressClone, x, y, w, h); | |
382 | + canvas.context.save(); | |
383 | + | |
384 | + // @ts-ignore | |
385 | + super.draw(); | |
386 | + | |
387 | + } catch (err) { | |
388 | + CanvasGauges.drawings.verifyError(err); | |
389 | + } | |
390 | + return this; | |
391 | + } | |
392 | + | |
393 | + getValueColor() { | |
394 | + if (this.contextProgressClone) { | |
395 | + let color = this.contextProgressClone.currentColor; | |
396 | + const options = this.options as CanvasDigitalGaugeOptions; | |
397 | + if (!color) { | |
398 | + if (options.neonGlowBrightness) { | |
399 | + color = getProgressColor(0, options.neonColorsRange); | |
400 | + } else { | |
401 | + color = getProgressColor(0, options.colorsRange); | |
402 | + } | |
403 | + } | |
404 | + return color; | |
405 | + } else { | |
406 | + return '#000'; | |
407 | + } | |
408 | + } | |
409 | +} | |
410 | + | |
411 | +function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, | |
412 | + options: CanvasDigitalGaugeOptions, | |
413 | + x: number, y: number, w: number, h: number): BarDimensions { | |
414 | + context.barDimensions = { | |
415 | + baseX: x, | |
416 | + baseY: y, | |
417 | + width: w, | |
418 | + height: h | |
419 | + }; | |
420 | + | |
421 | + const bd = context.barDimensions; | |
422 | + | |
423 | + let aspect = 1; | |
424 | + | |
425 | + if (options.gaugeType === 'horizontalBar') { | |
426 | + aspect = options.title === '' ? 2.5 : 2; | |
427 | + } else if (options.gaugeType === 'verticalBar') { | |
428 | + aspect = options.hideMinMax ? 0.35 : 0.5; | |
429 | + } else if (options.gaugeType === 'arc') { | |
430 | + aspect = 1.5; | |
431 | + } | |
432 | + | |
433 | + const currentAspect = w / h; | |
434 | + if (currentAspect > aspect) { | |
435 | + bd.width = (h * aspect); | |
436 | + bd.height = h; | |
437 | + } else { | |
438 | + bd.width = w; | |
439 | + bd.height = w / aspect; | |
440 | + } | |
441 | + | |
442 | + bd.origBaseX = bd.baseX; | |
443 | + bd.origBaseY = bd.baseY; | |
444 | + bd.baseX += (w - bd.width) / 2; | |
445 | + bd.baseY += (h - bd.height) / 2; | |
446 | + | |
447 | + if (options.gaugeType === 'donut') { | |
448 | + bd.fontSizeFactor = Math.max(bd.width, bd.height) / 125; | |
449 | + } else if (options.gaugeType === 'verticalBar' || (options.gaugeType === 'arc' && options.title === '')) { | |
450 | + bd.fontSizeFactor = Math.max(bd.width, bd.height) / 150; | |
451 | + } else { | |
452 | + bd.fontSizeFactor = Math.max(bd.width, bd.height) / 200; | |
453 | + } | |
454 | + | |
455 | + const gws = options.gaugeWidthScale; | |
456 | + | |
457 | + if (options.neonGlowBrightness) { | |
458 | + options.fontTitleHeight = determineFontHeight(options, 'Title', bd.fontSizeFactor); | |
459 | + options.fontLabelHeight = determineFontHeight(options, 'Label', bd.fontSizeFactor); | |
460 | + options.fontValueHeight = determineFontHeight(options, 'Value', bd.fontSizeFactor); | |
461 | + options.fontMinMaxHeight = determineFontHeight(options, 'MinMax', bd.fontSizeFactor); | |
462 | + } | |
463 | + | |
464 | + if (options.gaugeType === 'donut') { | |
465 | + bd.Ro = bd.width / 2 - bd.width / 20; | |
466 | + bd.Cy = bd.baseY + bd.height / 2; | |
467 | + if (options.title && typeof options.title === 'string' && options.title.length > 0) { | |
468 | + let titleOffset = determineFontHeight(options, 'Title', bd.fontSizeFactor).height; | |
469 | + titleOffset += bd.fontSizeFactor * 2; | |
470 | + bd.titleY = bd.baseY + titleOffset; | |
471 | + titleOffset += bd.fontSizeFactor * 2; | |
472 | + bd.Cy += titleOffset/2; | |
473 | + bd.Ro -= titleOffset/2; | |
474 | + } | |
475 | + bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws * 1.2; | |
476 | + bd.Cx = bd.baseX + bd.width / 2; | |
477 | + } else if (options.gaugeType === 'arc') { | |
478 | + if (options.title && typeof options.title === 'string' && options.title.length > 0) { | |
479 | + bd.Ro = bd.width / 2 - bd.width / 7; | |
480 | + bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws; | |
481 | + } else { | |
482 | + bd.Ro = bd.width / 2 - bd.fontSizeFactor * 4; | |
483 | + bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws * 1.2; | |
484 | + } | |
485 | + bd.Cx = bd.baseX + bd.width / 2; | |
486 | + bd.Cy = bd.baseY + bd.height / 1.25; | |
487 | + } else if (options.gaugeType === 'verticalBar') { | |
488 | + bd.Ro = bd.width / 2 - bd.width / 10; | |
489 | + bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws * (options.hideMinMax ? 4 : 2.5); | |
490 | + } else { // horizontalBar | |
491 | + bd.Ro = bd.width / 2 - bd.width / 10; | |
492 | + bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws; | |
493 | + } | |
494 | + | |
495 | + bd.strokeWidth = bd.Ro - bd.Ri; | |
496 | + bd.Rm = bd.Ri + bd.strokeWidth * 0.5; | |
497 | + | |
498 | + bd.fontValueBaseline = 'alphabetic'; | |
499 | + bd.fontMinMaxBaseline = 'alphabetic'; | |
500 | + bd.fontMinMaxAlign = 'center'; | |
501 | + | |
502 | + if (options.gaugeType === 'donut') { | |
503 | + bd.fontValueBaseline = 'middle'; | |
504 | + if (options.label && options.label.length > 0) { | |
505 | + const valueHeight = determineFontHeight(options, 'Value', bd.fontSizeFactor).height; | |
506 | + const labelHeight = determineFontHeight(options, 'Label', bd.fontSizeFactor).height; | |
507 | + const total = valueHeight + labelHeight; | |
508 | + bd.labelY = bd.Cy + total/2; | |
509 | + bd.valueY = bd.Cy - total/2 + valueHeight/2; | |
510 | + } else { | |
511 | + bd.valueY = bd.Cy; | |
512 | + } | |
513 | + } else if (options.gaugeType === 'arc') { | |
514 | + bd.titleY = bd.Cy - bd.Ro - 12 * bd.fontSizeFactor; | |
515 | + bd.valueY = bd.Cy; | |
516 | + bd.labelY = bd.Cy + (8 + options.fontLabelSize) * bd.fontSizeFactor; | |
517 | + bd.minY = bd.maxY = bd.labelY; | |
518 | + if (options.roundedLineCap) { | |
519 | + bd.minY += bd.strokeWidth/2; | |
520 | + bd.maxY += bd.strokeWidth/2; | |
521 | + } | |
522 | + bd.minX = bd.Cx - bd.Rm; | |
523 | + bd.maxX = bd.Cx + bd.Rm; | |
524 | + } else if (options.gaugeType === 'horizontalBar') { | |
525 | + bd.titleY = bd.baseY + 4 * bd.fontSizeFactor + | |
526 | + (options.title === '' ? 0 : options.fontTitleSize * bd.fontSizeFactor); | |
527 | + bd.titleBottom = bd.titleY + (options.title === '' ? 0 : 4) * bd.fontSizeFactor; | |
528 | + | |
529 | + bd.valueY = bd.titleBottom + | |
530 | + (options.hideValue ? 0 : options.fontValueSize * bd.fontSizeFactor); | |
531 | + | |
532 | + bd.barTop = bd.valueY + 8 * bd.fontSizeFactor; | |
533 | + bd.barBottom = bd.barTop + bd.strokeWidth; | |
534 | + | |
535 | + if (options.hideMinMax && options.label === '') { | |
536 | + bd.labelY = bd.barBottom; | |
537 | + bd.barLeft = bd.origBaseX + options.fontMinMaxSize/3 * bd.fontSizeFactor; | |
538 | + bd.barRight = bd.origBaseX + w + /*bd.width*/ - options.fontMinMaxSize/3 * bd.fontSizeFactor; | |
539 | + } else { | |
540 | + context.font = CanvasGauges.drawings.font(options, 'MinMax', bd.fontSizeFactor); | |
541 | + const minTextWidth = context.measureText(options.minValue+'').width; | |
542 | + const maxTextWidth = context.measureText(options.maxValue+'').width; | |
543 | + const maxW = Math.max(minTextWidth, maxTextWidth); | |
544 | + bd.minX = bd.origBaseX + maxW/2 + options.fontMinMaxSize/3 * bd.fontSizeFactor; | |
545 | + bd.maxX = bd.origBaseX + w + /*bd.width*/ - maxW/2 - options.fontMinMaxSize/3 * bd.fontSizeFactor; | |
546 | + bd.barLeft = bd.minX; | |
547 | + bd.barRight = bd.maxX; | |
548 | + bd.labelY = bd.barBottom + (8 + options.fontLabelSize) * bd.fontSizeFactor; | |
549 | + bd.minY = bd.maxY = bd.labelY; | |
550 | + } | |
551 | + } else if (options.gaugeType === 'verticalBar') { | |
552 | + bd.titleY = bd.baseY + ((options.title === '' ? 0 : options.fontTitleSize) + 8) * bd.fontSizeFactor; | |
553 | + bd.titleBottom = bd.titleY + (options.title === '' ? 0 : 4) * bd.fontSizeFactor; | |
554 | + | |
555 | + bd.valueY = bd.titleBottom + (options.hideValue ? 0 : options.fontValueSize * bd.fontSizeFactor); | |
556 | + bd.barTop = bd.valueY + 8 * bd.fontSizeFactor; | |
557 | + | |
558 | + bd.labelY = bd.baseY + bd.height - 16; | |
559 | + if (options.label === '') { | |
560 | + bd.barBottom = bd.labelY; | |
561 | + } else { | |
562 | + bd.barBottom = bd.labelY - (8 + options.fontLabelSize) * bd.fontSizeFactor; | |
563 | + } | |
564 | + bd.minX = bd.maxX = | |
565 | + bd.baseX + bd.width/2 + bd.strokeWidth/2 + options.fontMinMaxSize/3 * bd.fontSizeFactor; | |
566 | + bd.minY = bd.barBottom; | |
567 | + bd.maxY = bd.barTop; | |
568 | + bd.fontMinMaxBaseline = 'middle'; | |
569 | + bd.fontMinMaxAlign = 'left'; | |
570 | + } | |
571 | + | |
572 | + if (options.dashThickness) { | |
573 | + let circumference; | |
574 | + if (options.gaugeType === 'donut') { | |
575 | + circumference = Math.PI * bd.Rm * 2; | |
576 | + } else if (options.gaugeType === 'arc') { | |
577 | + circumference = Math.PI * bd.Rm; | |
578 | + } else if (options.gaugeType === 'horizontalBar') { | |
579 | + circumference = bd.barRight - bd.barLeft; | |
580 | + } else if (options.gaugeType === 'verticalBar') { | |
581 | + circumference = bd.barBottom - bd.barTop; | |
582 | + } | |
583 | + let dashCount = Math.floor(circumference / (options.dashThickness * bd.fontSizeFactor)); | |
584 | + if (options.gaugeType === 'donut') { | |
585 | + // tslint:disable-next-line:no-bitwise | |
586 | + dashCount = (dashCount | 1) - 1; | |
587 | + } else { | |
588 | + // tslint:disable-next-line:no-bitwise | |
589 | + dashCount = (dashCount - 1) | 1; | |
590 | + } | |
591 | + bd.dashLength = Math.ceil(circumference/dashCount); | |
592 | + } | |
593 | + | |
594 | + return bd; | |
595 | +} | |
596 | + | |
597 | +function determineFontHeight (options: CanvasDigitalGaugeOptions, target: string, baseSize: number): FontHeightInfo { | |
598 | + const fontStyleStr = 'font-style:' + options['font' + target + 'Style'] + ';font-weight:' + | |
599 | + options['font' + target + 'Weight'] + ';font-size:' + | |
600 | + options['font' + target + 'Size'] * baseSize + 'px;font-family:' + | |
601 | + options['font' + target]; | |
602 | + let result = CanvasDigitalGauge.heightCache[fontStyleStr]; | |
603 | + if (!result) { | |
604 | + const fontStyle = { | |
605 | + fontFamily: options['font' + target], | |
606 | + fontSize: options['font' + target + 'Size'] * baseSize + 'px', | |
607 | + fontWeight: options['font' + target + 'Weight'], | |
608 | + fontStyle: options['font' + target + 'Style'] | |
609 | + }; | |
610 | + const text = $('<span>Hg</span>').css(fontStyle); | |
611 | + const block = $('<div style="display: inline-block; width: 1px; height: 0px;"></div>'); | |
612 | + | |
613 | + const div = $('<div></div>'); | |
614 | + div.append(text, block); | |
615 | + | |
616 | + const body = $('body'); | |
617 | + body.append(div); | |
618 | + | |
619 | + try { | |
620 | + result = {}; | |
621 | + block.css({ verticalAlign: 'baseline' }); | |
622 | + result.ascent = block.offset().top - text.offset().top; | |
623 | + block.css({ verticalAlign: 'bottom' }); | |
624 | + result.height = block.offset().top - text.offset().top; | |
625 | + result.descent = result.height - result.ascent; | |
626 | + } finally { | |
627 | + div.remove(); | |
628 | + } | |
629 | + | |
630 | + CanvasDigitalGauge.heightCache[fontStyleStr] = result; | |
631 | + } | |
632 | + return result; | |
633 | +} | |
634 | + | |
635 | +function drawBackground(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) { | |
636 | + const {barLeft, barRight, barTop, barBottom, width, baseX, strokeWidth} = | |
637 | + context.barDimensions; | |
638 | + if (context.barDimensions.dashLength) { | |
639 | + context.setLineDash([context.barDimensions.dashLength]); | |
640 | + } | |
641 | + context.beginPath(); | |
642 | + context.strokeStyle = options.gaugeColor; | |
643 | + context.lineWidth = strokeWidth; | |
644 | + if (options.roundedLineCap) { | |
645 | + context.lineCap = 'round'; | |
646 | + } | |
647 | + if (options.gaugeType === 'donut') { | |
648 | + context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, | |
649 | + options.donutStartAngle, options.donutEndAngle); | |
650 | + context.stroke(); | |
651 | + } else if (options.gaugeType === 'arc') { | |
652 | + context.arc(context.barDimensions.Cx, context.barDimensions.Cy, | |
653 | + context.barDimensions.Rm, Math.PI, 2*Math.PI); | |
654 | + context.stroke(); | |
655 | + } else if (options.gaugeType === 'horizontalBar') { | |
656 | + context.moveTo(barLeft,barTop + strokeWidth/2); | |
657 | + context.lineTo(barRight,barTop + strokeWidth/2); | |
658 | + context.stroke(); | |
659 | + } else if (options.gaugeType === 'verticalBar') { | |
660 | + context.moveTo(baseX + width/2, barBottom); | |
661 | + context.lineTo(baseX + width/2, barTop); | |
662 | + context.stroke(); | |
663 | + } | |
664 | +} | |
665 | + | |
666 | +function drawText(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions, | |
667 | + target: string, text: string, textX: number, textY: number) { | |
668 | + context.fillStyle = options[(options.neonGlowBrightness ? 'neonColor' : 'color') + target]; | |
669 | + context.fillText(text, textX, textY); | |
670 | +} | |
671 | + | |
672 | +function drawDigitalTitle(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) { | |
673 | + if (!options.title || typeof options.title !== 'string') return; | |
674 | + | |
675 | + const {titleY, width, baseX, fontSizeFactor} = | |
676 | + context.barDimensions; | |
677 | + | |
678 | + const textX = Math.round(baseX + width / 2); | |
679 | + const textY = titleY; | |
680 | + | |
681 | + context.save(); | |
682 | + context.textAlign = 'center'; | |
683 | + context.font = CanvasGauges.drawings.font(options, 'Title', fontSizeFactor); | |
684 | + context.lineWidth = 0; | |
685 | + drawText(context, options, 'Title', options.title.toUpperCase(), textX, textY); | |
686 | +} | |
687 | + | |
688 | +function drawDigitalLabel(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) { | |
689 | + if (!options.label || options.label === '') return; | |
690 | + | |
691 | + const {labelY, baseX, width, fontSizeFactor} = | |
692 | + context.barDimensions; | |
693 | + | |
694 | + const textX = Math.round(baseX + width / 2); | |
695 | + const textY = labelY; | |
696 | + | |
697 | + context.save(); | |
698 | + context.textAlign = 'center'; | |
699 | + context.font = CanvasGauges.drawings.font(options, 'Label', fontSizeFactor); | |
700 | + context.lineWidth = 0; | |
701 | + drawText(context, options, 'Label', options.label.toUpperCase(), textX, textY); | |
702 | +} | |
703 | + | |
704 | +function drawDigitalMinMax(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) { | |
705 | + if (options.hideMinMax || options.gaugeType === 'donut') return; | |
706 | + | |
707 | + const {minY, maxY, minX, maxX, fontSizeFactor, fontMinMaxAlign, fontMinMaxBaseline} = | |
708 | + context.barDimensions; | |
709 | + | |
710 | + context.save(); | |
711 | + context.textAlign = fontMinMaxAlign; | |
712 | + context.textBaseline = fontMinMaxBaseline; | |
713 | + context.font = CanvasGauges.drawings.font(options, 'MinMax', fontSizeFactor); | |
714 | + context.lineWidth = 0; | |
715 | + drawText(context, options, 'MinMax', options.minValue+'', minX, minY); | |
716 | + drawText(context, options, 'MinMax', options.maxValue+'', maxX, maxY); | |
717 | +} | |
718 | + | |
719 | +function padValue(val: any, options: CanvasDigitalGaugeOptions): string { | |
720 | + const dec = options.valueDec; | |
721 | + let strVal; | |
722 | + let n; | |
723 | + | |
724 | + val = parseFloat(val); | |
725 | + n = (val < 0); | |
726 | + val = Math.abs(val); | |
727 | + | |
728 | + if (dec > 0) { | |
729 | + strVal = val.toFixed(dec).toString() | |
730 | + } else { | |
731 | + strVal = Math.round(val).toString(); | |
732 | + } | |
733 | + strVal = (n ? '-' : '') + strVal; | |
734 | + return strVal; | |
735 | +} | |
736 | + | |
737 | +function drawDigitalValue(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions, value: any) { | |
738 | + if (options.hideValue) return; | |
739 | + | |
740 | + const {valueY, baseX, width, fontSizeFactor, fontValueBaseline} = | |
741 | + context.barDimensions; | |
742 | + | |
743 | + const textX = Math.round(baseX + width / 2); | |
744 | + const textY = valueY; | |
745 | + | |
746 | + let text = options.valueText || padValue(value, options); | |
747 | + text += options.symbol; | |
748 | + | |
749 | + context.save(); | |
750 | + context.textAlign = 'center'; | |
751 | + context.textBaseline = fontValueBaseline; | |
752 | + context.font = CanvasGauges.drawings.font(options, 'Value', fontSizeFactor); | |
753 | + context.lineWidth = 0; | |
754 | + drawText(context, options, 'Value', text, textX, textY); | |
755 | +} | |
756 | + | |
757 | +function getProgressColor(progress: number, colorsRange: DigitalGaugeColorRange[]): string { | |
758 | + | |
759 | + if (progress === 0 || colorsRange.length === 1) { | |
760 | + return colorsRange[0].rgbString; | |
761 | + } | |
762 | + | |
763 | + for (let j = 0; j < colorsRange.length; j++) { | |
764 | + if (progress <= colorsRange[j].pct) { | |
765 | + const lower = colorsRange[j - 1]; | |
766 | + const upper = colorsRange[j]; | |
767 | + const range = upper.pct - lower.pct; | |
768 | + const rangePct = (progress - lower.pct) / range; | |
769 | + const pctLower = 1 - rangePct; | |
770 | + const pctUpper = rangePct; | |
771 | + const color = tinycolor({ | |
772 | + r: Math.floor(lower.color.r * pctLower + upper.color.r * pctUpper), | |
773 | + g: Math.floor(lower.color.g * pctLower + upper.color.g * pctUpper), | |
774 | + b: Math.floor(lower.color.b * pctLower + upper.color.b * pctUpper) | |
775 | + }); | |
776 | + return color.toRgbString(); | |
777 | + } | |
778 | + } | |
779 | +} | |
780 | + | |
781 | +function drawArcGlow(context: DigitalGaugeCanvasRenderingContext2D, | |
782 | + Cx: number, Cy: number, Ri: number, Rm: number, Ro: number, | |
783 | + color: string, progress: number, isDonut: boolean, | |
784 | + donutStartAngle?: number, donutEndAngle?: number) { | |
785 | + context.setLineDash([]); | |
786 | + const strokeWidth = Ro - Ri; | |
787 | + const blur = 0.55; | |
788 | + const edge = strokeWidth*blur; | |
789 | + context.lineWidth = strokeWidth+edge; | |
790 | + const stop = blur/(2*blur+2); | |
791 | + const glowGradient = context.createRadialGradient(Cx,Cy,Ri-edge/2,Cx,Cy,Ro+edge/2); | |
792 | + const color1 = tinycolor(color).setAlpha(0.5).toRgbString(); | |
793 | + const color2 = tinycolor(color).setAlpha(0).toRgbString(); | |
794 | + glowGradient.addColorStop(0,color2); | |
795 | + glowGradient.addColorStop(stop,color1); | |
796 | + glowGradient.addColorStop(1.0-stop,color1); | |
797 | + glowGradient.addColorStop(1,color2); | |
798 | + context.strokeStyle = glowGradient; | |
799 | + context.beginPath(); | |
800 | + const e = 0.01 * Math.PI; | |
801 | + if (isDonut) { | |
802 | + context.arc(Cx, Cy, Rm, donutStartAngle - e, donutStartAngle + | |
803 | + (donutEndAngle - donutStartAngle) * progress + e); | |
804 | + } else { | |
805 | + context.arc(Cx, Cy, Rm, Math.PI - e, Math.PI + Math.PI * progress + e); | |
806 | + } | |
807 | + context.stroke(); | |
808 | +} | |
809 | + | |
810 | +function drawBarGlow(context: DigitalGaugeCanvasRenderingContext2D, startX: number, startY: number, | |
811 | + endX: number, endY: number, color: string, strokeWidth: number, isVertical: boolean) { | |
812 | + context.setLineDash([]); | |
813 | + const blur = 0.55; | |
814 | + const edge = strokeWidth*blur; | |
815 | + context.lineWidth = strokeWidth+edge; | |
816 | + const stop = blur/(2*blur+2); | |
817 | + const gradientStartX = isVertical ? startX - context.lineWidth/2 : 0; | |
818 | + const gradientStartY = isVertical ? 0 : startY - context.lineWidth/2; | |
819 | + const gradientStopX = isVertical ? startX + context.lineWidth/2 : 0; | |
820 | + const gradientStopY = isVertical ? 0 : startY + context.lineWidth/2; | |
821 | + | |
822 | + const glowGradient = context.createLinearGradient(gradientStartX,gradientStartY,gradientStopX,gradientStopY); | |
823 | + const color1 = tinycolor(color).setAlpha(0.5).toRgbString(); | |
824 | + const color2 = tinycolor(color).setAlpha(0).toRgbString(); | |
825 | + glowGradient.addColorStop(0,color2); | |
826 | + glowGradient.addColorStop(stop,color1); | |
827 | + glowGradient.addColorStop(1.0-stop,color1); | |
828 | + glowGradient.addColorStop(1,color2); | |
829 | + context.strokeStyle = glowGradient; | |
830 | + const dx = isVertical ? 0 : 0.05 * context.lineWidth; | |
831 | + const dy = isVertical ? 0.05 * context.lineWidth : 0; | |
832 | + context.beginPath(); | |
833 | + context.moveTo(startX - dx, startY + dy); | |
834 | + context.lineTo(endX + dx, endY - dy); | |
835 | + context.stroke(); | |
836 | +} | |
837 | + | |
838 | +function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, | |
839 | + options: CanvasDigitalGaugeOptions, progress: number) { | |
840 | + let neonColor; | |
841 | + if (options.neonGlowBrightness) { | |
842 | + context.currentColor = neonColor = getProgressColor(progress, options.neonColorsRange); | |
843 | + } else { | |
844 | + context.currentColor = context.strokeStyle = getProgressColor(progress, options.colorsRange); | |
845 | + } | |
846 | + | |
847 | + const {barLeft, barRight, barTop, baseX, width, barBottom, Cx, Cy, Rm, Ro, Ri, strokeWidth} = | |
848 | + context.barDimensions; | |
849 | + | |
850 | + if (context.barDimensions.dashLength) { | |
851 | + context.setLineDash([context.barDimensions.dashLength]); | |
852 | + } | |
853 | + context.lineWidth = strokeWidth; | |
854 | + if (options.roundedLineCap) { | |
855 | + context.lineCap = 'round'; | |
856 | + } else { | |
857 | + context.lineCap = 'butt'; | |
858 | + } | |
859 | + if (options.gaugeType === 'donut') { | |
860 | + if (options.neonGlowBrightness) { | |
861 | + context.strokeStyle = neonColor; | |
862 | + } | |
863 | + context.beginPath(); | |
864 | + context.arc(Cx, Cy, Rm, options.donutStartAngle, options.donutStartAngle + | |
865 | + (options.donutEndAngle - options.donutStartAngle) * progress); | |
866 | + context.stroke(); | |
867 | + if (options.neonGlowBrightness && !options.isMobile) { | |
868 | + drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, | |
869 | + options.donutStartAngle, options.donutEndAngle); | |
870 | + } | |
871 | + } else if (options.gaugeType === 'arc') { | |
872 | + if (options.neonGlowBrightness) { | |
873 | + context.strokeStyle = neonColor; | |
874 | + } | |
875 | + context.beginPath(); | |
876 | + context.arc(Cx, Cy, Rm, Math.PI, Math.PI + Math.PI * progress); | |
877 | + context.stroke(); | |
878 | + if (options.neonGlowBrightness && !options.isMobile) { | |
879 | + drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, false); | |
880 | + } | |
881 | + } else if (options.gaugeType === 'horizontalBar') { | |
882 | + if (options.neonGlowBrightness) { | |
883 | + context.strokeStyle = neonColor; | |
884 | + } | |
885 | + context.beginPath(); | |
886 | + context.moveTo(barLeft,barTop + strokeWidth/2); | |
887 | + context.lineTo(barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2); | |
888 | + context.stroke(); | |
889 | + if (options.neonGlowBrightness && !options.isMobile) { | |
890 | + drawBarGlow(context, barLeft, barTop + strokeWidth/2, | |
891 | + barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2, | |
892 | + neonColor, strokeWidth, false); | |
893 | + } | |
894 | + } else if (options.gaugeType === 'verticalBar') { | |
895 | + if (options.neonGlowBrightness) { | |
896 | + context.strokeStyle = neonColor; | |
897 | + } | |
898 | + context.beginPath(); | |
899 | + context.moveTo(baseX + width/2, barBottom); | |
900 | + context.lineTo(baseX + width/2, barBottom - (barBottom-barTop)*progress); | |
901 | + context.stroke(); | |
902 | + if (options.neonGlowBrightness && !options.isMobile) { | |
903 | + drawBarGlow(context, baseX + width/2, barBottom, | |
904 | + baseX + width/2, barBottom - (barBottom-barTop)*progress, | |
905 | + neonColor, strokeWidth, true); | |
906 | + } | |
907 | + } | |
908 | + | |
909 | +} | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { JsonSettingsSchema } from '@shared/models/widget.models'; | |
18 | +import { GaugeType } from '@home/components/widget/lib/canvas-digital-gauge'; | |
19 | +import { AnimationRule } from '@home/components/widget/lib/analogue-gauge.models'; | |
20 | +import { FontSettings } from '@home/components/widget/lib/settings.models'; | |
21 | + | |
22 | +export interface DigitalGaugeSettings { | |
23 | + minValue?: number; | |
24 | + maxValue?: number; | |
25 | + gaugeType?: GaugeType; | |
26 | + donutStartAngle?: number; | |
27 | + neonGlowBrightness?: number; | |
28 | + dashThickness?: number; | |
29 | + roundedLineCap?: boolean; | |
30 | + title?: string; | |
31 | + showTitle?: boolean; | |
32 | + unitTitle?: string; | |
33 | + showUnitTitle?: boolean; | |
34 | + showTimestamp?: boolean; | |
35 | + timestampFormat?: string; | |
36 | + showValue?: boolean; | |
37 | + showMinMax?: boolean; | |
38 | + gaugeWidthScale?: number; | |
39 | + defaultColor?: string; | |
40 | + gaugeColor?: string; | |
41 | + levelColors?: string[]; | |
42 | + animation?: boolean; | |
43 | + animationDuration?: number; | |
44 | + animationRule?: AnimationRule; | |
45 | + titleFont?: FontSettings; | |
46 | + labelFont?: FontSettings; | |
47 | + valueFont?: FontSettings; | |
48 | + minMaxFont?: FontSettings; | |
49 | + decimals?: number; | |
50 | + units?: string; | |
51 | + hideValue?: boolean; | |
52 | + hideMinMax?: boolean; | |
53 | +} | |
54 | + | |
55 | +export const digitalGaugeSettingsSchema: JsonSettingsSchema = { | |
56 | + schema: { | |
57 | + type: 'object', | |
58 | + title: 'Settings', | |
59 | + properties: { | |
60 | + minValue: { | |
61 | + title: 'Minimum value', | |
62 | + type: 'number', | |
63 | + default: 0 | |
64 | + }, | |
65 | + maxValue: { | |
66 | + title: 'Maximum value', | |
67 | + type: 'number', | |
68 | + default: 100 | |
69 | + }, | |
70 | + gaugeType: { | |
71 | + title: 'Gauge type', | |
72 | + type: 'string', | |
73 | + default: 'arc' | |
74 | + }, | |
75 | + donutStartAngle: { | |
76 | + title: 'Angle to start from when in donut mode', | |
77 | + type: 'number', | |
78 | + default: 90 | |
79 | + }, | |
80 | + neonGlowBrightness: { | |
81 | + title: 'Neon glow effect brightness, (0-100), 0 - disable effect', | |
82 | + type: 'number', | |
83 | + default: 0 | |
84 | + }, | |
85 | + dashThickness: { | |
86 | + title: 'Thickness of the stripes, 0 - no stripes', | |
87 | + type: 'number', | |
88 | + default: 0 | |
89 | + }, | |
90 | + roundedLineCap: { | |
91 | + title: 'Display rounded line cap', | |
92 | + type: 'boolean', | |
93 | + default: false | |
94 | + }, | |
95 | + title: { | |
96 | + title: 'Gauge title', | |
97 | + type: 'string', | |
98 | + default: null | |
99 | + }, | |
100 | + showTitle: { | |
101 | + title: 'Show gauge title', | |
102 | + type: 'boolean', | |
103 | + default: false | |
104 | + }, | |
105 | + unitTitle: { | |
106 | + title: 'Unit title', | |
107 | + type: 'string', | |
108 | + default: null | |
109 | + }, | |
110 | + showUnitTitle: { | |
111 | + title: 'Show unit title', | |
112 | + type: 'boolean', | |
113 | + default: false | |
114 | + }, | |
115 | + showTimestamp: { | |
116 | + title: 'Show value timestamp', | |
117 | + type: 'boolean', | |
118 | + default: false | |
119 | + }, | |
120 | + timestampFormat: { | |
121 | + title: 'Timestamp format', | |
122 | + type: 'string', | |
123 | + default: 'yyyy-MM-dd HH:mm:ss' | |
124 | + }, | |
125 | + showValue: { | |
126 | + title: 'Show value text', | |
127 | + type: 'boolean', | |
128 | + default: true | |
129 | + }, | |
130 | + showMinMax: { | |
131 | + title: 'Show min and max values', | |
132 | + type: 'boolean', | |
133 | + default: true | |
134 | + }, | |
135 | + gaugeWidthScale: { | |
136 | + title: 'Width of the gauge element', | |
137 | + type: 'number', | |
138 | + default: 0.75 | |
139 | + }, | |
140 | + defaultColor: { | |
141 | + title: 'Default color', | |
142 | + type: 'string', | |
143 | + default: null | |
144 | + }, | |
145 | + gaugeColor: { | |
146 | + title: 'Background color of the gauge element', | |
147 | + type: 'string', | |
148 | + default: null | |
149 | + }, | |
150 | + levelColors: { | |
151 | + title: 'Colors of indicator, from lower to upper', | |
152 | + type: 'array', | |
153 | + items: { | |
154 | + title: 'Color', | |
155 | + type: 'string' | |
156 | + } | |
157 | + }, | |
158 | + animation: { | |
159 | + title: 'Enable animation', | |
160 | + type: 'boolean', | |
161 | + default: true | |
162 | + }, | |
163 | + animationDuration: { | |
164 | + title: 'Animation duration', | |
165 | + type: 'number', | |
166 | + default: 500 | |
167 | + }, | |
168 | + animationRule: { | |
169 | + title: 'Animation rule', | |
170 | + type: 'string', | |
171 | + default: 'linear' | |
172 | + }, | |
173 | + titleFont: { | |
174 | + title: 'Gauge title font', | |
175 | + type: 'object', | |
176 | + properties: { | |
177 | + family: { | |
178 | + title: 'Font family', | |
179 | + type: 'string', | |
180 | + default: 'Roboto' | |
181 | + }, | |
182 | + size: { | |
183 | + title: 'Size', | |
184 | + type: 'number', | |
185 | + default: 12 | |
186 | + }, | |
187 | + style: { | |
188 | + title: 'Style', | |
189 | + type: 'string', | |
190 | + default: 'normal' | |
191 | + }, | |
192 | + weight: { | |
193 | + title: 'Weight', | |
194 | + type: 'string', | |
195 | + default: '500' | |
196 | + }, | |
197 | + color: { | |
198 | + title: 'color', | |
199 | + type: 'string', | |
200 | + default: null | |
201 | + } | |
202 | + } | |
203 | + }, | |
204 | + labelFont: { | |
205 | + title: 'Font of label showing under value', | |
206 | + type: 'object', | |
207 | + properties: { | |
208 | + family: { | |
209 | + title: 'Font family', | |
210 | + type: 'string', | |
211 | + default: 'Roboto' | |
212 | + }, | |
213 | + size: { | |
214 | + title: 'Size', | |
215 | + type: 'number', | |
216 | + default: 8 | |
217 | + }, | |
218 | + style: { | |
219 | + title: 'Style', | |
220 | + type: 'string', | |
221 | + default: 'normal' | |
222 | + }, | |
223 | + weight: { | |
224 | + title: 'Weight', | |
225 | + type: 'string', | |
226 | + default: '500' | |
227 | + }, | |
228 | + color: { | |
229 | + title: 'color', | |
230 | + type: 'string', | |
231 | + default: null | |
232 | + } | |
233 | + } | |
234 | + }, | |
235 | + valueFont: { | |
236 | + title: 'Font of label showing current value', | |
237 | + type: 'object', | |
238 | + properties: { | |
239 | + family: { | |
240 | + title: 'Font family', | |
241 | + type: 'string', | |
242 | + default: 'Roboto' | |
243 | + }, | |
244 | + size: { | |
245 | + title: 'Size', | |
246 | + type: 'number', | |
247 | + default: 18 | |
248 | + }, | |
249 | + style: { | |
250 | + title: 'Style', | |
251 | + type: 'string', | |
252 | + default: 'normal' | |
253 | + }, | |
254 | + weight: { | |
255 | + title: 'Weight', | |
256 | + type: 'string', | |
257 | + default: '500' | |
258 | + }, | |
259 | + color: { | |
260 | + title: 'color', | |
261 | + type: 'string', | |
262 | + default: null | |
263 | + } | |
264 | + } | |
265 | + }, | |
266 | + minMaxFont: { | |
267 | + title: 'Font of minimum and maximum labels', | |
268 | + type: 'object', | |
269 | + properties: { | |
270 | + family: { | |
271 | + title: 'Font family', | |
272 | + type: 'string', | |
273 | + default: 'Roboto' | |
274 | + }, | |
275 | + size: { | |
276 | + title: 'Size', | |
277 | + type: 'number', | |
278 | + default: 10 | |
279 | + }, | |
280 | + style: { | |
281 | + title: 'Style', | |
282 | + type: 'string', | |
283 | + default: 'normal' | |
284 | + }, | |
285 | + weight: { | |
286 | + title: 'Weight', | |
287 | + type: 'string', | |
288 | + default: '500' | |
289 | + }, | |
290 | + color: { | |
291 | + title: 'color', | |
292 | + type: 'string', | |
293 | + default: null | |
294 | + } | |
295 | + } | |
296 | + } | |
297 | + } | |
298 | + }, | |
299 | + form: [ | |
300 | + 'minValue', | |
301 | + 'maxValue', | |
302 | + { | |
303 | + key: 'gaugeType', | |
304 | + type: 'rc-select', | |
305 | + multiple: false, | |
306 | + items: [ | |
307 | + { | |
308 | + value: 'arc', | |
309 | + label: 'Arc' | |
310 | + }, | |
311 | + { | |
312 | + value: 'donut', | |
313 | + label: 'Donut' | |
314 | + }, | |
315 | + { | |
316 | + value: 'horizontalBar', | |
317 | + label: 'Horizontal bar' | |
318 | + }, | |
319 | + { | |
320 | + value: 'verticalBar', | |
321 | + label: 'Vertical bar' | |
322 | + } | |
323 | + ] | |
324 | + }, | |
325 | + 'donutStartAngle', | |
326 | + 'neonGlowBrightness', | |
327 | + 'dashThickness', | |
328 | + 'roundedLineCap', | |
329 | + 'title', | |
330 | + 'showTitle', | |
331 | + 'unitTitle', | |
332 | + 'showUnitTitle', | |
333 | + 'showTimestamp', | |
334 | + 'timestampFormat', | |
335 | + 'showValue', | |
336 | + 'showMinMax', | |
337 | + 'gaugeWidthScale', | |
338 | + { | |
339 | + key: 'defaultColor', | |
340 | + type: 'color' | |
341 | + }, | |
342 | + { | |
343 | + key: 'gaugeColor', | |
344 | + type: 'color' | |
345 | + }, | |
346 | + { | |
347 | + key: 'levelColors', | |
348 | + items: [ | |
349 | + { | |
350 | + key: 'levelColors[]', | |
351 | + type: 'color' | |
352 | + } | |
353 | + ] | |
354 | + }, | |
355 | + 'animation', | |
356 | + 'animationDuration', | |
357 | + { | |
358 | + key: 'animationRule', | |
359 | + type: 'rc-select', | |
360 | + multiple: false, | |
361 | + items: [ | |
362 | + { | |
363 | + value: 'linear', | |
364 | + label: 'Linear' | |
365 | + }, | |
366 | + { | |
367 | + value: 'quad', | |
368 | + label: 'Quad' | |
369 | + }, | |
370 | + { | |
371 | + value: 'quint', | |
372 | + label: 'Quint' | |
373 | + }, | |
374 | + { | |
375 | + value: 'cycle', | |
376 | + label: 'Cycle' | |
377 | + }, | |
378 | + { | |
379 | + value: 'bounce', | |
380 | + label: 'Bounce' | |
381 | + }, | |
382 | + { | |
383 | + value: 'elastic', | |
384 | + label: 'Elastic' | |
385 | + }, | |
386 | + { | |
387 | + value: 'dequad', | |
388 | + label: 'Dequad' | |
389 | + }, | |
390 | + { | |
391 | + value: 'dequint', | |
392 | + label: 'Dequint' | |
393 | + }, | |
394 | + { | |
395 | + value: 'decycle', | |
396 | + label: 'Decycle' | |
397 | + }, | |
398 | + { | |
399 | + value: 'debounce', | |
400 | + label: 'Debounce' | |
401 | + }, | |
402 | + { | |
403 | + value: 'delastic', | |
404 | + label: 'Delastic' | |
405 | + } | |
406 | + ] | |
407 | + }, | |
408 | + { | |
409 | + key: 'titleFont', | |
410 | + items: [ | |
411 | + 'titleFont.family', | |
412 | + 'titleFont.size', | |
413 | + { | |
414 | + key: 'titleFont.style', | |
415 | + type: 'rc-select', | |
416 | + multiple: false, | |
417 | + items: [ | |
418 | + { | |
419 | + value: 'normal', | |
420 | + label: 'Normal' | |
421 | + }, | |
422 | + { | |
423 | + value: 'italic', | |
424 | + label: 'Italic' | |
425 | + }, | |
426 | + { | |
427 | + value: 'oblique', | |
428 | + label: 'Oblique' | |
429 | + } | |
430 | + ] | |
431 | + }, | |
432 | + { | |
433 | + key: 'titleFont.weight', | |
434 | + type: 'rc-select', | |
435 | + multiple: false, | |
436 | + items: [ | |
437 | + { | |
438 | + value: 'normal', | |
439 | + label: 'Normal' | |
440 | + }, | |
441 | + { | |
442 | + value: 'bold', | |
443 | + label: 'Bold' | |
444 | + }, | |
445 | + { | |
446 | + value: 'bolder', | |
447 | + label: 'Bolder' | |
448 | + }, | |
449 | + { | |
450 | + value: 'lighter', | |
451 | + label: 'Lighter' | |
452 | + }, | |
453 | + { | |
454 | + value: '100', | |
455 | + label: '100' | |
456 | + }, | |
457 | + { | |
458 | + value: '200', | |
459 | + label: '200' | |
460 | + }, | |
461 | + { | |
462 | + value: '300', | |
463 | + label: '300' | |
464 | + }, | |
465 | + { | |
466 | + value: '400', | |
467 | + label: '400' | |
468 | + }, | |
469 | + { | |
470 | + value: '500', | |
471 | + label: '500' | |
472 | + }, | |
473 | + { | |
474 | + value: '600', | |
475 | + label: '600' | |
476 | + }, | |
477 | + { | |
478 | + value: '700', | |
479 | + label: '800' | |
480 | + }, | |
481 | + { | |
482 | + value: '800', | |
483 | + label: '800' | |
484 | + }, | |
485 | + { | |
486 | + value: '900', | |
487 | + label: '900' | |
488 | + } | |
489 | + ] | |
490 | + }, | |
491 | + { | |
492 | + key: 'titleFont.color', | |
493 | + type: 'color' | |
494 | + } | |
495 | + ] | |
496 | + }, | |
497 | + { | |
498 | + key: 'labelFont', | |
499 | + items: [ | |
500 | + 'labelFont.family', | |
501 | + 'labelFont.size', | |
502 | + { | |
503 | + key: 'labelFont.style', | |
504 | + type: 'rc-select', | |
505 | + multiple: false, | |
506 | + items: [ | |
507 | + { | |
508 | + value: 'normal', | |
509 | + label: 'Normal' | |
510 | + }, | |
511 | + { | |
512 | + value: 'italic', | |
513 | + label: 'Italic' | |
514 | + }, | |
515 | + { | |
516 | + value: 'oblique', | |
517 | + label: 'Oblique' | |
518 | + } | |
519 | + ] | |
520 | + }, | |
521 | + { | |
522 | + key: 'labelFont.weight', | |
523 | + type: 'rc-select', | |
524 | + multiple: false, | |
525 | + items: [ | |
526 | + { | |
527 | + value: 'normal', | |
528 | + label: 'Normal' | |
529 | + }, | |
530 | + { | |
531 | + value: 'bold', | |
532 | + label: 'Bold' | |
533 | + }, | |
534 | + { | |
535 | + value: 'bolder', | |
536 | + label: 'Bolder' | |
537 | + }, | |
538 | + { | |
539 | + value: 'lighter', | |
540 | + label: 'Lighter' | |
541 | + }, | |
542 | + { | |
543 | + value: '100', | |
544 | + label: '100' | |
545 | + }, | |
546 | + { | |
547 | + value: '200', | |
548 | + label: '200' | |
549 | + }, | |
550 | + { | |
551 | + value: '300', | |
552 | + label: '300' | |
553 | + }, | |
554 | + { | |
555 | + value: '400', | |
556 | + label: '400' | |
557 | + }, | |
558 | + { | |
559 | + value: '500', | |
560 | + label: '500' | |
561 | + }, | |
562 | + { | |
563 | + value: '600', | |
564 | + label: '600' | |
565 | + }, | |
566 | + { | |
567 | + value: '700', | |
568 | + label: '800' | |
569 | + }, | |
570 | + { | |
571 | + value: '800', | |
572 | + label: '800' | |
573 | + }, | |
574 | + { | |
575 | + value: '900', | |
576 | + label: '900' | |
577 | + } | |
578 | + ] | |
579 | + }, | |
580 | + { | |
581 | + key: 'labelFont.color', | |
582 | + type: 'color' | |
583 | + } | |
584 | + ] | |
585 | + }, | |
586 | + { | |
587 | + key: 'valueFont', | |
588 | + items: [ | |
589 | + 'valueFont.family', | |
590 | + 'valueFont.size', | |
591 | + { | |
592 | + key: 'valueFont.style', | |
593 | + type: 'rc-select', | |
594 | + multiple: false, | |
595 | + items: [ | |
596 | + { | |
597 | + value: 'normal', | |
598 | + label: 'Normal' | |
599 | + }, | |
600 | + { | |
601 | + value: 'italic', | |
602 | + label: 'Italic' | |
603 | + }, | |
604 | + { | |
605 | + value: 'oblique', | |
606 | + label: 'Oblique' | |
607 | + } | |
608 | + ] | |
609 | + }, | |
610 | + { | |
611 | + key: 'valueFont.weight', | |
612 | + type: 'rc-select', | |
613 | + multiple: false, | |
614 | + items: [ | |
615 | + { | |
616 | + value: 'normal', | |
617 | + label: 'Normal' | |
618 | + }, | |
619 | + { | |
620 | + value: 'bold', | |
621 | + label: 'Bold' | |
622 | + }, | |
623 | + { | |
624 | + value: 'bolder', | |
625 | + label: 'Bolder' | |
626 | + }, | |
627 | + { | |
628 | + value: 'lighter', | |
629 | + label: 'Lighter' | |
630 | + }, | |
631 | + { | |
632 | + value: '100', | |
633 | + label: '100' | |
634 | + }, | |
635 | + { | |
636 | + value: '200', | |
637 | + label: '200' | |
638 | + }, | |
639 | + { | |
640 | + value: '300', | |
641 | + label: '300' | |
642 | + }, | |
643 | + { | |
644 | + value: '400', | |
645 | + label: '400' | |
646 | + }, | |
647 | + { | |
648 | + value: '500', | |
649 | + label: '500' | |
650 | + }, | |
651 | + { | |
652 | + value: '600', | |
653 | + label: '600' | |
654 | + }, | |
655 | + { | |
656 | + value: '700', | |
657 | + label: '800' | |
658 | + }, | |
659 | + { | |
660 | + value: '800', | |
661 | + label: '800' | |
662 | + }, | |
663 | + { | |
664 | + value: '900', | |
665 | + label: '900' | |
666 | + } | |
667 | + ] | |
668 | + }, | |
669 | + { | |
670 | + key: 'valueFont.color', | |
671 | + type: 'color' | |
672 | + } | |
673 | + ] | |
674 | + }, | |
675 | + { | |
676 | + key: 'minMaxFont', | |
677 | + items: [ | |
678 | + 'minMaxFont.family', | |
679 | + 'minMaxFont.size', | |
680 | + { | |
681 | + key: 'minMaxFont.style', | |
682 | + type: 'rc-select', | |
683 | + multiple: false, | |
684 | + items: [ | |
685 | + { | |
686 | + value: 'normal', | |
687 | + label: 'Normal' | |
688 | + }, | |
689 | + { | |
690 | + value: 'italic', | |
691 | + label: 'Italic' | |
692 | + }, | |
693 | + { | |
694 | + value: 'oblique', | |
695 | + label: 'Oblique' | |
696 | + } | |
697 | + ] | |
698 | + }, | |
699 | + { | |
700 | + key: 'minMaxFont.weight', | |
701 | + type: 'rc-select', | |
702 | + multiple: false, | |
703 | + items: [ | |
704 | + { | |
705 | + value: 'normal', | |
706 | + label: 'Normal' | |
707 | + }, | |
708 | + { | |
709 | + value: 'bold', | |
710 | + label: 'Bold' | |
711 | + }, | |
712 | + { | |
713 | + value: 'bolder', | |
714 | + label: 'Bolder' | |
715 | + }, | |
716 | + { | |
717 | + value: 'lighter', | |
718 | + label: 'Lighter' | |
719 | + }, | |
720 | + { | |
721 | + value: '100', | |
722 | + label: '100' | |
723 | + }, | |
724 | + { | |
725 | + value: '200', | |
726 | + label: '200' | |
727 | + }, | |
728 | + { | |
729 | + value: '300', | |
730 | + label: '300' | |
731 | + }, | |
732 | + { | |
733 | + value: '400', | |
734 | + label: '400' | |
735 | + }, | |
736 | + { | |
737 | + value: '500', | |
738 | + label: '500' | |
739 | + }, | |
740 | + { | |
741 | + value: '600', | |
742 | + label: '600' | |
743 | + }, | |
744 | + { | |
745 | + value: '700', | |
746 | + label: '800' | |
747 | + }, | |
748 | + { | |
749 | + value: '800', | |
750 | + label: '800' | |
751 | + }, | |
752 | + { | |
753 | + value: '900', | |
754 | + label: '900' | |
755 | + } | |
756 | + ] | |
757 | + }, | |
758 | + { | |
759 | + key: 'minMaxFont.color', | |
760 | + type: 'color' | |
761 | + } | |
762 | + ] | |
763 | + } | |
764 | + ] | |
765 | +}; | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import * as CanvasGauges from 'canvas-gauges'; | |
18 | +import { WidgetContext } from '@home/models/widget-component.models'; | |
19 | +import { DigitalGaugeSettings, digitalGaugeSettingsSchema } from '@home/components/widget/lib/digital-gauge.models'; | |
20 | +import * as tinycolor_ from 'tinycolor2'; | |
21 | +import { isDefined } from '@core/utils'; | |
22 | +import { prepareFontSettings } from '@home/components/widget/lib/settings.models'; | |
23 | +import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; | |
24 | +import { DatePipe } from '@angular/common'; | |
25 | +import { JsonSettingsSchema } from '@shared/models/widget.models'; | |
26 | +import GenericOptions = CanvasGauges.GenericOptions; | |
27 | + | |
28 | +const tinycolor = tinycolor_; | |
29 | + | |
30 | +const digitalGaugeSettingsSchemaValue = digitalGaugeSettingsSchema; | |
31 | + | |
32 | +export class TbCanvasDigitalGauge { | |
33 | + | |
34 | + private localSettings: DigitalGaugeSettings; | |
35 | + | |
36 | + private gauge: CanvasDigitalGauge; | |
37 | + | |
38 | + static get settingsSchema(): JsonSettingsSchema { | |
39 | + return digitalGaugeSettingsSchemaValue; | |
40 | + } | |
41 | + | |
42 | + constructor(protected ctx: WidgetContext, canvasId: string) { | |
43 | + const gaugeElement = $('#'+canvasId, ctx.$container)[0]; | |
44 | + const settings: DigitalGaugeSettings = ctx.settings; | |
45 | + | |
46 | + this.localSettings = {}; | |
47 | + this.localSettings.minValue = settings.minValue || 0; | |
48 | + this.localSettings.maxValue = settings.maxValue || 100; | |
49 | + this.localSettings.gaugeType = settings.gaugeType || 'arc'; | |
50 | + this.localSettings.neonGlowBrightness = settings.neonGlowBrightness || 0; | |
51 | + this.localSettings.dashThickness = settings.dashThickness || 0; | |
52 | + this.localSettings.roundedLineCap = settings.roundedLineCap === true; | |
53 | + | |
54 | + const dataKey = ctx.data[0].dataKey; | |
55 | + const keyColor = settings.defaultColor || dataKey.color; | |
56 | + | |
57 | + this.localSettings.unitTitle = ((settings.showUnitTitle === true) ? | |
58 | + (settings.unitTitle && settings.unitTitle.length > 0 ? | |
59 | + settings.unitTitle : dataKey.label) : ''); | |
60 | + | |
61 | + this.localSettings.showTimestamp = settings.showTimestamp === true; | |
62 | + this.localSettings.timestampFormat = settings.timestampFormat && settings.timestampFormat.length ? | |
63 | + settings.timestampFormat : 'yyyy-MM-dd HH:mm:ss'; | |
64 | + | |
65 | + this.localSettings.gaugeWidthScale = settings.gaugeWidthScale || 0.75; | |
66 | + this.localSettings.gaugeColor = settings.gaugeColor || tinycolor(keyColor).setAlpha(0.2).toRgbString(); | |
67 | + | |
68 | + if (!settings.levelColors || settings.levelColors.length <= 0) { | |
69 | + this.localSettings.levelColors = [keyColor]; | |
70 | + } else { | |
71 | + this.localSettings.levelColors = settings.levelColors.slice(); | |
72 | + } | |
73 | + | |
74 | + this.localSettings.decimals = isDefined(dataKey.decimals) ? dataKey.decimals : | |
75 | + ((isDefined(settings.decimals) && settings.decimals !== null) | |
76 | + ? settings.decimals : ctx.decimals); | |
77 | + | |
78 | + this.localSettings.units = dataKey.units && dataKey.units.length ? dataKey.units : | |
79 | + (isDefined(settings.units) && settings.units.length > 0 ? settings.units : ctx.units); | |
80 | + | |
81 | + this.localSettings.hideValue = settings.showValue !== true; | |
82 | + this.localSettings.hideMinMax = settings.showMinMax !== true; | |
83 | + | |
84 | + this.localSettings.title = ((settings.showTitle === true) ? | |
85 | + (settings.title && settings.title.length > 0 ? | |
86 | + settings.title : dataKey.label) : ''); | |
87 | + | |
88 | + if (!this.localSettings.unitTitle && this.localSettings.showTimestamp) { | |
89 | + this.localSettings.unitTitle = ' '; | |
90 | + } | |
91 | + | |
92 | + this.localSettings.titleFont = prepareFontSettings(settings.titleFont, { | |
93 | + size: 12, | |
94 | + style: 'normal', | |
95 | + weight: '500', | |
96 | + color: keyColor | |
97 | + }); | |
98 | + | |
99 | + this.localSettings.valueFont = prepareFontSettings(settings.valueFont, { | |
100 | + size: 18, | |
101 | + style: 'normal', | |
102 | + weight: '500', | |
103 | + color: keyColor | |
104 | + }); | |
105 | + | |
106 | + this.localSettings.minMaxFont = prepareFontSettings(settings.minMaxFont, { | |
107 | + size: 10, | |
108 | + style: 'normal', | |
109 | + weight: '500', | |
110 | + color: keyColor | |
111 | + }); | |
112 | + | |
113 | + this.localSettings.labelFont = prepareFontSettings(settings.labelFont, { | |
114 | + size: 8, | |
115 | + style: 'normal', | |
116 | + weight: '500', | |
117 | + color: keyColor | |
118 | + }); | |
119 | + | |
120 | + const gaugeData: CanvasDigitalGaugeOptions = { | |
121 | + renderTo: gaugeElement, | |
122 | + | |
123 | + gaugeWidthScale: this.localSettings.gaugeWidthScale, | |
124 | + gaugeColor: this.localSettings.gaugeColor, | |
125 | + levelColors: this.localSettings.levelColors, | |
126 | + | |
127 | + title: this.localSettings.title, | |
128 | + | |
129 | + fontTitleSize: this.localSettings.titleFont.size, | |
130 | + fontTitleStyle: this.localSettings.titleFont.style, | |
131 | + fontTitleWeight: this.localSettings.titleFont.weight, | |
132 | + colorTitle: this.localSettings.titleFont.color, | |
133 | + fontTitle: this.localSettings.titleFont.family, | |
134 | + | |
135 | + fontValueSize: this.localSettings.valueFont.size, | |
136 | + fontValueStyle: this.localSettings.valueFont.style, | |
137 | + fontValueWeight: this.localSettings.valueFont.weight, | |
138 | + colorValue: this.localSettings.valueFont.color, | |
139 | + fontValue: this.localSettings.valueFont.family, | |
140 | + | |
141 | + fontMinMaxSize: this.localSettings.minMaxFont.size, | |
142 | + fontMinMaxStyle: this.localSettings.minMaxFont.style, | |
143 | + fontMinMaxWeight: this.localSettings.minMaxFont.weight, | |
144 | + colorMinMax: this.localSettings.minMaxFont.color, | |
145 | + fontMinMax: this.localSettings.minMaxFont.family, | |
146 | + | |
147 | + fontLabelSize: this.localSettings.labelFont.size, | |
148 | + fontLabelStyle: this.localSettings.labelFont.style, | |
149 | + fontLabelWeight: this.localSettings.labelFont.weight, | |
150 | + colorLabel: this.localSettings.labelFont.color, | |
151 | + fontLabel: this.localSettings.labelFont.family, | |
152 | + | |
153 | + minValue: this.localSettings.minValue, | |
154 | + maxValue: this.localSettings.maxValue, | |
155 | + gaugeType: this.localSettings.gaugeType, | |
156 | + dashThickness: this.localSettings.dashThickness, | |
157 | + roundedLineCap: this.localSettings.roundedLineCap, | |
158 | + | |
159 | + symbol: this.localSettings.units, | |
160 | + label: this.localSettings.unitTitle, | |
161 | + showTimestamp: this.localSettings.showTimestamp, | |
162 | + hideValue: this.localSettings.hideValue, | |
163 | + hideMinMax: this.localSettings.hideMinMax, | |
164 | + | |
165 | + valueDec: this.localSettings.decimals, | |
166 | + | |
167 | + neonGlowBrightness: this.localSettings.neonGlowBrightness, | |
168 | + | |
169 | + // animations | |
170 | + animation: settings.animation !== false && !ctx.isMobile, | |
171 | + animationDuration: (isDefined(settings.animationDuration) && settings.animationDuration !== null) | |
172 | + ? settings.animationDuration : 500, | |
173 | + animationRule: settings.animationRule || 'linear', | |
174 | + | |
175 | + isMobile: ctx.isMobile | |
176 | + }; | |
177 | + | |
178 | + this.gauge = new CanvasDigitalGauge(gaugeData).draw(); | |
179 | + } | |
180 | + | |
181 | + update() { | |
182 | + if (this.ctx.data.length > 0) { | |
183 | + const cellData = this.ctx.data[0]; | |
184 | + if (cellData.data.length > 0) { | |
185 | + const tvPair = cellData.data[cellData.data.length - | |
186 | + 1]; | |
187 | + let timestamp; | |
188 | + if (this.localSettings.showTimestamp) { | |
189 | + timestamp = tvPair[0]; | |
190 | + const filter = this.ctx.$injector.get(DatePipe); | |
191 | + (this.gauge.options as CanvasDigitalGaugeOptions).label = | |
192 | + filter.transform(timestamp, this.localSettings.timestampFormat); | |
193 | + } | |
194 | + const value = tvPair[1]; | |
195 | + if(value !== this.gauge.value) { | |
196 | + this.gauge._value = value; | |
197 | + this.gauge.value = value; | |
198 | + } else if (this.localSettings.showTimestamp && this.gauge.timestamp !== timestamp) { | |
199 | + this.gauge.timestamp = timestamp; | |
200 | + } | |
201 | + } | |
202 | + } | |
203 | + } | |
204 | + | |
205 | + mobileModeChanged() { | |
206 | + const animation = this.ctx.settings.animation !== false && !this.ctx.isMobile; | |
207 | + this.gauge.update({animation, isMobile: this.ctx.isMobile} as CanvasDigitalGaugeOptions); | |
208 | + } | |
209 | + | |
210 | + resize() { | |
211 | + this.gauge.update({width: this.ctx.width, height: this.ctx.height} as GenericOptions); | |
212 | + } | |
213 | + | |
214 | +} | ... | ... |
... | ... | @@ -21,11 +21,11 @@ export type FontWeight = 'normal' | 'bold' | 'bolder' | 'lighter' |
21 | 21 | | '600' | '700' | '800' | '900'; |
22 | 22 | |
23 | 23 | export interface FontSettings { |
24 | - family: string; | |
25 | - size: number; | |
26 | - style: FontStyle; | |
27 | - weight: FontWeight; | |
28 | - color: string; | |
24 | + family?: string; | |
25 | + size?: number; | |
26 | + style?: FontStyle; | |
27 | + weight?: FontWeight; | |
28 | + color?: string; | |
29 | 29 | shadowColor?: string; |
30 | 30 | } |
31 | 31 | |
... | ... | @@ -36,3 +36,9 @@ export function getFontFamily(fontSettings: FontSettings): string { |
36 | 36 | } |
37 | 37 | return family; |
38 | 38 | } |
39 | + | |
40 | +export function prepareFontSettings(fontSettings: FontSettings, defaultFontSettings: FontSettings): FontSettings { | |
41 | + const result = {...defaultFontSettings, ...(fontSettings || {})}; | |
42 | + result.family = getFontFamily(result); | |
43 | + return result; | |
44 | +} | ... | ... |
... | ... | @@ -215,6 +215,7 @@ export class WidgetContext { |
215 | 215 | export interface IDynamicWidgetComponent { |
216 | 216 | readonly ctx: WidgetContext; |
217 | 217 | readonly errorMessages: string[]; |
218 | + readonly $injector: Injector; | |
218 | 219 | executingRpcRequest: boolean; |
219 | 220 | rpcEnabled: boolean; |
220 | 221 | rpcErrorText: string; | ... | ... |
... | ... | @@ -17,19 +17,19 @@ |
17 | 17 | --> |
18 | 18 | <mat-tab *ngIf="entity" |
19 | 19 | label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> |
20 | - <tb-attribute-table [active]="attributesTab.isActive" | |
20 | + <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" | |
21 | + [active]="attributesTab.isActive" | |
21 | 22 | [entityId]="entity.id" |
22 | - [entityName]="entity.name" | |
23 | - [defaultAttributeScope]="attributeScopes.SERVER_SCOPE"> | |
23 | + [entityName]="entity.name"> | |
24 | 24 | </tb-attribute-table> |
25 | 25 | </mat-tab> |
26 | 26 | <mat-tab *ngIf="entity" |
27 | 27 | label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> |
28 | - <tb-attribute-table [active]="telemetryTab.isActive" | |
28 | + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
29 | + disableAttributeScopeSelection | |
30 | + [active]="telemetryTab.isActive" | |
29 | 31 | [entityId]="entity.id" |
30 | - [entityName]="entity.name" | |
31 | - [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
32 | - disableAttributeScopeSelection> | |
32 | + [entityName]="entity.name"> | |
33 | 33 | </tb-attribute-table> |
34 | 34 | </mat-tab> |
35 | 35 | <mat-tab *ngIf="entity" |
... | ... | @@ -38,7 +38,7 @@ |
38 | 38 | </mat-tab> |
39 | 39 | <mat-tab *ngIf="entity" |
40 | 40 | label="{{ 'asset.events' | translate }}" #eventsTab="matTab"> |
41 | - <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" | |
41 | + <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="entity.tenantId.id" | |
42 | 42 | [entityId]="entity.id"></tb-event-table> |
43 | 43 | </mat-tab> |
44 | 44 | <mat-tab *ngIf="entity" |
... | ... | @@ -47,5 +47,5 @@ |
47 | 47 | </mat-tab> |
48 | 48 | <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" |
49 | 49 | label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> |
50 | - <tb-audit-log-table [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id" detailsMode="true"></tb-audit-log-table> | |
50 | + <tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table> | |
51 | 51 | </mat-tab> | ... | ... |
... | ... | @@ -17,19 +17,19 @@ |
17 | 17 | --> |
18 | 18 | <mat-tab *ngIf="entity" |
19 | 19 | label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> |
20 | - <tb-attribute-table [active]="attributesTab.isActive" | |
20 | + <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" | |
21 | + [active]="attributesTab.isActive" | |
21 | 22 | [entityId]="entity.id" |
22 | - [entityName]="entity.name" | |
23 | - [defaultAttributeScope]="attributeScopes.SERVER_SCOPE"> | |
23 | + [entityName]="entity.name"> | |
24 | 24 | </tb-attribute-table> |
25 | 25 | </mat-tab> |
26 | 26 | <mat-tab *ngIf="entity" |
27 | 27 | label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> |
28 | - <tb-attribute-table [active]="telemetryTab.isActive" | |
28 | + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
29 | + disableAttributeScopeSelection | |
30 | + [active]="telemetryTab.isActive" | |
29 | 31 | [entityId]="entity.id" |
30 | - [entityName]="entity.name" | |
31 | - [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
32 | - disableAttributeScopeSelection> | |
32 | + [entityName]="entity.name"> | |
33 | 33 | </tb-attribute-table> |
34 | 34 | </mat-tab> |
35 | 35 | <mat-tab *ngIf="entity" |
... | ... | @@ -38,7 +38,7 @@ |
38 | 38 | </mat-tab> |
39 | 39 | <mat-tab *ngIf="entity" |
40 | 40 | label="{{ 'customer.events' | translate }}" #eventsTab="matTab"> |
41 | - <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" | |
41 | + <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="entity.tenantId.id" | |
42 | 42 | [entityId]="entity.id"></tb-event-table> |
43 | 43 | </mat-tab> |
44 | 44 | <mat-tab *ngIf="entity" |
... | ... | @@ -47,5 +47,5 @@ |
47 | 47 | </mat-tab> |
48 | 48 | <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" |
49 | 49 | label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> |
50 | - <tb-audit-log-table [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.CUSTOMER" [customerId]="entity.id" detailsMode="true"></tb-audit-log-table> | |
50 | + <tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.CUSTOMER" [customerId]="entity.id"></tb-audit-log-table> | |
51 | 51 | </mat-tab> | ... | ... |
... | ... | @@ -17,19 +17,19 @@ |
17 | 17 | --> |
18 | 18 | <mat-tab *ngIf="entity" |
19 | 19 | label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> |
20 | - <tb-attribute-table [active]="attributesTab.isActive" | |
20 | + <tb-attribute-table [defaultAttributeScope]="attributeScopes.CLIENT_SCOPE" | |
21 | + [active]="attributesTab.isActive" | |
21 | 22 | [entityId]="entity.id" |
22 | - [entityName]="entity.name" | |
23 | - [defaultAttributeScope]="attributeScopes.CLIENT_SCOPE"> | |
23 | + [entityName]="entity.name"> | |
24 | 24 | </tb-attribute-table> |
25 | 25 | </mat-tab> |
26 | 26 | <mat-tab *ngIf="entity" |
27 | 27 | label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> |
28 | - <tb-attribute-table [active]="telemetryTab.isActive" | |
28 | + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
29 | + disableAttributeScopeSelection | |
30 | + [active]="telemetryTab.isActive" | |
29 | 31 | [entityId]="entity.id" |
30 | - [entityName]="entity.name" | |
31 | - [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
32 | - disableAttributeScopeSelection> | |
32 | + [entityName]="entity.name"> | |
33 | 33 | </tb-attribute-table> |
34 | 34 | </mat-tab> |
35 | 35 | <mat-tab *ngIf="entity" |
... | ... | @@ -38,7 +38,7 @@ |
38 | 38 | </mat-tab> |
39 | 39 | <mat-tab *ngIf="entity" |
40 | 40 | label="{{ 'device.events' | translate }}" #eventsTab="matTab"> |
41 | - <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" | |
41 | + <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="entity.tenantId.id" | |
42 | 42 | [entityId]="entity.id"></tb-event-table> |
43 | 43 | </mat-tab> |
44 | 44 | <mat-tab *ngIf="entity" |
... | ... | @@ -47,5 +47,5 @@ |
47 | 47 | </mat-tab> |
48 | 48 | <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" |
49 | 49 | label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> |
50 | - <tb-audit-log-table [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id" detailsMode="true"></tb-audit-log-table> | |
50 | + <tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table> | |
51 | 51 | </mat-tab> | ... | ... |
... | ... | @@ -17,19 +17,19 @@ |
17 | 17 | --> |
18 | 18 | <mat-tab *ngIf="entity" |
19 | 19 | label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> |
20 | - <tb-attribute-table [active]="attributesTab.isActive" | |
20 | + <tb-attribute-table [defaultAttributeScope]="attributeScopes.CLIENT_SCOPE" | |
21 | + [active]="attributesTab.isActive" | |
21 | 22 | [entityId]="entity.id" |
22 | - [entityName]="entity.name" | |
23 | - [defaultAttributeScope]="attributeScopes.CLIENT_SCOPE"> | |
23 | + [entityName]="entity.name"> | |
24 | 24 | </tb-attribute-table> |
25 | 25 | </mat-tab> |
26 | 26 | <mat-tab *ngIf="entity" |
27 | 27 | label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> |
28 | - <tb-attribute-table [active]="telemetryTab.isActive" | |
28 | + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
29 | + disableAttributeScopeSelection | |
30 | + [active]="telemetryTab.isActive" | |
29 | 31 | [entityId]="entity.id" |
30 | - [entityName]="entity.name" | |
31 | - [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
32 | - disableAttributeScopeSelection> | |
32 | + [entityName]="entity.name"> | |
33 | 33 | </tb-attribute-table> |
34 | 34 | </mat-tab> |
35 | 35 | <mat-tab *ngIf="entity" |
... | ... | @@ -38,7 +38,7 @@ |
38 | 38 | </mat-tab> |
39 | 39 | <mat-tab *ngIf="entity" |
40 | 40 | label="{{ 'entity-view.events' | translate }}" #eventsTab="matTab"> |
41 | - <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="entity.tenantId.id" | |
41 | + <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="entity.tenantId.id" | |
42 | 42 | [entityId]="entity.id"></tb-event-table> |
43 | 43 | </mat-tab> |
44 | 44 | <mat-tab *ngIf="entity" |
... | ... | @@ -47,5 +47,5 @@ |
47 | 47 | </mat-tab> |
48 | 48 | <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" |
49 | 49 | label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> |
50 | - <tb-audit-log-table [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id" detailsMode="true"></tb-audit-log-table> | |
50 | + <tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table> | |
51 | 51 | </mat-tab> | ... | ... |
... | ... | @@ -121,9 +121,9 @@ |
121 | 121 | </tb-rule-node> |
122 | 122 | </mat-tab> |
123 | 123 | <mat-tab *ngIf="editingRuleNode.ruleNodeId" label="{{ 'rulenode.events' | translate }}" #eventsTab="matTab"> |
124 | - <tb-event-table [active]="eventsTab.isActive" | |
125 | - [debugEventTypes]="[debugEventTypes.DEBUG_RULE_NODE]" | |
124 | + <tb-event-table [debugEventTypes]="[debugEventTypes.DEBUG_RULE_NODE]" | |
126 | 125 | [defaultEventType]="debugEventTypes.DEBUG_RULE_NODE" |
126 | + [active]="eventsTab.isActive" | |
127 | 127 | [tenantId]="ruleChain.tenantId.id" |
128 | 128 | [entityId]="editingRuleNode.ruleNodeId"></tb-event-table> |
129 | 129 | </mat-tab> | ... | ... |
... | ... | @@ -17,19 +17,19 @@ |
17 | 17 | --> |
18 | 18 | <mat-tab *ngIf="entity" |
19 | 19 | label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> |
20 | - <tb-attribute-table [active]="attributesTab.isActive" | |
20 | + <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" | |
21 | + [active]="attributesTab.isActive" | |
21 | 22 | [entityId]="entity.id" |
22 | - [entityName]="entity.name" | |
23 | - [defaultAttributeScope]="attributeScopes.SERVER_SCOPE"> | |
23 | + [entityName]="entity.name"> | |
24 | 24 | </tb-attribute-table> |
25 | 25 | </mat-tab> |
26 | 26 | <mat-tab *ngIf="entity" |
27 | 27 | label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> |
28 | - <tb-attribute-table [active]="telemetryTab.isActive" | |
28 | + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
29 | + disableAttributeScopeSelection | |
30 | + [active]="telemetryTab.isActive" | |
29 | 31 | [entityId]="entity.id" |
30 | - [entityName]="entity.name" | |
31 | - [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
32 | - disableAttributeScopeSelection> | |
32 | + [entityName]="entity.name"> | |
33 | 33 | </tb-attribute-table> |
34 | 34 | </mat-tab> |
35 | 35 | <mat-tab *ngIf="entity" |
... | ... | @@ -38,9 +38,9 @@ |
38 | 38 | </mat-tab> |
39 | 39 | <mat-tab *ngIf="entity" |
40 | 40 | label="{{ 'rulechain.events' | translate }}" #eventsTab="matTab"> |
41 | - <tb-event-table [active]="eventsTab.isActive" | |
42 | - [debugEventTypes]="[debugEventTypes.DEBUG_RULE_CHAIN]" | |
41 | + <tb-event-table [debugEventTypes]="[debugEventTypes.DEBUG_RULE_CHAIN]" | |
43 | 42 | [defaultEventType]="debugEventTypes.DEBUG_RULE_CHAIN" |
43 | + [active]="eventsTab.isActive" | |
44 | 44 | [tenantId]="entity.tenantId.id" |
45 | 45 | [entityId]="entity.id"></tb-event-table> |
46 | 46 | </mat-tab> |
... | ... | @@ -50,5 +50,5 @@ |
50 | 50 | </mat-tab> |
51 | 51 | <mat-tab *ngIf="entity && authUser.authority === authorities.TENANT_ADMIN" |
52 | 52 | label="{{ 'audit-log.audit-logs' | translate }}" #auditLogsTab="matTab"> |
53 | - <tb-audit-log-table [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id" detailsMode="true"></tb-audit-log-table> | |
53 | + <tb-audit-log-table detailsMode="true" [active]="auditLogsTab.isActive" [auditLogMode]="auditLogModes.ENTITY" [entityId]="entity.id"></tb-audit-log-table> | |
54 | 54 | </mat-tab> | ... | ... |
... | ... | @@ -17,19 +17,19 @@ |
17 | 17 | --> |
18 | 18 | <mat-tab *ngIf="entity" |
19 | 19 | label="{{ 'attribute.attributes' | translate }}" #attributesTab="matTab"> |
20 | - <tb-attribute-table [active]="attributesTab.isActive" | |
20 | + <tb-attribute-table [defaultAttributeScope]="attributeScopes.SERVER_SCOPE" | |
21 | + [active]="attributesTab.isActive" | |
21 | 22 | [entityId]="entity.id" |
22 | - [entityName]="entity.name" | |
23 | - [defaultAttributeScope]="attributeScopes.SERVER_SCOPE"> | |
23 | + [entityName]="entity.name"> | |
24 | 24 | </tb-attribute-table> |
25 | 25 | </mat-tab> |
26 | 26 | <mat-tab *ngIf="entity" |
27 | 27 | label="{{ 'attribute.latest-telemetry' | translate }}" #telemetryTab="matTab"> |
28 | - <tb-attribute-table [active]="telemetryTab.isActive" | |
28 | + <tb-attribute-table [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
29 | + disableAttributeScopeSelection | |
30 | + [active]="telemetryTab.isActive" | |
29 | 31 | [entityId]="entity.id" |
30 | - [entityName]="entity.name" | |
31 | - [defaultAttributeScope]="latestTelemetryTypes.LATEST_TELEMETRY" | |
32 | - disableAttributeScopeSelection> | |
32 | + [entityName]="entity.name"> | |
33 | 33 | </tb-attribute-table> |
34 | 34 | </mat-tab> |
35 | 35 | <mat-tab *ngIf="entity" |
... | ... | @@ -38,7 +38,7 @@ |
38 | 38 | </mat-tab> |
39 | 39 | <mat-tab *ngIf="entity" |
40 | 40 | label="{{ 'tenant.events' | translate }}" #eventsTab="matTab"> |
41 | - <tb-event-table [active]="eventsTab.isActive" [defaultEventType]="eventTypes.ERROR" [tenantId]="nullUid" | |
41 | + <tb-event-table [defaultEventType]="eventTypes.ERROR" [active]="eventsTab.isActive" [tenantId]="nullUid" | |
42 | 42 | [entityId]="entity.id"></tb-event-table> |
43 | 43 | </mat-tab> |
44 | 44 | <mat-tab *ngIf="entity" | ... | ... |
... | ... | @@ -91,6 +91,7 @@ import { TbFlot } from '@home/components/widget/lib/flot-widget'; |
91 | 91 | import { TbAnalogueCompass } from '@home/components/widget/lib/analogue-compass'; |
92 | 92 | import { TbAnalogueRadialGauge } from '@home/components/widget/lib/analogue-radial-gauge'; |
93 | 93 | import { TbAnalogueLinearGauge } from '@home/components/widget/lib/analogue-linear-gauge'; |
94 | +import { TbCanvasDigitalGauge } from '@home/components/widget/lib/digital-gauge'; | |
94 | 95 | |
95 | 96 | import * as tinycolor_ from 'tinycolor2'; |
96 | 97 | |
... | ... | @@ -102,3 +103,4 @@ const tinycolor = tinycolor_; |
102 | 103 | (window as any).TbAnalogueCompass = TbAnalogueCompass; |
103 | 104 | (window as any).TbAnalogueRadialGauge = TbAnalogueRadialGauge; |
104 | 105 | (window as any).TbAnalogueLinearGauge = TbAnalogueLinearGauge; |
106 | +(window as any).TbCanvasDigitalGauge = TbCanvasDigitalGauge; | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2019 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | + | |
18 | +// tslint:disable-next-line:no-namespace | |
19 | +declare namespace CanvasGauges { | |
20 | + const drawings: Drawings; | |
21 | +} | |
22 | + | |
23 | +interface Drawings { | |
24 | + font(options: CanvasGauges.GenericOptions, target: string, baseSize: number): string; | |
25 | + normalizedValue(options: CanvasGauges.GenericOptions): {normal: number, indented: number}; | |
26 | + verifyError(err: any); | |
27 | +} | ... | ... |
... | ... | @@ -19,7 +19,8 @@ |
19 | 19 | "src/typings/jquery.typings.d.ts", |
20 | 20 | "src/typings/jquery.flot.typings.d.ts", |
21 | 21 | "src/typings/jquery.jstree.typings.d.ts", |
22 | - "src/typings/split.js.typings.d.ts" | |
22 | + "src/typings/split.js.typings.d.ts", | |
23 | + "src/typings/canvas-gauges.typings.d.ts" | |
23 | 24 | ], |
24 | 25 | "paths": { |
25 | 26 | "@app/*": ["src/app/*"], | ... | ... |