diff --git a/application/src/main/resources/logback.xml b/application/src/main/resources/logback.xml index 02101a8eaa..e154de2d9b 100644 --- a/application/src/main/resources/logback.xml +++ b/application/src/main/resources/logback.xml @@ -29,6 +29,8 @@ + + diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 01b6bcfb5e..218bfb8255 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -615,6 +615,10 @@ swagger: queue: type: "${TB_QUEUE_TYPE:in-memory}" # in-memory or kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) + in_memory: + stats: + # For debug lvl + print-interval-ms: "${TB_QUEUE_IN_MEMORY_STATS_PRINT_INTERVAL_MS:60000}" kafka: bootstrap.servers: "${TB_KAFKA_SERVERS:localhost:9092}" acks: "${TB_KAFKA_ACKS:all}" diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryStorage.java b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryStorage.java index 994ca26305..31f7958bbf 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryStorage.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryStorage.java @@ -23,27 +23,21 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; @Slf4j public final class InMemoryStorage { private static InMemoryStorage instance; private final ConcurrentHashMap> storage; - private static ScheduledExecutorService statExecutor; private InMemoryStorage() { storage = new ConcurrentHashMap<>(); - statExecutor = Executors.newSingleThreadScheduledExecutor(); - statExecutor.scheduleAtFixedRate(this::printStats, 60, 60, TimeUnit.SECONDS); } - private void printStats() { + public void printStats() { storage.forEach((topic, queue) -> { if (queue.size() > 0) { - log.debug("Topic: [{}], Queue size: [{}]", topic, queue.size()); + log.debug("[{}] Queue Size [{}]", topic, queue.size()); } }); } @@ -90,9 +84,4 @@ public final class InMemoryStorage { storage.clear(); } - public void destroy() { - if (statExecutor != null) { - statExecutor.shutdownNow(); - } - } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueProducer.java b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueProducer.java index 84a9a1fdf0..cfcd788a16 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueProducer.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueProducer.java @@ -53,6 +53,6 @@ public class InMemoryTbQueueProducer implements TbQueuePro @Override public void stop() { - storage.destroy(); + } } diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java index fbdbeaab0c..8ebda6e7b9 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/provider/InMemoryMonolithQueueFactory.java @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.provider; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.gen.js.JsInvokeProtos; @@ -28,6 +29,7 @@ import org.thingsboard.server.queue.common.TbProtoJsQueueMsg; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.PartitionService; import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; +import org.thingsboard.server.queue.memory.InMemoryStorage; import org.thingsboard.server.queue.memory.InMemoryTbQueueConsumer; import org.thingsboard.server.queue.memory.InMemoryTbQueueProducer; import org.thingsboard.server.queue.settings.TbQueueCoreSettings; @@ -47,6 +49,7 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE private final TbQueueRuleEngineSettings ruleEngineSettings; private final TbQueueTransportApiSettings transportApiSettings; private final TbQueueTransportNotificationSettings transportNotificationSettings; + private final InMemoryStorage storage; public InMemoryMonolithQueueFactory(PartitionService partitionService, TbQueueCoreSettings coreSettings, TbQueueRuleEngineSettings ruleEngineSettings, @@ -59,6 +62,7 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE this.ruleEngineSettings = ruleEngineSettings; this.transportApiSettings = transportApiSettings; this.transportNotificationSettings = transportNotificationSettings; + this.storage = InMemoryStorage.getInstance(); } @Override @@ -120,4 +124,9 @@ public class InMemoryMonolithQueueFactory implements TbCoreQueueFactory, TbRuleE public TbQueueRequestTemplate, TbProtoQueueMsg> createRemoteJsRequestTemplate() { return null; } + + @Scheduled(fixedRateString = "${queue.in_memory.stats.print-interval-ms:60000}") + private void printInMemoryStats() { + storage.printStats(); + } } diff --git a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts index f6c9f29eb1..dc1a48c3c3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/dynamic-widget.component.ts @@ -41,6 +41,7 @@ import { CustomDialogService } from '@home/components/widget/dialog/custom-dialo import { DatePipe } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; import { DomSanitizer } from '@angular/platform-browser'; +import { Router } from '@angular/router'; @Directive() export class DynamicWidgetComponent extends PageComponent implements IDynamicWidgetComponent, OnInit, OnDestroy { @@ -77,6 +78,7 @@ export class DynamicWidgetComponent extends PageComponent implements IDynamicWid this.ctx.translate = $injector.get(TranslateService); this.ctx.http = $injector.get(HttpClient); this.ctx.sanitizer = $injector.get(DomSanitizer); + this.ctx.router = $injector.get(Router); this.ctx.$scope = this; if (this.ctx.defaultSubscription) { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts index 89c12860dd..564065e9be 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.models.ts @@ -163,7 +163,7 @@ export const analogueCompassSettingsSchema: JsonSettingsSchema = { form: [ { key: 'majorTicks', - items:[ + items: [ 'majorTicks[]' ] }, @@ -267,7 +267,7 @@ export const analogueCompassSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts index 11da2efd90..d3a8db18a7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-compass.ts @@ -43,7 +43,7 @@ export class TbAnalogueCompass extends TbBaseGauge 0) ? deepClone(settings.majorTicks) : - ['N','NE','E','SE','S','SW','W','NW']; + ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']; majorTicks.push(majorTicks[0]); return { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts index 19e6525a91..9919173fbc 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-gauge.models.ts @@ -492,7 +492,7 @@ export const analogueGaugeSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', @@ -581,7 +581,7 @@ export const analogueGaugeSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', @@ -670,7 +670,7 @@ export const analogueGaugeSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', @@ -759,7 +759,7 @@ export const analogueGaugeSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', @@ -842,7 +842,7 @@ export abstract class TbBaseGauge { private gauge: BaseGauge; protected constructor(protected ctx: WidgetContext, canvasId: string) { - const gaugeElement = $('#'+canvasId, ctx.$container)[0]; + const gaugeElement = $('#' + canvasId, ctx.$container)[0]; const settings: S = ctx.settings; const gaugeData: O = this.createGaugeOptions(gaugeElement, settings); this.gauge = this.createGauge(gaugeData as O).draw(); @@ -859,7 +859,7 @@ export abstract class TbBaseGauge { const tvPair = cellData.data[cellData.data.length - 1]; const value = tvPair[1]; - if(value !== this.gauge.value) { + if (value !== this.gauge.value) { this.gauge.value = value; } } @@ -876,10 +876,10 @@ export abstract class TbBaseGauge { } } -export abstract class TbAnalogueGauge extends TbBaseGauge { +export abstract class TbAnalogueGauge extends TbBaseGauge { protected constructor(ctx: WidgetContext, canvasId: string) { - super(ctx,canvasId); + super(ctx, canvasId); } protected createGaugeOptions(gaugeElement: HTMLElement, settings: S): O { @@ -891,26 +891,26 @@ export abstract class TbAnalogueGauge{ +export class TbAnalogueLinearGauge extends TbAnalogueGauge{ static get settingsSchema(): JsonSettingsSchema { return analogueLinearGaugeSettingsSchemaValue; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts index 1571c5c3d0..05c1969657 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/analogue-radial-gauge.ts @@ -28,7 +28,7 @@ import BaseGauge = CanvasGauges.BaseGauge; const analogueRadialGaugeSettingsSchemaValue = getAnalogueRadialGaugeSettingsSchema(); -export class TbAnalogueRadialGauge extends TbAnalogueGauge{ +export class TbAnalogueRadialGauge extends TbAnalogueGauge{ static get settingsSchema(): JsonSettingsSchema { return analogueRadialGaugeSettingsSchemaValue; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts index 507d84fcc4..82c0904a6c 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/canvas-digital-gauge.ts @@ -18,7 +18,7 @@ import * as CanvasGauges from 'canvas-gauges'; import { FontStyle, FontWeight } from '@home/components/widget/lib/settings.models'; import * as tinycolor_ from 'tinycolor2'; import { ColorFormats } from 'tinycolor2'; -import { isDefined, isString, isUndefined, padValue } from '@core/utils'; +import { isDefined, isDefinedAndNotNull, isString, isUndefined, padValue } from '@core/utils'; import GenericOptions = CanvasGauges.GenericOptions; import BaseGauge = CanvasGauges.BaseGauge; @@ -220,7 +220,7 @@ export class CanvasDigitalGauge extends BaseGauge { public _value: number; constructor(options: CanvasDigitalGaugeOptions) { - options = {...defaultDigitalGaugeOptions,...(options || {})}; + options = {...defaultDigitalGaugeOptions, ...(options || {})}; super(CanvasDigitalGauge.configure(options)); this.initValueClone(); } @@ -236,10 +236,10 @@ export class CanvasDigitalGauge extends BaseGauge { } if (options.gaugeType === 'donut') { - if (!options.donutStartAngle) { + if (!isDefinedAndNotNull(options.donutStartAngle)) { options.donutStartAngle = 1.5 * Math.PI; } - if (!options.donutEndAngle) { + if (!isDefinedAndNotNull(options.donutEndAngle)) { options.donutEndAngle = options.donutStartAngle + 2 * Math.PI; } } @@ -255,7 +255,7 @@ export class CanvasDigitalGauge extends BaseGauge { const levelColor: any = options.levelColors[i]; if (levelColor !== null) { let percentage: number; - if(isColorProperty){ + if (isColorProperty) { percentage = inc * i; } else { percentage = CanvasDigitalGauge.normalizeValue(levelColor.value, options.minValue, options.maxValue); @@ -280,7 +280,7 @@ export class CanvasDigitalGauge extends BaseGauge { options.ticksValue = []; for (const tick of options.ticks) { if (tick !== null) { - options.ticksValue.push(CanvasDigitalGauge.normalizeValue(tick, options.minValue, options.maxValue)) + options.ticksValue.push(CanvasDigitalGauge.normalizeValue(tick, options.minValue, options.maxValue)); } } @@ -294,7 +294,7 @@ export class CanvasDigitalGauge extends BaseGauge { return options; } - static normalizeValue (value: number, min: number, max: number): number { + static normalizeValue(value: number, min: number, max: number): number { const normalValue = (value - min) / (max - min); if (normalValue <= 0) { return 0; @@ -539,8 +539,8 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, titleOffset += bd.fontSizeFactor * 2; bd.titleY = bd.baseY + titleOffset; titleOffset += bd.fontSizeFactor * 2; - bd.Cy += titleOffset/2; - bd.Ro -= titleOffset/2; + bd.Cy += titleOffset / 2; + bd.Ro -= titleOffset / 2; } bd.Ri = bd.Ro - bd.width / 6.666666666666667 * gws * 1.2; bd.Cx = bd.baseX + bd.width / 2; @@ -575,8 +575,8 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, const valueHeight = determineFontHeight(options, 'Value', bd.fontSizeFactor).height; const labelHeight = determineFontHeight(options, 'Label', bd.fontSizeFactor).height; const total = valueHeight + labelHeight; - bd.labelY = bd.Cy + total/2; - bd.valueY = bd.Cy - total/2 + valueHeight/2; + bd.labelY = bd.Cy + total / 2; + bd.valueY = bd.Cy - total / 2 + valueHeight / 2; } else { bd.valueY = bd.Cy; } @@ -586,8 +586,8 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, bd.labelY = bd.Cy + (8 + options.fontLabelSize) * bd.fontSizeFactor; bd.minY = bd.maxY = bd.labelY; if (options.roundedLineCap) { - bd.minY += bd.strokeWidth/2; - bd.maxY += bd.strokeWidth/2; + bd.minY += bd.strokeWidth / 2; + bd.maxY += bd.strokeWidth / 2; } bd.minX = bd.Cx - bd.Rm; bd.maxX = bd.Cx + bd.Rm; @@ -604,15 +604,15 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, if (options.hideMinMax && options.label === '') { bd.labelY = bd.barBottom; - bd.barLeft = bd.origBaseX + options.fontMinMaxSize/3 * bd.fontSizeFactor; - bd.barRight = bd.origBaseX + w + /*bd.width*/ - options.fontMinMaxSize/3 * bd.fontSizeFactor; + bd.barLeft = bd.origBaseX + options.fontMinMaxSize / 3 * bd.fontSizeFactor; + bd.barRight = bd.origBaseX + w + /*bd.width*/ -options.fontMinMaxSize / 3 * bd.fontSizeFactor; } else { context.font = Drawings.font(options, 'MinMax', bd.fontSizeFactor); - const minTextWidth = context.measureText(options.minValue+'').width; - const maxTextWidth = context.measureText(options.maxValue+'').width; + const minTextWidth = context.measureText(options.minValue + '').width; + const maxTextWidth = context.measureText(options.maxValue + '').width; const maxW = Math.max(minTextWidth, maxTextWidth); - bd.minX = bd.origBaseX + maxW/2 + options.fontMinMaxSize/3 * bd.fontSizeFactor; - bd.maxX = bd.origBaseX + w + /*bd.width*/ - maxW/2 - options.fontMinMaxSize/3 * bd.fontSizeFactor; + bd.minX = bd.origBaseX + maxW / 2 + options.fontMinMaxSize / 3 * bd.fontSizeFactor; + bd.maxX = bd.origBaseX + w + /*bd.width*/ -maxW / 2 - options.fontMinMaxSize / 3 * bd.fontSizeFactor; bd.barLeft = bd.minX; bd.barRight = bd.maxX; bd.labelY = bd.barBottom + (8 + options.fontLabelSize) * bd.fontSizeFactor; @@ -632,7 +632,7 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, bd.barBottom = bd.labelY - (8 + options.fontLabelSize) * bd.fontSizeFactor; } bd.minX = bd.maxX = - bd.baseX + bd.width/2 + bd.strokeWidth/2 + options.fontMinMaxSize/3 * bd.fontSizeFactor; + bd.baseX + bd.width / 2 + bd.strokeWidth / 2 + options.fontMinMaxSize / 3 * bd.fontSizeFactor; bd.minY = bd.barBottom; bd.maxY = bd.barTop; bd.fontMinMaxBaseline = 'middle'; @@ -658,13 +658,13 @@ function barDimensions(context: DigitalGaugeCanvasRenderingContext2D, // tslint:disable-next-line:no-bitwise dashCount = (dashCount - 1) | 1; } - bd.dashLength = Math.ceil(circumference/dashCount); + bd.dashLength = Math.ceil(circumference / dashCount); } return bd; } -function determineFontHeight (options: CanvasDigitalGaugeOptions, target: string, baseSize: number): FontHeightInfo { +function determineFontHeight(options: CanvasDigitalGaugeOptions, target: string, baseSize: number): FontHeightInfo { const fontStyleStr = 'font-style:' + options['font' + target + 'Style'] + ';font-weight:' + options['font' + target + 'Weight'] + ';font-size:' + options['font' + target + 'Size'] * baseSize + 'px;font-family:' + @@ -688,9 +688,9 @@ function determineFontHeight (options: CanvasDigitalGaugeOptions, target: string try { result = {}; - block.css({ verticalAlign: 'baseline' }); + block.css({verticalAlign: 'baseline'}); result.ascent = block.offset().top - text.offset().top; - block.css({ verticalAlign: 'bottom' }); + block.css({verticalAlign: 'bottom'}); result.height = block.offset().top - text.offset().top; result.descent = result.height - result.ascent; } finally { @@ -720,15 +720,15 @@ function drawBackground(context: DigitalGaugeCanvasRenderingContext2D, options: context.stroke(); } else if (options.gaugeType === 'arc') { context.arc(context.barDimensions.Cx, context.barDimensions.Cy, - context.barDimensions.Rm, Math.PI, 2*Math.PI); + context.barDimensions.Rm, Math.PI, 2 * Math.PI); context.stroke(); } else if (options.gaugeType === 'horizontalBar') { - context.moveTo(barLeft,barTop + strokeWidth/2); - context.lineTo(barRight,barTop + strokeWidth/2); + context.moveTo(barLeft, barTop + strokeWidth / 2); + context.lineTo(barRight, barTop + strokeWidth / 2); context.stroke(); } else if (options.gaugeType === 'verticalBar') { - context.moveTo(baseX + width/2, barBottom); - context.lineTo(baseX + width/2, barTop); + context.moveTo(baseX + width / 2, barBottom); + context.lineTo(baseX + width / 2, barTop); context.stroke(); } } @@ -740,7 +740,9 @@ function drawText(context: DigitalGaugeCanvasRenderingContext2D, options: Canvas } function drawDigitalTitle(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) { - if (!options.title || typeof options.title !== 'string') return; + if (!options.title || typeof options.title !== 'string') { + return; + } const {titleY, width, baseX, fontSizeFactor} = context.barDimensions; @@ -756,7 +758,9 @@ function drawDigitalTitle(context: DigitalGaugeCanvasRenderingContext2D, options } function drawDigitalLabel(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) { - if (!options.label || options.label === '') return; + if (!options.label || options.label === '') { + return; + } const {labelY, baseX, width, fontSizeFactor} = context.barDimensions; @@ -772,7 +776,9 @@ function drawDigitalLabel(context: DigitalGaugeCanvasRenderingContext2D, options } function drawDigitalMinMax(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions) { - if (options.hideMinMax || options.gaugeType === 'donut') return; + if (options.hideMinMax || options.gaugeType === 'donut') { + return; + } const {minY, maxY, minX, maxX, fontSizeFactor, fontMinMaxAlign, fontMinMaxBaseline} = context.barDimensions; @@ -782,12 +788,14 @@ function drawDigitalMinMax(context: DigitalGaugeCanvasRenderingContext2D, option context.textBaseline = fontMinMaxBaseline; context.font = Drawings.font(options, 'MinMax', fontSizeFactor); context.lineWidth = 0; - drawText(context, options, 'MinMax', options.minValue+'', minX, minY); - drawText(context, options, 'MinMax', options.maxValue+'', maxX, maxY); + drawText(context, options, 'MinMax', options.minValue + '', minX, minY); + drawText(context, options, 'MinMax', options.maxValue + '', maxX, maxY); } function drawDigitalValue(context: DigitalGaugeCanvasRenderingContext2D, options: CanvasDigitalGaugeOptions, value: any) { - if (options.hideValue) return; + if (options.hideValue) { + return; + } const {valueY, baseX, width, fontSizeFactor, fontValueBaseline} = context.barDimensions; @@ -837,16 +845,16 @@ function drawArcGlow(context: DigitalGaugeCanvasRenderingContext2D, context.setLineDash([]); const strokeWidth = Ro - Ri; const blur = 0.55; - const edge = strokeWidth*blur; - context.lineWidth = strokeWidth+edge; - const stop = blur/(2*blur+2); - const glowGradient = context.createRadialGradient(Cx,Cy,Ri-edge/2,Cx,Cy,Ro+edge/2); + const edge = strokeWidth * blur; + context.lineWidth = strokeWidth + edge; + const stop = blur / (2 * blur + 2); + const glowGradient = context.createRadialGradient(Cx, Cy, Ri - edge / 2, Cx, Cy, Ro + edge / 2); const color1 = tinycolor(color).setAlpha(0.5).toRgbString(); const color2 = tinycolor(color).setAlpha(0).toRgbString(); - glowGradient.addColorStop(0,color2); - glowGradient.addColorStop(stop,color1); - glowGradient.addColorStop(1.0-stop,color1); - glowGradient.addColorStop(1,color2); + glowGradient.addColorStop(0, color2); + glowGradient.addColorStop(stop, color1); + glowGradient.addColorStop(1.0 - stop, color1); + glowGradient.addColorStop(1, color2); context.strokeStyle = glowGradient; context.beginPath(); const e = 0.01 * Math.PI; @@ -863,21 +871,21 @@ function drawBarGlow(context: DigitalGaugeCanvasRenderingContext2D, startX: numb endX: number, endY: number, color: string, strokeWidth: number, isVertical: boolean) { context.setLineDash([]); const blur = 0.55; - const edge = strokeWidth*blur; - context.lineWidth = strokeWidth+edge; - const stop = blur/(2*blur+2); - const gradientStartX = isVertical ? startX - context.lineWidth/2 : 0; - const gradientStartY = isVertical ? 0 : startY - context.lineWidth/2; - const gradientStopX = isVertical ? startX + context.lineWidth/2 : 0; - const gradientStopY = isVertical ? 0 : startY + context.lineWidth/2; - - const glowGradient = context.createLinearGradient(gradientStartX,gradientStartY,gradientStopX,gradientStopY); + const edge = strokeWidth * blur; + context.lineWidth = strokeWidth + edge; + const stop = blur / (2 * blur + 2); + const gradientStartX = isVertical ? startX - context.lineWidth / 2 : 0; + const gradientStartY = isVertical ? 0 : startY - context.lineWidth / 2; + const gradientStopX = isVertical ? startX + context.lineWidth / 2 : 0; + const gradientStopY = isVertical ? 0 : startY + context.lineWidth / 2; + + const glowGradient = context.createLinearGradient(gradientStartX, gradientStartY, gradientStopX, gradientStopY); const color1 = tinycolor(color).setAlpha(0.5).toRgbString(); const color2 = tinycolor(color).setAlpha(0).toRgbString(); - glowGradient.addColorStop(0,color2); - glowGradient.addColorStop(stop,color1); - glowGradient.addColorStop(1.0-stop,color1); - glowGradient.addColorStop(1,color2); + glowGradient.addColorStop(0, color2); + glowGradient.addColorStop(stop, color1); + glowGradient.addColorStop(1.0 - stop, color1); + glowGradient.addColorStop(1, color2); context.strokeStyle = glowGradient; const dx = isVertical ? 0 : 0.05 * context.lineWidth; const dy = isVertical ? 0.05 * context.lineWidth : 0; @@ -984,12 +992,12 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, context.strokeStyle = neonColor; } context.beginPath(); - context.moveTo(barLeft,barTop + strokeWidth/2); - context.lineTo(barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2); + context.moveTo(barLeft, barTop + strokeWidth / 2); + context.lineTo(barLeft + (barRight - barLeft) * progress, barTop + strokeWidth / 2); context.stroke(); if (options.neonGlowBrightness && !options.isMobile) { - drawBarGlow(context, barLeft, barTop + strokeWidth/2, - barLeft + (barRight-barLeft)*progress, barTop + strokeWidth/2, + drawBarGlow(context, barLeft, barTop + strokeWidth / 2, + barLeft + (barRight - barLeft) * progress, barTop + strokeWidth / 2, neonColor, strokeWidth, false); } drawTickBar(context, options.ticksValue, barLeft, barTop, barRight - barLeft, strokeWidth, @@ -999,12 +1007,12 @@ function drawProgress(context: DigitalGaugeCanvasRenderingContext2D, context.strokeStyle = neonColor; } context.beginPath(); - context.moveTo(baseX + width/2, barBottom); - context.lineTo(baseX + width/2, barBottom - (barBottom-barTop)*progress); + context.moveTo(baseX + width / 2, barBottom); + context.lineTo(baseX + width / 2, barBottom - (barBottom - barTop) * progress); context.stroke(); if (options.neonGlowBrightness && !options.isMobile) { - drawBarGlow(context, baseX + width/2, barBottom, - baseX + width/2, barBottom - (barBottom-barTop)*progress, + drawBarGlow(context, baseX + width / 2, barBottom, + baseX + width / 2, barBottom - (barBottom - barTop) * progress, neonColor, strokeWidth, true); } drawTickBar(context, options.ticksValue, baseX + width / 2, barTop, barTop - barBottom, strokeWidth, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts index 3c1b7d12c2..fa622cb2a5 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.models.ts @@ -694,7 +694,7 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', @@ -783,7 +783,7 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', @@ -872,7 +872,7 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', @@ -961,7 +961,7 @@ export const digitalGaugeSettingsSchema: JsonSettingsSchema = { }, { value: '700', - label: '800' + label: '700' }, { value: '800', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts index c2372135ad..11cc8dc732 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/digital-gauge.ts @@ -25,7 +25,7 @@ import { FixedLevelColors } from '@home/components/widget/lib/digital-gauge.models'; import * as tinycolor_ from 'tinycolor2'; -import { isDefined } from '@core/utils'; +import { isDefined, isDefinedAndNotNull } from '@core/utils'; import { prepareFontSettings } from '@home/components/widget/lib/settings.models'; import { CanvasDigitalGauge, CanvasDigitalGaugeOptions } from '@home/components/widget/lib/canvas-digital-gauge'; import { DatePipe } from '@angular/common'; @@ -53,7 +53,7 @@ export class TbCanvasDigitalGauge { } constructor(protected ctx: WidgetContext, canvasId: string) { - const gaugeElement = $('#'+canvasId, ctx.$container)[0]; + const gaugeElement = $('#' + canvasId, ctx.$container)[0]; const settings: DigitalGaugeSettings = ctx.settings; this.localSettings = {}; @@ -80,7 +80,7 @@ export class TbCanvasDigitalGauge { this.localSettings.useFixedLevelColor = settings.useFixedLevelColor || false; if (!settings.useFixedLevelColor) { - if (!settings.levelColors || settings.levelColors.length <= 0) { + if (!settings.levelColors || settings.levelColors.length === 0) { this.localSettings.levelColors = [keyColor]; } else { this.localSettings.levelColors = settings.levelColors.slice(); @@ -97,14 +97,15 @@ export class TbCanvasDigitalGauge { this.localSettings.colorTicks = settings.colorTicks || '#666'; this.localSettings.decimals = isDefined(dataKey.decimals) ? dataKey.decimals : - ((isDefined(settings.decimals) && settings.decimals !== null) - ? settings.decimals : ctx.decimals); + (isDefinedAndNotNull(settings.decimals) ? settings.decimals : ctx.decimals); this.localSettings.units = dataKey.units && dataKey.units.length ? dataKey.units : (isDefined(settings.units) && settings.units.length > 0 ? settings.units : ctx.units); this.localSettings.hideValue = settings.showValue !== true; this.localSettings.hideMinMax = settings.showMinMax !== true; + this.localSettings.donutStartAngle = isDefinedAndNotNull(settings.donutStartAngle) ? + -TbCanvasDigitalGauge.toRadians(settings.donutStartAngle) : null; this.localSettings.title = ((settings.showTitle === true) ? (settings.title && settings.title.length > 0 ? @@ -191,14 +192,15 @@ export class TbCanvasDigitalGauge { hideValue: this.localSettings.hideValue, hideMinMax: this.localSettings.hideMinMax, + donutStartAngle: this.localSettings.donutStartAngle, + valueDec: this.localSettings.decimals, neonGlowBrightness: this.localSettings.neonGlowBrightness, // animations animation: settings.animation !== false && !ctx.isMobile, - animationDuration: (isDefined(settings.animationDuration) && settings.animationDuration !== null) - ? settings.animationDuration : 500, + animationDuration: isDefinedAndNotNull(settings.animationDuration) ? settings.animationDuration : 500, animationRule: settings.animationRule || 'linear', isMobile: ctx.isMobile @@ -241,7 +243,7 @@ export class TbCanvasDigitalGauge { if (findDataKey) { findDataKey.settings.push(settings); } else { - datasource.dataKeys.push(dataKey) + datasource.dataKeys.push(dataKey); } } else { const datasourceAttribute: Datasource = { @@ -257,17 +259,23 @@ export class TbCanvasDigitalGauge { return datasources; } + private static toRadians(angle: number): number { + return angle * (Math.PI / 180); + } + init() { - if (this.localSettings.useFixedLevelColor) { - if (this.localSettings.fixedLevelColors && this.localSettings.fixedLevelColors.length > 0) { - this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors); - } + let updateSetting = false; - if (this.localSettings.showTicks) { - if (this.localSettings.ticksValue && this.localSettings.ticksValue.length) { - this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue); - } - } + if (this.localSettings.useFixedLevelColor && this.localSettings.fixedLevelColors?.length > 0) { + this.localSettings.levelColors = this.settingLevelColorsSubscribe(this.localSettings.fixedLevelColors); + updateSetting = true; + } + if (this.localSettings.showTicks && this.localSettings.ticksValue?.length) { + this.localSettings.ticks = this.settingTicksSubscribe(this.localSettings.ticksValue); + updateSetting = true; + } + + if (updateSetting) { this.updateSetting(); } } @@ -281,7 +289,7 @@ export class TbCanvasDigitalGauge { predefineLevelColors.push({ value: levelSetting.value, color - }) + }); } else if (levelSetting.entityAlias && levelSetting.attribute) { try { levelColorsDatasource = TbCanvasDigitalGauge.generateDatasource(this.ctx, levelColorsDatasource, @@ -293,7 +301,7 @@ export class TbCanvasDigitalGauge { } } - for(const levelColor of options){ + for (const levelColor of options) { if (levelColor.from) { setLevelColor.call(this, levelColor.from, levelColor.color); } @@ -313,9 +321,9 @@ export class TbCanvasDigitalGauge { let ticksDatasource: Datasource[] = []; const predefineTicks: number[] = []; - for(const tick of options){ + for (const tick of options) { if (tick.valueSource === 'predefinedValue' && isFinite(tick.value)) { - predefineTicks.push(tick.value) + predefineTicks.push(tick.value); } else if (tick.entityAlias && tick.attribute) { try { ticksDatasource = TbCanvasDigitalGauge @@ -398,7 +406,7 @@ export class TbCanvasDigitalGauge { filter.transform(timestamp, this.localSettings.timestampFormat); } const value = tvPair[1]; - if(value !== this.gauge.value) { + if (value !== this.gauge.value) { if (!this.gauge.options.animation) { this.gauge._value = value; } diff --git a/ui-ngx/src/app/modules/home/models/services.map.ts b/ui-ngx/src/app/modules/home/models/services.map.ts index ddc2aa5a1f..8a8fbbce4e 100644 --- a/ui-ngx/src/app/modules/home/models/services.map.ts +++ b/ui-ngx/src/app/modules/home/models/services.map.ts @@ -31,6 +31,7 @@ import { CustomerService } from '@core/http/customer.service'; import { DashboardService } from '@core/http/dashboard.service'; import { UserService } from '@core/http/user.service'; import { AlarmService } from '@core/http/alarm.service'; +import { Router } from '@angular/router'; export const ServicesMap = new Map>( [ @@ -49,6 +50,7 @@ export const ServicesMap = new Map>( ['date', DatePipe], ['utils', UtilsService], ['translate', TranslateService], - ['http', HttpClient] + ['http', HttpClient], + ['router', Router] ] ); diff --git a/ui-ngx/src/app/modules/home/models/widget-component.models.ts b/ui-ngx/src/app/modules/home/models/widget-component.models.ts index aa97d99876..3b434d805a 100644 --- a/ui-ngx/src/app/modules/home/models/widget-component.models.ts +++ b/ui-ngx/src/app/modules/home/models/widget-component.models.ts @@ -76,6 +76,7 @@ import { TranslateService } from '@ngx-translate/core'; import { PageLink } from '@shared/models/page/page-link'; import { SortOrder } from '@shared/models/page/sort-order'; import { DomSanitizer } from '@angular/platform-browser'; +import { Router } from '@angular/router'; export interface IWidgetAction { name: string; @@ -157,6 +158,7 @@ export class WidgetContext { translate: TranslateService; http: HttpClient; sanitizer: DomSanitizer; + router: Router; private changeDetectorValue: ChangeDetectorRef; diff --git a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts index 87301900c0..f9b42f12f5 100644 --- a/ui-ngx/src/app/shared/models/ace/service-completion.models.ts +++ b/ui-ngx/src/app/shared/models/ace/service-completion.models.ts @@ -1379,5 +1379,11 @@ export const serviceCompletions: TbEditorCompletions = { 'See DomSanitizer for API reference.', meta: 'service', type: 'DomSanitizer' + }, + router: { + description: 'Router Service
' + + 'See Router for API reference.', + meta: 'service', + type: 'Router' } };