try {
this.internalStopMessageSequence()
} catch (error) {
- logger.error(
- `${this.logPrefix()} Error while stopping the internal message sequence:`,
- error
- )
+ const e = ensureError(error)
+ logger.error(`${this.logPrefix()} Error while stopping the internal message sequence:`, e)
}
})
try {
await this.stop()
} catch (error) {
- logger.error(`${this.logPrefix()} Error stopping station during delete:`, error)
+ const e = ensureError(error)
+ logger.error(`${this.logPrefix()} Error stopping station during delete:`, e)
}
}
AutomaticTransactionGenerator.deleteInstance(this)
try {
rmSync(this.configurationFile, { force: true })
} catch (error) {
+ const e = ensureError(error)
logger.error(
`${this.logPrefix()} Failed to delete configuration file ${this.configurationFile}:`,
- error
+ e
)
}
}
try {
await this.stop(reason, graceful ? this.stationInfo?.stopTransactionsOnStopped : false)
} catch (error) {
- logger.error(`${this.logPrefix()} Error during reset stop phase:`, error)
+ const e = ensureError(error)
+ logger.error(`${this.logPrefix()} Error during reset stop phase:`, e)
return
}
await sleep(this.stationInfo?.resetTime ?? 0)
this.restartHeartbeat()
this.restartWebSocketPing()
} catch (error) {
+ const e = ensureError(error)
logger.error(
`${this.logPrefix()} ${FileType.ChargingStationTemplate} file monitoring error:`,
- error
+ e
)
}
}
}
} catch (error) {
if (!Array.isArray(request)) {
+ const e = ensureError(error)
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
- logger.error(`${this.logPrefix()} Incoming message '${request}' parsing error:`, error)
+ logger.error(`${this.logPrefix()} Incoming message '${request}' parsing error:`, e)
// OCPP 2.0.1 §4.2.3: respond with CALLERROR using messageId "-1"
if (this.stationInfo?.ocppVersion !== OCPPVersion.VERSION_16) {
await this.ocppRequestService
type ResponsePayload,
ResponseStatus,
} from '../../types/index.js'
-import { logger } from '../../utils/index.js'
+import { isNotEmptyArray, logger } from '../../utils/index.js'
import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'
const moduleName = 'UIServiceWorkerBroadcastChannel'
private buildResponsePayload (uuid: string): ResponsePayload {
const responsesArray = this.responses.get(uuid)?.responses ?? []
const responsesStatus =
- responsesArray.length > 0 &&
+ isNotEmptyArray(responsesArray) &&
responsesArray.every(response => response.status === ResponseStatus.SUCCESS)
? ResponseStatus.SUCCESS
: ResponseStatus.FAILURE
)
}
}
- if (allChargingProfiles.length === 0) {
+ if (isEmpty(allChargingProfiles)) {
return OCPP16Constants.OCPP_RESPONSE_REJECTED
}
const compositeSchedule = this.composeCompositeSchedule(
HashAlgorithmEnumType,
InstallCertificateUseEnumType,
} from '../../../types/index.js'
-import { convertToDate, getErrorMessage, isEmpty } from '../../../utils/index.js'
+import { convertToDate, getErrorMessage, isEmpty, isNotEmptyArray } from '../../../utils/index.js'
/**
* Interface for ChargingStation with certificate manager
.map(dirent => dirent.name)
for (const certType of certTypes) {
- if (filterTypes != null && filterTypes.length > 0) {
+ if (filterTypes != null && isNotEmptyArray(filterTypes)) {
if (!filterTypes.includes(certType as InstallCertificateUseEnumType)) {
continue
}
convertToIntOrNaN,
generateUUID,
isEmpty,
+ isNotEmptyArray,
isNotEmptyString,
logger,
promiseWithTimeout,
connectorId,
response.transactionId,
{
- ...(startedMeterValues.length > 0 && { meterValue: startedMeterValues }),
+ ...(isNotEmptyArray(startedMeterValues) && { meterValue: startedMeterValues }),
remoteStartId: request.remoteStartId,
}
).catch((error: unknown) => {
]
return order.indexOf(a.type) - order.indexOf(b.type)
})
- if (entry.attributes.length > 0) {
+ if (isNotEmptyArray(entry.attributes)) {
reportData.push({
component: entry.component,
variable: entry.variable,
transactionId?: string
): boolean {
const queue = connectorStatus.transactionEventQueue
- if (queue == null || queue.length === 0) {
+ if (queue == null || !isNotEmptyArray(queue)) {
return false
}
if (transactionId == null) {
const stationState = this.getStationState(chargingStation)
const cached = stationState.reportDataCache.get(commandPayload.requestId)
const reportData = cached ?? this.buildReportData(chargingStation, commandPayload.reportBase)
- if (!cached && reportData.length > 0) {
+ if (!cached && isNotEmptyArray(reportData)) {
stationState.reportDataCache.set(commandPayload.requestId, reportData)
}
- if (reportData.length === 0) {
+ if (!isNotEmptyArray(reportData)) {
logger.info(
`${chargingStation.logPrefix()} ${moduleName}.handleRequestGetBaseReport: No data available for reportBase ${commandPayload.reportBase}`
)
)
return {
- certificateHashDataChain:
- result.certificateHashDataChain.length > 0 ? result.certificateHashDataChain : undefined,
- status:
- result.certificateHashDataChain.length > 0
- ? GetInstalledCertificateStatusEnumType.Accepted
- : GetInstalledCertificateStatusEnumType.NotFound,
+ certificateHashDataChain: isNotEmptyArray(result.certificateHashDataChain)
+ ? result.certificateHashDataChain
+ : undefined,
+ status: isNotEmptyArray(result.certificateHashDataChain)
+ ? GetInstalledCertificateStatusEnumType.Accepted
+ : GetInstalledCertificateStatusEnumType.NotFound,
}
} catch (error) {
logger.error(
OCPP20ComponentName.OCPPCommCtrlr,
OCPP20RequiredVariableName.NetworkConfigurationPriority
)
- if (priorityValue.length > 0) {
+ if (isNotEmptyString(priorityValue)) {
const priorities = priorityValue.split(',').map(Number)
if (!priorities.includes(commandPayload.configurationSlot)) {
logger.warn(
)
if (
masterPassGroupId != null &&
- masterPassGroupId.length > 0 &&
+ isNotEmptyString(masterPassGroupId) &&
groupIdToken?.idToken === masterPassGroupId
) {
logger.debug(
chunks.push(reportData.slice(i, i + maxItemsPerMessage))
}
- if (chunks.length === 0) {
+ if (!isNotEmptyArray(chunks)) {
chunks.push(undefined) // undefined means reportData will be omitted from the request
}
requestId,
seqNo,
tbc: !isLastChunk,
- ...(chunk !== undefined && chunk.length > 0 && { reportData: chunk }),
+ ...(chunk !== undefined && isNotEmptyArray(chunk) && { reportData: chunk }),
}
await chargingStation.ocppRequestService.requestHandler<
if (
stationState.isDrainingSecurityEvents ||
!chargingStation.isWebSocketConnectionOpened() ||
- stationState.securityEventQueue.length === 0
+ !isNotEmptyArray(stationState.securityEventQueue)
) {
return
}
`${chargingStation.logPrefix()} ${moduleName}.sendQueuedSecurityEvents: Draining ${queue.length.toString()} queued security event(s)`
)
const drainNextEvent = (): void => {
- if (queue.length === 0 || !chargingStation.isWebSocketConnectionOpened()) {
+ if (!isNotEmptyArray(queue) || !chargingStation.isWebSocketConnectionOpened()) {
stationState.isDrainingSecurityEvents = false
return
}
return false
}
- if (chargingProfile.chargingSchedule.length === 0) {
+ if (!isNotEmptyArray(chargingProfile.chargingSchedule)) {
logger.warn(
`${chargingStation.logPrefix()} ${moduleName}.validateChargingProfile: Charging profile must contain at least one charging schedule`
)
return false
}
- if (schedule.chargingSchedulePeriod.length === 0) {
+ if (!isNotEmptyArray(schedule.chargingSchedulePeriod)) {
logger.warn(
`${chargingStation.logPrefix()} ${moduleName}.validateChargingSchedule: Schedule must contain at least one charging schedule period`
)
convertToIntOrNaN,
formatDurationMilliSeconds,
generateUUID,
+ isNotEmptyArray,
logger,
validateIdentifierString,
} from '../../../utils/index.js'
measurandsKey,
OCPP20ReadingContextEnumType.TRANSACTION_BEGIN
) as OCPP20MeterValue
- return startedMeterValue.sampledValue.length > 0 ? [startedMeterValue] : []
+ return isNotEmptyArray(startedMeterValue.sampledValue) ? [startedMeterValue] : []
} catch (error) {
logger.warn(
`${chargingStation.logPrefix()} ${moduleName}.buildTransactionStartedMeterValues: ${(error as Error).message}`
const connectorStatus = chargingStation.getConnectorStatus(connectorId)
if (
connectorStatus?.transactionEventQueue == null ||
- connectorStatus.transactionEventQueue.length === 0
+ !isNotEmptyArray(connectorStatus.transactionEventQueue)
) {
return
}
interval,
measurandsKey
) as OCPP20MeterValue
- if (meterValue.sampledValue.length > 0) {
+ if (isNotEmptyArray(meterValue.sampledValue)) {
cs.transactionEndedMeterValues?.push(meterValue)
}
}
{
idToken:
idTag != null ? { idToken: idTag, type: OCPP20IdTokenEnumType.ISO14443 } : undefined,
- ...(startedMeterValues.length > 0 && { meterValue: startedMeterValues }),
+ ...(isNotEmptyArray(startedMeterValues) && { meterValue: startedMeterValues }),
}
)
return {
}
}
}
- if (terminationPromises.length > 0) {
+ if (isNotEmptyArray(terminationPromises)) {
await Promise.all(terminationPromises)
}
}
measurandsKey,
OCPP20ReadingContextEnumType.TRANSACTION_END
) as OCPP20MeterValue
- if (finalMeterValue.sampledValue.length > 0) {
+ if (isNotEmptyArray(finalMeterValue.sampledValue)) {
return [...endedMeterValues, finalMeterValue]
}
} catch (error) {
`${chargingStation.logPrefix()} ${moduleName}.buildTransactionEndedMeterValues: ${(error as Error).message}`
)
}
- return endedMeterValues.length > 0 ? endedMeterValues : []
+ return isNotEmptyArray(endedMeterValues) ? endedMeterValues : []
}
private static readVariableAsIntervalMs (
transactionId,
{
evseId,
- meterValue: endedMeterValues.length > 0 ? endedMeterValues : undefined,
+ meterValue: isNotEmptyArray(endedMeterValues) ? endedMeterValues : undefined,
stoppedReason,
}
)
transactionEventRequest.idToken = commandParams.idToken
connectorStatus.transactionIdTokenSent = true
}
- if (commandParams.meterValue !== undefined && commandParams.meterValue.length > 0) {
+ if (commandParams.meterValue !== undefined && isNotEmptyArray(commandParams.meterValue)) {
transactionEventRequest.meterValue = commandParams.meterValue
}
if (commandParams.cableMaxCurrent !== undefined) {
SetVariableStatusEnumType,
type VariableType,
} from '../../../types/index.js'
-import { Constants, convertToIntOrNaN, logger } from '../../../utils/index.js'
+import { Constants, convertToIntOrNaN, isEmpty, logger } from '../../../utils/index.js'
import { type ChargingStation } from '../../ChargingStation.js'
import {
addConfigurationKey,
let variableValue = this.resolveVariableValue(chargingStation, component, variable)
- if (variableValue.length === 0) {
+ if (isEmpty(variableValue)) {
if (
resolvedAttributeType === AttributeEnumType.Target &&
variableMetadata.supportsTarget === true
ReasonCodeEnumType,
type VariableName,
} from '../../../types/index.js'
-import { Constants, convertToIntOrNaN, has, isEmpty } from '../../../utils/index.js'
+import {
+ Constants,
+ convertToFloat,
+ convertToInt,
+ convertToIntOrNaN,
+ has,
+ isEmpty,
+} from '../../../utils/index.js'
import { OCPP20Constants } from './OCPP20Constants.js'
/**
reason: ReasonCodeEnumType.InvalidValue,
}
}
- const num = Number(rawValue)
+ const num = convertToFloat(rawValue)
if (variableMetadata.positive && num <= 0) {
return {
info: 'Positive decimal > 0 required',
reason: ReasonCodeEnumType.InvalidValue,
}
}
- const num = Number(rawValue)
+ const num = convertToInt(rawValue)
if (variableMetadata.allowZero && !variableMetadata.positive && num < 0) {
return {
info: 'Integer >= 0 required',
}
}
const tokens = rawValue.split(',').map(t => t.trim())
- if (tokens.some(t => t.length === 0)) {
+ if (tokens.some(t => isEmpty(t))) {
return { info: 'Empty list member', ok: false, reason: ReasonCodeEnumType.InvalidValue }
}
const seen = new Set<string>()
RequestCommand,
StandardParametersKey,
} from '../../../../types/index.js'
-import { convertToBoolean, getErrorMessage, logger, truncateId } from '../../../../utils/index.js'
+import {
+ convertToBoolean,
+ getErrorMessage,
+ isEmpty,
+ logger,
+ truncateId,
+} from '../../../../utils/index.js'
import {
AuthContext,
AuthenticationMethod,
}
// Check length (OCPP 1.6 spec: max 20 characters)
- if (
- identifier.value.length === 0 ||
- identifier.value.length > AuthValidators.MAX_IDTAG_LENGTH
- ) {
+ if (isEmpty(identifier.value) || identifier.value.length > AuthValidators.MAX_IDTAG_LENGTH) {
return false
}
OCPP20RequiredVariableName,
OCPPVersion,
} from '../../../../types/index.js'
-import { getErrorMessage, logger, truncateId } from '../../../../utils/index.js'
+import { getErrorMessage, isEmpty, logger, truncateId } from '../../../../utils/index.js'
import {
AuthContext,
AuthenticationMethod,
}
// Check length (OCPP 2.0 spec: max 36 characters)
- if (identifier.value.length === 0 || identifier.value.length > 36) {
+ if (isEmpty(identifier.value) || identifier.value.length > 36) {
return false
}
import type { AuthCache, CacheStats } from '../interfaces/OCPPAuthService.js'
import type { AuthorizationResult } from '../types/AuthTypes.js'
-import { Constants, logger, truncateId } from '../../../../utils/index.js'
+import { Constants, logger, roundTo, truncateId } from '../../../../utils/index.js'
import { AuthorizationStatus } from '../types/AuthTypes.js'
const moduleName = 'InMemoryAuthCache'
return {
evictions: this.stats.evictions,
expiredEntries: this.stats.expired,
- hitRate: Math.round(hitRate * 100) / 100,
+ hitRate: roundTo(hitRate, 2),
hits: this.stats.hits,
memoryUsage,
misses: this.stats.misses,
getErrorMessage,
has,
logger,
+ roundTo,
truncateId,
} from '../../../../utils/index.js'
import { type ChargingStation } from '../../../index.js'
}
return {
- avgResponseTime: Math.round(avgResponseTime * 100) / 100,
- cacheHitRate: Math.round(cacheHitRate * 10000) / 100,
+ avgResponseTime: roundTo(avgResponseTime, 2),
+ cacheHitRate: roundTo(cacheHitRate * 100, 2),
failedAuth: this.metrics.failedAuth,
lastUpdatedDate: this.metrics.lastReset,
- localUsageRate: Math.round(localUsageRate * 10000) / 100,
+ localUsageRate: roundTo(localUsageRate * 100, 2),
rateLimit: rateLimitStatistics,
- remoteSuccessRate: Math.round(remoteSuccessRate * 10000) / 100,
+ remoteSuccessRate: roundTo(remoteSuccessRate * 100, 2),
successfulAuth: this.metrics.successfulAuth,
totalRequests: this.metrics.totalRequests,
}
Identifier,
} from '../types/AuthTypes.js'
-import { truncateId } from '../../../../utils/index.js'
+import { isEmpty, truncateId } from '../../../../utils/index.js'
import { AuthorizationStatus } from '../types/AuthTypes.js'
/**
* @returns The first ACCEPTED result, or the first result with merged metadata
*/
function mergeAuthResults (results: AuthorizationResult[]): AuthorizationResult | undefined {
- if (results.length === 0) {
+ if (isEmpty(results)) {
return undefined
}
case IdentifierType.MOBILE_APP:
case IdentifierType.NO_AUTHORIZATION:
// OCPP 2.0 types - use IdToken max length
- return typedIdentifier.value.length > 0 && typedIdentifier.value.length <= MAX_IDTOKEN_LENGTH
+ return (
+ isNotEmptyString(typedIdentifier.value) &&
+ typedIdentifier.value.length <= MAX_IDTOKEN_LENGTH
+ )
case IdentifierType.ID_TAG:
- return typedIdentifier.value.length > 0 && typedIdentifier.value.length <= MAX_IDTAG_LENGTH
+ return (
+ isNotEmptyString(typedIdentifier.value) && typedIdentifier.value.length <= MAX_IDTAG_LENGTH
+ )
default:
return false
-import { logger } from '../../../../utils/index.js'
+import { isNotEmptyArray, logger } from '../../../../utils/index.js'
import { type AuthConfiguration, AuthenticationError, AuthErrorCode } from '../types/AuthTypes.js'
const moduleName = 'AuthConfigValidator'
if (hasCertificate) enabledMethods.push('certificate')
if (hasOffline) enabledMethods.push('offline')
- if (enabledMethods.length > 0) {
+ if (isNotEmptyArray(enabledMethods)) {
logger.debug(`${moduleName}: Enabled authentication methods: ${enabledMethods.join(', ')}`)
}
}
type UIServerConfiguration,
type UUIDv4,
} from '../../types/index.js'
-import { generateUUID, getErrorMessage, logger } from '../../utils/index.js'
+import { generateUUID, getErrorMessage, isNotEmptyArray, logger } from '../../utils/index.js'
import { AbstractUIServer } from './AbstractUIServer.js'
import {
mcpToolSchemas,
}
return s.version !== OCPPVersion.VERSION_20 && s.version !== OCPPVersion.VERSION_201
})
- if (mismatched.length > 0) {
+ if (isNotEmptyArray(mismatched)) {
const ids = mismatched.map(s => s.hashId).join(', ')
const versions = [...new Set(mismatched.map(s => s.version ?? 'unknown'))].join(', ')
return UIMCPServer.createToolErrorResponse(
}
return {
status: err != null ? ResponseStatus.FAILURE : ResponseStatus.SUCCESS,
- ...(succeededStationInfos.length > 0 && {
+ ...(isNotEmptyArray(succeededStationInfos) && {
hashIdsSucceeded: succeededStationInfos.map(stationInfo => stationInfo.hashId),
}),
- ...(failedStationInfos.length > 0 && {
+ ...(isNotEmptyArray(failedStationInfos) && {
hashIdsFailed: failedStationInfos.map(stationInfo => stationInfo.hashId),
}),
...(err != null && { errorMessage: err.message, errorStack: err.stack }),
} from './ConfigurationUtils.js'
import { Constants } from './Constants.js'
import { ensureError, handleFileException } from './ErrorUtils.js'
-import { has, isCFEnvironment, isNotEmptyString, mergeDeepRight, once } from './Utils.js'
+import {
+ convertToInt,
+ has,
+ isCFEnvironment,
+ isNotEmptyString,
+ mergeDeepRight,
+ once,
+} from './Utils.js'
type ConfigurationSectionType =
| LogConfiguration
if (isCFEnvironment()) {
delete uiServerConfiguration.options?.host
if (uiServerConfiguration.options != null) {
- uiServerConfiguration.options.port = Number.parseInt(env.PORT ?? '')
+ uiServerConfiguration.options.port = convertToInt(env.PORT ?? '')
}
}
return uiServerConfiguration
Configuration.configurationFileReloading = false
})
.catch((error: unknown) => {
- throw typeof error === 'string' ? new Error(error) : error
+ throw ensureError(error)
})
} else {
Configuration.configurationFileReloading = false
import { ConfigurationSection, type LogConfiguration } from '../types/index.js'
import { Configuration } from './Configuration.js'
-import { insertAt, isNotEmptyString } from './Utils.js'
+import { insertAt, isEmpty, isNotEmptyString } from './Utils.js'
let loggerInstance: undefined | WinstonLogger
level: logConfiguration.level,
silent:
logConfiguration.enabled === false ||
- logTransports.length === 0 ||
+ isEmpty(logTransports) ||
process.env.NODE_ENV === 'test',
transports: logTransports,
})
-import { isNotEmptyArray } from './Utils.js'
+import { isEmpty, isNotEmptyArray } from './Utils.js'
export const average = (dataSet: number[]): number => {
if (!isNotEmptyArray<number>(dataSet)) {
if (percentile < 0 || percentile > 100) {
throw new RangeError('Percentile is not between 0 and 100')
}
- if (dataSet.length === 0) {
+ if (isEmpty(dataSet)) {
return 0
}
const sortedDataSet = dataSet.slice().sort((a, b) => a - b)