| "@asymmetrik/ngx-leaflet-markercluster": "^16.0.0", | "@asymmetrik/ngx-leaflet-markercluster": "^16.0.0", | ||||
| "leaflet": "^1.9.4", | "leaflet": "^1.9.4", | ||||
| "rxjs": "~7.8.0", | "rxjs": "~7.8.0", | ||||
| "rxjs-websockets": "^9.0.0", | |||||
| "tslib": "^2.3.0", | "tslib": "^2.3.0", | ||||
| "zone.js": "~0.13.0" | "zone.js": "~0.13.0" | ||||
| }, | }, |
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="topnav"> | |||||
| <h1>Test Socket</h1> | |||||
| </div> | |||||
| <div class="content"> | |||||
| <div class="card-grid"> | |||||
| <div class="card"> | |||||
| <p>Connect</p> | |||||
| <p class="state"><span id="Connect">{{ isConnected ? 'Online' : 'Offline' }}</span></p> | |||||
| </div> | |||||
| <div class="card"> | |||||
| <p class="state" id="State1">{{ state1 == '' ? '%STATE%' : state1}}</p> | |||||
| <p class="state" id="State2">{{ state2 == '' ? '%STATE%' : state2}}</p> | |||||
| <p class="state" id="State3">{{ state3 == '' ? '%STATE%' : state3}}</p> | |||||
| <p class="state" id="State4">{{ state4 == '' ? '%STATE%' : state4}}</p> | |||||
| <p class="state" id="State5">{{ state5 == '' ? '%STATE%' : state5}}</p> | |||||
| <p class="state" id="State6">{{ state6 == '' ? '%STATE%' : state6}}</p> | |||||
| <p><button (click)="toggleState1()" id="SetState1" class="button">Set State 1 On</button></p> | |||||
| <p><button (click)="toggleState2()" id="SetState2" class="button">Set State 2 On</button></p> | |||||
| </div> | |||||
| </div> | |||||
| </div> |
| background-color: green !important; | background-color: green !important; | ||||
| color: white !important; | color: white !important; | ||||
| } | } | ||||
| h1 { | |||||
| font-size: 1.8rem; | |||||
| color: white; | |||||
| } | |||||
| .topnav { | |||||
| overflow: hidden; | |||||
| background-color: #0A1128; | |||||
| } | |||||
| body { | |||||
| margin: 0; | |||||
| } | |||||
| p { | |||||
| text-align: center; | |||||
| } | |||||
| .content { | |||||
| padding: 50px; | |||||
| } | |||||
| .card-grid { | |||||
| max-width: 800px; | |||||
| margin: 0 auto; | |||||
| display: grid; | |||||
| grid-gap: 2rem; | |||||
| grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | |||||
| } | |||||
| .card { | |||||
| background-color: white; | |||||
| box-shadow: 2px 2px 12px 1px rgba(140, 140, 140, .5); | |||||
| } | |||||
| .card-title { | |||||
| font-size: 1.2rem; | |||||
| font-weight: bold; | |||||
| color: #034078 | |||||
| } | |||||
| .reading { | |||||
| font-size: 1.2rem; | |||||
| color: #1282A2; | |||||
| } | |||||
| .button { | |||||
| padding: 15px 50px; | |||||
| font-size: 24px; | |||||
| text-align: center; | |||||
| outline: none; | |||||
| color: #fff; | |||||
| background-color: #0f8b8d; | |||||
| border: none; | |||||
| border-radius: 5px; | |||||
| -webkit-touch-callout: none; | |||||
| -webkit-user-select: none; | |||||
| -khtml-user-select: none; | |||||
| -moz-user-select: none; | |||||
| -ms-user-select: none; | |||||
| user-select: none; | |||||
| -webkit-tap-highlight-color: rgba(0, 0, 0, 0); | |||||
| } | |||||
| /*.button:hover {background-color: #0f8b8d}*/ | |||||
| .button:active { | |||||
| background-color: #0f8b8d; | |||||
| box-shadow: 2 2px #CDCDCD; | |||||
| transform: translateY(2px); | |||||
| } | |||||
| .state { | |||||
| font-size: 1.5rem; | |||||
| color: #8c8c8c; | |||||
| font-weight: bold; | |||||
| text-align: center; | |||||
| } | |||||
| .content { | |||||
| padding: 30px; | |||||
| max-width: 600px; | |||||
| margin: 0 auto; | |||||
| } | |||||
| .card { | |||||
| background-color: #F8F7F9;; | |||||
| box-shadow: 2px 2px 12px 1px rgba(140, 140, 140, .5); | |||||
| padding-top: 10px; | |||||
| padding-bottom: 20px; | |||||
| } |
| import { Component } from '@angular/core'; | |||||
| import {ChangeDetectorRef, Component, OnChanges, OnDestroy, OnInit, SimpleChanges} from '@angular/core'; | |||||
| import {SocketService} from "../../../shared/services/socket.service"; | |||||
| import {Subscription} from "rxjs"; | |||||
| @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 { | |||||
| isClicked = false; | |||||
| 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; | |||||
| toggleColor(): void { | |||||
| this.isClicked = !this.isClicked; | |||||
| } | |||||
| 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); | |||||
| }); | |||||
| } | |||||
| }); | |||||
| } | |||||
| 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(); | |||||
| } | |||||
| getReadings(){ | |||||
| let str = { id: '0', type: 'get' }; | |||||
| this.socketService$.sendMessage(str); | |||||
| } | |||||
| 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); | |||||
| } | |||||
| 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'; | |||||
| } | |||||
| } | |||||
| } | } |
| import { Injectable } from '@angular/core'; | |||||
| import { webSocket, WebSocketSubject } from 'rxjs/webSocket'; | |||||
| import {BehaviorSubject, catchError, delayWhen, Observable, retryWhen, Subject, tap, timer} from "rxjs"; | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class SocketService { | |||||
| gateway = 'ws:/socket.aztrace.vn/ws'; | |||||
| websocket$!: WebSocketSubject<any> ; | |||||
| private isConnected: boolean = false; | |||||
| private messagesSubject$ = new Subject(); | |||||
| public messages$ = this.messagesSubject$.asObservable(); | |||||
| private statusSubject = new BehaviorSubject<boolean>(this.isConnected); | |||||
| public status$ = this.statusSubject.asObservable(); | |||||
| constructor() { | |||||
| } | |||||
| public connect(cfg: { reconnect: boolean } = {reconnect: false}): void { | |||||
| if (!this.websocket$ || this.websocket$.closed) { | |||||
| console.log('Trying to open a WebSocket connection…'); | |||||
| this.websocket$ = this.getNewWebSocket(); | |||||
| this.websocket$.subscribe((messages) => { | |||||
| this.messagesSubject$.next(messages); | |||||
| }); | |||||
| } | |||||
| } | |||||
| close() { | |||||
| this.websocket$.complete(); | |||||
| } | |||||
| sendMessage(msg: any) { | |||||
| this.websocket$.next(msg); | |||||
| } | |||||
| private getNewWebSocket() { | |||||
| return webSocket({ | |||||
| url: this.gateway, | |||||
| openObserver: { | |||||
| next: () => { | |||||
| console.log('Connection ok'); | |||||
| this.isConnected = true; | |||||
| this.statusSubject.next(this.isConnected); | |||||
| } | |||||
| }, | |||||
| closeObserver: { | |||||
| next: () => { | |||||
| console.log('Connection closed'); | |||||
| this.isConnected = false; | |||||
| this.connect({reconnect: true}); | |||||
| this.statusSubject.next(this.isConnected); | |||||
| } | |||||
| }, | |||||
| }); | |||||
| } | |||||
| } |
| import { NgModule } from "@angular/core"; | import { NgModule } from "@angular/core"; | ||||
| import { FlexLayoutModule } from "@angular/flex-layout"; | import { FlexLayoutModule } from "@angular/flex-layout"; | ||||
| import {MatButtonModule} from "@angular/material/button"; | |||||
| import { MatButtonModule } from "@angular/material/button"; | |||||
| @NgModule({ | @NgModule({ | ||||
| exports: [ | exports: [ |