From c4e4f8953a661f204a38ed57e882154e670b7ed8 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Tue, 18 Jul 2017 21:12:40 +0300 Subject: [PATCH 1/3] TB-70: Add knob control widget. --- .../widget_bundles/control_widgets.json | 16 ++ ui/src/app/api/widget.service.js | 4 +- ui/src/app/widget/lib/CanvasDigitalGauge.js | 16 +- ui/src/app/widget/lib/rpc/index.js | 21 ++ ui/src/app/widget/lib/rpc/knob.png | Bin 0 -> 16385 bytes .../widget/lib/rpc/shiny-knob.directive.js | 198 ++++++++++++++++++ ui/src/app/widget/lib/rpc/shiny-knob.scss | 80 +++++++ ui/src/app/widget/lib/rpc/shiny-knob.tpl.html | 30 +++ 8 files changed, 359 insertions(+), 6 deletions(-) create mode 100644 ui/src/app/widget/lib/rpc/index.js create mode 100644 ui/src/app/widget/lib/rpc/knob.png create mode 100644 ui/src/app/widget/lib/rpc/shiny-knob.directive.js create mode 100644 ui/src/app/widget/lib/rpc/shiny-knob.scss create mode 100644 ui/src/app/widget/lib/rpc/shiny-knob.tpl.html diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index ff920b1e32..b06c979211 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -36,6 +36,22 @@ "dataKeySettingsSchema": "{}\n", "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":true,\"backgroundColor\":\"#010101\",\"color\":\"rgba(255, 254, 254, 0.87)\",\"padding\":\"0px\",\"settings\":{\"parseGpioStatusFunction\":\"return body[pin] === true;\",\"gpioStatusChangeRequest\":{\"method\":\"setGpioStatus\",\"paramsBody\":\"{\\n \\\"pin\\\": \\\"{$pin}\\\",\\n \\\"enabled\\\": \\\"{$enabled}\\\"\\n}\"},\"requestTimeout\":500,\"switchPanelBackgroundColor\":\"#b71c1c\",\"gpioStatusRequest\":{\"method\":\"getGpioStatus\",\"paramsBody\":\"{}\"},\"gpioList\":[{\"pin\":1,\"label\":\"GPIO 1\",\"row\":0,\"col\":0,\"_uniqueKey\":0},{\"pin\":2,\"label\":\"GPIO 2\",\"row\":0,\"col\":1,\"_uniqueKey\":1},{\"pin\":3,\"label\":\"GPIO 3\",\"row\":1,\"col\":0,\"_uniqueKey\":2}]},\"title\":\"RPC remote shell\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" } + }, + { + "alias": "shiny_knob_control", + "name": "Shiny Knob Control", + "descriptor": { + "type": "rpc", + "sizeX": 2.5, + "sizeY": 3, + "resources": [], + "templateHtml": "", + "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel md-switch {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel md-switch > div.md-container {\n margin: 0;\n}\n\n.switch-panel.col-0 md-switch {\n padding-left: 8px;\n padding-right: 4px;\n}\n\n.switch-panel.col-1 md-switch {\n padding-left: 4px;\n padding-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", + "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"theme\": {\n \"title\": \"Knob theme\",\n \"type\": \"string\",\n \"default\": \"light\"\n }, \n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n {\n \"key\": \"theme\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"light\",\n \"label\": \"Light\"\n },\n {\n \"value\": \"dark\",\n \"label\": \"Dark\"\n }\n ]\n },\n \"requestTimeout\"\n ]\n}", + "dataKeySettingsSchema": "{}\n", + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"theme\":\"light\",\"initialValue\":50},\"title\":\"Shiny Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + } } ] } \ No newline at end of file diff --git a/ui/src/app/api/widget.service.js b/ui/src/app/api/widget.service.js index 8580b800bd..842b9768dd 100644 --- a/ui/src/app/api/widget.service.js +++ b/ui/src/app/api/widget.service.js @@ -22,6 +22,8 @@ import thingsboardTimeseriesTableWidget from '../widget/lib/timeseries-table-wid import thingsboardAlarmsTableWidget from '../widget/lib/alarms-table-widget'; import thingsboardEntitiesTableWidget from '../widget/lib/entities-table-widget'; +import thingsboardRpcWidgets from '../widget/lib/rpc'; + import TbFlot from '../widget/lib/flot-widget'; import TbAnalogueLinearGauge from '../widget/lib/analogue-linear-gauge'; import TbAnalogueRadialGauge from '../widget/lib/analogue-radial-gauge'; @@ -39,7 +41,7 @@ import thingsboardTypes from '../common/types.constant'; import thingsboardUtils from '../common/utils.service'; export default angular.module('thingsboard.api.widget', ['oc.lazyLoad', thingsboardLedLight, thingsboardTimeseriesTableWidget, - thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardTypes, thingsboardUtils]) + thingsboardAlarmsTableWidget, thingsboardEntitiesTableWidget, thingsboardRpcWidgets, thingsboardTypes, thingsboardUtils]) .factory('widgetService', WidgetService) .name; diff --git a/ui/src/app/widget/lib/CanvasDigitalGauge.js b/ui/src/app/widget/lib/CanvasDigitalGauge.js index e51c558fdf..e6c2e275d2 100644 --- a/ui/src/app/widget/lib/CanvasDigitalGauge.js +++ b/ui/src/app/widget/lib/CanvasDigitalGauge.js @@ -95,6 +95,12 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { options.value = options.minValue; } + if (options.gaugeType === 'donut') { + if (!options.donutStartAngle) { + options.donutStartAngle = 1.5 * Math.PI; + } + } + var colorsCount = options.levelColors.length; var inc = colorsCount > 1 ? (1 / (colorsCount - 1)) : 1; options.colorsRange = []; @@ -473,7 +479,7 @@ function drawBackground(context, options) { context.lineCap = 'round'; } if (options.gaugeType === 'donut') { - context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, 1.5 * Math.PI, 3.5 * Math.PI); + context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, options.donutStartAngle, options.donutStartAngle + 2 * Math.PI); context.stroke(); } else if (options.gaugeType === 'arc') { context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, Math.PI, 2*Math.PI); @@ -605,7 +611,7 @@ function getProgressColor(progress, colorsRange) { } } -function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut) { +function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut, donutStartAngle) { context.setLineDash([]); var strokeWidth = Ro - Ri; var blur = 0.55; @@ -623,7 +629,7 @@ function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut) { context.beginPath(); var e = 0.01 * Math.PI; if (isDonut) { - context.arc(Cx, Cy, Rm, 1.5 * Math.PI - e, 1.5 * Math.PI + 2 * Math.PI * progress + e); + context.arc(Cx, Cy, Rm, donutStartAngle - e, donutStartAngle + 2 * Math.PI * progress + e); } else { context.arc(Cx, Cy, Rm, Math.PI - e, Math.PI + Math.PI * progress + e); } @@ -682,10 +688,10 @@ function drawProgress(context, options, progress) { context.strokeStyle = neonColor; } context.beginPath(); - context.arc(Cx, Cy, Rm, 1.5 * Math.PI, 1.5 * Math.PI + 2 * Math.PI * progress); + context.arc(Cx, Cy, Rm, options.donutStartAngle, options.donutStartAngle + 2 * Math.PI * progress); context.stroke(); if (options.neonGlowBrightness && !options.isMobile) { - drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true); + drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle); } } else if (options.gaugeType === 'arc') { if (options.neonGlowBrightness) { diff --git a/ui/src/app/widget/lib/rpc/index.js b/ui/src/app/widget/lib/rpc/index.js new file mode 100644 index 0000000000..9f2798974b --- /dev/null +++ b/ui/src/app/widget/lib/rpc/index.js @@ -0,0 +1,21 @@ +/* + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import tbShinyKnob from './shiny-knob.directive'; + +export default angular.module('thingsboard.widgets.rpc', [ + tbShinyKnob +]).name; diff --git a/ui/src/app/widget/lib/rpc/knob.png b/ui/src/app/widget/lib/rpc/knob.png new file mode 100644 index 0000000000000000000000000000000000000000..24675b28adb975356eda3b3d715497da27750af8 GIT binary patch literal 16385 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 { + if (vm.ctx) { + init(); + } + }); + + function init() { + + vm.minValue = angular.isDefined(vm.ctx.settings.minValue) ? vm.ctx.settings.minValue : 0; + vm.maxValue = angular.isDefined(vm.ctx.settings.maxValue) ? vm.ctx.settings.maxValue : 100; + + vm.darkTheme = vm.ctx.settings.theme == 'dark'; + + var canvasBarData = { + renderTo: canvasBarElement[0], + hideValue: true, + neonGlowBrightness: vm.darkTheme ? 40 : 0, + gaugeWidthScale: 0.5, + gaugeColor: vm.darkTheme ? 'rgb(23, 26, 28)' : 'rgba(0,0,0,0)', + levelColors: levelColors, + minValue: vm.minValue, + maxValue: vm.maxValue, + gaugeType: 'donut', + dashThickness: 1.5, + donutStartAngle: Math.PI, + animation: false, + animationDuration: 250, + animationRule: 'linear' + }; + + canvasBar = new CanvasDigitalGauge(canvasBarData).draw(); + + knob.on('mousedown touchstart', (e) => { + e.preventDefault(); + var offset = knob.offset(); + var center = { + y : offset.top + knob.height()/2, + x: offset.left + knob.width()/2 + }; + + var a, b, deg, tmp, + rad2deg = 180/Math.PI; + + knob.on('mousemove.rem touchmove.rem', (e) => { + + e = (e.originalEvent.touches) ? e.originalEvent.touches[0] : e; + + a = center.y - e.pageY; + b = center.x - e.pageX; + deg = Math.atan2(a,b)*rad2deg; + + if(deg<0){ + deg = 360 + deg; + } + + if(startDeg == -1){ + startDeg = deg; + } + + tmp = Math.floor((deg-startDeg) + rotation); + + if(tmp < 0){ + tmp = 360 + tmp; + } + else if(tmp > 359){ + tmp = tmp % 360; + } + + if(snap && tmp < snap){ + tmp = 0; + } + if(Math.abs(tmp - lastDeg) > 180){ + return false; + } + currentDeg = tmp; + lastDeg = tmp; + + knobTop.css('transform','rotate('+(currentDeg)+'deg)'); + turn(currentDeg/359); + }); + + $document.on('mouseup.rem touchend.rem',() => { + knob.off('.rem'); + $document.off('.rem'); + rotation = currentDeg; + startDeg = -1; + }); + + }); + + vm.ctx.resize = resize; + resize(); + + var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : vm.minValue; + + setValue(initialValue); + } + + function resize() { + var width = knobContainer.width(); + var height = knobContainer.height(); + var size = Math.min(width, height); + knob.css({width: size, height: size}); + canvasBar.update({width: size, height: size}); + } + + function turn(ratio) { + var value = (vm.minValue + (vm.maxValue - vm.minValue)*ratio).toFixed(2); + if (canvasBar.value != value) { + canvasBar.value = value; + } + onValue(value); + } + + function setValue(value) { + var ratio = (value-vm.minValue) / (vm.maxValue - vm.minValue); + rotation = lastDeg = currentDeg = ratio*360; + knobTop.css('transform','rotate('+(currentDeg)+'deg)'); + if (canvasBar.value != value) { + canvasBar.value = value; + } + vm.value = value; + } + + function onValue(value) { + console.log(`onValue ${value}`); //eslint-disable-line + $scope.$applyAsync(() => { + vm.value = value; + }); + } + +} \ No newline at end of file diff --git a/ui/src/app/widget/lib/rpc/shiny-knob.scss b/ui/src/app/widget/lib/rpc/shiny-knob.scss new file mode 100644 index 0000000000..7ff204c427 --- /dev/null +++ b/ui/src/app/widget/lib/rpc/shiny-knob.scss @@ -0,0 +1,80 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +$knob-img: url('./knob.png'); + +$bars-margin-pct: percentage(0.2); +$shadow-size: 5; +$shadow-size-px: $shadow-size+px; +$shadow-offset-px: $shadow-size/2+px; + +.tb-shiny-knob { + width:100%; + height:100%; + &.dark { + background: #000;//$dark-bg-img #1f2129; + } + + .knob { + position: relative; + &[draggable] { + -moz-user-select: none; + -webkit-user-select: none; + user-select: none; + } + #canvasBar { + position:absolute; + top:0; + left:0; + bottom: 0; + right: 0; + } + .top{ + position:absolute; + top: calc(#{$bars-margin-pct} - #{$shadow-offset-px}); + left: $bars-margin-pct; + bottom: calc(#{$bars-margin-pct} + #{$shadow-offset-px}); + right: $bars-margin-pct; + background:$knob-img no-repeat; + background-size: contain; + z-index:10; + cursor:default !important; + &:after { + content:''; + width:10px; + height:10px; + background-color:#666; + position:absolute; + top:50%; + left:10px; + margin-top:-5px; + border-radius: 50%; + cursor:default !important; + box-shadow: 0 0 1px #5a5a5a inset; + } + } + .base{ + top: calc(#{$bars-margin-pct} - #{$shadow-offset-px}); + left: $bars-margin-pct; + bottom: calc(#{$bars-margin-pct} + #{$shadow-offset-px}); + right: $bars-margin-pct; + border-radius:50%; + box-shadow:0 $shadow-size-px 0 #4a5056,$shadow-size-px $shadow-size-px $shadow-size-px #000; + position:absolute; + z-index:1; + } + } +} diff --git a/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html b/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html new file mode 100644 index 0000000000..18094b39c5 --- /dev/null +++ b/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html @@ -0,0 +1,30 @@ + + +
+
+ {{ vm.value }} +
+
+
+ +
+
+
+
+
\ No newline at end of file From 64385b019e1bd31649776ecb6529a80fdd2f3542 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Wed, 19 Jul 2017 04:29:36 +0300 Subject: [PATCH 2/3] TB-70: Control knob widget. --- .../widget_bundles/control_widgets.json | 6 +- ui/src/app/widget/lib/CanvasDigitalGauge.js | 19 ++++++- ui/src/app/widget/lib/rpc/knob.png | Bin 16385 -> 0 bytes ui/src/app/widget/lib/rpc/knob.svg | 32 +++++++++++ .../widget/lib/rpc/shiny-knob.directive.js | 27 ++++++--- ui/src/app/widget/lib/rpc/shiny-knob.scss | 52 +++++++++++++----- ui/src/app/widget/lib/rpc/shiny-knob.tpl.html | 9 ++- 7 files changed, 113 insertions(+), 32 deletions(-) delete mode 100644 ui/src/app/widget/lib/rpc/knob.png create mode 100644 ui/src/app/widget/lib/rpc/knob.svg diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index b06c979211..a1cd3c9be9 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -42,11 +42,11 @@ "name": "Shiny Knob Control", "descriptor": { "type": "rpc", - "sizeX": 2.5, - "sizeY": 3, + "sizeX": 4, + "sizeY": 3.5, "resources": [], "templateHtml": "", - "templateCss": ".error {\n font-size: 14px !important;\n color: maroon;/*rgb(250,250,250);*/\n background-color: transparent;\n padding: 6px;\n}\n\n.error span {\n margin: auto;\n}\n\n.gpio-panel {\n padding-top: 10px;\n white-space: nowrap;\n}\n\n.switch-panel {\n margin: 0;\n height: 32px;\n width: 66px;\n min-width: 66px;\n}\n\n.switch-panel md-switch {\n margin: 0;\n width: 36px;\n min-width: 36px;\n}\n\n.switch-panel md-switch > div.md-container {\n margin: 0;\n}\n\n.switch-panel.col-0 md-switch {\n padding-left: 8px;\n padding-right: 4px;\n}\n\n.switch-panel.col-1 md-switch {\n padding-left: 4px;\n padding-right: 8px;\n}\n\n.gpio-row {\n height: 32px;\n}\n\n.pin {\n margin-top: auto;\n margin-bottom: auto;\n color: white;\n font-size: 12px;\n width: 16px;\n min-width: 16px;\n}\n\n.switch-panel.col-0 .pin {\n margin-left: auto;\n padding-left: 2px;\n text-align: right;\n}\n\n.switch-panel.col-1 .pin {\n margin-right: auto;\n \n text-align: left;\n}\n\n.gpio-left-label {\n margin-right: 8px;\n}\n\n.gpio-right-label {\n margin-left: 8px;\n}", + "templateCss": "", "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n", "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"theme\": {\n \"title\": \"Knob theme\",\n \"type\": \"string\",\n \"default\": \"light\"\n }, \n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n {\n \"key\": \"theme\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"light\",\n \"label\": \"Light\"\n },\n {\n \"value\": \"dark\",\n \"label\": \"Dark\"\n }\n ]\n },\n \"requestTimeout\"\n ]\n}", "dataKeySettingsSchema": "{}\n", diff --git a/ui/src/app/widget/lib/CanvasDigitalGauge.js b/ui/src/app/widget/lib/CanvasDigitalGauge.js index e6c2e275d2..1a40235191 100644 --- a/ui/src/app/widget/lib/CanvasDigitalGauge.js +++ b/ui/src/app/widget/lib/CanvasDigitalGauge.js @@ -238,6 +238,21 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { return this; } + getValueColor() { + if (this.contextProgressClone) { + var color = this.contextProgressClone.currentColor; + if (!color) { + if (this.options.neonGlowBrightness) { + color = getProgressColor(0, this.options.neonColorsRange); + } else { + color = getProgressColor(0, this.options.colorsRange); + } + } + return color; + } else { + return '#000'; + } + } } /* eslint-disable angular/document-service */ @@ -666,9 +681,9 @@ function drawBarGlow(context, startX, startY, endX, endY, color, strokeWidth, is function drawProgress(context, options, progress) { var neonColor; if (options.neonGlowBrightness) { - neonColor = getProgressColor(progress, options.neonColorsRange); + context.currentColor = neonColor = getProgressColor(progress, options.neonColorsRange); } else { - context.strokeStyle = getProgressColor(progress, options.colorsRange); + context.currentColor = context.strokeStyle = getProgressColor(progress, options.colorsRange); } let {barLeft, barRight, barTop, baseX, width, barBottom, Cx, Cy, Rm, Ro, Ri, strokeWidth} = diff --git a/ui/src/app/widget/lib/rpc/knob.png b/ui/src/app/widget/lib/rpc/knob.png deleted file mode 100644 index 24675b28adb975356eda3b3d715497da27750af8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16385 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 + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/ui/src/app/widget/lib/rpc/shiny-knob.directive.js b/ui/src/app/widget/lib/rpc/shiny-knob.directive.js index b1867996d6..e79f03b2d9 100644 --- a/ui/src/app/widget/lib/rpc/shiny-knob.directive.js +++ b/ui/src/app/widget/lib/rpc/shiny-knob.directive.js @@ -17,7 +17,6 @@ import './shiny-knob.scss'; import CanvasDigitalGauge from './../CanvasDigitalGauge'; -//import tinycolor from 'tinycolor2'; /* eslint-disable import/no-unresolved, import/default */ @@ -53,7 +52,11 @@ function ShinyKnobController($element, $scope, $document) { var knob = angular.element('.knob', $element), knobContainer = angular.element('#knob-container', $element), - knobTop = knob.find('.top'), + // knobTop = knob.find('.top'), + knobTopPointerContainer = knob.find('.top-pointer-container'), + knobTopPointer = knob.find('.top-pointer'), + knobValueBackground = knob.find('.value-background'), + knobValue = knob.find('.knob-value'), startDeg = -1, currentDeg = 0, rotation = 0, @@ -61,7 +64,7 @@ function ShinyKnobController($element, $scope, $document) { var canvasBarElement = angular.element('#canvasBar', $element); - var levelColors = ['rgb(0, 128, 0)', 'rgb(251, 192, 45)', 'rgb(244, 67, 54)']; + var levelColors = ['rgb(0, 128, 0)', 'rgb(251, 192, 45)', 'rgb(255, 0, 0)'];//['cyan'];// var canvasBar; $scope.$watch('vm.ctx', () => { @@ -80,14 +83,14 @@ function ShinyKnobController($element, $scope, $document) { var canvasBarData = { renderTo: canvasBarElement[0], hideValue: true, - neonGlowBrightness: vm.darkTheme ? 40 : 0, - gaugeWidthScale: 0.5, - gaugeColor: vm.darkTheme ? 'rgb(23, 26, 28)' : 'rgba(0,0,0,0)', + neonGlowBrightness: 40,//40,//vm.darkTheme ? 40 : 0, + gaugeWidthScale: 0.2, + gaugeColor: vm.darkTheme ? 'rgb(23, 26, 28)' : 'rgba(0, 0, 0, 0.75)', levelColors: levelColors, minValue: vm.minValue, maxValue: vm.maxValue, gaugeType: 'donut', - dashThickness: 1.5, + dashThickness: 0,//1.5, donutStartAngle: Math.PI, animation: false, animationDuration: 250, @@ -141,7 +144,7 @@ function ShinyKnobController($element, $scope, $document) { currentDeg = tmp; lastDeg = tmp; - knobTop.css('transform','rotate('+(currentDeg)+'deg)'); + knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)'); turn(currentDeg/359); }); @@ -168,6 +171,8 @@ function ShinyKnobController($element, $scope, $document) { var size = Math.min(width, height); knob.css({width: size, height: size}); canvasBar.update({width: size, height: size}); + var valHeight = knobValueBackground.height()/3.3; + knobValue.css({'fontSize': valHeight+'px', 'lineHeight': valHeight+'px'}); } function turn(ratio) { @@ -175,16 +180,20 @@ function ShinyKnobController($element, $scope, $document) { if (canvasBar.value != value) { canvasBar.value = value; } + knobTopPointer.css({'backgroundColor': canvasBar.getValueColor()}); + knobValue.css({'color': 'cyan'});//canvasBar.getValueColor()}); onValue(value); } function setValue(value) { var ratio = (value-vm.minValue) / (vm.maxValue - vm.minValue); rotation = lastDeg = currentDeg = ratio*360; - knobTop.css('transform','rotate('+(currentDeg)+'deg)'); + knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)'); if (canvasBar.value != value) { canvasBar.value = value; } + knobTopPointer.css({'backgroundColor': canvasBar.getValueColor()}); + knobValue.css({'color': 'cyan'});//canvasBar.getValueColor()}); vm.value = value; } diff --git a/ui/src/app/widget/lib/rpc/shiny-knob.scss b/ui/src/app/widget/lib/rpc/shiny-knob.scss index 7ff204c427..a636ec54ea 100644 --- a/ui/src/app/widget/lib/rpc/shiny-knob.scss +++ b/ui/src/app/widget/lib/rpc/shiny-knob.scss @@ -14,10 +14,11 @@ * limitations under the License. */ -$knob-img: url('./knob.png'); +$knob-img: url('./knob.svg'); -$bars-margin-pct: percentage(0.2); -$shadow-size: 5; +$bars-margin-pct: percentage(0.08); +$value-background-margin-pct: percentage(0.30); +$shadow-size: 0; $shadow-size-px: $shadow-size+px; $shadow-offset-px: $shadow-size/2+px; @@ -25,7 +26,7 @@ $shadow-offset-px: $shadow-size/2+px; width:100%; height:100%; &.dark { - background: #000;//$dark-bg-img #1f2129; + background: #000; } .knob { @@ -42,30 +43,51 @@ $shadow-offset-px: $shadow-size/2+px; bottom: 0; right: 0; } - .top{ + .value-background { + position:absolute; + top: $value-background-margin-pct; + left: $value-background-margin-pct; + bottom: $value-background-margin-pct; + right: $value-background-margin-pct; + border-radius: 50%; + background: #000; + color: #fff; + font-weight: 500; + z-index:4; + } + .top-pointer-container { position:absolute; top: calc(#{$bars-margin-pct} - #{$shadow-offset-px}); left: $bars-margin-pct; bottom: calc(#{$bars-margin-pct} + #{$shadow-offset-px}); right: $bars-margin-pct; - background:$knob-img no-repeat; - background-size: contain; - z-index:10; + z-index:3; cursor:default !important; - &:after { + .top-pointer { content:''; - width:10px; - height:10px; - background-color:#666; + width:5%; + height:5%; + background-color:#b5b5b5; position:absolute; top:50%; - left:10px; - margin-top:-5px; + left:5%; + margin-top:-2.5%; border-radius: 50%; cursor:default !important; - box-shadow: 0 0 1px #5a5a5a inset; + box-shadow: 1px 0 2px #040404; } } + .top{ + position:absolute; + top: calc(#{$bars-margin-pct} - #{$shadow-offset-px}); + left: $bars-margin-pct; + bottom: calc(#{$bars-margin-pct} + #{$shadow-offset-px}); + right: $bars-margin-pct; + background:$knob-img no-repeat; + background-size: contain; + z-index:2; + cursor:default !important; + } .base{ top: calc(#{$bars-margin-pct} - #{$shadow-offset-px}); left: $bars-margin-pct; diff --git a/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html b/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html index 18094b39c5..be48067684 100644 --- a/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html +++ b/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html @@ -17,12 +17,15 @@ -->
-
- {{ vm.value }} -
+
+ {{ vm.value }} +
+
+
+
From 3f0d947f05d2eb756303b3afbf766761093ced79 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 20 Jul 2017 15:10:16 +0300 Subject: [PATCH 3/3] TB-70: RPC widgets: Implemented knob control widget. --- .../widget_bundles/control_widgets.json | 14 +- .../components/widget/widget.controller.js | 7 +- ui/src/app/widget/lib/CanvasDigitalGauge.js | 13 +- ui/src/app/widget/lib/rpc/index.js | 4 +- ui/src/app/widget/lib/rpc/knob.directive.js | 388 ++++++++++++++++++ ui/src/app/widget/lib/rpc/knob.scss | 157 +++++++ ui/src/app/widget/lib/rpc/knob.svg | 56 +-- .../{shiny-knob.tpl.html => knob.tpl.html} | 19 +- .../widget/lib/rpc/shiny-knob.directive.js | 207 ---------- ui/src/app/widget/lib/rpc/shiny-knob.scss | 102 ----- 10 files changed, 613 insertions(+), 354 deletions(-) create mode 100644 ui/src/app/widget/lib/rpc/knob.directive.js create mode 100644 ui/src/app/widget/lib/rpc/knob.scss rename ui/src/app/widget/lib/rpc/{shiny-knob.tpl.html => knob.tpl.html} (51%) delete mode 100644 ui/src/app/widget/lib/rpc/shiny-knob.directive.js delete mode 100644 ui/src/app/widget/lib/rpc/shiny-knob.scss diff --git a/application/src/main/data/json/system/widget_bundles/control_widgets.json b/application/src/main/data/json/system/widget_bundles/control_widgets.json index a1cd3c9be9..b05a144c0a 100644 --- a/application/src/main/data/json/system/widget_bundles/control_widgets.json +++ b/application/src/main/data/json/system/widget_bundles/control_widgets.json @@ -38,19 +38,19 @@ } }, { - "alias": "shiny_knob_control", - "name": "Shiny Knob Control", + "alias": "knob_control", + "name": "Knob Control", "descriptor": { "type": "rpc", - "sizeX": 4, - "sizeY": 3.5, + "sizeX": 5, + "sizeY": 4.5, "resources": [], - "templateHtml": "", + "templateHtml": "", "templateCss": "", "controllerScript": "self.onInit = function() {\n var scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}\n\nself.onResize = function() {\n if (self.ctx.resize) {\n self.ctx.resize();\n }\n}\n\nself.onDestroy = function() {\n}\n", - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"theme\": {\n \"title\": \"Knob theme\",\n \"type\": \"string\",\n \"default\": \"light\"\n }, \n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n {\n \"key\": \"theme\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"light\",\n \"label\": \"Light\"\n },\n {\n \"value\": \"dark\",\n \"label\": \"Dark\"\n }\n ]\n },\n \"requestTimeout\"\n ]\n}", + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"minValue\": {\n \"title\": \"Minimum value\",\n \"type\": \"number\",\n \"default\": 0\n },\n \"maxValue\": {\n \"title\": \"Maximum value\",\n \"type\": \"number\",\n \"default\": 100\n },\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"number\",\n \"default\": 50\n },\n \"title\": {\n \"title\": \"Knob title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"getValueMethod\": {\n \"title\": \"Get value method\",\n \"type\": \"string\",\n \"default\": \"getValue\"\n },\n \"setValueMethod\": {\n \"title\": \"Set value method\",\n \"type\": \"string\",\n \"default\": \"setValue\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"minValue\", \"maxValue\", \"getValueMethod\", \"setValueMethod\", \"requestTimeout\"]\n },\n \"form\": [\n \"minValue\",\n \"maxValue\",\n \"initialValue\",\n \"getValueMethod\",\n \"setValueMethod\",\n \"title\",\n \"requestTimeout\"\n ]\n}", "dataKeySettingsSchema": "{}\n", - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"theme\":\"light\",\"initialValue\":50},\"title\":\"Shiny Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"maxValue\":100,\"initialValue\":50,\"minValue\":0,\"title\":\"Knob control\"},\"title\":\"Knob Control\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" } } ] diff --git a/ui/src/app/components/widget/widget.controller.js b/ui/src/app/components/widget/widget.controller.js index 1efbf7f8ea..f9ff7a6f5f 100644 --- a/ui/src/app/components/widget/widget.controller.js +++ b/ui/src/app/components/widget/widget.controller.js @@ -811,14 +811,17 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele return (val - parseFloat( val ) + 1) >= 0; } - function formatValue(value, dec, units) { + function formatValue(value, dec, units, showZeroDecimals) { if (angular.isDefined(value) && value !== null && isNumeric(value)) { var formatted = Number(value); if (angular.isDefined(dec)) { formatted = formatted.toFixed(dec); } - formatted = (formatted * 1).toString(); + if (!showZeroDecimals) { + formatted = (formatted * 1); + } + formatted = formatted.toString(); if (angular.isDefined(units) && units.length > 0) { formatted += ' ' + units; } diff --git a/ui/src/app/widget/lib/CanvasDigitalGauge.js b/ui/src/app/widget/lib/CanvasDigitalGauge.js index 1a40235191..08a54d818a 100644 --- a/ui/src/app/widget/lib/CanvasDigitalGauge.js +++ b/ui/src/app/widget/lib/CanvasDigitalGauge.js @@ -99,6 +99,9 @@ export default class CanvasDigitalGauge extends canvasGauges.BaseGauge { if (!options.donutStartAngle) { options.donutStartAngle = 1.5 * Math.PI; } + if (!options.donutEndAngle) { + options.donutEndAngle = options.donutStartAngle + 2 * Math.PI; + } } var colorsCount = options.levelColors.length; @@ -494,7 +497,7 @@ function drawBackground(context, options) { context.lineCap = 'round'; } if (options.gaugeType === 'donut') { - context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, options.donutStartAngle, options.donutStartAngle + 2 * Math.PI); + context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, options.donutStartAngle, options.donutEndAngle); context.stroke(); } else if (options.gaugeType === 'arc') { context.arc(context.barDimensions.Cx, context.barDimensions.Cy, context.barDimensions.Rm, Math.PI, 2*Math.PI); @@ -626,7 +629,7 @@ function getProgressColor(progress, colorsRange) { } } -function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut, donutStartAngle) { +function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut, donutStartAngle, donutEndAngle) { context.setLineDash([]); var strokeWidth = Ro - Ri; var blur = 0.55; @@ -644,7 +647,7 @@ function drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, color, progress, isDonut, donu context.beginPath(); var e = 0.01 * Math.PI; if (isDonut) { - context.arc(Cx, Cy, Rm, donutStartAngle - e, donutStartAngle + 2 * Math.PI * progress + e); + context.arc(Cx, Cy, Rm, donutStartAngle - e, donutStartAngle + (donutEndAngle - donutStartAngle) * progress + e); } else { context.arc(Cx, Cy, Rm, Math.PI - e, Math.PI + Math.PI * progress + e); } @@ -703,10 +706,10 @@ function drawProgress(context, options, progress) { context.strokeStyle = neonColor; } context.beginPath(); - context.arc(Cx, Cy, Rm, options.donutStartAngle, options.donutStartAngle + 2 * Math.PI * progress); + context.arc(Cx, Cy, Rm, options.donutStartAngle, options.donutStartAngle + (options.donutEndAngle - options.donutStartAngle) * progress); context.stroke(); if (options.neonGlowBrightness && !options.isMobile) { - drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle); + drawArcGlow(context, Cx, Cy, Ri, Rm, Ro, neonColor, progress, true, options.donutStartAngle, options.donutEndAngle); } } else if (options.gaugeType === 'arc') { if (options.neonGlowBrightness) { diff --git a/ui/src/app/widget/lib/rpc/index.js b/ui/src/app/widget/lib/rpc/index.js index 9f2798974b..ba07eee559 100644 --- a/ui/src/app/widget/lib/rpc/index.js +++ b/ui/src/app/widget/lib/rpc/index.js @@ -14,8 +14,8 @@ * limitations under the License. */ -import tbShinyKnob from './shiny-knob.directive'; +import tbKnob from './knob.directive'; export default angular.module('thingsboard.widgets.rpc', [ - tbShinyKnob + tbKnob ]).name; diff --git a/ui/src/app/widget/lib/rpc/knob.directive.js b/ui/src/app/widget/lib/rpc/knob.directive.js new file mode 100644 index 0000000000..d62d0fbba8 --- /dev/null +++ b/ui/src/app/widget/lib/rpc/knob.directive.js @@ -0,0 +1,388 @@ +/* + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import './knob.scss'; + +import CanvasDigitalGauge from './../CanvasDigitalGauge'; +import tinycolor from 'tinycolor2'; + +import { isNumber } from '../widget-utils'; + +/* eslint-disable import/no-unresolved, import/default */ + +import knobTemplate from './knob.tpl.html'; + +/* eslint-enable import/no-unresolved, import/default */ + +export default angular.module('thingsboard.widgets.rpc.knob', []) + .directive('tbKnob', Knob) + .name; + +/*@ngInject*/ +function Knob() { + return { + restrict: "E", + scope: true, + bindToController: { + ctx: '=' + }, + controller: KnobController, + controllerAs: 'vm', + templateUrl: knobTemplate + }; +} + +/*@ngInject*/ +function KnobController($element, $scope, $document) { + let vm = this; + + vm.value = 0; + vm.error = ''; + + var knob = angular.element('.knob', $element), + knobContainer = angular.element('#knob-container', $element), + knobTopPointerContainer = knob.find('.top-pointer-container'), + knobTopPointer = knob.find('.top-pointer'), + knobValueContainer = knob.find('.value-container'), + knobValue = knob.find('.knob-value'), + knobErrorContainer = knob.find('.error-container'), + knobError = knob.find('.knob-error'), + knobTitleContainer = knob.find('.title-container'), + knobTitle = knob.find('.knob-title'), + knobMinmaxContainer = knob.find('.minmax-container'), + minmaxLanel = knob.find('.minmax-label'), + textMeasure = knob.find('#text-measure'), + startDeg = -1, + currentDeg = 0, + rotation = 0, + lastDeg = 0, + moving = false; + + var minDeg = -45; + var maxDeg = 225; + + var canvasBarElement = angular.element('#canvasBar', $element); + + var levelColors = ['#19ff4b', '#ffff19', '#ff3232']; + + var canvasBar; + + $scope.$watch('vm.ctx', () => { + if (vm.ctx) { + init(); + } + }); + + function init() { + + vm.minValue = angular.isDefined(vm.ctx.settings.minValue) ? vm.ctx.settings.minValue : 0; + vm.maxValue = angular.isDefined(vm.ctx.settings.maxValue) ? vm.ctx.settings.maxValue : 100; + vm.title = angular.isDefined(vm.ctx.settings.title) ? vm.ctx.settings.title : ''; + + vm.darkTheme = vm.ctx.settings.theme == 'dark'; + + var canvasBarData = { + renderTo: canvasBarElement[0], + hideValue: true, + neonGlowBrightness: 0, + gaugeWidthScale: 0.4, + gaugeColor: 'rgba(0, 0, 0, 0)', + levelColors: levelColors, + minValue: vm.minValue, + maxValue: vm.maxValue, + gaugeType: 'donut', + dashThickness: 2, + donutStartAngle: 3/4*Math.PI, + donutEndAngle: 9/4*Math.PI, + animation: false + }; + + canvasBar = new CanvasDigitalGauge(canvasBarData).draw(); + + knob.on('click', (e) => { + if (moving) { + moving = false; + return false; + } + e.preventDefault(); + + var offset = knob.offset(); + var center = { + y : offset.top + knob.height()/2, + x: offset.left + knob.width()/2 + }; + var a, b, deg, + rad2deg = 180/Math.PI; + + e = (e.originalEvent.touches) ? e.originalEvent.touches[0] : e; + + a = center.y - e.pageY; + b = center.x - e.pageX; + deg = Math.atan2(a,b)*rad2deg; + if(deg < 0){ + deg = 360 + deg; + } + if (deg > maxDeg) { + if (deg - 360 > minDeg) { + deg = deg - 360; + } else { + return false; + } + } + currentDeg = deg; + lastDeg = deg; + knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)'); + turn(degreeToRatio(currentDeg)); + rotation = currentDeg; + startDeg = -1; + }); + + knob.on('mousedown touchstart', (e) => { + e.preventDefault(); + var offset = knob.offset(); + var center = { + y : offset.top + knob.height()/2, + x: offset.left + knob.width()/2 + }; + + var a, b, deg, tmp, + rad2deg = 180/Math.PI; + + knob.on('mousemove.rem touchmove.rem', (e) => { + moving = true; + e = (e.originalEvent.touches) ? e.originalEvent.touches[0] : e; + + a = center.y - e.pageY; + b = center.x - e.pageX; + deg = Math.atan2(a,b)*rad2deg; + if(deg < 0){ + deg = 360 + deg; + } + + if(startDeg == -1){ + startDeg = deg; + } + + tmp = Math.floor((deg-startDeg) + rotation); + + if(tmp < 0){ + tmp = 360 + tmp; + } + else if(tmp > 359){ + tmp = tmp % 360; + } + + if (tmp > maxDeg) { + if (tmp - 360 > minDeg) { + tmp = tmp - 360; + } else { + var deltaMax = Math.abs(maxDeg - lastDeg); + var deltaMin = Math.abs(minDeg - lastDeg); + if (deltaMax < deltaMin) { + tmp = maxDeg; + } else { + tmp = minDeg; + } + } + } + if(Math.abs(tmp - lastDeg) > 180){ + startDeg = deg; + rotation = currentDeg; + return false; + } + + currentDeg = tmp; + lastDeg = tmp; + + knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)'); + turn(degreeToRatio(currentDeg)); + }); + + $document.on('mouseup.rem touchend.rem',() => { + knob.off('.rem'); + $document.off('.rem'); + rotation = currentDeg; + startDeg = -1; + }); + + }); + vm.ctx.resize = resize; + resize(); + var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : vm.minValue; + setValue(initialValue); + + var subscription = vm.ctx.defaultSubscription; + var rpcEnabled = subscription.rpcEnabled; + + vm.isSimulated = $scope.widgetEditMode; + + vm.requestTimeout = 500; + if (vm.ctx.settings.requestTimeout) { + vm.requestTimeout = vm.ctx.settings.requestTimeout; + } + vm.getValueMethod = 'getValue'; + if (vm.ctx.settings.getValueMethod && vm.ctx.settings.getValueMethod.length) { + vm.getValueMethod = vm.ctx.settings.getValueMethod; + } + vm.setValueMethod = 'setValue'; + if (vm.ctx.settings.setValueMethod && vm.ctx.settings.setValueMethod.length) { + vm.setValueMethod = vm.ctx.settings.setValueMethod; + } + if (!rpcEnabled) { + onError('Target device is not set!'); + } else { + if (!vm.isSimulated) { + rpcRequestValue(); + } + } + } + + function ratioToDegree(ratio) { + return minDeg + ratio*(maxDeg-minDeg); + } + + function degreeToRatio(degree) { + return (degree-minDeg)/(maxDeg-minDeg); + } + + function resize() { + var width = knobContainer.width(); + var height = knobContainer.height(); + var size = Math.min(width, height); + knob.css({width: size, height: size}); + canvasBar.update({width: size, height: size}); + setFontSize(knobTitle, vm.title, knobTitleContainer.height(), knobTitleContainer.width()); + setFontSize(knobError, vm.error, knobErrorContainer.height(), knobErrorContainer.width()); + var minmaxHeight = knobMinmaxContainer.height(); + minmaxLanel.css({'fontSize': minmaxHeight+'px', 'lineHeight': minmaxHeight+'px'}); + checkValueSize(); + } + + function turn(ratio) { + var value = (vm.minValue + (vm.maxValue - vm.minValue)*ratio).toFixed(vm.ctx.decimals); + if (canvasBar.value != value) { + canvasBar.value = value; + } + updateColor(canvasBar.getValueColor()); + onValue(value); + } + + function setValue(value) { + var ratio = (value-vm.minValue) / (vm.maxValue - vm.minValue); + rotation = lastDeg = currentDeg = ratioToDegree(ratio); + knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)'); + if (canvasBar.value != value) { + canvasBar.value = value; + } + updateColor(canvasBar.getValueColor()); + vm.value = formatValue(value); + checkValueSize(); + } + + function updateColor(color) { + var glowColor = tinycolor(color).brighten(30).toHexString(); + knobValue.css({'color': glowColor}); + var textShadow = `${color} 1px 1px 10px, ${glowColor} 1px 1px 10px`; + knobValue.css({'textShadow': textShadow}); + knobTopPointer.css({'backgroundColor': glowColor}); + var boxShadow = `inset 1px 0 2px #040404, 1px 1px 8px 2px ${glowColor}`; + knobTopPointer.css({'boxShadow': boxShadow}); + } + + function onValue(value) { + $scope.$applyAsync(() => { + vm.value = formatValue(value); + checkValueSize(); + rpcUpdateValue(value); + }); + } + + function onError(error) { + $scope.$applyAsync(() => { + vm.error = error; + setFontSize(knobError, vm.error, knobErrorContainer.height(), knobErrorContainer.width()); + }); + } + + function formatValue(value) { + return vm.ctx.utils.formatValue(value, vm.ctx.decimals, vm.ctx.units, true); + } + + function checkValueSize() { + var fontSize = knobValueContainer.height()/3.3; + var containerWidth = knobValueContainer.width(); + setFontSize(knobValue, vm.value, fontSize, containerWidth); + } + + function setFontSize(element, text, fontSize, maxWidth) { + var textWidth = measureTextWidth(text, fontSize); + while (textWidth > maxWidth) { + fontSize--; + textWidth = measureTextWidth(text, fontSize); + } + element.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'}); + } + + function measureTextWidth(text, fontSize) { + textMeasure.css({'fontSize': fontSize+'px', 'lineHeight': fontSize+'px'}); + textMeasure.text(text); + return textMeasure.width(); + } + + function rpcRequestValue() { + vm.error = ''; + vm.ctx.controlApi.sendTwoWayCommand(vm.getValueMethod, null, vm.requestTimeout).then( + (responseBody) => { + if (isNumber(responseBody)) { + var numValue = Number(responseBody).toFixed(vm.ctx.decimals); + setValue(numValue); + } else { + var errorText = `Unable to parse response: ${responseBody}`; + onError(errorText); + } + }, + () => { + var errorText = vm.ctx.defaultSubscription.rpcErrorText; + onError(errorText); + } + ); + } + + function rpcUpdateValue(value) { + if (vm.executingUpdateValue) { + vm.scheduledValue = value; + return; + } else { + vm.scheduledValue = null; + vm.rpcValue = value; + vm.executingUpdateValue = true; + } + vm.error = ''; + vm.ctx.controlApi.sendOneWayCommand(vm.setValueMethod, value, vm.requestTimeout).then( + () => { + vm.executingUpdateValue = false; + if (vm.scheduledValue != null && vm.scheduledValue != vm.rpcValue) { + rpcUpdateValue(vm.scheduledValue); + } + }, + () => { + vm.executingUpdateValue = false; + var errorText = vm.ctx.defaultSubscription.rpcErrorText; + onError(errorText); + } + ); + } +} diff --git a/ui/src/app/widget/lib/rpc/knob.scss b/ui/src/app/widget/lib/rpc/knob.scss new file mode 100644 index 0000000000..3e57f663c0 --- /dev/null +++ b/ui/src/app/widget/lib/rpc/knob.scss @@ -0,0 +1,157 @@ +/** + * Copyright © 2016-2017 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +$knob-img: url('./knob.svg'); + +$bars-margin-pct: percentage(0.033); +$background-margin-pct: percentage(0.05); +$value-container-margin-pct: percentage(0.35); +$error-height: percentage(0.05); +$title-height: percentage(0.066); +$title-container-margin-pct: percentage(0.2); +$title-container-margin-bottom-pct: percentage(0.05); +$minmax-height: percentage(0.04); +$minmax-container-margin-pct: percentage(0.18); +$minmax-container-margin-bottom-pct: percentage(0.12); + +$background-color: #e6e7e8; + +.tb-knob { + width:100%; + height:100%; + background: $background-color; + &.dark { + background: #000; + } + + .knob { + position: relative; + &[draggable] { + -moz-user-select: none; + -webkit-user-select: none; + user-select: none; + } + #canvasBar { + position:absolute; + top:0; + left:0; + bottom: 0; + right: 0; + z-index: 2; + } + .canvas-background { + position:absolute; + top: $background-margin-pct; + left: $background-margin-pct; + bottom: $background-margin-pct; + right: $background-margin-pct; + border-radius: 50%; + background: #3f4346; + z-index:2; + } + .value-container { + position:absolute; + top: $value-container-margin-pct; + left: $value-container-margin-pct; + bottom: $value-container-margin-pct; + right: $value-container-margin-pct; + z-index:4; + .knob-value { + color: #fff; + font-weight: 500; + white-space: nowrap; + } + } + .error-container { + position:absolute; + top: 1%; + left: 0; + right: 0; + z-index:4; + height: $error-height; + .knob-error { + color: #ff3315; + white-space: nowrap; + } + } + .title-container { + position:absolute; + left: $title-container-margin-pct; + bottom: $title-container-margin-bottom-pct; + right: $title-container-margin-pct; + z-index:4; + height: $title-height; + .knob-title { + color: #757575; + font-weight: 500; + white-space: nowrap; + } + } + .minmax-container { + position:absolute; + left: $minmax-container-margin-pct; + bottom: $minmax-container-margin-bottom-pct; + right: $minmax-container-margin-pct; + z-index:4; + height: $minmax-height; + .minmax-label { + color: #757575; + font-weight: 500; + white-space: nowrap; + } + } + .top-pointer-container { + position:absolute; + top: $bars-margin-pct; + left: $bars-margin-pct; + bottom: $bars-margin-pct; + right: $bars-margin-pct; + z-index:3; + cursor:pointer !important; + .top-pointer { + content:''; + width:5%; + height:5%; + background-color:#b5b5b5; + position:absolute; + top:50%; + left:22%; + margin-top:-2.5%; + border-radius: 50%; + cursor:pointer !important; + box-shadow: 1px 0 2px #040404; + } + } + .top{ + position:absolute; + top: $bars-margin-pct; + left: $bars-margin-pct; + bottom: $bars-margin-pct; + right: $bars-margin-pct; + background:$knob-img no-repeat; + background-size: contain; + z-index:2; + cursor:pointer !important; + } + #text-measure { + position: absolute; + visibility: hidden; + height: auto; + width: auto; + white-space: nowrap; + } + } +} diff --git a/ui/src/app/widget/lib/rpc/knob.svg b/ui/src/app/widget/lib/rpc/knob.svg index d75c2b131e..81fe9949b7 100644 --- a/ui/src/app/widget/lib/rpc/knob.svg +++ b/ui/src/app/widget/lib/rpc/knob.svg @@ -1,32 +1,36 @@ - - - - - - - - + + + + + - - - + + + - + + + + + + + + + + - - - - image/svg+xml - - - - - - - - - - + + + + + + + + + + + + diff --git a/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html b/ui/src/app/widget/lib/rpc/knob.tpl.html similarity index 51% rename from ui/src/app/widget/lib/rpc/shiny-knob.tpl.html rename to ui/src/app/widget/lib/rpc/knob.tpl.html index be48067684..93276b44ed 100644 --- a/ui/src/app/widget/lib/rpc/shiny-knob.tpl.html +++ b/ui/src/app/widget/lib/rpc/knob.tpl.html @@ -16,18 +16,31 @@ --> -
+
-
+
{{ vm.value }}
+
-
+
+ {{ vm.error }} +
+
+ {{ vm.title }} +
+
+ min + + max +
+
\ No newline at end of file diff --git a/ui/src/app/widget/lib/rpc/shiny-knob.directive.js b/ui/src/app/widget/lib/rpc/shiny-knob.directive.js deleted file mode 100644 index e79f03b2d9..0000000000 --- a/ui/src/app/widget/lib/rpc/shiny-knob.directive.js +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright © 2016-2017 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import './shiny-knob.scss'; - -import CanvasDigitalGauge from './../CanvasDigitalGauge'; - -/* eslint-disable import/no-unresolved, import/default */ - -import shinyKnobTemplate from './shiny-knob.tpl.html'; - -/* eslint-enable import/no-unresolved, import/default */ - -export default angular.module('thingsboard.widgets.rpc.shinyKnob', []) - .directive('tbShinyKnob', ShinyKnob) - .name; - -/*@ngInject*/ -function ShinyKnob() { - return { - restrict: "E", - scope: true, - bindToController: { - ctx: '=' - }, - controller: ShinyKnobController, - controllerAs: 'vm', - templateUrl: shinyKnobTemplate - }; -} - -/*@ngInject*/ -function ShinyKnobController($element, $scope, $document) { - let vm = this; - - vm.value = 0; - - var snap = 0; - - var knob = angular.element('.knob', $element), - knobContainer = angular.element('#knob-container', $element), - // knobTop = knob.find('.top'), - knobTopPointerContainer = knob.find('.top-pointer-container'), - knobTopPointer = knob.find('.top-pointer'), - knobValueBackground = knob.find('.value-background'), - knobValue = knob.find('.knob-value'), - startDeg = -1, - currentDeg = 0, - rotation = 0, - lastDeg = 0; - - var canvasBarElement = angular.element('#canvasBar', $element); - - var levelColors = ['rgb(0, 128, 0)', 'rgb(251, 192, 45)', 'rgb(255, 0, 0)'];//['cyan'];// - var canvasBar; - - $scope.$watch('vm.ctx', () => { - if (vm.ctx) { - init(); - } - }); - - function init() { - - vm.minValue = angular.isDefined(vm.ctx.settings.minValue) ? vm.ctx.settings.minValue : 0; - vm.maxValue = angular.isDefined(vm.ctx.settings.maxValue) ? vm.ctx.settings.maxValue : 100; - - vm.darkTheme = vm.ctx.settings.theme == 'dark'; - - var canvasBarData = { - renderTo: canvasBarElement[0], - hideValue: true, - neonGlowBrightness: 40,//40,//vm.darkTheme ? 40 : 0, - gaugeWidthScale: 0.2, - gaugeColor: vm.darkTheme ? 'rgb(23, 26, 28)' : 'rgba(0, 0, 0, 0.75)', - levelColors: levelColors, - minValue: vm.minValue, - maxValue: vm.maxValue, - gaugeType: 'donut', - dashThickness: 0,//1.5, - donutStartAngle: Math.PI, - animation: false, - animationDuration: 250, - animationRule: 'linear' - }; - - canvasBar = new CanvasDigitalGauge(canvasBarData).draw(); - - knob.on('mousedown touchstart', (e) => { - e.preventDefault(); - var offset = knob.offset(); - var center = { - y : offset.top + knob.height()/2, - x: offset.left + knob.width()/2 - }; - - var a, b, deg, tmp, - rad2deg = 180/Math.PI; - - knob.on('mousemove.rem touchmove.rem', (e) => { - - e = (e.originalEvent.touches) ? e.originalEvent.touches[0] : e; - - a = center.y - e.pageY; - b = center.x - e.pageX; - deg = Math.atan2(a,b)*rad2deg; - - if(deg<0){ - deg = 360 + deg; - } - - if(startDeg == -1){ - startDeg = deg; - } - - tmp = Math.floor((deg-startDeg) + rotation); - - if(tmp < 0){ - tmp = 360 + tmp; - } - else if(tmp > 359){ - tmp = tmp % 360; - } - - if(snap && tmp < snap){ - tmp = 0; - } - if(Math.abs(tmp - lastDeg) > 180){ - return false; - } - currentDeg = tmp; - lastDeg = tmp; - - knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)'); - turn(currentDeg/359); - }); - - $document.on('mouseup.rem touchend.rem',() => { - knob.off('.rem'); - $document.off('.rem'); - rotation = currentDeg; - startDeg = -1; - }); - - }); - - vm.ctx.resize = resize; - resize(); - - var initialValue = angular.isDefined(vm.ctx.settings.initialValue) ? vm.ctx.settings.initialValue : vm.minValue; - - setValue(initialValue); - } - - function resize() { - var width = knobContainer.width(); - var height = knobContainer.height(); - var size = Math.min(width, height); - knob.css({width: size, height: size}); - canvasBar.update({width: size, height: size}); - var valHeight = knobValueBackground.height()/3.3; - knobValue.css({'fontSize': valHeight+'px', 'lineHeight': valHeight+'px'}); - } - - function turn(ratio) { - var value = (vm.minValue + (vm.maxValue - vm.minValue)*ratio).toFixed(2); - if (canvasBar.value != value) { - canvasBar.value = value; - } - knobTopPointer.css({'backgroundColor': canvasBar.getValueColor()}); - knobValue.css({'color': 'cyan'});//canvasBar.getValueColor()}); - onValue(value); - } - - function setValue(value) { - var ratio = (value-vm.minValue) / (vm.maxValue - vm.minValue); - rotation = lastDeg = currentDeg = ratio*360; - knobTopPointerContainer.css('transform','rotate('+(currentDeg)+'deg)'); - if (canvasBar.value != value) { - canvasBar.value = value; - } - knobTopPointer.css({'backgroundColor': canvasBar.getValueColor()}); - knobValue.css({'color': 'cyan'});//canvasBar.getValueColor()}); - vm.value = value; - } - - function onValue(value) { - console.log(`onValue ${value}`); //eslint-disable-line - $scope.$applyAsync(() => { - vm.value = value; - }); - } - -} \ No newline at end of file diff --git a/ui/src/app/widget/lib/rpc/shiny-knob.scss b/ui/src/app/widget/lib/rpc/shiny-knob.scss deleted file mode 100644 index a636ec54ea..0000000000 --- a/ui/src/app/widget/lib/rpc/shiny-knob.scss +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright © 2016-2017 The Thingsboard Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -$knob-img: url('./knob.svg'); - -$bars-margin-pct: percentage(0.08); -$value-background-margin-pct: percentage(0.30); -$shadow-size: 0; -$shadow-size-px: $shadow-size+px; -$shadow-offset-px: $shadow-size/2+px; - -.tb-shiny-knob { - width:100%; - height:100%; - &.dark { - background: #000; - } - - .knob { - position: relative; - &[draggable] { - -moz-user-select: none; - -webkit-user-select: none; - user-select: none; - } - #canvasBar { - position:absolute; - top:0; - left:0; - bottom: 0; - right: 0; - } - .value-background { - position:absolute; - top: $value-background-margin-pct; - left: $value-background-margin-pct; - bottom: $value-background-margin-pct; - right: $value-background-margin-pct; - border-radius: 50%; - background: #000; - color: #fff; - font-weight: 500; - z-index:4; - } - .top-pointer-container { - position:absolute; - top: calc(#{$bars-margin-pct} - #{$shadow-offset-px}); - left: $bars-margin-pct; - bottom: calc(#{$bars-margin-pct} + #{$shadow-offset-px}); - right: $bars-margin-pct; - z-index:3; - cursor:default !important; - .top-pointer { - content:''; - width:5%; - height:5%; - background-color:#b5b5b5; - position:absolute; - top:50%; - left:5%; - margin-top:-2.5%; - border-radius: 50%; - cursor:default !important; - box-shadow: 1px 0 2px #040404; - } - } - .top{ - position:absolute; - top: calc(#{$bars-margin-pct} - #{$shadow-offset-px}); - left: $bars-margin-pct; - bottom: calc(#{$bars-margin-pct} + #{$shadow-offset-px}); - right: $bars-margin-pct; - background:$knob-img no-repeat; - background-size: contain; - z-index:2; - cursor:default !important; - } - .base{ - top: calc(#{$bars-margin-pct} - #{$shadow-offset-px}); - left: $bars-margin-pct; - bottom: calc(#{$bars-margin-pct} + #{$shadow-offset-px}); - right: $bars-margin-pct; - border-radius:50%; - box-shadow:0 $shadow-size-px 0 #4a5056,$shadow-size-px $shadow-size-px $shadow-size-px #000; - position:absolute; - z-index:1; - } - } -}