4 type ChargingStationOptions
,
10 type UIServerConfigurationSection
13 type ResponseHandler
= {
14 procedureName
: ProcedureName
15 resolve
: (value
: ResponsePayload
| PromiseLike
<ResponsePayload
>) => void
16 reject
: (reason
?: unknown
) => void
19 export class UIClient
{
20 private static instance
: UIClient
| null = null
22 private ws
!: WebSocket
23 private responseHandlers
: Map
<string, ResponseHandler
>
25 private constructor(private uiServerConfiguration
: UIServerConfigurationSection
) {
27 this.responseHandlers
= new Map
<string, ResponseHandler
>()
30 public static getInstance(uiServerConfiguration
: UIServerConfigurationSection
): UIClient
{
31 if (UIClient
.instance
=== null) {
32 UIClient
.instance
= new UIClient(uiServerConfiguration
)
34 return UIClient
.instance
37 public setConfiguration(uiServerConfiguration
: UIServerConfigurationSection
): void {
39 this.uiServerConfiguration
= uiServerConfiguration
43 public registerWSEventListener
<K
extends keyof WebSocketEventMap
>(
45 listener
: (event
: WebSocketEventMap
[K
]) => void
47 this.ws
.addEventListener(event
, listener
)
50 public async startSimulator(): Promise
<ResponsePayload
> {
51 return this.sendRequest(ProcedureName
.START_SIMULATOR
, {})
54 public async stopSimulator(): Promise
<ResponsePayload
> {
55 return this.sendRequest(ProcedureName
.STOP_SIMULATOR
, {})
58 public async listTemplates(): Promise
<ResponsePayload
> {
59 return this.sendRequest(ProcedureName
.LIST_TEMPLATES
, {})
62 public async listChargingStations(): Promise
<ResponsePayload
> {
63 return this.sendRequest(ProcedureName
.LIST_CHARGING_STATIONS
, {})
66 public async addChargingStations(
68 numberOfStations
: number,
69 options
?: ChargingStationOptions
70 ): Promise
<ResponsePayload
> {
71 return this.sendRequest(ProcedureName
.ADD_CHARGING_STATIONS
, {
78 public async deleteChargingStation(hashId
: string): Promise
<ResponsePayload
> {
79 return this.sendRequest(ProcedureName
.DELETE_CHARGING_STATIONS
, { hashIds
: [hashId
] })
82 public async setSupervisionUrl(hashId
: string, supervisionUrl
: string): Promise
<ResponsePayload
> {
83 return this.sendRequest(ProcedureName
.SET_SUPERVISION_URL
, {
89 public async startChargingStation(hashId
: string): Promise
<ResponsePayload
> {
90 return this.sendRequest(ProcedureName
.START_CHARGING_STATION
, { hashIds
: [hashId
] })
93 public async stopChargingStation(hashId
: string): Promise
<ResponsePayload
> {
94 return this.sendRequest(ProcedureName
.STOP_CHARGING_STATION
, { hashIds
: [hashId
] })
97 public async openConnection(hashId
: string): Promise
<ResponsePayload
> {
98 return this.sendRequest(ProcedureName
.OPEN_CONNECTION
, {
103 public async closeConnection(hashId
: string): Promise
<ResponsePayload
> {
104 return this.sendRequest(ProcedureName
.CLOSE_CONNECTION
, {
109 public async startTransaction(
112 idTag
: string | undefined
113 ): Promise
<ResponsePayload
> {
114 return this.sendRequest(ProcedureName
.START_TRANSACTION
, {
121 public async stopTransaction(
123 transactionId
: number | undefined
124 ): Promise
<ResponsePayload
> {
125 return this.sendRequest(ProcedureName
.STOP_TRANSACTION
, {
131 public async startAutomaticTransactionGenerator(
134 ): Promise
<ResponsePayload
> {
135 return this.sendRequest(ProcedureName
.START_AUTOMATIC_TRANSACTION_GENERATOR
, {
137 connectorIds
: [connectorId
]
141 public async stopAutomaticTransactionGenerator(
144 ): Promise
<ResponsePayload
> {
145 return this.sendRequest(ProcedureName
.STOP_AUTOMATIC_TRANSACTION_GENERATOR
, {
147 connectorIds
: [connectorId
]
151 private openWS(): void {
153 this.uiServerConfiguration
.authentication
?.enabled
=== true &&
154 this.uiServerConfiguration
.authentication
?.type === AuthenticationType
.PROTOCOL_BASIC_AUTH
156 `${this.uiServerConfiguration.protocol}${this.uiServerConfiguration.version}`,
157 `authorization.basic.${btoa(`${this.uiServerConfiguration.authentication.username}:${this.uiServerConfiguration.authentication.password}`).replace(/={1,2}$
/, '')}`
159 : `${this.uiServerConfiguration.protocol}${this.uiServerConfiguration.version}
`
160 this.ws = new WebSocket(
161 `${this.uiServerConfiguration.secure === true ? ApplicationProtocol.WSS : ApplicationProtocol.WS}
://${this.uiServerConfiguration.host}:${this.uiServerConfiguration.port}`,
164 this.ws
.onopen
= openEvent
=> {
165 console
.info('WebSocket opened', openEvent
)
167 this.ws
.onmessage
= this.responseHandler
.bind(this)
168 this.ws
.onerror
= errorEvent
=> {
169 console
.error('WebSocket error: ', errorEvent
)
171 this.ws
.onclose
= closeEvent
=> {
172 console
.info('WebSocket closed: ', closeEvent
)
176 private async sendRequest(
177 procedureName
: ProcedureName
,
178 payload
: RequestPayload
179 ): Promise
<ResponsePayload
> {
180 return new Promise
<ResponsePayload
>((resolve
, reject
) => {
181 if (this.ws
.readyState
=== WebSocket
.OPEN
) {
182 const uuid
= crypto
.randomUUID()
183 const msg
= JSON
.stringify([uuid
, procedureName
, payload
])
184 const sendTimeout
= setTimeout(() => {
185 this.responseHandlers
.delete(uuid
)
186 return reject(new Error(`Send request '${procedureName}' message: connection timeout`))
190 this.responseHandlers
.set(uuid
, { procedureName
, resolve
, reject
})
192 this.responseHandlers
.delete(uuid
)
195 clearTimeout(sendTimeout
)
198 reject(new Error(`Send request '${procedureName}' message: connection closed`))
203 private responseHandler(messageEvent
: MessageEvent
<string>): void {
204 const response
= JSON
.parse(messageEvent
.data
) as ProtocolResponse
206 if (Array.isArray(response
) === false) {
207 throw new Error(`Response not an array: ${JSON.stringify(response, undefined, 2)}`)
210 const [uuid
, responsePayload
] = response
212 if (this.responseHandlers
.has(uuid
) === true) {
213 const { procedureName
, resolve
, reject
} = this.responseHandlers
.get(uuid
)!
214 switch (responsePayload
.status) {
215 case ResponseStatus
.SUCCESS
:
216 resolve(responsePayload
)
218 case ResponseStatus
.FAILURE
:
219 reject(responsePayload
)
224 `Response status for procedure '${procedureName}' not supported: '${responsePayload.status}'`
228 this.responseHandlers
.delete(uuid
)
230 throw new Error(`Not a response to a request: ${JSON.stringify(response, undefined, 2)}`)