X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2FChargingStation.ts;h=23724e79014584cdf9d5dc042cd29b0fcce0f249;hb=1185579a331f3484e8ed7882203d2e58466635dd;hp=ddaff652478e6d6898eef112dd3fcc6b2713add9;hpb=d812bdcbd13b39bf895bb3e01c0556d87c35a6d1;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index ddaff652..23724e79 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -1,99 +1,104 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import fs from 'fs'; import crypto from 'node:crypto'; -import path from 'path'; -import { URL } from 'url'; -import { parentPort } from 'worker_threads'; +import fs from 'node:fs'; +import path from 'node:path'; +import { URL } from 'node:url'; +import { parentPort } from 'node:worker_threads'; import merge from 'just-merge'; import WebSocket, { type RawData } from 'ws'; -import AuthorizedTagsCache from './AuthorizedTagsCache'; -import AutomaticTransactionGenerator from './AutomaticTransactionGenerator'; -import { ChargingStationConfigurationUtils } from './ChargingStationConfigurationUtils'; -import { ChargingStationUtils } from './ChargingStationUtils'; -import ChargingStationWorkerBroadcastChannel from './ChargingStationWorkerBroadcastChannel'; -import { MessageChannelUtils } from './MessageChannelUtils'; -import OCPP16IncomingRequestService from './ocpp/1.6/OCPP16IncomingRequestService'; -import OCPP16RequestService from './ocpp/1.6/OCPP16RequestService'; -import OCPP16ResponseService from './ocpp/1.6/OCPP16ResponseService'; -import { OCPP16ServiceUtils } from './ocpp/1.6/OCPP16ServiceUtils'; -import OCPP20IncomingRequestService from './ocpp/2.0/OCPP20IncomingRequestService'; -import OCPP20RequestService from './ocpp/2.0/OCPP20RequestService'; -import OCPP20ResponseService from './ocpp/2.0/OCPP20ResponseService'; -import type OCPPIncomingRequestService from './ocpp/OCPPIncomingRequestService'; -import type OCPPRequestService from './ocpp/OCPPRequestService'; -import { OCPPServiceUtils } from './ocpp/OCPPServiceUtils'; -import SharedLRUCache from './SharedLRUCache'; -import BaseError from '../exception/BaseError'; -import OCPPError from '../exception/OCPPError'; -import PerformanceStatistics from '../performance/PerformanceStatistics'; -import type { AutomaticTransactionGeneratorConfiguration } from '../types/AutomaticTransactionGenerator'; -import type { ChargingStationConfiguration } from '../types/ChargingStationConfiguration'; -import type { ChargingStationInfo } from '../types/ChargingStationInfo'; -import type { ChargingStationOcppConfiguration } from '../types/ChargingStationOcppConfiguration'; import { - type ChargingStationTemplate, - CurrentType, - PowerUnits, - type WsOptions, -} from '../types/ChargingStationTemplate'; -import { SupervisionUrlDistribution } from '../types/ConfigurationData'; -import type { ConnectorStatus } from '../types/ConnectorStatus'; -import { FileType } from '../types/FileType'; -import type { JsonType } from '../types/JsonType'; + AuthorizedTagsCache, + AutomaticTransactionGenerator, + ChargingStationConfigurationUtils, + ChargingStationUtils, + ChargingStationWorkerBroadcastChannel, + MessageChannelUtils, + SharedLRUCache, +} from './internal'; import { - ConnectorPhaseRotation, - StandardParametersKey, - SupportedFeatureProfiles, - VendorDefaultParametersKey, -} from '../types/ocpp/Configuration'; -import { ConnectorStatusEnum } from '../types/ocpp/ConnectorStatusEnum'; -import { ErrorType } from '../types/ocpp/ErrorType'; -import { MessageType } from '../types/ocpp/MessageType'; -import { MeterValue, MeterValueMeasurand } from '../types/ocpp/MeterValues'; -import { OCPPVersion } from '../types/ocpp/OCPPVersion'; + // OCPP16IncomingRequestService, + OCPP16RequestService, + // OCPP16ResponseService, + OCPP16ServiceUtils, + OCPP20IncomingRequestService, + OCPP20RequestService, + // OCPP20ResponseService, + type OCPPIncomingRequestService, + type OCPPRequestService, + // OCPPServiceUtils, +} from './ocpp'; +import { OCPP16IncomingRequestService } from './ocpp/1.6/OCPP16IncomingRequestService'; +import { OCPP16ResponseService } from './ocpp/1.6/OCPP16ResponseService'; +import { OCPP20ResponseService } from './ocpp/2.0/OCPP20ResponseService'; +import { OCPPServiceUtils } from './ocpp/OCPPServiceUtils'; +import { BaseError, OCPPError } from '../exception'; +import { PerformanceStatistics } from '../performance'; import { + type AutomaticTransactionGeneratorConfiguration, AvailabilityType, type BootNotificationRequest, + type BootNotificationResponse, type CachedRequest, + type ChargingStationConfiguration, + type ChargingStationInfo, + type ChargingStationOcppConfiguration, + type ChargingStationTemplate, + ConnectorPhaseRotation, + ConnectorStatus, + ConnectorStatusEnum, + CurrentType, type ErrorCallback, + type ErrorResponse, + ErrorType, + FileType, FirmwareStatus, type FirmwareStatusNotificationRequest, + type FirmwareStatusNotificationResponse, + type FirmwareUpgrade, type HeartbeatRequest, + type HeartbeatResponse, type IncomingRequest, - IncomingRequestCommand, + type IncomingRequestCommand, + type JsonType, + MessageType, + type MeterValue, + MeterValueMeasurand, type MeterValuesRequest, + type MeterValuesResponse, + OCPPVersion, type OutgoingRequest, + PowerUnits, + RegistrationStatusEnumType, RequestCommand, + type Response, type ResponseCallback, + StandardParametersKey, type StatusNotificationRequest, -} from '../types/ocpp/Requests'; -import { - type BootNotificationResponse, - type ErrorResponse, - type FirmwareStatusNotificationResponse, - type HeartbeatResponse, - type MeterValuesResponse, - RegistrationStatusEnumType, - type Response, type StatusNotificationResponse, -} from '../types/ocpp/Responses'; -import { StopTransactionReason, type StopTransactionRequest, type StopTransactionResponse, -} from '../types/ocpp/Transaction'; -import { WSError, WebSocketCloseEventStatusCode } from '../types/WebSocket'; -import Configuration from '../utils/Configuration'; -import Constants from '../utils/Constants'; -import { ACElectricUtils, DCElectricUtils } from '../utils/ElectricUtils'; -import FileUtils from '../utils/FileUtils'; -import logger from '../utils/Logger'; -import Utils from '../utils/Utils'; - -export default class ChargingStation { + SupervisionUrlDistribution, + SupportedFeatureProfiles, + VendorParametersKey, + type WSError, + WebSocketCloseEventStatusCode, + type WsOptions, +} from '../types'; +import { + ACElectricUtils, + Configuration, + Constants, + DCElectricUtils, + FileUtils, + Utils, + logger, +} from '../utils'; + +export class ChargingStation { public readonly index: number; public readonly templateFile: string; public stationInfo!: ChargingStationInfo; @@ -157,17 +162,19 @@ export default class ChargingStation { ); } - public logPrefix(): string { + public logPrefix = (): string => { return Utils.logPrefix( ` ${ - this?.stationInfo?.chargingStationId ?? - ChargingStationUtils.getChargingStationId(this.index, this.getTemplateFromFile()) + (Utils.isNotEmptyString(this?.stationInfo?.chargingStationId) && + this?.stationInfo?.chargingStationId) ?? + ChargingStationUtils.getChargingStationId(this.index, this.getTemplateFromFile()) ?? + '' } |` ); - } + }; public hasAuthorizedTags(): boolean { - return !Utils.isEmptyArray( + return Utils.isNotEmptyArray( this.authorizedTagsCache.getAuthorizedTags( ChargingStationUtils.getAuthorizationFile(this.stationInfo) ) @@ -506,12 +513,12 @@ export default class ChargingStation { this.openWSConnection(); // Monitor charging station template file this.templateFileWatcher = FileUtils.watchJsonFile( - this.logPrefix(), - FileType.ChargingStationTemplate, this.templateFile, - null, + FileType.ChargingStationTemplate, + this.logPrefix(), + undefined, (event, filename): void => { - if (filename && event === 'change') { + if (Utils.isNotEmptyString(filename) && event === 'change') { try { logger.debug( `${this.logPrefix()} ${FileType.ChargingStationTemplate} ${ @@ -710,7 +717,7 @@ export default class ChargingStation { this.getAutomaticTransactionGeneratorConfigurationFromTemplate(), this ); - if (!Utils.isEmptyArray(connectorIds)) { + if (Utils.isNotEmptyArray(connectorIds)) { for (const connectorId of connectorIds) { this.automaticTransactionGenerator?.startConnector(connectorId); } @@ -721,7 +728,7 @@ export default class ChargingStation { } public stopAutomaticTransactionGenerator(connectorIds?: number[]): void { - if (!Utils.isEmptyArray(connectorIds)) { + if (Utils.isNotEmptyArray(connectorIds)) { for (const connectorId of connectorIds) { this.automaticTransactionGenerator?.stopConnector(connectorId); } @@ -796,7 +803,7 @@ export default class ChargingStation { } private getSupervisionUrlOcppKey(): string { - return this.stationInfo.supervisionUrlOcppKey ?? VendorDefaultParametersKey.ConnectionUrl; + return this.stationInfo.supervisionUrlOcppKey ?? VendorParametersKey.ConnectionUrl; } private getTemplateFromFile(): ChargingStationTemplate | undefined { @@ -819,10 +826,10 @@ export default class ChargingStation { } } catch (error) { FileUtils.handleFileException( - this.logPrefix(), - FileType.ChargingStationTemplate, this.templateFile, - error as NodeJS.ErrnoException + FileType.ChargingStationTemplate, + error as NodeJS.ErrnoException, + this.logPrefix() ); } return template; @@ -862,7 +869,7 @@ export default class ChargingStation { ); stationInfo.ocppVersion = stationTemplate?.ocppVersion ?? OCPPVersion.VERSION_16; ChargingStationUtils.createSerialNumber(stationTemplate, stationInfo); - if (!Utils.isEmptyArray(stationTemplate?.power)) { + if (Utils.isNotEmptyArray(stationTemplate?.power)) { stationTemplate.power = stationTemplate.power as number[]; const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationTemplate.power.length); stationInfo.maximumPower = @@ -879,7 +886,7 @@ export default class ChargingStation { stationInfo.firmwareVersionPattern = stationTemplate?.firmwareVersionPattern ?? Constants.SEMVER_PATTERN; if ( - !Utils.isEmptyString(stationInfo.firmwareVersion) && + Utils.isNotEmptyString(stationInfo.firmwareVersion) && new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion) === false ) { logger.warn( @@ -888,8 +895,11 @@ export default class ChargingStation { } does not match firmware version pattern '${stationInfo.firmwareVersionPattern}'` ); } - stationInfo.firmwareUpgrade = merge( + stationInfo.firmwareUpgrade = merge( { + versionUpgrade: { + step: 1, + }, reset: true, }, stationTemplate?.firmwareUpgrade ?? {} @@ -1031,10 +1041,9 @@ export default class ChargingStation { } if ( this.stationInfo.firmwareStatus === FirmwareStatus.Installing && - !Utils.isEmptyString(this.stationInfo.firmwareVersion) && - !Utils.isEmptyString(this.stationInfo.firmwareVersionPattern) + Utils.isNotEmptyString(this.stationInfo.firmwareVersion) && + Utils.isNotEmptyString(this.stationInfo.firmwareVersionPattern) ) { - const versionStep = this.stationInfo.firmwareUpgrade?.versionUpgrade?.step ?? 1; const patternGroup: number | undefined = this.stationInfo.firmwareUpgrade?.versionUpgrade?.patternGroup ?? this.stationInfo.firmwareVersion?.split('.').length; @@ -1043,7 +1052,8 @@ export default class ChargingStation { ?.slice(1, patternGroup + 1); const patchLevelIndex = match.length - 1; match[patchLevelIndex] = ( - Utils.convertToInt(match[patchLevelIndex]) + versionStep + Utils.convertToInt(match[patchLevelIndex]) + + this.stationInfo.firmwareUpgrade?.versionUpgrade?.step ).toString(); this.stationInfo.firmwareVersion = match?.join('.'); } @@ -1096,7 +1106,7 @@ export default class ChargingStation { ); } if ( - !Utils.isEmptyString(this.stationInfo?.amperageLimitationOcppKey) && + Utils.isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && !ChargingStationConfigurationUtils.getConfigurationKey( this, this.stationInfo.amperageLimitationOcppKey @@ -1335,10 +1345,10 @@ export default class ChargingStation { } } catch (error) { FileUtils.handleFileException( - this.logPrefix(), - FileType.ChargingStationConfiguration, this.configurationFile, - error as NodeJS.ErrnoException + FileType.ChargingStationConfiguration, + error as NodeJS.ErrnoException, + this.logPrefix() ); } } @@ -1352,7 +1362,7 @@ export default class ChargingStation { fs.mkdirSync(path.dirname(this.configurationFile), { recursive: true }); } const configurationData: ChargingStationConfiguration = - this.getConfigurationFromFile() ?? {}; + Utils.cloneObject(this.getConfigurationFromFile()) ?? {}; this.ocppConfiguration?.configurationKey && (configurationData.configurationKey = this.ocppConfiguration.configurationKey); this.stationInfo && (configurationData.stationInfo = this.stationInfo); @@ -1381,10 +1391,10 @@ export default class ChargingStation { } } catch (error) { FileUtils.handleFileException( - this.logPrefix(), - FileType.ChargingStationConfiguration, this.configurationFile, - error as NodeJS.ErrnoException + FileType.ChargingStationConfiguration, + error as NodeJS.ErrnoException, + this.logPrefix() ); } } else { @@ -1404,7 +1414,10 @@ export default class ChargingStation { const configurationFromFile = this.getConfigurationFromFile(); configuration = configurationFromFile?.configurationKey && configurationFromFile; } - configuration && delete configuration.stationInfo; + if (!Utils.isNullOrUndefined(configuration)) { + delete configuration.stationInfo; + delete configuration.configurationHash; + } return configuration; } @@ -1761,7 +1774,7 @@ export default class ChargingStation { private getAmperageLimitation(): number | undefined { if ( - !Utils.isEmptyString(this.stationInfo?.amperageLimitationOcppKey) && + Utils.isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && ChargingStationConfigurationUtils.getConfigurationKey( this, this.stationInfo.amperageLimitationOcppKey @@ -1922,7 +1935,7 @@ export default class ChargingStation { private getConfiguredSupervisionUrl(): URL { const supervisionUrls = this.stationInfo?.supervisionUrls ?? Configuration.getSupervisionUrls(); - if (!Utils.isEmptyArray(supervisionUrls)) { + if (Utils.isNotEmptyArray(supervisionUrls)) { switch (Configuration.getSupervisionUrlDistribution()) { case SupervisionUrlDistribution.ROUND_ROBIN: // FIXME