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