Commit ba6c2c1a1b046ce45e532cf2c312c3332761b58f
1 parent
70ddf204
UI: Implement state data aggregation. Introduce State chart.
Showing
12 changed files
with
358 additions
and
123 deletions
@@ -152,6 +152,22 @@ | @@ -152,6 +152,22 @@ | ||
152 | "dataKeySettingsSchema": "{}", | 152 | "dataKeySettingsSchema": "{}", |
153 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":true,\"tooltipIndividual\":false},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" | 153 | "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"legend\":{\"show\":true,\"position\":\"nw\",\"backgroundColor\":\"#f0f0f0\",\"backgroundOpacity\":0.85,\"labelBoxBorderColor\":\"rgba(1, 1, 1, 0.45)\"},\"decimals\":1,\"stack\":true,\"tooltipIndividual\":false},\"title\":\"Timeseries Bars - Flot\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null}" |
154 | } | 154 | } |
155 | + }, | ||
156 | + { | ||
157 | + "alias": "state_chart", | ||
158 | + "name": "State Chart", | ||
159 | + "descriptor": { | ||
160 | + "type": "timeseries", | ||
161 | + "sizeX": 8, | ||
162 | + "sizeY": 5, | ||
163 | + "resources": [], | ||
164 | + "templateHtml": "", | ||
165 | + "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n", | ||
166 | + "controllerScript": "self.onInit = function() {\n self.ctx.flot = new TbFlot(self.ctx, 'state'); \n}\n\nself.onDataUpdated = function() {\n self.ctx.flot.update();\n}\n\nself.onResize = function() {\n self.ctx.flot.resize();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true\n };\n}\n\nself.onEditModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.onMobileModeChanged = function() {\n self.ctx.flot.checkMouseEvents();\n}\n\nself.getSettingsSchema = function() {\n return TbFlot.settingsSchema;\n}\n\nself.getDataKeySettingsSchema = function() {\n return TbFlot.datakeySettingsSchema(true);\n}\n\nself.onDestroy = function() {\n self.ctx.flot.destroy();\n}\n", | ||
167 | + "settingsSchema": "{}", | ||
168 | + "dataKeySettingsSchema": "{}", | ||
169 | + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"shadowSize\":4,\"fontColor\":\"#545454\",\"fontSize\":10,\"xaxis\":{\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"showLabels\":true,\"color\":\"#545454\",\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"grid\":{\"color\":\"#545454\",\"tickColor\":\"#DDDDDD\",\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1},\"stack\":false,\"tooltipIndividual\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"smoothLines\":false},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"legendConfig\":{\"position\":\"bottom\",\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false}}" | ||
170 | + } | ||
155 | } | 171 | } |
156 | ] | 172 | ] |
157 | } | 173 | } |
@@ -96,9 +96,9 @@ | @@ -96,9 +96,9 @@ | ||
96 | "templateHtml": "<tb-led-indicator ctx='ctx'></tb-led-indicator>", | 96 | "templateHtml": "<tb-led-indicator ctx='ctx'></tb-led-indicator>", |
97 | "templateCss": "", | 97 | "templateCss": "", |
98 | "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", | 98 | "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", |
99 | - "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"scheckStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"scheckStatusMethod\", \"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"scheckStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}", | 99 | + "settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"initialValue\": {\n \"title\": \"Initial value\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"title\": {\n \"title\": \"LED title\",\n \"type\": \"string\",\n \"default\": \"\"\n },\n \"ledColor\": {\n \"title\": \"LED Color\",\n \"type\": \"string\",\n \"default\": \"green\"\n },\n \"performCheckStatus\": {\n \"title\": \"Perform RPC device status check\",\n \"type\": \"boolean\",\n \"default\": true\n },\n \"scheckStatusMethod\": {\n \"title\": \"RPC check device status method\",\n \"type\": \"string\",\n \"default\": \"checkStatus\"\n },\n \"retrieveValueMethod\": {\n \"title\": \"Retrieve led status value using method\",\n \"type\": \"string\",\n \"default\": \"attribute\"\n },\n \"valueAttribute\": {\n \"title\": \"Device attribute/timeseries containing led status value\",\n \"type\": \"string\",\n \"default\": \"value\"\n },\n \"parseValueFunction\": {\n \"title\": \"Parse led status value function, f(data), returns boolean\",\n \"type\": \"string\",\n \"default\": \"return data ? true : false;\"\n },\n \"requestTimeout\": {\n \"title\": \"RPC request timeout (ms)\",\n \"type\": \"number\",\n \"default\": 500\n }\n },\n \"required\": [\"scheckStatusMethod\", \"valueAttribute\", \"requestTimeout\"]\n },\n \"form\": [\n \"initialValue\",\n \"title\",\n {\n \"key\": \"ledColor\",\n \"type\": \"color\"\n },\n \"performCheckStatus\",\n \"scheckStatusMethod\",\n {\n \"key\": \"retrieveValueMethod\",\n \"type\": \"rc-select\",\n \"multiple\": false,\n \"items\": [\n {\n \"value\": \"attribute\",\n \"label\": \"Subscribe for attribute\"\n },\n {\n \"value\": \"timeseries\",\n \"label\": \"Subscribe for timeseries\"\n }\n ]\n },\n \"valueAttribute\",\n {\n \"key\": \"parseValueFunction\",\n \"type\": \"javascript\"\n },\n \"requestTimeout\"\n ]\n}", |
100 | "dataKeySettingsSchema": "{}\n", | 100 | "dataKeySettingsSchema": "{}\n", |
101 | - "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"scheckStatusMethod\":\"checkStatus\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\"},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" | 101 | + "defaultConfig": "{\"targetDeviceAliases\":[],\"showTitle\":false,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"requestTimeout\":500,\"initialValue\":true,\"title\":\"Led indicator\",\"ledColor\":\"#4caf50\",\"scheckStatusMethod\":\"checkStatus\",\"valueAttribute\":\"value\",\"retrieveValueMethod\":\"attribute\",\"parseValueFunction\":\"return data ? true : false;\",\"performCheckStatus\":true},\"title\":\"Led indicator\",\"dropShadow\":true,\"enableFullscreen\":false,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{},\"decimals\":2}" |
102 | } | 102 | } |
103 | } | 103 | } |
104 | ] | 104 | ] |
@@ -186,7 +186,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | @@ -186,7 +186,7 @@ function AttributeService($http, $q, $filter, types, telemetryWebsocketService) | ||
186 | types.dataKeyType.timeseries : types.dataKeyType.attribute; | 186 | types.dataKeyType.timeseries : types.dataKeyType.attribute; |
187 | 187 | ||
188 | var subscriber = { | 188 | var subscriber = { |
189 | - subscriptionCommand: subscriptionCommand, | 189 | + subscriptionCommands: [subscriptionCommand], |
190 | type: type, | 190 | type: type, |
191 | onData: function (data) { | 191 | onData: function (data) { |
192 | if (data.data) { | 192 | if (data.data) { |
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | export default class DataAggregator { | 17 | export default class DataAggregator { |
18 | 18 | ||
19 | constructor(onDataCb, tsKeyNames, startTs, limit, aggregationType, timeWindow, interval, | 19 | constructor(onDataCb, tsKeyNames, startTs, limit, aggregationType, timeWindow, interval, |
20 | - steppedChart, types, $timeout, $filter) { | 20 | + stateData, types, $timeout, $filter) { |
21 | this.onDataCb = onDataCb; | 21 | this.onDataCb = onDataCb; |
22 | this.tsKeyNames = tsKeyNames; | 22 | this.tsKeyNames = tsKeyNames; |
23 | this.dataBuffer = {}; | 23 | this.dataBuffer = {}; |
@@ -35,8 +35,10 @@ export default class DataAggregator { | @@ -35,8 +35,10 @@ export default class DataAggregator { | ||
35 | this.limit = limit; | 35 | this.limit = limit; |
36 | this.timeWindow = timeWindow; | 36 | this.timeWindow = timeWindow; |
37 | this.interval = interval; | 37 | this.interval = interval; |
38 | - this.steppedChart = steppedChart; | ||
39 | - this.firstStepDataReceived = !this.steppedChart; | 38 | + this.stateData = stateData; |
39 | + if (this.stateData) { | ||
40 | + this.lastPrevKvPairData = {}; | ||
41 | + } | ||
40 | this.aggregationTimeout = Math.max(this.interval, 1000); | 42 | this.aggregationTimeout = Math.max(this.interval, 1000); |
41 | switch (aggregationType) { | 43 | switch (aggregationType) { |
42 | case types.aggregation.min.value: | 44 | case types.aggregation.min.value: |
@@ -81,10 +83,6 @@ export default class DataAggregator { | @@ -81,10 +83,6 @@ export default class DataAggregator { | ||
81 | }, this.aggregationTimeout, false); | 83 | }, this.aggregationTimeout, false); |
82 | } | 84 | } |
83 | 85 | ||
84 | - onFirstStepData(data) { | ||
85 | - this.firstStepData = data; | ||
86 | - } | ||
87 | - | ||
88 | onData(data, update, history, apply) { | 86 | onData(data, update, history, apply) { |
89 | if (!this.dataReceived || this.resetPending) { | 87 | if (!this.dataReceived || this.resetPending) { |
90 | var updateIntervalScheduledTime = true; | 88 | var updateIntervalScheduledTime = true; |
@@ -158,6 +156,10 @@ export default class DataAggregator { | @@ -158,6 +156,10 @@ export default class DataAggregator { | ||
158 | var keyData = this.dataBuffer[key]; | 156 | var keyData = this.dataBuffer[key]; |
159 | for (var aggTimestamp in aggKeyData) { | 157 | for (var aggTimestamp in aggKeyData) { |
160 | if (aggTimestamp <= this.startTs) { | 158 | if (aggTimestamp <= this.startTs) { |
159 | + if (this.stateData && | ||
160 | + (!this.lastPrevKvPairData[key] || this.lastPrevKvPairData[key][0] < aggTimestamp)) { | ||
161 | + this.lastPrevKvPairData[key] = [Number(aggTimestamp), aggKeyData[aggTimestamp].aggValue]; | ||
162 | + } | ||
161 | delete aggKeyData[aggTimestamp]; | 163 | delete aggKeyData[aggTimestamp]; |
162 | } else if (aggTimestamp <= this.endTs) { | 164 | } else if (aggTimestamp <= this.endTs) { |
163 | var aggData = aggKeyData[aggTimestamp]; | 165 | var aggData = aggKeyData[aggTimestamp]; |
@@ -166,6 +168,9 @@ export default class DataAggregator { | @@ -166,6 +168,9 @@ export default class DataAggregator { | ||
166 | } | 168 | } |
167 | } | 169 | } |
168 | keyData = this.$filter('orderBy')(keyData, '+this[0]'); | 170 | keyData = this.$filter('orderBy')(keyData, '+this[0]'); |
171 | + if (this.stateData) { | ||
172 | + this.updateStateBounds(keyData, angular.copy(this.lastPrevKvPairData[key])); | ||
173 | + } | ||
169 | if (keyData.length > this.limit) { | 174 | if (keyData.length > this.limit) { |
170 | keyData = keyData.slice(keyData.length - this.limit); | 175 | keyData = keyData.slice(keyData.length - this.limit); |
171 | } | 176 | } |
@@ -174,6 +179,34 @@ export default class DataAggregator { | @@ -174,6 +179,34 @@ export default class DataAggregator { | ||
174 | return this.dataBuffer; | 179 | return this.dataBuffer; |
175 | } | 180 | } |
176 | 181 | ||
182 | + updateStateBounds(keyData, lastPrevKvPair) { | ||
183 | + if (lastPrevKvPair) { | ||
184 | + lastPrevKvPair[0] = this.startTs; | ||
185 | + } | ||
186 | + var firstKvPair; | ||
187 | + if (!keyData.length) { | ||
188 | + if (lastPrevKvPair) { | ||
189 | + firstKvPair = lastPrevKvPair; | ||
190 | + keyData.push(firstKvPair); | ||
191 | + } | ||
192 | + } else { | ||
193 | + firstKvPair = keyData[0]; | ||
194 | + } | ||
195 | + if (firstKvPair && firstKvPair[0] > this.startTs) { | ||
196 | + if (lastPrevKvPair) { | ||
197 | + keyData.unshift(lastPrevKvPair); | ||
198 | + } | ||
199 | + } | ||
200 | + if (keyData.length) { | ||
201 | + var lastKvPair = keyData[keyData.length-1]; | ||
202 | + if (lastKvPair[0] < this.endTs) { | ||
203 | + lastKvPair = angular.copy(lastKvPair); | ||
204 | + lastKvPair[0] = this.endTs; | ||
205 | + keyData.push(lastKvPair); | ||
206 | + } | ||
207 | + } | ||
208 | + } | ||
209 | + | ||
177 | destroy() { | 210 | destroy() { |
178 | if (this.intervalTimeoutHandle) { | 211 | if (this.intervalTimeoutHandle) { |
179 | this.$timeout.cancel(this.intervalTimeoutHandle); | 212 | this.$timeout.cancel(this.intervalTimeoutHandle); |
@@ -105,7 +105,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -105,7 +105,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
105 | var datasourceType = datasourceSubscription.datasourceType; | 105 | var datasourceType = datasourceSubscription.datasourceType; |
106 | var datasourceData = {}; | 106 | var datasourceData = {}; |
107 | var dataKeys = {}; | 107 | var dataKeys = {}; |
108 | - var subscribers = {}; | 108 | + var subscribers = []; |
109 | var history = datasourceSubscription.subscriptionTimewindow && | 109 | var history = datasourceSubscription.subscriptionTimewindow && |
110 | datasourceSubscription.subscriptionTimewindow.fixedWindow; | 110 | datasourceSubscription.subscriptionTimewindow.fixedWindow; |
111 | var realtime = datasourceSubscription.subscriptionTimewindow && | 111 | var realtime = datasourceSubscription.subscriptionTimewindow && |
@@ -249,7 +249,6 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -249,7 +249,6 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
249 | if (tsKeys.length > 0) { | 249 | if (tsKeys.length > 0) { |
250 | 250 | ||
251 | var subscriber; | 251 | var subscriber; |
252 | - var subscriptionCommand; | ||
253 | 252 | ||
254 | if (history) { | 253 | if (history) { |
255 | 254 | ||
@@ -265,45 +264,103 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -265,45 +264,103 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
265 | }; | 264 | }; |
266 | 265 | ||
267 | subscriber = { | 266 | subscriber = { |
268 | - historyCommand: historyCommand, | 267 | + historyCommands: [ historyCommand ], |
269 | type: types.dataKeyType.timeseries, | 268 | type: types.dataKeyType.timeseries, |
270 | - onData: function (data) { | ||
271 | - if (data.data) { | ||
272 | - for (var key in data.data) { | 269 | + subsTw: subsTw |
270 | + }; | ||
271 | + | ||
272 | + if (subsTw.aggregation.stateData) { | ||
273 | + subscriber.firstStateHistoryCommand = createFirstStateHistoryCommand(subsTw.fixedWindow.startTimeMs, tsKeys); | ||
274 | + subscriber.historyCommands.push(subscriber.firstStateHistoryCommand); | ||
275 | + } | ||
276 | + | ||
277 | + subscriber.onData = function (data, subscriptionId) { | ||
278 | + if (this.subsTw.aggregation.stateData && | ||
279 | + this.firstStateHistoryCommand && this.firstStateHistoryCommand.cmdId == subscriptionId) { | ||
280 | + if (this.data) { | ||
281 | + onStateHistoryData(data, this.data, this.subsTw.aggregation.limit, | ||
282 | + subsTw.fixedWindow.startTimeMs, this.subsTw.fixedWindow.endTimeMs, | ||
283 | + (data) => { | ||
284 | + onData(data.data, types.dataKeyType.timeseries, true); | ||
285 | + }); | ||
286 | + } else { | ||
287 | + this.firstStateData = data; | ||
288 | + } | ||
289 | + } else { | ||
290 | + if (this.subsTw.aggregation.stateData) { | ||
291 | + if (this.firstStateData) { | ||
292 | + onStateHistoryData(this.firstStateData, data, this.subsTw.aggregation.limit, | ||
293 | + this.subsTw.fixedWindow.startTimeMs, this.subsTw.fixedWindow.endTimeMs, | ||
294 | + (data) => { | ||
295 | + onData(data.data, types.dataKeyType.timeseries, true); | ||
296 | + }); | ||
297 | + } else { | ||
298 | + this.data = data; | ||
299 | + } | ||
300 | + } else { | ||
301 | + for (key in data.data) { | ||
273 | var keyData = data.data[key]; | 302 | var keyData = data.data[key]; |
274 | data.data[key] = $filter('orderBy')(keyData, '+this[0]'); | 303 | data.data[key] = $filter('orderBy')(keyData, '+this[0]'); |
275 | } | 304 | } |
276 | onData(data.data, types.dataKeyType.timeseries, true); | 305 | onData(data.data, types.dataKeyType.timeseries, true); |
277 | } | 306 | } |
278 | - }, | ||
279 | - onReconnected: function() {} | 307 | + } |
280 | }; | 308 | }; |
281 | - | 309 | + subscriber.onReconnected = function() {}; |
282 | telemetryWebsocketService.subscribe(subscriber); | 310 | telemetryWebsocketService.subscribe(subscriber); |
283 | - subscribers[subscriber.historyCommand.cmdId] = subscriber; | ||
284 | - | ||
285 | - if (subsTw.aggregation.steppedChart) { | ||
286 | - createFirstStepSubscription(subsTw, tsKeys); | ||
287 | - } | 311 | + subscribers.push(subscriber); |
288 | 312 | ||
289 | } else { | 313 | } else { |
290 | 314 | ||
291 | - subscriptionCommand = { | 315 | + var subscriptionCommand = { |
292 | entityType: datasourceSubscription.entityType, | 316 | entityType: datasourceSubscription.entityType, |
293 | entityId: datasourceSubscription.entityId, | 317 | entityId: datasourceSubscription.entityId, |
294 | keys: tsKeys | 318 | keys: tsKeys |
295 | }; | 319 | }; |
296 | 320 | ||
297 | subscriber = { | 321 | subscriber = { |
298 | - subscriptionCommand: subscriptionCommand, | 322 | + subscriptionCommands: [subscriptionCommand], |
299 | type: types.dataKeyType.timeseries | 323 | type: types.dataKeyType.timeseries |
300 | }; | 324 | }; |
301 | 325 | ||
302 | if (datasourceSubscription.type === types.widgetType.timeseries.value) { | 326 | if (datasourceSubscription.type === types.widgetType.timeseries.value) { |
327 | + subscriber.subsTw = subsTw; | ||
303 | updateRealtimeSubscriptionCommand(subscriptionCommand, subsTw); | 328 | updateRealtimeSubscriptionCommand(subscriptionCommand, subsTw); |
329 | + | ||
330 | + if (subsTw.aggregation.stateData) { | ||
331 | + subscriber.firstStateSubscriptionCommand = createFirstStateHistoryCommand(subsTw.startTs, tsKeys); | ||
332 | + subscriber.historyCommands = [subscriber.firstStateSubscriptionCommand]; | ||
333 | + } | ||
304 | dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.timeseries); | 334 | dataAggregator = createRealtimeDataAggregator(subsTw, tsKeyNames, types.dataKeyType.timeseries); |
305 | - subscriber.onData = function(data) { | ||
306 | - dataAggregator.onData(data, false, false, true); | 335 | + subscriber.onData = function(data, subscriptionId) { |
336 | + if (this.subsTw.aggregation.stateData && | ||
337 | + this.firstStateSubscriptionCommand && this.firstStateSubscriptionCommand.cmdId == subscriptionId) { | ||
338 | + if (this.data) { | ||
339 | + onStateHistoryData(data, this.data, this.subsTw.aggregation.limit, | ||
340 | + this.subsTw.startTs, this.subsTw.startTs + this.subsTw.aggregation.timeWindow, | ||
341 | + (data) => { | ||
342 | + dataAggregator.onData(data, false, false, true); | ||
343 | + }); | ||
344 | + this.stateDataReceived = true; | ||
345 | + } else { | ||
346 | + this.firstStateData = data; | ||
347 | + } | ||
348 | + } else { | ||
349 | + if (this.subsTw.aggregation.stateData && !this.stateDataReceived) { | ||
350 | + if (this.firstStateData) { | ||
351 | + onStateHistoryData(this.firstStateData, data, this.subsTw.aggregation.limit, | ||
352 | + this.subsTw.startTs, this.subsTw.startTs + this.subsTw.aggregation.timeWindow, | ||
353 | + (data) => { | ||
354 | + dataAggregator.onData(data, false, false, true); | ||
355 | + }); | ||
356 | + this.stateDataReceived = true; | ||
357 | + } else { | ||
358 | + this.data = data; | ||
359 | + } | ||
360 | + } else { | ||
361 | + dataAggregator.onData(data, false, false, true); | ||
362 | + } | ||
363 | + } | ||
307 | } | 364 | } |
308 | subscriber.onReconnected = function() { | 365 | subscriber.onReconnected = function() { |
309 | var newSubsTw = null; | 366 | var newSubsTw = null; |
@@ -315,14 +372,16 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -315,14 +372,16 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
315 | listener.setRealtimeSubscription(newSubsTw); | 372 | listener.setRealtimeSubscription(newSubsTw); |
316 | } | 373 | } |
317 | } | 374 | } |
318 | - updateRealtimeSubscriptionCommand(this.subscriptionCommand, newSubsTw); | 375 | + this.subsTw = newSubsTw; |
376 | + this.firstStateData = null; | ||
377 | + this.data = null; | ||
378 | + this.stateDataReceived = false; | ||
379 | + updateRealtimeSubscriptionCommand(this.subscriptionCommands[0], this.subsTw); | ||
380 | + if (this.subsTw.aggregation.stateData) { | ||
381 | + updateFirstStateHistoryCommand(this.firstStateSubscriptionCommand, this.subsTw.startTs); | ||
382 | + } | ||
319 | dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval); | 383 | dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval); |
320 | } | 384 | } |
321 | - | ||
322 | - if (subsTw.aggregation.steppedChart) { | ||
323 | - createFirstStepSubscription(subsTw, tsKeys); | ||
324 | - } | ||
325 | - | ||
326 | } else { | 385 | } else { |
327 | subscriber.onReconnected = function() {} | 386 | subscriber.onReconnected = function() {} |
328 | subscriber.onData = function(data) { | 387 | subscriber.onData = function(data) { |
@@ -333,21 +392,21 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -333,21 +392,21 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
333 | } | 392 | } |
334 | 393 | ||
335 | telemetryWebsocketService.subscribe(subscriber); | 394 | telemetryWebsocketService.subscribe(subscriber); |
336 | - subscribers[subscriber.subscriptionCommand.cmdId] = subscriber; | 395 | + subscribers.push(subscriber); |
337 | 396 | ||
338 | } | 397 | } |
339 | } | 398 | } |
340 | 399 | ||
341 | if (attrKeys.length > 0) { | 400 | if (attrKeys.length > 0) { |
342 | 401 | ||
343 | - subscriptionCommand = { | 402 | + var attrsSubscriptionCommand = { |
344 | entityType: datasourceSubscription.entityType, | 403 | entityType: datasourceSubscription.entityType, |
345 | entityId: datasourceSubscription.entityId, | 404 | entityId: datasourceSubscription.entityId, |
346 | keys: attrKeys | 405 | keys: attrKeys |
347 | }; | 406 | }; |
348 | 407 | ||
349 | subscriber = { | 408 | subscriber = { |
350 | - subscriptionCommand: subscriptionCommand, | 409 | + subscriptionCommands: [attrsSubscriptionCommand], |
351 | type: types.dataKeyType.attribute, | 410 | type: types.dataKeyType.attribute, |
352 | onData: function (data) { | 411 | onData: function (data) { |
353 | if (data.data) { | 412 | if (data.data) { |
@@ -358,7 +417,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -358,7 +417,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
358 | }; | 417 | }; |
359 | 418 | ||
360 | telemetryWebsocketService.subscribe(subscriber); | 419 | telemetryWebsocketService.subscribe(subscriber); |
361 | - subscribers[subscriber.cmdId] = subscriber; | 420 | + subscribers.push(subscriber); |
362 | 421 | ||
363 | } | 422 | } |
364 | 423 | ||
@@ -388,35 +447,47 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -388,35 +447,47 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
388 | } | 447 | } |
389 | } | 448 | } |
390 | 449 | ||
391 | - function createFirstStepSubscription(subsTw, tsKeys) { | ||
392 | - var startStepCommand = { | 450 | + function createFirstStateHistoryCommand(startTs, tsKeys) { |
451 | + return { | ||
393 | entityType: datasourceSubscription.entityType, | 452 | entityType: datasourceSubscription.entityType, |
394 | entityId: datasourceSubscription.entityId, | 453 | entityId: datasourceSubscription.entityId, |
395 | keys: tsKeys, | 454 | keys: tsKeys, |
396 | - startTs: subsTw.fixedWindow.startTimeMs - YEAR, | ||
397 | - endTs: subsTw.fixedWindow.startTimeMs, | ||
398 | - interval: subsTw.aggregation.interval, | 455 | + startTs: startTs - YEAR, |
456 | + endTs: startTs, | ||
457 | + interval: 1000, | ||
399 | limit: 1, | 458 | limit: 1, |
400 | - agg: subsTw.aggregation.type | ||
401 | - }; | ||
402 | - var subscriber = { | ||
403 | - historyCommand: startStepCommand, | ||
404 | - type: types.dataKeyType.timeseries, | ||
405 | - onData: function (data) { | ||
406 | - if (data.data) { | ||
407 | - for (var key in data.data) { | ||
408 | - var keyData = data.data[key]; | ||
409 | - data.data[key] = $filter('orderBy')(keyData, '+this[0]'); | ||
410 | - } | ||
411 | - //onData(data.data, types.dataKeyType.timeseries, true); | ||
412 | - //TODO: onStartStepData | ||
413 | - } | ||
414 | - }, | ||
415 | - onReconnected: function() {} | 459 | + agg: types.aggregation.none.value |
416 | }; | 460 | }; |
461 | + } | ||
462 | + | ||
463 | + function updateFirstStateHistoryCommand(stateHistoryCommand, startTs) { | ||
464 | + stateHistoryCommand.startTs = startTs - YEAR; | ||
465 | + stateHistoryCommand.endTs = startTs; | ||
466 | + } | ||
417 | 467 | ||
418 | - telemetryWebsocketService.subscribe(subscriber); | ||
419 | - subscribers[subscriber.historyCommand.cmdId] = subscriber; | 468 | + function onStateHistoryData(firstStateData, data, limit, startTs, endTs, onData) { |
469 | + for (var key in data.data) { | ||
470 | + var keyData = data.data[key]; | ||
471 | + data.data[key] = $filter('orderBy')(keyData, '+this[0]'); | ||
472 | + keyData = data.data[key]; | ||
473 | + if (keyData.length < limit) { | ||
474 | + var firstStateKeyData = firstStateData.data[key]; | ||
475 | + if (firstStateKeyData.length) { | ||
476 | + var firstStateDataTsKv = firstStateKeyData[0]; | ||
477 | + firstStateDataTsKv[0] = startTs; | ||
478 | + firstStateKeyData = [ | ||
479 | + [ startTs, firstStateKeyData[0][1] ] | ||
480 | + ]; | ||
481 | + keyData.unshift(firstStateDataTsKv); | ||
482 | + } | ||
483 | + } | ||
484 | + if (keyData.length) { | ||
485 | + var lastTsKv = angular.copy(keyData[keyData.length-1]); | ||
486 | + lastTsKv[0] = endTs; | ||
487 | + keyData.push(lastTsKv); | ||
488 | + } | ||
489 | + } | ||
490 | + onData(data); | ||
420 | } | 491 | } |
421 | 492 | ||
422 | function createRealtimeDataAggregator(subsTw, tsKeyNames, dataKeyType) { | 493 | function createRealtimeDataAggregator(subsTw, tsKeyNames, dataKeyType) { |
@@ -430,7 +501,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -430,7 +501,7 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
430 | subsTw.aggregation.type, | 501 | subsTw.aggregation.type, |
431 | subsTw.aggregation.timeWindow, | 502 | subsTw.aggregation.timeWindow, |
432 | subsTw.aggregation.interval, | 503 | subsTw.aggregation.interval, |
433 | - subsTw.aggregation.steppedChart, | 504 | + subsTw.aggregation.stateData, |
434 | types, | 505 | types, |
435 | $timeout, | 506 | $timeout, |
436 | $filter | 507 | $filter |
@@ -451,14 +522,14 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | @@ -451,14 +522,14 @@ function DatasourceSubscription(datasourceSubscription, telemetryWebsocketServic | ||
451 | timer = null; | 522 | timer = null; |
452 | } | 523 | } |
453 | if (datasourceType === types.datasourceType.entity) { | 524 | if (datasourceType === types.datasourceType.entity) { |
454 | - for (var cmdId in subscribers) { | ||
455 | - var subscriber = subscribers[cmdId]; | 525 | + for (var i=0;i<subscribers.length;i++) { |
526 | + var subscriber = subscribers[i]; | ||
456 | telemetryWebsocketService.unsubscribe(subscriber); | 527 | telemetryWebsocketService.unsubscribe(subscriber); |
457 | if (subscriber.onDestroy) { | 528 | if (subscriber.onDestroy) { |
458 | subscriber.onDestroy(); | 529 | subscriber.onDestroy(); |
459 | } | 530 | } |
460 | } | 531 | } |
461 | - subscribers = {}; | 532 | + subscribers.length = 0; |
462 | } | 533 | } |
463 | if (dataAggregator) { | 534 | if (dataAggregator) { |
464 | dataAggregator.destroy(); | 535 | dataAggregator.destroy(); |
@@ -128,7 +128,7 @@ export default class Subscription { | @@ -128,7 +128,7 @@ export default class Subscription { | ||
128 | stDiff: this.ctx.stDiff | 128 | stDiff: this.ctx.stDiff |
129 | } | 129 | } |
130 | this.useDashboardTimewindow = options.useDashboardTimewindow; | 130 | this.useDashboardTimewindow = options.useDashboardTimewindow; |
131 | - this.steppedChart = options.steppedChart; | 131 | + this.stateData = options.stateData; |
132 | if (this.useDashboardTimewindow) { | 132 | if (this.useDashboardTimewindow) { |
133 | this.timeWindowConfig = angular.copy(options.dashboardTimewindow); | 133 | this.timeWindowConfig = angular.copy(options.dashboardTimewindow); |
134 | } else { | 134 | } else { |
@@ -612,7 +612,7 @@ export default class Subscription { | @@ -612,7 +612,7 @@ export default class Subscription { | ||
612 | this.subscriptionTimewindow = | 612 | this.subscriptionTimewindow = |
613 | this.ctx.timeService.createSubscriptionTimewindow( | 613 | this.ctx.timeService.createSubscriptionTimewindow( |
614 | this.timeWindowConfig, | 614 | this.timeWindowConfig, |
615 | - this.timeWindow.stDiff, this.steppedChart); | 615 | + this.timeWindow.stDiff, this.stateData); |
616 | } | 616 | } |
617 | this.updateTimewindow(); | 617 | this.updateTimewindow(); |
618 | return this.subscriptionTimewindow; | 618 | return this.subscriptionTimewindow; |
@@ -34,6 +34,7 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | @@ -34,6 +34,7 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | ||
34 | lastCmdId = 0, | 34 | lastCmdId = 0, |
35 | subscribers = {}, | 35 | subscribers = {}, |
36 | subscribersCount = 0, | 36 | subscribersCount = 0, |
37 | + commands = {}, | ||
37 | cmdsWrapper = { | 38 | cmdsWrapper = { |
38 | tsSubCmds: [], | 39 | tsSubCmds: [], |
39 | historyCmds: [], | 40 | historyCmds: [], |
@@ -120,7 +121,10 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | @@ -120,7 +121,10 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | ||
120 | if (!isReconnect) { | 121 | if (!isReconnect) { |
121 | reconnectSubscribers = []; | 122 | reconnectSubscribers = []; |
122 | for (var id in subscribers) { | 123 | for (var id in subscribers) { |
123 | - reconnectSubscribers.push(subscribers[id]); | 124 | + var subscriber = subscribers[id]; |
125 | + if (reconnectSubscribers.indexOf(subscriber) === -1) { | ||
126 | + reconnectSubscribers.push(subscriber); | ||
127 | + } | ||
124 | } | 128 | } |
125 | reset(false); | 129 | reset(false); |
126 | isReconnect = true; | 130 | isReconnect = true; |
@@ -138,7 +142,7 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | @@ -138,7 +142,7 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | ||
138 | if (data.subscriptionId) { | 142 | if (data.subscriptionId) { |
139 | var subscriber = subscribers[data.subscriptionId]; | 143 | var subscriber = subscribers[data.subscriptionId]; |
140 | if (subscriber && data) { | 144 | if (subscriber && data) { |
141 | - var keys = fetchKeys(subscriber); | 145 | + var keys = fetchKeys(data.subscriptionId); |
142 | if (!data.data) { | 146 | if (!data.data) { |
143 | data.data = {}; | 147 | data.data = {}; |
144 | } | 148 | } |
@@ -148,20 +152,15 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | @@ -148,20 +152,15 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | ||
148 | data.data[key] = []; | 152 | data.data[key] = []; |
149 | } | 153 | } |
150 | } | 154 | } |
151 | - subscriber.onData(data); | 155 | + subscriber.onData(data, data.subscriptionId); |
152 | } | 156 | } |
153 | } | 157 | } |
154 | } | 158 | } |
155 | checkToClose(); | 159 | checkToClose(); |
156 | } | 160 | } |
157 | 161 | ||
158 | - function fetchKeys(subscriber) { | ||
159 | - var command; | ||
160 | - if (angular.isDefined(subscriber.subscriptionCommand)) { | ||
161 | - command = subscriber.subscriptionCommand; | ||
162 | - } else { | ||
163 | - command = subscriber.historyCommand; | ||
164 | - } | 162 | + function fetchKeys(subscriptionId) { |
163 | + var command = commands[subscriptionId]; | ||
165 | if (command && command.keys && command.keys.length > 0) { | 164 | if (command && command.keys && command.keys.length > 0) { |
166 | return command.keys.split(","); | 165 | return command.keys.split(","); |
167 | } else { | 166 | } else { |
@@ -176,41 +175,73 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | @@ -176,41 +175,73 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | ||
176 | 175 | ||
177 | function subscribe (subscriber) { | 176 | function subscribe (subscriber) { |
178 | isActive = true; | 177 | isActive = true; |
179 | - var cmdId = nextCmdId(); | ||
180 | - subscribers[cmdId] = subscriber; | ||
181 | - subscribersCount++; | ||
182 | - if (angular.isDefined(subscriber.subscriptionCommand)) { | ||
183 | - subscriber.subscriptionCommand.cmdId = cmdId; | ||
184 | - if (subscriber.type === types.dataKeyType.timeseries) { | ||
185 | - cmdsWrapper.tsSubCmds.push(subscriber.subscriptionCommand); | ||
186 | - } else if (subscriber.type === types.dataKeyType.attribute) { | ||
187 | - cmdsWrapper.attrSubCmds.push(subscriber.subscriptionCommand); | 178 | + var cmdId; |
179 | + if (angular.isDefined(subscriber.subscriptionCommands)) { | ||
180 | + for (var i=0;i<subscriber.subscriptionCommands.length;i++) { | ||
181 | + var subscriptionCommand = subscriber.subscriptionCommands[i]; | ||
182 | + cmdId = nextCmdId(); | ||
183 | + subscribers[cmdId] = subscriber; | ||
184 | + subscriptionCommand.cmdId = cmdId; | ||
185 | + commands[cmdId] = subscriptionCommand; | ||
186 | + if (subscriber.type === types.dataKeyType.timeseries) { | ||
187 | + cmdsWrapper.tsSubCmds.push(subscriptionCommand); | ||
188 | + } else if (subscriber.type === types.dataKeyType.attribute) { | ||
189 | + cmdsWrapper.attrSubCmds.push(subscriptionCommand); | ||
190 | + } | ||
188 | } | 191 | } |
189 | - } else if (angular.isDefined(subscriber.historyCommand)) { | ||
190 | - subscriber.historyCommand.cmdId = cmdId; | ||
191 | - cmdsWrapper.historyCmds.push(subscriber.historyCommand); | ||
192 | } | 192 | } |
193 | + if (angular.isDefined(subscriber.historyCommands)) { | ||
194 | + for (i=0;i<subscriber.historyCommands.length;i++) { | ||
195 | + var historyCommand = subscriber.historyCommands[i]; | ||
196 | + cmdId = nextCmdId(); | ||
197 | + subscribers[cmdId] = subscriber; | ||
198 | + historyCommand.cmdId = cmdId; | ||
199 | + commands[cmdId] = historyCommand; | ||
200 | + cmdsWrapper.historyCmds.push(historyCommand); | ||
201 | + } | ||
202 | + } | ||
203 | + subscribersCount++; | ||
193 | publishCommands(); | 204 | publishCommands(); |
194 | } | 205 | } |
195 | 206 | ||
196 | function unsubscribe (subscriber) { | 207 | function unsubscribe (subscriber) { |
197 | if (isActive) { | 208 | if (isActive) { |
198 | var cmdId = null; | 209 | var cmdId = null; |
199 | - if (subscriber.subscriptionCommand) { | ||
200 | - subscriber.subscriptionCommand.unsubscribe = true; | ||
201 | - if (subscriber.type === types.dataKeyType.timeseries) { | ||
202 | - cmdsWrapper.tsSubCmds.push(subscriber.subscriptionCommand); | ||
203 | - } else if (subscriber.type === types.dataKeyType.attribute) { | ||
204 | - cmdsWrapper.attrSubCmds.push(subscriber.subscriptionCommand); | 210 | + if (subscriber.subscriptionCommands) { |
211 | + for (var i=0;i<subscriber.subscriptionCommands.length;i++) { | ||
212 | + var subscriptionCommand = subscriber.subscriptionCommands[i]; | ||
213 | + subscriptionCommand.unsubscribe = true; | ||
214 | + if (subscriber.type === types.dataKeyType.timeseries) { | ||
215 | + cmdsWrapper.tsSubCmds.push(subscriptionCommand); | ||
216 | + } else if (subscriber.type === types.dataKeyType.attribute) { | ||
217 | + cmdsWrapper.attrSubCmds.push(subscriptionCommand); | ||
218 | + } | ||
219 | + cmdId = subscriptionCommand.cmdId; | ||
220 | + if (cmdId) { | ||
221 | + if (subscribers[cmdId]) { | ||
222 | + delete subscribers[cmdId]; | ||
223 | + } | ||
224 | + if (commands[cmdId]) { | ||
225 | + delete commands[cmdId]; | ||
226 | + } | ||
227 | + } | ||
205 | } | 228 | } |
206 | - cmdId = subscriber.subscriptionCommand.cmdId; | ||
207 | - } else if (subscriber.historyCommand) { | ||
208 | - cmdId = subscriber.historyCommand.cmdId; | ||
209 | } | 229 | } |
210 | - if (cmdId && subscribers[cmdId]) { | ||
211 | - delete subscribers[cmdId]; | ||
212 | - subscribersCount--; | 230 | + if (subscriber.historyCommands) { |
231 | + for (i=0;i<subscriber.historyCommands.length;i++) { | ||
232 | + var historyCommand = subscriber.historyCommands[i]; | ||
233 | + cmdId = historyCommand.cmdId; | ||
234 | + if (cmdId) { | ||
235 | + if (subscribers[cmdId]) { | ||
236 | + delete subscribers[cmdId]; | ||
237 | + } | ||
238 | + if (commands[cmdId]) { | ||
239 | + delete commands[cmdId]; | ||
240 | + } | ||
241 | + } | ||
242 | + } | ||
213 | } | 243 | } |
244 | + subscribersCount--; | ||
214 | publishCommands(); | 245 | publishCommands(); |
215 | } | 246 | } |
216 | } | 247 | } |
@@ -268,6 +299,7 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | @@ -268,6 +299,7 @@ function TelemetryWebsocketService($rootScope, $websocket, $timeout, $window, ty | ||
268 | lastCmdId = 0; | 299 | lastCmdId = 0; |
269 | subscribers = {}; | 300 | subscribers = {}; |
270 | subscribersCount = 0; | 301 | subscribersCount = 0; |
302 | + commands = {}; | ||
271 | cmdsWrapper.tsSubCmds = []; | 303 | cmdsWrapper.tsSubCmds = []; |
272 | cmdsWrapper.historyCmds = []; | 304 | cmdsWrapper.historyCmds = []; |
273 | cmdsWrapper.attrSubCmds = []; | 305 | cmdsWrapper.attrSubCmds = []; |
@@ -261,7 +261,7 @@ function TimeService($translate, types) { | @@ -261,7 +261,7 @@ function TimeService($translate, types) { | ||
261 | return historyTimewindow; | 261 | return historyTimewindow; |
262 | } | 262 | } |
263 | 263 | ||
264 | - function createSubscriptionTimewindow(timewindow, stDiff, steppedChart) { | 264 | + function createSubscriptionTimewindow(timewindow, stDiff, stateData) { |
265 | 265 | ||
266 | var subscriptionTimewindow = { | 266 | var subscriptionTimewindow = { |
267 | fixedWindow: null, | 267 | fixedWindow: null, |
@@ -273,12 +273,12 @@ function TimeService($translate, types) { | @@ -273,12 +273,12 @@ function TimeService($translate, types) { | ||
273 | } | 273 | } |
274 | }; | 274 | }; |
275 | var aggTimewindow = 0; | 275 | var aggTimewindow = 0; |
276 | - if (steppedChart) { | 276 | + if (stateData) { |
277 | subscriptionTimewindow.aggregation = { | 277 | subscriptionTimewindow.aggregation = { |
278 | interval: SECOND, | 278 | interval: SECOND, |
279 | limit: MAX_LIMIT, | 279 | limit: MAX_LIMIT, |
280 | type: types.aggregation.none.value, | 280 | type: types.aggregation.none.value, |
281 | - steppedChart: true | 281 | + stateData: true |
282 | }; | 282 | }; |
283 | } else { | 283 | } else { |
284 | subscriptionTimewindow.aggregation = { | 284 | subscriptionTimewindow.aggregation = { |
@@ -288,7 +288,7 @@ function TimeService($translate, types) { | @@ -288,7 +288,7 @@ function TimeService($translate, types) { | ||
288 | }; | 288 | }; |
289 | } | 289 | } |
290 | 290 | ||
291 | - if (angular.isDefined(timewindow.aggregation) && !steppedChart) { | 291 | + if (angular.isDefined(timewindow.aggregation) && !stateData) { |
292 | subscriptionTimewindow.aggregation = { | 292 | subscriptionTimewindow.aggregation = { |
293 | type: timewindow.aggregation.type || types.aggregation.avg.value, | 293 | type: timewindow.aggregation.type || types.aggregation.avg.value, |
294 | limit: timewindow.aggregation.limit || AVG_LIMIT | 294 | limit: timewindow.aggregation.limit || AVG_LIMIT |
@@ -561,7 +561,7 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr | @@ -561,7 +561,7 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr | ||
561 | maxDatasources: -1, //unlimited | 561 | maxDatasources: -1, //unlimited |
562 | maxDataKeys: -1, //unlimited | 562 | maxDataKeys: -1, //unlimited |
563 | dataKeysOptional: false, | 563 | dataKeysOptional: false, |
564 | - steppedChart: false | 564 | + stateData: false |
565 | }; | 565 | }; |
566 | ' }\n\n' + | 566 | ' }\n\n' + |
567 | 567 | ||
@@ -632,8 +632,8 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr | @@ -632,8 +632,8 @@ function WidgetService($rootScope, $http, $q, $filter, $ocLazyLoad, $window, $tr | ||
632 | if (angular.isUndefined(result.typeParameters.dataKeysOptional)) { | 632 | if (angular.isUndefined(result.typeParameters.dataKeysOptional)) { |
633 | result.typeParameters.dataKeysOptional = false; | 633 | result.typeParameters.dataKeysOptional = false; |
634 | } | 634 | } |
635 | - if (angular.isUndefined(result.typeParameters.steppedChart)) { | ||
636 | - result.typeParameters.steppedChart = false; | 635 | + if (angular.isUndefined(result.typeParameters.stateData)) { |
636 | + result.typeParameters.stateData = false; | ||
637 | } | 637 | } |
638 | if (angular.isFunction(widgetTypeInstance.actionSources)) { | 638 | if (angular.isFunction(widgetTypeInstance.actionSources)) { |
639 | result.actionSources = widgetTypeInstance.actionSources(); | 639 | result.actionSources = widgetTypeInstance.actionSources(); |
@@ -340,7 +340,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele | @@ -340,7 +340,7 @@ export default function WidgetController($scope, $state, $timeout, $window, $ele | ||
340 | if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) { | 340 | if (widget.type !== types.widgetType.rpc.value && widget.type !== types.widgetType.static.value) { |
341 | options = { | 341 | options = { |
342 | type: widget.type, | 342 | type: widget.type, |
343 | - steppedChart: vm.typeParameters.steppedChart | 343 | + stateData: vm.typeParameters.stateData |
344 | } | 344 | } |
345 | if (widget.type == types.widgetType.alarm.value) { | 345 | if (widget.type == types.widgetType.alarm.value) { |
346 | options.alarmSource = angular.copy(widget.config.alarmSource); | 346 | options.alarmSource = angular.copy(widget.config.alarmSource); |
@@ -55,7 +55,7 @@ export default class TbFlot { | @@ -55,7 +55,7 @@ export default class TbFlot { | ||
55 | 55 | ||
56 | var tbFlot = this; | 56 | var tbFlot = this; |
57 | 57 | ||
58 | - function seriesInfoDiv(label, color, value, units, trackDecimals, active, percent) { | 58 | + function seriesInfoDiv(label, color, value, units, trackDecimals, active, percent, valueFormatFunction) { |
59 | var divElement = $('<div></div>'); | 59 | var divElement = $('<div></div>'); |
60 | divElement.css({ | 60 | divElement.css({ |
61 | display: "flex", | 61 | display: "flex", |
@@ -83,7 +83,12 @@ export default class TbFlot { | @@ -83,7 +83,12 @@ export default class TbFlot { | ||
83 | }); | 83 | }); |
84 | } | 84 | } |
85 | divElement.append(labelSpan); | 85 | divElement.append(labelSpan); |
86 | - var valueContent = tbFlot.ctx.utils.formatValue(value, trackDecimals, units); | 86 | + var valueContent; |
87 | + if (valueFormatFunction) { | ||
88 | + valueContent = valueFormatFunction(value); | ||
89 | + } else { | ||
90 | + valueContent = tbFlot.ctx.utils.formatValue(value, trackDecimals, units); | ||
91 | + } | ||
87 | if (angular.isNumber(percent)) { | 92 | if (angular.isNumber(percent)) { |
88 | valueContent += ' (' + Math.round(percent) + ' %)'; | 93 | valueContent += ' (' + Math.round(percent) + ' %)'; |
89 | } | 94 | } |
@@ -107,7 +112,7 @@ export default class TbFlot { | @@ -107,7 +112,7 @@ export default class TbFlot { | ||
107 | var units = item.series.dataKey.units && item.series.dataKey.units.length ? item.series.dataKey.units : tbFlot.ctx.trackUnits; | 112 | var units = item.series.dataKey.units && item.series.dataKey.units.length ? item.series.dataKey.units : tbFlot.ctx.trackUnits; |
108 | var decimals = angular.isDefined(item.series.dataKey.decimals) ? item.series.dataKey.decimals : tbFlot.ctx.trackDecimals; | 113 | var decimals = angular.isDefined(item.series.dataKey.decimals) ? item.series.dataKey.decimals : tbFlot.ctx.trackDecimals; |
109 | var divElement = seriesInfoDiv(item.series.dataKey.label, item.series.dataKey.color, | 114 | var divElement = seriesInfoDiv(item.series.dataKey.label, item.series.dataKey.color, |
110 | - item.datapoint[1][0][1], units, decimals, true, item.series.percent); | 115 | + item.datapoint[1][0][1], units, decimals, true, item.series.percent, item.series.dataKey.tooltipValueFormatFunction); |
111 | return divElement.prop('outerHTML'); | 116 | return divElement.prop('outerHTML'); |
112 | }; | 117 | }; |
113 | } else { | 118 | } else { |
@@ -132,7 +137,7 @@ export default class TbFlot { | @@ -132,7 +137,7 @@ export default class TbFlot { | ||
132 | var units = seriesHoverInfo.units && seriesHoverInfo.units.length ? seriesHoverInfo.units : tbFlot.ctx.trackUnits; | 137 | var units = seriesHoverInfo.units && seriesHoverInfo.units.length ? seriesHoverInfo.units : tbFlot.ctx.trackUnits; |
133 | var decimals = angular.isDefined(seriesHoverInfo.decimals) ? seriesHoverInfo.decimals : tbFlot.ctx.trackDecimals; | 138 | var decimals = angular.isDefined(seriesHoverInfo.decimals) ? seriesHoverInfo.decimals : tbFlot.ctx.trackDecimals; |
134 | var divElement = seriesInfoDiv(seriesHoverInfo.label, seriesHoverInfo.color, | 139 | var divElement = seriesInfoDiv(seriesHoverInfo.label, seriesHoverInfo.color, |
135 | - seriesHoverInfo.value, units, decimals, seriesHoverInfo.index === seriesIndex); | 140 | + seriesHoverInfo.value, units, decimals, seriesHoverInfo.index === seriesIndex, null, seriesHoverInfo.tooltipValueFormatFunction); |
136 | content += divElement.prop('outerHTML'); | 141 | content += divElement.prop('outerHTML'); |
137 | } | 142 | } |
138 | return content; | 143 | return content; |
@@ -168,7 +173,7 @@ export default class TbFlot { | @@ -168,7 +173,7 @@ export default class TbFlot { | ||
168 | } | 173 | } |
169 | }; | 174 | }; |
170 | 175 | ||
171 | - if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') { | 176 | + if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { |
172 | options.xaxis = { | 177 | options.xaxis = { |
173 | mode: 'time', | 178 | mode: 'time', |
174 | timezone: 'browser', | 179 | timezone: 'browser', |
@@ -196,6 +201,9 @@ export default class TbFlot { | @@ -196,6 +201,9 @@ export default class TbFlot { | ||
196 | if (settings.yaxis && settings.yaxis.showLabels === false) { | 201 | if (settings.yaxis && settings.yaxis.showLabels === false) { |
197 | return ''; | 202 | return ''; |
198 | } | 203 | } |
204 | + if (this.ticksFormatterFunction) { | ||
205 | + return this.ticksFormatterFunction(value); | ||
206 | + } | ||
199 | var factor = this.tickDecimals ? Math.pow(10, this.tickDecimals) : 1, | 207 | var factor = this.tickDecimals ? Math.pow(10, this.tickDecimals) : 1, |
200 | formatted = "" + Math.round(value * factor) / factor; | 208 | formatted = "" + Math.round(value * factor) / factor; |
201 | if (this.tickDecimals != null) { | 209 | if (this.tickDecimals != null) { |
@@ -218,6 +226,13 @@ export default class TbFlot { | @@ -218,6 +226,13 @@ export default class TbFlot { | ||
218 | this.yaxis.labelFont.color = this.yaxis.font.color; | 226 | this.yaxis.labelFont.color = this.yaxis.font.color; |
219 | this.yaxis.labelFont.size = this.yaxis.font.size+2; | 227 | this.yaxis.labelFont.size = this.yaxis.font.size+2; |
220 | this.yaxis.labelFont.weight = "bold"; | 228 | this.yaxis.labelFont.weight = "bold"; |
229 | + if (settings.yaxis.ticksFormatter && settings.yaxis.ticksFormatter.length) { | ||
230 | + try { | ||
231 | + this.yaxis.ticksFormatterFunction = new Function('value', settings.yaxis.ticksFormatter); | ||
232 | + } catch (e) { | ||
233 | + this.yaxis.ticksFormatterFunction = null; | ||
234 | + } | ||
235 | + } | ||
221 | } | 236 | } |
222 | 237 | ||
223 | options.grid.borderWidth = 1; | 238 | options.grid.borderWidth = 1; |
@@ -271,7 +286,7 @@ export default class TbFlot { | @@ -271,7 +286,7 @@ export default class TbFlot { | ||
271 | } | 286 | } |
272 | } | 287 | } |
273 | 288 | ||
274 | - if (this.chartType === 'stepped') { | 289 | + if (this.chartType === 'state') { |
275 | options.series.lines = { | 290 | options.series.lines = { |
276 | steps: true, | 291 | steps: true, |
277 | show: true | 292 | show: true |
@@ -331,11 +346,28 @@ export default class TbFlot { | @@ -331,11 +346,28 @@ export default class TbFlot { | ||
331 | var colors = []; | 346 | var colors = []; |
332 | this.yaxes = []; | 347 | this.yaxes = []; |
333 | var yaxesMap = {}; | 348 | var yaxesMap = {}; |
349 | + | ||
350 | + var tooltipValueFormatFunction = null; | ||
351 | + if (this.ctx.settings.tooltipValueFormatter && this.ctx.settings.tooltipValueFormatter.length) { | ||
352 | + try { | ||
353 | + tooltipValueFormatFunction = new Function('value', this.ctx.settings.tooltipValueFormatter); | ||
354 | + } catch (e) { | ||
355 | + tooltipValueFormatFunction = null; | ||
356 | + } | ||
357 | + } | ||
358 | + | ||
334 | for (var i = 0; i < this.subscription.data.length; i++) { | 359 | for (var i = 0; i < this.subscription.data.length; i++) { |
335 | var series = this.subscription.data[i]; | 360 | var series = this.subscription.data[i]; |
336 | colors.push(series.dataKey.color); | 361 | colors.push(series.dataKey.color); |
337 | var keySettings = series.dataKey.settings; | 362 | var keySettings = series.dataKey.settings; |
338 | - | 363 | + series.dataKey.tooltipValueFormatFunction = tooltipValueFormatFunction; |
364 | + if (keySettings.tooltipValueFormatter && keySettings.tooltipValueFormatter.length) { | ||
365 | + try { | ||
366 | + series.dataKey.tooltipValueFormatFunction = new Function('value', keySettings.tooltipValueFormatter); | ||
367 | + } catch (e) { | ||
368 | + series.dataKey.tooltipValueFormatFunction = tooltipValueFormatFunction; | ||
369 | + } | ||
370 | + } | ||
339 | series.lines = { | 371 | series.lines = { |
340 | fill: keySettings.fillLines === true, | 372 | fill: keySettings.fillLines === true, |
341 | show: this.chartType === 'line' ? keySettings.showLines !== false : keySettings.showLines === true | 373 | show: this.chartType === 'line' ? keySettings.showLines !== false : keySettings.showLines === true |
@@ -389,7 +421,7 @@ export default class TbFlot { | @@ -389,7 +421,7 @@ export default class TbFlot { | ||
389 | 421 | ||
390 | this.options.colors = colors; | 422 | this.options.colors = colors; |
391 | this.options.yaxes = angular.copy(this.yaxes); | 423 | this.options.yaxes = angular.copy(this.yaxes); |
392 | - if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') { | 424 | + if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { |
393 | if (this.chartType === 'bar') { | 425 | if (this.chartType === 'bar') { |
394 | this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; | 426 | this.options.series.bars.barWidth = this.subscription.timeWindow.interval * 0.6; |
395 | } | 427 | } |
@@ -432,6 +464,14 @@ export default class TbFlot { | @@ -432,6 +464,14 @@ export default class TbFlot { | ||
432 | yaxis.position = position; | 464 | yaxis.position = position; |
433 | 465 | ||
434 | yaxis.keysInfo = []; | 466 | yaxis.keysInfo = []; |
467 | + | ||
468 | + if (keySettings.axisTicksFormatter && keySettings.axisTicksFormatter.length) { | ||
469 | + try { | ||
470 | + yaxis.ticksFormatterFunction = new Function('value', keySettings.axisTicksFormatter); | ||
471 | + } catch (e) { | ||
472 | + yaxis.ticksFormatterFunction = this.yaxis.ticksFormatterFunction; | ||
473 | + } | ||
474 | + } | ||
435 | return yaxis; | 475 | return yaxis; |
436 | } | 476 | } |
437 | 477 | ||
@@ -442,7 +482,7 @@ export default class TbFlot { | @@ -442,7 +482,7 @@ export default class TbFlot { | ||
442 | } | 482 | } |
443 | if (this.subscription) { | 483 | if (this.subscription) { |
444 | if (!this.isMouseInteraction && this.ctx.plot) { | 484 | if (!this.isMouseInteraction && this.ctx.plot) { |
445 | - if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'stepped') { | 485 | + if (this.chartType === 'line' || this.chartType === 'bar' || this.chartType === 'state') { |
446 | 486 | ||
447 | var axisVisibilityChanged = false; | 487 | var axisVisibilityChanged = false; |
448 | if (this.yaxis) { | 488 | if (this.yaxis) { |
@@ -654,6 +694,11 @@ export default class TbFlot { | @@ -654,6 +694,11 @@ export default class TbFlot { | ||
654 | "type": "boolean", | 694 | "type": "boolean", |
655 | "default": false | 695 | "default": false |
656 | }, | 696 | }, |
697 | + "tooltipValueFormatter": { | ||
698 | + "title": "Tooltip value format function, f(value)", | ||
699 | + "type": "string", | ||
700 | + "default": "" | ||
701 | + }, | ||
657 | "grid": { | 702 | "grid": { |
658 | "title": "Grid settings", | 703 | "title": "Grid settings", |
659 | "type": "object", | 704 | "type": "object", |
@@ -739,6 +784,11 @@ export default class TbFlot { | @@ -739,6 +784,11 @@ export default class TbFlot { | ||
739 | "title": "Ticks color", | 784 | "title": "Ticks color", |
740 | "type": "string", | 785 | "type": "string", |
741 | "default": null | 786 | "default": null |
787 | + }, | ||
788 | + "ticksFormatter": { | ||
789 | + "title": "Ticks formatter function, f(value)", | ||
790 | + "type": "string", | ||
791 | + "default": "" | ||
742 | } | 792 | } |
743 | } | 793 | } |
744 | } | 794 | } |
@@ -757,6 +807,10 @@ export default class TbFlot { | @@ -757,6 +807,10 @@ export default class TbFlot { | ||
757 | "tooltipIndividual", | 807 | "tooltipIndividual", |
758 | "tooltipCumulative", | 808 | "tooltipCumulative", |
759 | { | 809 | { |
810 | + "key": "tooltipValueFormatter", | ||
811 | + "type": "javascript" | ||
812 | + }, | ||
813 | + { | ||
760 | "key": "grid", | 814 | "key": "grid", |
761 | "items": [ | 815 | "items": [ |
762 | { | 816 | { |
@@ -797,6 +851,10 @@ export default class TbFlot { | @@ -797,6 +851,10 @@ export default class TbFlot { | ||
797 | { | 851 | { |
798 | "key": "yaxis.color", | 852 | "key": "yaxis.color", |
799 | "type": "color" | 853 | "type": "color" |
854 | + }, | ||
855 | + { | ||
856 | + "key": "yaxis.ticksFormatter", | ||
857 | + "type": "javascript" | ||
800 | } | 858 | } |
801 | ] | 859 | ] |
802 | } | 860 | } |
@@ -830,6 +888,11 @@ export default class TbFlot { | @@ -830,6 +888,11 @@ export default class TbFlot { | ||
830 | "type": "boolean", | 888 | "type": "boolean", |
831 | "default": false | 889 | "default": false |
832 | }, | 890 | }, |
891 | + "tooltipValueFormatter": { | ||
892 | + "title": "Tooltip value format function, f(value)", | ||
893 | + "type": "string", | ||
894 | + "default": "" | ||
895 | + }, | ||
833 | "showSeparateAxis": { | 896 | "showSeparateAxis": { |
834 | "title": "Show separate axis", | 897 | "title": "Show separate axis", |
835 | "type": "boolean", | 898 | "type": "boolean", |
@@ -849,6 +912,11 @@ export default class TbFlot { | @@ -849,6 +912,11 @@ export default class TbFlot { | ||
849 | "title": "Axis position", | 912 | "title": "Axis position", |
850 | "type": "string", | 913 | "type": "string", |
851 | "default": "left" | 914 | "default": "left" |
915 | + }, | ||
916 | + "axisTicksFormatter": { | ||
917 | + "title": "Ticks formatter function, f(value)", | ||
918 | + "type": "string", | ||
919 | + "default": "" | ||
852 | } | 920 | } |
853 | }, | 921 | }, |
854 | "required": ["showLines", "fillLines", "showPoints"] | 922 | "required": ["showLines", "fillLines", "showPoints"] |
@@ -857,6 +925,10 @@ export default class TbFlot { | @@ -857,6 +925,10 @@ export default class TbFlot { | ||
857 | "showLines", | 925 | "showLines", |
858 | "fillLines", | 926 | "fillLines", |
859 | "showPoints", | 927 | "showPoints", |
928 | + { | ||
929 | + "key": "tooltipValueFormatter", | ||
930 | + "type": "javascript" | ||
931 | + }, | ||
860 | "showSeparateAxis", | 932 | "showSeparateAxis", |
861 | "axisTitle", | 933 | "axisTitle", |
862 | "axisTickDecimals", | 934 | "axisTickDecimals", |
@@ -874,8 +946,11 @@ export default class TbFlot { | @@ -874,8 +946,11 @@ export default class TbFlot { | ||
874 | "label": "Right" | 946 | "label": "Right" |
875 | } | 947 | } |
876 | ] | 948 | ] |
949 | + }, | ||
950 | + { | ||
951 | + "key": "axisTicksFormatter", | ||
952 | + "type": "javascript" | ||
877 | } | 953 | } |
878 | - | ||
879 | ] | 954 | ] |
880 | } | 955 | } |
881 | } | 956 | } |
@@ -1129,6 +1204,7 @@ export default class TbFlot { | @@ -1129,6 +1204,7 @@ export default class TbFlot { | ||
1129 | label: series.dataKey.label, | 1204 | label: series.dataKey.label, |
1130 | units: series.dataKey.units, | 1205 | units: series.dataKey.units, |
1131 | decimals: series.dataKey.decimals, | 1206 | decimals: series.dataKey.decimals, |
1207 | + tooltipValueFormatFunction: series.dataKey.tooltipValueFormatFunction, | ||
1132 | time: pointTime, | 1208 | time: pointTime, |
1133 | distance: hoverDistance, | 1209 | distance: hoverDistance, |
1134 | index: i | 1210 | index: i |
@@ -130,15 +130,22 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) { | @@ -130,15 +130,22 @@ function LedIndicatorController($element, $scope, $timeout, utils, types) { | ||
130 | } | 130 | } |
131 | } | 131 | } |
132 | 132 | ||
133 | - vm.checkStatusMethod = 'checkStatus'; | ||
134 | - if (vm.ctx.settings.checkStatusMethod && vm.ctx.settings.checkStatusMethod.length) { | ||
135 | - vm.checkStatusMethod = vm.ctx.settings.checkStatusMethod; | 133 | + vm.performCheckStatus = vm.ctx.settings.performCheckStatus != false; |
134 | + if (vm.performCheckStatus) { | ||
135 | + vm.checkStatusMethod = 'checkStatus'; | ||
136 | + if (vm.ctx.settings.checkStatusMethod && vm.ctx.settings.checkStatusMethod.length) { | ||
137 | + vm.checkStatusMethod = vm.ctx.settings.checkStatusMethod; | ||
138 | + } | ||
136 | } | 139 | } |
137 | if (!rpcEnabled) { | 140 | if (!rpcEnabled) { |
138 | onError('Target device is not set!'); | 141 | onError('Target device is not set!'); |
139 | } else { | 142 | } else { |
140 | if (!vm.isSimulated) { | 143 | if (!vm.isSimulated) { |
141 | - rpcCheckStatus(); | 144 | + if (vm.performCheckStatus) { |
145 | + rpcCheckStatus(); | ||
146 | + } else { | ||
147 | + subscribeForValue(); | ||
148 | + } | ||
142 | } | 149 | } |
143 | } | 150 | } |
144 | } | 151 | } |