| log | | {<br />"enabled": true,<br />"file": "logs/combined.log",<br />"errorFile": "logs/error.log",<br />"statisticsInterval": 60,<br />"level": "info",<br />"console": false,<br />"format": "simple",<br />"rotate": true<br />} | {<br />enabled?: boolean;<br />file?: string;<br />errorFile?: string;<br />statisticsInterval?: number;<br />level?: string;<br />console?: boolean;<br />format?: string;<br />rotate?: boolean;<br />maxFiles?: string \| number;<br />maxSize?: string \| number;<br />} | Log configuration section:<br />- _enabled_: enable logging<br />- _file_: log file relative path<br />- _errorFile_: error log file relative path<br />- _statisticsInterval_: seconds between charging stations statistics output in the logs<br />- _level_: emerg/alert/crit/error/warning/notice/info/debug [winston](https://github.com/winstonjs/winston) logging level</br >- _console_: output logs on the console<br />- _format_: [winston](https://github.com/winstonjs/winston) log format<br />- _rotate_: enable daily log files rotation<br />- _maxFiles_: maximum number of log files: https://github.com/winstonjs/winston-daily-rotate-file#options<br />- _maxSize_: maximum size of log files in bytes, or units of kb, mb, and gb: https://github.com/winstonjs/winston-daily-rotate-file#options |
| worker | | {<br />"processType": "workerSet",<br />"startDelay": 500,<br />"elementStartDelay": 0,<br />"elementsPerWorker": 'auto',<br />"poolMinSize": 4,<br />"poolMaxSize": 16<br />} | {<br />processType?: WorkerProcessType;<br />startDelay?: number;<br />elementStartDelay?: number;<br />elementsPerWorker?: number \| 'auto' \| 'all';<br />poolMinSize?: number;<br />poolMaxSize?: number;<br />resourceLimits?: ResourceLimits;<br />} | Worker configuration section:<br />- _processType_: worker threads process type (`workerSet`/`fixedPool`/`dynamicPool`)<br />- _startDelay_: milliseconds to wait at worker threads startup (only for `workerSet` worker threads process type)<br />- _elementStartDelay_: milliseconds to wait at charging station startup<br />- _elementsPerWorker_: number of charging stations per worker threads for the `workerSet` process type (`auto` means (number of stations) / (number of CPUs) \* 1.5 if (number of stations) > (number of CPUs), otherwise 1; `all` means a unique worker will run all charging stations)<br />- _poolMinSize_: worker threads pool minimum number of threads</br >- _poolMaxSize_: worker threads pool maximum number of threads<br />- _resourceLimits_: worker threads [resource limits](https://nodejs.org/api/worker_threads.html#new-workerfilename-options) object option |
| uiServer | | {<br />"enabled": false,<br />"type": "ws",<br />"version": "1.1",<br />"options": {<br />"host": "localhost",<br />"port": 8080<br />}<br />} | {<br />enabled?: boolean;<br />type?: ApplicationProtocol;<br />version?: ApplicationProtocolVersion;<br />options?: ServerOptions;<br />authentication?: {<br />enabled: boolean;<br />type: AuthenticationType;<br />username?: string;<br />password?: string;<br />}<br />} | UI server configuration section:<br />- _enabled_: enable UI server<br />- _type_: 'http' or 'ws'<br />- _version_: HTTP version '1.1' or '2.0'<br />- _options_: node.js net module [listen options](https://nodejs.org/api/net.html#serverlistenoptions-callback)<br />- _authentication_: authentication type configuration section |
-| performanceStorage | | {<br />"enabled": false,<br />"type": "jsonfile",<br />"uri": "file:///performance/performanceRecords.json"<br />} | {<br />enabled?: boolean;<br />type?: string;<br />uri?: string;<br />} | Performance storage configuration section:<br />- _enabled_: enable performance storage<br />- _type_: 'jsonfile' or 'mongodb'<br />- _uri_: storage URI |
+| performanceStorage | | {<br />"enabled": true,<br />"type": "none",<br />} | {<br />enabled?: boolean;<br />type?: string;<br />uri?: string;<br />} | Performance storage configuration section:<br />- _enabled_: enable performance storage<br />- _type_: 'jsonfile', 'mongodb' or 'none'<br />- _uri_: storage URI |
| stationTemplateUrls | | {}[] | {<br />file: string;<br />numberOfStations: number;<br />}[] | array of charging station configuration templates URIs configuration section (charging station configuration template file name and number of stations) |
#### Worker process model
`ProcedureName`: 'setSupervisionUrl'
`PDU`: {
`hashIds`: charging station unique identifier strings array (optional, default: all charging stations),
- `uri`: string
+ `url`: string
}
- Response:
`responsesFailed`: failed responses payload array (optional)
}
+###### Performance Statistics
+
+- Request:
+ `ProcedureName`: 'performanceStatistics'
+ `PDU`: {}
+
+- Response:
+ `PDU`: {
+ `status`: 'success' | 'failure'
+ `performanceStatistics`: Statistics[]
+ }
+
###### List Charging Stations
- Request:
"processType": "workerSet"
},
"performanceStorage": {
- "enabled": false,
- "type": "jsonfile"
+ "enabled": true,
+ "type": "none"
},
"uiServer": {
"enabled": false,
{
"_type": "export",
"__export_format": 4,
- "__export_date": "2024-02-01T10:00:36.181Z",
+ "__export_date": "2024-02-05T15:02:32.625Z",
"__export_source": "insomnia.desktop.app:v8.6.0",
"resources": [
{
"description": "",
"_type": "websocket_request"
},
+ {
+ "_id": "ws-req_0bab7a97ceda4944976a463f616dec5c",
+ "parentId": "wrk_64c9d5670f014930baf668326b95e601",
+ "modified": 1707143784125,
+ "created": 1707143784125,
+ "name": "performanceStatistics",
+ "url": "{{ _.baseUrl }}",
+ "metaSortKey": -1671191988790.125,
+ "headers": [
+ {
+ "id": "pair_9a64d3b0bc654ab68710ef138f00d3f5",
+ "name": "Sec-WebSocket-Protocol",
+ "value": "{{ _.protocol }}{{ _.version }}",
+ "description": ""
+ }
+ ],
+ "authentication": {
+ "type": "basic",
+ "useISO88591": false,
+ "disabled": false,
+ "username": "{{ _.username }}",
+ "password": "{{ _.password }}"
+ },
+ "parameters": [],
+ "pathParameters": [],
+ "settingEncodeUrl": true,
+ "settingStoreCookies": true,
+ "settingSendCookies": true,
+ "settingFollowRedirects": "global",
+ "description": "",
+ "_type": "websocket_request"
+ },
{
"_id": "ws-req_ebe5a555a6344dfba7e29f857af11d08",
"parentId": "wrk_64c9d5670f014930baf668326b95e601",
{
"_id": "ws-payload_5a7ab577051646ff9975c34ccf900f18",
"parentId": "ws-req_6154d7eed8ba498ca6da5245e205a329",
- "modified": 1706781503850,
+ "modified": 1707145313292,
"created": 1671192074985,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"stopSimulator\",\n\t{}\n]",
{
"_id": "ws-payload_b6275ce690b5411eb84265642cda2014",
"parentId": "ws-req_6815f92a40cf410383b99302180164f6",
- "modified": 1706781539872,
+ "modified": 1707145312464,
"created": 1671297215182,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"startSimulator\",\n\t{}\n]",
{
"_id": "ws-payload_19c357dbe2fd4925aa43f58726af8b36",
"parentId": "ws-req_cd2c7d152f834abea3def6f66c63c7e2",
- "modified": 1706781504424,
+ "modified": 1707145314106,
"created": 1671297360441,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"openConnection\",\n\t{}\n]",
{
"_id": "ws-payload_c61ab0e9f9ee43fe96782dfbeabf97d2",
"parentId": "ws-req_8f579f886db842118bf0e1835e7aa750",
- "modified": 1706780850139,
+ "modified": 1707145314837,
"created": 1671297412505,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"closeConnection\",\n\t{}\n]",
{
"_id": "ws-payload_2872e2656c164769acf98cc7ba7ea028",
"parentId": "ws-req_e5902850ac1d40369bd6e942a2755a9d",
- "modified": 1706781508144,
+ "modified": 1707145316242,
"created": 1671297544207,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"listChargingStations\",\n\t{}\n]",
{
"_id": "ws-payload_edebda0226aa43f88712d7feb60ac645",
"parentId": "ws-req_ebe5a555a6344dfba7e29f857af11d08",
- "modified": 1706781518372,
+ "modified": 1707145319096,
"created": 1671297697172,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"startChargingStation\",\n\t{\n\t\t\"hashIds\": [\n\t\t\t\"5b82a559d2b453f6277e272e134ae824ae358cfb6ee2415af9f7c2f325ef8b3e930aeeadcd866df4b8aec58786e60ae7\"\n\t\t]\n\t}\n]",
{
"_id": "ws-payload_20cb03a0142d44a98ddb7bc59ccfea11",
"parentId": "ws-req_720b5d562a0f42929ef9aa16019728fe",
- "modified": 1706781522084,
+ "modified": 1707145319951,
"created": 1671297731073,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"stopChargingStation\",\n\t{\n\t\t\"hashIds\": [\n\t\t\t\"5b82a559d2b453f6277e272e134ae824ae358cfb6ee2415af9f7c2f325ef8b3e930aeeadcd866df4b8aec58786e60ae7\"\n\t\t]\n\t}\n]",
{
"_id": "ws-payload_d8e66e0f933e4d74bb5fbff4d15a44bf",
"parentId": "ws-req_23025e078480491daf01406b2b5e9cc2",
- "modified": 1706781522633,
+ "modified": 1707145321035,
"created": 1671298432039,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"startAutomaticTransactionGenerator\",\n\t{}\n]",
{
"_id": "ws-payload_4f502389fb3348ee9dcaf3419fbc49ba",
"parentId": "ws-req_bdbae9eeb408489ca8e3283b437ccbf4",
- "modified": 1706781524682,
+ "modified": 1707145321730,
"created": 1671298535001,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"stopAutomaticTransactionGenerator\",\n\t{}\n]",
{
"_id": "ws-payload_1b02fae03f9c4f54af23911678519841",
"parentId": "ws-req_f836c127aca54a909a110a16b791c29b",
- "modified": 1706781536669,
+ "modified": 1707145326651,
"created": 1673277254287,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"diagnosticsStatusNotification\",\n\t{\n\t\t\"status\": \"Uploaded\"\n\t}\n]",
{
"_id": "ws-payload_b563d5d8dc284ebb8f9dd2083734cc45",
"parentId": "ws-req_c326f21a473c430081d7229a82c69b33",
- "modified": 1706781537962,
+ "modified": 1707145327532,
"created": 1673279189375,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"firmwareStatusNotification\",\n\t{\n\t\t\"status\": \"Downloading\"\n\t}\n]",
{
"_id": "ws-payload_e2e7b8a7d8694b94a16868fcd0b90916",
"parentId": "ws-req_d88784511f704224999e41bd53ba71b8",
- "modified": 1706781534291,
+ "modified": 1707145325829,
"created": 1673728879079,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"statusNotification\",\n\t{\n\t\t\"connectorId\": 1,\n\t\t\"status\": \"Available\",\n\t\t\"errorCode\": \"NoError\"\n\t}\n]",
{
"_id": "ws-payload_bf36aa8e9c8646d6a37697c6496e257f",
"parentId": "ws-req_fc239903df2d46bb998c16dbcb8cafea",
- "modified": 1706781527560,
+ "modified": 1707145324132,
"created": 1674411426307,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"startTransaction\",\n\t{\n\t\t\"hashIds\": [\n\t\t\t\"0058d8b50e422cce5bbd0c0a4ad13d5d657e8a88670dcf04c1b2b563fea3db5b96a3686278b374ed050e21baef89060e\"\n\t\t],\n\t\t\"connectorId\": 1,\n\t\t\"idTag\": \"test\"\n\t}\n]",
{
"_id": "ws-payload_43c713bdb0e64cbda34b3102f42da321",
"parentId": "ws-req_e3db8f3f31c947c1969a6a257b65a2d5",
- "modified": 1706781531466,
+ "modified": 1707144801240,
"created": 1674411483206,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"stopTransaction\",\n\t{\n\t\t\"hashIds\": [\n\t\t\t\"0058d8b50e422cce5bbd0c0a4ad13d5d657e8a88670dcf04c1b2b563fea3db5b96a3686278b374ed050e21baef89060e\"\n\t\t],\n\t\t\"transactionId\": 235051179\n\t}\n]",
{
"_id": "ws-payload_95c28d71c8d940bb83ac514f8916a66d",
"parentId": "ws-req_afbfa6e6824b427e99e735c0b1eabe3b",
- "modified": 1706781526291,
+ "modified": 1707145322952,
"created": 1678991663554,
"name": "New Payload",
- "value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"setSupervisionUrl\",\n\t{\n\t\t\"hashIds\": [\n\t\t\t\"31f796168755b5dec7114fddebf951fcf37dd9c72e3f511fec5a86148be057ff239a57a51e65d254ec456dcd22f0df5a\"\n\t\t],\n\t\t\"url\": \"wss://ev-ocpp-json-server-plugncharge.cfapps.eu12.hana.ondemand.com/OCPP16/1839eb8b-b05e-49d5-bfff-04426b24834b/MBXDMg2i/6ceb0ecc\"\n\t}\n]",
+ "value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"setSupervisonUrl\",\n\t{\n\t\t\"hashIds\": [\n\t\t\t\"5b82a559d2b453f6277e272e134ae824ae358cfb6ee2415af9f7c2f325ef8b3e930aeeadcd866df4b8aec58786e60ae7\"\n\t\t],\n\t\t\"url\": \"wss://domain.tld\"\n\t}\n]",
"mode": "application/json",
"_type": "websocket_payload"
},
{
"_id": "ws-payload_3e1dffbcefcc481286b44c694b9e6496",
"parentId": "ws-req_3a0ff14878b449f4be3dfbb7432b5f87",
- "modified": 1706781510837,
+ "modified": 1707145315238,
"created": 1706726300041,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"listTemplates\",\n\t{}\n]",
{
"_id": "ws-payload_5e2dfed34a104c28b887c885ada1b4af",
"parentId": "ws-req_8777c5635dd64fccbc2b0f450be656c0",
- "modified": 1706781515138,
+ "modified": 1707145316885,
"created": 1706778795544,
"name": "New Payload",
"value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"addChargingStations\",\n\t{\n\t\t\"template\": \"evlink.station-template\",\n\t\t\"numberOfStations\": 1\n\t}\n]",
"mode": "application/json",
"_type": "websocket_payload"
+ },
+ {
+ "_id": "ws-payload_494fba679fa644ccb318f092e780834f",
+ "parentId": "ws-req_0bab7a97ceda4944976a463f616dec5c",
+ "modified": 1707145317588,
+ "created": 1707143784130,
+ "name": "New Payload",
+ "value": "[\n\t\"{% uuid 'v4' %}\",\n\t\"performanceStatistics\",\n\t{}\n]",
+ "mode": "application/json",
+ "_type": "websocket_payload"
}
]
}
{
"_type": "export",
"__export_format": 4,
- "__export_date": "2024-02-01T10:00:15.210Z",
+ "__export_date": "2024-02-05T15:10:03.585Z",
"__export_source": "insomnia.desktop.app:v8.6.0",
"resources": [
{
"settingFollowRedirects": "global",
"_type": "request"
},
+ {
+ "_id": "req_eb5ab162f6d64b87b61968263e743309",
+ "parentId": "wrk_509d4a5094fa485ba93e53bc735e8ac3",
+ "modified": 1707145606591,
+ "created": 1707145585253,
+ "url": "{{baseUrl}}/{{protocol}}/{{version}}/performanceStatistics",
+ "name": "performanceStatistics",
+ "description": "",
+ "method": "POST",
+ "body": { "mimeType": "application/json", "text": "{}" },
+ "parameters": [],
+ "headers": [
+ {
+ "name": "Content-Type",
+ "value": "application/json",
+ "id": "pair_af9f914ca52f407488bc6df6c7db3a08"
+ }
+ ],
+ "authentication": {
+ "type": "basic",
+ "useISO88591": false,
+ "disabled": false,
+ "username": "{{username}}",
+ "password": "{{password}}"
+ },
+ "metaSortKey": -999999962.5,
+ "isPrivate": false,
+ "pathParameters": [],
+ "settingStoreCookies": true,
+ "settingSendCookies": true,
+ "settingDisableRenderRequestBody": false,
+ "settingEncodeUrl": true,
+ "settingRebuildPath": true,
+ "settingFollowRedirects": "global",
+ "_type": "request"
+ },
{
"_id": "req_85f61e3486114d52abd4e18f212afd59",
"parentId": "wrk_509d4a5094fa485ba93e53bc735e8ac3",
return this.chargingStationsByTemplate.get(templateName)?.lastIndex ?? 0
}
+ public getPerformanceStatistics (): IterableIterator<Statistics> | undefined {
+ return this.storage?.getPerformanceStatistics()
+ }
+
private get numberOfAddedChargingStations (): number {
return [...this.chargingStationsByTemplate.values()].reduce(
(accumulator, value) => accumulator + value.added,
ResponseStatus,
type UIServerConfiguration
} from '../../types/index.js'
-import { Constants, generateUUID, isNotEmptyString, logPrefix, logger } from '../../utils/index.js'
+import {
+ Constants,
+ JSONStringifyWithMapSupport,
+ generateUUID,
+ isNotEmptyString,
+ logPrefix,
+ logger
+} from '../../utils/index.js'
const moduleName = 'UIHttpServer'
.writeHead(this.responseStatusToStatusCode(payload.status), {
'Content-Type': 'application/json'
})
- .end(JSON.stringify(payload))
+ .end(JSONStringifyWithMapSupport(payload))
} else {
logger.error(
`${this.logPrefix(moduleName, 'sendResponse')} Response for unknown request id: ${uuid}`
} from '../../types/index.js'
import {
Constants,
+ JSONStringifyWithMapSupport,
getWebSocketCloseEventStatusString,
isNotEmptyString,
logPrefix,
if (this.hasResponseHandler(responseId)) {
const ws = this.responseHandlers.get(responseId) as WebSocket
if (ws.readyState === WebSocket.OPEN) {
- ws.send(JSON.stringify(response))
+ ws.send(JSONStringifyWithMapSupport(response))
} else {
logger.error(
`${this.logPrefix(
import {
BroadcastChannelProcedureName,
type BroadcastChannelRequestPayload,
+ ConfigurationSection,
type JsonType,
ProcedureName,
type ProtocolRequest,
type ProtocolVersion,
type RequestPayload,
type ResponsePayload,
- ResponseStatus
+ ResponseStatus,
+ type StorageConfiguration
} from '../../../types/index.js'
-import { isAsyncFunction, isNotEmptyArray, logger } from '../../../utils/index.js'
+import { Configuration, isAsyncFunction, isNotEmptyArray, logger } from '../../../utils/index.js'
import { Bootstrap } from '../../Bootstrap.js'
import { UIServiceWorkerBroadcastChannel } from '../../broadcast-channel/UIServiceWorkerBroadcastChannel.js'
import type { AbstractUIServer } from '../AbstractUIServer.js'
[ProcedureName.LIST_TEMPLATES, this.handleListTemplates.bind(this)],
[ProcedureName.LIST_CHARGING_STATIONS, this.handleListChargingStations.bind(this)],
[ProcedureName.ADD_CHARGING_STATIONS, this.handleAddChargingStations.bind(this)],
+ [ProcedureName.PERFORMANCE_STATISTICS, this.handlePerformanceStatistics.bind(this)],
[ProcedureName.START_SIMULATOR, this.handleStartSimulator.bind(this)],
[ProcedureName.STOP_SIMULATOR, this.handleStopSimulator.bind(this)]
])
}
}
+ private handlePerformanceStatistics (): ResponsePayload {
+ if (
+ Configuration.getConfigurationSection<StorageConfiguration>(
+ ConfigurationSection.performanceStorage
+ ).enabled !== true
+ ) {
+ return {
+ status: ResponseStatus.FAILURE,
+ errorMessage: 'Performance statistics storage is not enabled'
+ } satisfies ResponsePayload
+ }
+ try {
+ return {
+ status: ResponseStatus.SUCCESS,
+ performanceStatistics: [
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ ...Bootstrap.getInstance().getPerformanceStatistics()!
+ ] as JsonType[]
+ }
+ } catch (error) {
+ return {
+ status: ResponseStatus.FAILURE,
+ errorMessage: (error as Error).message,
+ errorStack: (error as Error).stack
+ } satisfies ResponsePayload
+ }
+ }
+
private async handleStartSimulator (): Promise<ResponsePayload> {
try {
await Bootstrap.getInstance().start()
return { status: ResponseStatus.SUCCESS }
- } catch {
- return { status: ResponseStatus.FAILURE }
+ } catch (error) {
+ return {
+ status: ResponseStatus.FAILURE,
+ errorMessage: (error as Error).message,
+ errorStack: (error as Error).stack
+ } satisfies ResponsePayload
}
}
try {
await Bootstrap.getInstance().stop()
return { status: ResponseStatus.SUCCESS }
- } catch {
- return { status: ResponseStatus.FAILURE }
+ } catch (error) {
+ return {
+ status: ResponseStatus.FAILURE,
+ errorMessage: (error as Error).message,
+ errorStack: (error as Error).stack
+ } satisfies ResponsePayload
}
}
}
} from '../../utils/index.js'
export class JsonFileStorage extends Storage {
- private static performanceRecords: Map<string, Statistics>
-
private fd?: number
constructor (storageUri: string, logPrefix: string) {
}
public storePerformanceStatistics (performanceStatistics: Statistics): void {
+ this.setPerformanceStatistics(performanceStatistics)
this.checkPerformanceRecordsFile()
- JsonFileStorage.performanceRecords.set(performanceStatistics.id, performanceStatistics)
AsyncLock.runExclusive(AsyncLockType.performance, () => {
writeSync(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.fd!,
- JSONStringifyWithMapSupport([...JsonFileStorage.performanceRecords.values()], 2),
+ JSONStringifyWithMapSupport([...this.getPerformanceStatistics()], 2),
0,
'utf8'
)
}
public open (): void {
- JsonFileStorage.performanceRecords = new Map<string, Statistics>()
try {
if (this.fd == null) {
if (!existsSync(dirname(this.dbName))) {
}
public close (): void {
- JsonFileStorage.performanceRecords.clear()
+ this.clearPerformanceStatistics()
try {
if (this.fd != null) {
closeSync(this.fd)
public async storePerformanceStatistics (performanceStatistics: Statistics): Promise<void> {
try {
+ this.setPerformanceStatistics(performanceStatistics)
await this.orm?.em.upsert({
...performanceStatistics,
statisticsData: Array.from(performanceStatistics.statisticsData, ([name, value]) => ({
}
public async close (): Promise<void> {
+ this.clearPerformanceStatistics()
try {
if (this.orm != null) {
await this.orm.close()
public async storePerformanceStatistics (performanceStatistics: Statistics): Promise<void> {
try {
+ this.setPerformanceStatistics(performanceStatistics)
this.checkDBConnection()
await this.client
?.db(this.dbName)
}
public async close (): Promise<void> {
+ this.clearPerformanceStatistics()
try {
if (this.connected && this.client != null) {
await this.client.close()
--- /dev/null
+// Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
+
+import { Storage } from './Storage.js'
+import type { Statistics } from '../../types/index.js'
+
+export class None extends Storage {
+ constructor () {
+ super('none://none', 'none')
+ }
+
+ public storePerformanceStatistics (performanceStatistics: Statistics): void {
+ this.setPerformanceStatistics(performanceStatistics)
+ }
+
+ public open (): void {
+ /** Intentionally empty */
+ }
+
+ public close (): void {
+ this.clearPerformanceStatistics()
+ }
+}
protected readonly storageUri: URL
protected readonly logPrefix: string
protected dbName!: string
+ private static readonly performanceStatistics = new Map<string, Statistics>()
constructor (storageUri: string, logPrefix: string) {
this.storageUri = new URL(storageUri)
}
}
+ public getPerformanceStatistics (): IterableIterator<Statistics> {
+ return Storage.performanceStatistics.values()
+ }
+
+ protected setPerformanceStatistics (performanceStatistics: Statistics): void {
+ Storage.performanceStatistics.set(performanceStatistics.id, performanceStatistics)
+ }
+
+ protected clearPerformanceStatistics (): void {
+ Storage.performanceStatistics.clear()
+ }
+
public abstract open (): void | Promise<void>
public abstract close (): void | Promise<void>
public abstract storePerformanceStatistics (
import { JsonFileStorage } from './JsonFileStorage.js'
import { MikroOrmStorage } from './MikroOrmStorage.js'
import { MongoDBStorage } from './MongoDBStorage.js'
+import { None } from './None.js'
import type { Storage } from './Storage.js'
import { BaseError } from '../../exception/index.js'
import { StorageType } from '../../types/index.js'
case StorageType.MYSQL:
storageInstance = new MikroOrmStorage(connectionUri, logPrefix, type)
break
+ case StorageType.NONE:
+ storageInstance = new None()
+ break
default:
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
throw new BaseError(`${logPrefix} Unknown storage type: ${type}`)
export enum StorageType {
+ NONE = 'none',
JSON_FILE = 'jsonfile',
MONGO_DB = 'mongodb',
MYSQL = 'mysql',
LIST_TEMPLATES = 'listTemplates',
LIST_CHARGING_STATIONS = 'listChargingStations',
ADD_CHARGING_STATIONS = 'addChargingStations',
+ PERFORMANCE_STATISTICS = 'performanceStatistics',
START_CHARGING_STATION = 'startChargingStation',
STOP_CHARGING_STATION = 'stopChargingStation',
OPEN_CONNECTION = 'openConnection',
}
break
case StorageType.JSON_FILE:
- default:
storageConfiguration = {
enabled: false,
type: StorageType.JSON_FILE,
uri: getDefaultPerformanceStorageUri(StorageType.JSON_FILE)
}
break
+ case StorageType.NONE:
+ default:
+ storageConfiguration = {
+ enabled: true,
+ type: StorageType.NONE
+ }
+ break
}
if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) {
storageConfiguration = {
import { Constants } from './Constants.js'
import {
type EmptyObject,
+ type ProtocolResponse,
type TimestampedData,
WebSocketCloseEventStatusString
} from '../types/index.js'
}
export const JSONStringifyWithMapSupport = (
- object: Record<string, unknown> | Array<Record<string, unknown>> | Map<unknown, unknown>,
- space?: number
+ object:
+ | Record<string, unknown>
+ | Array<Record<string, unknown>>
+ | Map<unknown, unknown>
+ | ProtocolResponse,
+ space?: string | number
): string => {
return JSON.stringify(
object,