| export const ICON = { | |||||
| sensorOff: `<div style="display: flex; flex-direction: row; gap: 5px"> | |||||
| <img style="width: 30px; height: 30px;" src="assets/images/sensor-off.png"> | |||||
| <img style="width: 30px; height: 30px;" src="assets/images/camera.png"> | |||||
| </div>`, | |||||
| sensorOn: `<div style="display: flex; flex-direction: row; gap: 5px"> | |||||
| <img style="width: 30px; height: 30px;" src="assets/images/sensor-on.png"> | |||||
| <img style="width: 30px; height: 30px;" src="assets/images/camera.png"> | |||||
| </div>`, | |||||
| sensorActive: `<div> | |||||
| <div style="width: 30px; height: 30px; position: absolute; display: flex; justify-content: center; align-items: center; border-radius: 50%; background: linear-gradient(#ff0000, #C70039);"> | |||||
| <div style="position: absolute !important; width: 100%; height: 100%; background: #ff0000; border-radius: 50%; z-index: 1; animation: sensor-on 2s ease-out infinite;"></div> | |||||
| <div style="position: absolute !important; width: 100%; height: 100%; background: #ff0000; border-radius: 50%; z-index: 1; animation: sensor-on 2s ease-out infinite; animation-delay: 1s;"></div> | |||||
| <img src="assets/images/sensor-on.png" style="width: 30px; height: 30px; z-index: 9;"> | |||||
| </div> | |||||
| <img style="width: 30px; height: 30px;margin-left: 40px;" src="assets/images/camera.png"> | |||||
| <style> | |||||
| @keyframes sensor-on { | |||||
| 100% { | |||||
| transform: scale(2); | |||||
| opacity: 0; | |||||
| } | |||||
| } | |||||
| </style> | |||||
| </div>`, | |||||
| fireContent: `<div style="display: flex; | |||||
| flex-direction: row; | |||||
| justify-content: space-between;"> | |||||
| <div style="color: #F33152; | |||||
| padding: 5px 13px; | |||||
| background-color: rgba(243, 49, 82, 0.1); | |||||
| border-radius: 20px; | |||||
| text-align: center; | |||||
| width: 100px;">Sự cố cháy</div> | |||||
| <a href="/overview/camera-stream"> | |||||
| <svg width="24px" height="24px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none"> | |||||
| <g stroke="currentColor" stroke-width="2"> | |||||
| <path d="M16 16V8a1 1 0 00-1-1H5a1 1 0 00-1 1v8a1 1 0 001 1h10a1 1 0 001-1z"/> | |||||
| <path stroke-linejoin="round" d="M20 7l-4 3v4l4 3V7z"/> | |||||
| </g> | |||||
| </svg> | |||||
| </a> | |||||
| </div> | |||||
| <div><strong>Địa điểm:</strong> 120 Xa Lộ Hà Nội, Thành Phố, Thủ Đức, Thành phố Hồ Chí Minh</div> | |||||
| <div><strong>Tọa độ:</strong> 10.8661° N, 106.8029° E</div> | |||||
| <div><strong>Thời gian:</strong> 01:54, 16/05/2022</div> | |||||
| <a href="/overview/overall-ground" target="_blank">Xem chi tiết</a>`, | |||||
| sensorActiveBK: ` | |||||
| <div style="width: 30px; height: 30px; position: absolute; display: flex; justify-content: center; align-items: center; border-radius: 50%; background: linear-gradient(#ff0000, #C70039);"> | |||||
| <div style="position: absolute !important; width: 100%; height: 100%; background: #ff0000; border-radius: 50%; z-index: 1; animation: sensor-on 2s ease-out infinite;"></div> | |||||
| <div style="position: absolute !important; width: 100%; height: 100%; background: #ff0000; border-radius: 50%; z-index: 1; animation: sensor-on 2s ease-out infinite; animation-delay: 1s;"></div> | |||||
| <img src="assets/images/sensor-on.png" style="width: 30px; height: 30px; z-index: 9;"> | |||||
| </div> | |||||
| <style> | |||||
| @keyframes sensor-on { | |||||
| 100% { | |||||
| transform: scale(2); | |||||
| opacity: 0; | |||||
| } | |||||
| } | |||||
| </style> | |||||
| `, | |||||
| } |
| <h3>Camera Stream</h3> | |||||
| <canvas #videoPlayer></canvas> | |||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { CameraStreamComponent } from './camera-stream.component'; | |||||
| describe('CameraStreamComponent', () => { | |||||
| let component: CameraStreamComponent; | |||||
| let fixture: ComponentFixture<CameraStreamComponent>; | |||||
| beforeEach(() => { | |||||
| TestBed.configureTestingModule({ | |||||
| declarations: [CameraStreamComponent] | |||||
| }); | |||||
| fixture = TestBed.createComponent(CameraStreamComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); |
| import {Component, OnInit, ViewChild, ElementRef, AfterViewInit, Renderer2} from '@angular/core'; | |||||
| import {loadPlayer, Player} from "rtsp-relay/browser"; | |||||
| @Component({ | |||||
| selector: 'app-camera-stream', | |||||
| templateUrl: './camera-stream.component.html', | |||||
| styleUrls: ['./camera-stream.component.scss'] | |||||
| }) | |||||
| export class CameraStreamComponent implements OnInit, AfterViewInit{ | |||||
| player?: Player; | |||||
| @ViewChild('videoPlayer') | |||||
| videoPlayer?: ElementRef<HTMLCanvasElement>; | |||||
| ngOnInit() { | |||||
| } | |||||
| constructor(private el: ElementRef, private renderer: Renderer2) {} | |||||
| async ngAfterViewInit() { | |||||
| this.player = await loadPlayer({ | |||||
| url: 'ws://localhost:8081', | |||||
| canvas: this.videoPlayer!.nativeElement, | |||||
| onDisconnect: () => console.log('Connection lost!'), | |||||
| }); | |||||
| console.log('Connected!', this.player); | |||||
| } | |||||
| } |
| <div class="map-container"> | <div class="map-container"> | ||||
| <h2>ATMs Security Monitoring and Control</h2> | |||||
| <div class="map-frame"> | <div class="map-frame"> | ||||
| <div id="map"></div> <!-- Ensure no extra space after "map" --> | |||||
| <div id="map"></div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| .map-frame { | .map-frame { | ||||
| border: 2px solid black; | border: 2px solid black; | ||||
| height: 100%; | |||||
| height: 90%; | |||||
| } | } | ||||
| #map { | #map { | ||||
| padding: 3px 2px; | padding: 3px 2px; | ||||
| background-color: rbg(243,49,82,0.1); | background-color: rbg(243,49,82,0.1); | ||||
| } | } | ||||
| .hidden-background{ | |||||
| background: transparent; | |||||
| border: none; | |||||
| } |
| import {AfterViewInit, Component, OnDestroy, OnInit} from '@angular/core'; | |||||
| import { AfterViewInit, Component, OnDestroy, OnInit } from '@angular/core'; | |||||
| import * as L from 'leaflet'; | import * as L from 'leaflet'; | ||||
| import {Subscription} from "rxjs"; | |||||
| import {SocketService} from "../../../shared/services/socket.service"; | |||||
| import { Subscription } from 'rxjs'; | |||||
| import { SocketService } from '../../../shared/services/socket.service'; | |||||
| import {ICON} from "../../../app.constants"; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-map', | selector: 'app-map', | ||||
| styleUrls: ['./map.component.scss'] | styleUrls: ['./map.component.scss'] | ||||
| }) | }) | ||||
| export class MapComponent implements OnInit, AfterViewInit, OnDestroy { | export class MapComponent implements OnInit, AfterViewInit, OnDestroy { | ||||
| private map: any; | |||||
| houseIcon = L.icon({ | |||||
| iconUrl: '../../../../assets/images/icon.png', | |||||
| iconSize: [38, 48], | |||||
| shadowSize: [50, 64], | |||||
| }); | |||||
| fireIcon = L.icon({ | |||||
| iconUrl: '../../../../assets/images/fire.gif', | |||||
| iconSize: [38, 48], | |||||
| }) | |||||
| sensorOnIcon = L.divIcon({ | |||||
| className: '', | |||||
| html:'<div style="width: 30px; height: 30px; position: absolute; display: flex; justify-content: center; align-items: center; border-radius: 50%; background: linear-gradient(#ff0000, #C70039);">'+ | |||||
| ' <div style="position: absolute !important; width: 100%; height: 100%; background: #ff0000; border-radius: 50%; z-index: 1; animation: sensor-on 2s ease-out infinite;"></div>'+ | |||||
| ' <div style="position: absolute !important; width: 100%; height: 100%; background: #ff0000; border-radius: 50%; z-index: 1; animation: sensor-on 2s ease-out infinite; animation-delay: 1s;"></div>'+ | |||||
| ' <img src="assets/images/sensor-on.png" style="width: 30px; height: 30px; z-index: 9;">'+ | |||||
| '</div>'+ | |||||
| '<style>'+ | |||||
| '@keyframes sensor-on {'+ | |||||
| ' 100% {'+ | |||||
| ' transform: scale(2);'+ | |||||
| ' opacity: 0;'+ | |||||
| ' }'+ | |||||
| '}'+ | |||||
| '</style>', | |||||
| iconSize: [38, 48] | |||||
| private map!: L.Map; | |||||
| private featureGroup!: L.FeatureGroup; | |||||
| private statusSubscription?: Subscription; | |||||
| private messageSubscription?: Subscription; | |||||
| sensorOff = ICON.sensorOff; | |||||
| sensorOn = ICON.sensorOn; | |||||
| sensorActive = ICON.sensorActiveBK; | |||||
| sensorActiveIcon = L.divIcon({ | |||||
| html: this.sensorActive, | |||||
| iconSize: [38, 48], | |||||
| className: 'hidden-background' | |||||
| }); | }); | ||||
| // | |||||
| // sensorOffIcon = L.divIcon({ | |||||
| // html: this.sensorOff, | |||||
| // iconSize: [38, 38], | |||||
| // className: 'hidden-background' | |||||
| // }); | |||||
| // sensorOnIcon = L.divIcon({ | |||||
| // html: this.sensorOn, | |||||
| // iconSize: [38, 38], | |||||
| // className: 'hidden-background' | |||||
| // }); | |||||
| sensorOffIcon = L.icon({ | sensorOffIcon = L.icon({ | ||||
| iconUrl: '../../../../assets/images/sensor-off.png', | iconUrl: '../../../../assets/images/sensor-off.png', | ||||
| iconSize: [38, 38], | iconSize: [38, 38], | ||||
| }) | |||||
| }); | |||||
| state5 = false; | |||||
| state6 = false; | |||||
| sensorOnIcon = L.icon({ | |||||
| iconUrl: '../../../../assets/images/sensor-on.png', | |||||
| iconSize: [38, 38], | |||||
| }); | |||||
| private statusSubscription?: Subscription; | |||||
| private messageSubscription?: Subscription; | |||||
| constructor(private socketService$: SocketService) {} | |||||
| state5 = false; | |||||
| state6 = false; | |||||
| constructor(private socketService$: SocketService) { } | |||||
| ngOnInit() { | ngOnInit() { | ||||
| this.statusSubscription = this.socketService$.status$.subscribe(isConnected => { | this.statusSubscription = this.socketService$.status$.subscribe(isConnected => { | ||||
| if (isConnected) { | if (isConnected) { | ||||
| this.messageSubscription = this.socketService$.messages$.subscribe(message => { | |||||
| this.messageSubscription = this.socketService$.messages$.subscribe(message => { | |||||
| this.onMessage(message); | this.onMessage(message); | ||||
| }); | }); | ||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| ngOnDestroy(): void { | |||||
| if (this.statusSubscription) { | |||||
| this.statusSubscription.unsubscribe(); | |||||
| } | |||||
| if (this.messageSubscription) { | |||||
| this.messageSubscription.unsubscribe(); | |||||
| } | |||||
| this.socketService$.close(); | |||||
| } | |||||
| onMessage(message: any) { | |||||
| if (message.id == '0' && message.type === 'get') { | |||||
| this.state5 = message.state5 === '1'; | |||||
| this.state6 = message.state6 === '1' ; | |||||
| this.updateIcons(); | |||||
| } | |||||
| } | |||||
| ngAfterViewInit(): void { | ngAfterViewInit(): void { | ||||
| this.initMap(); | this.initMap(); | ||||
| } | } | ||||
| ngOnDestroy(): void { | |||||
| this.statusSubscription?.unsubscribe(); | |||||
| this.messageSubscription?.unsubscribe(); | |||||
| this.socketService$.close(); | |||||
| } | |||||
| private initMap(): void { | private initMap(): void { | ||||
| const mapContainer = document.getElementById('map'); | const mapContainer = document.getElementById('map'); | ||||
| if (mapContainer) { | if (mapContainer) { | ||||
| this.map = L.map('map', { | this.map = L.map('map', { | ||||
| center: [10.8494, 106.7537 ], | |||||
| center: [10.8494, 106.7537], | |||||
| zoom: 12 | zoom: 12 | ||||
| }); | }); | ||||
| const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |||||
| L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |||||
| maxZoom: 18, | maxZoom: 18, | ||||
| minZoom: 3, | minZoom: 3, | ||||
| attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' | attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>' | ||||
| }); | |||||
| }).addTo(this.map); | |||||
| tiles.addTo(this.map); | |||||
| this.addIconToMap() | |||||
| this.featureGroup = L.featureGroup().addTo(this.map); | |||||
| this.addIconToMap(); | |||||
| } else { | } else { | ||||
| console.error('Map container not found'); | console.error('Map container not found'); | ||||
| } | } | ||||
| } | } | ||||
| onMessage(message: any) { | |||||
| if (message.id == '0' && message.type === 'get') { | |||||
| this.state5 = message.state5 === '1'; | |||||
| this.state6 = message.state6 === '1'; | |||||
| this.updateIcons(); | |||||
| } | |||||
| } | |||||
| addIconToMap() { | addIconToMap() { | ||||
| //add marker | |||||
| let popupContent = `<div>Vinhome quận 9</div> | |||||
| <a href="/overview/overall-ground" target="_blank">Xem chi tiết</a>`; | |||||
| let fireContent = `<div style="color: #F33152; | |||||
| padding: 2px 13px; | |||||
| background-color: rgba(243, 49, 82, 0.1); | |||||
| border-radius: 20px; | |||||
| text-align: center; | |||||
| width: 100px;"> | |||||
| Sự cố cháy | |||||
| </div> | |||||
| <div><strong>Địa điểm:</strong> 120 Xa Lộ Hà Nội, Thành Phố, Thủ Đức, Thành phố Hồ Chí Minh</div> | |||||
| <div><strong>Tọa độ:</strong> 10.8661° N, 106.8029° E</div> | |||||
| <div><strong>Thời gian:</strong> 01:54, 16/05/2022</div> | |||||
| <a href="/overview/overall-ground" target="_blank">Xem chi tiết</a>` | |||||
| L.marker([10.8356, 106.8300], {icon: this.state5 ? this.sensorOnIcon : this.sensorOffIcon}) | |||||
| const popupContent = `<div>Vinhome quận 9</div> | |||||
| <a href="/overview/overall-ground" target="_blank">Xem chi tiết</a>`; | |||||
| const fireContent = ICON.fireContent; | |||||
| L.marker([10.8356, 106.8300], {icon: this.applyIcon(this.state5, this.state6)}) | |||||
| .addTo(this.map) | .addTo(this.map) | ||||
| .bindPopup(popupContent); | .bindPopup(popupContent); | ||||
| L.marker([10.8661, 106.8029], {icon: this.state6 ? this.sensorOnIcon : this.sensorOffIcon}) | |||||
| L.marker([10.8661, 106.8029], {icon: this.applyIcon(this.state5, this.state6)}) | |||||
| .addTo(this.map) | .addTo(this.map) | ||||
| .bindPopup(fireContent); | .bindPopup(fireContent); | ||||
| } | } | ||||
| this.addIconToMap(); | this.addIconToMap(); | ||||
| } | } | ||||
| applyIcon(state1: boolean, state2: boolean): L.Icon | L.DivIcon { | |||||
| if (state1 && state2) { | |||||
| return this.sensorActiveIcon; | |||||
| } | |||||
| if (state1) { | |||||
| return this.sensorOnIcon; | |||||
| } | |||||
| return this.sensorOffIcon; | |||||
| } | |||||
| } | } |
| <div class="card-state"> | <div class="card-state"> | ||||
| <img src="assets/images/ground.png"> | <img src="assets/images/ground.png"> | ||||
| <p class="state t1" id="State1" [ngClass]="{'sensor-on': state1 === 'ON'}"> | |||||
| <img [src]="getImageSource(state1)" style="width: 30px; height: 30px"> | |||||
| </p> | |||||
| <p class="state t2" id="State2" [ngClass]="{'sensor-on': state2 === 'ON'}"> | |||||
| <img [src]="getImageSource(state2)" style="width: 30px; height: 30px"> | |||||
| </p> | |||||
| <p class="state t3" id="State3" [ngClass]="{'sensor-on': state3 === 'ON'}"> | |||||
| <img [src]="getImageSource(state3)" style="width: 30px; height: 30px"> | |||||
| </p> | |||||
| <p class="state t4" id="State4" [ngClass]="{'sensor-on': state4 === 'ON'}"> | |||||
| <img [src]="getImageSource(state4)" style="width: 30px; height: 30px"> | |||||
| </p> | |||||
| <p class="state t5" id="State5" [ngClass]="{'sensor-on': state5 === 'ON'}"> | |||||
| <img [src]="getImageSource(state5)" style="width: 30px; height: 30px"> | |||||
| </p> | |||||
| <p class="state t6" id="State6" [ngClass]="{'sensor-on': state6 === 'ON'}"> | |||||
| <img [src]="getImageSource(state6)" style="width: 30px; height: 30px"> | |||||
| </p> | |||||
| <div *ngFor="let state of states; let i = index" | |||||
| [ngClass]="{'sensor-on': Sstate1 && Sstate2}" | |||||
| class="state t{{i + 1}}" | |||||
| id="State{{i + 1}}"> | |||||
| <img [src]="getImageSource()" style="width: 30px; height: 30px"> | |||||
| </div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div fxFlex="30" fxLayout="column" fxLayoutGap="50px"> | <div fxFlex="30" fxLayout="column" fxLayoutGap="50px"> | ||||
| <div>Công tắc 1 <button [disabled]="!isConnected" mat-flat-button color="{{Sstate1 ? 'accent' : 'primary'}}" (click)="toggleState1()">{{ Sstate1 ? 'ON' : 'OFF'}}</button></div> | |||||
| <div>Công tắc 2 <button [disabled]="!isConnected" mat-flat-button color="{{Sstate2 ? 'accent' : 'primary'}}" (click)="toggleState2()">{{ Sstate2 ? 'ON' : 'OFF'}}</button></div> | |||||
| <div>Công tắc 1 <button [disabled]="!isConnected" mat-flat-button color="{{Sstate1 ? 'accent' : 'primary'}}" (click)="toggleState1()">{{ Sstate1 ? 'DISARM' : 'ARM'}}</button></div> | |||||
| <div>Công tắc 2 <button [disabled]="!isConnected" mat-flat-button color="{{Sstate2 ? 'accent' : 'primary'}}" (click)="toggleState2()">{{ Sstate2 ? 'TẮT CẢNH BÁO' : 'BẬT CẢNH BÁO'}}</button></div> | |||||
| <div>Live Camera <button mat-stroked-button color="primary" [routerLink]="'/overview/camera-stream'"> Xem chi tiết </button></div> | |||||
| </div> | </div> | ||||
| </div> | </div> |
| import {ChangeDetectorRef, Component, OnChanges, OnDestroy, OnInit, SimpleChanges} from '@angular/core'; | import {ChangeDetectorRef, Component, OnChanges, OnDestroy, OnInit, SimpleChanges} from '@angular/core'; | ||||
| import {SocketService} from "../../../shared/services/socket.service"; | import {SocketService} from "../../../shared/services/socket.service"; | ||||
| import {Subscription} from "rxjs"; | import {Subscription} from "rxjs"; | ||||
| import * as L from "leaflet"; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-overall-ground', | |||||
| templateUrl: './overall-ground.component.html', | |||||
| styleUrls: ['./overall-ground.component.scss'] | |||||
| selector: 'app-overall-ground', | |||||
| templateUrl: './overall-ground.component.html', | |||||
| styleUrls: ['./overall-ground.component.scss'] | |||||
| }) | }) | ||||
| export class OverallGroundComponent implements OnInit, OnDestroy{ | |||||
| isClicked = false; | |||||
| isConnected = false; | |||||
| state1 = ''; | |||||
| state2 = ''; | |||||
| state3 = ''; | |||||
| state4 = ''; | |||||
| state5 = ''; | |||||
| state6 = ''; | |||||
| Sstate1 = false; | |||||
| Sstate2 = false; | |||||
| private statusSubscription?: Subscription; | |||||
| private messageSubscription?: Subscription; | |||||
| private intervalId: any; | |||||
| export class OverallGroundComponent implements OnInit, OnDestroy { | |||||
| isClicked = false; | |||||
| isConnected = false; | |||||
| states = Array(6).fill(0); | |||||
| state1 = ''; | |||||
| state2 = ''; | |||||
| state3 = ''; | |||||
| state4 = ''; | |||||
| state5 = ''; | |||||
| state6 = ''; | |||||
| Sstate1 = false; | |||||
| Sstate2 = false; | |||||
| imageIcon = 'assets/images/sensor-off.png'; | |||||
| private statusSubscription?: Subscription; | |||||
| private messageSubscription?: Subscription; | |||||
| private intervalId: any; | |||||
| constructor(private socketService$: SocketService) { | |||||
| } | |||||
| constructor(private socketService$: SocketService) { | |||||
| } | |||||
| ngOnInit() { | |||||
| this.socketService$.connect(); | |||||
| this.statusSubscription = this.socketService$.status$.subscribe(isConnected => { | |||||
| this.isConnected = isConnected; | |||||
| if (this.isConnected) { | |||||
| this.intervalId = setInterval(() => this.getReadings(), 2000); | |||||
| this.messageSubscription = this.socketService$.messages$.subscribe(message => { | |||||
| this.onMessage(message); | |||||
| }); | |||||
| } | |||||
| ngOnInit() { | |||||
| // this.socketService$.connect(); | |||||
| this.statusSubscription = this.socketService$.status$.subscribe(isConnected => { | |||||
| this.isConnected = isConnected; | |||||
| if (this.isConnected) { | |||||
| this.intervalId = setInterval(() => this.getReadings(), 2000); | |||||
| this.messageSubscription = this.socketService$.messages$.subscribe(message => { | |||||
| this.onMessage(message); | |||||
| }); | }); | ||||
| } | |||||
| } | |||||
| }); | |||||
| } | |||||
| toggleColor(): void { | |||||
| this.isClicked = !this.isClicked; | |||||
| } | |||||
| toggleColor(): void { | |||||
| this.isClicked = !this.isClicked; | |||||
| } | |||||
| ngOnDestroy(): void { | |||||
| if (this.statusSubscription) { | |||||
| this.statusSubscription.unsubscribe(); | |||||
| } | |||||
| if (this.messageSubscription) { | |||||
| this.messageSubscription.unsubscribe(); | |||||
| } | |||||
| if (this.intervalId) { | |||||
| clearInterval(this.intervalId); | |||||
| } | |||||
| this.socketService$.close(); | |||||
| ngOnDestroy(): void { | |||||
| if (this.statusSubscription) { | |||||
| this.statusSubscription.unsubscribe(); | |||||
| } | } | ||||
| getReadings(){ | |||||
| let str = { id: '0', type: 'get' }; | |||||
| this.socketService$.sendMessage(str); | |||||
| if (this.messageSubscription) { | |||||
| this.messageSubscription.unsubscribe(); | |||||
| } | } | ||||
| toggleState1() { | |||||
| this.Sstate1 = !this.Sstate1; | |||||
| let str = { id: '0', type: 'cmd', state1: this.Sstate1.toString() }; | |||||
| this.socketService$.sendMessage(str); | |||||
| if (this.intervalId) { | |||||
| clearInterval(this.intervalId); | |||||
| } | } | ||||
| this.socketService$.close(); | |||||
| } | |||||
| toggleState2() { | |||||
| this.Sstate2 = !this.Sstate2; | |||||
| let str = { id: '0', type: 'cmd', state2: this.Sstate2.toString() }; | |||||
| this.socketService$.sendMessage(str); | |||||
| } | |||||
| getReadings() { | |||||
| let str = {id: '0', type: 'get'}; | |||||
| this.socketService$.sendMessage(str); | |||||
| } | |||||
| getImageSource(state: string): string { | |||||
| return state === 'ON' ? 'assets/images/sensor-on.png' : 'assets/images/sensor-off.png'; | |||||
| } | |||||
| onMessage(message: any) { | |||||
| if (message.id == '0' && message.type === 'get') { | |||||
| this.state1 = message.state1 === '1' ? 'ON' : 'OFF'; | |||||
| this.state2 = message.state2 === '1' ? 'ON' : 'OFF'; | |||||
| this.state3 = message.state3 === '1' ? 'ON' : 'OFF'; | |||||
| this.state4 = message.state4 === '1' ? 'ON' : 'OFF'; | |||||
| this.state5 = message.state5 === '1' ? 'ON' : 'OFF'; | |||||
| this.state6 = message.state6 === '1' ? 'ON' : 'OFF'; | |||||
| } | |||||
| this.Sstate1 = this.state5 === 'ON'; | |||||
| this.Sstate2 = this.state6 === 'ON'; | |||||
| toggleState1() { | |||||
| this.Sstate1 = !this.Sstate1; | |||||
| let str = {id: '0', type: 'cmd', state1: this.Sstate1.toString()}; | |||||
| this.socketService$.sendMessage(str); | |||||
| } | |||||
| toggleState2() { | |||||
| this.Sstate2 = !this.Sstate2; | |||||
| let str = {id: '0', type: 'cmd', state2: this.Sstate2.toString()}; | |||||
| this.socketService$.sendMessage(str); | |||||
| } | |||||
| getImageSource(): string { | |||||
| return (this.Sstate1 && this.Sstate2) || this.Sstate1 ? 'assets/images/sensor-on.png' : 'assets/images/sensor-off.png'; | |||||
| } | |||||
| onMessage(message: any) { | |||||
| if (message.id == '0' && message.type === 'get') { | |||||
| this.state1 = message.state1 === '1' ? 'ON' : 'OFF'; | |||||
| this.state2 = message.state2 === '1' ? 'ON' : 'OFF'; | |||||
| this.state3 = message.state3 === '1' ? 'ON' : 'OFF'; | |||||
| this.state4 = message.state4 === '1' ? 'ON' : 'OFF'; | |||||
| this.state5 = message.state5 === '1' ? 'ON' : 'OFF'; | |||||
| this.state6 = message.state6 === '1' ? 'ON' : 'OFF'; | |||||
| } | } | ||||
| this.Sstate1 = this.state5 === 'ON'; | |||||
| this.Sstate2 = this.state6 === 'ON'; | |||||
| } | |||||
| } | } |
| import {MapComponent} from "./map/map.component"; | import {MapComponent} from "./map/map.component"; | ||||
| import {SharedMaterialModule} from "../../shared/shared-material.module"; | import {SharedMaterialModule} from "../../shared/shared-material.module"; | ||||
| import {MatIconModule} from "@angular/material/icon"; | import {MatIconModule} from "@angular/material/icon"; | ||||
| import { CameraStreamComponent } from './camera-stream/camera-stream.component'; | |||||
| import {FormsModule} from "@angular/forms"; | |||||
| @NgModule({ | @NgModule({ | ||||
| declarations: [ | declarations: [ | ||||
| OverallGroundComponent, | OverallGroundComponent, | ||||
| MapComponent | |||||
| MapComponent, | |||||
| CameraStreamComponent | |||||
| ], | ], | ||||
| imports: [ | imports: [ | ||||
| CommonModule, | CommonModule, | ||||
| RouterModule.forChild(overviewRoutes), | RouterModule.forChild(overviewRoutes), | ||||
| SharedMaterialModule, | SharedMaterialModule, | ||||
| MatIconModule | |||||
| ] | ] | ||||
| }) | }) | ||||
| export class OverviewModule { } | export class OverviewModule { } |
| import {Routes} from "@angular/router"; | import {Routes} from "@angular/router"; | ||||
| import {MapComponent} from "./map/map.component"; | import {MapComponent} from "./map/map.component"; | ||||
| import {OverallGroundComponent} from "./overall-ground/overall-ground.component"; | import {OverallGroundComponent} from "./overall-ground/overall-ground.component"; | ||||
| import {CameraStreamComponent} from "./camera-stream/camera-stream.component"; | |||||
| export const overviewRoutes: Routes = [ | export const overviewRoutes: Routes = [ | ||||
| { | { | ||||
| { | { | ||||
| path: 'overall-ground', | path: 'overall-ground', | ||||
| component: OverallGroundComponent | component: OverallGroundComponent | ||||
| }, | |||||
| { | |||||
| path: 'camera-stream', | |||||
| component: CameraStreamComponent | |||||
| } | } | ||||
| ]; | ]; |
| import { Injectable } from '@angular/core'; | import { Injectable } from '@angular/core'; | ||||
| import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; | import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; | ||||
| import { BehaviorSubject, Subject } from "rxjs"; | import { BehaviorSubject, Subject } from "rxjs"; | ||||
| import { config } from "../../../config"; | |||||
| import { config } from "../../../assets/config/config"; | |||||
| @Injectable({ | @Injectable({ | ||||
| providedIn: 'root' | providedIn: 'root' | ||||
| }) | }) | ||||
| export class SocketService { | export class SocketService { | ||||
| gateway = config.gateway; | gateway = config.gateway; | ||||
| websocket$!: WebSocketSubject<any> ; | websocket$!: WebSocketSubject<any> ; | ||||
| private isConnected: boolean = false; | private isConnected: boolean = false; | ||||
| private messagesSubject$ = new Subject(); | private messagesSubject$ = new Subject(); | ||||
| constructor() { | constructor() { | ||||
| } | } | ||||
| public connect(cfg: { reconnect: boolean } = {reconnect: false}): void { | public connect(cfg: { reconnect: boolean } = {reconnect: false}): void { | ||||
| if (!this.websocket$ || this.websocket$.closed) { | if (!this.websocket$ || this.websocket$.closed) { | ||||
| console.log('Trying to open a WebSocket connection…'); | console.log('Trying to open a WebSocket connection…'); |
| export const config = { | |||||
| gateway: 'wss:/socket.aztrace.vn/ws' | |||||
| }; | |||||
| // export function loadConfig() { | |||||
| // return { | |||||
| // gateway: 'wss:/socket.aztrace.vn/ws' | |||||
| // }; | |||||
| // } |
| const WebSocket = require('ws'); | |||||
| const http = require('http'); | |||||
| const server = http.createServer(); | |||||
| const wss = new WebSocket.Server({ server }); | |||||
| wss.on('connection', function connection(ws) { | |||||
| ws.on('message', function incoming(message) { | |||||
| // Log tin nhắn nhận được | |||||
| console.log('Nhận được: %s', message); | |||||
| }); | |||||
| // Bắt sự kiện khi kết nối đóng | |||||
| ws.on('close', function close() { | |||||
| console.log('Client đã ngắt kết nối'); | |||||
| }); | |||||
| }); | |||||
| server.listen(8081, function listening() { | |||||
| console.log('WebSocket server đang lắng nghe trên cổng 8081'); | |||||
| }); |
| ffmpeg -rtsp_transport tcp -i rtsp://admin:hd543211@@xuanphuong32.dyndns.org:8001/cam/realmonitor?channel=7&subtype=0 ^ | |||||
| -f mpegts -codec:v mpeg1video -s 640x480 -b:v 800k -r 30 ^ | |||||
| -codec:a mp2 -b:a 128k ^ | |||||
| http://localhost:8081 |