unit.component.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import {Component, ElementRef, HostListener, OnDestroy, OnInit, QueryList, Renderer2, ViewChildren} from '@angular/core';
  2. import {ActivatedRoute} from '@angular/router';
  3. import {map, tap} from 'rxjs/operators';
  4. import * as moment from 'moment-timezone';
  5. import {GraphLoader} from '../../shared/graph-loading/graphloader';
  6. import {SensorsService} from '../../shared/api/endpoints/services/sensors.service';
  7. import {HttpResponse} from '@angular/common/http';
  8. import {ToastService} from '../../shared/services/toast.service';
  9. import {Sensor} from '../../shared/api/endpoints/models/sensor';
  10. import {ObservationService} from '../../shared/api/endpoints/services/observation.service';
  11. import {SensorType} from '../../shared/api/endpoints/models/sensor-type';
  12. import {Subscription} from 'rxjs';
  13. import type {View} from 'vega';
  14. @Component({
  15. selector: 'app-unit',
  16. templateUrl: './unit.component.html',
  17. styleUrls: ['./unit.component.scss']
  18. })
  19. export class UnitComponent implements OnInit, OnDestroy {
  20. preselectedSensors: string;
  21. unitId: number;
  22. viewCount = 0;
  23. data = [];
  24. time = [];
  25. from: Date = moment().hour(0).minutes(0).subtract(7, 'days').toDate();
  26. to: Date = moment().toDate();
  27. today: Date = moment().toDate();
  28. observationsData: any[] = [];
  29. sensorGroups = [];
  30. selectedSensors: string[] = [];
  31. sensors: Sensor[];
  32. dateChanged = false;
  33. sensorTypes: SensorType[];
  34. unitDescription: string;
  35. subscription: Subscription[] = [];
  36. showIntervalError = false;
  37. // One flag per group
  38. showVega: Record<string, boolean> = {};
  39. showVegaCounter: Record<string, number> = {};
  40. graphViews: Record<string, View> = {};
  41. // Grab *all* containers that match the template ref vegaContainer
  42. @ViewChildren('vegaContainer') vegaContainers!: QueryList<ElementRef>;
  43. constructor(
  44. private activatedRoute: ActivatedRoute,
  45. private sensorService: SensorsService,
  46. private toastService: ToastService,
  47. private observationService: ObservationService,
  48. private route: ActivatedRoute,
  49. private renderer: Renderer2
  50. ) {
  51. this.getInitData();
  52. // get unit sensors and prepare them for view
  53. this.sensorService.getUnitSensors({ unit_id: this.unitId }).pipe(
  54. tap(sens => {
  55. this.sensors = sens;
  56. this.sensors.sort((a, b) => a.sensorId - b.sensorId);
  57. }),
  58. tap(() => {
  59. if (this.sensors && this.sensors.length > 0) {
  60. this.sensors.forEach(sensor => {
  61. const sensorType = sensor.sensorId.toString().slice(0, 5);
  62. if (!this.sensorGroups.some(group => group === sensorType)) { // create sensor groups only for unit sensors
  63. this.sensorGroups.push(sensorType);
  64. setTimeout(() => {
  65. GraphLoader.getGraph(null, null, null, '#vega_container_' + sensor.sensorId.toString().slice(0, 5), null);
  66. }, 0);
  67. }
  68. });
  69. }
  70. })
  71. ).toPromise().then();
  72. }
  73. /**
  74. * Fires on every resize event
  75. */
  76. @HostListener('window:resize', ['$event'])
  77. public onWindowResize(event: UIEvent): void {
  78. this.onResize();
  79. }
  80. private onResize() {
  81. const itemCount = Object.keys(this.graphViews).length;
  82. console.log('Number of entries:', itemCount);
  83. Object.entries(this.graphViews).forEach(([key, view]) => {
  84. // Process each pair here
  85. const sensorGroupElement = '#vega_container_' + key;
  86. const box = document.getElementById('vega_container_' + key);
  87. const boxWidth = box.getBoundingClientRect().width;
  88. const boxHeight = box.getBoundingClientRect().height;
  89. if (view) {
  90. const newWidth = box.getBoundingClientRect().width - 50;
  91. console.log('Key:', key, 'NewWidth:', newWidth);
  92. view.width(newWidth).height(300).runAsync();
  93. }
  94. });
  95. }
  96. /**
  97. * Unsubscribe after leaving
  98. */
  99. ngOnDestroy(): void {
  100. this.subscription.forEach(subs => subs.unsubscribe());
  101. }
  102. /**
  103. * Sets up default data
  104. */
  105. getInitData() {
  106. this.route.queryParams.subscribe(params => {
  107. if (params.unitDescription) {
  108. this.unitDescription = params.unitDescription;
  109. }
  110. });
  111. this.sensorService.getSensorTypes().toPromise().then(types => this.sensorTypes = types);
  112. this.unitId = parseInt(this.activatedRoute.snapshot.paramMap.get('unitId'), 10);
  113. }
  114. ngOnInit(): void {
  115. }
  116. /**
  117. * Shows get data button
  118. */
  119. onDateChanged() {
  120. if (moment(this.to).diff(moment(this.from), 'months') > 6){
  121. this.dateChanged = false;
  122. this.showIntervalError = true;
  123. }
  124. else if (this.to < this.from) {
  125. this.dateChanged = false;
  126. this.showIntervalError = true;
  127. }
  128. else{
  129. this.dateChanged = true;
  130. this.showIntervalError = false;
  131. }
  132. }
  133. /**
  134. * Gets data based on selected time range
  135. */
  136. showGraph(changedDate: boolean = true, changedSensor: string = null) {
  137. const range: Date[] = [this.from, this.to];
  138. this.getObservations(range, changedDate, changedSensor);
  139. }
  140. /**
  141. * Check button handler.
  142. * @param sensorId checked sensorId
  143. * @param event event for getting if checked or unchecked
  144. */
  145. async addSensorToGraph(sensorId: string, event) {
  146. const groupId = sensorId.toString().slice(0, 5);
  147. const sensorGroupElement = '#vega_container_' + groupId;
  148. // if the checkmark is checked then the graph will be displayed
  149. this.graphViews[groupId] = null;
  150. this.showVega[groupId] = false;
  151. if (event.checked.includes(sensorId.toString()))
  152. this.showVega[groupId] = true;
  153. const box = document.getElementById('vega_container_' + groupId);
  154. const boxWidth = box.getBoundingClientRect().width;
  155. const boxHeight = box.getBoundingClientRect().height;
  156. GraphLoader.setSize(boxWidth - 50, 300);
  157. if (!this.selectedSensors.find(sensId => sensId.toString().slice(0, 5) === groupId)) { // if group of sensors is empty show empty graph
  158. // GraphLoader.getAnalyticsGraph(null, null, null, sensorGroupElement);
  159. this.graphViews[groupId] = await GraphLoader.getGraph(null, null, null, sensorGroupElement, null);
  160. } else {
  161. // use observations data
  162. if (event.checked) { // if checked > add to graph
  163. if (this.observationsData.some(sens => sens.sensorId.toString() === sensorId)) { // if already data for selected sensor in memory
  164. this.graphViews[groupId] = await GraphLoader.getGraph(this.filteredSelectedSensors(groupId),
  165. this.filteredObservationData(groupId), this.filteredSensorsInfos(groupId), sensorGroupElement, false);
  166. } else { // get data from server for added sensor and show graph for selected sensors
  167. this.showGraph(false, sensorId);
  168. }
  169. } else { // remove sensor from graph
  170. this.graphViews[groupId] = await GraphLoader.getGraph(this.filteredSelectedSensors(groupId), this.filteredObservationData(groupId),
  171. this.filteredSensorsInfos(groupId), sensorGroupElement, false);
  172. }
  173. }
  174. // no data in graph -> the graph element will be destroyed
  175. if (!this.showVega[groupId]) {
  176. const container = document.getElementById(`vega_container_${groupId}`);
  177. if (container) {
  178. container.innerHTML = '';
  179. this.graphViews[groupId] = null;
  180. }
  181. console.log(`Cleared children of #vega_container_${groupId}`);
  182. }
  183. }
  184. /**
  185. * Filter observations data only fro selected sensors.
  186. * @param sensorGroupId id of changed sensor group
  187. */
  188. filteredObservationData(sensorGroupId: string): any {
  189. return this.observationsData.filter(sen => this.selectedSensors.includes(sen.sensorId.toString()) &&
  190. sen.sensorId.toString().slice(0, 5) === sensorGroupId);
  191. }
  192. /**
  193. * Filter only selected sensors for group of sensors
  194. * @param sensorGroupId group of sensors
  195. */
  196. filteredSelectedSensors(sensorGroupId: string): any {
  197. return this.selectedSensors.filter(sen => sen.toString().slice(0, 5) === sensorGroupId);
  198. }
  199. /**
  200. * Get sensors only for group
  201. * @param sensorGroupId group id
  202. */
  203. filteredSensorsInfos(sensorGroupId: string): any {
  204. return this.sensors.filter(sen => this.selectedSensors.includes(sen.sensorId.toString()) &&
  205. sen.sensorId.toString().slice(0, 5) === sensorGroupId);
  206. }
  207. /**
  208. * Gets data from observation endpoint
  209. * @param range from and to interval
  210. * @param changedDate determines if dates changed so we need refresh all data
  211. * @param changedSensorId if selecting sensor only fetch data for this server
  212. */
  213. getObservations(range: Date[], changedDate: boolean, changedSensorId: string) {
  214. if (changedDate) { // if changed date we need new data for all sensors
  215. this.observationsData = []; // empty observation data
  216. this.selectedSensors.forEach(selectSens => {
  217. this.observationEndpointRequest(selectSens, range);
  218. });
  219. } else { // add data for added sensor
  220. this.observationEndpointRequest(changedSensorId, range);
  221. }
  222. }
  223. /**
  224. * Endpoint request to get observation data for sensor
  225. * @param sensorId sensor id to get data
  226. * @param range from and to interval
  227. */
  228. observationEndpointRequest(sensorId: string, range: Date[]) {
  229. this.observationService.getObservation$Response({
  230. unit_id: this.unitId,
  231. sensor_id: parseInt(sensorId, 10),
  232. from: moment(range[0]).format('yyyy-MM-DD HH:mm:ssZ').slice(0, -3),
  233. to: moment(range[1]).format('yyyy-MM-DD HH:mm:ssZ').slice(0, -3)
  234. }).pipe(
  235. map((response: HttpResponse<any>) => {
  236. if (response.status === 200) {
  237. return response.body;
  238. } else if (response.status === 204) {
  239. this.toastService.showWarningNoData();
  240. return response.body;
  241. } else {
  242. return false;
  243. }
  244. })
  245. ).subscribe(
  246. async observations => {
  247. if (observations) {
  248. const groupId = sensorId.toString().slice(0, 5);
  249. this.observationsData.push({
  250. sensorId, sensor:
  251. this.sensors.find(sens => sens.sensorId.toString() === sensorId.toString()), data: observations
  252. });
  253. const view = '#vega_container_' + sensorId.toString().slice(0, 5);
  254. this.graphViews[groupId] = await GraphLoader.getGraph(this.filteredSelectedSensors(groupId),
  255. this.filteredObservationData(groupId), this.filteredSensorsInfos(groupId), view, false);
  256. }
  257. }, err => this.toastService.showError(err.error.message));
  258. }
  259. }