| supervisionUrls | | [] | string \| string[] | string or strings array containing connection URIs to OCPP-J servers |
| supervisionUser | | undefined | string | basic HTTP authentication user to OCPP-J server |
| supervisionPassword | | undefined | string | basic HTTP authentication password to OCPP-J server |
-| supervisionUrlOcppConfiguration | true/false | false | boolean | allow supervision URL configuration via a vendor OCPP parameter key |
+| supervisionUrlOcppConfiguration | true/false | false | boolean | enable supervision URL configuration via a vendor OCPP parameter key |
| supervisionUrlOcppKey | | 'ConnectionUrl' | string | the vendor string that will be used as a vendor OCPP parameter key to set the supervision URL |
+| autoStart | true/false | true | boolean | enable automatic start of added charging station from template |
| ocppVersion | 1.6/2.0/2.0.1 | 1.6 | string | OCPP version |
| ocppProtocol | json | json | string | OCPP protocol |
-| ocppStrictCompliance | true/false | true | boolean | strict adherence to the OCPP version and protocol specifications with OCPP commands PDU validation against [OCA](https://www.openchargealliance.org/) JSON schemas |
+| ocppStrictCompliance | true/false | true | boolean | enable strict adherence to the OCPP version and protocol specifications with OCPP commands PDU validation against [OCA](https://www.openchargealliance.org/) JSON schemas |
| ocppPersistentConfiguration | true/false | true | boolean | enable persistent OCPP parameters storage by charging stations 'hashId'. The persistency is ensured by the charging stations configuration files in [dist/assets/configurations](dist/assets/configurations) |
| stationInfoPersistentConfiguration | true/false | true | boolean | enable persistent station information and specifications storage by charging stations 'hashId'. The persistency is ensured by the charging stations configuration files in [dist/assets/configurations](dist/assets/configurations) |
| automaticTransactionGeneratorPersistentConfiguration | true/false | true | boolean | enable persistent automatic transaction generator configuration storage by charging stations 'hashId'. The persistency is ensured by the charging stations configuration files in [dist/assets/configurations](dist/assets/configurations) |
import {
type ChargingStationData,
type ChargingStationWorkerData,
+ type ChargingStationWorkerEventError,
type ChargingStationWorkerMessage,
type ChargingStationWorkerMessageData,
ChargingStationWorkerMessageEvents,
interface TemplateChargingStations {
configured: number
+ added: number
started: number
lastIndex: number
}
return this.chargingStationsByTemplate.get(templateName)?.lastIndex ?? 0
}
+ private get numberOfAddedChargingStations (): number {
+ return [...this.chargingStationsByTemplate.values()].reduce(
+ (accumulator, value) => accumulator + value.added,
+ 0
+ )
+ }
+
private get numberOfStartedChargingStations (): number {
return [...this.chargingStationsByTemplate.values()].reduce(
(accumulator, value) => accumulator + value.started,
if (!this.started) {
if (!this.starting) {
this.starting = true
+ this.on(ChargingStationWorkerMessageEvents.added, this.workerEventAdded)
this.on(ChargingStationWorkerMessageEvents.started, this.workerEventStarted)
this.on(ChargingStationWorkerMessageEvents.stopped, this.workerEventStopped)
this.on(ChargingStationWorkerMessageEvents.updated, this.workerEventUpdated)
// )
try {
switch (msg.event) {
+ case ChargingStationWorkerMessageEvents.added:
+ this.emit(ChargingStationWorkerMessageEvents.added, msg.data as ChargingStationData)
+ break
case ChargingStationWorkerMessageEvents.started:
this.emit(ChargingStationWorkerMessageEvents.started, msg.data as ChargingStationData)
break
msg.data as Statistics
)
break
- case ChargingStationWorkerMessageEvents.startWorkerElementError:
+ case ChargingStationWorkerMessageEvents.workerElementError:
logger.error(
- `${this.logPrefix()} ${moduleName}.messageHandler: Error occurred while starting worker element:`,
+ `${this.logPrefix()} ${moduleName}.messageHandler: Error occurred while handling '${(msg.data as ChargingStationWorkerEventError).event}' event on worker:`,
msg.data
)
- this.emit(ChargingStationWorkerMessageEvents.startWorkerElementError, msg.data)
+ this.emit(ChargingStationWorkerMessageEvents.workerElementError, msg.data)
break
- case ChargingStationWorkerMessageEvents.startedWorkerElement:
+ case ChargingStationWorkerMessageEvents.addedWorkerElement:
break
default:
throw new BaseError(
}
}
+ private readonly workerEventAdded = (data: ChargingStationData): void => {
+ this.uiServer?.chargingStations.set(data.stationInfo.hashId, data)
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ ++this.chargingStationsByTemplate.get(data.stationInfo.templateName)!.added
+ logger.info(
+ `${this.logPrefix()} ${moduleName}.workerEventAdded: Charging station ${
+ data.stationInfo.chargingStationId
+ } (hashId: ${data.stationInfo.hashId}) added (${
+ this.numberOfAddedChargingStations
+ } added from ${this.numberOfConfiguredChargingStations} configured charging station(s))`
+ )
+ }
+
private readonly workerEventStarted = (data: ChargingStationData): void => {
this.uiServer?.chargingStations.set(data.stationInfo.hashId, data)
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
data.stationInfo.chargingStationId
} (hashId: ${data.stationInfo.hashId}) started (${
this.numberOfStartedChargingStations
- } started from ${this.numberOfConfiguredChargingStations} configured charging station(s))`
+ } started from ${this.numberOfAddedChargingStations} added charging station(s))`
)
}
data.stationInfo.chargingStationId
} (hashId: ${data.stationInfo.hashId}) stopped (${
this.numberOfStartedChargingStations
- } started from ${this.numberOfConfiguredChargingStations} configured charging station(s))`
+ } started from ${this.numberOfAddedChargingStations} added charging station(s))`
)
}
const templateName = parse(stationTemplateUrl.file).name
this.chargingStationsByTemplate.set(templateName, {
configured: stationTemplateUrl.numberOfStations,
+ added: 0,
started: 0,
lastIndex: 0
})
Configuration,
Constants,
DCElectricUtils,
+ buildAddedMessage,
buildChargingStationAutomaticTransactionGeneratorConfiguration,
buildConnectorsStatus,
buildEvsesStatus,
this.idTagsCache = IdTagsCache.getInstance()
this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this)
+ this.on(ChargingStationEvents.added, () => {
+ parentPort?.postMessage(buildAddedMessage(this))
+ })
this.on(ChargingStationEvents.started, () => {
parentPort?.postMessage(buildStartedMessage(this))
})
})
this.initialize()
+
+ this.stationInfo?.autoStart === true && this.start()
}
public get hasEvses (): boolean {
}
}
+ public add (): void {
+ this.emit(ChargingStationEvents.added)
+ }
+
public start (): void {
if (!this.started) {
if (!this.starting) {
}
const stationInfo = stationTemplateToStationInfo(stationTemplate)
stationInfo.hashId = getHashId(this.index, stationTemplate)
+ stationInfo.autoStart = stationTemplate.autoStart ?? true
stationInfo.templateName = parse(this.templateFile).name
stationInfo.chargingStationId = getChargingStationId(this.index, stationTemplate)
stationInfo.ocppVersion = stationTemplate.ocppVersion ?? OCPPVersion.VERSION_16
if (stationInfo.templateName == null) {
stationInfo.templateName = parse(this.templateFile).name
}
+ if (stationInfo.autoStart == null) {
+ stationInfo.autoStart = true
+ }
}
}
return stationInfo
import { ChargingStation } from './ChargingStation.js'
import { BaseError } from '../exception/index.js'
-import type { ChargingStationWorkerData } from '../types/index.js'
-import { Configuration } from '../utils/index.js'
+import type {
+ ChargingStationData,
+ ChargingStationWorkerData,
+ ChargingStationWorkerEventError,
+ ChargingStationWorkerMessage
+} from '../types/index.js'
+import { Configuration, buildChargingStationDataPayload } from '../utils/index.js'
import { type WorkerMessage, WorkerMessageEvents } from '../worker/index.js'
-/**
- * Adds and starts a charging station instance
- *
- * @param data - data sent to worker
- */
-const addChargingStation = (data?: ChargingStationWorkerData): void => {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- new ChargingStation(data!.index, data!.templateFile).start()
-}
-
-// eslint-disable-next-line @typescript-eslint/no-extraneous-class
-class ChargingStationWorker<Data extends ChargingStationWorkerData> {
- constructor () {
- // Add message listener to create and start charging station from the main thread
- parentPort?.on('message', (message: WorkerMessage<Data>) => {
- switch (message.event) {
- case WorkerMessageEvents.startWorkerElement:
- try {
- addChargingStation(message.data)
- parentPort?.postMessage({
- event: WorkerMessageEvents.startedWorkerElement
- })
- } catch (error) {
- parentPort?.postMessage({
- event: WorkerMessageEvents.startWorkerElementError,
- data: {
- name: (error as Error).name,
- message: (error as Error).message,
- stack: (error as Error).stack
- }
- })
- }
- break
- default:
- throw new BaseError(
- `Unknown worker event: '${message.event}' received with data: '${JSON.stringify(
- message.data,
- undefined,
- 2
- )}'`
- )
- }
- })
- }
-}
-
-export let chargingStationWorker:
-| ChargingStationWorker<ChargingStationWorkerData>
-| ThreadWorker<ChargingStationWorkerData>
+export let chargingStationWorker: object
if (Configuration.workerPoolInUse()) {
- chargingStationWorker = new ThreadWorker<ChargingStationWorkerData>(addChargingStation)
+ chargingStationWorker = new ThreadWorker<ChargingStationWorkerData>(
+ (data?: ChargingStationWorkerData): void => {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ new ChargingStation(data!.index, data!.templateFile).add()
+ }
+ )
} else {
+ // eslint-disable-next-line @typescript-eslint/no-extraneous-class
+ class ChargingStationWorker<Data extends ChargingStationWorkerData> {
+ constructor () {
+ parentPort?.on('message', (message: WorkerMessage<Data>) => {
+ switch (message.event) {
+ case WorkerMessageEvents.addWorkerElement:
+ try {
+ const chargingStation = new ChargingStation(
+ message.data.index,
+ message.data.templateFile
+ )
+ chargingStation.add()
+ parentPort?.postMessage({
+ event: WorkerMessageEvents.addedWorkerElement,
+ data: buildChargingStationDataPayload(chargingStation)
+ } satisfies ChargingStationWorkerMessage<ChargingStationData>)
+ } catch (error) {
+ parentPort?.postMessage({
+ event: WorkerMessageEvents.workerElementError,
+ data: {
+ event: WorkerMessageEvents.addWorkerElement,
+ name: (error as Error).name,
+ message: (error as Error).message,
+ stack: (error as Error).stack
+ }
+ } satisfies ChargingStationWorkerMessage<ChargingStationWorkerEventError>)
+ }
+ break
+ default:
+ throw new BaseError(
+ `Unknown worker event: '${message.event}' received with data: '${JSON.stringify(
+ message.data,
+ undefined,
+ 2
+ )}'`
+ )
+ }
+ })
+ }
+ }
chargingStationWorker = new ChargingStationWorker<ChargingStationWorkerData>()
}
export enum ChargingStationEvents {
+ added = 'added',
started = 'started',
stopped = 'stopped',
updated = 'updated',
supervisionUrlOcppKey?: string
supervisionUser?: string
supervisionPassword?: string
+ autoStart?: boolean
ocppVersion?: OCPPVersion
ocppProtocol?: OCPPProtocol
ocppStrictCompliance?: boolean
| ChargingStationEvents
| ChargingStationMessageEvents
-export type ChargingStationWorkerMessageData = ChargingStationData | Statistics
+export interface ChargingStationWorkerEventError extends WorkerData {
+ event: WorkerMessageEvents
+ name: string
+ message: string
+ stack?: string
+}
+
+export type ChargingStationWorkerMessageData =
+ | ChargingStationData
+ | Statistics
+ | ChargingStationWorkerEventError
export type ChargingStationWorkerMessage<T extends ChargingStationWorkerMessageData> = Omit<
WorkerMessage<T>,
export {
type ChargingStationData,
type ChargingStationWorkerData,
+ type ChargingStationWorkerEventError,
type ChargingStationWorkerMessage,
type ChargingStationWorkerMessageData,
ChargingStationWorkerMessageEvents,
type Statistics
} from '../types/index.js'
+export const buildAddedMessage = (
+ chargingStation: ChargingStation
+): ChargingStationWorkerMessage<ChargingStationData> => {
+ return {
+ event: ChargingStationWorkerMessageEvents.added,
+ data: buildChargingStationDataPayload(chargingStation)
+ }
+}
+
export const buildStartedMessage = (
chargingStation: ChargingStation
): ChargingStationWorkerMessage<ChargingStationData> => {
}
}
-const buildChargingStationDataPayload = (chargingStation: ChargingStation): ChargingStationData => {
+export const buildChargingStationDataPayload = (
+ chargingStation: ChargingStation
+): ChargingStationData => {
return {
started: chargingStation.started,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
export { Constants } from './Constants.js'
export {
handleFileException,
+ handleSendMessageError,
handleUncaughtException,
handleUnhandledRejection,
- handleSendMessageError,
setDefaultErrorParams
} from './ErrorUtils.js'
export { watchJsonFile } from './FileUtils.js'
export {
+ buildAddedMessage,
+ buildChargingStationDataPayload,
buildPerformanceStatisticsMessage,
- buildUpdatedMessage,
buildStartedMessage,
- buildStoppedMessage
+ buildStoppedMessage,
+ buildUpdatedMessage
} from './MessageChannelUtils.js'
export {
- isAsyncFunction,
JSONStringifyWithMapSupport,
clone,
convertToBoolean,
getRandomInteger,
getWebSocketCloseEventStatusString,
isArraySorted,
+ isAsyncFunction,
isEmptyArray,
isEmptyObject,
isEmptyString,
}
const workerSetElement = await this.getWorkerSetElement()
workerSetElement.worker.postMessage({
- event: WorkerMessageEvents.startWorkerElement,
+ event: WorkerMessageEvents.addWorkerElement,
data: elementData
})
++workerSetElement.numberOfWorkerElements
})
worker.on('message', this.workerOptions.poolOptions?.messageHandler ?? EMPTY_FUNCTION)
worker.on('message', (message: WorkerMessage<WorkerData>) => {
- if (message.event === WorkerMessageEvents.startedWorkerElement) {
- this.emitter?.emit(WorkerSetEvents.elementStarted, this.info)
- } else if (message.event === WorkerMessageEvents.startWorkerElementError) {
+ if (message.event === WorkerMessageEvents.addedWorkerElement) {
+ this.emitter?.emit(WorkerSetEvents.elementAdded, this.info)
+ } else if (message.event === WorkerMessageEvents.workerElementError) {
this.emitter?.emit(WorkerSetEvents.elementError, message.data)
}
})
export enum WorkerProcessType {
workerSet = 'workerSet',
+ fixedPool = 'fixedPool',
/** @experimental */
- dynamicPool = 'dynamicPool',
- fixedPool = 'fixedPool'
+ dynamicPool = 'dynamicPool'
}
export interface SetInfo {
started = 'started',
stopped = 'stopped',
error = 'error',
- elementStarted = 'elementStarted',
+ elementAdded = 'elementAdded',
elementError = 'elementError'
}
}
export enum WorkerMessageEvents {
- startWorkerElement = 'startWorkerElement',
- startWorkerElementError = 'startWorkerElementError',
- startedWorkerElement = 'startedWorkerElement'
+ addWorkerElement = 'addWorkerElement',
+ addedWorkerElement = 'addedWorkerElement',
+ workerElementError = 'workerElementError'
}