connectorId: number
): number | undefined {
let limit: number, matchingChargingProfile: ChargingProfile;
- let chargingProfiles: ChargingProfile[] = [];
// Get charging profiles for connector and sort by stack level
- chargingProfiles = chargingStation
- .getConnectorStatus(connectorId)
- ?.chargingProfiles?.sort((a, b) => b.stackLevel - a.stackLevel);
+ const chargingProfiles = Utils.cloneObject(
+ chargingStation
+ .getConnectorStatus(connectorId)
+ ?.chargingProfiles?.sort((a, b) => b.stackLevel - a.stackLevel) ?? []
+ );
// Get profiles on connector 0
if (chargingStation.getConnectorStatus(0)?.chargingProfiles) {
chargingProfiles.push(
matchingChargingProfile: ChargingProfile;
} | null {
const debugLogMsg = `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`;
+ const currentMoment = moment();
+ const currentDate = new Date();
for (const chargingProfile of chargingProfiles) {
// Set helpers
- const currentMoment = moment();
const chargingSchedule = chargingProfile.chargingSchedule;
+ if (!chargingSchedule?.startSchedule) {
+ logger.warn(
+ `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not defined in charging profile id ${chargingProfile.chargingProfileId}`
+ );
+ }
// Check type (recurring) and if it is already active
// Adjust the daily recurring schedule to today
if (
chargingProfile.recurrencyKind === RecurrencyKindType.DAILY &&
currentMoment.isAfter(chargingSchedule.startSchedule)
) {
- const currentDate = new Date();
- chargingSchedule.startSchedule = new Date(chargingSchedule.startSchedule);
+ if (!(chargingSchedule?.startSchedule instanceof Date)) {
+ logger.warn(
+ `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: startSchedule is not a Date object in charging profile id ${chargingProfile.chargingProfileId}. Trying to convert it to a Date object`
+ );
+ chargingSchedule.startSchedule = new Date(chargingSchedule.startSchedule);
+ }
chargingSchedule.startSchedule.setFullYear(
currentDate.getFullYear(),
currentDate.getMonth(),
type ClearChargingProfileResponse,
ErrorType,
type GenericResponse,
+ GenericStatus,
type GetConfigurationRequest,
type GetConfigurationResponse,
type GetDiagnosticsRequest,
OCPP16ChargePointStatus,
type OCPP16ChargingProfile,
OCPP16ChargingProfilePurposeType,
+ type OCPP16ChargingSchedule,
type OCPP16ClearCacheRequest,
type OCPP16DataTransferRequest,
type OCPP16DataTransferResponse,
OCPP16FirmwareStatus,
type OCPP16FirmwareStatusNotificationRequest,
type OCPP16FirmwareStatusNotificationResponse,
+ type OCPP16GetCompositeScheduleRequest,
+ type OCPP16GetCompositeScheduleResponse,
type OCPP16HeartbeatRequest,
type OCPP16HeartbeatResponse,
OCPP16IncomingRequestCommand,
OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION,
this.handleRequestChangeConfiguration.bind(this),
],
+ [
+ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
+ this.handleRequestGetCompositeSchedule.bind(this),
+ ],
[
OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
this.handleRequestSetChargingProfile.bind(this),
'constructor'
),
],
+ [
+ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleRequest>(
+ '../../../assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json',
+ moduleName,
+ 'constructor'
+ ),
+ ],
[
OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileRequest>(
return OCPPConstants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED;
}
+ private handleRequestGetCompositeSchedule(
+ chargingStation: ChargingStation,
+ commandPayload: OCPP16GetCompositeScheduleRequest
+ ): OCPP16GetCompositeScheduleResponse {
+ if (
+ OCPP16ServiceUtils.checkFeatureProfile(
+ chargingStation,
+ OCPP16SupportedFeatureProfiles.SmartCharging,
+ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE
+ ) === false
+ ) {
+ return OCPPConstants.OCPP_RESPONSE_REJECTED;
+ }
+ if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
+ logger.error(
+ `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector Id ${
+ commandPayload.connectorId
+ }`
+ );
+ return OCPPConstants.OCPP_RESPONSE_REJECTED;
+ }
+ if (
+ Utils.isEmptyArray(
+ chargingStation.getConnectorStatus(commandPayload.connectorId)?.chargingProfiles
+ )
+ ) {
+ return OCPPConstants.OCPP_RESPONSE_REJECTED;
+ }
+ const startDate = new Date();
+ const endDate = new Date(startDate.getTime() + commandPayload.duration * 1000);
+ let compositeSchedule: OCPP16ChargingSchedule;
+ for (const chargingProfile of chargingStation.getConnectorStatus(commandPayload.connectorId)
+ .chargingProfiles) {
+ // FIXME: build the composite schedule including the local power limit, the stack level, the charging rate unit, etc.
+ if (
+ chargingProfile.chargingSchedule?.startSchedule >= startDate &&
+ chargingProfile.chargingSchedule?.startSchedule <= endDate
+ ) {
+ compositeSchedule = chargingProfile.chargingSchedule;
+ break;
+ }
+ }
+ return {
+ status: GenericStatus.Accepted,
+ scheduleStart: compositeSchedule?.startSchedule,
+ connectorId: commandPayload.connectorId,
+ chargingSchedule: compositeSchedule,
+ };
+ }
+
private handleRequestClearChargingProfile(
chargingStation: ChargingStation,
commandPayload: ClearChargingProfileRequest
type OCPP16DataTransferResponse,
type OCPP16DiagnosticsStatusNotificationResponse,
type OCPP16FirmwareStatusNotificationResponse,
+ type OCPP16GetCompositeScheduleResponse,
type OCPP16HeartbeatResponse,
OCPP16IncomingRequestCommand,
type OCPP16MeterValuesRequest,
'constructor'
),
],
+ [
+ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE,
+ OCPP16ServiceUtils.parseJsonSchemaFile<OCPP16GetCompositeScheduleResponse>(
+ '../../../assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json',
+ moduleName,
+ 'constructor'
+ ),
+ ],
[
OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE,
OCPP16ServiceUtils.parseJsonSchemaFile<SetChargingProfileResponse>(
OCPP16ChargePointStatus,
type OCPP16ChargingProfile,
OCPP16ChargingProfilePurposeType,
+ type OCPP16ChargingSchedule,
type OCPP16ClearCacheRequest,
type OCPP16DataTransferRequest,
type OCPP16DataTransferResponse,
OCPP16FirmwareStatus,
type OCPP16FirmwareStatusNotificationRequest,
type OCPP16FirmwareStatusNotificationResponse,
+ type OCPP16GetCompositeScheduleRequest,
+ type OCPP16GetCompositeScheduleResponse,
type OCPP16HeartbeatRequest,
type OCPP16HeartbeatResponse,
OCPP16IncomingRequestCommand,
recurrencyKind?: OCPP16RecurrencyKindType;
validFrom?: Date;
validTo?: Date;
- chargingSchedule: ChargingSchedule;
+ chargingSchedule: OCPP16ChargingSchedule;
}
-interface ChargingSchedule extends JsonObject {
+export interface OCPP16ChargingSchedule extends JsonObject {
duration?: number;
startSchedule?: Date;
chargingRateUnit: OCPP16ChargingRateUnitType;
OCPP16ChargePointStatus,
OCPP16ChargingProfile,
OCPP16ChargingProfilePurposeType,
+ OCPP16ChargingRateUnitType,
OCPP16DiagnosticsStatus,
OCPP16StandardParametersKey,
OCPP16VendorParametersKey,
UNLOCK_CONNECTOR = 'UnlockConnector',
GET_CONFIGURATION = 'GetConfiguration',
CHANGE_CONFIGURATION = 'ChangeConfiguration',
+ GET_COMPOSITE_SCHEDULE = 'GetCompositeSchedule',
SET_CHARGING_PROFILE = 'SetChargingProfile',
CLEAR_CHARGING_PROFILE = 'ClearChargingProfile',
REMOTE_START_TRANSACTION = 'RemoteStartTransaction',
type: ResetType;
}
+export interface OCPP16GetCompositeScheduleRequest extends JsonObject {
+ connectorId: number;
+ duration: number;
+ chargingRateUnit?: OCPP16ChargingRateUnitType;
+}
+
export interface SetChargingProfileRequest extends JsonObject {
connectorId: number;
csChargingProfiles: OCPP16ChargingProfile;
import type {
EmptyObject,
+ GenericStatus,
JsonObject,
+ OCPP16ChargingSchedule,
OCPPConfigurationKey,
RegistrationStatusEnumType,
} from '../../internal';
NOT_SUPPORTED = 'NotSupported',
}
+export interface OCPP16GetCompositeScheduleResponse extends JsonObject {
+ status: GenericStatus;
+ connectorId?: number;
+ scheduleStart?: Date;
+ chargingSchedule?: OCPP16ChargingSchedule;
+}
+
export interface SetChargingProfileResponse extends JsonObject {
status: OCPP16ChargingProfileStatus;
}