Skip to content
This repository has been archived by the owner on Jun 12, 2024. It is now read-only.

Commit

Permalink
Merge pull request #230 from OliveGerste/Extend-charging-profile-support
Browse files Browse the repository at this point in the history
  • Loading branch information
Jérôme Benoit authored May 7, 2022
2 parents 5cc4b63 + 3e0905a commit a68137e
Showing 1 changed file with 113 additions and 65 deletions.
178 changes: 113 additions & 65 deletions src/charging-station/ChargingStation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
ChargingRateUnitType,
ChargingSchedulePeriod,
} from '../types/ocpp/ChargingProfile';
import { ChargingProfileKindType, RecurrencyKindType } from '../types/ocpp/1.6/ChargingProfile';
import ChargingStationConfiguration, { Section } from '../types/ChargingStationConfiguration';
import ChargingStationOcppConfiguration, {
ConfigurationKey,
Expand Down Expand Up @@ -82,6 +83,7 @@ import Utils from '../utils/Utils';
import crypto from 'crypto';
import fs from 'fs';
import logger from '../utils/Logger';
import moment from 'moment';
import { parentPort } from 'worker_threads';
import path from 'path';

Expand Down Expand Up @@ -738,80 +740,60 @@ export default class ChargingStation {
}

public getChargingProfilePowerLimit(connectorId: number): number | undefined {
const timestamp = new Date().getTime();
let limit: number;
let matchingChargingProfile: ChargingProfile;
let chargingSchedulePeriods: ChargingSchedulePeriod[] = [];
if (!Utils.isEmptyArray(this.getConnectorStatus(connectorId)?.chargingProfiles)) {
const chargingProfiles: ChargingProfile[] = this.getConnectorStatus(
connectorId
).chargingProfiles.filter(
(chargingProfile) =>
timestamp >= chargingProfile.chargingSchedule?.startSchedule.getTime() &&
timestamp <
chargingProfile.chargingSchedule?.startSchedule.getTime() +
chargingProfile.chargingSchedule.duration * 1000 &&
chargingProfile?.stackLevel === Math.max(...chargingProfiles.map((cp) => cp?.stackLevel))
let chargingProfiles: ChargingProfile[] = [];
// Get profiles for connector and sort by stack level
chargingProfiles = this.getConnectorStatus(connectorId).chargingProfiles.sort(
(a, b) => b.stackLevel - a.stackLevel
);
// Get profiles on connector 0
if (this.getConnectorStatus(0).chargingProfiles) {
chargingProfiles.push(
...this.getConnectorStatus(0).chargingProfiles.sort((a, b) => b.stackLevel - a.stackLevel)
);
if (!Utils.isEmptyArray(chargingProfiles)) {
for (const chargingProfile of chargingProfiles) {
if (!Utils.isEmptyArray(chargingProfile.chargingSchedule.chargingSchedulePeriod)) {
chargingSchedulePeriods =
chargingProfile.chargingSchedule.chargingSchedulePeriod.filter(
(chargingSchedulePeriod, index) => {
timestamp >=
chargingProfile.chargingSchedule.startSchedule.getTime() +
chargingSchedulePeriod.startPeriod * 1000 &&
((chargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1] &&
timestamp <
chargingProfile.chargingSchedule.startSchedule.getTime() +
chargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1]
?.startPeriod *
1000) ||
!chargingProfile.chargingSchedule.chargingSchedulePeriod[index + 1]);
}
);
if (!Utils.isEmptyArray(chargingSchedulePeriods)) {
matchingChargingProfile = chargingProfile;
break;
}
}
}
}
}
let limit: number;
if (!Utils.isEmptyArray(chargingSchedulePeriods)) {
switch (this.getCurrentOutType()) {
case CurrentType.AC:
limit =
matchingChargingProfile.chargingSchedule.chargingRateUnit === ChargingRateUnitType.WATT
? chargingSchedulePeriods[0].limit
: ACElectricUtils.powerTotal(
this.getNumberOfPhases(),
this.getVoltageOut(),
chargingSchedulePeriods[0].limit
);
break;
case CurrentType.DC:
limit =
matchingChargingProfile.chargingSchedule.chargingRateUnit === ChargingRateUnitType.WATT
? chargingSchedulePeriods[0].limit
: DCElectricUtils.power(this.getVoltageOut(), chargingSchedulePeriods[0].limit);
if (!Utils.isEmptyArray(chargingProfiles)) {
const result = this.getCurrentLimitFromOCPPProfiles(chargingProfiles);
if (!Utils.isNullOrUndefined(result)) {
limit = result.limit;
matchingChargingProfile = result.matchingChargingProfile;
switch (this.getCurrentOutType()) {
case CurrentType.AC:
limit =
matchingChargingProfile.chargingSchedule.chargingRateUnit ===
ChargingRateUnitType.WATT
? limit
: ACElectricUtils.powerTotal(this.getNumberOfPhases(), this.getVoltageOut(), limit);
break;
case CurrentType.DC:
limit =
matchingChargingProfile.chargingSchedule.chargingRateUnit ===
ChargingRateUnitType.WATT
? limit
: DCElectricUtils.power(this.getVoltageOut(), limit);
}

const connectorMaximumPower = this.getMaximumPower() / this.stationInfo.powerDivider;
if (limit > connectorMaximumPower) {
logger.error(
`${this.logPrefix()} Charging profile id ${
matchingChargingProfile.chargingProfileId
} limit is greater than connector id ${connectorId} maximum, dump charging profiles' stack: %j`,
this.getConnectorStatus(connectorId).chargingProfiles
);
limit = connectorMaximumPower;
}
}
}
const connectorMaximumPower = this.getMaximumPower() / this.stationInfo.powerDivider;
if (limit > connectorMaximumPower) {
logger.error(
`${this.logPrefix()} Charging profile id ${
matchingChargingProfile.chargingProfileId
} limit is greater than connector id ${connectorId} maximum, dump charging profiles' stack: %j`,
this.getConnectorStatus(connectorId).chargingProfiles
);
limit = connectorMaximumPower;
}
return limit;
}

public setChargingProfile(connectorId: number, cp: ChargingProfile): void {
// Add support for connectorID 0
if (!Array.isArray(this.getConnectorStatus(connectorId).chargingProfiles)) {
this.getConnectorStatus(connectorId).chargingProfiles = [];
}
let cpReplaced = false;
if (!Utils.isEmptyArray(this.getConnectorStatus(connectorId).chargingProfiles)) {
this.getConnectorStatus(connectorId).chargingProfiles?.forEach(
Expand Down Expand Up @@ -2248,4 +2230,70 @@ export default class ChargingStation {
this.getConnectorStatus(connectorId).energyActiveImportRegisterValue = 0;
this.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue = 0;
}

private getCurrentLimitFromOCPPProfiles(chargingProfiles: ChargingProfile[]): {
limit: number;
matchingChargingProfile: ChargingProfile;
} {
for (const chargingProfile of chargingProfiles) {
// Profiles should already be sorted by connectorID and Stack Level (highest stack level has prio)
// Set helpers
const now = moment();
const chargingSchedule = chargingProfile.chargingSchedule;
// Check type (Recurring) and if it is already active
// Adjust the Daily Recurring Schedule to today
if (
chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING &&
chargingProfile.recurrencyKind === RecurrencyKindType.DAILY &&
now.isAfter(chargingSchedule.startSchedule)
) {
const currentDate = new Date();
chargingSchedule.startSchedule = new Date(chargingSchedule.startSchedule);
chargingSchedule.startSchedule.setFullYear(
currentDate.getFullYear(),
currentDate.getMonth(),
currentDate.getDate()
);
// Check if the start of the schedule is yesterday
if (moment(chargingSchedule.startSchedule).isAfter(now)) {
chargingSchedule.startSchedule.setDate(currentDate.getDate() - 1);
}
} else if (moment(chargingSchedule.startSchedule).isAfter(now)) {
return null;
}
// Check if the Charging Profile is active
if (moment(chargingSchedule.startSchedule).add(chargingSchedule.duration, 's').isAfter(now)) {
let lastButOneSchedule: ChargingSchedulePeriod;
// Search the right Schedule Period
for (const schedulePeriod of chargingSchedule.chargingSchedulePeriod) {
// Handling of only one period
if (
chargingSchedule.chargingSchedulePeriod.length === 1 &&
schedulePeriod.startPeriod === 0
) {
return { limit: schedulePeriod.limit, matchingChargingProfile: chargingProfile };
}
// Find the right Schedule Periods
if (
moment(chargingSchedule.startSchedule).add(schedulePeriod.startPeriod, 's').isAfter(now)
) {
// Found the schedule: Last but one is the correct one
return { limit: lastButOneSchedule.limit, matchingChargingProfile: chargingProfile };
}
// Keep it
lastButOneSchedule = schedulePeriod;
// Handle the last schedule period
if (
schedulePeriod.startPeriod ===
chargingSchedule.chargingSchedulePeriod[
chargingSchedule.chargingSchedulePeriod.length - 1
].startPeriod
) {
return { limit: lastButOneSchedule.limit, matchingChargingProfile: chargingProfile };
}
}
}
}
return null;
}
}

0 comments on commit a68137e

Please sign in to comment.