Commit 7cfa352a60dc3323c4987df6656a4cc0deeef3c6
Merge branch 'vvlladd28-feature/timeWindow/quick-interval'
Showing
18 changed files
with
772 additions
and
190 deletions
... | ... | @@ -15,7 +15,12 @@ |
15 | 15 | /// |
16 | 16 | |
17 | 17 | import { SubscriptionData, SubscriptionDataHolder } from '@app/shared/models/telemetry/telemetry.models'; |
18 | -import { AggregationType } from '@shared/models/time/time.models'; | |
18 | +import { | |
19 | + AggregationType, | |
20 | + calculateIntervalEndTime, | |
21 | + calculateIntervalStartTime, getCurrentTime, | |
22 | + QuickTimeInterval, SubscriptionTimewindow | |
23 | +} from '@shared/models/time/time.models'; | |
19 | 24 | import { UtilsService } from '@core/services/utils.service'; |
20 | 25 | import { deepClone } from '@core/utils'; |
21 | 26 | import Timeout = NodeJS.Timeout; |
... | ... | @@ -73,33 +78,29 @@ export class DataAggregator { |
73 | 78 | private resetPending = false; |
74 | 79 | private updatedData = false; |
75 | 80 | |
76 | - private noAggregation = this.aggregationType === AggregationType.NONE; | |
77 | - private aggregationTimeout = Math.max(this.interval, 1000); | |
81 | + private noAggregation = this.subsTw.aggregation.type === AggregationType.NONE; | |
82 | + private aggregationTimeout = Math.max(this.subsTw.aggregation.interval, 1000); | |
78 | 83 | private readonly aggFunction: AggFunction; |
79 | 84 | |
80 | 85 | private intervalTimeoutHandle: Timeout; |
81 | 86 | private intervalScheduledTime: number; |
82 | 87 | |
88 | + private startTs = this.subsTw.startTs + this.subsTw.tsOffset; | |
83 | 89 | private endTs: number; |
84 | 90 | private elapsed: number; |
85 | 91 | |
86 | 92 | constructor(private onDataCb: onAggregatedData, |
87 | 93 | private tsKeyNames: string[], |
88 | - private startTs: number, | |
89 | - private limit: number, | |
90 | - private aggregationType: AggregationType, | |
91 | - private timeWindow: number, | |
92 | - private interval: number, | |
93 | - private stateData: boolean, | |
94 | + private subsTw: SubscriptionTimewindow, | |
94 | 95 | private utils: UtilsService, |
95 | 96 | private ignoreDataUpdateOnIntervalTick: boolean) { |
96 | 97 | this.tsKeyNames.forEach((key) => { |
97 | 98 | this.dataBuffer[key] = []; |
98 | 99 | }); |
99 | - if (this.stateData) { | |
100 | + if (this.subsTw.aggregation.stateData) { | |
100 | 101 | this.lastPrevKvPairData = {}; |
101 | 102 | } |
102 | - switch (this.aggregationType) { | |
103 | + switch (this.subsTw.aggregation.type) { | |
103 | 104 | case AggregationType.MIN: |
104 | 105 | this.aggFunction = min; |
105 | 106 | break; |
... | ... | @@ -129,18 +130,21 @@ export class DataAggregator { |
129 | 130 | return prevOnDataCb; |
130 | 131 | } |
131 | 132 | |
132 | - public reset(startTs: number, timeWindow: number, interval: number) { | |
133 | + public reset(subsTw: SubscriptionTimewindow) { | |
133 | 134 | if (this.intervalTimeoutHandle) { |
134 | 135 | clearTimeout(this.intervalTimeoutHandle); |
135 | 136 | this.intervalTimeoutHandle = null; |
136 | 137 | } |
138 | + this.subsTw = subsTw; | |
137 | 139 | this.intervalScheduledTime = this.utils.currentPerfTime(); |
138 | - this.startTs = startTs; | |
139 | - this.timeWindow = timeWindow; | |
140 | - this.interval = interval; | |
141 | - this.endTs = this.startTs + this.timeWindow; | |
140 | + this.startTs = this.subsTw.startTs + this.subsTw.tsOffset; | |
141 | + if (this.subsTw.quickInterval) { | |
142 | + this.endTs = calculateIntervalEndTime(this.subsTw.quickInterval, null, this.subsTw.timezone) + this.subsTw.tsOffset; | |
143 | + } else { | |
144 | + this.endTs = this.startTs + this.subsTw.aggregation.timeWindow; | |
145 | + } | |
142 | 146 | this.elapsed = 0; |
143 | - this.aggregationTimeout = Math.max(this.interval, 1000); | |
147 | + this.aggregationTimeout = Math.max(this.subsTw.aggregation.interval, 1000); | |
144 | 148 | this.resetPending = true; |
145 | 149 | this.updatedData = false; |
146 | 150 | this.intervalTimeoutHandle = setTimeout(this.onInterval.bind(this), this.aggregationTimeout); |
... | ... | @@ -161,7 +165,11 @@ export class DataAggregator { |
161 | 165 | if (!this.dataReceived) { |
162 | 166 | this.elapsed = 0; |
163 | 167 | this.dataReceived = true; |
164 | - this.endTs = this.startTs + this.timeWindow; | |
168 | + if (this.subsTw.quickInterval) { | |
169 | + this.endTs = calculateIntervalEndTime(this.subsTw.quickInterval, null, this.subsTw.timezone) + this.subsTw.tsOffset; | |
170 | + } else { | |
171 | + this.endTs = this.startTs + this.subsTw.aggregation.timeWindow; | |
172 | + } | |
165 | 173 | } |
166 | 174 | if (this.resetPending) { |
167 | 175 | this.resetPending = false; |
... | ... | @@ -195,12 +203,19 @@ export class DataAggregator { |
195 | 203 | this.intervalTimeoutHandle = null; |
196 | 204 | } |
197 | 205 | if (!history) { |
198 | - const delta = Math.floor(this.elapsed / this.interval); | |
206 | + const delta = Math.floor(this.elapsed / this.subsTw.aggregation.interval); | |
199 | 207 | if (delta || !this.data) { |
200 | - this.startTs += delta * this.interval; | |
201 | - this.endTs += delta * this.interval; | |
208 | + const tickTs = delta * this.subsTw.aggregation.interval; | |
209 | + if (this.subsTw.quickInterval) { | |
210 | + const currentDate = getCurrentTime(this.subsTw.timezone); | |
211 | + this.startTs = calculateIntervalStartTime(this.subsTw.quickInterval, currentDate) + this.subsTw.tsOffset; | |
212 | + this.endTs = calculateIntervalEndTime(this.subsTw.quickInterval, currentDate) + this.subsTw.tsOffset; | |
213 | + } else { | |
214 | + this.startTs += tickTs; | |
215 | + this.endTs += tickTs; | |
216 | + } | |
202 | 217 | this.data = this.updateData(); |
203 | - this.elapsed = this.elapsed - delta * this.interval; | |
218 | + this.elapsed = this.elapsed - delta * this.subsTw.aggregation.interval; | |
204 | 219 | } |
205 | 220 | } else { |
206 | 221 | this.data = this.updateData(); |
... | ... | @@ -223,7 +238,7 @@ export class DataAggregator { |
223 | 238 | let keyData = this.dataBuffer[key]; |
224 | 239 | aggKeyData.forEach((aggData, aggTimestamp) => { |
225 | 240 | if (aggTimestamp <= this.startTs) { |
226 | - if (this.stateData && | |
241 | + if (this.subsTw.aggregation.stateData && | |
227 | 242 | (!this.lastPrevKvPairData[key] || this.lastPrevKvPairData[key][0] < aggTimestamp)) { |
228 | 243 | this.lastPrevKvPairData[key] = [aggTimestamp, aggData.aggValue]; |
229 | 244 | } |
... | ... | @@ -235,11 +250,11 @@ export class DataAggregator { |
235 | 250 | } |
236 | 251 | }); |
237 | 252 | keyData.sort((set1, set2) => set1[0] - set2[0]); |
238 | - if (this.stateData) { | |
253 | + if (this.subsTw.aggregation.stateData) { | |
239 | 254 | this.updateStateBounds(keyData, deepClone(this.lastPrevKvPairData[key])); |
240 | 255 | } |
241 | - if (keyData.length > this.limit) { | |
242 | - keyData = keyData.slice(keyData.length - this.limit); | |
256 | + if (keyData.length > this.subsTw.aggregation.limit) { | |
257 | + keyData = keyData.slice(keyData.length - this.subsTw.aggregation.limit); | |
243 | 258 | } |
244 | 259 | this.dataBuffer[key] = keyData; |
245 | 260 | } |
... | ... | @@ -275,7 +290,7 @@ export class DataAggregator { |
275 | 290 | } |
276 | 291 | |
277 | 292 | private processAggregatedData(data: SubscriptionData): AggregationMap { |
278 | - const isCount = this.aggregationType === AggregationType.COUNT; | |
293 | + const isCount = this.subsTw.aggregation.type === AggregationType.COUNT; | |
279 | 294 | const aggregationMap: AggregationMap = {}; |
280 | 295 | for (const key of Object.keys(data)) { |
281 | 296 | let aggKeyData = aggregationMap[key]; |
... | ... | @@ -300,7 +315,7 @@ export class DataAggregator { |
300 | 315 | } |
301 | 316 | |
302 | 317 | private updateAggregatedData(data: SubscriptionData) { |
303 | - const isCount = this.aggregationType === AggregationType.COUNT; | |
318 | + const isCount = this.subsTw.aggregation.type === AggregationType.COUNT; | |
304 | 319 | for (const key of Object.keys(data)) { |
305 | 320 | let aggKeyData = this.aggregationMap[key]; |
306 | 321 | if (!aggKeyData) { |
... | ... | @@ -312,7 +327,8 @@ export class DataAggregator { |
312 | 327 | const timestamp = kvPair[0]; |
313 | 328 | const value = this.convertValue(kvPair[1]); |
314 | 329 | const aggTimestamp = this.noAggregation ? timestamp : (this.startTs + |
315 | - Math.floor((timestamp - this.startTs) / this.interval) * this.interval + this.interval / 2); | |
330 | + Math.floor((timestamp - this.startTs) / this.subsTw.aggregation.interval) * | |
331 | + this.subsTw.aggregation.interval + this.subsTw.aggregation.interval / 2); | |
316 | 332 | let aggData = aggKeyData.get(aggTimestamp); |
317 | 333 | if (!aggData) { |
318 | 334 | aggData = { | ... | ... |
... | ... | @@ -237,7 +237,7 @@ export class EntityDataSubscription { |
237 | 237 | }; |
238 | 238 | |
239 | 239 | if (this.entityDataSubscriptionOptions.isPaginatedDataSubscription) { |
240 | - this.prepareSubscriptionCommands(this.dataCommand); | |
240 | + this.prepareSubscriptionCommands(); | |
241 | 241 | } |
242 | 242 | |
243 | 243 | this.subscriber.subscriptionCommands.push(this.dataCommand); |
... | ... | @@ -256,8 +256,8 @@ export class EntityDataSubscription { |
256 | 256 | if (this.started) { |
257 | 257 | const targetCommand = this.entityDataSubscriptionOptions.isPaginatedDataSubscription ? this.dataCommand : this.subsCommand; |
258 | 258 | if (this.entityDataSubscriptionOptions.type === widgetType.timeseries && |
259 | - !this.history && this.tsFields.length) { | |
260 | - const newSubsTw: SubscriptionTimewindow = this.listener.updateRealtimeSubscription(); | |
259 | + !this.history && this.tsFields.length) { | |
260 | + const newSubsTw = this.listener.updateRealtimeSubscription(); | |
261 | 261 | this.subsTw = newSubsTw; |
262 | 262 | targetCommand.tsCmd.startTs = this.subsTw.startTs; |
263 | 263 | targetCommand.tsCmd.timeWindow = this.subsTw.aggregation.timeWindow; |
... | ... | @@ -266,9 +266,10 @@ export class EntityDataSubscription { |
266 | 266 | targetCommand.tsCmd.agg = this.subsTw.aggregation.type; |
267 | 267 | targetCommand.tsCmd.fetchLatestPreviousPoint = this.subsTw.aggregation.stateData; |
268 | 268 | this.dataAggregators.forEach((dataAggregator) => { |
269 | - dataAggregator.reset(newSubsTw.startTs, newSubsTw.aggregation.timeWindow, newSubsTw.aggregation.interval); | |
269 | + dataAggregator.reset(newSubsTw); | |
270 | 270 | }); |
271 | 271 | } |
272 | + this.subscriber.setTsOffset(this.subsTw.tsOffset); | |
272 | 273 | targetCommand.query = this.dataCommand.query; |
273 | 274 | this.subscriber.subscriptionCommands = [targetCommand]; |
274 | 275 | } else { |
... | ... | @@ -393,7 +394,7 @@ export class EntityDataSubscription { |
393 | 394 | if (this.datasourceType === DatasourceType.entity) { |
394 | 395 | this.subsCommand = new EntityDataCmd(); |
395 | 396 | this.subsCommand.cmdId = this.dataCommand.cmdId; |
396 | - this.prepareSubscriptionCommands(this.subsCommand); | |
397 | + this.prepareSubscriptionCommands(); | |
397 | 398 | if (!this.subsCommand.isEmpty()) { |
398 | 399 | this.subscriber.subscriptionCommands = [this.subsCommand]; |
399 | 400 | this.subscriber.update(); |
... | ... | @@ -404,11 +405,11 @@ export class EntityDataSubscription { |
404 | 405 | this.started = true; |
405 | 406 | } |
406 | 407 | |
407 | - private prepareSubscriptionCommands(cmd: EntityDataCmd) { | |
408 | + private prepareSubscriptionCommands() { | |
408 | 409 | if (this.entityDataSubscriptionOptions.type === widgetType.timeseries) { |
409 | 410 | if (this.tsFields.length > 0) { |
410 | 411 | if (this.history) { |
411 | - cmd.historyCmd = { | |
412 | + this.subsCommand.historyCmd = { | |
412 | 413 | keys: this.tsFields.map(key => key.key), |
413 | 414 | startTs: this.subsTw.fixedWindow.startTimeMs, |
414 | 415 | endTs: this.subsTw.fixedWindow.endTimeMs, |
... | ... | @@ -418,7 +419,7 @@ export class EntityDataSubscription { |
418 | 419 | fetchLatestPreviousPoint: this.subsTw.aggregation.stateData |
419 | 420 | }; |
420 | 421 | } else { |
421 | - cmd.tsCmd = { | |
422 | + this.subsCommand.tsCmd = { | |
422 | 423 | keys: this.tsFields.map(key => key.key), |
423 | 424 | startTs: this.subsTw.startTs, |
424 | 425 | timeWindow: this.subsTw.aggregation.timeWindow, |
... | ... | @@ -429,9 +430,10 @@ export class EntityDataSubscription { |
429 | 430 | }; |
430 | 431 | } |
431 | 432 | } |
433 | + this.subscriber.setTsOffset(this.subsTw.tsOffset); | |
432 | 434 | } else if (this.entityDataSubscriptionOptions.type === widgetType.latest) { |
433 | 435 | if (this.latestValues.length > 0) { |
434 | - cmd.latestCmd = { | |
436 | + this.subsCommand.latestCmd = { | |
435 | 437 | keys: this.latestValues |
436 | 438 | }; |
437 | 439 | } |
... | ... | @@ -745,12 +747,7 @@ export class EntityDataSubscription { |
745 | 747 | this.onData(data, dataKeyType, dataIndex, detectChanges, dataUpdatedCb); |
746 | 748 | }, |
747 | 749 | tsKeyNames, |
748 | - subsTw.startTs, | |
749 | - subsTw.aggregation.limit, | |
750 | - subsTw.aggregation.type, | |
751 | - subsTw.aggregation.timeWindow, | |
752 | - subsTw.aggregation.interval, | |
753 | - subsTw.aggregation.stateData, | |
750 | + subsTw, | |
754 | 751 | this.utils, |
755 | 752 | this.entityDataSubscriptionOptions.ignoreDataUpdateOnIntervalTick |
756 | 753 | ); |
... | ... | @@ -827,7 +824,8 @@ export class EntityDataSubscription { |
827 | 824 | startTime = dataKey.lastUpdateTime + this.frequency; |
828 | 825 | endTime = dataKey.lastUpdateTime + deltaElapsed; |
829 | 826 | } else { |
830 | - startTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.startTs; | |
827 | + startTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.startTs + | |
828 | + this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; | |
831 | 829 | endTime = startTime + this.entityDataSubscriptionOptions.subscriptionTimewindow.realtimeWindowMs + this.frequency; |
832 | 830 | if (this.entityDataSubscriptionOptions.subscriptionTimewindow.aggregation.type === AggregationType.NONE) { |
833 | 831 | const time = endTime - this.frequency * this.entityDataSubscriptionOptions.subscriptionTimewindow.aggregation.limit; |
... | ... | @@ -835,8 +833,10 @@ export class EntityDataSubscription { |
835 | 833 | } |
836 | 834 | } |
837 | 835 | } else { |
838 | - startTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.startTimeMs; | |
839 | - endTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.endTimeMs; | |
836 | + startTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.startTimeMs + | |
837 | + this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; | |
838 | + endTime = this.entityDataSubscriptionOptions.subscriptionTimewindow.fixedWindow.endTimeMs + | |
839 | + this.entityDataSubscriptionOptions.subscriptionTimewindow.tsOffset; | |
840 | 840 | } |
841 | 841 | } |
842 | 842 | generatedData.data[`${dataKey.name}_${dataKey.index}`] = this.generateSeries(dataKey, index, startTime, endTime); | ... | ... |
... | ... | @@ -37,8 +37,10 @@ import { |
37 | 37 | } from '@app/shared/models/widget.models'; |
38 | 38 | import { HttpErrorResponse } from '@angular/common/http'; |
39 | 39 | import { |
40 | + calculateIntervalEndTime, | |
41 | + calculateIntervalStartTime, | |
40 | 42 | createSubscriptionTimewindow, |
41 | - createTimewindowForComparison, | |
43 | + createTimewindowForComparison, getCurrentTime, | |
42 | 44 | SubscriptionTimewindow, |
43 | 45 | Timewindow, |
44 | 46 | toHistoryTimewindow, |
... | ... | @@ -837,9 +839,11 @@ export class WidgetSubscription implements IWidgetSubscription { |
837 | 839 | if (this.alarmDataListener) { |
838 | 840 | this.ctx.alarmDataService.stopSubscription(this.alarmDataListener); |
839 | 841 | } |
842 | + | |
840 | 843 | if (this.timeWindowConfig) { |
841 | 844 | this.updateRealtimeSubscription(); |
842 | 845 | } |
846 | + | |
843 | 847 | this.alarmDataListener = { |
844 | 848 | subscriptionTimewindow: this.subscriptionTimewindow, |
845 | 849 | alarmSource: this.alarmSource, |
... | ... | @@ -1080,12 +1084,21 @@ export class WidgetSubscription implements IWidgetSubscription { |
1080 | 1084 | |
1081 | 1085 | private updateTimewindow() { |
1082 | 1086 | this.timeWindow.interval = this.subscriptionTimewindow.aggregation.interval || 1000; |
1087 | + this.timeWindow.timezone = this.subscriptionTimewindow.timezone; | |
1083 | 1088 | if (this.subscriptionTimewindow.realtimeWindowMs) { |
1084 | - this.timeWindow.maxTime = moment().valueOf() + this.timeWindow.stDiff; | |
1085 | - this.timeWindow.minTime = this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs; | |
1089 | + if (this.subscriptionTimewindow.quickInterval) { | |
1090 | + const currentDate = getCurrentTime(this.subscriptionTimewindow.timezone); | |
1091 | + this.timeWindow.maxTime = calculateIntervalEndTime( | |
1092 | + this.subscriptionTimewindow.quickInterval, currentDate) + this.subscriptionTimewindow.tsOffset; | |
1093 | + this.timeWindow.minTime = calculateIntervalStartTime( | |
1094 | + this.subscriptionTimewindow.quickInterval, currentDate) + this.subscriptionTimewindow.tsOffset; | |
1095 | + } else { | |
1096 | + this.timeWindow.maxTime = moment().valueOf() + this.subscriptionTimewindow.tsOffset + this.timeWindow.stDiff; | |
1097 | + this.timeWindow.minTime = this.timeWindow.maxTime - this.subscriptionTimewindow.realtimeWindowMs; | |
1098 | + } | |
1086 | 1099 | } else if (this.subscriptionTimewindow.fixedWindow) { |
1087 | - this.timeWindow.maxTime = this.subscriptionTimewindow.fixedWindow.endTimeMs; | |
1088 | - this.timeWindow.minTime = this.subscriptionTimewindow.fixedWindow.startTimeMs; | |
1100 | + this.timeWindow.maxTime = this.subscriptionTimewindow.fixedWindow.endTimeMs + this.subscriptionTimewindow.tsOffset; | |
1101 | + this.timeWindow.minTime = this.subscriptionTimewindow.fixedWindow.startTimeMs + this.subscriptionTimewindow.tsOffset; | |
1089 | 1102 | } |
1090 | 1103 | } |
1091 | 1104 | |
... | ... | @@ -1103,12 +1116,13 @@ export class WidgetSubscription implements IWidgetSubscription { |
1103 | 1116 | |
1104 | 1117 | private updateComparisonTimewindow() { |
1105 | 1118 | this.comparisonTimeWindow.interval = this.timewindowForComparison.aggregation.interval || 1000; |
1119 | + this.comparisonTimeWindow.timezone = this.timewindowForComparison.timezone; | |
1106 | 1120 | if (this.timewindowForComparison.realtimeWindowMs) { |
1107 | 1121 | this.comparisonTimeWindow.maxTime = moment(this.timeWindow.maxTime).subtract(1, this.timeForComparison).valueOf(); |
1108 | - this.comparisonTimeWindow.minTime = this.comparisonTimeWindow.maxTime - this.timewindowForComparison.realtimeWindowMs; | |
1122 | + this.comparisonTimeWindow.minTime = moment(this.timeWindow.minTime).subtract(1, this.timeForComparison).valueOf(); | |
1109 | 1123 | } else if (this.timewindowForComparison.fixedWindow) { |
1110 | - this.comparisonTimeWindow.maxTime = this.timewindowForComparison.fixedWindow.endTimeMs; | |
1111 | - this.comparisonTimeWindow.minTime = this.timewindowForComparison.fixedWindow.startTimeMs; | |
1124 | + this.comparisonTimeWindow.maxTime = this.timewindowForComparison.fixedWindow.endTimeMs + this.timewindowForComparison.tsOffset; | |
1125 | + this.comparisonTimeWindow.minTime = this.timewindowForComparison.fixedWindow.startTimeMs + this.timewindowForComparison.tsOffset; | |
1112 | 1126 | } |
1113 | 1127 | } |
1114 | 1128 | |
... | ... | @@ -1335,7 +1349,7 @@ export class WidgetSubscription implements IWidgetSubscription { |
1335 | 1349 | this.onDataUpdated(); |
1336 | 1350 | } |
1337 | 1351 | |
1338 | - private alarmsUpdated(_updated: Array<AlarmData>, alarms: PageData<AlarmData>) { | |
1352 | + private alarmsUpdated(updated: Array<AlarmData>, alarms: PageData<AlarmData>) { | |
1339 | 1353 | this.alarmsLoaded(alarms, 0, 0); |
1340 | 1354 | } |
1341 | 1355 | ... | ... |
... | ... | @@ -95,6 +95,7 @@ |
95 | 95 | <tb-timewindow *ngIf="widget.hasTimewindow" |
96 | 96 | #timewindowComponent |
97 | 97 | aggregation="{{widget.hasAggregation}}" |
98 | + timezone="true" | |
98 | 99 | [isEdit]="isEdit" |
99 | 100 | [(ngModel)]="widgetComponent.widget.config.timewindow" |
100 | 101 | (ngModelChange)="widgetComponent.onTimewindowChanged($event)"> | ... | ... |
... | ... | @@ -55,7 +55,13 @@ import { EntityTypeTranslation } from '@shared/models/entity-type.models'; |
55 | 55 | import { DialogService } from '@core/services/dialog.service'; |
56 | 56 | import { AddEntityDialogComponent } from './add-entity-dialog.component'; |
57 | 57 | import { AddEntityDialogData, EntityAction } from '@home/models/entity/entity-component.models'; |
58 | -import { HistoryWindowType, Timewindow } from '@shared/models/time/time.models'; | |
58 | +import { | |
59 | + calculateIntervalEndTime, | |
60 | + calculateIntervalStartTime, | |
61 | + getCurrentTime, | |
62 | + HistoryWindowType, | |
63 | + Timewindow | |
64 | +} from '@shared/models/time/time.models'; | |
59 | 65 | import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; |
60 | 66 | import { TbAnchorComponent } from '@shared/components/tb-anchor.component'; |
61 | 67 | import { isDefined, isUndefined } from '@core/utils'; |
... | ... | @@ -296,6 +302,10 @@ export class EntitiesTableComponent extends PageComponent implements AfterViewIn |
296 | 302 | const currentTime = Date.now(); |
297 | 303 | timePageLink.startTime = currentTime - this.timewindow.history.timewindowMs; |
298 | 304 | timePageLink.endTime = currentTime; |
305 | + } else if (this.timewindow.history.historyType === HistoryWindowType.INTERVAL) { | |
306 | + const currentDate = getCurrentTime(); | |
307 | + timePageLink.startTime = calculateIntervalStartTime(this.timewindow.history.quickInterval, currentDate); | |
308 | + timePageLink.endTime = calculateIntervalEndTime(this.timewindow.history.quickInterval, currentDate); | |
299 | 309 | } else { |
300 | 310 | timePageLink.startTime = this.timewindow.history.fixedTimewindow.startTimeMs; |
301 | 311 | timePageLink.endTime = this.timewindow.history.fixedTimewindow.endTimeMs; | ... | ... |
... | ... | @@ -94,11 +94,10 @@ export class AlarmScheduleComponent implements ControlValueAccessor, Validator, |
94 | 94 | items: this.fb.array(Array.from({length: 7}, (value, i) => this.defaultItemsScheduler(i)), this.validateItems) |
95 | 95 | }); |
96 | 96 | this.alarmScheduleForm.get('type').valueChanges.subscribe((type) => { |
97 | - getDefaultTimezone().subscribe((defaultTimezone) => { | |
98 | - this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); | |
99 | - this.updateValidators(type, true); | |
100 | - this.alarmScheduleForm.updateValueAndValidity(); | |
101 | - }); | |
97 | + const defaultTimezone = getDefaultTimezone(); | |
98 | + this.alarmScheduleForm.reset({type, items: this.defaultItems, timezone: defaultTimezone}, {emitEvent: false}); | |
99 | + this.updateValidators(type, true); | |
100 | + this.alarmScheduleForm.updateValueAndValidity(); | |
102 | 101 | }); |
103 | 102 | this.alarmScheduleForm.valueChanges.subscribe(() => { |
104 | 103 | this.updateModel(); | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2021 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<section class="interval-section" fxLayout="row" fxFlex> | |
19 | + <mat-form-field fxFlex> | |
20 | + <mat-label translate>timewindow.interval</mat-label> | |
21 | + <mat-select [disabled]="disabled" [(ngModel)]="modelValue" (ngModelChange)="onIntervalChange()"> | |
22 | + <mat-option *ngFor="let interval of intervals" [value]="interval"> | |
23 | + {{ timeIntervalTranslationMap.get(interval) | translate}} | |
24 | + </mat-option> | |
25 | + </mat-select> | |
26 | + </mat-form-field> | |
27 | +</section> | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | + | |
17 | +:host { | |
18 | + min-width: 364px; | |
19 | +} | ... | ... |
1 | +/// | |
2 | +/// Copyright © 2016-2021 The Thingsboard Authors | |
3 | +/// | |
4 | +/// Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | +/// you may not use this file except in compliance with the License. | |
6 | +/// You may obtain a copy of the License at | |
7 | +/// | |
8 | +/// http://www.apache.org/licenses/LICENSE-2.0 | |
9 | +/// | |
10 | +/// Unless required by applicable law or agreed to in writing, software | |
11 | +/// distributed under the License is distributed on an "AS IS" BASIS, | |
12 | +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | +/// See the License for the specific language governing permissions and | |
14 | +/// limitations under the License. | |
15 | +/// | |
16 | + | |
17 | +import { Component, forwardRef, Input, OnInit } from '@angular/core'; | |
18 | +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; | |
19 | +import { QuickTimeInterval, QuickTimeIntervalTranslationMap } from '@shared/models/time/time.models'; | |
20 | + | |
21 | +@Component({ | |
22 | + selector: 'tb-quick-time-interval', | |
23 | + templateUrl: './quick-time-interval.component.html', | |
24 | + styleUrls: ['./quick-time-interval.component.scss'], | |
25 | + providers: [ | |
26 | + { | |
27 | + provide: NG_VALUE_ACCESSOR, | |
28 | + useExisting: forwardRef(() => QuickTimeIntervalComponent), | |
29 | + multi: true | |
30 | + } | |
31 | + ] | |
32 | +}) | |
33 | +export class QuickTimeIntervalComponent implements OnInit, ControlValueAccessor { | |
34 | + | |
35 | + private allIntervals = Object.values(QuickTimeInterval); | |
36 | + | |
37 | + modelValue: QuickTimeInterval; | |
38 | + timeIntervalTranslationMap = QuickTimeIntervalTranslationMap; | |
39 | + | |
40 | + rendered = false; | |
41 | + | |
42 | + @Input() disabled: boolean; | |
43 | + | |
44 | + @Input() onlyCurrentInterval = false; | |
45 | + | |
46 | + private propagateChange = (_: any) => {}; | |
47 | + | |
48 | + constructor() { | |
49 | + } | |
50 | + | |
51 | + get intervals() { | |
52 | + if (this.onlyCurrentInterval) { | |
53 | + return this.allIntervals.filter(interval => interval.startsWith('CURRENT_')); | |
54 | + } | |
55 | + return this.allIntervals; | |
56 | + } | |
57 | + | |
58 | + ngOnInit(): void { | |
59 | + } | |
60 | + | |
61 | + registerOnChange(fn: any): void { | |
62 | + this.propagateChange = fn; | |
63 | + } | |
64 | + | |
65 | + registerOnTouched(fn: any): void { | |
66 | + } | |
67 | + | |
68 | + setDisabledState(isDisabled: boolean): void { | |
69 | + this.disabled = isDisabled; | |
70 | + } | |
71 | + | |
72 | + writeValue(interval: QuickTimeInterval): void { | |
73 | + this.modelValue = interval; | |
74 | + } | |
75 | + | |
76 | + onIntervalChange() { | |
77 | + this.propagateChange(this.modelValue); | |
78 | + } | |
79 | +} | ... | ... |
... | ... | @@ -21,16 +21,43 @@ |
21 | 21 | <mat-tab-group dynamicHeight [ngClass]="{'tb-headless': historyOnly}" |
22 | 22 | (selectedIndexChange)="timewindowForm.markAsDirty()" [(selectedIndex)]="timewindow.selectedTab"> |
23 | 23 | <mat-tab label="{{ 'timewindow.realtime' | translate }}"> |
24 | - <div formGroupName="realtime" class="mat-content mat-padding" fxLayout="column"> | |
25 | - <tb-timeinterval | |
26 | - [(hideFlag)]="timewindow.hideInterval" | |
27 | - (hideFlagChange)="onHideIntervalChanged()" | |
28 | - [isEdit]="isEdit" | |
29 | - formControlName="timewindowMs" | |
30 | - predefinedName="timewindow.last" | |
31 | - [required]="timewindow.selectedTab === timewindowTypes.REALTIME" | |
32 | - style="padding-top: 8px;"></tb-timeinterval> | |
33 | - </div> | |
24 | + <section fxLayout="row"> | |
25 | + <section *ngIf="isEdit" fxLayout="column" style="padding-top: 8px; padding-left: 16px;"> | |
26 | + <label class="tb-small hide-label" translate>timewindow.hide</label> | |
27 | + <mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="timewindow.hideInterval" | |
28 | + (ngModelChange)="onHideIntervalChanged()"></mat-checkbox> | |
29 | + </section> | |
30 | + <section fxLayout="column" fxFlex [fxShow]="isEdit || !timewindow.hideInterval"> | |
31 | + <div formGroupName="realtime" class="mat-content mat-padding" style="padding-top: 8px;"> | |
32 | + <mat-radio-group formControlName="realtimeType"> | |
33 | + <mat-radio-button [value]="realtimeTypes.LAST_INTERVAL" color="primary"> | |
34 | + <section fxLayout="column"> | |
35 | + <span translate>timewindow.last</span> | |
36 | + <tb-timeinterval | |
37 | + formControlName="timewindowMs" | |
38 | + predefinedName="timewindow.last" | |
39 | + [fxShow]="timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL" | |
40 | + [required]="timewindow.selectedTab === timewindowTypes.REALTIME && | |
41 | + timewindowForm.get('realtime.realtimeType').value === realtimeTypes.LAST_INTERVAL" | |
42 | + style="padding-top: 8px;"></tb-timeinterval> | |
43 | + </section> | |
44 | + </mat-radio-button> | |
45 | + <mat-radio-button [value]="realtimeTypes.INTERVAL" color="primary"> | |
46 | + <section fxLayout="column"> | |
47 | + <span translate>timewindow.interval</span> | |
48 | + <tb-quick-time-interval | |
49 | + formControlName="quickInterval" | |
50 | + onlyCurrentInterval="true" | |
51 | + [fxShow]="timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL" | |
52 | + [required]="timewindow.selectedTab === timewindowTypes.REALTIME && | |
53 | + timewindowForm.get('realtime.realtimeType').value === realtimeTypes.INTERVAL" | |
54 | + style="padding-top: 8px; min-width: 364px"></tb-quick-time-interval> | |
55 | + </section> | |
56 | + </mat-radio-button> | |
57 | + </mat-radio-group> | |
58 | + </div> | |
59 | + </section> | |
60 | + </section> | |
34 | 61 | </mat-tab> |
35 | 62 | <mat-tab label="{{ 'timewindow.history' | translate }}"> |
36 | 63 | <section fxLayout="row"> |
... | ... | @@ -65,6 +92,17 @@ |
65 | 92 | style="padding-top: 8px;"></tb-datetime-period> |
66 | 93 | </section> |
67 | 94 | </mat-radio-button> |
95 | + <mat-radio-button [value]="historyTypes.INTERVAL" color="primary"> | |
96 | + <section fxLayout="column"> | |
97 | + <span translate>timewindow.interval</span> | |
98 | + <tb-quick-time-interval | |
99 | + formControlName="quickInterval" | |
100 | + [fxShow]="timewindowForm.get('history.historyType').value === historyTypes.INTERVAL" | |
101 | + [required]="timewindow.selectedTab === timewindowTypes.HISTORY && | |
102 | + timewindowForm.get('history.historyType').value === historyTypes.INTERVAL" | |
103 | + style="padding-top: 8px; min-width: 364px"></tb-quick-time-interval> | |
104 | + </section> | |
105 | + </mat-radio-button> | |
68 | 106 | </mat-radio-group> |
69 | 107 | </div> |
70 | 108 | </section> |
... | ... | @@ -139,6 +177,16 @@ |
139 | 177 | predefinedName="aggregation.group-interval"> |
140 | 178 | </tb-timeinterval> |
141 | 179 | </div> |
180 | + <div *ngIf="timezone" class="mat-content mat-padding" fxLayout="row"> | |
181 | + <section fxLayout="column" [fxShow]="isEdit"> | |
182 | + <label class="tb-small hide-label" translate>timewindow.hide</label> | |
183 | + <mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="timewindow.hideTimezone" | |
184 | + (ngModelChange)="onHideTimezoneChanged()"></mat-checkbox> | |
185 | + </section> | |
186 | + <tb-timezone-select fxFlex [fxShow]="isEdit || !timewindow.hideTimezone" | |
187 | + formControlName="timezone"> | |
188 | + </tb-timezone-select> | |
189 | + </div> | |
142 | 190 | <div fxLayout="row" class="tb-panel-actions" fxLayoutAlign="end center"> |
143 | 191 | <button type="button" |
144 | 192 | mat-button | ... | ... |
... | ... | @@ -20,6 +20,8 @@ import { |
20 | 20 | AggregationType, |
21 | 21 | DAY, |
22 | 22 | HistoryWindowType, |
23 | + quickTimeIntervalPeriod, | |
24 | + RealtimeWindowType, | |
23 | 25 | Timewindow, |
24 | 26 | TimewindowType |
25 | 27 | } from '@shared/models/time/time.models'; |
... | ... | @@ -36,6 +38,7 @@ export interface TimewindowPanelData { |
36 | 38 | historyOnly: boolean; |
37 | 39 | timewindow: Timewindow; |
38 | 40 | aggregation: boolean; |
41 | + timezone: boolean; | |
39 | 42 | isEdit: boolean; |
40 | 43 | } |
41 | 44 | |
... | ... | @@ -50,6 +53,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
50 | 53 | |
51 | 54 | aggregation = false; |
52 | 55 | |
56 | + timezone = false; | |
57 | + | |
53 | 58 | isEdit = false; |
54 | 59 | |
55 | 60 | timewindow: Timewindow; |
... | ... | @@ -60,6 +65,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
60 | 65 | |
61 | 66 | historyTypes = HistoryWindowType; |
62 | 67 | |
68 | + realtimeTypes = RealtimeWindowType; | |
69 | + | |
63 | 70 | timewindowTypes = TimewindowType; |
64 | 71 | |
65 | 72 | aggregationTypes = AggregationType; |
... | ... | @@ -78,6 +85,7 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
78 | 85 | this.historyOnly = data.historyOnly; |
79 | 86 | this.timewindow = data.timewindow; |
80 | 87 | this.aggregation = data.aggregation; |
88 | + this.timezone = data.timezone; | |
81 | 89 | this.isEdit = data.isEdit; |
82 | 90 | } |
83 | 91 | |
... | ... | @@ -85,10 +93,16 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
85 | 93 | const hideInterval = this.timewindow.hideInterval || false; |
86 | 94 | const hideAggregation = this.timewindow.hideAggregation || false; |
87 | 95 | const hideAggInterval = this.timewindow.hideAggInterval || false; |
96 | + const hideTimezone = this.timewindow.hideTimezone || false; | |
88 | 97 | |
89 | 98 | this.timewindowForm = this.fb.group({ |
90 | 99 | realtime: this.fb.group( |
91 | 100 | { |
101 | + realtimeType: this.fb.control({ | |
102 | + value: this.timewindow.realtime && typeof this.timewindow.realtime.realtimeType !== 'undefined' | |
103 | + ? this.timewindow.realtime.realtimeType : RealtimeWindowType.LAST_INTERVAL, | |
104 | + disabled: hideInterval | |
105 | + }), | |
92 | 106 | timewindowMs: [ |
93 | 107 | this.timewindow.realtime && typeof this.timewindow.realtime.timewindowMs !== 'undefined' |
94 | 108 | ? this.timewindow.realtime.timewindowMs : null |
... | ... | @@ -96,7 +110,12 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
96 | 110 | interval: [ |
97 | 111 | this.timewindow.realtime && typeof this.timewindow.realtime.interval !== 'undefined' |
98 | 112 | ? this.timewindow.realtime.interval : null |
99 | - ] | |
113 | + ], | |
114 | + quickInterval: this.fb.control({ | |
115 | + value: this.timewindow.realtime && typeof this.timewindow.realtime.quickInterval !== 'undefined' | |
116 | + ? this.timewindow.realtime.quickInterval : null, | |
117 | + disabled: hideInterval | |
118 | + }) | |
100 | 119 | } |
101 | 120 | ), |
102 | 121 | history: this.fb.group( |
... | ... | @@ -119,6 +138,11 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
119 | 138 | value: this.timewindow.history && typeof this.timewindow.history.fixedTimewindow !== 'undefined' |
120 | 139 | ? this.timewindow.history.fixedTimewindow : null, |
121 | 140 | disabled: hideInterval |
141 | + }), | |
142 | + quickInterval: this.fb.control({ | |
143 | + value: this.timewindow.history && typeof this.timewindow.history.quickInterval !== 'undefined' | |
144 | + ? this.timewindow.history.quickInterval : null, | |
145 | + disabled: hideInterval | |
122 | 146 | }) |
123 | 147 | } |
124 | 148 | ), |
... | ... | @@ -135,21 +159,29 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
135 | 159 | disabled: hideAggInterval |
136 | 160 | }, [Validators.min(this.minDatapointsLimit()), Validators.max(this.maxDatapointsLimit())]) |
137 | 161 | } |
138 | - ) | |
162 | + ), | |
163 | + timezone: this.fb.control({ | |
164 | + value: this.timewindow.timezone !== 'undefined' | |
165 | + ? this.timewindow.timezone : null, | |
166 | + disabled: hideTimezone | |
167 | + }) | |
139 | 168 | }); |
140 | 169 | } |
141 | 170 | |
142 | 171 | update() { |
143 | 172 | const timewindowFormValue = this.timewindowForm.getRawValue(); |
144 | 173 | this.timewindow.realtime = { |
174 | + realtimeType: timewindowFormValue.realtime.realtimeType, | |
145 | 175 | timewindowMs: timewindowFormValue.realtime.timewindowMs, |
176 | + quickInterval: timewindowFormValue.realtime.quickInterval, | |
146 | 177 | interval: timewindowFormValue.realtime.interval |
147 | 178 | }; |
148 | 179 | this.timewindow.history = { |
149 | 180 | historyType: timewindowFormValue.history.historyType, |
150 | 181 | timewindowMs: timewindowFormValue.history.timewindowMs, |
151 | 182 | interval: timewindowFormValue.history.interval, |
152 | - fixedTimewindow: timewindowFormValue.history.fixedTimewindow | |
183 | + fixedTimewindow: timewindowFormValue.history.fixedTimewindow, | |
184 | + quickInterval: timewindowFormValue.history.quickInterval, | |
153 | 185 | }; |
154 | 186 | if (this.aggregation) { |
155 | 187 | this.timewindow.aggregation = { |
... | ... | @@ -157,6 +189,9 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
157 | 189 | limit: timewindowFormValue.aggregation.limit |
158 | 190 | }; |
159 | 191 | } |
192 | + if (this.timezone) { | |
193 | + this.timewindow.timezone = timewindowFormValue.timezone; | |
194 | + } | |
160 | 195 | this.result = this.timewindow; |
161 | 196 | this.overlayRef.dispose(); |
162 | 197 | } |
... | ... | @@ -174,11 +209,23 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
174 | 209 | } |
175 | 210 | |
176 | 211 | minRealtimeAggInterval() { |
177 | - return this.timeService.minIntervalLimit(this.timewindowForm.get('realtime.timewindowMs').value); | |
212 | + return this.timeService.minIntervalLimit(this.currentRealtimeTimewindow()); | |
178 | 213 | } |
179 | 214 | |
180 | 215 | maxRealtimeAggInterval() { |
181 | - return this.timeService.maxIntervalLimit(this.timewindowForm.get('realtime.timewindowMs').value); | |
216 | + return this.timeService.maxIntervalLimit(this.currentRealtimeTimewindow()); | |
217 | + } | |
218 | + | |
219 | + currentRealtimeTimewindow(): number { | |
220 | + const timeWindowFormValue = this.timewindowForm.getRawValue(); | |
221 | + switch (timeWindowFormValue.realtime.realtimeType) { | |
222 | + case RealtimeWindowType.LAST_INTERVAL: | |
223 | + return timeWindowFormValue.realtime.timewindowMs; | |
224 | + case RealtimeWindowType.INTERVAL: | |
225 | + return quickTimeIntervalPeriod(timeWindowFormValue.realtime.quickInterval); | |
226 | + default: | |
227 | + return DAY; | |
228 | + } | |
182 | 229 | } |
183 | 230 | |
184 | 231 | minHistoryAggInterval() { |
... | ... | @@ -193,6 +240,8 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
193 | 240 | const timewindowFormValue = this.timewindowForm.getRawValue(); |
194 | 241 | if (timewindowFormValue.history.historyType === HistoryWindowType.LAST_INTERVAL) { |
195 | 242 | return timewindowFormValue.history.timewindowMs; |
243 | + } else if (timewindowFormValue.history.historyType === HistoryWindowType.INTERVAL) { | |
244 | + return quickTimeIntervalPeriod(timewindowFormValue.history.quickInterval); | |
196 | 245 | } else if (timewindowFormValue.history.fixedTimewindow) { |
197 | 246 | return timewindowFormValue.history.fixedTimewindow.endTimeMs - |
198 | 247 | timewindowFormValue.history.fixedTimewindow.startTimeMs; |
... | ... | @@ -206,10 +255,18 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
206 | 255 | this.timewindowForm.get('history.historyType').disable({emitEvent: false}); |
207 | 256 | this.timewindowForm.get('history.timewindowMs').disable({emitEvent: false}); |
208 | 257 | this.timewindowForm.get('history.fixedTimewindow').disable({emitEvent: false}); |
258 | + this.timewindowForm.get('history.quickInterval').disable({emitEvent: false}); | |
259 | + this.timewindowForm.get('realtime.realtimeType').disable({emitEvent: false}); | |
260 | + this.timewindowForm.get('realtime.timewindowMs').disable({emitEvent: false}); | |
261 | + this.timewindowForm.get('realtime.quickInterval').disable({emitEvent: false}); | |
209 | 262 | } else { |
210 | 263 | this.timewindowForm.get('history.historyType').enable({emitEvent: false}); |
211 | 264 | this.timewindowForm.get('history.timewindowMs').enable({emitEvent: false}); |
212 | 265 | this.timewindowForm.get('history.fixedTimewindow').enable({emitEvent: false}); |
266 | + this.timewindowForm.get('history.quickInterval').enable({emitEvent: false}); | |
267 | + this.timewindowForm.get('realtime.realtimeType').enable({emitEvent: false}); | |
268 | + this.timewindowForm.get('realtime.timewindowMs').enable({emitEvent: false}); | |
269 | + this.timewindowForm.get('realtime.quickInterval').enable({emitEvent: false}); | |
213 | 270 | } |
214 | 271 | this.timewindowForm.markAsDirty(); |
215 | 272 | } |
... | ... | @@ -232,4 +289,13 @@ export class TimewindowPanelComponent extends PageComponent implements OnInit { |
232 | 289 | this.timewindowForm.markAsDirty(); |
233 | 290 | } |
234 | 291 | |
292 | + onHideTimezoneChanged() { | |
293 | + if (this.timewindow.hideTimezone) { | |
294 | + this.timewindowForm.get('timezone').disable({emitEvent: false}); | |
295 | + } else { | |
296 | + this.timewindowForm.get('timezone').enable({emitEvent: false}); | |
297 | + } | |
298 | + this.timewindowForm.markAsDirty(); | |
299 | + } | |
300 | + | |
235 | 301 | } | ... | ... |
... | ... | @@ -33,6 +33,8 @@ import { |
33 | 33 | cloneSelectedTimewindow, |
34 | 34 | HistoryWindowType, |
35 | 35 | initModelFromDefaultTimewindow, |
36 | + QuickTimeIntervalTranslationMap, | |
37 | + RealtimeWindowType, | |
36 | 38 | Timewindow, |
37 | 39 | TimewindowType |
38 | 40 | } from '@shared/models/time/time.models'; |
... | ... | @@ -89,6 +91,17 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces |
89 | 91 | return this.aggregationValue; |
90 | 92 | } |
91 | 93 | |
94 | + timezoneValue = false; | |
95 | + | |
96 | + @Input() | |
97 | + set timezone(val) { | |
98 | + this.timezoneValue = coerceBooleanProperty(val); | |
99 | + } | |
100 | + | |
101 | + get timezone() { | |
102 | + return this.timezoneValue; | |
103 | + } | |
104 | + | |
92 | 105 | isToolbarValue = false; |
93 | 106 | |
94 | 107 | @Input() |
... | ... | @@ -169,7 +182,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces |
169 | 182 | }); |
170 | 183 | if (isGtXs) { |
171 | 184 | config.minWidth = '417px'; |
172 | - config.maxHeight = '440px'; | |
185 | + config.maxHeight = '500px'; | |
173 | 186 | const panelHeight = 375; |
174 | 187 | const panelWidth = 417; |
175 | 188 | const el = this.timewindowPanelOrigin.elementRef.nativeElement; |
... | ... | @@ -225,6 +238,7 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces |
225 | 238 | timewindow: deepClone(this.innerValue), |
226 | 239 | historyOnly: this.historyOnly, |
227 | 240 | aggregation: this.aggregation, |
241 | + timezone: this.timezone, | |
228 | 242 | isEdit: this.isEdit |
229 | 243 | } |
230 | 244 | ); |
... | ... | @@ -272,14 +286,20 @@ export class TimewindowComponent implements OnInit, OnDestroy, ControlValueAcces |
272 | 286 | |
273 | 287 | updateDisplayValue() { |
274 | 288 | if (this.innerValue.selectedTab === TimewindowType.REALTIME && !this.historyOnly) { |
275 | - this.innerValue.displayValue = this.translate.instant('timewindow.realtime') + ' - ' + | |
276 | - this.translate.instant('timewindow.last-prefix') + ' ' + | |
277 | - this.millisecondsToTimeStringPipe.transform(this.innerValue.realtime.timewindowMs); | |
289 | + this.innerValue.displayValue = this.translate.instant('timewindow.realtime') + ' - '; | |
290 | + if (this.innerValue.realtime.realtimeType === RealtimeWindowType.INTERVAL) { | |
291 | + this.innerValue.displayValue += this.translate.instant(QuickTimeIntervalTranslationMap.get(this.innerValue.realtime.quickInterval)); | |
292 | + } else { | |
293 | + this.innerValue.displayValue += this.translate.instant('timewindow.last-prefix') + ' ' + | |
294 | + this.millisecondsToTimeStringPipe.transform(this.innerValue.realtime.timewindowMs); | |
295 | + } | |
278 | 296 | } else { |
279 | 297 | this.innerValue.displayValue = !this.historyOnly ? (this.translate.instant('timewindow.history') + ' - ') : ''; |
280 | 298 | if (this.innerValue.history.historyType === HistoryWindowType.LAST_INTERVAL) { |
281 | 299 | this.innerValue.displayValue += this.translate.instant('timewindow.last-prefix') + ' ' + |
282 | 300 | this.millisecondsToTimeStringPipe.transform(this.innerValue.history.timewindowMs); |
301 | + } else if (this.innerValue.history.historyType === HistoryWindowType.INTERVAL) { | |
302 | + this.innerValue.displayValue += this.translate.instant(QuickTimeIntervalTranslationMap.get(this.innerValue.history.quickInterval)); | |
283 | 303 | } else { |
284 | 304 | const startString = this.datePipe.transform(this.innerValue.history.fixedTimewindow.startTimeMs, 'yyyy-MM-dd HH:mm:ss'); |
285 | 305 | const endString = this.datePipe.transform(this.innerValue.history.fixedTimewindow.endTimeMs, 'yyyy-MM-dd HH:mm:ss'); | ... | ... |
... | ... | @@ -16,7 +16,7 @@ |
16 | 16 | |
17 | 17 | import { AfterViewInit, Component, forwardRef, Input, NgZone, OnInit, ViewChild } from '@angular/core'; |
18 | 18 | import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; |
19 | -import { Observable } from 'rxjs'; | |
19 | +import { Observable, of } from 'rxjs'; | |
20 | 20 | import { map, mergeMap, share, tap } from 'rxjs/operators'; |
21 | 21 | import { Store } from '@ngrx/store'; |
22 | 22 | import { AppState } from '@app/core/core.state'; |
... | ... | @@ -43,10 +43,6 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af |
43 | 43 | |
44 | 44 | defaultTimezoneId: string = null; |
45 | 45 | |
46 | - timezones$ = getTimezones().pipe( | |
47 | - share() | |
48 | - ); | |
49 | - | |
50 | 46 | @Input() |
51 | 47 | set defaultTimezone(timezone: string) { |
52 | 48 | if (this.defaultTimezoneId !== timezone) { |
... | ... | @@ -138,23 +134,20 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af |
138 | 134 | |
139 | 135 | writeValue(value: string | null): void { |
140 | 136 | this.searchText = ''; |
141 | - getTimezoneInfo(value, this.defaultTimezoneId, this.userTimezoneByDefaultValue).subscribe( | |
142 | - (foundTimezone) => { | |
143 | - if (foundTimezone !== null) { | |
144 | - this.selectTimezoneFormGroup.get('timezone').patchValue(foundTimezone, {emitEvent: false}); | |
145 | - if (foundTimezone.id !== value) { | |
146 | - setTimeout(() => { | |
147 | - this.updateView(foundTimezone.id); | |
148 | - }, 0); | |
149 | - } else { | |
150 | - this.modelValue = value; | |
151 | - } | |
152 | - } else { | |
153 | - this.modelValue = null; | |
154 | - this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); | |
155 | - } | |
137 | + const foundTimezone = getTimezoneInfo(value, this.defaultTimezoneId, this.userTimezoneByDefaultValue); | |
138 | + if (foundTimezone !== null) { | |
139 | + this.selectTimezoneFormGroup.get('timezone').patchValue(foundTimezone, {emitEvent: false}); | |
140 | + if (foundTimezone.id !== value) { | |
141 | + setTimeout(() => { | |
142 | + this.updateView(foundTimezone.id); | |
143 | + }, 0); | |
144 | + } else { | |
145 | + this.modelValue = value; | |
156 | 146 | } |
157 | - ); | |
147 | + } else { | |
148 | + this.modelValue = null; | |
149 | + this.selectTimezoneFormGroup.get('timezone').patchValue('', {emitEvent: false}); | |
150 | + } | |
158 | 151 | this.dirty = true; |
159 | 152 | } |
160 | 153 | |
... | ... | @@ -170,14 +163,12 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af |
170 | 163 | this.ignoreClosePanel = false; |
171 | 164 | } else { |
172 | 165 | if (!this.modelValue && (this.defaultTimezoneId || this.userTimezoneByDefaultValue)) { |
173 | - getTimezoneInfo(this.defaultTimezoneId, this.defaultTimezoneId, this.userTimezoneByDefaultValue).subscribe( | |
174 | - (defaultTimezoneInfo) => { | |
175 | - if (defaultTimezoneInfo !== null) { | |
176 | - this.ngZone.run(() => { | |
177 | - this.selectTimezoneFormGroup.get('timezone').reset(defaultTimezoneInfo, {emitEvent: true}); | |
178 | - }); | |
179 | - } | |
180 | - }); | |
166 | + const defaultTimezoneInfo = getTimezoneInfo(this.defaultTimezoneId, this.defaultTimezoneId, this.userTimezoneByDefaultValue); | |
167 | + if (defaultTimezoneInfo !== null) { | |
168 | + this.ngZone.run(() => { | |
169 | + this.selectTimezoneFormGroup.get('timezone').reset(defaultTimezoneInfo, {emitEvent: true}); | |
170 | + }); | |
171 | + } | |
181 | 172 | } |
182 | 173 | } |
183 | 174 | } |
... | ... | @@ -196,12 +187,10 @@ export class TimezoneSelectComponent implements ControlValueAccessor, OnInit, Af |
196 | 187 | fetchTimezones(searchText?: string): Observable<Array<TimezoneInfo>> { |
197 | 188 | this.searchText = searchText; |
198 | 189 | if (searchText && searchText.length) { |
199 | - return getTimezones().pipe( | |
200 | - map((timezones) => timezones.filter((timezoneInfo) => | |
201 | - timezoneInfo.name.toLowerCase().includes(searchText.toLowerCase()))) | |
202 | - ); | |
190 | + return of(getTimezones().filter((timezoneInfo) => | |
191 | + timezoneInfo.name.toLowerCase().includes(searchText.toLowerCase()))); | |
203 | 192 | } |
204 | - return getTimezones(); | |
193 | + return of(getTimezones()); | |
205 | 194 | } |
206 | 195 | |
207 | 196 | clear() { | ... | ... |
... | ... | @@ -430,6 +430,38 @@ export class EntityDataUpdate extends DataUpdate<EntityData> { |
430 | 430 | constructor(msg: EntityDataUpdateMsg) { |
431 | 431 | super(msg); |
432 | 432 | } |
433 | + | |
434 | + public prepareData(tsOffset: number) { | |
435 | + if (this.data) { | |
436 | + this.processEntityData(this.data.data, tsOffset); | |
437 | + } | |
438 | + if (this.update) { | |
439 | + this.processEntityData(this.update, tsOffset); | |
440 | + } | |
441 | + } | |
442 | + | |
443 | + private processEntityData(data: Array<EntityData>, tsOffset: number) { | |
444 | + for (const entityData of data) { | |
445 | + if (entityData.timeseries) { | |
446 | + for (const key of Object.keys(entityData.timeseries)) { | |
447 | + const tsValues = entityData.timeseries[key]; | |
448 | + for (const tsValue of tsValues) { | |
449 | + tsValue.ts += tsOffset; | |
450 | + } | |
451 | + } | |
452 | + } | |
453 | + if (entityData.latest) { | |
454 | + for (const entityKeyType of Object.keys(entityData.latest)) { | |
455 | + const keyTypeValues = entityData.latest[entityKeyType]; | |
456 | + for (const key of Object.keys(keyTypeValues)) { | |
457 | + const tsValue = keyTypeValues[key]; | |
458 | + tsValue.ts += tsOffset; | |
459 | + } | |
460 | + } | |
461 | + } | |
462 | + } | |
463 | + } | |
464 | + | |
433 | 465 | } |
434 | 466 | |
435 | 467 | export class AlarmDataUpdate extends DataUpdate<AlarmData> { |
... | ... | @@ -468,6 +500,8 @@ export class TelemetrySubscriber { |
468 | 500 | |
469 | 501 | private zone: NgZone; |
470 | 502 | |
503 | + private tsOffset = 0; | |
504 | + | |
471 | 505 | public subscriptionCommands: Array<WebsocketCmd>; |
472 | 506 | |
473 | 507 | public data$ = this.dataSubject.asObservable(); |
... | ... | @@ -522,6 +556,10 @@ export class TelemetrySubscriber { |
522 | 556 | this.reconnectSubject.complete(); |
523 | 557 | } |
524 | 558 | |
559 | + public setTsOffset(tsOffset: number) { | |
560 | + this.tsOffset = tsOffset; | |
561 | + } | |
562 | + | |
525 | 563 | public onData(message: SubscriptionUpdate) { |
526 | 564 | const cmdId = message.subscriptionId; |
527 | 565 | let keys: string[]; |
... | ... | @@ -545,6 +583,9 @@ export class TelemetrySubscriber { |
545 | 583 | } |
546 | 584 | |
547 | 585 | public onEntityData(message: EntityDataUpdate) { |
586 | + if (this.tsOffset) { | |
587 | + message.prepareData(this.tsOffset); | |
588 | + } | |
548 | 589 | if (this.zone) { |
549 | 590 | this.zone.run( |
550 | 591 | () => { | ... | ... |
... | ... | @@ -17,9 +17,7 @@ |
17 | 17 | import { TimeService } from '@core/services/time.service'; |
18 | 18 | import { deepClone, isDefined, isUndefined } from '@app/core/utils'; |
19 | 19 | import * as moment_ from 'moment'; |
20 | -import { Observable } from 'rxjs/internal/Observable'; | |
21 | -import { from, of } from 'rxjs'; | |
22 | -import { map, mergeMap, tap } from 'rxjs/operators'; | |
20 | +import * as monentTz from 'moment-timezone'; | |
23 | 21 | |
24 | 22 | const moment = moment_; |
25 | 23 | |
... | ... | @@ -27,6 +25,7 @@ export const SECOND = 1000; |
27 | 25 | export const MINUTE = 60 * SECOND; |
28 | 26 | export const HOUR = 60 * MINUTE; |
29 | 27 | export const DAY = 24 * HOUR; |
28 | +export const WEEK = 7 * DAY; | |
30 | 29 | export const YEAR = DAY * 365; |
31 | 30 | |
32 | 31 | export enum TimewindowType { |
... | ... | @@ -34,14 +33,25 @@ export enum TimewindowType { |
34 | 33 | HISTORY |
35 | 34 | } |
36 | 35 | |
36 | +export enum RealtimeWindowType { | |
37 | + LAST_INTERVAL, | |
38 | + INTERVAL | |
39 | +} | |
40 | + | |
37 | 41 | export enum HistoryWindowType { |
38 | 42 | LAST_INTERVAL, |
39 | - FIXED | |
43 | + FIXED, | |
44 | + INTERVAL | |
40 | 45 | } |
41 | 46 | |
42 | 47 | export interface IntervalWindow { |
43 | 48 | interval?: number; |
44 | 49 | timewindowMs?: number; |
50 | + quickInterval?: QuickTimeInterval; | |
51 | +} | |
52 | + | |
53 | +export interface RealtimeWindow extends IntervalWindow{ | |
54 | + realtimeType?: RealtimeWindowType; | |
45 | 55 | } |
46 | 56 | |
47 | 57 | export interface FixedWindow { |
... | ... | @@ -85,10 +95,12 @@ export interface Timewindow { |
85 | 95 | hideInterval?: boolean; |
86 | 96 | hideAggregation?: boolean; |
87 | 97 | hideAggInterval?: boolean; |
98 | + hideTimezone?: boolean; | |
88 | 99 | selectedTab?: TimewindowType; |
89 | - realtime?: IntervalWindow; | |
100 | + realtime?: RealtimeWindow; | |
90 | 101 | history?: HistoryWindow; |
91 | 102 | aggregation?: Aggregation; |
103 | + timezone?: string; | |
92 | 104 | } |
93 | 105 | |
94 | 106 | export interface SubscriptionAggregation extends Aggregation { |
... | ... | @@ -99,6 +111,9 @@ export interface SubscriptionAggregation extends Aggregation { |
99 | 111 | |
100 | 112 | export interface SubscriptionTimewindow { |
101 | 113 | startTs?: number; |
114 | + quickInterval?: QuickTimeInterval; | |
115 | + timezone?: string; | |
116 | + tsOffset?: number; | |
102 | 117 | realtimeWindowMs?: number; |
103 | 118 | fixedWindow?: FixedWindow; |
104 | 119 | aggregation?: SubscriptionAggregation; |
... | ... | @@ -108,9 +123,46 @@ export interface WidgetTimewindow { |
108 | 123 | minTime?: number; |
109 | 124 | maxTime?: number; |
110 | 125 | interval?: number; |
126 | + timezone?: string; | |
111 | 127 | stDiff?: number; |
112 | 128 | } |
113 | 129 | |
130 | +export enum QuickTimeInterval { | |
131 | + YESTERDAY = 'YESTERDAY', | |
132 | + DAY_BEFORE_YESTERDAY = 'DAY_BEFORE_YESTERDAY', | |
133 | + THIS_DAY_LAST_WEEK = 'THIS_DAY_LAST_WEEK', | |
134 | + PREVIOUS_WEEK = 'PREVIOUS_WEEK', | |
135 | + PREVIOUS_MONTH = 'PREVIOUS_MONTH', | |
136 | + PREVIOUS_YEAR = 'PREVIOUS_YEAR', | |
137 | + CURRENT_HOUR = 'CURRENT_HOUR', | |
138 | + CURRENT_DAY = 'CURRENT_DAY', | |
139 | + CURRENT_DAY_SO_FAR = 'CURRENT_DAY_SO_FAR', | |
140 | + CURRENT_WEEK = 'CURRENT_WEEK', | |
141 | + CURRENT_WEEK_SO_FAR = 'CURRENT_WEEK_SO_WAR', | |
142 | + CURRENT_MONTH = 'CURRENT_MONTH', | |
143 | + CURRENT_MONTH_SO_FAR = 'CURRENT_MONTH_SO_FAR', | |
144 | + CURRENT_YEAR = 'CURRENT_YEAR', | |
145 | + CURRENT_YEAR_SO_FAR = 'CURRENT_YEAR_SO_FAR' | |
146 | +} | |
147 | + | |
148 | +export const QuickTimeIntervalTranslationMap = new Map<QuickTimeInterval, string>([ | |
149 | + [QuickTimeInterval.YESTERDAY, 'timeinterval.predefined.yesterday'], | |
150 | + [QuickTimeInterval.DAY_BEFORE_YESTERDAY, 'timeinterval.predefined.day-before-yesterday'], | |
151 | + [QuickTimeInterval.THIS_DAY_LAST_WEEK, 'timeinterval.predefined.this-day-last-week'], | |
152 | + [QuickTimeInterval.PREVIOUS_WEEK, 'timeinterval.predefined.previous-week'], | |
153 | + [QuickTimeInterval.PREVIOUS_MONTH, 'timeinterval.predefined.previous-month'], | |
154 | + [QuickTimeInterval.PREVIOUS_YEAR, 'timeinterval.predefined.previous-year'], | |
155 | + [QuickTimeInterval.CURRENT_HOUR, 'timeinterval.predefined.current-hour'], | |
156 | + [QuickTimeInterval.CURRENT_DAY, 'timeinterval.predefined.current-day'], | |
157 | + [QuickTimeInterval.CURRENT_DAY_SO_FAR, 'timeinterval.predefined.current-day-so-far'], | |
158 | + [QuickTimeInterval.CURRENT_WEEK, 'timeinterval.predefined.current-week'], | |
159 | + [QuickTimeInterval.CURRENT_WEEK_SO_FAR, 'timeinterval.predefined.current-week-so-far'], | |
160 | + [QuickTimeInterval.CURRENT_MONTH, 'timeinterval.predefined.current-month'], | |
161 | + [QuickTimeInterval.CURRENT_MONTH_SO_FAR, 'timeinterval.predefined.current-month-so-far'], | |
162 | + [QuickTimeInterval.CURRENT_YEAR, 'timeinterval.predefined.current-year'], | |
163 | + [QuickTimeInterval.CURRENT_YEAR_SO_FAR, 'timeinterval.predefined.current-year-so-far'] | |
164 | +]); | |
165 | + | |
114 | 166 | export function historyInterval(timewindowMs: number): Timewindow { |
115 | 167 | const timewindow: Timewindow = { |
116 | 168 | selectedTab: TimewindowType.HISTORY, |
... | ... | @@ -129,10 +181,13 @@ export function defaultTimewindow(timeService: TimeService): Timewindow { |
129 | 181 | hideInterval: false, |
130 | 182 | hideAggregation: false, |
131 | 183 | hideAggInterval: false, |
184 | + hideTimezone: false, | |
132 | 185 | selectedTab: TimewindowType.REALTIME, |
133 | 186 | realtime: { |
187 | + realtimeType: RealtimeWindowType.LAST_INTERVAL, | |
134 | 188 | interval: SECOND, |
135 | - timewindowMs: MINUTE | |
189 | + timewindowMs: MINUTE, | |
190 | + quickInterval: QuickTimeInterval.CURRENT_DAY | |
136 | 191 | }, |
137 | 192 | history: { |
138 | 193 | historyType: HistoryWindowType.LAST_INTERVAL, |
... | ... | @@ -141,7 +196,8 @@ export function defaultTimewindow(timeService: TimeService): Timewindow { |
141 | 196 | fixedTimewindow: { |
142 | 197 | startTimeMs: currentTime - DAY, |
143 | 198 | endTimeMs: currentTime |
144 | - } | |
199 | + }, | |
200 | + quickInterval: QuickTimeInterval.CURRENT_DAY | |
145 | 201 | }, |
146 | 202 | aggregation: { |
147 | 203 | type: AggregationType.AVG, |
... | ... | @@ -157,6 +213,7 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T |
157 | 213 | model.hideInterval = value.hideInterval; |
158 | 214 | model.hideAggregation = value.hideAggregation; |
159 | 215 | model.hideAggInterval = value.hideAggInterval; |
216 | + model.hideTimezone = value.hideTimezone; | |
160 | 217 | if (isUndefined(value.selectedTab)) { |
161 | 218 | if (value.realtime) { |
162 | 219 | model.selectedTab = TimewindowType.REALTIME; |
... | ... | @@ -170,7 +227,20 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T |
170 | 227 | if (isDefined(value.realtime.interval)) { |
171 | 228 | model.realtime.interval = value.realtime.interval; |
172 | 229 | } |
173 | - model.realtime.timewindowMs = value.realtime.timewindowMs; | |
230 | + if (isUndefined(value.realtime.realtimeType)) { | |
231 | + if (isDefined(value.realtime.quickInterval)) { | |
232 | + model.realtime.realtimeType = RealtimeWindowType.INTERVAL; | |
233 | + } else { | |
234 | + model.realtime.realtimeType = RealtimeWindowType.LAST_INTERVAL; | |
235 | + } | |
236 | + } else { | |
237 | + model.realtime.realtimeType = value.realtime.realtimeType; | |
238 | + } | |
239 | + if (model.realtime.realtimeType === RealtimeWindowType.INTERVAL) { | |
240 | + model.realtime.quickInterval = value.realtime.quickInterval; | |
241 | + } else { | |
242 | + model.realtime.timewindowMs = value.realtime.timewindowMs; | |
243 | + } | |
174 | 244 | } else { |
175 | 245 | if (isDefined(value.history.interval)) { |
176 | 246 | model.history.interval = value.history.interval; |
... | ... | @@ -178,6 +248,8 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T |
178 | 248 | if (isUndefined(value.history.historyType)) { |
179 | 249 | if (isDefined(value.history.timewindowMs)) { |
180 | 250 | model.history.historyType = HistoryWindowType.LAST_INTERVAL; |
251 | + } else if (isDefined(value.history.quickInterval)) { | |
252 | + model.history.historyType = HistoryWindowType.INTERVAL; | |
181 | 253 | } else { |
182 | 254 | model.history.historyType = HistoryWindowType.FIXED; |
183 | 255 | } |
... | ... | @@ -186,6 +258,8 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T |
186 | 258 | } |
187 | 259 | if (model.history.historyType === HistoryWindowType.LAST_INTERVAL) { |
188 | 260 | model.history.timewindowMs = value.history.timewindowMs; |
261 | + } else if (model.history.historyType === HistoryWindowType.INTERVAL) { | |
262 | + model.history.quickInterval = value.history.quickInterval; | |
189 | 263 | } else { |
190 | 264 | model.history.fixedTimewindow.startTimeMs = value.history.fixedTimewindow.startTimeMs; |
191 | 265 | model.history.fixedTimewindow.endTimeMs = value.history.fixedTimewindow.endTimeMs; |
... | ... | @@ -197,6 +271,7 @@ export function initModelFromDefaultTimewindow(value: Timewindow, timeService: T |
197 | 271 | } |
198 | 272 | model.aggregation.limit = value.aggregation.limit || Math.floor(timeService.getMaxDatapointsLimit() / 2); |
199 | 273 | } |
274 | + model.timezone = value.timezone; | |
200 | 275 | } |
201 | 276 | return model; |
202 | 277 | } |
... | ... | @@ -223,6 +298,7 @@ export function toHistoryTimewindow(timewindow: Timewindow, startTimeMs: number, |
223 | 298 | hideInterval: timewindow.hideInterval || false, |
224 | 299 | hideAggregation: timewindow.hideAggregation || false, |
225 | 300 | hideAggInterval: timewindow.hideAggInterval || false, |
301 | + hideTimezone: timewindow.hideTimezone || false, | |
226 | 302 | selectedTab: TimewindowType.HISTORY, |
227 | 303 | history: { |
228 | 304 | historyType: HistoryWindowType.FIXED, |
... | ... | @@ -235,7 +311,8 @@ export function toHistoryTimewindow(timewindow: Timewindow, startTimeMs: number, |
235 | 311 | aggregation: { |
236 | 312 | type: aggType, |
237 | 313 | limit |
238 | - } | |
314 | + }, | |
315 | + timezone: timewindow.timezone | |
239 | 316 | }; |
240 | 317 | return historyTimewindow; |
241 | 318 | } |
... | ... | @@ -249,8 +326,15 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num |
249 | 326 | interval: SECOND, |
250 | 327 | limit: timeService.getMaxDatapointsLimit(), |
251 | 328 | type: AggregationType.AVG |
252 | - } | |
329 | + }, | |
330 | + timezone: timewindow.timezone, | |
331 | + tsOffset: 0 | |
253 | 332 | }; |
333 | + if (timewindow.timezone) { | |
334 | + const tz = getTimezone(timewindow.timezone); | |
335 | + const localOffset = moment().utcOffset(); | |
336 | + subscriptionTimewindow.tsOffset = (tz.utcOffset() - localOffset) * 60 * 1000; | |
337 | + } | |
254 | 338 | let aggTimewindow = 0; |
255 | 339 | if (stateData) { |
256 | 340 | subscriptionTimewindow.aggregation.type = AggregationType.NONE; |
... | ... | @@ -267,33 +351,65 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num |
267 | 351 | selectedTab = isDefined(timewindow.realtime) ? TimewindowType.REALTIME : TimewindowType.HISTORY; |
268 | 352 | } |
269 | 353 | if (selectedTab === TimewindowType.REALTIME) { |
270 | - subscriptionTimewindow.realtimeWindowMs = timewindow.realtime.timewindowMs; | |
354 | + let realtimeType = timewindow.realtime.realtimeType; | |
355 | + if (isUndefined(realtimeType)) { | |
356 | + if (isDefined(timewindow.realtime.quickInterval)) { | |
357 | + realtimeType = RealtimeWindowType.INTERVAL; | |
358 | + } else { | |
359 | + realtimeType = RealtimeWindowType.LAST_INTERVAL; | |
360 | + } | |
361 | + } | |
362 | + if (realtimeType === RealtimeWindowType.INTERVAL) { | |
363 | + const currentDate = getCurrentTime(timewindow.timezone); | |
364 | + subscriptionTimewindow.realtimeWindowMs = | |
365 | + getSubscriptionRealtimeWindowFromTimeInterval(timewindow.realtime.quickInterval, currentDate); | |
366 | + subscriptionTimewindow.quickInterval = timewindow.realtime.quickInterval; | |
367 | + subscriptionTimewindow.startTs = calculateIntervalStartTime(timewindow.realtime.quickInterval, currentDate); | |
368 | + } else { | |
369 | + subscriptionTimewindow.realtimeWindowMs = timewindow.realtime.timewindowMs; | |
370 | + subscriptionTimewindow.startTs = Date.now() + stDiff - subscriptionTimewindow.realtimeWindowMs; | |
371 | + } | |
271 | 372 | subscriptionTimewindow.aggregation.interval = |
272 | 373 | timeService.boundIntervalToTimewindow(subscriptionTimewindow.realtimeWindowMs, timewindow.realtime.interval, |
273 | 374 | subscriptionTimewindow.aggregation.type); |
274 | - subscriptionTimewindow.startTs = Date.now() + stDiff - subscriptionTimewindow.realtimeWindowMs; | |
275 | - const startDiff = subscriptionTimewindow.startTs % subscriptionTimewindow.aggregation.interval; | |
276 | 375 | aggTimewindow = subscriptionTimewindow.realtimeWindowMs; |
277 | - if (startDiff) { | |
278 | - subscriptionTimewindow.startTs -= startDiff; | |
279 | - aggTimewindow += subscriptionTimewindow.aggregation.interval; | |
376 | + if (realtimeType !== RealtimeWindowType.INTERVAL) { | |
377 | + const startDiff = subscriptionTimewindow.startTs % subscriptionTimewindow.aggregation.interval; | |
378 | + if (startDiff) { | |
379 | + subscriptionTimewindow.startTs -= startDiff; | |
380 | + aggTimewindow += subscriptionTimewindow.aggregation.interval; | |
381 | + } | |
280 | 382 | } |
281 | 383 | } else { |
282 | 384 | let historyType = timewindow.history.historyType; |
283 | 385 | if (isUndefined(historyType)) { |
284 | - historyType = isDefined(timewindow.history.timewindowMs) ? HistoryWindowType.LAST_INTERVAL : HistoryWindowType.FIXED; | |
386 | + if (isDefined(timewindow.history.timewindowMs)) { | |
387 | + historyType = HistoryWindowType.LAST_INTERVAL; | |
388 | + } else if (isDefined(timewindow.history.quickInterval)) { | |
389 | + historyType = HistoryWindowType.INTERVAL; | |
390 | + } else { | |
391 | + historyType = HistoryWindowType.FIXED; | |
392 | + } | |
285 | 393 | } |
286 | 394 | if (historyType === HistoryWindowType.LAST_INTERVAL) { |
287 | - const currentTime = Date.now(); | |
395 | + const currentDate = getCurrentTime(timewindow.timezone); | |
396 | + const currentTime = currentDate.valueOf(); | |
288 | 397 | subscriptionTimewindow.fixedWindow = { |
289 | 398 | startTimeMs: currentTime - timewindow.history.timewindowMs, |
290 | 399 | endTimeMs: currentTime |
291 | 400 | }; |
292 | 401 | aggTimewindow = timewindow.history.timewindowMs; |
402 | + } else if (historyType === HistoryWindowType.INTERVAL) { | |
403 | + const currentDate = getCurrentTime(timewindow.timezone); | |
404 | + subscriptionTimewindow.fixedWindow = { | |
405 | + startTimeMs: calculateIntervalStartTime(timewindow.history.quickInterval, currentDate), | |
406 | + endTimeMs: calculateIntervalEndTime(timewindow.history.quickInterval, currentDate) | |
407 | + }; | |
408 | + aggTimewindow = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs; | |
293 | 409 | } else { |
294 | 410 | subscriptionTimewindow.fixedWindow = { |
295 | - startTimeMs: timewindow.history.fixedTimewindow.startTimeMs, | |
296 | - endTimeMs: timewindow.history.fixedTimewindow.endTimeMs | |
411 | + startTimeMs: timewindow.history.fixedTimewindow.startTimeMs - subscriptionTimewindow.tsOffset, | |
412 | + endTimeMs: timewindow.history.fixedTimewindow.endTimeMs - subscriptionTimewindow.tsOffset | |
297 | 413 | }; |
298 | 414 | aggTimewindow = subscriptionTimewindow.fixedWindow.endTimeMs - subscriptionTimewindow.fixedWindow.startTimeMs; |
299 | 415 | } |
... | ... | @@ -309,6 +425,127 @@ export function createSubscriptionTimewindow(timewindow: Timewindow, stDiff: num |
309 | 425 | return subscriptionTimewindow; |
310 | 426 | } |
311 | 427 | |
428 | +function getSubscriptionRealtimeWindowFromTimeInterval(interval: QuickTimeInterval, currentDate: moment_.Moment): number { | |
429 | + switch (interval) { | |
430 | + case QuickTimeInterval.CURRENT_HOUR: | |
431 | + return HOUR; | |
432 | + case QuickTimeInterval.CURRENT_DAY: | |
433 | + case QuickTimeInterval.CURRENT_DAY_SO_FAR: | |
434 | + return DAY; | |
435 | + case QuickTimeInterval.CURRENT_WEEK: | |
436 | + case QuickTimeInterval.CURRENT_WEEK_SO_FAR: | |
437 | + return WEEK; | |
438 | + case QuickTimeInterval.CURRENT_MONTH: | |
439 | + case QuickTimeInterval.CURRENT_MONTH_SO_FAR: | |
440 | + return currentDate.endOf('month').diff(currentDate.clone().startOf('month')); | |
441 | + case QuickTimeInterval.CURRENT_YEAR: | |
442 | + case QuickTimeInterval.CURRENT_YEAR_SO_FAR: | |
443 | + return currentDate.endOf('year').diff(currentDate.clone().startOf('year')); | |
444 | + } | |
445 | +} | |
446 | + | |
447 | +export function calculateIntervalEndTime(interval: QuickTimeInterval, currentDate: moment_.Moment = null, tz: string = ''): number { | |
448 | + currentDate = currentDate ? currentDate.clone() : getCurrentTime(tz); | |
449 | + switch (interval) { | |
450 | + case QuickTimeInterval.YESTERDAY: | |
451 | + currentDate.subtract(1, 'days'); | |
452 | + return currentDate.endOf('day').valueOf(); | |
453 | + case QuickTimeInterval.DAY_BEFORE_YESTERDAY: | |
454 | + currentDate.subtract(2, 'days'); | |
455 | + return currentDate.endOf('day').valueOf(); | |
456 | + case QuickTimeInterval.THIS_DAY_LAST_WEEK: | |
457 | + currentDate.subtract(1, 'weeks'); | |
458 | + return currentDate.endOf('day').valueOf(); | |
459 | + case QuickTimeInterval.PREVIOUS_WEEK: | |
460 | + currentDate.subtract(1, 'weeks'); | |
461 | + return currentDate.endOf('week').valueOf(); | |
462 | + case QuickTimeInterval.PREVIOUS_MONTH: | |
463 | + currentDate.subtract(1, 'months'); | |
464 | + return currentDate.endOf('month').valueOf(); | |
465 | + case QuickTimeInterval.PREVIOUS_YEAR: | |
466 | + currentDate.subtract(1, 'years'); | |
467 | + return currentDate.endOf('year').valueOf(); | |
468 | + case QuickTimeInterval.CURRENT_HOUR: | |
469 | + return currentDate.endOf('hour').valueOf(); | |
470 | + case QuickTimeInterval.CURRENT_DAY: | |
471 | + return currentDate.endOf('day').valueOf(); | |
472 | + case QuickTimeInterval.CURRENT_WEEK: | |
473 | + return currentDate.endOf('week').valueOf(); | |
474 | + case QuickTimeInterval.CURRENT_MONTH: | |
475 | + return currentDate.endOf('month').valueOf(); | |
476 | + case QuickTimeInterval.CURRENT_YEAR: | |
477 | + return currentDate.endOf('year').valueOf(); | |
478 | + case QuickTimeInterval.CURRENT_DAY_SO_FAR: | |
479 | + case QuickTimeInterval.CURRENT_WEEK_SO_FAR: | |
480 | + case QuickTimeInterval.CURRENT_MONTH_SO_FAR: | |
481 | + case QuickTimeInterval.CURRENT_YEAR_SO_FAR: | |
482 | + return currentDate.valueOf(); | |
483 | + } | |
484 | +} | |
485 | + | |
486 | +export function calculateIntervalStartTime(interval: QuickTimeInterval, currentDate: moment_.Moment = null, tz: string = ''): number { | |
487 | + currentDate = currentDate ? currentDate.clone() : getCurrentTime(tz); | |
488 | + switch (interval) { | |
489 | + case QuickTimeInterval.YESTERDAY: | |
490 | + currentDate.subtract(1, 'days'); | |
491 | + return currentDate.startOf('day').valueOf(); | |
492 | + case QuickTimeInterval.DAY_BEFORE_YESTERDAY: | |
493 | + currentDate.subtract(2, 'days'); | |
494 | + return currentDate.startOf('day').valueOf(); | |
495 | + case QuickTimeInterval.THIS_DAY_LAST_WEEK: | |
496 | + currentDate.subtract(1, 'weeks'); | |
497 | + return currentDate.startOf('day').valueOf(); | |
498 | + case QuickTimeInterval.PREVIOUS_WEEK: | |
499 | + currentDate.subtract(1, 'weeks'); | |
500 | + return currentDate.startOf('week').valueOf(); | |
501 | + case QuickTimeInterval.PREVIOUS_MONTH: | |
502 | + currentDate.subtract(1, 'months'); | |
503 | + return currentDate.startOf('month').valueOf(); | |
504 | + case QuickTimeInterval.PREVIOUS_YEAR: | |
505 | + currentDate.subtract(1, 'years'); | |
506 | + return currentDate.startOf('year').valueOf(); | |
507 | + case QuickTimeInterval.CURRENT_HOUR: | |
508 | + return currentDate.startOf('hour').valueOf(); | |
509 | + case QuickTimeInterval.CURRENT_DAY: | |
510 | + case QuickTimeInterval.CURRENT_DAY_SO_FAR: | |
511 | + return currentDate.startOf('day').valueOf(); | |
512 | + case QuickTimeInterval.CURRENT_WEEK: | |
513 | + case QuickTimeInterval.CURRENT_WEEK_SO_FAR: | |
514 | + return currentDate.startOf('week').valueOf(); | |
515 | + case QuickTimeInterval.CURRENT_MONTH: | |
516 | + case QuickTimeInterval.CURRENT_MONTH_SO_FAR: | |
517 | + return currentDate.startOf('month').valueOf(); | |
518 | + case QuickTimeInterval.CURRENT_YEAR: | |
519 | + case QuickTimeInterval.CURRENT_YEAR_SO_FAR: | |
520 | + return currentDate.startOf('year').valueOf(); | |
521 | + } | |
522 | +} | |
523 | + | |
524 | +export function quickTimeIntervalPeriod(interval: QuickTimeInterval): number { | |
525 | + switch (interval) { | |
526 | + case QuickTimeInterval.CURRENT_HOUR: | |
527 | + return HOUR; | |
528 | + case QuickTimeInterval.YESTERDAY: | |
529 | + case QuickTimeInterval.DAY_BEFORE_YESTERDAY: | |
530 | + case QuickTimeInterval.THIS_DAY_LAST_WEEK: | |
531 | + case QuickTimeInterval.CURRENT_DAY: | |
532 | + case QuickTimeInterval.CURRENT_DAY_SO_FAR: | |
533 | + return DAY; | |
534 | + case QuickTimeInterval.PREVIOUS_WEEK: | |
535 | + case QuickTimeInterval.CURRENT_WEEK: | |
536 | + case QuickTimeInterval.CURRENT_WEEK_SO_FAR: | |
537 | + return WEEK; | |
538 | + case QuickTimeInterval.PREVIOUS_MONTH: | |
539 | + case QuickTimeInterval.CURRENT_MONTH: | |
540 | + case QuickTimeInterval.CURRENT_MONTH_SO_FAR: | |
541 | + return DAY * 30; | |
542 | + case QuickTimeInterval.PREVIOUS_YEAR: | |
543 | + case QuickTimeInterval.CURRENT_YEAR: | |
544 | + case QuickTimeInterval.CURRENT_YEAR_SO_FAR: | |
545 | + return YEAR; | |
546 | + } | |
547 | +} | |
548 | + | |
312 | 549 | export function createTimewindowForComparison(subscriptionTimewindow: SubscriptionTimewindow, |
313 | 550 | timeUnit: moment_.unitOfTime.DurationConstructor): SubscriptionTimewindow { |
314 | 551 | const timewindowForComparison: SubscriptionTimewindow = { |
... | ... | @@ -339,6 +576,7 @@ export function cloneSelectedTimewindow(timewindow: Timewindow): Timewindow { |
339 | 576 | cloned.hideInterval = timewindow.hideInterval || false; |
340 | 577 | cloned.hideAggregation = timewindow.hideAggregation || false; |
341 | 578 | cloned.hideAggInterval = timewindow.hideAggInterval || false; |
579 | + cloned.hideTimezone = timewindow.hideTimezone || false; | |
342 | 580 | if (isDefined(timewindow.selectedTab)) { |
343 | 581 | cloned.selectedTab = timewindow.selectedTab; |
344 | 582 | if (timewindow.selectedTab === TimewindowType.REALTIME) { |
... | ... | @@ -348,6 +586,7 @@ export function cloneSelectedTimewindow(timewindow: Timewindow): Timewindow { |
348 | 586 | } |
349 | 587 | } |
350 | 588 | cloned.aggregation = deepClone(timewindow.aggregation); |
589 | + cloned.timezone = timewindow.timezone; | |
351 | 590 | return cloned; |
352 | 591 | } |
353 | 592 | |
... | ... | @@ -358,6 +597,8 @@ export function cloneSelectedHistoryTimewindow(historyWindow: HistoryWindow): Hi |
358 | 597 | cloned.interval = historyWindow.interval; |
359 | 598 | if (historyWindow.historyType === HistoryWindowType.LAST_INTERVAL) { |
360 | 599 | cloned.timewindowMs = historyWindow.timewindowMs; |
600 | + } else if (historyWindow.historyType === HistoryWindowType.INTERVAL) { | |
601 | + cloned.quickInterval = historyWindow.quickInterval; | |
361 | 602 | } else if (historyWindow.historyType === HistoryWindowType.FIXED) { |
362 | 603 | cloned.fixedTimewindow = deepClone(historyWindow.fixedTimewindow); |
363 | 604 | } |
... | ... | @@ -375,7 +616,7 @@ export const defaultTimeIntervals = new Array<TimeInterval>( |
375 | 616 | { |
376 | 617 | name: 'timeinterval.seconds-interval', |
377 | 618 | translateParams: {seconds: 1}, |
378 | - value: 1 * SECOND | |
619 | + value: SECOND | |
379 | 620 | }, |
380 | 621 | { |
381 | 622 | name: 'timeinterval.seconds-interval', |
... | ... | @@ -400,7 +641,7 @@ export const defaultTimeIntervals = new Array<TimeInterval>( |
400 | 641 | { |
401 | 642 | name: 'timeinterval.minutes-interval', |
402 | 643 | translateParams: {minutes: 1}, |
403 | - value: 1 * MINUTE | |
644 | + value: MINUTE | |
404 | 645 | }, |
405 | 646 | { |
406 | 647 | name: 'timeinterval.minutes-interval', |
... | ... | @@ -430,7 +671,7 @@ export const defaultTimeIntervals = new Array<TimeInterval>( |
430 | 671 | { |
431 | 672 | name: 'timeinterval.hours-interval', |
432 | 673 | translateParams: {hours: 1}, |
433 | - value: 1 * HOUR | |
674 | + value: HOUR | |
434 | 675 | }, |
435 | 676 | { |
436 | 677 | name: 'timeinterval.hours-interval', |
... | ... | @@ -455,7 +696,7 @@ export const defaultTimeIntervals = new Array<TimeInterval>( |
455 | 696 | { |
456 | 697 | name: 'timeinterval.days-interval', |
457 | 698 | translateParams: {days: 1}, |
458 | - value: 1 * DAY | |
699 | + value: DAY | |
459 | 700 | }, |
460 | 701 | { |
461 | 702 | name: 'timeinterval.days-interval', |
... | ... | @@ -495,60 +736,50 @@ export interface TimezoneInfo { |
495 | 736 | let timezones: TimezoneInfo[] = null; |
496 | 737 | let defaultTimezone: string = null; |
497 | 738 | |
498 | -export function getTimezones(): Observable<TimezoneInfo[]> { | |
499 | - if (timezones) { | |
500 | - return of(timezones); | |
501 | - } else { | |
502 | - return from(import('moment-timezone')).pipe( | |
503 | - map((monentTz) => { | |
504 | - return monentTz.tz.names().map((zoneName) => { | |
505 | - const tz = monentTz.tz(zoneName); | |
506 | - return { | |
507 | - id: zoneName, | |
508 | - name: zoneName.replace(/_/g, ' '), | |
509 | - offset: `UTC${tz.format('Z')}`, | |
510 | - nOffset: tz.utcOffset() | |
511 | - }; | |
512 | - }); | |
513 | - }), | |
514 | - tap((zones) => { | |
515 | - timezones = zones; | |
516 | - }) | |
517 | - ); | |
739 | +export function getTimezones(): TimezoneInfo[] { | |
740 | + if (!timezones) { | |
741 | + timezones = monentTz.tz.names().map((zoneName) => { | |
742 | + const tz = monentTz.tz(zoneName); | |
743 | + return { | |
744 | + id: zoneName, | |
745 | + name: zoneName.replace(/_/g, ' '), | |
746 | + offset: `UTC${tz.format('Z')}`, | |
747 | + nOffset: tz.utcOffset() | |
748 | + }; | |
749 | + }); | |
518 | 750 | } |
751 | + return timezones; | |
519 | 752 | } |
520 | 753 | |
521 | -export function getTimezoneInfo(timezoneId: string, defaultTimezoneId?: string, userTimezoneByDefault?: boolean): Observable<TimezoneInfo> { | |
522 | - return getTimezones().pipe( | |
523 | - mergeMap((timezoneList) => { | |
524 | - let foundTimezone = timezoneList.find(timezoneInfo => timezoneInfo.id === timezoneId); | |
525 | - if (!foundTimezone) { | |
526 | - if (userTimezoneByDefault) { | |
527 | - return getDefaultTimezone().pipe( | |
528 | - map((userTimezone) => { | |
529 | - return timezoneList.find(timezoneInfo => timezoneInfo.id === userTimezone); | |
530 | - }) | |
531 | - ); | |
532 | - } else if (defaultTimezoneId) { | |
533 | - foundTimezone = timezoneList.find(timezoneInfo => timezoneInfo.id === defaultTimezoneId); | |
534 | - } | |
535 | - } | |
536 | - return of(foundTimezone); | |
537 | - }) | |
538 | - ); | |
754 | +export function getTimezoneInfo(timezoneId: string, defaultTimezoneId?: string, userTimezoneByDefault?: boolean): TimezoneInfo { | |
755 | + const timezoneList = getTimezones(); | |
756 | + let foundTimezone = timezoneId ? timezoneList.find(timezoneInfo => timezoneInfo.id === timezoneId) : null; | |
757 | + if (!foundTimezone) { | |
758 | + if (userTimezoneByDefault) { | |
759 | + const userTimezone = getDefaultTimezone(); | |
760 | + foundTimezone = timezoneList.find(timezoneInfo => timezoneInfo.id === userTimezone); | |
761 | + } else if (defaultTimezoneId) { | |
762 | + foundTimezone = timezoneList.find(timezoneInfo => timezoneInfo.id === defaultTimezoneId); | |
763 | + } | |
764 | + } | |
765 | + return foundTimezone; | |
539 | 766 | } |
540 | 767 | |
541 | -export function getDefaultTimezone(): Observable<string> { | |
542 | - if (defaultTimezone) { | |
543 | - return of(defaultTimezone); | |
768 | +export function getDefaultTimezone(): string { | |
769 | + if (!defaultTimezone) { | |
770 | + defaultTimezone = monentTz.tz.guess(); | |
771 | + } | |
772 | + return defaultTimezone; | |
773 | +} | |
774 | + | |
775 | +export function getCurrentTime(tz?: string): moment_.Moment { | |
776 | + if (tz) { | |
777 | + return moment().tz(tz); | |
544 | 778 | } else { |
545 | - return from(import('moment-timezone')).pipe( | |
546 | - map((monentTz) => { | |
547 | - return monentTz.tz.guess(); | |
548 | - }), | |
549 | - tap((zone) => { | |
550 | - defaultTimezone = zone; | |
551 | - }) | |
552 | - ); | |
779 | + return moment(); | |
553 | 780 | } |
554 | 781 | } |
782 | + | |
783 | +export function getTimezone(tz: string): moment_.Moment { | |
784 | + return moment.tz(tz); | |
785 | +} | ... | ... |
... | ... | @@ -140,6 +140,7 @@ import { TimezoneSelectComponent } from '@shared/components/time/timezone-select |
140 | 140 | import { FileSizePipe } from '@shared/pipe/file-size.pipe'; |
141 | 141 | import { WidgetsBundleSearchComponent } from '@shared/components/widgets-bundle-search.component'; |
142 | 142 | import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; |
143 | +import { QuickTimeIntervalComponent } from '@shared/components/time/quick-time-interval.component'; | |
143 | 144 | |
144 | 145 | @NgModule({ |
145 | 146 | providers: [ |
... | ... | @@ -175,6 +176,7 @@ import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; |
175 | 176 | TimewindowComponent, |
176 | 177 | TimewindowPanelComponent, |
177 | 178 | TimeintervalComponent, |
179 | + QuickTimeIntervalComponent, | |
178 | 180 | DashboardSelectComponent, |
179 | 181 | DashboardSelectPanelComponent, |
180 | 182 | DatetimePeriodComponent, |
... | ... | @@ -302,6 +304,7 @@ import { SelectableColumnsPipe } from '@shared/pipe/selectable-columns.pipe'; |
302 | 304 | TimewindowComponent, |
303 | 305 | TimewindowPanelComponent, |
304 | 306 | TimeintervalComponent, |
307 | + QuickTimeIntervalComponent, | |
305 | 308 | DashboardSelectComponent, |
306 | 309 | DatetimePeriodComponent, |
307 | 310 | DatetimeComponent, | ... | ... |
... | ... | @@ -2118,7 +2118,24 @@ |
2118 | 2118 | "hours": "Hours", |
2119 | 2119 | "minutes": "Minutes", |
2120 | 2120 | "seconds": "Seconds", |
2121 | - "advanced": "Advanced" | |
2121 | + "advanced": "Advanced", | |
2122 | + "predefined": { | |
2123 | + "yesterday": "Yesterday", | |
2124 | + "day-before-yesterday": "Day before yesterday", | |
2125 | + "this-day-last-week": "This day last week", | |
2126 | + "previous-week": "Previous week", | |
2127 | + "previous-month": "Previous month", | |
2128 | + "previous-year": "Previous year", | |
2129 | + "current-hour": "Current hour", | |
2130 | + "current-day": "Current day", | |
2131 | + "current-day-so-far": "Current day so far", | |
2132 | + "current-week": "Current week", | |
2133 | + "current-week-so-far": "Current week so far", | |
2134 | + "current-month": "Current month", | |
2135 | + "current-month-so-far": "Current month so far", | |
2136 | + "current-year": "Current year", | |
2137 | + "current-year-so-far": "Current year so far" | |
2138 | + } | |
2122 | 2139 | }, |
2123 | 2140 | "timeunit": { |
2124 | 2141 | "seconds": "Seconds", |
... | ... | @@ -2139,7 +2156,8 @@ |
2139 | 2156 | "date-range": "Date range", |
2140 | 2157 | "last": "Last", |
2141 | 2158 | "time-period": "Time period", |
2142 | - "hide": "Hide" | |
2159 | + "hide": "Hide", | |
2160 | + "interval": "Interval" | |
2143 | 2161 | }, |
2144 | 2162 | "user": { |
2145 | 2163 | "user": "User", | ... | ... |