From 5959eb2df872dbdb27032bfd114024be38c32749 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 27 Mar 2026 19:50:01 +0100 Subject: [PATCH] refactor: eliminate all non-null assertion suppressions with proper null guards Remove 104 eslint-disable-next-line @typescript-eslint/no-non-null-assertion suppressions across 23 files by replacing non-null assertions (!) with semantically correct alternatives: local variable extraction with null guards, optional chaining (?.), nullish coalescing (??) with proper default constants, and error logging for invalid states. Key improvements: - Use existing DEFAULT_POOL_MAX_SIZE, DEFAULT_POOL_MIN_SIZE, and DEFAULT_ELEMENTS_PER_WORKER constants instead of hardcoded fallbacks - Add proper error logging for powerDivider undefined/invalid states instead of silently computing wrong values - Add connectorId null guard with throw in handleMeterValues instead of silently defaulting to connector 0 - Ensure .finally() always sends a response to prevent caller hangs - Align variable naming with codebase conventions (connectorStatus, templateStatistics, commandStatisticsData, entryStatisticsData) --- src/charging-station/Bootstrap.ts | 70 ++++---- src/charging-station/ChargingStation.ts | 151 +++++++++++------- src/charging-station/ConfigurationKeyUtils.ts | 9 +- src/charging-station/Helpers.ts | 37 +++-- src/charging-station/IdTagsCache.ts | 9 +- src/charging-station/SharedLRUCache.ts | 8 +- .../ChargingStationWorkerBroadcastChannel.ts | 30 ++-- .../UIServiceWorkerBroadcastChannel.ts | 26 +-- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 31 ++-- .../ocpp/1.6/OCPP16RequestService.ts | 14 +- .../ocpp/1.6/OCPP16ResponseService.ts | 26 ++- .../ocpp/1.6/OCPP16ServiceUtils.ts | 21 ++- .../ocpp/2.0/OCPP20IncomingRequestService.ts | 15 +- .../ocpp/OCPPIncomingRequestService.ts | 11 +- .../ocpp/OCPPResponseService.ts | 11 +- src/charging-station/ocpp/OCPPServiceUtils.ts | 23 ++- .../ui-services/AbstractUIService.ts | 6 +- src/performance/PerformanceStatistics.ts | 132 ++++++--------- src/utils/AsyncLock.ts | 9 +- src/utils/Configuration.ts | 15 +- src/utils/Logger.ts | 3 +- src/utils/MessageChannelUtils.ts | 9 +- src/worker/WorkerSet.ts | 18 ++- 23 files changed, 373 insertions(+), 311 deletions(-) diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index 80df1fa5..ed0817a2 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -44,7 +44,13 @@ import { logger, logPrefix, } from '../utils/index.js' -import { DEFAULT_ELEMENTS_PER_WORKER, type WorkerAbstract, WorkerFactory } from '../worker/index.js' +import { + DEFAULT_ELEMENTS_PER_WORKER, + DEFAULT_POOL_MAX_SIZE, + DEFAULT_POOL_MIN_SIZE, + type WorkerAbstract, + WorkerFactory, +} from '../worker/index.js' import { buildTemplateName, waitChargingStationEvents } from './Helpers.js' import { UIServerFactory } from './ui-server/UIServerFactory.js' @@ -163,10 +169,11 @@ export class Bootstrap extends EventEmitter { } public getLastContiguousIndex (templateName: string): number { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const indexes = [...this.templateStatistics.get(templateName)!.indexes] - .concat(0) - .sort((a, b) => a - b) + const templateStatistics = this.templateStatistics.get(templateName) + if (templateStatistics == null) { + return 0 + } + const indexes = [...templateStatistics.indexes].concat(0).sort((a, b) => a - b) for (let i = 0; i < indexes.length - 1; i++) { if (indexes[i + 1] - indexes[i] !== 1) { return indexes[i] @@ -213,14 +220,12 @@ export class Bootstrap extends EventEmitter { ConfigurationSection.performanceStorage ) if (performanceStorageConfiguration.enabled === true) { - this.storage = StorageFactory.getStorage( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - performanceStorageConfiguration.type!, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - performanceStorageConfiguration.uri!, - this.logPrefix() - ) - await this.storage.open() + const storageType = performanceStorageConfiguration.type + const storageUri = performanceStorageConfiguration.uri + if (storageType != null && storageUri != null) { + this.storage = StorageFactory.getStorage(storageType, storageUri, this.logPrefix()) + await this.storage.open() + } } this.uiServer.setChargingStationTemplates( Configuration.getStationTemplateUrls()?.map(stationTemplateUrl => @@ -237,8 +242,7 @@ export class Bootstrap extends EventEmitter { this.uiServerStarted = true } // Start ChargingStation object instance in worker thread - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - for (const stationTemplateUrl of Configuration.getStationTemplateUrls()!) { + for (const stationTemplateUrl of Configuration.getStationTemplateUrls() ?? []) { const nbStations = stationTemplateUrl.numberOfStations const sequentialAdd = (Configuration.getConfigurationSection( @@ -365,8 +369,7 @@ export class Bootstrap extends EventEmitter { } private initializeCounters (): void { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const stationTemplateUrls = Configuration.getStationTemplateUrls()! + const stationTemplateUrls = Configuration.getStationTemplateUrls() ?? [] if (isNotEmptyArray(stationTemplateUrls)) { for (const stationTemplateUrl of stationTemplateUrls) { const templateName = buildTemplateName(stationTemplateUrl.file) @@ -430,6 +433,9 @@ export class Bootstrap extends EventEmitter { default: elementsPerWorker = workerConfiguration.elementsPerWorker ?? DEFAULT_ELEMENTS_PER_WORKER } + if (workerConfiguration.processType == null) { + throw new BaseError('Worker process type is not defined in configuration') + } this.workerImplementation = WorkerFactory.getWorkerImplementation< ChargingStationWorkerData, ChargingStationInfo @@ -438,15 +444,12 @@ export class Bootstrap extends EventEmitter { dirname(fileURLToPath(import.meta.url)), `ChargingStationWorker${extname(fileURLToPath(import.meta.url))}` ), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - workerConfiguration.processType!, + workerConfiguration.processType, { elementAddDelay: workerConfiguration.elementAddDelay, elementsPerWorker, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - poolMaxSize: workerConfiguration.poolMaxSize!, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - poolMinSize: workerConfiguration.poolMinSize!, + poolMaxSize: workerConfiguration.poolMaxSize ?? DEFAULT_POOL_MAX_SIZE, + poolMinSize: workerConfiguration.poolMinSize ?? DEFAULT_POOL_MIN_SIZE, poolOptions: { messageHandler: this.messageHandler.bind(this) as MessageHandler, ...(workerConfiguration.resourceLimits != null && { @@ -617,10 +620,11 @@ export class Bootstrap extends EventEmitter { if (this.uiServer.deleteChargingStationData(data.stationInfo.hashId)) { this.uiServer.scheduleClientNotification() } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const templateStatistics = this.templateStatistics.get(data.stationInfo.templateName)! - --templateStatistics.added - templateStatistics.indexes.delete(data.stationInfo.templateIndex) + const templateStatistics = this.templateStatistics.get(data.stationInfo.templateName) + if (templateStatistics != null) { + --templateStatistics.added + templateStatistics.indexes.delete(data.stationInfo.templateIndex) + } logger.info( `${this.logPrefix()} ${moduleName}.workerEventDeleted: Charging station ${ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions @@ -650,8 +654,10 @@ export class Bootstrap extends EventEmitter { if (this.uiServer.setChargingStationData(data.stationInfo.hashId, data)) { this.uiServer.scheduleClientNotification() } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ++this.templateStatistics.get(data.stationInfo.templateName)!.started + const templateStatistics = this.templateStatistics.get(data.stationInfo.templateName) + if (templateStatistics != null) { + ++templateStatistics.started + } logger.info( `${this.logPrefix()} ${moduleName}.workerEventStarted: Charging station ${ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions @@ -664,8 +670,10 @@ export class Bootstrap extends EventEmitter { if (this.uiServer.setChargingStationData(data.stationInfo.hashId, data)) { this.uiServer.scheduleClientNotification() } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - --this.templateStatistics.get(data.stationInfo.templateName)!.started + const templateStatistics = this.templateStatistics.get(data.stationInfo.templateName) + if (templateStatistics != null) { + --templateStatistics.started + } logger.info( `${this.logPrefix()} ${moduleName}.workerEventStopped: Charging station ${ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 68beff82..4a59aac3 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -506,8 +506,8 @@ export class ChargingStation extends EventEmitter { } const connectorMaximumPower = (this.stationInfo?.maximumPower ?? 0) / (this.powerDivider ?? 1) const chargingStationChargingProfilesLimit = - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getChargingStationChargingProfilesLimit(this)! / this.powerDivider! + (getChargingStationChargingProfilesLimit(this) ?? Number.POSITIVE_INFINITY) / + (this.powerDivider ?? 1) const connectorChargingProfilesLimit = getConnectorChargingProfilesLimit(this, connectorId) return min( Number.isNaN(connectorMaximumPower) ? Number.POSITIVE_INFINITY : connectorMaximumPower, @@ -555,9 +555,9 @@ export class ChargingStation extends EventEmitter { transactionId: number | string | undefined, rounded = false ): number { + const connectorId = this.getConnectorIdByTransactionId(transactionId) return this.getEnergyActiveImportRegister( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)!), + connectorId != null ? this.getConnectorStatus(connectorId) : undefined, rounded ) } @@ -655,11 +655,10 @@ export class ChargingStation extends EventEmitter { * @returns The number of phases (3 for AC, 0 for DC) */ public getNumberOfPhases (stationInfo?: ChargingStationInfo): number { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const localStationInfo = stationInfo ?? this.stationInfo! + const localStationInfo = stationInfo ?? this.stationInfo switch (this.getCurrentOutType(stationInfo)) { case CurrentType.AC: - return localStationInfo.numberOfPhases ?? 3 + return localStationInfo?.numberOfPhases ?? 3 case CurrentType.DC: return 0 } @@ -721,8 +720,7 @@ export class ChargingStation extends EventEmitter { public getReserveConnectorZeroSupported (): boolean { return convertToBoolean( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getConfigurationKey(this, StandardParametersKey.ReserveConnectorZeroSupported)!.value + getConfigurationKey(this, StandardParametersKey.ReserveConnectorZeroSupported)?.value ) } @@ -751,8 +749,7 @@ export class ChargingStation extends EventEmitter { public getVoltageOut (stationInfo?: ChargingStationInfo): Voltage { return ( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (stationInfo ?? this.stationInfo!).voltageOut ?? + (stationInfo ?? this.stationInfo)?.voltageOut ?? getDefaultVoltageOut(this.getCurrentOutType(stationInfo), this.logPrefix(), this.templateFile) ) } @@ -776,8 +773,8 @@ export class ChargingStation extends EventEmitter { } public hasIdTags (): boolean { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return isNotEmptyArray(this.idTagsCache.getIdTags(getIdTagsFile(this.stationInfo!)!)) + const idTagsFile = this.stationInfo != null ? getIdTagsFile(this.stationInfo) : undefined + return idTagsFile != null && isNotEmptyArray(this.idTagsCache.getIdTags(idTagsFile)) } public inAcceptedState (): boolean { @@ -963,12 +960,17 @@ export class ChargingStation extends EventEmitter { reservation: Reservation, reason: ReservationTerminationReason ): Promise { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const connector = this.getConnectorStatus(reservation.connectorId)! + const connectorStatus = this.getConnectorStatus(reservation.connectorId) + if (connectorStatus == null) { + logger.error( + `${this.logPrefix()} Trying to remove reservation on non-existent connector id ${reservation.connectorId.toString()}` + ) + return + } switch (reason) { case ReservationTerminationReason.CONNECTOR_STATE_CHANGED: case ReservationTerminationReason.TRANSACTION_STARTED: - delete connector.reservation + delete connectorStatus.reservation break case ReservationTerminationReason.EXPIRED: case ReservationTerminationReason.REPLACE_EXISTING: @@ -981,7 +983,7 @@ export class ChargingStation extends EventEmitter { } as unknown as StatusNotificationRequest, { send: reservation.connectorId !== 0 } ) - delete connector.reservation + delete connectorStatus.reservation break default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions @@ -1071,8 +1073,11 @@ export class ChargingStation extends EventEmitter { } file have changed, reload` ) this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.idTagsCache.deleteIdTags(getIdTagsFile(this.stationInfo!)!) + const idTagsFile = + this.stationInfo != null ? getIdTagsFile(this.stationInfo) : undefined + if (idTagsFile != null) { + this.idTagsCache.deleteIdTags(idTagsFile) + } OCPPAuthServiceFactory.clearInstance(this) // Initialize this.initialize() @@ -1377,24 +1382,26 @@ export class ChargingStation extends EventEmitter { break case SupervisionUrlDistribution.CHARGING_STATION_AFFINITY: case SupervisionUrlDistribution.ROUND_ROBIN: - default: - !Object.values(SupervisionUrlDistribution).includes( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - Configuration.getSupervisionUrlDistribution()! - ) && + default: { + const supervisionUrlDistribution = Configuration.getSupervisionUrlDistribution() + if ( + supervisionUrlDistribution != null && + !Object.values(SupervisionUrlDistribution).includes(supervisionUrlDistribution) + ) { logger.warn( - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, @typescript-eslint/no-base-to-string - `${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' in configuration from values '${SupervisionUrlDistribution.toString()}', defaulting to '${ + // eslint-disable-next-line @typescript-eslint/no-base-to-string + `${this.logPrefix()} Unknown supervision url distribution '${supervisionUrlDistribution}' in configuration from values '${SupervisionUrlDistribution.toString()}', defaulting to '${ SupervisionUrlDistribution.CHARGING_STATION_AFFINITY }'` ) + } configuredSupervisionUrlIndex = (this.index - 1) % supervisionUrls.length break + } } configuredSupervisionUrl = supervisionUrls[configuredSupervisionUrlIndex] } else if (typeof supervisionUrls === 'string') { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - configuredSupervisionUrl = supervisionUrls! + configuredSupervisionUrl = supervisionUrls } if (isNotEmptyString(configuredSupervisionUrl)) { return new URL(configuredSupervisionUrl) @@ -1406,10 +1413,9 @@ export class ChargingStation extends EventEmitter { private getCurrentOutType (stationInfo?: ChargingStationInfo): CurrentType { return ( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (stationInfo ?? this.stationInfo!).currentOutType ?? - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - Constants.DEFAULT_STATION_INFO.currentOutType! + (stationInfo ?? this.stationInfo)?.currentOutType ?? + Constants.DEFAULT_STATION_INFO.currentOutType ?? + CurrentType.AC ) } @@ -1436,8 +1442,8 @@ export class ChargingStation extends EventEmitter { } private getMaximumAmperage (stationInfo?: ChargingStationInfo): number | undefined { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const maximumPower = (stationInfo ?? this.stationInfo!).maximumPower! + const localStationInfo = stationInfo ?? this.stationInfo + const maximumPower = localStationInfo?.maximumPower ?? 0 switch (this.getCurrentOutType(stationInfo)) { case CurrentType.AC: return ACElectricUtils.amperagePerPhaseFromPower( @@ -1554,8 +1560,12 @@ export class ChargingStation extends EventEmitter { } private getStationInfoFromTemplate (): ChargingStationInfo { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const stationTemplate = this.getTemplateFromFile()! + const stationTemplate = this.getTemplateFromFile() + if (stationTemplate == null) { + const errorMsg = `Failed to read charging station template file ${this.templateFile}` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) + } checkTemplate(stationTemplate, this.logPrefix(), this.templateFile) const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation) warnTemplateKeysDeprecationOnce(stationTemplate, this.logPrefix(), this.templateFile) @@ -1632,8 +1642,9 @@ export class ChargingStation extends EventEmitter { } private getUseConnectorId0 (stationTemplate?: ChargingStationTemplate): boolean { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return stationTemplate?.useConnectorId0 ?? Constants.DEFAULT_STATION_INFO.useConnectorId0! + return ( + stationTemplate?.useConnectorId0 ?? Constants.DEFAULT_STATION_INFO.useConnectorId0 ?? true + ) } private handleErrorMessage (errorResponse: ErrorResponse): void { @@ -1647,8 +1658,16 @@ export class ChargingStation extends EventEmitter { { errorDetails, errorMessage, errorType } ) } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)! + const cachedRequest = this.getCachedRequest(messageType, messageId) + if (cachedRequest == null) { + throw new OCPPError( + ErrorType.INTERNAL_ERROR, + `Cached request for error message id '${messageId}' is nullish`, + undefined, + { errorDetails, errorMessage, errorType } + ) + } + const [, errorCallback, requestCommandName] = cachedRequest logger.debug( `${this.logPrefix()} << Command '${requestCommandName}' received error response payload: ${JSON.stringify( errorResponse @@ -1697,11 +1716,16 @@ export class ChargingStation extends EventEmitter { ) } // Respond - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const [responseCallback, , requestCommandName, requestPayload] = this.getCachedRequest( - messageType, - messageId - )! + const cachedRequest = this.getCachedRequest(messageType, messageId) + if (cachedRequest == null) { + throw new OCPPError( + ErrorType.INTERNAL_ERROR, + `Cached request for response message id '${messageId}' is nullish`, + undefined, + commandPayload + ) + } + const [responseCallback, , requestCommandName, requestPayload] = cachedRequest logger.debug( `${this.logPrefix()} << Command '${requestCommandName}' received response payload: ${JSON.stringify( response @@ -1718,8 +1742,12 @@ export class ChargingStation extends EventEmitter { } private initialize (options?: ChargingStationOptions): void { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const stationTemplate = this.getTemplateFromFile()! + const stationTemplate = this.getTemplateFromFile() + if (stationTemplate == null) { + const errorMsg = `Failed to read charging station template file ${this.templateFile}` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) + } checkTemplate(stationTemplate, this.logPrefix(), this.templateFile) this.configurationFile = join( dirname(this.templateFile.replace('station-templates', 'configurations')), @@ -2038,8 +2066,7 @@ export class ChargingStation extends EventEmitter { this, this.stationInfo.amperageLimitationOcppKey, // prettier-ignore - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (this.stationInfo.maximumAmperage! * getAmperageLimitationUnitDivider(this.stationInfo)).toString() + ((this.stationInfo.maximumAmperage ?? 0) * getAmperageLimitationUnitDivider(this.stationInfo)).toString() ) } if (getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles) == null) { @@ -2068,18 +2095,18 @@ export class ChargingStation extends EventEmitter { if (this.hasEvses) { for (const evseStatus of this.evses.values()) { for (const connectorId of evseStatus.connectors.keys()) { - connectorsPhaseRotation.push( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getPhaseRotationValue(connectorId, this.getNumberOfPhases())! - ) + const phaseRotation = getPhaseRotationValue(connectorId, this.getNumberOfPhases()) + if (phaseRotation != null) { + connectorsPhaseRotation.push(phaseRotation) + } } } } else { for (const connectorId of this.connectors.keys()) { - connectorsPhaseRotation.push( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - getPhaseRotationValue(connectorId, this.getNumberOfPhases())! - ) + const phaseRotation = getPhaseRotationValue(connectorId, this.getNumberOfPhases()) + if (phaseRotation != null) { + connectorsPhaseRotation.push(phaseRotation) + } } } addConfigurationKey( @@ -2239,10 +2266,12 @@ export class ChargingStation extends EventEmitter { case MessageType.CALL_ERROR_MESSAGE: case MessageType.CALL_RESULT_MESSAGE: if (this.requests.has(messageId)) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ;[, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)! - // Reject the deferred promise in case of error at response handling (rejecting an already fulfilled promise is a no-op) - errorCallback(ocppError, false) + const cachedRequest = this.getCachedRequest(messageType, messageId) + if (cachedRequest != null) { + ;[, errorCallback, requestCommandName] = cachedRequest + // Reject the deferred promise in case of error at response handling (rejecting an already fulfilled promise is a no-op) + errorCallback(ocppError, false) + } } else { // Remove the request from the cache in case of error at response handling this.requests.delete(messageId) diff --git a/src/charging-station/ConfigurationKeyUtils.ts b/src/charging-station/ConfigurationKeyUtils.ts index 79b57ec4..826cbe00 100644 --- a/src/charging-station/ConfigurationKeyUtils.ts +++ b/src/charging-station/ConfigurationKeyUtils.ts @@ -167,8 +167,7 @@ export const addConfigurationKey = ( if (params.overwrite) { chargingStation.ocppConfiguration.configurationKey[keyIndex] = { ...chargingStation.ocppConfiguration.configurationKey[keyIndex], - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - readonly: options.readonly!, + readonly: options.readonly ?? false, reboot: options.reboot, value, visible: options.visible, @@ -179,8 +178,7 @@ export const addConfigurationKey = ( configurationKey.reboot = options.reboot } if (options.readonly != null && configurationKey.readonly !== options.readonly) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - configurationKey.readonly = options.readonly! + configurationKey.readonly = options.readonly } if (options.visible != null && configurationKey.visible !== options.visible) { configurationKey.visible = options.visible @@ -194,8 +192,7 @@ export const addConfigurationKey = ( } else { chargingStation.ocppConfiguration.configurationKey.push({ key: resolveKey(chargingStation, key), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - readonly: options.readonly!, + readonly: options.readonly ?? false, reboot: options.reboot, value, visible: options.visible, diff --git a/src/charging-station/Helpers.ts b/src/charging-station/Helpers.ts index 885506a0..d3ffaace 100644 --- a/src/charging-station/Helpers.ts +++ b/src/charging-station/Helpers.ts @@ -590,7 +590,8 @@ export const prepareConnectorStatus = (connectorStatus: ConnectorStatus): Connec .map(chargingProfile => { const chargingSchedule = getSingleChargingSchedule(chargingProfile) if (chargingSchedule != null) { - chargingSchedule.startSchedule = convertToDate(chargingSchedule.startSchedule) + chargingSchedule.startSchedule = + convertToDate(chargingSchedule.startSchedule) ?? new Date() } chargingProfile.validFrom = convertToDate(chargingProfile.validFrom) chargingProfile.validTo = convertToDate(chargingProfile.validTo) @@ -1104,8 +1105,7 @@ const getChargingProfilesLimit = ( logger.warn( `${chargingStation.logPrefix()} ${moduleName}.getChargingProfilesLimit: Charging profile id ${chargingProfileId} startSchedule property is not a Date instance. Trying to convert it to a Date instance` ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingSchedule.startSchedule = convertToDate(chargingSchedule.startSchedule)! + chargingSchedule.startSchedule = convertToDate(chargingSchedule.startSchedule) ?? new Date() } if (chargingSchedule.duration == null) { logger.debug( @@ -1388,12 +1388,11 @@ const prepareRecurringChargingProfile = ( let recurringIntervalTranslated = false let recurringInterval: Interval | undefined switch (chargingProfile.recurrencyKind) { - case RecurrencyKindType.DAILY: + case RecurrencyKindType.DAILY: { + const startSchedule = chargingSchedule.startSchedule ?? new Date() recurringInterval = { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - end: addDays(chargingSchedule.startSchedule!, 1), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - start: chargingSchedule.startSchedule!, + end: addDays(startSchedule, 1), + start: startSchedule, } checkRecurringChargingProfileDuration(chargingProfile, recurringInterval, logPrefix) if ( @@ -1412,12 +1411,12 @@ const prepareRecurringChargingProfile = ( recurringIntervalTranslated = true } break - case RecurrencyKindType.WEEKLY: + } + case RecurrencyKindType.WEEKLY: { + const startSchedule = chargingSchedule.startSchedule ?? new Date() recurringInterval = { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - end: addWeeks(chargingSchedule.startSchedule!, 1), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - start: chargingSchedule.startSchedule!, + end: addWeeks(startSchedule, 1), + start: startSchedule, } checkRecurringChargingProfileDuration(chargingProfile, recurringInterval, logPrefix) if ( @@ -1436,22 +1435,26 @@ const prepareRecurringChargingProfile = ( recurringIntervalTranslated = true } break + } default: logger.error( // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `${logPrefix} ${moduleName}.prepareRecurringChargingProfile: Recurring ${chargingProfile.recurrencyKind} charging profile id ${chargingProfileId} is not supported` ) } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (recurringIntervalTranslated && !isWithinInterval(currentDate, recurringInterval!)) { + if ( + recurringIntervalTranslated && + recurringInterval != null && + !isWithinInterval(currentDate, recurringInterval) + ) { logger.error( `${logPrefix} ${moduleName}.prepareRecurringChargingProfile: Recurring ${ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions chargingProfile.recurrencyKind } charging profile id ${chargingProfileId} recurrency time interval [${toDate( - recurringInterval?.start as Date + recurringInterval.start as Date ).toISOString()}, ${toDate( - recurringInterval?.end as Date + recurringInterval.end as Date ).toISOString()}] has not been properly translated to current date ${ isDate(currentDate) ? currentDate.toISOString() : currentDate.toString() } ` diff --git a/src/charging-station/IdTagsCache.ts b/src/charging-station/IdTagsCache.ts index 166280c8..055a1936 100644 --- a/src/charging-station/IdTagsCache.ts +++ b/src/charging-station/IdTagsCache.ts @@ -133,20 +133,17 @@ export class IdTagsCache { } private getRandomIdTag (hashId: string, file: string): string { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const idTags = this.getIdTags(file)! + const idTags = this.getIdTags(file) ?? [] const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId) this.idTagsCachesAddressableIndexes.set( addressableKey, Math.floor(secureRandom() * idTags.length) ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)!] + return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey) ?? 0] } private getRoundRobinIdTag (hashId: string, file: string): string { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const idTags = this.getIdTags(file)! + const idTags = this.getIdTags(file) ?? [] const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId) const idTagIndex = this.idTagsCachesAddressableIndexes.get(addressableKey) ?? 0 const idTag = idTags[idTagIndex] diff --git a/src/charging-station/SharedLRUCache.ts b/src/charging-station/SharedLRUCache.ts index 2a28f432..98286f55 100644 --- a/src/charging-station/SharedLRUCache.ts +++ b/src/charging-station/SharedLRUCache.ts @@ -68,8 +68,9 @@ export class SharedLRUCache { ): void { if (this.isChargingStationConfigurationCacheable(chargingStationConfiguration)) { this.set( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.getChargingStationConfigurationKey(chargingStationConfiguration.configurationHash!), + this.getChargingStationConfigurationKey( + chargingStationConfiguration.configurationHash ?? '' + ), chargingStationConfiguration ) } @@ -77,8 +78,7 @@ export class SharedLRUCache { public setChargingStationTemplate (chargingStationTemplate: ChargingStationTemplate): void { this.set( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.getChargingStationTemplateKey(chargingStationTemplate.templateHash!), + this.getChargingStationTemplateKey(chargingStationTemplate.templateHash ?? ''), chargingStationTemplate ) } diff --git a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts index 74323b59..990afba4 100644 --- a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts @@ -270,8 +270,10 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne ): Promise { if (this.commandHandlers.has(command)) { this.cleanRequestPayload(command, requestPayload) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const commandHandler = this.commandHandlers.get(command)! + const commandHandler = this.commandHandlers.get(command) + if (commandHandler == null) { + throw new BaseError(`Unknown worker broadcast channel command: '${command}'`) + } if (isAsyncFunction(commandHandler)) { return await commandHandler(requestPayload) } @@ -394,14 +396,18 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne requestPayload?: BroadcastChannelRequestPayload ): Promise { const connectorId = requestPayload?.connectorId + if (connectorId == null) { + throw new BaseError( + `${this.chargingStation.logPrefix()} ${moduleName}.handleMeterValues: Missing connectorId in request payload` + ) + } switch (this.chargingStation.stationInfo?.ocppVersion) { case OCPPVersion.VERSION_16: { const configuredMeterValueSampleInterval = getConfigurationKey( this.chargingStation, StandardParametersKey.MeterValueSampleInterval ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const transactionId = this.chargingStation.getConnectorStatus(connectorId!)?.transactionId + const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId return await this.chargingStation.ocppRequestService.requestHandler< MeterValuesRequest, MeterValuesResponse @@ -426,10 +432,8 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: { const alignedDataInterval = OCPP20ServiceUtils.getAlignedDataInterval(this.chargingStation) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const evseId = this.chargingStation.getEvseIdByConnectorId(connectorId!) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const transactionId = this.chargingStation.getConnectorStatus(connectorId!)?.transactionId + const evseId = this.chargingStation.getEvseIdByConnectorId(connectorId) + const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId return await this.chargingStation.ocppRequestService.requestHandler< MeterValuesRequest, MeterValuesResponse @@ -553,8 +557,14 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne return undefined }) .finally(() => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.sendResponse([uuid, responsePayload!]) + this.sendResponse([ + uuid, + responsePayload ?? { + command, + hashId: this.chargingStation.stationInfo?.hashId, + status: ResponseStatus.FAILURE, + }, + ]) }) } } diff --git a/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts index b1bd85c6..8037b67b 100644 --- a/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts @@ -96,21 +96,21 @@ export class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChannel { responsesReceived: 1, }) } else { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const responses = this.responses.get(uuid)! - if (responses.responsesReceived < responses.responsesExpected) { - ++responses.responsesReceived - responses.responses.push(responsePayload) - } else { - logger.debug( - `${this.uiService.logPrefix(moduleName, 'responseHandler')} Received response after all expected responses:`, - { responsePayload, uuid } - ) + const responses = this.responses.get(uuid) + if (responses != null) { + if (responses.responsesReceived < responses.responsesExpected) { + ++responses.responsesReceived + responses.responses.push(responsePayload) + } else { + logger.debug( + `${this.uiService.logPrefix(moduleName, 'responseHandler')} Received response after all expected responses:`, + { responsePayload, uuid } + ) + } } } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const responses = this.responses.get(uuid)! - if (responses.responsesReceived >= responses.responsesExpected) { + const responses = this.responses.get(uuid) + if (responses != null && responses.responsesReceived >= responses.responsesExpected) { this.uiService.sendResponse(uuid, this.buildResponsePayload(uuid)) this.responses.delete(uuid) this.uiService.deleteBroadcastChannelRequest(uuid) diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index 4c8fad6e..8266f289 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -618,14 +618,16 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { chargingProfile.chargingSchedule.startSchedule ) } - if (chargingProfile.chargingSchedule.duration == null) { + if ( + chargingProfile.chargingSchedule.duration == null && + chargingProfile.chargingSchedule.startSchedule != null + ) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.composeCompositeSchedule: Charging profile id ${chargingProfile.chargingProfileId.toString()} has no duration defined and will be set to the maximum time allowed` ) chargingProfile.chargingSchedule.duration = differenceInSeconds( maxTime, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingProfile.chargingSchedule.startSchedule! + chargingProfile.chargingSchedule.startSchedule ) } if ( @@ -1114,14 +1116,18 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { const logConfiguration = Configuration.getConfigurationSection( ConfigurationSection.log ) + const logFile = logConfiguration.file + if (logFile == null) { + logger.warn( + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: log file not configured` + ) + return OCPP16Constants.OCPP_RESPONSE_EMPTY + } const logFiles = readdirSync( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - resolve((fileURLToPath(import.meta.url), '../', dirname(logConfiguration.file!))) + resolve((fileURLToPath(import.meta.url), '../', dirname(logFile))) ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .filter(file => file.endsWith(extname(logConfiguration.file!))) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .map(file => join(dirname(logConfiguration.file!), file)) + .filter(file => file.endsWith(extname(logFile))) + .map(file => join(dirname(logFile), file)) // eslint-disable-next-line @typescript-eslint/restrict-template-expressions const diagnosticsArchive = `${chargingStation.stationInfo?.chargingStationId}_logs.tar.gz` create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive)) @@ -1535,11 +1541,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED } if ( + connectorId != null && !OCPP16ServiceUtils.isConnectorIdValid( chargingStation, OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - connectorId! + connectorId ) ) { return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED @@ -1609,8 +1615,7 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { ) return OCPP16Constants.OCPP_RESPONSE_EMPTY } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - commandPayload.retrieveDate = convertToDate(commandPayload.retrieveDate)! + commandPayload.retrieveDate = convertToDate(commandPayload.retrieveDate) ?? new Date() if ( chargingStation.stationInfo?.firmwareStatus === OCPP16FirmwareStatus.Downloading || chargingStation.stationInfo?.firmwareStatus === OCPP16FirmwareStatus.Downloaded || diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index d55e10d7..75b15f13 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -199,13 +199,12 @@ export class OCPP16RequestService extends OCPPRequestService { commandParams.connectorId as number, commandParams.idTag as string ) && { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion reservationId: chargingStation.getReservationBy( 'connectorId', chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved ? 0 : (commandParams.connectorId as number) - )!.reservationId, + )?.reservationId, }), ...commandParams, } as unknown as Request @@ -227,15 +226,14 @@ export class OCPP16RequestService extends OCPPRequestService { idTag: chargingStation.getTransactionIdTag(commandParams.transactionId as number), meterStop: energyActiveImportRegister, timestamp: new Date(), - ...(chargingStation.stationInfo?.transactionDataMeterValues === true && { + ...(chargingStation.stationInfo?.transactionDataMeterValues === true && + connectorId != null && { transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.getConnectorStatus(connectorId!)! - .transactionBeginMeterValue! as OCPP16MeterValue, + chargingStation.getConnectorStatus(connectorId) + ?.transactionBeginMeterValue as OCPP16MeterValue, OCPP16ServiceUtils.buildTransactionEndMeterValue( chargingStation, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - connectorId!, + connectorId, energyActiveImportRegister ) as OCPP16MeterValue ), diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 7ed63956..6960de74 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -173,8 +173,10 @@ export class OCPP16ResponseService extends OCPPResponseService { } } if (authorizeConnectorId != null) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const authorizeConnectorStatus = chargingStation.getConnectorStatus(authorizeConnectorId)! + const authorizeConnectorStatus = chargingStation.getConnectorStatus(authorizeConnectorId) + if (authorizeConnectorStatus == null) { + return + } if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { authorizeConnectorStatus.idTagAuthorized = true logger.debug( @@ -439,8 +441,13 @@ export class OCPP16ResponseService extends OCPPResponseService { }#${connectorId.toString()} for idTag '${truncateId(requestPayload.idTag)}'` ) if (chargingStation.stationInfo?.powerSharedByConnectors === true) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ++chargingStation.powerDivider! + if (chargingStation.powerDivider != null) { + ++chargingStation.powerDivider + } else { + logger.error( + `${chargingStation.logPrefix()} ${moduleName}.handleResponseStartTransaction: powerDivider is undefined, cannot increment` + ) + } } const configuredMeterValueSampleInterval = getConfigurationKey( chargingStation, @@ -517,8 +524,15 @@ export class OCPP16ResponseService extends OCPPResponseService { } as OCPP16StatusNotificationRequest) } if (chargingStation.stationInfo?.powerSharedByConnectors === true) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.powerDivider!-- + if (chargingStation.powerDivider != null && chargingStation.powerDivider > 0) { + --chargingStation.powerDivider + } else { + logger.error( + `${chargingStation.logPrefix()} ${moduleName}.handleResponseStopTransaction: powerDivider is ${ + chargingStation.powerDivider?.toString() ?? 'undefined' + }, cannot decrement` + ) + } } const transactionConnectorStatus = chargingStation.getConnectorStatus(transactionConnectorId) resetConnectorStatus(transactionConnectorStatus) diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index 0edd3aa2..057a8f40 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -144,8 +144,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { for (const connectorId of connectorIds) { let response: OCPP16ChangeAvailabilityResponse = OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const connectorStatus = chargingStation.getConnectorStatus(connectorId)! + const connectorStatus = chargingStation.getConnectorStatus(connectorId) + if (connectorStatus == null) { + continue + } if (connectorStatus.transactionStarted === true) { response = OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED } @@ -551,8 +553,10 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { logger.warn( `${chargingStation.logPrefix()} ${moduleName}.setChargingProfile: Trying to set a charging profile on connector id ${connectorId.toString()} with an uninitialized charging profiles array attribute, applying deferred initialization` ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = [] + const connectorStatus = chargingStation.getConnectorStatus(connectorId) + if (connectorStatus != null) { + connectorStatus.chargingProfiles = [] + } } if (!Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { logger.warn( @@ -699,11 +703,12 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { chargingSchedule: OCPP16ChargingSchedule, compositeInterval: Interval ): OCPP16ChargingSchedule | undefined => { + if (chargingSchedule.startSchedule == null || chargingSchedule.duration == null) { + return undefined + } const chargingScheduleInterval: Interval = { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - start: chargingSchedule.startSchedule!, + end: addSeconds(chargingSchedule.startSchedule, chargingSchedule.duration), + start: chargingSchedule.startSchedule, } if (areIntervalsOverlapping(chargingScheduleInterval, compositeInterval)) { chargingSchedule.chargingSchedulePeriod.sort((a, b) => a.startPeriod - b.startPeriod) diff --git a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts index 981a0fa8..7f88d8fe 100644 --- a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts @@ -403,7 +403,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { ) => { if (response.status === RequestStartStopStatusEnumType.Accepted) { const connectorId = chargingStation.getConnectorIdByTransactionId(response.transactionId) - if (connectorId != null) { + if (connectorId != null && response.transactionId != null) { const connectorStatus = chargingStation.getConnectorStatus(connectorId) const startedMeterValues = connectorStatus != null @@ -414,8 +414,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { OCPP20TransactionEventEnumType.Started, OCPP20TriggerReasonEnumType.RemoteStart, connectorId, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - response.transactionId!, + response.transactionId, startedMeterValues.length > 0 ? { meterValue: startedMeterValues } : undefined ).catch((error: unknown) => { logger.error( @@ -1261,7 +1260,7 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { ) if (maxChainSizeKey?.value != null) { const maxChainSize = parseInt(maxChainSizeKey.value, 10) - if (!isNaN(maxChainSize) && maxChainSize > 0) { + if (!Number.isNaN(maxChainSize) && maxChainSize > 0) { const chainByteSize = Buffer.byteLength(certificateChain, 'utf8') if (chainByteSize > maxChainSize) { logger.warn( @@ -3004,12 +3003,12 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { idTokenString: string ): boolean { try { + const idTagsFile = + chargingStation.stationInfo != null ? getIdTagsFile(chargingStation.stationInfo) : undefined return ( chargingStation.hasIdTags() && - chargingStation.idTagsCache - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .getIdTags(getIdTagsFile(chargingStation.stationInfo!)!) - ?.includes(idTokenString) === true + idTagsFile != null && + chargingStation.idTagsCache.getIdTags(idTagsFile)?.includes(idTokenString) === true ) } catch (error) { logger.error( diff --git a/src/charging-station/ocpp/OCPPIncomingRequestService.ts b/src/charging-station/ocpp/OCPPIncomingRequestService.ts index ea7f2674..c3a13f7b 100644 --- a/src/charging-station/ocpp/OCPPIncomingRequestService.ts +++ b/src/charging-station/ocpp/OCPPIncomingRequestService.ts @@ -99,8 +99,15 @@ export abstract class OCPPIncomingRequestService extends EventEmitter { ) { try { this.validateIncomingRequestPayload(chargingStation, commandName, commandPayload) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const incomingRequestHandler = this.incomingRequestHandlers.get(commandName)! + const incomingRequestHandler = this.incomingRequestHandlers.get(commandName) + if (incomingRequestHandler == null) { + throw new OCPPError( + ErrorType.NOT_IMPLEMENTED, + `${commandName} incoming request handler not found`, + commandName, + commandPayload + ) + } if (isAsyncFunction(incomingRequestHandler)) { response = (await incomingRequestHandler(chargingStation, commandPayload)) as ResType } else { diff --git a/src/charging-station/ocpp/OCPPResponseService.ts b/src/charging-station/ocpp/OCPPResponseService.ts index ca799443..96d27f29 100644 --- a/src/charging-station/ocpp/OCPPResponseService.ts +++ b/src/charging-station/ocpp/OCPPResponseService.ts @@ -85,8 +85,15 @@ export abstract class OCPPResponseService { logger.debug( `${chargingStation.logPrefix()} ${this.moduleName}.responseHandler: Handling '${commandName}' response` ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const responseHandler = this.responseHandlers.get(commandName)! + const responseHandler = this.responseHandlers.get(commandName) + if (responseHandler == null) { + throw new OCPPError( + ErrorType.NOT_IMPLEMENTED, + `${commandName} response handler not found`, + commandName, + payload + ) + } if (isAsyncFunction(responseHandler)) { await responseHandler(chargingStation, payload, requestPayload) } else { diff --git a/src/charging-station/ocpp/OCPPServiceUtils.ts b/src/charging-station/ocpp/OCPPServiceUtils.ts index 9ed49dcb..8beae8f6 100644 --- a/src/charging-station/ocpp/OCPPServiceUtils.ts +++ b/src/charging-station/ocpp/OCPPServiceUtils.ts @@ -249,14 +249,12 @@ export const isIdTagAuthorized = async ( } const isIdTagLocalAuthorized = (chargingStation: ChargingStation, idTag: string): boolean => { + const idTagsFile = + chargingStation.stationInfo != null ? getIdTagsFile(chargingStation.stationInfo) : undefined return ( chargingStation.hasIdTags() && - isNotEmptyString( - chargingStation.idTagsCache - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .getIdTags(getIdTagsFile(chargingStation.stationInfo!)!) - ?.find(tag => tag === idTag) - ) + idTagsFile != null && + isNotEmptyString(chargingStation.idTagsCache.getIdTags(idTagsFile)?.find(tag => tag === idTag)) ) } @@ -265,8 +263,10 @@ const isIdTagRemoteAuthorized = async ( connectorId: number, idTag: string ): Promise => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - chargingStation.getConnectorStatus(connectorId)!.authorizeIdTag = idTag + const connectorStatus = chargingStation.getConnectorStatus(connectorId) + if (connectorStatus != null) { + connectorStatus.authorizeIdTag = idTag + } switch (chargingStation.stationInfo?.ocppVersion) { case OCPPVersion.VERSION_16: return ( @@ -1997,16 +1997,15 @@ const getLimitFromSampledValueTemplateCustomValue = ( return max( min( (!Number.isNaN(parsedValue) ? parsedValue : Number.POSITIVE_INFINITY) * - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - options.unitMultiplier!, + (options.unitMultiplier ?? 1), maxLimit ), minLimit ) } return ( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (!Number.isNaN(parsedValue) ? parsedValue : options.fallbackValue!) * options.unitMultiplier! + (!Number.isNaN(parsedValue) ? parsedValue : (options.fallbackValue ?? 0)) * + (options.unitMultiplier ?? 1) ) } diff --git a/src/charging-station/ui-server/ui-services/AbstractUIService.ts b/src/charging-station/ui-server/ui-services/AbstractUIService.ts index cddb5dcd..03b57805 100644 --- a/src/charging-station/ui-server/ui-services/AbstractUIService.ts +++ b/src/charging-station/ui-server/ui-services/AbstractUIService.ts @@ -153,8 +153,10 @@ export abstract class AbstractUIService { } // Call the request handler to build the response payload - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const requestHandler = this.requestHandlers.get(command)! + const requestHandler = this.requestHandlers.get(command) + if (requestHandler == null) { + throw new BaseError(`'${command}' request handler not found`) + } if (isAsyncFunction(requestHandler)) { responsePayload = await requestHandler(uuid, command, requestPayload) } else { diff --git a/src/performance/PerformanceStatistics.ts b/src/performance/PerformanceStatistics.ts index 518ec8c4..008350da 100644 --- a/src/performance/PerformanceStatistics.ts +++ b/src/performance/PerformanceStatistics.ts @@ -127,48 +127,42 @@ export class PerformanceStatistics { messageType: MessageType ): void { switch (messageType) { - case MessageType.CALL_ERROR_MESSAGE: - if ( - this.statistics.statisticsData.has(command) && - this.statistics.statisticsData.get(command)?.errorCount != null - ) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ++this.statistics.statisticsData.get(command)!.errorCount! + case MessageType.CALL_ERROR_MESSAGE: { + const commandStatisticsData = this.statistics.statisticsData.get(command) + if (commandStatisticsData?.errorCount != null) { + ++commandStatisticsData.errorCount } else { this.statistics.statisticsData.set(command, { - ...this.statistics.statisticsData.get(command), + ...commandStatisticsData, errorCount: 1, }) } break - case MessageType.CALL_MESSAGE: - if ( - this.statistics.statisticsData.has(command) && - this.statistics.statisticsData.get(command)?.requestCount != null - ) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ++this.statistics.statisticsData.get(command)!.requestCount! + } + case MessageType.CALL_MESSAGE: { + const commandStatisticsData = this.statistics.statisticsData.get(command) + if (commandStatisticsData?.requestCount != null) { + ++commandStatisticsData.requestCount } else { this.statistics.statisticsData.set(command, { - ...this.statistics.statisticsData.get(command), + ...commandStatisticsData, requestCount: 1, }) } break - case MessageType.CALL_RESULT_MESSAGE: - if ( - this.statistics.statisticsData.has(command) && - this.statistics.statisticsData.get(command)?.responseCount != null - ) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ++this.statistics.statisticsData.get(command)!.responseCount! + } + case MessageType.CALL_RESULT_MESSAGE: { + const commandStatisticsData = this.statistics.statisticsData.get(command) + if (commandStatisticsData?.responseCount != null) { + ++commandStatisticsData.responseCount } else { this.statistics.statisticsData.set(command, { - ...this.statistics.statisticsData.get(command), + ...commandStatisticsData, responseCount: 1, }) } break + } default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions logger.error(`${this.logPrefix()} wrong message type ${messageType}`) @@ -210,62 +204,45 @@ export class PerformanceStatistics { if (!this.statistics.statisticsData.has(entry.name)) { this.statistics.statisticsData.set(entry.name, {}) } - // Update current statistics - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.timeMeasurementCount = - (this.statistics.statisticsData.get(entry.name)?.timeMeasurementCount ?? 0) + 1 - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.currentTimeMeasurement = entry.duration - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.minTimeMeasurement = min( - entry.duration, - this.statistics.statisticsData.get(entry.name)?.minTimeMeasurement ?? Number.POSITIVE_INFINITY - ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.maxTimeMeasurement = max( - entry.duration, - this.statistics.statisticsData.get(entry.name)?.maxTimeMeasurement ?? Number.NEGATIVE_INFINITY - ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.totalTimeMeasurement = - (this.statistics.statisticsData.get(entry.name)?.totalTimeMeasurement ?? 0) + entry.duration - if ( - !( - this.statistics.statisticsData.get(entry.name)?.measurementTimeSeries instanceof - CircularBuffer + const entryStatisticsData = this.statistics.statisticsData.get(entry.name) + if (entryStatisticsData != null) { + // Update current statistics + entryStatisticsData.timeMeasurementCount = (entryStatisticsData.timeMeasurementCount ?? 0) + 1 + entryStatisticsData.currentTimeMeasurement = entry.duration + entryStatisticsData.minTimeMeasurement = min( + entry.duration, + entryStatisticsData.minTimeMeasurement ?? Number.POSITIVE_INFINITY ) - ) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.measurementTimeSeries = - new CircularBuffer( + entryStatisticsData.maxTimeMeasurement = max( + entry.duration, + entryStatisticsData.maxTimeMeasurement ?? Number.NEGATIVE_INFINITY + ) + entryStatisticsData.totalTimeMeasurement = + (entryStatisticsData.totalTimeMeasurement ?? 0) + entry.duration + if (!(entryStatisticsData.measurementTimeSeries instanceof CircularBuffer)) { + entryStatisticsData.measurementTimeSeries = new CircularBuffer( Array, Constants.DEFAULT_CIRCULAR_BUFFER_CAPACITY ) + } + entryStatisticsData.measurementTimeSeries.push({ + timestamp: entry.startTime, + value: entry.duration, + }) + const timeMeasurementValues = extractTimeSeriesValues( + entryStatisticsData.measurementTimeSeries + ) + entryStatisticsData.avgTimeMeasurement = average(timeMeasurementValues) + entryStatisticsData.medTimeMeasurement = median(timeMeasurementValues) + entryStatisticsData.ninetyFiveThPercentileTimeMeasurement = percentile( + timeMeasurementValues, + 95 + ) + entryStatisticsData.stdTimeMeasurement = std( + timeMeasurementValues, + entryStatisticsData.avgTimeMeasurement + ) } - this.statistics.statisticsData.get(entry.name)?.measurementTimeSeries?.push({ - timestamp: entry.startTime, - value: entry.duration, - }) - const timeMeasurementValues = extractTimeSeriesValues( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)! - .measurementTimeSeries as CircularBuffer - ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.avgTimeMeasurement = - average(timeMeasurementValues) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.medTimeMeasurement = - median(timeMeasurementValues) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.ninetyFiveThPercentileTimeMeasurement = - percentile(timeMeasurementValues, 95) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.stdTimeMeasurement = std( - timeMeasurementValues, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.statistics.statisticsData.get(entry.name)!.avgTimeMeasurement - ) this.statistics.updatedAt = new Date() if ( Configuration.getConfigurationSection( @@ -307,10 +284,7 @@ export class PerformanceStatistics { ConfigurationSection.log ) const logStatisticsInterval = - logConfiguration.enabled === true - ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - logConfiguration.statisticsInterval! - : 0 + logConfiguration.enabled === true ? (logConfiguration.statisticsInterval ?? 0) : 0 if (logStatisticsInterval > 0 && this.displayInterval == null) { this.displayInterval = setInterval(() => { this.logStatistics() diff --git a/src/utils/AsyncLock.ts b/src/utils/AsyncLock.ts index 4e2c8151..0c829858 100644 --- a/src/utils/AsyncLock.ts +++ b/src/utils/AsyncLock.ts @@ -46,11 +46,12 @@ export class AsyncLock { } private static getAsyncLock (type: AsyncLockType): AsyncLock { - if (!AsyncLock.asyncLocks.has(type)) { - AsyncLock.asyncLocks.set(type, new AsyncLock()) + let asyncLock = AsyncLock.asyncLocks.get(type) + if (asyncLock == null) { + asyncLock = new AsyncLock() + AsyncLock.asyncLocks.set(type, asyncLock) } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return AsyncLock.asyncLocks.get(type)! + return asyncLock } private static release (type: AsyncLockType): void { diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index fbe02787..a3a16cfe 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -196,10 +196,12 @@ export class Configuration { } public static workerPoolInUse (): boolean { - return [WorkerProcessType.dynamicPool, WorkerProcessType.fixedPool].includes( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - Configuration.getConfigurationSection(ConfigurationSection.worker) - .processType! + const processType = Configuration.getConfigurationSection( + ConfigurationSection.worker + ).processType + return ( + processType != null && + [WorkerProcessType.dynamicPool, WorkerProcessType.fixedPool].includes(processType) ) } @@ -316,8 +318,9 @@ export class Configuration { ...(deprecatedWorkerConfiguration as Partial), ...(has(ConfigurationSection.worker, configData) && configData?.worker), } - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - checkWorkerProcessType(workerConfiguration.processType!) + if (workerConfiguration.processType != null) { + checkWorkerProcessType(workerConfiguration.processType) + } checkWorkerElementsPerWorker(workerConfiguration.elementsPerWorker) return workerConfiguration } diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index 96c94a62..8e4d9dfe 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -72,8 +72,7 @@ const getLoggerInstance = (): WinstonLogger => { } const logFormat = format.combine( format.splat(), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - (format[logConfiguration.format! as keyof FormatWrap] as FormatWrap)() + (format[(logConfiguration.format ?? 'simple') as keyof FormatWrap] as FormatWrap)() ) loggerInstance = createLogger({ format: logFormat, diff --git a/src/utils/MessageChannelUtils.ts b/src/utils/MessageChannelUtils.ts index 9335c644..2ee6ccfe 100644 --- a/src/utils/MessageChannelUtils.ts +++ b/src/utils/MessageChannelUtils.ts @@ -4,6 +4,8 @@ import type { ChargingStation } from '../charging-station/index.js' import { type ChargingStationData, + type ChargingStationInfo, + type ChargingStationOcppConfiguration, type ChargingStationWorkerMessage, ChargingStationWorkerMessageEvents, type Statistics, @@ -99,11 +101,10 @@ const buildChargingStationDataPayload = (chargingStation: ChargingStation): Char bootNotificationResponse: chargingStation.bootNotificationResponse, connectors: buildConnectorEntries(chargingStation), evses: buildEvseEntries(chargingStation), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ocppConfiguration: chargingStation.ocppConfiguration!, + ocppConfiguration: + chargingStation.ocppConfiguration ?? ({} as ChargingStationOcppConfiguration), started: chargingStation.started, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - stationInfo: chargingStation.stationInfo!, + stationInfo: chargingStation.stationInfo ?? ({} as ChargingStationInfo), supervisionUrl: chargingStation.wsConnectionUrl.href, timestamp: Date.now(), wsState: chargingStation.wsConnection?.readyState, diff --git a/src/worker/WorkerSet.ts b/src/worker/WorkerSet.ts index 78a47e72..b8e4ec24 100644 --- a/src/worker/WorkerSet.ts +++ b/src/worker/WorkerSet.ts @@ -5,7 +5,7 @@ import { EventEmitterAsyncResource } from 'node:events' import { SHARE_ENV, Worker } from 'node:worker_threads' import { WorkerAbstract } from './WorkerAbstract.js' -import { EMPTY_FUNCTION, workerSetVersion } from './WorkerConstants.js' +import { DEFAULT_ELEMENTS_PER_WORKER, EMPTY_FUNCTION, workerSetVersion } from './WorkerConstants.js' import { type SetInfo, type UUIDv4, @@ -33,8 +33,7 @@ export class WorkerSet extends Worke (accumulator, workerSetElement) => accumulator + workerSetElement.numberOfWorkerElements, 0 ), - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - elementsPerWorker: this.maxElementsPerWorker!, + elementsPerWorker: this.maxElementsPerWorker ?? DEFAULT_ELEMENTS_PER_WORKER, size: this.size, started: this.started, type: 'set', @@ -167,9 +166,12 @@ export class WorkerSet extends Worke worker.on('message', (message: WorkerMessage) => { const { data, event, uuid } = message if (this.promiseResponseMap.has(uuid)) { + const responseWrapper = this.promiseResponseMap.get(uuid) + if (responseWrapper == null) { + return + } let error: Error - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const { reject, resolve, workerSetElement } = this.promiseResponseMap.get(uuid)! + const { reject, resolve, workerSetElement } = responseWrapper switch (event) { case WorkerMessageEvents.addedWorkerElement: ++workerSetElement.numberOfWorkerElements @@ -241,8 +243,10 @@ export class WorkerSet extends Worke private async getWorkerSetElement (): Promise { let chosenWorkerSetElement: undefined | WorkerSetElement for (const workerSetElement of this.workerSet) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (workerSetElement.numberOfWorkerElements < this.workerOptions.elementsPerWorker!) { + if ( + workerSetElement.numberOfWorkerElements < + (this.workerOptions.elementsPerWorker ?? DEFAULT_ELEMENTS_PER_WORKER) + ) { chosenWorkerSetElement = workerSetElement break } -- 2.43.0