Version 1.1.66
[e-mobility-charging-stations-simulator.git] / src / ui / web / src / composable / UIClient.ts
CommitLineData
32de5a57
LM
1import { JsonType } from '@/type/JsonType';
2import {
3 ProcedureName,
4 ProtocolResponse,
5 ResponsePayload,
6 ResponseStatus,
7} from '@/type/UIProtocol';
8import Utils from './Utils';
9import config from '@/assets/config';
10import { v4 as uuidv4 } from 'uuid';
11
12type ResponseHandler = {
13 resolve: (value: ResponsePayload | PromiseLike<ResponsePayload>) => void;
14 reject: (reason?: any) => void;
15 procedureName: ProcedureName;
16};
17
18export default class UIClient {
19 private static _instance: UIClient | null = null;
20
5a010bf0 21 private _ws!: WebSocket;
32de5a57
LM
22 private _responseHandlers: Map<string, ResponseHandler>;
23
24 private constructor() {
5a010bf0 25 this.openWS();
32de5a57 26 this._responseHandlers = new Map<string, ResponseHandler>();
32de5a57
LM
27 }
28
29 public static get instance() {
30 if (UIClient._instance === null) {
31 UIClient._instance = new UIClient();
32 }
33 return UIClient._instance;
34 }
35
36 public onOpen(listener: (this: WebSocket, ev: Event) => void) {
37 this._ws.addEventListener('open', listener);
38 }
39
5a010bf0
JB
40 public async startSimulator(): Promise<ResponsePayload> {
41 return this.sendRequest(ProcedureName.START_SIMULATOR, {});
42 }
43
44 public async stopSimulator(): Promise<ResponsePayload> {
45 return this.sendRequest(ProcedureName.STOP_SIMULATOR, {});
46 }
32de5a57 47
5a010bf0 48 public async listChargingStations(): Promise<ResponsePayload> {
32de5a57
LM
49 return this.sendRequest(ProcedureName.LIST_CHARGING_STATIONS, {});
50 }
51
52 public async startTransaction(
53 hashId: string,
54 connectorId: number,
5a010bf0 55 idTag: string | undefined
32de5a57 56 ): Promise<ResponsePayload> {
32de5a57
LM
57 return this.sendRequest(ProcedureName.START_TRANSACTION, {
58 hashId,
59 connectorId,
60 idTag,
61 });
62 }
63
5a010bf0
JB
64 public async stopTransaction(
65 hashId: string,
66 transactionId: number | undefined
67 ): Promise<ResponsePayload> {
32de5a57
LM
68 return this.sendRequest(ProcedureName.STOP_TRANSACTION, {
69 hashId,
70 transactionId,
71 });
72 }
73
5a010bf0
JB
74 public async openConnection(hashId: string): Promise<ResponsePayload> {
75 return this.sendRequest(ProcedureName.OPEN_CONNECTION, {
76 hashId,
77 });
78 }
79
80 public async closeConnection(hashId: string): Promise<ResponsePayload> {
81 return this.sendRequest(ProcedureName.CLOSE_CONNECTION, {
82 hashId,
83 });
84 }
85
86 private openWS(): void {
87 this._ws = new WebSocket(
88 `ws://${config.emobility.host}:${config.emobility.port}`,
89 config.emobility.protocol
90 );
91 this._ws.onmessage = this.responseHandler.bind(this);
92 }
93
32de5a57
LM
94 private setResponseHandler(
95 id: string,
96 resolve: (value: ResponsePayload | PromiseLike<ResponsePayload>) => void,
97 reject: (reason?: any) => void,
98 procedureName: ProcedureName
99 ): void {
100 this._responseHandlers.set(id, { resolve, reject, procedureName });
101 }
102
103 private getResponseHandler(id: string): ResponseHandler | undefined {
104 return this._responseHandlers.get(id);
105 }
106
107 private async sendRequest(command: ProcedureName, data: JsonType): Promise<ResponsePayload> {
108 let uuid: string;
109 return Utils.promiseWithTimeout(
110 new Promise((resolve, reject) => {
111 uuid = uuidv4();
112 const msg = JSON.stringify([uuid, command, data]);
113
5a010bf0
JB
114 if (this._ws.readyState !== WebSocket.OPEN) {
115 this.openWS();
116 }
117 if (this._ws.readyState === WebSocket.OPEN) {
32de5a57
LM
118 this._ws.send(msg);
119 } else {
120 throw new Error(`Send request ${command} message: connection not opened`);
121 }
122
123 this.setResponseHandler(uuid, resolve, reject, command);
124 }),
125 60 * 1000,
126 Error(`Send request ${command} message timeout`),
127 () => {
128 this._responseHandlers.delete(uuid);
129 }
130 );
131 }
132
97f0a1a5 133 private responseHandler(messageEvent: MessageEvent<string>): void {
32de5a57
LM
134 const data = JSON.parse(messageEvent.data) as ProtocolResponse;
135
53e5fd67
JB
136 if (Array.isArray(data) === false) {
137 throw new Error('Response not an array: ' + JSON.stringify(data, null, 2));
32de5a57
LM
138 }
139
140 const [uuid, response] = data;
141
142 if (this._responseHandlers.has(uuid) === true) {
143 switch (response.status) {
144 case ResponseStatus.SUCCESS:
145 this.getResponseHandler(uuid)?.resolve(response);
146 break;
147 case ResponseStatus.FAILURE:
148 this.getResponseHandler(uuid)?.reject(response);
149 break;
150 default:
151 throw new Error(`Response status not supported: ${response.status}`);
152 }
153 } else {
154 throw new Error('Not a response to a request: ' + JSON.stringify(data, null, 2));
155 }
156 }
157}