import { parentPort } from 'node:worker_threads';
import type { JSONSchemaType } from 'ajv';
+import { secondsToMilliseconds } from 'date-fns';
import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
import {
type ChargingStation,
addConfigurationKey,
getConfigurationKey,
+ hasReservationExpired,
resetConnectorStatus,
} from '../../../charging-station';
import { OCPPError } from '../../../exception';
import {
- type ChangeAvailabilityResponse,
type ChangeConfigurationResponse,
type ClearChargingProfileResponse,
ErrorType,
type GenericResponse,
type GetConfigurationResponse,
type GetDiagnosticsResponse,
- type JsonObject,
type JsonType,
OCPP16AuthorizationStatus,
type OCPP16AuthorizeRequest,
type OCPP16AuthorizeResponse,
type OCPP16BootNotificationResponse,
+ type OCPP16ChangeAvailabilityResponse,
OCPP16ChargePointStatus,
type OCPP16DataTransferResponse,
type OCPP16DiagnosticsStatusNotificationResponse,
type OCPP16UpdateFirmwareResponse,
OCPPVersion,
RegistrationStatusEnumType,
+ ReservationTerminationReason,
type ResponseHandler,
type SetChargingProfileResponse,
type UnlockConnectorResponse,
export class OCPP16ResponseService extends OCPPResponseService {
public jsonIncomingRequestResponseSchemas: Map<
OCPP16IncomingRequestCommand,
- JSONSchemaType<JsonObject>
+ JSONSchemaType<JsonType>
>;
private responseHandlers: Map<OCPP16RequestCommand, ResponseHandler>;
- private jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>;
+ private jsonSchemas: Map<OCPP16RequestCommand, JSONSchemaType<JsonType>>;
public constructor() {
// if (new.target?.name === moduleName) {
this.emptyResponseHandler.bind(this) as ResponseHandler,
],
]);
- this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonObject>>([
+ this.jsonSchemas = new Map<OCPP16RequestCommand, JSONSchemaType<JsonType>>([
[
OCPP16RequestCommand.BOOT_NOTIFICATION,
OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16BootNotificationResponse>(
],
[
OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY,
- OCPP16ServiceUtils.parseJsonSchemaFile<ChangeAvailabilityResponse>(
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16ChangeAvailabilityResponse>(
'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json',
moduleName,
'constructor',
ErrorType.NOT_IMPLEMENTED,
`${commandName} is not implemented to handle response PDU ${JSON.stringify(
payload,
- null,
+ undefined,
2,
)}`,
commandName,
ErrorType.SECURITY_ERROR,
`${commandName} cannot be issued to handle response PDU ${JSON.stringify(
payload,
- null,
+ undefined,
2,
)} while the charging station is not registered on the central server.`,
commandName,
}
}
}
+ const authorizeConnectorStatus = chargingStation.getConnectorStatus(authorizeConnectorId!);
const authorizeConnectorIdDefined = !isNullOrUndefined(authorizeConnectorId);
if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) {
- authorizeConnectorIdDefined &&
- (chargingStation.getConnectorStatus(authorizeConnectorId!)!.idTagAuthorized = true);
+ if (authorizeConnectorIdDefined) {
+ // authorizeConnectorStatus!.authorizeIdTag = requestPayload.idTag;
+ authorizeConnectorStatus!.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;
+ authorizeConnectorStatus!.idTagAuthorized = false;
+ delete authorizeConnectorStatus?.authorizeIdTag;
}
logger.debug(
`${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
payload: OCPP16StartTransactionResponse,
requestPayload: OCPP16StartTransactionRequest,
): Promise<void> {
- const transactionConnectorId = requestPayload.connectorId;
- if (
- transactionConnectorId === 0 ||
- chargingStation.hasConnector(transactionConnectorId) === false
- ) {
+ const { connectorId } = requestPayload;
+ if (connectorId === 0 || chargingStation.hasConnector(connectorId) === false) {
logger.error(
- `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${transactionConnectorId.toString()}`,
+ `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${connectorId}`,
);
return;
}
+ const connectorStatus = chargingStation.getConnectorStatus(connectorId);
if (
- chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
- true &&
+ connectorStatus?.transactionRemoteStarted === true &&
chargingStation.getAuthorizeRemoteTxRequests() === true &&
chargingStation.getLocalAuthListEnabled() === true &&
- chargingStation.hasIdTags() &&
- chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false
+ chargingStation.hasIdTags() === true &&
+ connectorStatus?.idTagLocalAuthorized === false
) {
logger.error(
- `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${chargingStation.getConnectorStatus(
- transactionConnectorId,
- )?.localAuthorizeIdTag} on connector id ${transactionConnectorId.toString()}`,
+ `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${connectorStatus?.localAuthorizeIdTag} on connector id ${connectorId}`,
);
- await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
+ await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
}
if (
- chargingStation.getConnectorStatus(transactionConnectorId)?.transactionRemoteStarted ===
- true &&
+ connectorStatus?.transactionRemoteStarted === true &&
chargingStation.getAuthorizeRemoteTxRequests() === true &&
- chargingStation.getMustAuthorizeAtRemoteStart() === true &&
- chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized === false &&
- chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized === false
+ chargingStation.getRemoteAuthorization() === true &&
+ connectorStatus?.idTagLocalAuthorized === false &&
+ connectorStatus?.idTagAuthorized === false
) {
logger.error(
- `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${chargingStation.getConnectorStatus(
- transactionConnectorId,
- )?.authorizeIdTag} on connector id ${transactionConnectorId.toString()}`,
+ `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${connectorStatus?.authorizeIdTag} on connector id ${connectorId}`,
);
- await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
+ await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
}
if (
- chargingStation.getConnectorStatus(transactionConnectorId)?.idTagAuthorized &&
- chargingStation.getConnectorStatus(transactionConnectorId)?.authorizeIdTag !==
- requestPayload.idTag
+ connectorStatus?.idTagAuthorized &&
+ connectorStatus?.authorizeIdTag !== requestPayload.idTag
) {
logger.error(
`${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
requestPayload.idTag
- } different from the authorize request one ${chargingStation.getConnectorStatus(
- transactionConnectorId,
- )?.authorizeIdTag} on connector id ${transactionConnectorId.toString()}`,
+ } different from the authorize request one ${connectorStatus?.authorizeIdTag} on connector id ${connectorId}`,
);
- await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
+ await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
}
if (
- chargingStation.getConnectorStatus(transactionConnectorId)?.idTagLocalAuthorized &&
- chargingStation.getConnectorStatus(transactionConnectorId)?.localAuthorizeIdTag !==
- requestPayload.idTag
+ connectorStatus?.idTagLocalAuthorized &&
+ connectorStatus?.localAuthorizeIdTag !== requestPayload.idTag
) {
logger.error(
`${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${
requestPayload.idTag
- } different from the local authorized one ${chargingStation.getConnectorStatus(
- transactionConnectorId,
- )?.localAuthorizeIdTag} on connector id ${transactionConnectorId.toString()}`,
+ } different from the local authorized one ${connectorStatus?.localAuthorizeIdTag} on connector id ${connectorId}`,
);
- await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
+ await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
}
- if (chargingStation.getConnectorStatus(transactionConnectorId)?.transactionStarted === true) {
+ if (connectorStatus?.transactionStarted === true) {
logger.error(
- `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${transactionConnectorId.toString()}:`,
- chargingStation.getConnectorStatus(transactionConnectorId),
+ `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${connectorId} by idTag ${connectorStatus?.transactionIdTag}`,
);
return;
}
if (chargingStation.hasEvses) {
for (const [evseId, evseStatus] of chargingStation.evses) {
if (evseStatus.connectors.size > 1) {
- for (const [connectorId, connectorStatus] of evseStatus.connectors) {
- if (
- transactionConnectorId !== connectorId &&
- connectorStatus?.transactionStarted === true
- ) {
+ for (const [id, status] of evseStatus.connectors) {
+ if (id !== connectorId && status?.transactionStarted === true) {
logger.error(
- `${chargingStation.logPrefix()} Trying to start a transaction on an already used evse id ${evseId.toString()}:`,
- evseStatus,
- );
- await this.resetConnectorOnStartTransactionError(
- chargingStation,
- transactionConnectorId,
+ `${chargingStation.logPrefix()} Trying to start a transaction on an already used evse id ${evseId} by connector id ${id} with idTag ${status?.transactionIdTag}`,
);
+ await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
}
}
}
}
if (
- chargingStation.getConnectorStatus(transactionConnectorId)?.status !==
- OCPP16ChargePointStatus.Available &&
- chargingStation.getConnectorStatus(transactionConnectorId)?.status !==
- OCPP16ChargePointStatus.Preparing
+ connectorStatus?.status !== OCPP16ChargePointStatus.Available &&
+ connectorStatus?.status !== OCPP16ChargePointStatus.Preparing
) {
logger.error(
- `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${transactionConnectorId.toString()} with status ${chargingStation.getConnectorStatus(
- transactionConnectorId,
- )?.status}`,
+ `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId} with status ${connectorStatus?.status}`,
);
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 ${
+ `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId} with a non integer transaction id ${
payload.transactionId
}, converting to integer`,
);
}
if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) {
- chargingStation.getConnectorStatus(transactionConnectorId)!.transactionStarted = true;
- chargingStation.getConnectorStatus(transactionConnectorId)!.transactionStart =
- requestPayload.timestamp;
- chargingStation.getConnectorStatus(transactionConnectorId)!.transactionId =
- payload.transactionId;
- chargingStation.getConnectorStatus(transactionConnectorId)!.transactionIdTag =
- requestPayload.idTag;
- chargingStation.getConnectorStatus(
- transactionConnectorId,
- )!.transactionEnergyActiveImportRegisterValue = 0;
- chargingStation.getConnectorStatus(transactionConnectorId)!.transactionBeginMeterValue =
+ connectorStatus.transactionStarted = true;
+ connectorStatus.transactionStart = requestPayload.timestamp;
+ connectorStatus.transactionId = payload.transactionId;
+ connectorStatus.transactionIdTag = requestPayload.idTag;
+ connectorStatus.transactionEnergyActiveImportRegisterValue = 0;
+ connectorStatus.transactionBeginMeterValue =
OCPP16ServiceUtils.buildTransactionBeginMeterValue(
chargingStation,
- transactionConnectorId,
+ connectorId,
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,
OCPP16MeterValuesResponse
>(chargingStation, OCPP16RequestCommand.METER_VALUES, {
- connectorId: transactionConnectorId,
+ connectorId,
transactionId: payload.transactionId,
- meterValue: [
- chargingStation.getConnectorStatus(transactionConnectorId)!.transactionBeginMeterValue,
- ],
+ meterValue: [connectorStatus.transactionBeginMeterValue],
} as OCPP16MeterValuesRequest));
await OCPP16ServiceUtils.sendAndSetConnectorStatus(
chargingStation,
- transactionConnectorId,
+ connectorId,
OCPP16ChargePointStatus.Charging,
);
logger.info(
- `${chargingStation.logPrefix()} Transaction with id ${payload.transactionId.toString()} STARTED on ${
+ `${chargingStation.logPrefix()} Transaction with id ${payload.transactionId} STARTED on ${
chargingStation.stationInfo.chargingStationId
- }#${transactionConnectorId.toString()} for idTag '${requestPayload.idTag}'`,
+ }#${connectorId} for idTag '${requestPayload.idTag}'`,
);
if (chargingStation.stationInfo.powerSharedByConnectors) {
++chargingStation.powerDivider;
OCPP16StandardParametersKey.MeterValueSampleInterval,
);
chargingStation.startMeterValues(
- transactionConnectorId,
+ connectorId,
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
+ } REJECTED on ${
+ chargingStation.stationInfo.chargingStationId
+ }#${connectorId} with status '${payload.idTagInfo?.status}', idTag '${
+ requestPayload.idTag
+ }'${
+ OCPP16ServiceUtils.hasReservation(chargingStation, connectorId, requestPayload.idTag)
+ ? `, reservationId '${requestPayload.reservationId}'`
+ : ''
+ }`,
);
- await this.resetConnectorOnStartTransactionError(chargingStation, transactionConnectorId);
+ await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
}
}
chargingStation: ChargingStation,
connectorId: number,
): Promise<void> {
- resetConnectorStatus(chargingStation.getConnectorStatus(connectorId)!);
+ const connectorStatus = chargingStation.getConnectorStatus(connectorId);
+ resetConnectorStatus(connectorStatus!);
chargingStation.stopMeterValues(connectorId);
- parentPort?.postMessage(buildUpdatedMessage(chargingStation));
- if (
- chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.Available
- ) {
+ if (connectorStatus?.status !== OCPP16ChargePointStatus.Available) {
await OCPP16ServiceUtils.sendAndSetConnectorStatus(
chargingStation,
connectorId,
OCPP16ChargePointStatus.Available,
);
}
+ parentPort?.postMessage(buildUpdatedMessage(chargingStation));
}
private async handleResponseStopTransaction(
);
if (isNullOrUndefined(transactionConnectorId)) {
logger.error(
- `${chargingStation.logPrefix()} Trying to stop a non existing transaction with id ${requestPayload.transactionId.toString()}`,
+ `${chargingStation.logPrefix()} Trying to stop a non existing transaction with id ${
+ requestPayload.transactionId
+ }`,
);
return;
}
resetConnectorStatus(chargingStation.getConnectorStatus(transactionConnectorId!)!);
chargingStation.stopMeterValues(transactionConnectorId!);
parentPort?.postMessage(buildUpdatedMessage(chargingStation));
- const logMsg = `${chargingStation.logPrefix()} Transaction with id ${requestPayload.transactionId.toString()} STOPPED on ${
+ const logMsg = `${chargingStation.logPrefix()} Transaction with id ${
+ requestPayload.transactionId
+ } STOPPED on ${
chargingStation.stationInfo.chargingStationId
- }#${transactionConnectorId?.toString()} with status '${
- payload.idTagInfo?.status ?? 'undefined'
- }'`;
+ }#${transactionConnectorId} with status '${payload.idTagInfo?.status ?? 'undefined'}'`;
if (
isNullOrUndefined(payload.idTagInfo) ||
payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED