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