1 import { ProcedureName
, ResponseStatus
, type RequestPayload
} from
'@/types/UIProtocol';
2 import type { ProtocolResponse
, ResponsePayload
} from
'@/types/UIProtocol';
4 import Utils from
'./Utils';
5 import config from
'@/assets/config';
7 type ResponseHandler
= {
8 procedureName
: ProcedureName
;
9 resolve
: (value
: ResponsePayload
| PromiseLike
<ResponsePayload
>) => void;
10 reject
: (reason
?: any) => void;
13 export default class UIClient
{
14 private static _instance
: UIClient
| null = null;
16 private _ws
!: WebSocket
;
17 private _responseHandlers
: Map
<string, ResponseHandler
>;
19 private constructor() {
21 this._responseHandlers
= new Map
<string, ResponseHandler
>();
24 public static getInstance() {
25 if (UIClient
._instance
=== null) {
26 UIClient
._instance
= new UIClient();
28 return UIClient
._instance
;
31 public registerWSonOpenListener(listener
: (event
: Event
) => void) {
32 this._ws
.addEventListener('open', listener
);
35 public async startSimulator(): Promise
<ResponsePayload
> {
36 return this.sendRequest(ProcedureName
.START_SIMULATOR
, {});
39 public async stopSimulator(): Promise
<ResponsePayload
> {
40 return this.sendRequest(ProcedureName
.STOP_SIMULATOR
, {});
43 public async listChargingStations(): Promise
<ResponsePayload
> {
44 return this.sendRequest(ProcedureName
.LIST_CHARGING_STATIONS
, {});
47 public async startChargingStation(hashId
: string): Promise
<ResponsePayload
> {
48 return this.sendRequest(ProcedureName
.START_CHARGING_STATION
, { hashIds
: [hashId
] });
51 public async stopChargingStation(hashId
: string): Promise
<ResponsePayload
> {
52 return this.sendRequest(ProcedureName
.STOP_CHARGING_STATION
, { hashIds
: [hashId
] });
55 public async openConnection(hashId
: string): Promise
<ResponsePayload
> {
56 return this.sendRequest(ProcedureName
.OPEN_CONNECTION
, {
61 public async closeConnection(hashId
: string): Promise
<ResponsePayload
> {
62 return this.sendRequest(ProcedureName
.CLOSE_CONNECTION
, {
67 public async startTransaction(
70 idTag
: string | undefined
71 ): Promise
<ResponsePayload
> {
72 return this.sendRequest(ProcedureName
.START_TRANSACTION
, {
79 public async stopTransaction(
81 transactionId
: number | undefined
82 ): Promise
<ResponsePayload
> {
83 return this.sendRequest(ProcedureName
.STOP_TRANSACTION
, {
89 public async startAutomaticTransactionGenerator(
92 ): Promise
<ResponsePayload
> {
93 return this.sendRequest(ProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
, {
95 connectorIds
: [connectorId
],
99 public async stopAutomaticTransactionGenerator(
102 ): Promise
<ResponsePayload
> {
103 return this.sendRequest(ProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
, {
105 connectorIds
: [connectorId
],
109 private openWS(): void {
110 this._ws
= new WebSocket(
111 `ws://${config.uiServer.host}:${config.uiServer.port}`,
112 config
.uiServer
.protocol
114 this._ws
.onmessage
= this.responseHandler
.bind(this);
115 this._ws
.onerror
= (errorEvent
) => {
116 console
.error('WebSocket error: ', errorEvent
);
118 this._ws
.onclose
= (closeEvent
) => {
119 console
.info('WebSocket closed: ', closeEvent
);
123 private setResponseHandler(
125 procedureName
: ProcedureName
,
126 resolve
: (value
: ResponsePayload
| PromiseLike
<ResponsePayload
>) => void,
127 reject
: (reason
?: any) => void
129 this._responseHandlers
.set(id
, { procedureName
, resolve
, reject
});
132 private getResponseHandler(id
: string): ResponseHandler
| undefined {
133 return this._responseHandlers
.get(id
);
136 private deleteResponseHandler(id
: string): boolean {
137 return this._responseHandlers
.delete(id
);
140 private async sendRequest(
141 command
: ProcedureName
,
143 ): Promise
<ResponsePayload
> {
145 return Utils
.promiseWithTimeout(
146 new Promise((resolve
, reject
) => {
147 uuid
= crypto
.randomUUID();
148 const msg
= JSON
.stringify([uuid
, command
, data
]);
150 if (this._ws
.readyState
!== WebSocket
.OPEN
) {
153 if (this._ws
.readyState
=== WebSocket
.OPEN
) {
156 throw new Error(`Send request '${command}' message: connection not opened`);
159 this.setResponseHandler(uuid
, command
, resolve
, reject
);
162 Error(`Send request '${command}' message timeout`),
164 this._responseHandlers
.delete(uuid
);
169 private responseHandler(messageEvent
: MessageEvent
<string>): void {
170 const response
= JSON
.parse(messageEvent
.data
) as ProtocolResponse
;
172 if (Array.isArray(response
) === false) {
173 throw new Error('Response not an array: ' + JSON
.stringify(response
, null, 2));
176 const [uuid
, responsePayload
] = response
;
178 if (this._responseHandlers
.has(uuid
) === true) {
179 switch (responsePayload
.status) {
180 case ResponseStatus
.SUCCESS
:
181 this.getResponseHandler(uuid
)?.resolve(responsePayload
);
183 case ResponseStatus
.FAILURE
:
184 this.getResponseHandler(uuid
)?.reject(responsePayload
);
187 console
.error(`Response status not supported: ${responsePayload.status}`);
189 this.deleteResponseHandler(uuid
);
191 throw new Error('Not a response to a request: ' + JSON
.stringify(response
, null, 2));