- Response:
`PDU`: {
`status`: 'success' | 'failure'
+ `hashIdsSucceeded`: charging station unique identifier strings array (optional),
+ `hashIdsFailed`: charging station unique identifier strings array (optional)
}
###### Delete Charging Stations
import { type Storage, StorageFactory } from '../performance/index.js'
import {
type ChargingStationData,
+ type ChargingStationInfo,
type ChargingStationOptions,
type ChargingStationWorkerData,
type ChargingStationWorkerEventError,
export class Bootstrap extends EventEmitter {
private static instance: Bootstrap | null = null
- private workerImplementation?: WorkerAbstract<ChargingStationWorkerData>
+ private workerImplementation?: WorkerAbstract<ChargingStationWorkerData, ChargingStationInfo>
private readonly uiServer: AbstractUIServer
private storage?: Storage
private readonly templateStatistics: Map<string, TemplateStatistics>
: 1
break
}
- this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(
+ this.workerImplementation = WorkerFactory.getWorkerImplementation<
+ ChargingStationWorkerData,
+ ChargingStationInfo
+ >(
join(
dirname(fileURLToPath(import.meta.url)),
`ChargingStationWorker${extname(fileURLToPath(import.meta.url))}`
index: number,
templateFile: string,
options?: ChargingStationOptions
- ): Promise<void> {
+ ): Promise<ChargingStationInfo | undefined> {
if (!this.started && !this.starting) {
throw new BaseError(
'Cannot add charging station while the charging stations simulator is not started'
)
}
- await this.workerImplementation?.addElement({
+ const stationInfo = await this.workerImplementation?.addElement({
index,
templateFile: join(
dirname(fileURLToPath(import.meta.url)),
const templateStatistics = this.templateStatistics.get(buildTemplateName(templateFile))!
++templateStatistics.added
templateStatistics.indexes.add(index)
+ return stationInfo
}
private gracefulShutdown (): void {
import { BaseError } from '../exception/index.js'
import type {
- ChargingStationData,
+ ChargingStationInfo,
ChargingStationWorkerData,
ChargingStationWorkerEventError,
ChargingStationWorkerMessage
} from '../types/index.js'
-import { buildChargingStationDataPayload, Configuration } from '../utils/index.js'
+import { Configuration } from '../utils/index.js'
import { type WorkerMessage, WorkerMessageEvents } from '../worker/index.js'
import { ChargingStation } from './ChargingStation.js'
export let chargingStationWorker: object
if (Configuration.workerPoolInUse()) {
- chargingStationWorker = new ThreadWorker<ChargingStationWorkerData>(
- (data?: ChargingStationWorkerData): void => {
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-new
- new ChargingStation(data!.index, data!.templateFile, data!.options)
- }
- )
+ chargingStationWorker = new ThreadWorker<
+ ChargingStationWorkerData,
+ ChargingStationInfo | undefined
+ >((data?: ChargingStationWorkerData): ChargingStationInfo | undefined => {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion, no-new
+ return new ChargingStation(data!.index, data!.templateFile, data!.options).stationInfo
+ })
} else {
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
class ChargingStationWorker<Data extends ChargingStationWorkerData> {
)
parentPort?.postMessage({
event: WorkerMessageEvents.addedWorkerElement,
- data: buildChargingStationDataPayload(chargingStation)
- } satisfies ChargingStationWorkerMessage<ChargingStationData>)
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ data: chargingStation.stationInfo!
+ } satisfies ChargingStationWorkerMessage<ChargingStationInfo>)
} catch (error) {
parentPort?.postMessage({
event: WorkerMessageEvents.workerElementError,
data: {
- event: WorkerMessageEvents.addWorkerElement,
+ event: message.event,
name: (error as Error).name,
message: (error as Error).message,
stack: (error as Error).stack
import {
BroadcastChannelProcedureName,
type BroadcastChannelRequestPayload,
+ type ChargingStationInfo,
type ChargingStationOptions,
ConfigurationSection,
type JsonObject,
errorMessage: `Template '${template}' not found`
} satisfies ResponsePayload
}
+ const stationInfos: ChargingStationInfo[] = []
for (let i = 0; i < numberOfStations; i++) {
+ let stationInfo: ChargingStationInfo | undefined
try {
- await Bootstrap.getInstance().addChargingStation(
+ stationInfo = await Bootstrap.getInstance().addChargingStation(
Bootstrap.getInstance().getLastIndex(template) + 1,
`${template}.json`,
options
)
+ if (stationInfo != null) {
+ stationInfos.push(stationInfo)
+ }
} catch (error) {
return {
status: ResponseStatus.FAILURE,
+ ...(stationInfo?.hashId != null && { hashIdsFailed: [stationInfo.hashId] }),
errorMessage: (error as Error).message,
errorStack: (error as Error).stack
} satisfies ResponsePayload
}
}
return {
- status: ResponseStatus.SUCCESS
+ status: ResponseStatus.SUCCESS,
+ hashIdsSucceeded: stationInfos.map(stationInfo => stationInfo.hashId)
}
}
}
export type ChargingStationWorkerMessageData =
+ | ChargingStationInfo
| ChargingStationData
| Statistics
| ChargingStationWorkerEventError
import type { SetInfo, WorkerData, WorkerOptions } from './WorkerTypes.js'
-export abstract class WorkerAbstract<T extends WorkerData> {
+export abstract class WorkerAbstract<D extends WorkerData, R extends WorkerData> {
protected readonly workerScript: string
protected readonly workerOptions: WorkerOptions
public abstract readonly info: PoolInfo | SetInfo
*
* @param elementData -
*/
- public abstract addElement (elementData: T): Promise<void>
+ public abstract addElement (elementData: D): Promise<R>
}
import type { WorkerData, WorkerOptions } from './WorkerTypes.js'
import { randomizeDelay, sleep } from './WorkerUtils.js'
-export class WorkerDynamicPool extends WorkerAbstract<WorkerData> {
- private readonly pool: DynamicThreadPool<WorkerData>
+export class WorkerDynamicPool<D extends WorkerData, R extends WorkerData> extends WorkerAbstract<
+D,
+R
+> {
+ private readonly pool: DynamicThreadPool<D, R>
/**
* Creates a new `WorkerDynamicPool`.
*/
constructor (workerScript: string, workerOptions: WorkerOptions) {
super(workerScript, workerOptions)
- this.pool = new DynamicThreadPool<WorkerData>(
+ this.pool = new DynamicThreadPool<D, R>(
this.workerOptions.poolMinSize,
this.workerOptions.poolMaxSize,
this.workerScript,
}
/** @inheritDoc */
- public async addElement (elementData: WorkerData): Promise<void> {
- await this.pool.execute(elementData)
+ public async addElement (elementData: D): Promise<R> {
+ const response = await this.pool.execute(elementData)
// Start element sequentially to optimize memory at startup
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.workerOptions.elementAddDelay! > 0 &&
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
(await sleep(randomizeDelay(this.workerOptions.elementAddDelay!)))
+ return response
}
}
// This is intentional
}
- public static getWorkerImplementation<T extends WorkerData>(
+ public static getWorkerImplementation<D extends WorkerData, R extends WorkerData>(
workerScript: string,
workerProcessType: WorkerProcessType,
workerOptions?: WorkerOptions
- ): WorkerAbstract<T> | undefined {
+ ): WorkerAbstract<D, R> {
if (!isMainThread) {
throw new Error('Cannot get a worker implementation outside the main thread')
}
workerOptions = mergeDeepRight<WorkerOptions>(DEFAULT_WORKER_OPTIONS, workerOptions ?? {})
- let workerImplementation: WorkerAbstract<T>
switch (workerProcessType) {
case WorkerProcessType.workerSet:
- workerImplementation = new WorkerSet(workerScript, workerOptions)
- break
+ return new WorkerSet<D, R>(workerScript, workerOptions)
case WorkerProcessType.fixedPool:
- workerImplementation = new WorkerFixedPool(workerScript, workerOptions)
- break
+ return new WorkerFixedPool<D, R>(workerScript, workerOptions)
case WorkerProcessType.dynamicPool:
- workerImplementation = new WorkerDynamicPool(workerScript, workerOptions)
- break
+ return new WorkerDynamicPool<D, R>(workerScript, workerOptions)
default:
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new Error(`Worker implementation type '${workerProcessType}' not found`)
}
- return workerImplementation
}
}
import type { WorkerData, WorkerOptions } from './WorkerTypes.js'
import { randomizeDelay, sleep } from './WorkerUtils.js'
-export class WorkerFixedPool extends WorkerAbstract<WorkerData> {
- private readonly pool: FixedThreadPool<WorkerData>
+export class WorkerFixedPool<D extends WorkerData, R extends WorkerData> extends WorkerAbstract<
+D,
+R
+> {
+ private readonly pool: FixedThreadPool<D, R>
/**
* Creates a new `WorkerFixedPool`.
*/
constructor (workerScript: string, workerOptions: WorkerOptions) {
super(workerScript, workerOptions)
- this.pool = new FixedThreadPool(
+ this.pool = new FixedThreadPool<D, R>(
this.workerOptions.poolMaxSize,
this.workerScript,
this.workerOptions.poolOptions
}
/** @inheritDoc */
- public async addElement (elementData: WorkerData): Promise<void> {
- await this.pool.execute(elementData)
+ public async addElement (elementData: D): Promise<R> {
+ const response = await this.pool.execute(elementData)
// Start element sequentially to optimize memory at startup
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.workerOptions.elementAddDelay! > 0 &&
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
(await sleep(randomizeDelay(this.workerOptions.elementAddDelay!)))
+ return response
}
}
} from './WorkerTypes.js'
import { randomizeDelay, sleep } from './WorkerUtils.js'
-export class WorkerSet extends WorkerAbstract<WorkerData> {
+export class WorkerSet<D extends WorkerData, R extends WorkerData> extends WorkerAbstract<D, R> {
public readonly emitter: EventEmitterAsyncResource | undefined
private readonly workerSet: Set<WorkerSetElement>
private started: boolean
}
/** @inheritDoc */
- public async addElement (elementData: WorkerData): Promise<void> {
+ public async addElement (elementData: D): Promise<R> {
if (!this.started) {
throw new Error('Cannot add a WorkerSet element: not started')
}
const workerSetElement = await this.getWorkerSetElement()
+ const waitForAddedWorkerElement = new Promise<R>((resolve, reject) => {
+ const messageHandler = (message: WorkerMessage<R>): void => {
+ if (message.event === WorkerMessageEvents.addedWorkerElement) {
+ ++workerSetElement.numberOfWorkerElements
+ resolve(message.data)
+ workerSetElement.worker.off('message', messageHandler)
+ } else if (message.event === WorkerMessageEvents.workerElementError) {
+ // eslint-disable-next-line @typescript-eslint/prefer-promise-reject-errors
+ reject(message.data)
+ workerSetElement.worker.off('message', messageHandler)
+ }
+ }
+ workerSetElement.worker.on('message', messageHandler)
+ })
workerSetElement.worker.postMessage({
event: WorkerMessageEvents.addWorkerElement,
data: elementData
})
- ++workerSetElement.numberOfWorkerElements
+ const response = await waitForAddedWorkerElement
// Add element sequentially to optimize memory at startup
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (this.workerOptions.elementAddDelay! > 0) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await sleep(randomizeDelay(this.workerOptions.elementAddDelay!))
}
+ return response
}
/**
...this.workerOptions.poolOptions?.workerOptions
})
worker.on('message', this.workerOptions.poolOptions?.messageHandler ?? EMPTY_FUNCTION)
- worker.on('message', (message: WorkerMessage<WorkerData>) => {
+ worker.on('message', (message: WorkerMessage<R>) => {
if (message.event === WorkerMessageEvents.addedWorkerElement) {
this.emitter?.emit(WorkerSetEvents.elementAdded, this.info)
} else if (message.event === WorkerMessageEvents.workerElementError) {