type BootNotificationRequest,
type BootNotificationResponse,
type CachedRequest,
+ type ChargingStationAutomaticTransactionGeneratorConfiguration,
type ChargingStationConfiguration,
type ChargingStationInfo,
type ChargingStationOcppConfiguration,
} from '../types';
import {
ACElectricUtils,
+ AsyncLock,
+ AsyncLockType,
Configuration,
Constants,
DCElectricUtils,
};
public hasIdTags(): boolean {
- const idTagsFile = ChargingStationUtils.getIdTagsFile(this.stationInfo);
- return Utils.isNotEmptyArray(this.idTagsCache.getIdTags(idTagsFile));
+ return Utils.isNotEmptyArray(
+ this.idTagsCache.getIdTags(ChargingStationUtils.getIdTagsFile(this.stationInfo))
+ );
}
public getEnableStatistics(): boolean {
public getVoltageOut(stationInfo?: ChargingStationInfo): number | undefined {
const defaultVoltageOut = ChargingStationUtils.getDefaultVoltageOut(
this.getCurrentOutType(stationInfo),
- this.templateFile,
- this.logPrefix()
+ this.logPrefix(),
+ this.templateFile
);
- const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo;
- return !Utils.isUndefined(localStationInfo.voltageOut)
- ? localStationInfo.voltageOut
- : defaultVoltageOut;
+ return (stationInfo ?? this.stationInfo).voltageOut ?? defaultVoltageOut;
}
public getMaximumPower(stationInfo?: ChargingStationInfo): number {
this.initialize();
// Restart the ATG
this.stopAutomaticTransactionGenerator();
- if (
- this.getAutomaticTransactionGeneratorConfigurationFromTemplate()?.enable === true
- ) {
+ if (this.getAutomaticTransactionGeneratorConfiguration()?.enable === true) {
this.startAutomaticTransactionGenerator();
}
if (this.getEnableStatistics() === true) {
this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash);
delete this.bootNotificationResponse;
this.started = false;
+ this.saveConfiguration();
parentPort?.postMessage(MessageChannelUtils.buildStoppedMessage(this));
this.stopping = false;
} else {
}
}
- public startAutomaticTransactionGenerator(
- connectorIds?: number[],
- automaticTransactionGeneratorConfiguration?: AutomaticTransactionGeneratorConfiguration
- ): void {
- this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(
- automaticTransactionGeneratorConfiguration ??
- this.getAutomaticTransactionGeneratorConfigurationFromTemplate(),
- this
- );
+ public getAutomaticTransactionGeneratorConfiguration():
+ | AutomaticTransactionGeneratorConfiguration
+ | undefined {
+ const automaticTransactionGeneratorConfigurationFromFile =
+ this.getConfigurationFromFile()?.automaticTransactionGenerator;
+ if (automaticTransactionGeneratorConfigurationFromFile) {
+ return automaticTransactionGeneratorConfigurationFromFile;
+ }
+ return this.getTemplateFromFile()?.AutomaticTransactionGenerator;
+ }
+
+ public startAutomaticTransactionGenerator(connectorIds?: number[]): void {
+ this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this);
if (Utils.isNotEmptyArray(connectorIds)) {
for (const connectorId of connectorIds) {
this.automaticTransactionGenerator?.startConnector(connectorId);
} else {
this.automaticTransactionGenerator?.start();
}
+ this.saveChargingStationAutomaticTransactionGeneratorConfiguration();
parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
}
} else {
this.automaticTransactionGenerator?.stop();
}
+ this.saveChargingStationAutomaticTransactionGeneratorConfiguration();
parentPort?.postMessage(MessageChannelUtils.buildUpdatedMessage(this));
}
private getStationInfoFromTemplate(): ChargingStationInfo {
const stationTemplate: ChargingStationTemplate | undefined = this.getTemplateFromFile();
- if (Utils.isNullOrUndefined(stationTemplate)) {
- const errorMsg = `Failed to read charging station template file ${this.templateFile}`;
- logger.error(`${this.logPrefix()} ${errorMsg}`);
- throw new BaseError(errorMsg);
- }
- if (Utils.isEmptyObject(stationTemplate)) {
- const errorMsg = `Empty charging station information from template file ${this.templateFile}`;
- logger.error(`${this.logPrefix()} ${errorMsg}`);
- throw new BaseError(errorMsg);
- }
+ ChargingStationUtils.checkTemplateFile(stationTemplate, this.logPrefix(), this.templateFile);
ChargingStationUtils.warnTemplateKeysDeprecation(
- this.templateFile,
stationTemplate,
- this.logPrefix()
+ this.logPrefix(),
+ this.templateFile
);
+ if (stationTemplate?.Connectors) {
+ ChargingStationUtils.checkConnectorsConfiguration(
+ stationTemplate,
+ this.logPrefix(),
+ this.templateFile
+ );
+ }
const stationInfo: ChargingStationInfo =
ChargingStationUtils.stationTemplateToStationInfo(stationTemplate);
stationInfo.hashId = ChargingStationUtils.getHashId(this.index, stationTemplate);
? stationTemplate.resetTime * 1000
: Constants.CHARGING_STATION_DEFAULT_RESET_TIME;
stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo);
- if (stationInfo?.Connectors) {
- ChargingStationUtils.checkConnectorsConfiguration(
- stationInfo,
- this.templateFile,
- this.logPrefix()
- );
- }
- delete stationInfo?.Connectors;
- delete stationInfo?.Evses;
return stationInfo;
}
private initialize(): void {
const stationTemplate = this.getTemplateFromFile();
- if (Utils.isNullOrUndefined(stationTemplate)) {
- const errorMsg = `Failed to read charging station template file ${this.templateFile}`;
- logger.error(`${this.logPrefix()} ${errorMsg}`);
- throw new BaseError(errorMsg);
- }
+ ChargingStationUtils.checkTemplateFile(stationTemplate, this.logPrefix(), this.templateFile);
this.configurationFile = path.join(
path.dirname(this.templateFile.replace('station-templates', 'configurations')),
`${ChargingStationUtils.getHashId(this.index, stationTemplate)}.json`
);
- this.initializeConnectorsOrEvses(stationTemplate);
+ const chargingStationConfiguration = this.getConfigurationFromFile();
+ const featureFlag = false;
+ if (
+ featureFlag &&
+ (chargingStationConfiguration?.connectorsStatus || chargingStationConfiguration?.evsesStatus)
+ ) {
+ this.initializeConnectorsOrEvsesFromFile(chargingStationConfiguration);
+ } else {
+ this.initializeConnectorsOrEvsesFromTemplate(stationTemplate);
+ }
this.stationInfo = this.getStationInfo();
if (
this.stationInfo.firmwareStatus === FirmwareStatus.Installing &&
this.saveOcppConfiguration();
}
- private initializeConnectorsOrEvses(stationTemplate: ChargingStationTemplate) {
+ private initializeConnectorsOrEvsesFromFile(configuration: ChargingStationConfiguration): void {
+ if (configuration?.connectorsStatus && !configuration?.evsesStatus) {
+ for (const [connectorId, connectorStatus] of configuration.connectorsStatus.entries()) {
+ this.connectors.set(connectorId, Utils.cloneObject<ConnectorStatus>(connectorStatus));
+ }
+ } else if (configuration?.evsesStatus && !configuration?.connectorsStatus) {
+ for (const [evseId, evseStatusConfiguration] of configuration.evsesStatus.entries()) {
+ const evseStatus = Utils.cloneObject<EvseStatusConfiguration>(evseStatusConfiguration);
+ delete evseStatus.connectorsStatus;
+ this.evses.set(evseId, {
+ ...(evseStatus as EvseStatus),
+ connectors: new Map<number, ConnectorStatus>(
+ evseStatusConfiguration.connectorsStatus.map((connectorStatus, connectorId) => [
+ connectorId,
+ connectorStatus,
+ ])
+ ),
+ });
+ }
+ } else if (configuration?.evsesStatus && configuration?.connectorsStatus) {
+ const errorMsg = `Connectors and evses defined at the same time in configuration file ${this.configurationFile}`;
+ logger.error(`${this.logPrefix()} ${errorMsg}`);
+ throw new BaseError(errorMsg);
+ } else {
+ const errorMsg = `No connectors or evses defined in configuration file ${this.configurationFile}`;
+ logger.error(`${this.logPrefix()} ${errorMsg}`);
+ throw new BaseError(errorMsg);
+ }
+ }
+
+ private initializeConnectorsOrEvsesFromTemplate(stationTemplate: ChargingStationTemplate) {
if (stationTemplate?.Connectors && !stationTemplate?.Evses) {
- this.initializeConnectors(stationTemplate);
+ this.initializeConnectorsFromTemplate(stationTemplate);
} else if (stationTemplate?.Evses && !stationTemplate?.Connectors) {
- this.initializeEvses(stationTemplate);
+ this.initializeEvsesFromTemplate(stationTemplate);
} else if (stationTemplate?.Evses && stationTemplate?.Connectors) {
const errorMsg = `Connectors and evses defined at the same time in template file ${this.templateFile}`;
logger.error(`${this.logPrefix()} ${errorMsg}`);
}
}
- private initializeConnectors(stationTemplate: ChargingStationTemplate): void {
+ private initializeConnectorsFromTemplate(stationTemplate: ChargingStationTemplate): void {
if (!stationTemplate?.Connectors && this.connectors.size === 0) {
const errorMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined`;
logger.error(`${this.logPrefix()} ${errorMsg}`);
const { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors } =
ChargingStationUtils.checkConnectorsConfiguration(
stationTemplate,
- this.templateFile,
- this.logPrefix()
+ this.logPrefix(),
+ this.templateFile
);
const connectorsConfigHash = crypto
.createHash(Constants.DEFAULT_HASH_ALGORITHM)
}
}
- private initializeEvses(stationTemplate: ChargingStationTemplate): void {
+ private initializeEvsesFromTemplate(stationTemplate: ChargingStationTemplate): void {
if (!stationTemplate?.Evses && this.evses.size === 0) {
const errorMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`;
logger.error(`${this.logPrefix()} ${errorMsg}`);
return configuration;
}
+ private saveChargingStationAutomaticTransactionGeneratorConfiguration(
+ stationTemplate?: ChargingStationTemplate
+ ): void {
+ this.saveConfiguration({
+ automaticTransactionGenerator: (stationTemplate ?? this.getTemplateFromFile())
+ .AutomaticTransactionGenerator,
+ ...(!Utils.isNullOrUndefined(this.automaticTransactionGenerator?.connectorsStatus) && {
+ automaticTransactionGeneratorStatuses: [
+ ...this.automaticTransactionGenerator.connectorsStatus.values(),
+ ],
+ }),
+ });
+ }
+
private saveConnectorsStatus() {
- if (this.getOcppPersistentConfiguration()) {
- this.saveConfiguration();
- }
+ this.saveConfiguration();
}
private saveEvsesStatus() {
- if (this.getOcppPersistentConfiguration()) {
- this.saveConfiguration();
- }
+ this.saveConfiguration();
}
- private saveConfiguration(): void {
+ private saveConfiguration(
+ chargingStationAutomaticTransactionGeneratorConfiguration?: ChargingStationAutomaticTransactionGeneratorConfiguration
+ ): void {
if (this.configurationFile) {
try {
if (!fs.existsSync(path.dirname(this.configurationFile))) {
fs.mkdirSync(path.dirname(this.configurationFile), { recursive: true });
}
- const configurationData: ChargingStationConfiguration =
- Utils.cloneObject(this.getConfigurationFromFile()) ?? {};
- if (this.stationInfo) {
+ let configurationData: ChargingStationConfiguration =
+ Utils.cloneObject<ChargingStationConfiguration>(this.getConfigurationFromFile()) ?? {};
+ if (this.getStationInfoPersistentConfiguration() && this.stationInfo) {
configurationData.stationInfo = this.stationInfo;
}
- if (this.ocppConfiguration?.configurationKey) {
+ if (this.getOcppPersistentConfiguration() && this.ocppConfiguration?.configurationKey) {
configurationData.configurationKey = this.ocppConfiguration.configurationKey;
}
+ if (chargingStationAutomaticTransactionGeneratorConfiguration) {
+ configurationData = merge<ChargingStationConfiguration>(
+ configurationData,
+ chargingStationAutomaticTransactionGeneratorConfiguration
+ );
+ }
if (this.connectors.size > 0) {
configurationData.connectorsStatus = [...this.connectors.values()].map(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.update(JSON.stringify(configurationData))
.digest('hex');
if (this.configurationFileHash !== configurationHash) {
- configurationData.configurationHash = configurationHash;
- const measureId = `${FileType.ChargingStationConfiguration} write`;
- const beginId = PerformanceStatistics.beginMeasure(measureId);
- const fileDescriptor = fs.openSync(this.configurationFile, 'w');
- fs.writeFileSync(fileDescriptor, JSON.stringify(configurationData, null, 2), 'utf8');
- fs.closeSync(fileDescriptor);
- PerformanceStatistics.endMeasure(measureId, beginId);
- this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash);
- this.sharedLRUCache.setChargingStationConfiguration(configurationData);
- this.configurationFileHash = configurationHash;
+ AsyncLock.acquire(AsyncLockType.configuration)
+ .then(() => {
+ configurationData.configurationHash = configurationHash;
+ const measureId = `${FileType.ChargingStationConfiguration} write`;
+ const beginId = PerformanceStatistics.beginMeasure(measureId);
+ const fileDescriptor = fs.openSync(this.configurationFile, 'w');
+ fs.writeFileSync(fileDescriptor, JSON.stringify(configurationData, null, 2), 'utf8');
+ fs.closeSync(fileDescriptor);
+ PerformanceStatistics.endMeasure(measureId, beginId);
+ this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash);
+ this.sharedLRUCache.setChargingStationConfiguration(configurationData);
+ this.configurationFileHash = configurationHash;
+ })
+ .catch((error) => {
+ FileUtils.handleFileException(
+ this.configurationFile,
+ FileType.ChargingStationConfiguration,
+ error as NodeJS.ErrnoException,
+ this.logPrefix()
+ );
+ })
+ .finally(() => {
+ AsyncLock.release(AsyncLockType.configuration).catch(Constants.EMPTY_FUNCTION);
+ });
} else {
logger.debug(
`${this.logPrefix()} Not saving unchanged charging station configuration file ${
}
private getOcppConfigurationFromFile(): ChargingStationOcppConfiguration | undefined {
- let configuration: ChargingStationConfiguration | undefined;
if (this.getOcppPersistentConfiguration() === true) {
- const configurationFromFile = this.getConfigurationFromFile();
- configuration = configurationFromFile?.configurationKey && configurationFromFile;
- }
- if (!Utils.isNullOrUndefined(configuration)) {
- delete configuration.stationInfo;
- delete configuration.configurationHash;
+ return { configurationKey: this.getConfigurationFromFile()?.configurationKey };
}
- return configuration;
}
private getOcppConfiguration(): ChargingStationOcppConfiguration | undefined {
await OCPPServiceUtils.sendAndSetConnectorStatus(
this,
connectorId,
- connectorBootStatus
+ connectorBootStatus,
+ evseId
);
}
}
}
// Start the ATG
- if (this.getAutomaticTransactionGeneratorConfigurationFromTemplate()?.enable === true) {
+ if (this.getAutomaticTransactionGeneratorConfiguration()?.enable === true) {
this.startAutomaticTransactionGenerator();
}
this.wsConnectionRestarted === true && this.flushMessageBuffer();
OCPPServiceUtils.buildStatusNotificationRequest(
this,
connectorId,
- ConnectorStatusEnum.Unavailable
+ ConnectorStatusEnum.Unavailable,
+ evseId
)
);
delete connectorStatus?.status;
// Stop heartbeat
this.stopHeartbeat();
// Stop the ATG if needed
- if (this.automaticTransactionGenerator?.configuration?.stopOnConnectionFailure === true) {
+ if (this.getAutomaticTransactionGeneratorConfiguration().stopOnConnectionFailure === true) {
this.stopAutomaticTransactionGenerator();
}
if (
);
}
}
-
- private getAutomaticTransactionGeneratorConfigurationFromTemplate():
- | AutomaticTransactionGeneratorConfiguration
- | undefined {
- return this.getTemplateFromFile()?.AutomaticTransactionGenerator;
- }
}