import BaseError from '../exception/BaseError';
import type OCPPError from '../exception/OCPPError';
+import { StandardParametersKey } from '../types/ocpp/Configuration';
import {
- HeartbeatRequest,
+ type BootNotificationRequest,
+ type HeartbeatRequest,
+ type MeterValuesRequest,
RequestCommand,
type StatusNotificationRequest,
} from '../types/ocpp/Requests';
-import type { HeartbeatResponse, StatusNotificationResponse } from '../types/ocpp/Responses';
+import {
+ type BootNotificationResponse,
+ type HeartbeatResponse,
+ type MeterValuesResponse,
+ RegistrationStatus,
+ type StatusNotificationResponse,
+} from '../types/ocpp/Responses';
import {
AuthorizationStatus,
- StartTransactionRequest,
- StartTransactionResponse,
- StopTransactionRequest,
- StopTransactionResponse,
+ type AuthorizeRequest,
+ type AuthorizeResponse,
+ type StartTransactionRequest,
+ type StartTransactionResponse,
+ type StopTransactionRequest,
+ type StopTransactionResponse,
} from '../types/ocpp/Transaction';
import {
BroadcastChannelProcedureName,
- BroadcastChannelRequest,
- BroadcastChannelRequestPayload,
- BroadcastChannelResponsePayload,
- MessageEvent,
+ type BroadcastChannelRequest,
+ type BroadcastChannelRequestPayload,
+ type BroadcastChannelResponsePayload,
+ type MessageEvent,
} from '../types/WorkerBroadcastChannel';
import { ResponseStatus } from '../ui/web/src/types/UIProtocol';
+import Constants from '../utils/Constants';
import logger from '../utils/Logger';
import Utils from '../utils/Utils';
import type ChargingStation from './ChargingStation';
+import { ChargingStationConfigurationUtils } from './ChargingStationConfigurationUtils';
+import { OCPP16ServiceUtils } from './ocpp/1.6/OCPP16ServiceUtils';
import WorkerBroadcastChannel from './WorkerBroadcastChannel';
const moduleName = 'ChargingStationWorkerBroadcastChannel';
type CommandResponse =
| StartTransactionResponse
| StopTransactionResponse
+ | AuthorizeResponse
+ | BootNotificationResponse
| StatusNotificationResponse
- | HeartbeatResponse;
+ | HeartbeatResponse
+ | MeterValuesResponse;
type CommandHandler = (
requestPayload?: BroadcastChannelRequestPayload
export default class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel {
private readonly commandHandlers: Map<BroadcastChannelProcedureName, CommandHandler>;
-
private readonly chargingStation: ChargingStation;
constructor(chargingStation: ChargingStation) {
BroadcastChannelProcedureName.CLOSE_CONNECTION,
() => this.chargingStation.closeWSConnection(),
],
+ [
+ BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
+ (requestPayload?: BroadcastChannelRequestPayload) =>
+ this.chargingStation.startAutomaticTransactionGenerator(requestPayload.connectorIds),
+ ],
+ [
+ BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
+ (requestPayload?: BroadcastChannelRequestPayload) =>
+ this.chargingStation.stopAutomaticTransactionGenerator(requestPayload.connectorIds),
+ ],
[
BroadcastChannelProcedureName.START_TRANSACTION,
async (requestPayload?: BroadcastChannelRequestPayload) =>
this.chargingStation.ocppRequestService.requestHandler<
StartTransactionRequest,
StartTransactionResponse
- >(this.chargingStation, RequestCommand.START_TRANSACTION, {
- connectorId: requestPayload.connectorId,
- idTag: requestPayload.idTag,
- }),
+ >(this.chargingStation, RequestCommand.START_TRANSACTION, requestPayload),
],
[
BroadcastChannelProcedureName.STOP_TRANSACTION,
StopTransactionRequest,
StartTransactionResponse
>(this.chargingStation, RequestCommand.STOP_TRANSACTION, {
- transactionId: requestPayload.transactionId,
meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId(
requestPayload.transactionId,
true
),
- idTag: requestPayload.idTag,
- reason: requestPayload.reason,
+ ...requestPayload,
}),
],
[
- BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
- (requestPayload?: BroadcastChannelRequestPayload) =>
- this.chargingStation.startAutomaticTransactionGenerator(requestPayload.connectorIds),
+ BroadcastChannelProcedureName.AUTHORIZE,
+ async (requestPayload?: BroadcastChannelRequestPayload) =>
+ this.chargingStation.ocppRequestService.requestHandler<
+ AuthorizeRequest,
+ AuthorizeResponse
+ >(this.chargingStation, RequestCommand.AUTHORIZE, requestPayload),
],
[
- BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
- (requestPayload?: BroadcastChannelRequestPayload) =>
- this.chargingStation.stopAutomaticTransactionGenerator(requestPayload.connectorIds),
+ BroadcastChannelProcedureName.BOOT_NOTIFICATION,
+ async (requestPayload?: BroadcastChannelRequestPayload) => {
+ this.chargingStation.bootNotificationResponse =
+ await this.chargingStation.ocppRequestService.requestHandler<
+ BootNotificationRequest,
+ BootNotificationResponse
+ >(
+ this.chargingStation,
+ RequestCommand.BOOT_NOTIFICATION,
+ {
+ ...this.chargingStation.bootNotificationRequest,
+ ...requestPayload,
+ },
+ {
+ skipBufferingOnError: true,
+ }
+ );
+ return this.chargingStation.bootNotificationResponse;
+ },
],
[
BroadcastChannelProcedureName.STATUS_NOTIFICATION,
this.chargingStation.ocppRequestService.requestHandler<
StatusNotificationRequest,
StatusNotificationResponse
- >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, {
- connectorId: requestPayload.connectorId,
- errorCode: requestPayload.errorCode,
- status: requestPayload.status,
- ...(requestPayload.info && { info: requestPayload.info }),
- ...(requestPayload.timestamp && { timestamp: requestPayload.timestamp }),
- ...(requestPayload.vendorId && { vendorId: requestPayload.vendorId }),
- ...(requestPayload.vendorErrorCode && {
- vendorErrorCode: requestPayload.vendorErrorCode,
- }),
- }),
+ >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, requestPayload),
],
[
BroadcastChannelProcedureName.HEARTBEAT,
- async (requestPayload?: BroadcastChannelRequestPayload) => {
- delete requestPayload.hashId;
- delete requestPayload.hashIds;
- delete requestPayload.connectorIds;
- return this.chargingStation.ocppRequestService.requestHandler<
+ async (requestPayload?: BroadcastChannelRequestPayload) =>
+ this.chargingStation.ocppRequestService.requestHandler<
HeartbeatRequest,
HeartbeatResponse
- >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload);
+ >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload),
+ ],
+ [
+ BroadcastChannelProcedureName.METER_VALUES,
+ async (requestPayload?: BroadcastChannelRequestPayload) => {
+ const configuredMeterValueSampleInterval =
+ ChargingStationConfigurationUtils.getConfigurationKey(
+ chargingStation,
+ StandardParametersKey.MeterValueSampleInterval
+ );
+ return this.chargingStation.ocppRequestService.requestHandler<
+ MeterValuesRequest,
+ MeterValuesResponse
+ >(this.chargingStation, RequestCommand.METER_VALUES, {
+ meterValue: [
+ OCPP16ServiceUtils.buildMeterValue(
+ this.chargingStation,
+ requestPayload.connectorId,
+ this.chargingStation.getConnectorStatus(requestPayload.connectorId)?.transactionId,
+ configuredMeterValueSampleInterval
+ ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000
+ : Constants.DEFAULT_METER_VALUES_INTERVAL
+ ),
+ ],
+ ...requestPayload,
+ });
},
],
]);
return;
}
const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest;
-
if (
requestPayload?.hashIds !== undefined &&
requestPayload?.hashIds?.includes(this.chargingStation.stationInfo.hashId) === false
);
return;
}
-
let responsePayload: BroadcastChannelResponsePayload;
let commandResponse: CommandResponse | void;
try {
status: ResponseStatus.SUCCESS,
};
} else {
- responsePayload = {
- hashId: this.chargingStation.stationInfo.hashId,
- status: this.commandResponseToResponseStatus(command, commandResponse as CommandResponse),
- };
+ responsePayload = this.commandResponseToResponsePayload(
+ command,
+ requestPayload,
+ commandResponse as CommandResponse
+ );
}
} catch (error) {
logger.error(
errorStack: (error as Error).stack,
errorDetails: (error as OCPPError).details,
};
+ } finally {
+ this.sendResponse([uuid, responsePayload]);
}
- this.sendResponse([uuid, responsePayload]);
}
private messageErrorHandler(messageEvent: MessageEvent): void {
logger.error(
`${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`,
- { messageEvent }
+ messageEvent
);
}
requestPayload: BroadcastChannelRequestPayload
): Promise<CommandResponse | void> {
if (this.commandHandlers.has(command) === true) {
+ this.cleanRequestPayload(command, requestPayload);
return this.commandHandlers.get(command)(requestPayload);
}
throw new BaseError(`Unknown worker broadcast channel command: ${command}`);
}
+ private cleanRequestPayload(
+ command: BroadcastChannelProcedureName,
+ requestPayload: BroadcastChannelRequestPayload
+ ): void {
+ delete requestPayload.hashId;
+ delete requestPayload.hashIds;
+ [
+ BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR,
+ BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR,
+ ].includes(command) === false && delete requestPayload.connectorIds;
+ }
+
+ private commandResponseToResponsePayload(
+ command: BroadcastChannelProcedureName,
+ requestPayload: BroadcastChannelRequestPayload,
+ commandResponse: CommandResponse
+ ): BroadcastChannelResponsePayload {
+ const responseStatus = this.commandResponseToResponseStatus(command, commandResponse);
+ if (responseStatus === ResponseStatus.SUCCESS) {
+ return {
+ hashId: this.chargingStation.stationInfo.hashId,
+ status: responseStatus,
+ };
+ }
+ return {
+ hashId: this.chargingStation.stationInfo.hashId,
+ status: responseStatus,
+ command,
+ requestPayload,
+ commandResponse,
+ };
+ }
+
private commandResponseToResponseStatus(
command: BroadcastChannelProcedureName,
commandResponse: CommandResponse
switch (command) {
case BroadcastChannelProcedureName.START_TRANSACTION:
case BroadcastChannelProcedureName.STOP_TRANSACTION:
+ case BroadcastChannelProcedureName.AUTHORIZE:
if (
- (commandResponse as StartTransactionResponse | StopTransactionResponse)?.idTagInfo
- ?.status === AuthorizationStatus.ACCEPTED
+ (
+ commandResponse as
+ | StartTransactionResponse
+ | StopTransactionResponse
+ | AuthorizeResponse
+ )?.idTagInfo?.status === AuthorizationStatus.ACCEPTED
) {
return ResponseStatus.SUCCESS;
}
return ResponseStatus.FAILURE;
+ case BroadcastChannelProcedureName.BOOT_NOTIFICATION:
+ if (commandResponse?.status === RegistrationStatus.ACCEPTED) {
+ return ResponseStatus.SUCCESS;
+ }
+ return ResponseStatus.FAILURE;
case BroadcastChannelProcedureName.STATUS_NOTIFICATION:
+ case BroadcastChannelProcedureName.METER_VALUES:
if (Utils.isEmptyObject(commandResponse) === true) {
return ResponseStatus.SUCCESS;
}