You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

221 lines
6.8KB

  1. import {
  2. AfterViewInit,
  3. Component,
  4. OnDestroy,
  5. OnInit,
  6. Renderer2,
  7. } from '@angular/core';
  8. import * as L from 'leaflet';
  9. import 'leaflet.markercluster';
  10. import { Subscription, take } from 'rxjs';
  11. import { SocketService } from '../../../shared/services/socket.service';
  12. import { MatDialog } from '@angular/material/dialog';
  13. import { AlarmSoundService } from '../../../shared/services/alarm-sound.service';
  14. import { CameraDialogComponent } from '../../../shared/component/camera-dialog/camera-dialog.component';
  15. import { alarmData, alarmDemo } from '../data/fake-data';
  16. import { ConfirmDialogService } from '../../../shared/services/confirm-dialog.service';
  17. @Component({
  18. selector: 'app-centralized-security-management',
  19. templateUrl: './centralized-security-management.component.html',
  20. styleUrls: ['./centralized-security-management.component.scss'],
  21. })
  22. export class CentralizedSecurityManagementComponent
  23. implements OnInit, AfterViewInit, OnDestroy
  24. {
  25. private map!: L.Map;
  26. private markers!: L.MarkerClusterGroup;
  27. private statusSubscription?: Subscription;
  28. private messageSubscription?: Subscription;
  29. data = alarmData;
  30. alarmDemo = alarmDemo;
  31. state1 = false;
  32. state2 = false;
  33. state5 = false;
  34. state6 = false;
  35. isReady = true;
  36. constructor(
  37. private socketService$: SocketService,
  38. private dialog: MatDialog,
  39. private renderer: Renderer2,
  40. private alarmSoundService$: AlarmSoundService,
  41. private confirmDialogService$: ConfirmDialogService,
  42. ) {}
  43. ngOnInit() {
  44. this.statusSubscription = this.socketService$.status$.subscribe(
  45. (isConnected) => {
  46. if (isConnected) {
  47. this.socketService$.sendMessage({ id: '0', type: 'get' });
  48. this.messageSubscription = this.socketService$.messages$.subscribe(
  49. (message) => {
  50. this.onMessage(message);
  51. },
  52. );
  53. }
  54. },
  55. );
  56. }
  57. openDialog(): void {
  58. this.dialog.open(CameraDialogComponent, {
  59. width: '80vw',
  60. data: '',
  61. });
  62. }
  63. ngAfterViewInit(): void {
  64. this.initMap();
  65. }
  66. ngOnDestroy(): void {
  67. this.statusSubscription?.unsubscribe();
  68. this.messageSubscription?.unsubscribe();
  69. this.socketService$.close();
  70. this.alarmSoundService$.stopAlarm();
  71. }
  72. initMap(): void {
  73. const mapContainer = document.getElementById('map');
  74. if (mapContainer) {
  75. this.map = L.map('map', {
  76. center: [10.7483, 106.7537],
  77. zoom: 12,
  78. });
  79. L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  80. maxZoom: 15,
  81. minZoom: 3,
  82. attribution:
  83. '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
  84. }).addTo(this.map);
  85. this.addIconsToMap();
  86. } else {
  87. console.error('Map container not found');
  88. }
  89. }
  90. onMessage(message: any) {
  91. if (message.id == '0' && message.type === 'get') {
  92. this.state1 = message.state1 === '0'; // 1 OFF // alarm 12h
  93. this.state2 = message.state2 === '0'; // 1 OFF // alarm 1h
  94. this.state5 = message.state5 === '1'; // 1 ON, 0 OFF
  95. this.isReady = message.ready === '1';
  96. // this.alarmSoundService$.startAlarm(true, true, true, this.state2);
  97. this.updateIcons();
  98. }
  99. }
  100. addIconsToMap(): void {
  101. this.markers = L.markerClusterGroup();
  102. this.data.forEach((item) => this.addMarker(item));
  103. this.addMarker(this.alarmDemo, true);
  104. this.map.addLayer(this.markers);
  105. }
  106. addMarker(item: any, isDemo: boolean = false): void {
  107. const icon = isDemo
  108. ? this.getIcon(this.state5, this.isReady, this.state1, this.state2)
  109. : this.createIcon(item.warning);
  110. const marker = L.marker(
  111. [item.detail.coordinates.lat, item.detail.coordinates.lng],
  112. { icon },
  113. ).bindPopup(this.popupDetail(item));
  114. marker.on('popupopen', (event) => this.bindPopupEvents(event));
  115. this.markers.addLayer(marker);
  116. }
  117. bindPopupEvents(event: any): void {
  118. const popupContainer = event.popup.getElement();
  119. const button = popupContainer?.querySelector('.dynamic-button');
  120. if (button) {
  121. this.renderer.listen(button, 'click', () => this.openDialog());
  122. }
  123. }
  124. updateIcons(): void {
  125. this.markers.clearLayers();
  126. this.addIconsToMap();
  127. }
  128. popupDetail(item: any): string {
  129. return `
  130. <div class="tooltip" style="width: 200px">
  131. <div style="display: flex; flex-direction: row; justify-content: space-between; margin-bottom: 5px">
  132. <div style="color: #F33152; padding: 5px 13px; background-color: rgba(243, 49, 82, 0.1); border-radius: 20px; text-align: center; max-width: 180px;">${item.title}</div>
  133. <a class="dynamic-button">
  134. <svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none">
  135. <g stroke="currentColor" stroke-width="2">
  136. <path d="M16 16V8a1 1 0 00-1-1H5a1 1 0 00-1 1v8a1 1 0 001 1h10a1 1 0 001-1z"/>
  137. <path stroke-linejoin="round" d="M20 7l-4 3v4l4 3V7z"/>
  138. </g>
  139. </svg>
  140. </a>
  141. </div>
  142. <div><strong>Địa điểm:</strong> ${item.detail.position}</div>
  143. <div><strong>Tọa độ:</strong> ${item.detail.coordinates.lat}, ${item.detail.coordinates.lng}</div>
  144. <div><strong>Thời gian:</strong> ${item.detail.time}</div>
  145. <a href="/overview/overall-ground" target="_blank">Xem chi tiết</a>
  146. </div>`;
  147. }
  148. createIcon(
  149. active: boolean,
  150. className: string = '',
  151. text: any = [],
  152. ): L.Icon<L.IconOptions> | L.DivIcon {
  153. if (text.length < 1) {
  154. return L.icon({
  155. iconUrl: active
  156. ? '../../../../assets/images/sensor-on.png'
  157. : '../../../../assets/images/sensor-off.png',
  158. iconSize: [30, 30],
  159. className: className,
  160. });
  161. } else {
  162. let htmlContent = '';
  163. text.forEach((item: any) => {
  164. htmlContent += `<span><b>${item}</b></span><br>`;
  165. });
  166. return L.divIcon({
  167. html: `<div class="icon">
  168. <div style="z-index:9999" class="sensor-on">
  169. <img alt="icon-alarm" src="assets/images/sensor-on.png" style="width: 30px; height: 30px">
  170. </div>
  171. <div style="background: #F11E1E; color: #FFF; padding: 2px 3px; font-size: 11px; margin-top: 10px; text-align: center">
  172. ${htmlContent}
  173. </div>
  174. </div>`,
  175. iconSize: [200, 30],
  176. className: className,
  177. });
  178. }
  179. }
  180. getIcon(
  181. isTurnOn: boolean,
  182. isReady: boolean,
  183. fireArm: boolean,
  184. fenceArm: boolean,
  185. ): L.Icon<L.IconOptions> | L.DivIcon {
  186. if (isTurnOn && isReady) {
  187. let text = [];
  188. if (fireArm && fenceArm) {
  189. text.push('FIRE ALARM', 'FENCE ALARM');
  190. } else if (fireArm) {
  191. text.push('FIRE ALARM');
  192. } else if (fenceArm) {
  193. text.push('FENCE ALARM');
  194. }
  195. return this.createIcon(true, '', text);
  196. }
  197. return this.createIcon(false);
  198. }
  199. }