return Constants.DEFAULT_EV_CONNECTION_TIMEOUT
}
+ /**
+ * Resolves the first connector ID for a given EVSE ID.
+ * @param evseId - The EVSE ID
+ * @returns The connector ID or undefined if not found
+ */
+ public getConnectorIdByEvseId (evseId: number): number | undefined {
+ return this.iterateConnectors().find(({ evseId: id }) => id === evseId)?.connectorId
+ }
+
/**
* Resolves the connector ID for a given transaction ID.
* @param transactionId - The transaction ID to resolve
}
public getConnectorStatus (connectorId: number): ConnectorStatus | undefined {
- if (this.hasEvses) {
- for (const evseStatus of this.evses.values()) {
- if (evseStatus.connectors.has(connectorId)) {
- return evseStatus.connectors.get(connectorId)
- }
- }
- return undefined
- }
- return this.connectors.get(connectorId)
+ return this.iterateConnectors().find(({ connectorId: id }) => id === connectorId)
+ ?.connectorStatus
}
/**
* @returns The EVSE ID or undefined if not found
*/
public getEvseIdByConnectorId (connectorId: number): number | undefined {
- if (!this.hasEvses) {
- return undefined
- }
- for (const [evseId, evseStatus] of this.evses) {
- if (evseStatus.connectors.has(connectorId)) {
- return evseId
- }
- }
- return undefined
+ return this.iterateConnectors().find(({ connectorId: id }) => id === connectorId)?.evseId
}
/**
}
public getNumberOfConnectors (): number {
- if (this.hasEvses) {
- let numberOfConnectors = 0
- for (const [evseId, evseStatus] of this.evses) {
- if (evseId > 0) {
- numberOfConnectors += evseStatus.connectors.size
- }
- }
- return numberOfConnectors
- }
- return this.connectors.has(0) ? this.connectors.size - 1 : this.connectors.size
+ return this.iterateConnectors(true).reduce(count => count + 1, 0)
}
public getNumberOfEvses (): number {
}
public hasConnector (connectorId: number): boolean {
- if (this.hasEvses) {
- for (const evseStatus of this.evses.values()) {
- if (evseStatus.connectors.has(connectorId)) {
- return true
- }
- }
- return false
- }
- return this.connectors.has(connectorId)
+ return this.iterateConnectors().some(({ connectorId: id }) => id === connectorId)
}
public hasEvse (evseId: number): boolean {
}
private getNumberOfReservationsOnConnectorZero (): number {
- if (
- (this.hasEvses && this.evses.get(0)?.connectors.get(0)?.reservation != null) ||
- (!this.hasEvses && this.connectors.get(0)?.reservation != null)
- ) {
+ if (this.getConnectorStatus(0)?.reservation != null) {
return 1
}
return 0
private async handleMeterValues (
requestPayload?: BroadcastChannelRequestPayload
): Promise<MeterValuesResponse> {
- const connectorId = requestPayload?.connectorId
+ const payloadEvseId = (requestPayload as undefined | { evseId?: number })?.evseId
+ const connectorId =
+ requestPayload?.connectorId ??
+ (payloadEvseId != null
+ ? this.chargingStation.getConnectorIdByEvseId(payloadEvseId)
+ : undefined)
if (connectorId == null) {
throw new BaseError(
- `${this.chargingStation.logPrefix()} ${moduleName}.handleMeterValues: Missing connectorId in request payload`
+ `${this.chargingStation.logPrefix()} ${moduleName}.handleMeterValues: Missing connectorId or evseId in request payload`
)
}
- switch (this.chargingStation.stationInfo?.ocppVersion) {
- case OCPPVersion.VERSION_16: {
- const configuredMeterValueSampleInterval = getConfigurationKey(
- this.chargingStation,
- StandardParametersKey.MeterValueSampleInterval
- )
- const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId
- return await this.chargingStation.ocppRequestService.requestHandler<
- MeterValuesRequest,
- MeterValuesResponse
- >(
- this.chargingStation,
- RequestCommand.METER_VALUES,
- {
- meterValue: [
- buildMeterValue(
- this.chargingStation,
- convertToInt(transactionId),
- configuredMeterValueSampleInterval != null
- ? secondsToMilliseconds(convertToInt(configuredMeterValueSampleInterval.value))
- : Constants.DEFAULT_METER_VALUES_INTERVAL
- ),
- ],
- ...requestPayload,
- } as MeterValuesRequest,
- this.requestParams
- )
- }
- case OCPPVersion.VERSION_20:
- case OCPPVersion.VERSION_201: {
- const alignedDataInterval = OCPP20ServiceUtils.getAlignedDataInterval(this.chargingStation)
- const evseId = this.chargingStation.getEvseIdByConnectorId(connectorId)
- const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId
- return await this.chargingStation.ocppRequestService.requestHandler<
- MeterValuesRequest,
- MeterValuesResponse
- >(
- this.chargingStation,
- RequestCommand.METER_VALUES,
- {
- evseId,
- meterValue: [
- buildMeterValue(
- this.chargingStation,
-
- transactionId,
- alignedDataInterval
- ),
- ],
- ...requestPayload,
- } as MeterValuesRequest,
- this.requestParams
- )
- }
- default:
- throw new BaseError(
- `${this.chargingStation.logPrefix()} ${moduleName}.handleMeterValues: Unsupported OCPP version for MeterValues`
- )
- }
+ const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId
+ const isOcpp2 =
+ this.chargingStation.stationInfo?.ocppVersion === OCPPVersion.VERSION_20 ||
+ this.chargingStation.stationInfo?.ocppVersion === OCPPVersion.VERSION_201
+ const interval = isOcpp2
+ ? OCPP20ServiceUtils.getAlignedDataInterval(this.chargingStation)
+ : (() => {
+ const key = getConfigurationKey(
+ this.chargingStation,
+ StandardParametersKey.MeterValueSampleInterval
+ )
+ return key != null
+ ? secondsToMilliseconds(convertToInt(key.value))
+ : Constants.DEFAULT_METER_VALUES_INTERVAL
+ })()
+ return await this.chargingStation.ocppRequestService.requestHandler<
+ MeterValuesRequest,
+ MeterValuesResponse
+ >(
+ this.chargingStation,
+ RequestCommand.METER_VALUES,
+ {
+ ...(isOcpp2
+ ? {
+ evseId: payloadEvseId ?? this.chargingStation.getEvseIdByConnectorId(connectorId),
+ }
+ : { connectorId }),
+ meterValue: [buildMeterValue(this.chargingStation, transactionId, interval)],
+ ...requestPayload,
+ } as MeterValuesRequest,
+ this.requestParams
+ )
}
private async handleStopTransaction (
commandPayload
)
}
- const connectorId: number | undefined = evse.connectors.keys().next().value
+ const connectorId = chargingStation.getConnectorIdByEvseId(resolvedEvseId)
const connectorStatus =
connectorId != null ? chargingStation.getConnectorStatus(connectorId) : null
connectorsCount: 2,
stationInfo: { ocppVersion: OCPPVersion.VERSION_201 },
})
- const firstEvse = chargingStation.getEvseStatus(1)
- const firstConnector = firstEvse?.connectors.values().next().value
+ const firstConnectorId = chargingStation.getConnectorIdByEvseId(1)
+ const firstConnector =
+ firstConnectorId != null ? chargingStation.getConnectorStatus(firstConnectorId) : undefined
if (firstConnector != null) {
firstConnector.reservation = createTestReservation(false)
}
connectorsCount: 2,
stationInfo: { ocppVersion: OCPPVersion.VERSION_201 },
})
- const firstEvse = chargingStation.getEvseStatus(1)
- const firstConnector = firstEvse?.connectors.values().next().value
+ const firstConnectorId = chargingStation.getConnectorIdByEvseId(1)
+ const firstConnector =
+ firstConnectorId != null ? chargingStation.getConnectorStatus(firstConnectorId) : undefined
if (firstConnector != null) {
firstConnector.reservation = createTestReservation(true)
}
EvseEntry,
EvseStatus,
Reservation,
+ ReservationKey,
StopTransactionReason,
} from '../../../src/types/index.js'
}
}
- // Clear EVSE connector transaction state and timers
- for (const { evseStatus } of station.iterateEvses()) {
- for (const connectorStatus of evseStatus.connectors.values()) {
- if (connectorStatus.transactionUpdatedMeterValuesSetInterval != null) {
- clearInterval(connectorStatus.transactionUpdatedMeterValuesSetInterval)
- connectorStatus.transactionUpdatedMeterValuesSetInterval = undefined
- }
- if (connectorStatus.transactionEndedMeterValuesSetInterval != null) {
- clearInterval(connectorStatus.transactionEndedMeterValuesSetInterval)
- connectorStatus.transactionEndedMeterValuesSetInterval = undefined
- }
- }
- }
-
// Clear requests map
station.requests.clear()
getConnectionTimeout (): number {
return connectionTimeout
},
+ getConnectorIdByEvseId (evseId: number): number | undefined {
+ return this.iterateConnectors().find(({ evseId: id }) => id === evseId)?.connectorId
+ },
getConnectorIdByTransactionId (transactionId: number | string | undefined): number | undefined {
if (transactionId == null) {
return undefined
- } else if (useEvses) {
- for (const evseStatus of evses.values()) {
- for (const [connectorId, connectorStatus] of evseStatus.connectors) {
- if (connectorStatus.transactionId === transactionId) {
- return connectorId
- }
- }
- }
- } else {
- for (const connectorId of connectors.keys()) {
- if (this.getConnectorStatus(connectorId)?.transactionId === transactionId) {
- return connectorId
- }
- }
}
- return undefined
+ return this.iterateConnectors().find(
+ ({ connectorStatus }) => connectorStatus.transactionId === transactionId
+ )?.connectorId
},
getConnectorMaximumAvailablePower (_connectorId: number): number {
return stationInfoOverrides?.maximumPower ?? 22000
},
- // Methods
getConnectorStatus (connectorId: number): ConnectorStatus | undefined {
- if (useEvses) {
- for (const evseStatus of evses.values()) {
- if (evseStatus.connectors.has(connectorId)) {
- return evseStatus.connectors.get(connectorId)
- }
- }
- return undefined
- }
- return connectors.get(connectorId)
+ return this.iterateConnectors().find(({ connectorId: id }) => id === connectorId)
+ ?.connectorStatus
},
getEnergyActiveImportRegisterByConnectorId (connectorId: number, rounded = false): number {
const connectorStatus = this.getConnectorStatus(connectorId)
return this.getEnergyActiveImportRegisterByConnectorId(connectorId, rounded)
},
getEvseIdByConnectorId (connectorId: number): number | undefined {
- if (!useEvses) {
- return undefined
- }
- for (const [evseId, evseStatus] of evses) {
- if (evseStatus.connectors.has(connectorId)) {
- return evseId
- }
- }
- return undefined
+ return this.iterateConnectors().find(({ connectorId: id }) => id === connectorId)?.evseId
},
getEvseIdByTransactionId (transactionId: number | string | undefined): number | undefined {
if (transactionId == null) {
return undefined
- } else if (useEvses) {
- for (const [evseId, evseStatus] of evses) {
- for (const connectorStatus of evseStatus.connectors.values()) {
- if (connectorStatus.transactionId === transactionId) {
- return evseId
- }
- }
- }
}
- return undefined
+ return this.iterateConnectors().find(
+ ({ connectorStatus }) => connectorStatus.transactionId === transactionId
+ )?.evseId
},
getEvseStatus (evseId: number): EvseStatus | undefined {
return evses.get(evseId)
return false // Default to false in mock
},
getNumberOfConnectors (): number {
- if (useEvses) {
- let numberOfConnectors = 0
- for (const [evseId, evseStatus] of evses) {
- if (evseId > 0) {
- numberOfConnectors += evseStatus.connectors.size
- }
- }
- return numberOfConnectors
- }
- return connectors.has(0) ? connectors.size - 1 : connectors.size
+ return this.iterateConnectors(true).reduce(count => count + 1, 0)
},
getNumberOfEvses (): number {
return evses.has(0) ? evses.size - 1 : evses.size
},
getNumberOfRunningTransactions (): number {
- let numberOfRunningTransactions = 0
- if (useEvses) {
- for (const [evseId, evseStatus] of evses) {
- if (evseId === 0) {
- continue
- }
- for (const connectorStatus of evseStatus.connectors.values()) {
- if (connectorStatus.transactionStarted === true) {
- ++numberOfRunningTransactions
- }
- }
- }
- } else {
- for (const connectorId of connectors.keys()) {
- if (
- connectorId > 0 &&
- this.getConnectorStatus(connectorId)?.transactionStarted === true
- ) {
- ++numberOfRunningTransactions
- }
- }
- }
- return numberOfRunningTransactions
+ return this.iterateConnectors(true).reduce(
+ (count, { connectorStatus }) =>
+ connectorStatus.transactionStarted === true ? count + 1 : count,
+ 0
+ )
},
- getReservationBy (filterKey: string, value: unknown): Record<string, unknown> | undefined {
- if (useEvses) {
- for (const evseStatus of evses.values()) {
- for (const connectorStatus of evseStatus.connectors.values()) {
- if (connectorStatus.reservation?.[filterKey] === value) {
- return connectorStatus.reservation
- }
- }
- }
- } else {
- for (const connectorStatus of connectors.values()) {
- if (connectorStatus.reservation?.[filterKey] === value) {
- return connectorStatus.reservation
- }
- }
- }
- return undefined
+ getReservationBy (filterKey: ReservationKey, value: number | string): Reservation | undefined {
+ return this.iterateConnectors().find(
+ ({ connectorStatus }) => connectorStatus.reservation?.[filterKey] === value
+ )?.connectorStatus.reservation
},
getTransactionIdTag (transactionId: number): string | undefined {
- if (useEvses) {
- for (const evseStatus of evses.values()) {
- for (const connectorStatus of evseStatus.connectors.values()) {
- if (connectorStatus.transactionId === transactionId) {
- return connectorStatus.transactionIdTag
- }
- }
- }
- } else {
- for (const connectorId of connectors.keys()) {
- if (this.getConnectorStatus(connectorId)?.transactionId === transactionId) {
- return this.getConnectorStatus(connectorId)?.transactionIdTag
- }
- }
- }
- return undefined
+ return this.iterateConnectors().find(
+ ({ connectorStatus }) => connectorStatus.transactionId === transactionId
+ )?.connectorStatus.transactionIdTag
},
getWebSocketPingInterval (): number {
return websocketPingInterval
},
hasConnector (connectorId: number): boolean {
- if (useEvses) {
- for (const evseStatus of evses.values()) {
- if (evseStatus.connectors.has(connectorId)) {
- return true
- }
- }
- return false
- }
- return connectors.has(connectorId)
+ return this.iterateConnectors().some(({ connectorId: id }) => id === connectorId)
},
hasEvse (evseId: number): boolean {
resetConnectorStatus(connectorStatus, connectorId === 0)
}
- // Reset EVSE connector statuses
+ // Reset EVSE availability
for (const { evseStatus } of station.iterateEvses()) {
evseStatus.availability = AvailabilityType.Operative
- for (const connectorStatus of evseStatus.connectors.values()) {
- resetConnectorStatus(connectorStatus, false)
- }
}
// Clear requests
import { afterEach, beforeEach, describe, it, mock } from 'node:test'
import type {
- EvseStatus,
OCPP20ResetRequest,
OCPP20ResetResponse,
Reservation,
}
// Assign reservation to first connector
- const evse: EvseStatus | undefined = station.getEvseStatus(1)
- if (evse) {
- const connectorId = [...evse.connectors.keys()][0]
- const connectorStatus = evse.connectors.get(connectorId)
- if (connectorStatus) {
- connectorStatus.reservation = mockReservation as Reservation
- }
+ const connectorId = station.getConnectorIdByEvseId(1)
+ const connectorStatus =
+ connectorId != null ? station.getConnectorStatus(connectorId) : undefined
+ if (connectorStatus != null) {
+ connectorStatus.reservation = mockReservation as Reservation
}
const resetRequest: OCPP20ResetRequest = {
}
// Assign expired reservation to first connector
- const evse: EvseStatus | undefined = station.getEvseStatus(1)
- if (evse) {
- const connectorId = [...evse.connectors.keys()][0]
- const connectorStatus = evse.connectors.get(connectorId)
- if (connectorStatus) {
- connectorStatus.reservation = mockReservation as Reservation
- }
+ const connectorId = station.getConnectorIdByEvseId(1)
+ const connectorStatus =
+ connectorId != null ? station.getConnectorStatus(connectorId) : undefined
+ if (connectorStatus != null) {
+ connectorStatus.reservation = mockReservation as Reservation
}
const resetRequest: OCPP20ResetRequest = {
id: 1,
idTag: 'test-tag',
}
- const evse: EvseStatus | undefined = station.getEvseStatus(1)
- if (evse) {
- const connectorId = [...evse.connectors.keys()][0]
- const connectorStatus = evse.connectors.get(connectorId)
- if (connectorStatus) {
- connectorStatus.reservation = mockReservation as Reservation
- }
+ const connectorId = station.getConnectorIdByEvseId(1)
+ const connectorStatus =
+ connectorId != null ? station.getConnectorStatus(connectorId) : undefined
+ if (connectorStatus != null) {
+ connectorStatus.reservation = mockReservation as Reservation
}
const resetRequest: OCPP20ResetRequest = {
await it('should return OngoingAuthorizedTransaction when specified connector has active transaction', async () => {
const { mockStation } = createUnlockConnectorStation()
- const evseStatus = mockStation.getEvseStatus(1)
- const connectorStatus = evseStatus?.connectors.get(1)
+ const connectorStatus = mockStation.getConnectorStatus(1)
if (connectorStatus != null) {
connectorStatus.transactionId = 'tx-001'
}
})
const multiConnectorStation = station as MockChargingStation
- const evseStatus = multiConnectorStation.getEvseStatus(1)
- const connector2 = evseStatus?.connectors.get(2)
+ const connector2 = multiConnectorStation.getConnectorStatus(2)
if (connector2 != null) {
connector2.transactionId = 'tx-other'
}
})
// Set an active transaction on EVSE 1's connector
- const evse1 = evseStation.getEvseStatus(1)
- if (evse1 != null) {
- const firstConnector = evse1.connectors.values().next().value
- if (firstConnector != null) {
- firstConnector.transactionId = 'tx-active-001'
- }
+ const evse1ConnectorId = evseStation.getConnectorIdByEvseId(1)
+ const firstConnector =
+ evse1ConnectorId != null ? evseStation.getConnectorStatus(evse1ConnectorId) : undefined
+ if (firstConnector != null) {
+ firstConnector.transactionId = 'tx-active-001'
}
const request: OCPP20UpdateFirmwareRequest = {
])
// Set active transactions on EVSE 1 and EVSE 2
- const evse1 = trackingStation.getEvseStatus(1)
- const evse2 = trackingStation.getEvseStatus(2)
- const evse1Connector = evse1?.connectors.values().next().value
- const evse2Connector = evse2?.connectors.values().next().value
+ const evse1ConnectorId = trackingStation.getConnectorIdByEvseId(1)
+ const evse2ConnectorId = trackingStation.getConnectorIdByEvseId(2)
+ const evse1Connector =
+ evse1ConnectorId != null
+ ? trackingStation.getConnectorStatus(evse1ConnectorId)
+ : undefined
+ const evse2Connector =
+ evse2ConnectorId != null
+ ? trackingStation.getConnectorStatus(evse2ConnectorId)
+ : undefined
if (evse1Connector != null) evse1Connector.transactionId = 'tx-fw-001'
if (evse2Connector != null) evse2Connector.transactionId = 'tx-fw-002'
* @param chargingStation Charging station instance whose connector state should be reset.
*/
export function resetConnectorTransactionState (chargingStation: ChargingStation): void {
- if (chargingStation.hasEvses) {
- for (const { evseStatus } of chargingStation.iterateEvses()) {
- for (const connectorStatus of evseStatus.connectors.values()) {
- connectorStatus.transactionStarted = false
- connectorStatus.transactionId = undefined
- connectorStatus.transactionIdTag = undefined
- connectorStatus.transactionGroupIdToken = undefined
- connectorStatus.transactionStart = undefined
- connectorStatus.transactionEnergyActiveImportRegisterValue = 0
- connectorStatus.remoteStartId = undefined
- connectorStatus.status = ConnectorStatusEnum.Available
- connectorStatus.chargingProfiles = []
- }
- }
- } else {
- for (const { connectorStatus } of chargingStation.iterateConnectors(true)) {
- connectorStatus.transactionStarted = false
- connectorStatus.transactionId = undefined
- connectorStatus.transactionIdTag = undefined
- connectorStatus.transactionGroupIdToken = undefined
- connectorStatus.transactionStart = undefined
- connectorStatus.transactionEnergyActiveImportRegisterValue = 0
- connectorStatus.remoteStartId = undefined
- connectorStatus.status = ConnectorStatusEnum.Available
- connectorStatus.chargingProfiles = []
- }
+ for (const { connectorStatus } of chargingStation.iterateConnectors(true)) {
+ connectorStatus.transactionStarted = false
+ connectorStatus.transactionId = undefined
+ connectorStatus.transactionIdTag = undefined
+ connectorStatus.transactionGroupIdToken = undefined
+ connectorStatus.transactionStart = undefined
+ connectorStatus.transactionEnergyActiveImportRegisterValue = 0
+ connectorStatus.remoteStartId = undefined
+ connectorStatus.status = ConnectorStatusEnum.Available
+ connectorStatus.chargingProfiles = []
}
}