Commit | Line | Data |
---|---|---|
32de5a57 LM |
1 | import { JsonType } from '@/type/JsonType'; |
2 | import { | |
3 | ProcedureName, | |
4 | ProtocolResponse, | |
5 | ResponsePayload, | |
6 | ResponseStatus, | |
7 | } from '@/type/UIProtocol'; | |
8 | import Utils from './Utils'; | |
9 | import config from '@/assets/config'; | |
10 | import { v4 as uuidv4 } from 'uuid'; | |
11 | ||
12 | type ResponseHandler = { | |
13 | resolve: (value: ResponsePayload | PromiseLike<ResponsePayload>) => void; | |
14 | reject: (reason?: any) => void; | |
15 | procedureName: ProcedureName; | |
16 | }; | |
17 | ||
18 | export 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 | ||
16fe3949 | 36 | public registerWSonOpenListener(listener: (this: WebSocket, ev: Event) => void) { |
32de5a57 LM |
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 | ||
8fc2e5cc JB |
52 | public async startChargingStation(hashId: string): Promise<ResponsePayload> { |
53 | return this.sendRequest(ProcedureName.START_CHARGING_STATION, { hashId }); | |
54 | } | |
55 | ||
56 | public async stopChargingStation(hashId: string): Promise<ResponsePayload> { | |
57 | return this.sendRequest(ProcedureName.STOP_CHARGING_STATION, { hashId }); | |
58 | } | |
59 | ||
60 | public async openConnection(hashId: string): Promise<ResponsePayload> { | |
61 | return this.sendRequest(ProcedureName.OPEN_CONNECTION, { | |
62 | hashId, | |
63 | }); | |
64 | } | |
65 | ||
66 | public async closeConnection(hashId: string): Promise<ResponsePayload> { | |
67 | return this.sendRequest(ProcedureName.CLOSE_CONNECTION, { | |
68 | hashId, | |
69 | }); | |
70 | } | |
71 | ||
32de5a57 LM |
72 | public async startTransaction( |
73 | hashId: string, | |
74 | connectorId: number, | |
5a010bf0 | 75 | idTag: string | undefined |
32de5a57 | 76 | ): Promise<ResponsePayload> { |
32de5a57 LM |
77 | return this.sendRequest(ProcedureName.START_TRANSACTION, { |
78 | hashId, | |
79 | connectorId, | |
80 | idTag, | |
81 | }); | |
82 | } | |
83 | ||
5a010bf0 JB |
84 | public async stopTransaction( |
85 | hashId: string, | |
86 | transactionId: number | undefined | |
87 | ): Promise<ResponsePayload> { | |
32de5a57 LM |
88 | return this.sendRequest(ProcedureName.STOP_TRANSACTION, { |
89 | hashId, | |
90 | transactionId, | |
91 | }); | |
92 | } | |
93 | ||
5a010bf0 JB |
94 | private openWS(): void { |
95 | this._ws = new WebSocket( | |
96 | `ws://${config.emobility.host}:${config.emobility.port}`, | |
97 | config.emobility.protocol | |
98 | ); | |
99 | this._ws.onmessage = this.responseHandler.bind(this); | |
100 | } | |
101 | ||
32de5a57 LM |
102 | private setResponseHandler( |
103 | id: string, | |
104 | resolve: (value: ResponsePayload | PromiseLike<ResponsePayload>) => void, | |
105 | reject: (reason?: any) => void, | |
106 | procedureName: ProcedureName | |
107 | ): void { | |
108 | this._responseHandlers.set(id, { resolve, reject, procedureName }); | |
109 | } | |
110 | ||
111 | private getResponseHandler(id: string): ResponseHandler | undefined { | |
112 | return this._responseHandlers.get(id); | |
113 | } | |
114 | ||
115 | private async sendRequest(command: ProcedureName, data: JsonType): Promise<ResponsePayload> { | |
116 | let uuid: string; | |
117 | return Utils.promiseWithTimeout( | |
118 | new Promise((resolve, reject) => { | |
119 | uuid = uuidv4(); | |
120 | const msg = JSON.stringify([uuid, command, data]); | |
121 | ||
5a010bf0 JB |
122 | if (this._ws.readyState !== WebSocket.OPEN) { |
123 | this.openWS(); | |
124 | } | |
125 | if (this._ws.readyState === WebSocket.OPEN) { | |
32de5a57 LM |
126 | this._ws.send(msg); |
127 | } else { | |
128 | throw new Error(`Send request ${command} message: connection not opened`); | |
129 | } | |
130 | ||
131 | this.setResponseHandler(uuid, resolve, reject, command); | |
132 | }), | |
133 | 60 * 1000, | |
134 | Error(`Send request ${command} message timeout`), | |
135 | () => { | |
136 | this._responseHandlers.delete(uuid); | |
137 | } | |
138 | ); | |
139 | } | |
140 | ||
97f0a1a5 | 141 | private responseHandler(messageEvent: MessageEvent<string>): void { |
32de5a57 LM |
142 | const data = JSON.parse(messageEvent.data) as ProtocolResponse; |
143 | ||
53e5fd67 JB |
144 | if (Array.isArray(data) === false) { |
145 | throw new Error('Response not an array: ' + JSON.stringify(data, null, 2)); | |
32de5a57 LM |
146 | } |
147 | ||
148 | const [uuid, response] = data; | |
149 | ||
150 | if (this._responseHandlers.has(uuid) === true) { | |
151 | switch (response.status) { | |
152 | case ResponseStatus.SUCCESS: | |
153 | this.getResponseHandler(uuid)?.resolve(response); | |
154 | break; | |
155 | case ResponseStatus.FAILURE: | |
156 | this.getResponseHandler(uuid)?.reject(response); | |
157 | break; | |
158 | default: | |
159 | throw new Error(`Response status not supported: ${response.status}`); | |
160 | } | |
161 | } else { | |
162 | throw new Error('Not a response to a request: ' + JSON.stringify(data, null, 2)); | |
163 | } | |
164 | } | |
165 | } |