import type { JSONSchemaType } from 'ajv';
-import { type ChargingStation, getIdTagsFile } from '../../../charging-station';
+import { type ChargingStation, getIdTagsFile, hasFeatureProfile } from '../../../charging-station';
import { OCPPError } from '../../../exception';
import {
+ type ClearChargingProfileRequest,
+ type ConnectorStatus,
CurrentType,
ErrorType,
type JsonType,
featureProfile: OCPP16SupportedFeatureProfiles,
command: OCPP16RequestCommand | OCPP16IncomingRequestCommand,
): boolean {
- if (!chargingStation.hasFeatureProfile(featureProfile)) {
+ if (!hasFeatureProfile(chargingStation, featureProfile)) {
logger.warn(
`${chargingStation.logPrefix()} Trying to '${command}' without '${featureProfile}' feature enabled in ${
OCPP16StandardParametersKey.SupportedFeatureProfiles
OCPP16MeterValueMeasurand.VOLTAGE,
phaseLineToNeutralValue as OCPP16MeterValuePhase,
);
- let voltagePhaseLineToNeutralMeasurandValue: number;
+ let voltagePhaseLineToNeutralMeasurandValue: number | undefined;
if (voltagePhaseLineToNeutralSampledValueTemplate) {
const voltagePhaseLineToNeutralSampledValueTemplateValue =
voltagePhaseLineToNeutralSampledValueTemplate.value
OCPP16MeterValueMeasurand.VOLTAGE,
phaseLineToLineValue as OCPP16MeterValuePhase,
);
- let voltagePhaseLineToLineMeasurandValue: number;
+ let voltagePhaseLineToLineMeasurandValue: number | undefined;
if (voltagePhaseLineToLineSampledValueTemplate) {
const voltagePhaseLineToLineSampledValueTemplateValue =
voltagePhaseLineToLineSampledValueTemplate.value
if (powerSampledValueTemplate) {
OCPP16ServiceUtils.checkMeasurandPowerDivider(
chargingStation,
- powerSampledValueTemplate.measurand,
+ powerSampledValueTemplate.measurand!,
);
const errMsg = `MeterValues measurand ${
powerSampledValueTemplate.measurand ??
powerSampledValueTemplate.measurand ??
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
} measurand value`;
- const powerMeasurandValues = {} as MeasurandValues;
+ const powerMeasurandValues: MeasurandValues = {} as MeasurandValues;
const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1;
const connectorMaximumAvailablePower =
chargingStation.getConnectorMaximumAvailablePower(connectorId);
const connectorMaximumPowerPerPhase = Math.round(
connectorMaximumAvailablePower / chargingStation.getNumberOfPhases(),
);
- const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue) ?? 0;
+ const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue!) ?? 0;
const connectorMinimumPowerPerPhase = Math.round(
connectorMinimumPower / chargingStation.getNumberOfPhases(),
);
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
const phase1FluctuatedValue =
- powerPerPhaseSampledValueTemplates?.L1?.value &&
+ powerPerPhaseSampledValueTemplates.L1?.value &&
getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L1.value,
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
const phase2FluctuatedValue =
- powerPerPhaseSampledValueTemplates?.L2?.value &&
+ powerPerPhaseSampledValueTemplates.L2?.value &&
getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L2.value,
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
const phase3FluctuatedValue =
- powerPerPhaseSampledValueTemplates?.L3?.value &&
+ powerPerPhaseSampledValueTemplates.L3?.value &&
getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
powerPerPhaseSampledValueTemplates.L3.value,
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
powerMeasurandValues.L1 =
- phase1FluctuatedValue ??
- defaultFluctuatedPowerPerPhase ??
+ (phase1FluctuatedValue as number) ??
+ (defaultFluctuatedPowerPerPhase as number) ??
getRandomFloatRounded(
connectorMaximumPowerPerPhase / unitDivider,
connectorMinimumPowerPerPhase / unitDivider,
);
powerMeasurandValues.L2 =
- phase2FluctuatedValue ??
- defaultFluctuatedPowerPerPhase ??
+ (phase2FluctuatedValue as number) ??
+ (defaultFluctuatedPowerPerPhase as number) ??
getRandomFloatRounded(
connectorMaximumPowerPerPhase / unitDivider,
connectorMinimumPowerPerPhase / unitDivider,
);
powerMeasurandValues.L3 =
- phase3FluctuatedValue ??
- defaultFluctuatedPowerPerPhase ??
+ (phase3FluctuatedValue as number) ??
+ (defaultFluctuatedPowerPerPhase as number) ??
getRandomFloatRounded(
connectorMaximumPowerPerPhase / unitDivider,
connectorMinimumPowerPerPhase / unitDivider,
const phaseValue = `L${phase}-N`;
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(
- (powerPerPhaseSampledValueTemplates[`L${phase}`] as SampledValueTemplate) ??
- powerSampledValueTemplate,
- powerMeasurandValues[`L${phase}`] as number,
+ powerPerPhaseSampledValueTemplates[
+ `L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates
+ ]! ?? powerSampledValueTemplate,
+ powerMeasurandValues[`L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates],
undefined,
phaseValue as OCPP16MeterValuePhase,
),
if (currentSampledValueTemplate) {
OCPP16ServiceUtils.checkMeasurandPowerDivider(
chargingStation,
- currentSampledValueTemplate.measurand,
+ currentSampledValueTemplate.measurand!,
);
const errMsg = `MeterValues measurand ${
currentSampledValueTemplate.measurand ??
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
const phase1FluctuatedValue =
- currentPerPhaseSampledValueTemplates?.L1?.value &&
+ currentPerPhaseSampledValueTemplates.L1?.value &&
getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L1.value,
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
const phase2FluctuatedValue =
- currentPerPhaseSampledValueTemplates?.L2?.value &&
+ currentPerPhaseSampledValueTemplates.L2?.value &&
getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L2.value,
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
const phase3FluctuatedValue =
- currentPerPhaseSampledValueTemplates?.L3?.value &&
+ currentPerPhaseSampledValueTemplates.L3?.value &&
getRandomFloatFluctuatedRounded(
OCPP16ServiceUtils.getLimitFromSampledValueTemplateCustomValue(
currentPerPhaseSampledValueTemplates.L3.value,
Constants.DEFAULT_FLUCTUATION_PERCENT,
);
currentMeasurandValues.L1 =
- phase1FluctuatedValue ??
- defaultFluctuatedAmperagePerPhase ??
+ (phase1FluctuatedValue as number) ??
+ (defaultFluctuatedAmperagePerPhase as number) ??
getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
currentMeasurandValues.L2 =
- phase2FluctuatedValue ??
- defaultFluctuatedAmperagePerPhase ??
+ (phase2FluctuatedValue as number) ??
+ (defaultFluctuatedAmperagePerPhase as number) ??
getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
currentMeasurandValues.L3 =
- phase3FluctuatedValue ??
- defaultFluctuatedAmperagePerPhase ??
+ (phase3FluctuatedValue as number) ??
+ (defaultFluctuatedAmperagePerPhase as number) ??
getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage);
} else {
currentMeasurandValues.L1 = currentSampledValueTemplate.value
const phaseValue = `L${phase}`;
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(
- (currentPerPhaseSampledValueTemplates[phaseValue] as SampledValueTemplate) ??
- currentSampledValueTemplate,
- currentMeasurandValues[phaseValue] as number,
+ currentPerPhaseSampledValueTemplates[
+ phaseValue as keyof MeasurandPerPhaseSampledValueTemplates
+ ]! ?? currentSampledValueTemplate,
+ currentMeasurandValues[phaseValue as keyof MeasurandPerPhaseSampledValueTemplates],
undefined,
phaseValue as OCPP16MeterValuePhase,
),
if (energySampledValueTemplate) {
OCPP16ServiceUtils.checkMeasurandPowerDivider(
chargingStation,
- energySampledValueTemplate.measurand,
+ energySampledValueTemplate.measurand!,
);
const unitDivider =
energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
)
: getRandomFloatRounded(connectorMaximumEnergyRounded);
// Persist previous value on connector
- if (
- connector &&
- isNullOrUndefined(connector.energyActiveImportRegisterValue) === false &&
- connector.energyActiveImportRegisterValue >= 0 &&
- isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) === false &&
- connector.transactionEnergyActiveImportRegisterValue >= 0
- ) {
- connector.energyActiveImportRegisterValue += energyValueRounded;
- connector.transactionEnergyActiveImportRegisterValue += energyValueRounded;
- } else {
- connector.energyActiveImportRegisterValue = 0;
- connector.transactionEnergyActiveImportRegisterValue = 0;
+ if (connector) {
+ if (
+ isNullOrUndefined(connector.energyActiveImportRegisterValue) === false &&
+ connector.energyActiveImportRegisterValue! >= 0 &&
+ isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) === false &&
+ connector.transactionEnergyActiveImportRegisterValue! >= 0
+ ) {
+ connector.energyActiveImportRegisterValue! += energyValueRounded;
+ connector.transactionEnergyActiveImportRegisterValue! += energyValueRounded;
+ } else {
+ connector.energyActiveImportRegisterValue = 0;
+ connector.transactionEnergyActiveImportRegisterValue = 0;
+ }
}
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(
`${chargingStation.logPrefix()} MeterValues measurand ${
meterValue.sampledValue[sampledValuesIndex].measurand ??
OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER
- }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${roundTo(
- interval / (3600 * 1000),
- 4,
- )}h`,
+ }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${interval}ms`,
);
}
}
const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(
- sampledValueTemplate,
+ sampledValueTemplate!,
roundTo((meterStart ?? 0) / unitDivider, 4),
MeterValueContext.TRANSACTION_BEGIN,
),
const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1;
meterValue.sampledValue.push(
OCPP16ServiceUtils.buildSampledValue(
- sampledValueTemplate,
+ sampledValueTemplate!,
roundTo((meterStop ?? 0) / unitDivider, 4),
MeterValueContext.TRANSACTION_END,
),
logger.error(
`${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an uninitialized charging profiles array attribute, applying deferred initialization`,
);
- chargingStation.getConnectorStatus(connectorId).chargingProfiles = [];
+ chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = [];
}
if (
Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) === false
logger.error(
`${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an improper attribute type for the charging profiles array, applying proper type initialization`,
);
- chargingStation.getConnectorStatus(connectorId).chargingProfiles = [];
+ chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = [];
}
let cpReplaced = false;
if (isNotEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) {
(chargingProfile.stackLevel === cp.stackLevel &&
chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose)
) {
- chargingStation.getConnectorStatus(connectorId).chargingProfiles[index] = cp;
+ chargingStation.getConnectorStatus(connectorId)!.chargingProfiles![index] = cp;
cpReplaced = true;
}
});
!cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp);
}
+ public static clearChargingProfiles = (
+ chargingStation: ChargingStation,
+ commandPayload: ClearChargingProfileRequest,
+ chargingProfiles: OCPP16ChargingProfile[] | undefined,
+ ): boolean => {
+ let clearedCP = false;
+ if (isNotEmptyArray(chargingProfiles)) {
+ chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => {
+ let clearCurrentCP = false;
+ if (chargingProfile.chargingProfileId === commandPayload.id) {
+ clearCurrentCP = true;
+ }
+ if (
+ !commandPayload.chargingProfilePurpose &&
+ chargingProfile.stackLevel === commandPayload.stackLevel
+ ) {
+ clearCurrentCP = true;
+ }
+ if (
+ !chargingProfile.stackLevel &&
+ chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
+ ) {
+ clearCurrentCP = true;
+ }
+ if (
+ chargingProfile.stackLevel === commandPayload.stackLevel &&
+ chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose
+ ) {
+ clearCurrentCP = true;
+ }
+ if (clearCurrentCP) {
+ chargingProfiles.splice(index, 1);
+ logger.debug(
+ `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`,
+ chargingProfile,
+ );
+ clearedCP = true;
+ }
+ });
+ }
+ return clearedCP;
+ };
+
public static parseJsonSchemaFile<T extends JsonType>(
relativePath: string,
moduleName?: string,
idTag: string,
): Promise<boolean> {
let authorized = false;
- const connectorStatus = chargingStation.getConnectorStatus(connectorId);
+ const connectorStatus: ConnectorStatus = chargingStation.getConnectorStatus(connectorId)!;
if (OCPP16ServiceUtils.isIdTagLocalAuthorized(chargingStation, idTag)) {
connectorStatus.localAuthorizeIdTag = idTag;
connectorStatus.idTagLocalAuthorized = true;
const sampledValueContext = context ?? sampledValueTemplate?.context ?? null;
const sampledValueLocation =
sampledValueTemplate?.location ??
- OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate?.measurand ?? null);
+ OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate.measurand!);
const sampledValuePhase = phase ?? sampledValueTemplate?.phase ?? null;
return {
...(!isNullOrUndefined(sampledValueTemplate.unit) && {
...(!isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }),
...(!isNullOrUndefined(sampledValueValue) && { value: sampledValueValue.toString() }),
...(!isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }),
- };
+ } as OCPP16SampledValue;
}
private static checkMeasurandPowerDivider(
chargingStation.hasIdTags() === true &&
isNotEmptyString(
chargingStation.idTagsCache
- .getIdTags(getIdTagsFile(chargingStation.stationInfo))
+ .getIdTags(getIdTagsFile(chargingStation.stationInfo)!)
?.find((tag) => tag === idTag),
)
);