4 type ConfigurationData
,
12 type ResponseHandler
= {
13 procedureName
: ProcedureName
14 resolve
: (value
: ResponsePayload
| PromiseLike
<ResponsePayload
>) => void
15 reject
: (reason
?: unknown
) => void
18 export class UIClient
{
19 private static instance
: UIClient
| null = null
21 private ws
!: WebSocket
22 private responseHandlers
: Map
<string, ResponseHandler
>
24 private constructor(private configuration
: ConfigurationData
) {
26 this.responseHandlers
= new Map
<string, ResponseHandler
>()
29 public static getInstance(configuration
: ConfigurationData
) {
30 if (UIClient
.instance
=== null) {
31 UIClient
.instance
= new UIClient(configuration
)
33 return UIClient
.instance
36 public registerWSonOpenListener(listener
: (event
: Event
) => void) {
37 this.ws
.addEventListener('open', listener
)
40 public async startSimulator(): Promise
<ResponsePayload
> {
41 return this.sendRequest(ProcedureName
.START_SIMULATOR
, {})
44 public async stopSimulator(): Promise
<ResponsePayload
> {
45 return this.sendRequest(ProcedureName
.STOP_SIMULATOR
, {})
48 public async listChargingStations(): Promise
<ResponsePayload
> {
49 return this.sendRequest(ProcedureName
.LIST_CHARGING_STATIONS
, {})
52 public async startChargingStation(hashId
: string): Promise
<ResponsePayload
> {
53 return this.sendRequest(ProcedureName
.START_CHARGING_STATION
, { hashIds
: [hashId
] })
56 public async stopChargingStation(hashId
: string): Promise
<ResponsePayload
> {
57 return this.sendRequest(ProcedureName
.STOP_CHARGING_STATION
, { hashIds
: [hashId
] })
60 public async openConnection(hashId
: string): Promise
<ResponsePayload
> {
61 return this.sendRequest(ProcedureName
.OPEN_CONNECTION
, {
66 public async closeConnection(hashId
: string): Promise
<ResponsePayload
> {
67 return this.sendRequest(ProcedureName
.CLOSE_CONNECTION
, {
72 public async startTransaction(
75 idTag
: string | undefined
76 ): Promise
<ResponsePayload
> {
77 return this.sendRequest(ProcedureName
.START_TRANSACTION
, {
84 public async stopTransaction(
86 transactionId
: number | undefined
87 ): Promise
<ResponsePayload
> {
88 return this.sendRequest(ProcedureName
.STOP_TRANSACTION
, {
94 public async startAutomaticTransactionGenerator(
97 ): Promise
<ResponsePayload
> {
98 return this.sendRequest(ProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
, {
100 connectorIds
: [connectorId
]
104 public async stopAutomaticTransactionGenerator(
107 ): Promise
<ResponsePayload
> {
108 return this.sendRequest(ProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
, {
110 connectorIds
: [connectorId
]
114 private openWS(): void {
116 this.configuration
.uiServer
.authentication
?.enabled
=== true &&
117 this.configuration
.uiServer
.authentication
?.type === AuthenticationType
.PROTOCOL_BASIC_AUTH
119 `${this.configuration.uiServer.protocol}${this.configuration.uiServer.version}`,
120 `authorization.basic.${btoa(`${this.configuration.uiServer.authentication.username}:${this.configuration.uiServer.authentication.password}`).replace(/={1,2}$
/, '')}`
122 : `${this.configuration.uiServer.protocol}${this.configuration.uiServer.version}
`
123 this.ws = new WebSocket(
124 `${this.configuration.uiServer.secure === true ? ApplicationProtocol.WSS : ApplicationProtocol.WS}
://${this.configuration.uiServer.host}:${this.configuration.uiServer.port}`,
127 this.ws
.onopen
= openEvent
=> {
128 console
.info('WebSocket opened', openEvent
)
130 this.ws
.onmessage
= this.responseHandler
.bind(this)
131 this.ws
.onerror
= errorEvent
=> {
132 console
.error('WebSocket error: ', errorEvent
)
134 this.ws
.onclose
= closeEvent
=> {
135 console
.info('WebSocket closed: ', closeEvent
)
139 private async sendRequest(
140 procedureName
: ProcedureName
,
141 payload
: RequestPayload
142 ): Promise
<ResponsePayload
> {
143 return new Promise
<ResponsePayload
>((resolve
, reject
) => {
144 if (this.ws
.readyState
!== WebSocket
.OPEN
) {
147 if (this.ws
.readyState
=== WebSocket
.OPEN
) {
148 const uuid
= crypto
.randomUUID()
149 const msg
= JSON
.stringify([uuid
, procedureName
, payload
])
150 const sendTimeout
= setTimeout(() => {
151 this.responseHandlers
.delete(uuid
)
152 return reject(new Error(`Send request '${procedureName}' message: connection timeout`))
156 this.responseHandlers
.set(uuid
, { procedureName
, resolve
, reject
})
158 this.responseHandlers
.delete(uuid
)
161 clearTimeout(sendTimeout
)
164 reject(new Error(`Send request '${procedureName}' message: connection closed`))
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, undefined, 2)}`)
176 const [uuid
, responsePayload
] = response
178 if (this.responseHandlers
.has(uuid
) === true) {
179 const { procedureName
, resolve
, reject
} = this.responseHandlers
.get(uuid
)!
180 switch (responsePayload
.status) {
181 case ResponseStatus
.SUCCESS
:
182 resolve(responsePayload
)
184 case ResponseStatus
.FAILURE
:
185 reject(responsePayload
)
189 `Response status for procedure '${procedureName}' not supported: '${responsePayload.status}'`
192 this.responseHandlers
.delete(uuid
)
194 throw new Error(`Not a response to a request: ${JSON.stringify(response, undefined, 2)}`)