Web UI: rename some directories to a sensible name
[e-mobility-charging-stations-simulator.git] / src / charging-station / ChargingStationWorkerBroadcastChannel.ts
1 import BaseError from '../exception/BaseError';
2 import { RequestCommand } from '../types/ocpp/Requests';
3 import {
4 AuthorizationStatus,
5 StartTransactionRequest,
6 StartTransactionResponse,
7 StopTransactionReason,
8 StopTransactionRequest,
9 StopTransactionResponse,
10 } from '../types/ocpp/Transaction';
11 import {
12 BroadcastChannelProcedureName,
13 BroadcastChannelRequest,
14 BroadcastChannelRequestPayload,
15 BroadcastChannelResponsePayload,
16 MessageEvent,
17 } from '../types/WorkerBroadcastChannel';
18 import { ResponseStatus } from '../ui/web/src/types/UIProtocol';
19 import logger from '../utils/Logger';
20 import type ChargingStation from './ChargingStation';
21 import WorkerBroadcastChannel from './WorkerBroadcastChannel';
22
23 const moduleName = 'ChargingStationWorkerBroadcastChannel';
24
25 type CommandResponse = StartTransactionResponse | StopTransactionResponse;
26
27 export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel {
28 private readonly chargingStation: ChargingStation;
29
30 constructor(chargingStation: ChargingStation) {
31 super();
32 this.chargingStation = chargingStation;
33 this.onmessage = this.requestHandler.bind(this) as (message: MessageEvent) => void;
34 this.onmessageerror = this.messageErrorHandler.bind(this) as (message: MessageEvent) => void;
35 }
36
37 private async requestHandler(messageEvent: MessageEvent): Promise<void> {
38 if (this.isResponse(messageEvent.data)) {
39 return;
40 }
41 this.validateMessageEvent(messageEvent);
42
43 const [uuid, command, requestPayload] = messageEvent.data as BroadcastChannelRequest;
44
45 if (
46 requestPayload?.hashId === undefined &&
47 (requestPayload?.hashIds as string[])?.includes(this.chargingStation.hashId) === false
48 ) {
49 return;
50 }
51 if (
52 requestPayload?.hashIds === undefined &&
53 requestPayload?.hashId !== this.chargingStation.hashId
54 ) {
55 return;
56 }
57 if (requestPayload?.hashId !== undefined) {
58 logger.warn(
59 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' instead`
60 );
61 }
62
63 let responsePayload: BroadcastChannelResponsePayload;
64 let commandResponse: CommandResponse;
65 try {
66 commandResponse = await this.commandHandler(command, requestPayload);
67 if (commandResponse === undefined) {
68 responsePayload = {
69 hashId: this.chargingStation.hashId,
70 status: ResponseStatus.SUCCESS,
71 };
72 } else {
73 responsePayload = {
74 hashId: this.chargingStation.hashId,
75 status: this.commandResponseToResponseStatus(commandResponse),
76 };
77 }
78 } catch (error) {
79 logger.error(
80 `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`,
81 error
82 );
83 responsePayload = {
84 hashId: this.chargingStation.hashId,
85 status: ResponseStatus.FAILURE,
86 command,
87 requestPayload,
88 commandResponse,
89 errorMessage: (error as Error).message,
90 errorStack: (error as Error).stack,
91 };
92 }
93 this.sendResponse([uuid, responsePayload]);
94 }
95
96 private messageErrorHandler(messageEvent: MessageEvent): void {
97 logger.error(
98 `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
99 { messageEvent, messageEventData: messageEvent.data }
100 );
101 }
102
103 private async commandHandler(
104 command: BroadcastChannelProcedureName,
105 requestPayload: BroadcastChannelRequestPayload
106 ): Promise<CommandResponse | undefined> {
107 switch (command) {
108 case BroadcastChannelProcedureName.START_TRANSACTION:
109 return this.chargingStation.ocppRequestService.requestHandler<
110 StartTransactionRequest,
111 StartTransactionResponse
112 >(this.chargingStation, RequestCommand.START_TRANSACTION, {
113 connectorId: requestPayload.connectorId,
114 idTag: requestPayload.idTag,
115 });
116 case BroadcastChannelProcedureName.STOP_TRANSACTION:
117 return this.chargingStation.ocppRequestService.requestHandler<
118 StopTransactionRequest,
119 StopTransactionResponse
120 >(this.chargingStation, RequestCommand.STOP_TRANSACTION, {
121 transactionId: requestPayload.transactionId,
122 meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId(
123 requestPayload.transactionId
124 ),
125 idTag: this.chargingStation.getTransactionIdTag(requestPayload.transactionId),
126 reason: StopTransactionReason.NONE,
127 });
128 case BroadcastChannelProcedureName.START_CHARGING_STATION:
129 this.chargingStation.start();
130 break;
131 case BroadcastChannelProcedureName.STOP_CHARGING_STATION:
132 await this.chargingStation.stop();
133 break;
134 case BroadcastChannelProcedureName.OPEN_CONNECTION:
135 this.chargingStation.openWSConnection();
136 break;
137 case BroadcastChannelProcedureName.CLOSE_CONNECTION:
138 this.chargingStation.closeWSConnection();
139 break;
140 default:
141 // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
142 throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
143 }
144 }
145
146 private commandResponseToResponseStatus(commandResponse: CommandResponse): ResponseStatus {
147 if (commandResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) {
148 return ResponseStatus.SUCCESS;
149 }
150 return ResponseStatus.FAILURE;
151 }
152 }