import { parentPort } from 'node:worker_threads';
import type { JSONSchemaType } from 'ajv';
+import { secondsToMilliseconds } from 'date-fns';
import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
import {
type ChargingStation,
- ChargingStationConfigurationUtils,
+ addConfigurationKey,
+ getConfigurationKey,
+ hasReservationExpired,
resetConnectorStatus,
} from '../../../charging-station';
import { OCPPError } from '../../../exception';
import {
- type ChangeAvailabilityResponse,
type ChangeConfigurationResponse,
type ClearChargingProfileResponse,
ErrorType,
type OCPP16AuthorizeRequest,
type OCPP16AuthorizeResponse,
type OCPP16BootNotificationResponse,
+ type OCPP16ChangeAvailabilityResponse,
OCPP16ChargePointStatus,
type OCPP16DataTransferResponse,
type OCPP16DiagnosticsStatusNotificationResponse,
type OCPP16UpdateFirmwareResponse,
OCPPVersion,
RegistrationStatusEnumType,
+ ReservationTerminationReason,
type ResponseHandler,
type SetChargingProfileResponse,
type UnlockConnectorResponse,
// }
super(OCPPVersion.VERSION_16);
this.responseHandlers = new Map<OCPP16RequestCommand, ResponseHandler>([
- [OCPP16RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
- [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)],
- [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this)],
- [OCPP16RequestCommand.START_TRANSACTION, this.handleResponseStartTransaction.bind(this)],
- [OCPP16RequestCommand.STOP_TRANSACTION, this.handleResponseStopTransaction.bind(this)],
- [OCPP16RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
- [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this)],
- [OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
- [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this)],
- [OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)],
+ [
+ OCPP16RequestCommand.BOOT_NOTIFICATION,
+ this.handleResponseBootNotification.bind(this) as ResponseHandler,
+ ],
+ [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this) as ResponseHandler],
+ [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this) as ResponseHandler],
+ [
+ OCPP16RequestCommand.START_TRANSACTION,
+ this.handleResponseStartTransaction.bind(this) as ResponseHandler,
+ ],
+ [
+ OCPP16RequestCommand.STOP_TRANSACTION,
+ this.handleResponseStopTransaction.bind(this) as ResponseHandler,
+ ],
+ [
+ OCPP16RequestCommand.STATUS_NOTIFICATION,
+ this.emptyResponseHandler.bind(this) as ResponseHandler,
+ ],
+ [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this) as ResponseHandler],
+ [
+ OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION,
+ this.emptyResponseHandler.bind(this) as ResponseHandler,
+ ],
+ [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this) as ResponseHandler],
+ [
+ OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION,
+ this.emptyResponseHandler.bind(this) as ResponseHandler,
+ ],
]);
this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
[
],
[
OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
- OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityResponse>(
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ChangeAvailabilityResponse>(
'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json',
moduleName,
'constructor',
) => boolean;
}
- public async responseHandler(
+ public async responseHandler<ReqType extends JsonType, ResType extends JsonType>(
chargingStation: ChargingStation,
commandName: OCPP16RequestCommand,
- payload: JsonType,
- requestPayload: JsonType,
+ payload: ResType,
+ requestPayload: ReqType,
): Promise<void> {
if (
chargingStation.isRegistered() === true ||
) {
try {
this.validatePayload(chargingStation, commandName, payload);
- await this.responseHandlers.get(commandName)(chargingStation, payload, requestPayload);
+ await this.responseHandlers.get(commandName)!(chargingStation, payload, requestPayload);
} catch (error) {
logger.error(
`${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`,
return this.validateResponsePayload(
chargingStation,
commandName,
- this.jsonSchemas.get(commandName),
+ this.jsonSchemas.get(commandName)!,
payload,
);
}
payload: OCPP16BootNotificationResponse,
): void {
if (payload.status === RegistrationStatusEnumType.ACCEPTED) {
- ChargingStationConfigurationUtils.addConfigurationKey(
+ addConfigurationKey(
chargingStation,
OCPP16StandardParametersKey.HeartbeatInterval,
payload.interval.toString(),
{},
{ overwrite: true, save: true },
);
- ChargingStationConfigurationUtils.addConfigurationKey(
+ addConfigurationKey(
chargingStation,
OCPP16StandardParametersKey.HeartBeatInterval,
payload.interval.toString(),
payload: OCPP16AuthorizeResponse,
requestPayload: OCPP16AuthorizeRequest,
): void {
- let authorizeConnectorId: number;
+ let authorizeConnectorId: number | undefined;
if (chargingStation.hasEvses) {
for (const [evseId, evseStatus] of chargingStation.evses) {
if (evseId > 0) {
}
const authorizeConnectorIdDefined = !isNullOrUndefined(authorizeConnectorId);
if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
- authorizeConnectorIdDefined &&
- (chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
+ if (authorizeConnectorIdDefined) {
+ // chargingStation.getConnectorStatus(authorizeConnectorId!)!.authorizeIdTag =
+ // requestPayload.idTag;
+ chargingStation.getConnectorStatus(authorizeConnectorId!)!.idTagAuthorized = true;
+ }
logger.debug(
`${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${
authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''
);
} else {
if (authorizeConnectorIdDefined) {
- chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false;
- delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
+ chargingStation.getConnectorStatus(authorizeConnectorId!)!.idTagAuthorized = false;
+ delete chargingStation.getConnectorStatus(authorizeConnectorId!)?.authorizeIdTag;
}
logger.debug(
`${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
true &&
chargingStation.getAuthorizeRemoteTxRequests() === true &&
chargingStation.getLocalAuthListEnabled() === true &&
- chargingStation.hasIdTags() &&
+ chargingStation.hasIdTags() === true &&
chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false
) {
logger.error(
chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
true &&
chargingStation.getAuthorizeRemoteTxRequests() === true &&
- chargingStation.getMustAuthorizeAtRemoteStart() === true &&
+ chargingStation.getRemoteAuthorization() === true &&
chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false &&
chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized === false
) {
);
return;
}
- if (!Number.isInteger(payload.transactionId)) {
+ if (!Number.isSafeInteger(payload.transactionId)) {
logger.warn(
`${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with a non integer transaction id ${
payload.transactionId
}
if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
- chargingStation.getConnectorStatus(transactionConnectorId).transactionStarted = true;
- chargingStation.getConnectorStatus(transactionConnectorId).transactionId =
+ chargingStation.getConnectorStatus(transactionConnectorId)!.transactionStarted = true;
+ chargingStation.getConnectorStatus(transactionConnectorId)!.transactionStart =
+ requestPayload.timestamp;
+ chargingStation.getConnectorStatus(transactionConnectorId)!.transactionId =
payload.transactionId;
- chargingStation.getConnectorStatus(transactionConnectorId).transactionIdTag =
+ chargingStation.getConnectorStatus(transactionConnectorId)!.transactionIdTag =
requestPayload.idTag;
chargingStation.getConnectorStatus(
transactionConnectorId,
- ).transactionEnergyActiveImportRegisterValue = 0;
- chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue =
+ )!.transactionEnergyActiveImportRegisterValue = 0;
+ chargingStation.getConnectorStatus(transactionConnectorId)!.transactionBeginMeterValue =
OCPP16ServiceUtils.buildTransactionBeginMeterValue(
chargingStation,
transactionConnectorId,
requestPayload.meterStart,
);
+ if (requestPayload.reservationId) {
+ const reservation = chargingStation.getReservationBy(
+ 'reservationId',
+ requestPayload.reservationId,
+ )!;
+ if (reservation.idTag !== requestPayload.idTag) {
+ logger.warn(
+ `${chargingStation.logPrefix()} Reserved transaction ${
+ payload.transactionId
+ } started with a different idTag ${requestPayload.idTag} than the reservation one ${
+ reservation.idTag
+ }`,
+ );
+ }
+ if (hasReservationExpired(reservation)) {
+ logger.warn(
+ `${chargingStation.logPrefix()} Reserved transaction ${
+ payload.transactionId
+ } started with expired reservation ${
+ requestPayload.reservationId
+ } (expiry date: ${reservation.expiryDate.toISOString()}))`,
+ );
+ }
+ await chargingStation.removeReservation(
+ reservation,
+ ReservationTerminationReason.TRANSACTION_STARTED,
+ );
+ }
chargingStation.getBeginEndMeterValues() &&
(await chargingStation.ocppRequestService.requestHandler<
OCPP16MeterValuesRequest,
connectorId: transactionConnectorId,
transactionId: payload.transactionId,
meterValue: [
- chargingStation.getConnectorStatus(transactionConnectorId).transactionBeginMeterValue,
+ chargingStation.getConnectorStatus(transactionConnectorId)!.transactionBeginMeterValue,
],
- }));
+ } as OCPP16MeterValuesRequest));
await OCPP16ServiceUtils.sendAndSetConnectorStatus(
chargingStation,
transactionConnectorId,
if (chargingStation.stationInfo.powerSharedByConnectors) {
++chargingStation.powerDivider;
}
- const configuredMeterValueSampleInterval =
- ChargingStationConfigurationUtils.getConfigurationKey(
- chargingStation,
- OCPP16StandardParametersKey.MeterValueSampleInterval,
- );
+ const configuredMeterValueSampleInterval = getConfigurationKey(
+ chargingStation,
+ OCPP16StandardParametersKey.MeterValueSampleInterval,
+ );
chargingStation.startMeterValues(
transactionConnectorId,
configuredMeterValueSampleInterval
- ? convertToInt(configuredMeterValueSampleInterval.value) * 1000
+ ? secondsToMilliseconds(convertToInt(configuredMeterValueSampleInterval.value))
: Constants.DEFAULT_METER_VALUES_INTERVAL,
);
} else {
logger.warn(
- `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED with status '${payload
- .idTagInfo?.status}', idTag '${requestPayload.idTag}'`,
+ `${chargingStation.logPrefix()} Starting transaction with id ${payload.transactionId.toString()} REJECTED on ${
+ chargingStation.stationInfo.chargingStationId
+ }#${transactionConnectorId.toString()} with status '${payload.idTagInfo?.status}', idTag '${
+ requestPayload.idTag
+ }'${
+ OCPP16ServiceUtils.hasReservation(
+ chargingStation,
+ transactionConnectorId,
+ requestPayload.idTag,
+ )
+ ? `, reservationId '${requestPayload.reservationId}'`
+ : ''
+ }`,
);
await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
}
chargingStation: ChargingStation,
connectorId: number,
): Promise<void> {
- resetConnectorStatus(chargingStation.getConnectorStatus(connectorId));
+ resetConnectorStatus(chargingStation.getConnectorStatus(connectorId)!);
chargingStation.stopMeterValues(connectorId);
- parentPort?.postMessage(buildUpdatedMessage(chargingStation));
if (
chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
) {
OCPP16ChargePointStatus.Available,
);
}
+ parentPort?.postMessage(buildUpdatedMessage(chargingStation));
}
private async handleResponseStopTransaction(
meterValue: [
OCPP16ServiceUtils.buildTransactionEndMeterValue(
chargingStation,
- transactionConnectorId,
+ transactionConnectorId!,
requestPayload.meterStop,
),
],
}));
if (
chargingStation.isChargingStationAvailable() === false ||
- chargingStation.isConnectorAvailable(transactionConnectorId) === false
+ chargingStation.isConnectorAvailable(transactionConnectorId!) === false
) {
await OCPP16ServiceUtils.sendAndSetConnectorStatus(
chargingStation,
- transactionConnectorId,
+ transactionConnectorId!,
OCPP16ChargePointStatus.Unavailable,
);
} else {
await OCPP16ServiceUtils.sendAndSetConnectorStatus(
chargingStation,
- transactionConnectorId,
+ transactionConnectorId!,
OCPP16ChargePointStatus.Available,
);
}
if (chargingStation.stationInfo.powerSharedByConnectors) {
chargingStation.powerDivider--;
}
- resetConnectorStatus(chargingStation.getConnectorStatus(transactionConnectorId));
- chargingStation.stopMeterValues(transactionConnectorId);
+ resetConnectorStatus(chargingStation.getConnectorStatus(transactionConnectorId!)!);
+ chargingStation.stopMeterValues(transactionConnectorId!);
parentPort?.postMessage(buildUpdatedMessage(chargingStation));
const logMsg = `${chargingStation.logPrefix()} Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${
chargingStation.stationInfo.chargingStationId