From: Jérôme Benoit Date: Fri, 29 Dec 2023 16:14:21 +0000 (+0100) Subject: chore: switch coding style to JS standard X-Git-Tag: v1.2.31~21 X-Git-Url: https://git.piment-noir.org/?a=commitdiff_plain;h=66a7748ddeda8c94d7562a1ce58d440319654a4c;p=e-mobility-charging-stations-simulator.git chore: switch coding style to JS standard Signed-off-by: Jérôme Benoit --- diff --git a/.eslintignore b/.eslintignore index 3895cf42..f0c525d4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ dist/ +ui/web # FIXME: ESM import parse error build-requirements.js diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 00000000..659ebd99 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,83 @@ +// eslint-disable-next-line n/no-unpublished-require +const { defineConfig } = require('eslint-define-config') + +module.exports = defineConfig({ + root: true, + env: { + es2022: true, + node: true + }, + parserOptions: { + ecmaVersion: 2022, + sourceType: 'module' + }, + plugins: ['import'], + extends: ['eslint:recommended', 'plugin:import/recommended'], + settings: { + 'import/resolver': { + typescript: { + project: './tsconfig.json' + } + } + }, + rules: { + 'sort-imports': [ + 'error', + { + ignoreCase: false, + ignoreDeclarationSort: true, + ignoreMemberSort: false, + memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], + allowSeparatedGroups: true + } + ], + 'import/order': [ + 'error', + { + groups: [ + 'builtin', // Built-in imports (come from NodeJS native) go first + 'external', // <- External imports + 'internal', // <- Absolute imports + ['sibling', 'parent'], // <- Relative imports, the sibling and parent types they can be mingled together + 'index', // <- Index imports + 'unknown' // <- Unknown + ], + 'newlines-between': 'always', + alphabetize: { + /* Sort in ascending order. Options: ["ignore", "asc", "desc"] */ + order: 'asc', + /* Ignore case. Options: [true, false] */ + caseInsensitive: true + } + } + ] + }, + overrides: [ + { + files: ['**/*.ts'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json' + }, + plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc'], + extends: [ + 'plugin:@typescript-eslint/recommended-type-checked', + 'plugin:@typescript-eslint/stylistic-type-checked', + 'plugin:import/typescript', + 'standard-with-typescript' + ], + rules: { + 'operator-linebreak': 'off', + 'tsdoc/syntax': 'warn' + } + }, + { + files: ['**/*.js', '**/*.cjs', '**/*.mjs'], + plugins: ['jsdoc'], + extends: ['plugin:n/recommended', 'plugin:jsdoc/recommended', 'standard'], + rules: { + 'n/shebang': 'off' + } + } + ] +}) diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 408af99e..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,167 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/eslintrc", - "root": true, - "env": { - "es2022": true, - "node": true - }, - "parserOptions": { - "ecmaVersion": 2022, - "sourceType": "module" - }, - "plugins": ["import"], - "extends": ["eslint:recommended", "plugin:import/recommended", "plugin:prettier/recommended"], - "settings": { - "import/resolver": { - "typescript": { - "project": "./tsconfig.json" - } - } - }, - "rules": { - "semi": ["error", "always"], - "space-before-blocks": ["error", "always"], - "curly": ["error", "all"], - "brace-style": "error", - "no-else-return": "error", - "no-extra-bind": "error", - "no-lone-blocks": "error", - "no-multi-spaces": "error", - "no-empty": "error", - "no-return-assign": ["error", "always"], - "no-useless-catch": "error", - "no-useless-return": "error", - "no-multiple-empty-lines": [ - "error", - { - "max": 2, - "maxEOF": 1 - } - ], - "block-spacing": "error", - "eol-last": ["error", "always"], - "consistent-this": ["error", "self"], - "func-call-spacing": ["error", "never"], - "keyword-spacing": ["error"], - "id-blacklist": [ - "error", - "any", - "Number", - "number", - "String", - "string", - "Boolean", - "boolean", - "Undefined", - "undefined", - "Symbol", - "symbol" - ], - "linebreak-style": ["error", "unix"], - "max-len": [ - "warn", - { - "code": 100, - "ignoreUrls": true - } - ], - "no-lonely-if": "error", - "no-trailing-spaces": "error", - "no-whitespace-before-property": "error", - "no-shadow": "error", - "space-in-parens": ["error", "never"], - "space-infix-ops": "error", - "space-unary-ops": "error", - "spaced-comment": ["error", "always"], - "switch-colon-spacing": "error", - "arrow-parens": ["error", "always"], - "arrow-spacing": "error", - "no-duplicate-imports": "error", - "no-var": "error", - "prefer-const": "error", - "sort-imports": [ - "error", - { - "ignoreCase": false, - "ignoreDeclarationSort": true, - "ignoreMemberSort": false, - "memberSyntaxSortOrder": ["none", "all", "multiple", "single"], - "allowSeparatedGroups": true - } - ], - "import/order": [ - "error", - { - "groups": [ - "builtin", // Built-in imports (come from NodeJS native) go first - "external", // <- External imports - "internal", // <- Absolute imports - ["sibling", "parent"], // <- Relative imports, the sibling and parent types they can be mingled together - "index", // <- Index imports - "unknown" // <- Unknown - ], - "newlines-between": "always", - "alphabetize": { - /* Sort in ascending order. Options: ["ignore", "asc", "desc"] */ - "order": "asc", - /* Ignore case. Options: [true, false] */ - "caseInsensitive": true - } - } - ], - "object-curly-spacing": ["error", "always"], - "lines-between-class-members": [ - "error", - "always", - { - "exceptAfterSingleLine": true - } - ], - "quotes": [ - "error", - "single", - { - "avoidEscape": true, - "allowTemplateLiterals": false - } - ] - }, - "overrides": [ - { - "files": ["**/*.ts"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "project": true, - "tsconfigRootDir": "./" - }, - "plugins": ["@typescript-eslint", "eslint-plugin-tsdoc"], - "extends": [ - "plugin:@typescript-eslint/recommended-type-checked", - "plugin:@typescript-eslint/stylistic-type-checked", - "plugin:import/typescript" - ], - "rules": { - "tsdoc/syntax": "warn", - "@typescript-eslint/array-type": "off", - "semi": "off", - "@typescript-eslint/semi": ["error", "always"], - "@typescript-eslint/no-empty-function": "warn", - "@typescript-eslint/member-ordering": "error", - "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/no-floating-promises": "error", - "@typescript-eslint/promise-function-async": "error", - "@typescript-eslint/no-misused-promises": "error", - "no-shadow": "off", - "@typescript-eslint/no-shadow": "error" - } - }, - { - "files": ["**/*.js", "**/*.cjs", "**/*.mjs"], - "plugins": ["jsdoc"], - "extends": ["plugin:jsdoc/recommended", "plugin:n/recommended"], - "rules": { - "n/shebang": "off" - } - } - ] -} diff --git a/.lintstagedrc.js b/.lintstagedrc.js index 08c56631..0124480b 100644 --- a/.lintstagedrc.js +++ b/.lintstagedrc.js @@ -1,5 +1,5 @@ export default { '{src,tests}/**/*.{ts,tsx,cts,mts}': ['prettier --cache --write', 'eslint --cache --fix'], '**/*.{json,md,yml,yaml}': ['prettier --cache --write'], - '**/*.{js,jsx,cjs,mjs}': ['prettier --cache --write', 'eslint --cache --fix'], -}; + '**/*.{js,jsx,cjs,mjs}': ['prettier --cache --write', 'eslint --cache --fix'] +} diff --git a/.prettierrc.json b/.prettierrc.json index 6414c22d..6632aaeb 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,5 +1,7 @@ { "$schema": "https://json.schemastore.org/prettierrc", "printWidth": 100, - "singleQuote": true + "semi": false, + "singleQuote": true, + "trailingComma": "none" } diff --git a/README.md b/README.md index 2a791ce8..f5e6467f 100644 --- a/README.md +++ b/README.md @@ -215,17 +215,17 @@ But the modifications to test have to be done to the files in the build target d ```ts type AutomaticTransactionGeneratorConfiguration = { - enable: boolean; - minDuration: number; - maxDuration: number; - minDelayBetweenTwoTransactions: number; - maxDelayBetweenTwoTransactions: number; - probabilityOfStart: number; - stopAfterHours: number; - stopOnConnectionFailure: boolean; - requireAuthorize?: boolean; - idTagDistribution?: 'random' | 'round-robin' | 'connector-affinity'; -}; + enable: boolean + minDuration: number + maxDuration: number + minDelayBetweenTwoTransactions: number + maxDelayBetweenTwoTransactions: number + probabilityOfStart: number + stopAfterHours: number + stopOnConnectionFailure: boolean + requireAuthorize?: boolean + idTagDistribution?: 'random' | 'round-robin' | 'connector-affinity' +} ``` ##### Example: diff --git a/build-requirements.js b/build-requirements.js index 632e7a34..9cbda92b 100644 --- a/build-requirements.js +++ b/build-requirements.js @@ -1,22 +1,22 @@ -import chalk from 'chalk'; -import semVer from 'semver'; -import packageJson from './package.json' assert { type: 'json' }; -import { version, exit } from 'node:process'; +import chalk from 'chalk' +import semVer from 'semver' +import packageJson from './package.json' assert { type: 'json' } +import { version, exit } from 'node:process' /** * Check if the current node version match the required engines version. */ export const checkNodeVersion = () => { - const enginesNodeVersion = packageJson.engines.node; + const enginesNodeVersion = packageJson.engines.node if (semVer.satisfies(version, enginesNodeVersion) === false) { console.error( chalk.red( - `Required node version ${enginesNodeVersion} not satisfied with current version ${version}.`, - ), - ); + `Required node version ${enginesNodeVersion} not satisfied with current version ${version}.` + ) + ) // eslint-disable-next-line n/no-process-exit - exit(1); + exit(1) } -}; +} -checkNodeVersion(); +checkNodeVersion() diff --git a/bundle.js b/bundle.js index 4bdee327..e96d450c 100644 --- a/bundle.js +++ b/bundle.js @@ -1,19 +1,17 @@ /* eslint-disable n/no-unpublished-import */ -import { env } from 'node:process'; +import { env } from 'node:process' -import chalk from 'chalk'; -import { build } from 'esbuild'; -import { clean } from 'esbuild-plugin-clean'; -import { copy } from 'esbuild-plugin-copy'; +import chalk from 'chalk' +import { build } from 'esbuild' +import { clean } from 'esbuild-plugin-clean' +import { copy } from 'esbuild-plugin-copy' -const isDevelopmentBuild = env.BUILD === 'development'; -const sourcemap = !!isDevelopmentBuild; +const isDevelopmentBuild = env.BUILD === 'development' +const sourcemap = !!isDevelopmentBuild -(async () => { - console.info( - chalk.green(`Building in ${isDevelopmentBuild ? 'development' : 'production'} mode`), - ); - console.time('Build time'); +;(async () => { + console.info(chalk.green(`Building in ${isDevelopmentBuild ? 'development' : 'production'} mode`)) + console.time('Build time') await build({ entryPoints: ['./src/start.ts', './src/charging-station/ChargingStationWorker.ts'], bundle: true, @@ -37,7 +35,7 @@ const sourcemap = !!isDevelopmentBuild; 'winston', 'winston/*', 'winston-daily-rotate-file', - 'ws', + 'ws' ], minify: true, sourcemap, @@ -51,30 +49,30 @@ const sourcemap = !!isDevelopmentBuild; './dist/assets/*.json', './dist/assets/json-schemas', './dist/assets/station-templates', - './dist/assets/ui-protocol', - ], + './dist/assets/ui-protocol' + ] }), copy({ assets: [ { from: ['./src/assets/config.json'], - to: ['./assets'], + to: ['./assets'] }, { from: ['./src/assets/idtags!(-template)*.json'], - to: ['./assets'], + to: ['./assets'] }, { from: ['./src/assets/json-schemas/**/*.json'], - to: ['./assets/json-schemas'], + to: ['./assets/json-schemas'] }, { from: ['./src/assets/station-templates/**/*.json'], - to: ['./assets/station-templates'], - }, - ], - }), - ], - }); - console.timeEnd('Build time'); -})(); + to: ['./assets/station-templates'] + } + ] + }) + ] + }) + console.timeEnd('Build time') +})() diff --git a/commitlint.config.js b/commitlint.config.js index 3f5e287f..7c4ff4d9 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1 +1 @@ -export default { extends: ['@commitlint/config-conventional'] }; +export default { extends: ['@commitlint/config-conventional'] } diff --git a/mikro-orm.config-template.ts b/mikro-orm.config-template.ts index 4ef43dff..8b01a42c 100644 --- a/mikro-orm.config-template.ts +++ b/mikro-orm.config-template.ts @@ -1,10 +1,10 @@ -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' -import { TsMorphMetadataProvider } from '@mikro-orm/reflection'; +import { TsMorphMetadataProvider } from '@mikro-orm/reflection' -import { PerformanceData, PerformanceRecord } from './src/types'; -import { Constants } from './src/utils'; +import { PerformanceData, PerformanceRecord } from './src/types/index.js' +import { Constants } from './src/utils/index.js' export default { metadataProvider: TsMorphMetadataProvider, @@ -12,6 +12,6 @@ export default { type: 'sqlite', clientUrl: `file://${join( dirname(fileURLToPath(import.meta.url)), - `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`, - )}`, -}; + `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db` + )}` +} diff --git a/package.json b/package.json index 8ee93144..6d773d66 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "clean:node_modules": "npx rimraf node_modules", "lint": "cross-env TIMING=1 eslint --cache src", "lint:fix": "cross-env TIMING=1 eslint --cache --fix src", - "format": "prettier --cache --write .", + "format": "prettier --cache --write .; ts-standard --fix .", "test": "glob -c \"c8 node --import tsx --test\" \"tests/**/*.test.ts\"", "test:debug": "glob -c \"node --import tsx --test --inspect\" \"tests/**/*.test.ts\"", "coverage": "c8 report --reporter=lcov", @@ -137,6 +137,9 @@ "esbuild-plugin-copy": "^2.1.1", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", + "eslint-config-standard": "^17.1.0", + "eslint-config-standard-with-typescript": "^43.0.0", + "eslint-define-config": "^2.1.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jsdoc": "^46.9.1", @@ -151,6 +154,8 @@ "release-it": "^17.0.1", "rimraf": "^5.0.5", "semver": "^7.5.4", + "ts-node": "^10.9.2", + "ts-standard": "^12.0.2", "tsx": "^4.7.0", "typescript": "~5.3.3" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 645f985e..2bb140e3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -138,6 +138,15 @@ devDependencies: eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@8.56.0) + eslint-config-standard: + specifier: ^17.1.0 + version: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.5.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0) + eslint-config-standard-with-typescript: + specifier: ^43.0.0 + version: 43.0.0(@typescript-eslint/eslint-plugin@6.16.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.5.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-define-config: + specifier: ^2.1.0 + version: 2.1.0 eslint-import-resolver-typescript: specifier: ^3.6.1 version: 3.6.1(@typescript-eslint/parser@6.16.0)(eslint-plugin-import@2.29.1)(eslint@8.56.0) @@ -180,6 +189,12 @@ devDependencies: semver: specifier: ^7.5.3 version: 7.5.4 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.10.5)(typescript@5.3.3) + ts-standard: + specifier: ^12.0.2 + version: 12.0.2(eslint-import-resolver-typescript@3.6.1)(typescript@5.3.3) tsx: specifier: ^4.7.0 version: 4.7.0 @@ -570,6 +585,13 @@ packages: chalk: 4.1.2 dev: true + /@cspotcode/source-map-support@0.8.1: + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + dev: true + /@dabh/diagnostics@2.0.3: resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==} dependencies: @@ -927,6 +949,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /@jridgewell/trace-mapping@0.3.9: + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /@ljharb/through@2.3.11: resolution: {integrity: sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==} engines: {node: '>= 0.4'} @@ -1452,6 +1481,22 @@ packages: path-browserify: 1.0.1 dev: false + /@tsconfig/node10@1.0.9: + resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} + dev: true + + /@tsconfig/node12@1.0.11: + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + dev: true + + /@tsconfig/node14@1.0.3: + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + dev: true + + /@tsconfig/node16@1.0.4: + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + dev: true + /@types/geojson@7946.0.13: resolution: {integrity: sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ==} @@ -1567,6 +1612,34 @@ packages: '@types/yargs-parser': 21.0.3 dev: true + /@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.10.0 + '@typescript-eslint/parser': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/type-utils': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + debug: 4.3.4 + eslint: 8.56.0 + graphemer: 1.4.0 + ignore: 5.3.0 + natural-compare-lite: 1.4.0 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/eslint-plugin@6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1596,6 +1669,26 @@ packages: - supports-color dev: true + /@typescript-eslint/parser@5.62.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) + debug: 4.3.4 + eslint: 8.56.0 + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/parser@6.16.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1617,6 +1710,14 @@ packages: - supports-color dev: true + /@typescript-eslint/scope-manager@5.62.0: + resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + dev: true + /@typescript-eslint/scope-manager@6.16.0: resolution: {integrity: sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1625,6 +1726,26 @@ packages: '@typescript-eslint/visitor-keys': 6.16.0 dev: true + /@typescript-eslint/type-utils@5.62.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + debug: 4.3.4 + eslint: 8.56.0 + tsutils: 3.21.0(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/type-utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1645,11 +1766,37 @@ packages: - supports-color dev: true + /@typescript-eslint/types@5.62.0: + resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + /@typescript-eslint/types@6.16.0: resolution: {integrity: sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==} engines: {node: ^16.0.0 || >=18.0.0} dev: true + /@typescript-eslint/typescript-estree@5.62.0(typescript@5.3.3): + resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@5.3.3) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/typescript-estree@6.16.0(typescript@5.3.3): resolution: {integrity: sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1672,6 +1819,26 @@ packages: - supports-color dev: true + /@typescript-eslint/utils@5.62.0(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.6 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.3.3) + eslint: 8.56.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + /@typescript-eslint/utils@6.16.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1691,6 +1858,14 @@ packages: - typescript dev: true + /@typescript-eslint/visitor-keys@5.62.0: + resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.62.0 + eslint-visitor-keys: 3.4.3 + dev: true + /@typescript-eslint/visitor-keys@6.16.0: resolution: {integrity: sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==} engines: {node: ^16.0.0 || >=18.0.0} @@ -1942,6 +2117,10 @@ packages: readable-stream: 3.6.2 optional: true + /arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + dev: true + /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} dev: true @@ -2022,6 +2201,16 @@ packages: is-string: 1.0.7 dev: true + /array.prototype.tosorted@1.1.2: + resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-shim-unscopables: 1.0.2 + get-intrinsic: 1.2.2 + dev: true + /arraybuffer.prototype.slice@1.0.2: resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} engines: {node: '>= 0.4'} @@ -2089,6 +2278,12 @@ packages: /async@3.2.5: resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} + /asynciterator.prototype@1.0.0: + resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} + dependencies: + has-symbols: 1.0.3 + dev: true + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: true @@ -3110,6 +3305,10 @@ packages: sha.js: 2.4.11 dev: true + /create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + dev: true + /cross-argv@1.0.0: resolution: {integrity: sha512-uAVe/bgNHlPdP1VE4Sk08u9pAJ7o1x/tVQtX77T5zlhYhuwOWtVkPBEtHdvF5cq48VzeCG5i1zN4dQc8pwLYrw==} dev: true @@ -3531,6 +3730,11 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + dev: true + /diffie-hellman@5.0.3: resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} dependencies: @@ -3759,6 +3963,25 @@ packages: stop-iteration-iterator: 1.0.0 dev: true + /es-iterator-helpers@1.0.15: + resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==} + dependencies: + asynciterator.prototype: 1.0.0 + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + es-set-tostringtag: 2.0.2 + function-bind: 1.1.2 + get-intrinsic: 1.2.2 + globalthis: 1.0.3 + has-property-descriptors: 1.0.1 + has-proto: 1.0.1 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + iterator.prototype: 1.1.2 + safe-array-concat: 1.0.1 + dev: true + /es-set-tostringtag@2.0.2: resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} engines: {node: '>= 0.4'} @@ -3961,6 +4184,94 @@ packages: eslint: 8.56.0 dev: true + /eslint-config-standard-jsx@11.0.0(eslint-plugin-react@7.33.2)(eslint@8.56.0): + resolution: {integrity: sha512-+1EV/R0JxEK1L0NGolAr8Iktm3Rgotx3BKwgaX+eAuSX8D952LULKtjgZD3F+e6SvibONnhLwoTi9DPxN5LvvQ==} + peerDependencies: + eslint: ^8.8.0 + eslint-plugin-react: ^7.28.0 + dependencies: + eslint: 8.56.0 + eslint-plugin-react: 7.33.2(eslint@8.56.0) + dev: true + + /eslint-config-standard-with-typescript@23.0.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-iaaWifImn37Z1OXbNW1es7KI+S7D408F9ys0bpaQf2temeBWlvb0Nc5qHkOgYaRb5QxTZT32GGeN1gtswASOXA==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^5.0.0 + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: ^15.0.0 + eslint-plugin-promise: ^6.0.0 + typescript: '*' + dependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard: 17.0.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-config-standard-with-typescript@43.0.0(@typescript-eslint/eslint-plugin@6.16.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.5.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-AT0qK01M5bmsWiE3UZvaQO5da1y1n6uQckAKqGNe6zPW5IOzgMLXZxw77nnFm+C11nxAZXsCPrbsgJhSrGfX6Q==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^6.4.0 + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: '^15.0.0 || ^16.0.0 ' + eslint-plugin-promise: ^6.0.0 + typescript: '*' + dependencies: + '@typescript-eslint/eslint-plugin': 6.16.0(@typescript-eslint/parser@6.16.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 6.16.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.5.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-n: 16.5.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + typescript: 5.3.3 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-config-standard@17.0.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0): + resolution: {integrity: sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==} + peerDependencies: + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: ^15.0.0 + eslint-plugin-promise: ^6.0.0 + dependencies: + eslint: 8.56.0 + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + dev: true + + /eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.5.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0): + resolution: {integrity: sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: '^15.0.0 || ^16.0.0 ' + eslint-plugin-promise: ^6.0.0 + dependencies: + eslint: 8.56.0 + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-n: 16.5.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + dev: true + + /eslint-define-config@2.1.0: + resolution: {integrity: sha512-QUp6pM9pjKEVannNAbSJNeRuYwW3LshejfyBBpjeMGaJjaDUpVps4C6KVR8R7dWZnD3i0synmrE36znjTkJvdQ==} + engines: {node: '>=18.0.0', npm: '>=9.0.0', pnpm: '>=8.6.0'} + dev: true + /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: @@ -3994,6 +4305,36 @@ packages: - supports-color dev: true + /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + debug: 3.2.7 + eslint: 8.56.0 + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.16.0)(eslint-plugin-import@2.29.1)(eslint@8.56.0) + transitivePeerDependencies: + - supports-color + dev: true + /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} @@ -4036,6 +4377,52 @@ packages: eslint-compat-utils: 0.1.2(eslint@8.56.0) dev: true + /eslint-plugin-es@4.1.0(eslint@8.56.0): + resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + dependencies: + eslint: 8.56.0 + eslint-utils: 2.1.0 + regexpp: 3.2.0 + dev: true + + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.3 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.56.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + hasown: 2.0.0 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.1 + object.values: 1.1.7 + semver: 7.5.4 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.16.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} @@ -4091,6 +4478,23 @@ packages: - supports-color dev: true + /eslint-plugin-n@15.7.0(eslint@8.56.0): + resolution: {integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==} + engines: {node: '>=12.22.0'} + peerDependencies: + eslint: '>=7.0.0' + dependencies: + builtins: 5.0.1 + eslint: 8.56.0 + eslint-plugin-es: 4.1.0(eslint@8.56.0) + eslint-utils: 3.0.0(eslint@8.56.0) + ignore: 5.3.0 + is-core-module: 2.13.1 + minimatch: 3.1.2 + resolve: 1.22.8 + semver: 7.5.4 + dev: true + /eslint-plugin-n@16.5.0(eslint@8.56.0): resolution: {integrity: sha512-Hw02Bj1QrZIlKyj471Tb1jSReTl4ghIMHGuBGiMVmw+s0jOPbI4CBuYpGbZr+tdQ+VAvSK6FDSta3J4ib/SKHQ==} engines: {node: '>=16.0.0'} @@ -4131,6 +4535,40 @@ packages: synckit: 0.8.8 dev: true + /eslint-plugin-promise@6.1.1(eslint@8.56.0): + resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + dependencies: + eslint: 8.56.0 + dev: true + + /eslint-plugin-react@7.33.2(eslint@8.56.0): + resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + dependencies: + array-includes: 3.1.7 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.2 + doctrine: 2.1.0 + es-iterator-helpers: 1.0.15 + eslint: 8.56.0 + estraverse: 5.3.0 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.7 + object.fromentries: 2.0.7 + object.hasown: 1.1.3 + object.values: 1.1.7 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 7.5.4 + string.prototype.matchall: 4.0.10 + dev: true + /eslint-plugin-tsdoc@0.2.17: resolution: {integrity: sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==} dependencies: @@ -4138,6 +4576,14 @@ packages: '@microsoft/tsdoc-config': 0.16.2 dev: true + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + /eslint-scope@7.2.2: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4146,6 +4592,33 @@ packages: estraverse: 5.3.0 dev: true + /eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + dependencies: + eslint-visitor-keys: 1.3.0 + dev: true + + /eslint-utils@3.0.0(eslint@8.56.0): + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + dependencies: + eslint: 8.56.0 + eslint-visitor-keys: 2.1.0 + dev: true + + /eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + dev: true + + /eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + dev: true + /eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -4492,6 +4965,14 @@ packages: path-exists: 4.0.0 dev: true + /find-up@6.3.0: + resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + dev: true + /flame-gradient@1.0.0: resolution: {integrity: sha512-9ejk16/DqvQJ4dHsh68W/4N0zmVQ60zukyUuEHrTbf5pJvP4JqlIdke86Z9174PZokRCXAntY5+H1txSyC7mUA==} dependencies: @@ -4726,6 +5207,11 @@ packages: resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} engines: {node: '>=8.0.0'} + /get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + dev: true + /get-stream@5.2.0: resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} engines: {node: '>=8'} @@ -5440,6 +5926,13 @@ packages: resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} dev: false + /is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + /is-bigint@1.0.4: resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} dependencies: @@ -5528,6 +6021,12 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} + /is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + dependencies: + call-bind: 1.0.5 + dev: true + /is-fullwidth-code-point@1.0.0: resolution: {integrity: sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==} engines: {node: '>=0.10.0'} @@ -5736,12 +6235,23 @@ packages: engines: {node: '>=12'} dev: true + /is-weakmap@2.0.1: + resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} + dev: true + /is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} dependencies: call-bind: 1.0.5 dev: true + /is-weakset@2.0.2: + resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} + dependencies: + call-bind: 1.0.5 + get-intrinsic: 1.2.2 + dev: true + /is-wsl@1.1.0: resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} engines: {node: '>=4'} @@ -5818,6 +6328,16 @@ packages: iterate-iterator: 1.0.2 dev: true + /iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + dependencies: + define-properties: 1.2.1 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + reflect.getprototypeof: 1.0.4 + set-function-name: 2.0.1 + dev: true + /jackspeak@2.3.6: resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} engines: {node: '>=14'} @@ -5912,6 +6432,10 @@ packages: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} dev: true + /json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + dev: true + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -5990,6 +6514,16 @@ packages: verror: 1.10.0 dev: true + /jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + dependencies: + array-includes: 3.1.7 + array.prototype.flat: 1.3.2 + object.assign: 4.1.5 + object.values: 1.1.7 + dev: true + /just-merge@3.2.0: resolution: {integrity: sha512-cNh5FWt44hx4SpQS1xZU8Tzr/fQA69pqCdjbwxmaYYIOuRfA8EIg+dn1bGmIW03ZUtR2vkMOCjWKc+jIbpauSw==} dev: false @@ -6137,6 +6671,22 @@ packages: wrap-ansi: 9.0.0 dev: true + /load-json-file@5.3.0: + resolution: {integrity: sha512-cJGP40Jc/VXUsp8/OrnyKyTZ1y6v/dphm3bioS+RrKXjK2BB6wHUd6JptZEFDGgGahMT+InnZO5i1Ei9mpC8Bw==} + engines: {node: '>=6'} + dependencies: + graceful-fs: 4.2.11 + parse-json: 4.0.0 + pify: 4.0.1 + strip-bom: 3.0.0 + type-fest: 0.3.1 + dev: true + + /load-json-file@7.0.1: + resolution: {integrity: sha512-Gnxj3ev3mB5TkVBGad0JM6dmLiQL+o0t23JPBZ9sd+yvSLk05mFoqKBw5N8gbbkU4TNXyqCgIrl/VM17OgUIgQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -6159,6 +6709,13 @@ packages: p-locate: 5.0.0 dev: true + /locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-locate: 6.0.0 + dev: true + /lodash-es@4.17.21: resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} dev: true @@ -6355,6 +6912,10 @@ packages: semver: 7.5.4 dev: true + /make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + dev: true + /make-fetch-happen@9.1.0: resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==} engines: {node: '>= 10'} @@ -6860,6 +7421,10 @@ packages: hasBin: true dev: true + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + /natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true @@ -7133,6 +7698,15 @@ packages: object-keys: 1.1.1 dev: true + /object.entries@1.1.7: + resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + /object.fromentries@2.0.7: resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} engines: {node: '>= 0.4'} @@ -7151,6 +7725,13 @@ packages: get-intrinsic: 1.2.2 dev: true + /object.hasown@1.1.3: + resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==} + dependencies: + define-properties: 1.2.1 + es-abstract: 1.22.3 + dev: true + /object.values@1.1.7: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} @@ -7324,6 +7905,13 @@ packages: yocto-queue: 0.1.0 dev: true + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + /p-locate@3.0.0: resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} engines: {node: '>=6'} @@ -7345,6 +7933,13 @@ packages: p-limit: 3.1.0 dev: true + /p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + p-limit: 4.0.0 + dev: true + /p-map@4.0.0: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} @@ -7440,6 +8035,14 @@ packages: hasBin: true dev: true + /parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + dev: true + /parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -7475,6 +8078,11 @@ packages: engines: {node: '>=8'} dev: true + /path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + /path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -7551,6 +8159,27 @@ packages: engines: {node: '>=0.10.0'} dev: true + /pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + dev: true + + /pkg-conf@3.1.0: + resolution: {integrity: sha512-m0OTbR/5VPNPqO1ph6Fqbj7Hv6QU7gR/tQW40ZqrL1rjgCU85W6C1bJn0BItuJqnR98PWzw7Z8hHeChD1WrgdQ==} + engines: {node: '>=6'} + dependencies: + find-up: 3.0.0 + load-json-file: 5.3.0 + dev: true + + /pkg-conf@4.0.0: + resolution: {integrity: sha512-7dmgi4UY4qk+4mj5Cd8v/GExPo0K+SlY+hulOSdfZ/T6jVH6//y7NtzZo5WrfhDBxuQ0jCa7fLZmNaNh7EWL/w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + find-up: 6.3.0 + load-json-file: 7.0.1 + dev: true + /pkg-up@3.1.0: resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==} engines: {node: '>=8'} @@ -7681,6 +8310,14 @@ packages: iterate-value: 1.0.2 dev: true + /prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + dev: true + /proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} dev: true @@ -7855,6 +8492,10 @@ packages: strip-json-comments: 2.0.1 dev: true + /react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + dev: true + /react-is@18.2.0: resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} dev: true @@ -7941,6 +8582,18 @@ packages: /reflect-metadata@0.1.13: resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + /reflect.getprototypeof@1.0.4: + resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + globalthis: 1.0.3 + which-builtin-type: 1.1.3 + dev: true + /regexp.prototype.flags@1.5.1: resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} engines: {node: '>= 0.4'} @@ -7950,6 +8603,11 @@ packages: set-function-name: 2.0.1 dev: true + /regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + dev: true + /registry-auth-token@4.2.2: resolution: {integrity: sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==} engines: {node: '>=6.0.0'} @@ -8102,6 +8760,15 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + dependencies: + is-core-module: 2.13.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + /responselike@3.0.0: resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} engines: {node: '>=14.16'} @@ -8601,6 +9268,16 @@ packages: escape-string-regexp: 2.0.0 dev: true + /standard-engine@15.1.0: + resolution: {integrity: sha512-VHysfoyxFu/ukT+9v49d4BRXIokFRZuH3z1VRxzFArZdjSCFpro6rEIU3ji7e4AoAtuSfKBkiOmsrDqKW5ZSRw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + get-stdin: 8.0.0 + minimist: 1.2.8 + pkg-conf: 3.1.0 + xdg-basedir: 4.0.0 + dev: true + /static-eval@2.1.0: resolution: {integrity: sha512-agtxZ/kWSsCkI5E4QifRwsaPs0P0JmZV6dkLz6ILYfFYQGn+5plctanRN+IC8dJRiFkyXHrwEE3W9Wmx67uDbw==} dependencies: @@ -8761,6 +9438,20 @@ packages: strip-ansi: 7.1.0 dev: true + /string.prototype.matchall@4.0.10: + resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==} + dependencies: + call-bind: 1.0.5 + define-properties: 1.2.1 + es-abstract: 1.22.3 + get-intrinsic: 1.2.2 + has-symbols: 1.0.3 + internal-slot: 1.0.6 + regexp.prototype.flags: 1.5.1 + set-function-name: 2.0.1 + side-channel: 1.0.4 + dev: true + /string.prototype.trim@1.2.8: resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} engines: {node: '>= 0.4'} @@ -9087,6 +9778,63 @@ packages: code-block-writer: 12.0.0 dev: false + /ts-node@10.9.2(@types/node@20.10.5)(typescript@5.3.3): + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.10.5 + acorn: 8.11.2 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.3.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + dev: true + + /ts-standard@12.0.2(eslint-import-resolver-typescript@3.6.1)(typescript@5.3.3): + resolution: {integrity: sha512-XX2wrB9fKKTfBj4yD3ABm9iShzZcS2iWcPK8XzlBvuL20+wMiLgiz/k5tXgZwTaYq5wRhbks1Y9PelhujF/9ag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + peerDependencies: + typescript: '*' + dependencies: + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + eslint: 8.56.0 + eslint-config-standard-jsx: 11.0.0(eslint-plugin-react@7.33.2)(eslint@8.56.0) + eslint-config-standard-with-typescript: 23.0.0(@typescript-eslint/eslint-plugin@5.62.0)(eslint-plugin-import@2.29.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.56.0)(typescript@5.3.3) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@5.62.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0) + eslint-plugin-n: 15.7.0(eslint@8.56.0) + eslint-plugin-promise: 6.1.1(eslint@8.56.0) + eslint-plugin-react: 7.33.2(eslint@8.56.0) + minimist: 1.2.8 + pkg-conf: 4.0.0 + standard-engine: 15.1.0 + typescript: 5.3.3 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + /tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} dependencies: @@ -9112,6 +9860,16 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + /tsutils@3.21.0(typescript@5.3.3): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 5.3.3 + dev: true + /tsx@4.7.0: resolution: {integrity: sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==} engines: {node: '>=18.0.0'} @@ -9183,6 +9941,11 @@ packages: engines: {node: '>=10'} dev: true + /type-fest@0.3.1: + resolution: {integrity: sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==} + engines: {node: '>=6'} + dev: true + /type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -9480,6 +10243,10 @@ packages: hasBin: true dev: true + /v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + dev: true + /v8-to-istanbul@9.2.0: resolution: {integrity: sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==} engines: {node: '>=10.12.0'} @@ -9564,6 +10331,33 @@ packages: is-symbol: 1.0.4 dev: true + /which-builtin-type@1.1.3: + resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} + engines: {node: '>= 0.4'} + dependencies: + function.prototype.name: 1.1.6 + has-tostringtag: 1.0.0 + is-async-function: 2.0.0 + is-date-object: 1.0.5 + is-finalizationregistry: 1.0.2 + is-generator-function: 1.0.10 + is-regex: 1.1.4 + is-weakref: 1.0.2 + isarray: 2.0.5 + which-boxed-primitive: 1.0.2 + which-collection: 1.0.1 + which-typed-array: 1.1.13 + dev: true + + /which-collection@1.0.1: + resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} + dependencies: + is-map: 2.0.2 + is-set: 2.0.2 + is-weakmap: 2.0.1 + is-weakset: 2.0.2 + dev: true + /which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} dev: true @@ -9823,7 +10617,17 @@ packages: yargs-parser: 21.1.1 dev: true + /yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + dev: true + /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true diff --git a/prepare.js b/prepare.js index f5d01054..256ce916 100644 --- a/prepare.js +++ b/prepare.js @@ -1,8 +1,8 @@ -import { env } from 'node:process'; +import { env } from 'node:process' -const isCIEnvironment = env.CI !== undefined; -const isCFEnvironment = env.VCAP_APPLICATION !== undefined; +const isCIEnvironment = env.CI !== undefined +const isCFEnvironment = env.VCAP_APPLICATION !== undefined if (isCFEnvironment === false && isCIEnvironment === false) { // eslint-disable-next-line n/no-unpublished-import - import('husky').then(({ install }) => install()); + import('husky').then(({ install }) => install()) } diff --git a/skip-preinstall.js b/skip-preinstall.js index 8f5c9d55..fde48de5 100644 --- a/skip-preinstall.js +++ b/skip-preinstall.js @@ -1,10 +1,10 @@ -import { env, exit } from 'node:process'; +import { env, exit } from 'node:process' -const skipPreinstall = env.SKIP_PREINSTALL || env.VCAP_APPLICATION !== undefined; +const skipPreinstall = env.SKIP_PREINSTALL || env.VCAP_APPLICATION !== undefined if (skipPreinstall) { // eslint-disable-next-line n/no-process-exit - exit(); + exit() } else { // eslint-disable-next-line n/no-process-exit - exit(1); + exit(1) } diff --git a/src/charging-station/AutomaticTransactionGenerator.ts b/src/charging-station/AutomaticTransactionGenerator.ts index abe271fd..9da371cd 100644 --- a/src/charging-station/AutomaticTransactionGenerator.ts +++ b/src/charging-station/AutomaticTransactionGenerator.ts @@ -1,13 +1,13 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { hoursToMilliseconds, secondsToMilliseconds } from 'date-fns'; +import { hoursToMilliseconds, secondsToMilliseconds } from 'date-fns' -import type { ChargingStation } from './ChargingStation.js'; -import { checkChargingStation } from './Helpers.js'; -import { IdTagsCache } from './IdTagsCache.js'; -import { isIdTagAuthorized } from './ocpp/index.js'; -import { BaseError } from '../exception/index.js'; -import { PerformanceStatistics } from '../performance/index.js'; +import type { ChargingStation } from './ChargingStation.js' +import { checkChargingStation } from './Helpers.js' +import { IdTagsCache } from './IdTagsCache.js' +import { isIdTagAuthorized } from './ocpp/index.js' +import { BaseError } from '../exception/index.js' +import { PerformanceStatistics } from '../performance/index.js' import { AuthorizationStatus, RequestCommand, @@ -15,8 +15,8 @@ import { type StartTransactionResponse, type Status, StopTransactionReason, - type StopTransactionResponse, -} from '../types/index.js'; + type StopTransactionResponse +} from '../types/index.js' import { Constants, cloneObject, @@ -26,347 +26,365 @@ import { logPrefix, logger, secureRandom, - sleep, -} from '../utils/index.js'; + sleep +} from '../utils/index.js' export class AutomaticTransactionGenerator { private static readonly instances: Map = new Map< - string, - AutomaticTransactionGenerator - >(); + string, + AutomaticTransactionGenerator + >() - public readonly connectorsStatus: Map; - public started: boolean; - private starting: boolean; - private stopping: boolean; - private readonly chargingStation: ChargingStation; + public readonly connectorsStatus: Map + public started: boolean + private starting: boolean + private stopping: boolean + private readonly chargingStation: ChargingStation - private constructor(chargingStation: ChargingStation) { - this.started = false; - this.starting = false; - this.stopping = false; - this.chargingStation = chargingStation; - this.connectorsStatus = new Map(); - this.initializeConnectorsStatus(); + private constructor (chargingStation: ChargingStation) { + this.started = false + this.starting = false + this.stopping = false + this.chargingStation = chargingStation + this.connectorsStatus = new Map() + this.initializeConnectorsStatus() } - public static getInstance( - chargingStation: ChargingStation, + public static getInstance ( + chargingStation: ChargingStation ): AutomaticTransactionGenerator | undefined { - if (AutomaticTransactionGenerator.instances.has(chargingStation.stationInfo.hashId) === false) { + if (!AutomaticTransactionGenerator.instances.has(chargingStation.stationInfo.hashId)) { AutomaticTransactionGenerator.instances.set( chargingStation.stationInfo.hashId, - new AutomaticTransactionGenerator(chargingStation), - ); + new AutomaticTransactionGenerator(chargingStation) + ) } - return AutomaticTransactionGenerator.instances.get(chargingStation.stationInfo.hashId); + return AutomaticTransactionGenerator.instances.get(chargingStation.stationInfo.hashId) } - public start(): void { - if (checkChargingStation(this.chargingStation, this.logPrefix()) === false) { - return; + public start (): void { + if (!checkChargingStation(this.chargingStation, this.logPrefix())) { + return } - if (this.started === true) { - logger.warn(`${this.logPrefix()} is already started`); - return; + if (this.started) { + logger.warn(`${this.logPrefix()} is already started`) + return } - if (this.starting === true) { - logger.warn(`${this.logPrefix()} is already starting`); - return; + if (this.starting) { + logger.warn(`${this.logPrefix()} is already starting`) + return } - this.starting = true; - this.startConnectors(); - this.started = true; - this.starting = false; + this.starting = true + this.startConnectors() + this.started = true + this.starting = false } - public stop(): void { - if (this.started === false) { - logger.warn(`${this.logPrefix()} is already stopped`); - return; + public stop (): void { + if (!this.started) { + logger.warn(`${this.logPrefix()} is already stopped`) + return } - if (this.stopping === true) { - logger.warn(`${this.logPrefix()} is already stopping`); - return; + if (this.stopping) { + logger.warn(`${this.logPrefix()} is already stopping`) + return } - this.stopping = true; - this.stopConnectors(); - this.started = false; - this.stopping = false; + this.stopping = true + this.stopConnectors() + this.started = false + this.stopping = false } - public startConnector(connectorId: number): void { - if (checkChargingStation(this.chargingStation, this.logPrefix(connectorId)) === false) { - return; + public startConnector (connectorId: number): void { + if (!checkChargingStation(this.chargingStation, this.logPrefix(connectorId))) { + return } - if (this.connectorsStatus.has(connectorId) === false) { - logger.error(`${this.logPrefix(connectorId)} starting on non existing connector`); - throw new BaseError(`Connector ${connectorId} does not exist`); + if (!this.connectorsStatus.has(connectorId)) { + logger.error(`${this.logPrefix(connectorId)} starting on non existing connector`) + throw new BaseError(`Connector ${connectorId} does not exist`) } if (this.connectorsStatus.get(connectorId)?.start === false) { - this.internalStartConnector(connectorId).catch(Constants.EMPTY_FUNCTION); + this.internalStartConnector(connectorId).catch(Constants.EMPTY_FUNCTION) } else if (this.connectorsStatus.get(connectorId)?.start === true) { - logger.warn(`${this.logPrefix(connectorId)} is already started on connector`); + logger.warn(`${this.logPrefix(connectorId)} is already started on connector`) } } - public stopConnector(connectorId: number): void { - if (this.connectorsStatus.has(connectorId) === false) { - logger.error(`${this.logPrefix(connectorId)} stopping on non existing connector`); - throw new BaseError(`Connector ${connectorId} does not exist`); + public stopConnector (connectorId: number): void { + if (!this.connectorsStatus.has(connectorId)) { + logger.error(`${this.logPrefix(connectorId)} stopping on non existing connector`) + throw new BaseError(`Connector ${connectorId} does not exist`) } if (this.connectorsStatus.get(connectorId)?.start === true) { - this.connectorsStatus.get(connectorId)!.start = false; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.start = false } else if (this.connectorsStatus.get(connectorId)?.start === false) { - logger.warn(`${this.logPrefix(connectorId)} is already stopped on connector`); + logger.warn(`${this.logPrefix(connectorId)} is already stopped on connector`) } } - private startConnectors(): void { + private startConnectors (): void { if ( this.connectorsStatus?.size > 0 && this.connectorsStatus.size !== this.chargingStation.getNumberOfConnectors() ) { - this.connectorsStatus.clear(); - this.initializeConnectorsStatus(); + this.connectorsStatus.clear() + this.initializeConnectorsStatus() } if (this.chargingStation.hasEvses) { for (const [evseId, evseStatus] of this.chargingStation.evses) { if (evseId > 0) { for (const connectorId of evseStatus.connectors.keys()) { - this.startConnector(connectorId); + this.startConnector(connectorId) } } } } else { for (const connectorId of this.chargingStation.connectors.keys()) { if (connectorId > 0) { - this.startConnector(connectorId); + this.startConnector(connectorId) } } } } - private stopConnectors(): void { + private stopConnectors (): void { if (this.chargingStation.hasEvses) { for (const [evseId, evseStatus] of this.chargingStation.evses) { if (evseId > 0) { for (const connectorId of evseStatus.connectors.keys()) { - this.stopConnector(connectorId); + this.stopConnector(connectorId) } } } } else { for (const connectorId of this.chargingStation.connectors.keys()) { if (connectorId > 0) { - this.stopConnector(connectorId); + this.stopConnector(connectorId) } } } } - private async internalStartConnector(connectorId: number): Promise { - this.setStartConnectorStatus(connectorId); + private async internalStartConnector (connectorId: number): Promise { + this.setStartConnectorStatus(connectorId) logger.info( `${this.logPrefix( - connectorId, + connectorId )} started on connector and will run for ${formatDurationMilliSeconds( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.connectorsStatus.get(connectorId)!.stopDate!.getTime() - - this.connectorsStatus.get(connectorId)!.startDate!.getTime(), - )}`, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.startDate!.getTime() + )}` + ) while (this.connectorsStatus.get(connectorId)?.start === true) { - await this.waitChargingStationServiceInitialization(connectorId); - await this.waitChargingStationAvailable(connectorId); - await this.waitConnectorAvailable(connectorId); + await this.waitChargingStationServiceInitialization(connectorId) + await this.waitChargingStationAvailable(connectorId) + await this.waitConnectorAvailable(connectorId) if (!this.canStartConnector(connectorId)) { - this.stopConnector(connectorId); - break; + this.stopConnector(connectorId) + break } const wait = secondsToMilliseconds( getRandomInteger( this.chargingStation.getAutomaticTransactionGeneratorConfiguration() .maxDelayBetweenTwoTransactions, this.chargingStation.getAutomaticTransactionGeneratorConfiguration() - .minDelayBetweenTwoTransactions, - ), - ); - logger.info(`${this.logPrefix(connectorId)} waiting for ${formatDurationMilliSeconds(wait)}`); - await sleep(wait); - const start = secureRandom(); + .minDelayBetweenTwoTransactions + ) + ) + logger.info(`${this.logPrefix(connectorId)} waiting for ${formatDurationMilliSeconds(wait)}`) + await sleep(wait) + const start = secureRandom() if ( start < this.chargingStation.getAutomaticTransactionGeneratorConfiguration().probabilityOfStart ) { - this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0 // Start transaction - const startResponse = await this.startTransaction(connectorId); + const startResponse = await this.startTransaction(connectorId) if (startResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { // Wait until end of transaction const waitTrxEnd = secondsToMilliseconds( getRandomInteger( this.chargingStation.getAutomaticTransactionGeneratorConfiguration().maxDuration, - this.chargingStation.getAutomaticTransactionGeneratorConfiguration().minDuration, - ), - ); + this.chargingStation.getAutomaticTransactionGeneratorConfiguration().minDuration + ) + ) logger.info( `${this.logPrefix( - connectorId, + connectorId )} transaction started with id ${this.chargingStation.getConnectorStatus(connectorId) - ?.transactionId} and will stop in ${formatDurationMilliSeconds(waitTrxEnd)}`, - ); - await sleep(waitTrxEnd); - await this.stopTransaction(connectorId); + ?.transactionId} and will stop in ${formatDurationMilliSeconds(waitTrxEnd)}` + ) + await sleep(waitTrxEnd) + await this.stopTransaction(connectorId) } } else { - ++this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions!; - ++this.connectorsStatus.get(connectorId)!.skippedTransactions!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions! + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.skippedTransactions! logger.info( `${this.logPrefix(connectorId)} skipped consecutively ${this.connectorsStatus.get( - connectorId, + connectorId )?.skippedConsecutiveTransactions}/${this.connectorsStatus.get(connectorId) - ?.skippedTransactions} transaction(s)`, - ); + ?.skippedTransactions} transaction(s)` + ) } - this.connectorsStatus.get(connectorId)!.lastRunDate = new Date(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.lastRunDate = new Date() } - this.connectorsStatus.get(connectorId)!.stoppedDate = new Date(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.stoppedDate = new Date() logger.info( `${this.logPrefix( - connectorId, + connectorId )} stopped on connector and lasted for ${formatDurationMilliSeconds( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.connectorsStatus.get(connectorId)!.stoppedDate!.getTime() - - this.connectorsStatus.get(connectorId)!.startDate!.getTime(), - )}`, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.startDate!.getTime() + )}` + ) logger.debug( `${this.logPrefix(connectorId)} connector status: %j`, - this.connectorsStatus.get(connectorId), - ); + this.connectorsStatus.get(connectorId) + ) } - private setStartConnectorStatus(connectorId: number): void { - this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0; + private setStartConnectorStatus (connectorId: number): void { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.skippedConsecutiveTransactions = 0 const previousRunDuration = - this.connectorsStatus.get(connectorId)?.startDate && - this.connectorsStatus.get(connectorId)?.lastRunDate - ? this.connectorsStatus.get(connectorId)!.lastRunDate!.getTime() - + this.connectorsStatus.get(connectorId)?.startDate != null && + this.connectorsStatus.get(connectorId)?.lastRunDate != null + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.lastRunDate!.getTime() - + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.connectorsStatus.get(connectorId)!.startDate!.getTime() - : 0; - this.connectorsStatus.get(connectorId)!.startDate = new Date(); + : 0 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.startDate = new Date() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.connectorsStatus.get(connectorId)!.stopDate = new Date( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.connectorsStatus.get(connectorId)!.startDate!.getTime() + hoursToMilliseconds( - this.chargingStation.getAutomaticTransactionGeneratorConfiguration().stopAfterHours, + this.chargingStation.getAutomaticTransactionGeneratorConfiguration().stopAfterHours ) - - previousRunDuration, - ); - this.connectorsStatus.get(connectorId)!.start = true; + previousRunDuration + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.connectorsStatus.get(connectorId)!.start = true } - private canStartConnector(connectorId: number): boolean { + private canStartConnector (connectorId: number): boolean { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (new Date() > this.connectorsStatus.get(connectorId)!.stopDate!) { - return false; + return false } - if (this.chargingStation.inAcceptedState() === false) { + if (!this.chargingStation.inAcceptedState()) { logger.error( `${this.logPrefix( - connectorId, - )} entered in transaction loop while the charging station is not in accepted state`, - ); - return false; + connectorId + )} entered in transaction loop while the charging station is not in accepted state` + ) + return false } - if (this.chargingStation.isChargingStationAvailable() === false) { + if (!this.chargingStation.isChargingStationAvailable()) { logger.info( `${this.logPrefix( - connectorId, - )} entered in transaction loop while the charging station is unavailable`, - ); - return false; + connectorId + )} entered in transaction loop while the charging station is unavailable` + ) + return false } - if (this.chargingStation.isConnectorAvailable(connectorId) === false) { + if (!this.chargingStation.isConnectorAvailable(connectorId)) { logger.info( `${this.logPrefix( - connectorId, - )} entered in transaction loop while the connector ${connectorId} is unavailable`, - ); - return false; + connectorId + )} entered in transaction loop while the connector ${connectorId} is unavailable` + ) + return false } - return true; + return true } - private async waitChargingStationServiceInitialization(connectorId: number): Promise { - let logged = false; - while (!this.chargingStation?.ocppRequestService) { + private async waitChargingStationServiceInitialization (connectorId: number): Promise { + let logged = false + while (this.chargingStation?.ocppRequestService == null) { if (!logged) { logger.info( `${this.logPrefix( - connectorId, - )} transaction loop waiting for charging station service to be initialized`, - ); - logged = true; + connectorId + )} transaction loop waiting for charging station service to be initialized` + ) + logged = true } - await sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME); + await sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME) } } - private async waitChargingStationAvailable(connectorId: number): Promise { - let logged = false; + private async waitChargingStationAvailable (connectorId: number): Promise { + let logged = false while (!this.chargingStation.isChargingStationAvailable()) { if (!logged) { logger.info( `${this.logPrefix( - connectorId, - )} transaction loop waiting for charging station to be available`, - ); - logged = true; + connectorId + )} transaction loop waiting for charging station to be available` + ) + logged = true } - await sleep(Constants.CHARGING_STATION_ATG_AVAILABILITY_TIME); + await sleep(Constants.CHARGING_STATION_ATG_AVAILABILITY_TIME) } } - private async waitConnectorAvailable(connectorId: number): Promise { - let logged = false; + private async waitConnectorAvailable (connectorId: number): Promise { + let logged = false while (!this.chargingStation.isConnectorAvailable(connectorId)) { if (!logged) { logger.info( `${this.logPrefix( - connectorId, - )} transaction loop waiting for connector ${connectorId} to be available`, - ); - logged = true; + connectorId + )} transaction loop waiting for connector ${connectorId} to be available` + ) + logged = true } - await sleep(Constants.CHARGING_STATION_ATG_AVAILABILITY_TIME); + await sleep(Constants.CHARGING_STATION_ATG_AVAILABILITY_TIME) } } - private initializeConnectorsStatus(): void { + private initializeConnectorsStatus (): void { if (this.chargingStation.hasEvses) { for (const [evseId, evseStatus] of this.chargingStation.evses) { if (evseId > 0) { for (const connectorId of evseStatus.connectors.keys()) { - this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId)); + this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId)) } } } } else { for (const connectorId of this.chargingStation.connectors.keys()) { if (connectorId > 0) { - this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId)); + this.connectorsStatus.set(connectorId, this.getConnectorStatus(connectorId)) } } } } - private getConnectorStatus(connectorId: number): Status { - const connectorStatus = this.chargingStation.getAutomaticTransactionGeneratorStatuses()?.[ - connectorId - ] - ? cloneObject( - this.chargingStation.getAutomaticTransactionGeneratorStatuses()![connectorId], + private getConnectorStatus (connectorId: number): Status { + const connectorStatus = + this.chargingStation.getAutomaticTransactionGeneratorStatuses()?.[connectorId] != null + ? cloneObject( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.chargingStation.getAutomaticTransactionGeneratorStatuses()![connectorId] ) - : undefined; - this.resetConnectorStatus(connectorStatus); + : undefined + this.resetConnectorStatus(connectorStatus) return ( connectorStatus ?? { start: false, @@ -380,144 +398,154 @@ export class AutomaticTransactionGenerator { acceptedStopTransactionRequests: 0, rejectedStopTransactionRequests: 0, skippedConsecutiveTransactions: 0, - skippedTransactions: 0, + skippedTransactions: 0 } - ); + ) } - private resetConnectorStatus(connectorStatus: Status | undefined): void { + private resetConnectorStatus (connectorStatus: Status | undefined): void { if (connectorStatus === undefined) { - return; + return } - delete connectorStatus?.startDate; - delete connectorStatus?.lastRunDate; - delete connectorStatus?.stopDate; - delete connectorStatus?.stoppedDate; + delete connectorStatus?.startDate + delete connectorStatus?.lastRunDate + delete connectorStatus?.stopDate + delete connectorStatus?.stoppedDate if ( !this.started && - (connectorStatus.start === true || - this.chargingStation.getAutomaticTransactionGeneratorConfiguration().enable === false) + (connectorStatus.start || + !this.chargingStation.getAutomaticTransactionGeneratorConfiguration().enable) ) { - connectorStatus.start = false; + connectorStatus.start = false } } - private async startTransaction( - connectorId: number, + private async startTransaction ( + connectorId: number ): Promise { - const measureId = 'StartTransaction with ATG'; - const beginId = PerformanceStatistics.beginMeasure(measureId); - let startResponse: StartTransactionResponse | undefined; + const measureId = 'StartTransaction with ATG' + const beginId = PerformanceStatistics.beginMeasure(measureId) + let startResponse: StartTransactionResponse | undefined if (this.chargingStation.hasIdTags()) { const idTag = IdTagsCache.getInstance().getIdTag( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.chargingStation.getAutomaticTransactionGeneratorConfiguration().idTagDistribution!, this.chargingStation, - connectorId, - ); + connectorId + ) const startTransactionLogMsg = `${this.logPrefix( - connectorId, - )} start transaction with an idTag '${idTag}'`; + connectorId + )} start transaction with an idTag '${idTag}'` if (this.getRequireAuthorize()) { - ++this.connectorsStatus.get(connectorId)!.authorizeRequests!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.authorizeRequests! if (await isIdTagAuthorized(this.chargingStation, connectorId, idTag)) { - ++this.connectorsStatus.get(connectorId)!.acceptedAuthorizeRequests!; - logger.info(startTransactionLogMsg); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.acceptedAuthorizeRequests! + logger.info(startTransactionLogMsg) // Start transaction startResponse = await this.chargingStation.ocppRequestService.requestHandler< - StartTransactionRequest, - StartTransactionResponse + StartTransactionRequest, + StartTransactionResponse >(this.chargingStation, RequestCommand.START_TRANSACTION, { connectorId, - idTag, - }); - this.handleStartTransactionResponse(connectorId, startResponse); - PerformanceStatistics.endMeasure(measureId, beginId); - return startResponse; + idTag + }) + this.handleStartTransactionResponse(connectorId, startResponse) + PerformanceStatistics.endMeasure(measureId, beginId) + return startResponse } - ++this.connectorsStatus.get(connectorId)!.rejectedAuthorizeRequests!; - PerformanceStatistics.endMeasure(measureId, beginId); - return startResponse; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.rejectedAuthorizeRequests! + PerformanceStatistics.endMeasure(measureId, beginId) + return startResponse } - logger.info(startTransactionLogMsg); + logger.info(startTransactionLogMsg) // Start transaction startResponse = await this.chargingStation.ocppRequestService.requestHandler< - StartTransactionRequest, - StartTransactionResponse + StartTransactionRequest, + StartTransactionResponse >(this.chargingStation, RequestCommand.START_TRANSACTION, { connectorId, - idTag, - }); - this.handleStartTransactionResponse(connectorId, startResponse); - PerformanceStatistics.endMeasure(measureId, beginId); - return startResponse; + idTag + }) + this.handleStartTransactionResponse(connectorId, startResponse) + PerformanceStatistics.endMeasure(measureId, beginId) + return startResponse } - logger.info(`${this.logPrefix(connectorId)} start transaction without an idTag`); + logger.info(`${this.logPrefix(connectorId)} start transaction without an idTag`) startResponse = await this.chargingStation.ocppRequestService.requestHandler< - StartTransactionRequest, - StartTransactionResponse - >(this.chargingStation, RequestCommand.START_TRANSACTION, { connectorId }); - this.handleStartTransactionResponse(connectorId, startResponse); - PerformanceStatistics.endMeasure(measureId, beginId); - return startResponse; + StartTransactionRequest, + StartTransactionResponse + >(this.chargingStation, RequestCommand.START_TRANSACTION, { connectorId }) + this.handleStartTransactionResponse(connectorId, startResponse) + PerformanceStatistics.endMeasure(measureId, beginId) + return startResponse } - private async stopTransaction( + private async stopTransaction ( connectorId: number, - reason = StopTransactionReason.LOCAL, + reason = StopTransactionReason.LOCAL ): Promise { - const measureId = 'StopTransaction with ATG'; - const beginId = PerformanceStatistics.beginMeasure(measureId); - let stopResponse: StopTransactionResponse | undefined; + const measureId = 'StopTransaction with ATG' + const beginId = PerformanceStatistics.beginMeasure(measureId) + let stopResponse: StopTransactionResponse | undefined if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) { logger.info( `${this.logPrefix( - connectorId, + connectorId )} stop transaction with id ${this.chargingStation.getConnectorStatus(connectorId) - ?.transactionId}`, - ); - stopResponse = await this.chargingStation.stopTransactionOnConnector(connectorId, reason); - ++this.connectorsStatus.get(connectorId)!.stopTransactionRequests!; + ?.transactionId}` + ) + stopResponse = await this.chargingStation.stopTransactionOnConnector(connectorId, reason) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.stopTransactionRequests! if (stopResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { - ++this.connectorsStatus.get(connectorId)!.acceptedStopTransactionRequests!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.acceptedStopTransactionRequests! } else { - ++this.connectorsStatus.get(connectorId)!.rejectedStopTransactionRequests!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.rejectedStopTransactionRequests! } } else { - const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId; + const transactionId = this.chargingStation.getConnectorStatus(connectorId)?.transactionId logger.debug( `${this.logPrefix(connectorId)} stopping a not started transaction${ !isNullOrUndefined(transactionId) ? ` with id ${transactionId}` : '' - }`, - ); + }` + ) } - PerformanceStatistics.endMeasure(measureId, beginId); - return stopResponse; + PerformanceStatistics.endMeasure(measureId, beginId) + return stopResponse } - private getRequireAuthorize(): boolean { + private getRequireAuthorize (): boolean { return ( this.chargingStation.getAutomaticTransactionGeneratorConfiguration()?.requireAuthorize ?? true - ); + ) } - private logPrefix = (connectorId?: number): string => { + private readonly logPrefix = (connectorId?: number): string => { return logPrefix( ` ${this.chargingStation.stationInfo.chargingStationId} | ATG${ !isNullOrUndefined(connectorId) ? ` on connector #${connectorId}` : '' - }:`, - ); - }; + }:` + ) + } - private handleStartTransactionResponse( + private handleStartTransactionResponse ( connectorId: number, - startResponse: StartTransactionResponse, + startResponse: StartTransactionResponse ): void { - ++this.connectorsStatus.get(connectorId)!.startTransactionRequests!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.startTransactionRequests! if (startResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { - ++this.connectorsStatus.get(connectorId)!.acceptedStartTransactionRequests!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.acceptedStartTransactionRequests! } else { - logger.warn(`${this.logPrefix(connectorId)} start transaction rejected`); - ++this.connectorsStatus.get(connectorId)!.rejectedStartTransactionRequests!; + logger.warn(`${this.logPrefix(connectorId)} start transaction rejected`) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.connectorsStatus.get(connectorId)!.rejectedStartTransactionRequests! } } } diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index b84de928..731cc477 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -1,19 +1,19 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { EventEmitter } from 'node:events'; -import { dirname, extname, join } from 'node:path'; -import process, { exit } from 'node:process'; -import { fileURLToPath } from 'node:url'; +import { EventEmitter } from 'node:events' +import { dirname, extname, join } from 'node:path' +import process, { exit } from 'node:process' +import { fileURLToPath } from 'node:url' -import chalk from 'chalk'; -import { availableParallelism } from 'poolifier'; +import chalk from 'chalk' +import { availableParallelism } from 'poolifier' -import { waitChargingStationEvents } from './Helpers.js'; -import type { AbstractUIServer } from './ui-server/AbstractUIServer.js'; -import { UIServerFactory } from './ui-server/UIServerFactory.js'; -import { version } from '../../package.json'; -import { BaseError } from '../exception/index.js'; -import { type Storage, StorageFactory } from '../performance/index.js'; +import { waitChargingStationEvents } from './Helpers.js' +import type { AbstractUIServer } from './ui-server/AbstractUIServer.js' +import { UIServerFactory } from './ui-server/UIServerFactory.js' +import { version } from '../../package.json' +import { BaseError } from '../exception/index.js' +import { type Storage, StorageFactory } from '../performance/index.js' import { type ChargingStationData, type ChargingStationWorkerData, @@ -26,8 +26,8 @@ import { type Statistics, type StorageConfiguration, type UIServerConfiguration, - type WorkerConfiguration, -} from '../types/index.js'; + type WorkerConfiguration +} from '../types/index.js' import { Configuration, Constants, @@ -38,104 +38,109 @@ import { isNotEmptyArray, isNullOrUndefined, logPrefix, - logger, -} from '../utils/index.js'; -import { type WorkerAbstract, WorkerFactory } from '../worker/index.js'; + logger +} from '../utils/index.js' +import { type WorkerAbstract, WorkerFactory } from '../worker/index.js' -const moduleName = 'Bootstrap'; +const moduleName = 'Bootstrap' enum exitCodes { succeeded = 0, missingChargingStationsConfiguration = 1, noChargingStationTemplates = 2, - gracefulShutdownError = 3, + gracefulShutdownError = 3 } export class Bootstrap extends EventEmitter { - private static instance: Bootstrap | null = null; - public numberOfChargingStations!: number; - public numberOfChargingStationTemplates!: number; - private workerImplementation?: WorkerAbstract; - private readonly uiServer?: AbstractUIServer; - private storage?: Storage; - private numberOfStartedChargingStations!: number; - private readonly version: string = version; - private initializedCounters: boolean; - private started: boolean; - private starting: boolean; - private stopping: boolean; + private static instance: Bootstrap | null = null + public numberOfChargingStations!: number + public numberOfChargingStationTemplates!: number + private workerImplementation?: WorkerAbstract + private readonly uiServer?: AbstractUIServer + private storage?: Storage + private numberOfStartedChargingStations!: number + private readonly version: string = version + private initializedCounters: boolean + private started: boolean + private starting: boolean + private stopping: boolean - private constructor() { - super(); + private constructor () { + super() for (const signal of ['SIGINT', 'SIGQUIT', 'SIGTERM']) { - process.on(signal, this.gracefulShutdown.bind(this)); + process.on(signal, this.gracefulShutdown.bind(this)) } // Enable unconditionally for now - handleUnhandledRejection(); - handleUncaughtException(); - this.started = false; - this.starting = false; - this.stopping = false; - this.initializedCounters = false; - this.initializeCounters(); + handleUnhandledRejection() + handleUncaughtException() + this.started = false + this.starting = false + this.stopping = false + this.initializedCounters = false + this.initializeCounters() this.uiServer = UIServerFactory.getUIServerImplementation( - Configuration.getConfigurationSection(ConfigurationSection.uiServer), - ); - Configuration.configurationChangeCallback = async () => Bootstrap.getInstance().restart(false); + Configuration.getConfigurationSection(ConfigurationSection.uiServer) + ) + Configuration.configurationChangeCallback = async () => { + await Bootstrap.getInstance().restart(false) + } } - public static getInstance(): Bootstrap { + public static getInstance (): Bootstrap { if (Bootstrap.instance === null) { - Bootstrap.instance = new Bootstrap(); + Bootstrap.instance = new Bootstrap() } - return Bootstrap.instance; + return Bootstrap.instance } - public async start(): Promise { - if (this.started === false) { - if (this.starting === false) { - this.starting = true; - this.on(ChargingStationWorkerMessageEvents.started, this.workerEventStarted); - this.on(ChargingStationWorkerMessageEvents.stopped, this.workerEventStopped); - this.on(ChargingStationWorkerMessageEvents.updated, this.workerEventUpdated); + public async start (): Promise { + if (!this.started) { + if (!this.starting) { + this.starting = true + this.on(ChargingStationWorkerMessageEvents.started, this.workerEventStarted) + this.on(ChargingStationWorkerMessageEvents.stopped, this.workerEventStopped) + this.on(ChargingStationWorkerMessageEvents.updated, this.workerEventUpdated) this.on( ChargingStationWorkerMessageEvents.performanceStatistics, - this.workerEventPerformanceStatistics, - ); - this.initializeCounters(); + this.workerEventPerformanceStatistics + ) + this.initializeCounters() const workerConfiguration = Configuration.getConfigurationSection( - ConfigurationSection.worker, - ); - this.initializeWorkerImplementation(workerConfiguration); - await this.workerImplementation?.start(); + ConfigurationSection.worker + ) + this.initializeWorkerImplementation(workerConfiguration) + await this.workerImplementation?.start() const performanceStorageConfiguration = Configuration.getConfigurationSection( - ConfigurationSection.performanceStorage, - ); + ConfigurationSection.performanceStorage + ) if (performanceStorageConfiguration.enabled === true) { this.storage = StorageFactory.getStorage( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion performanceStorageConfiguration.type!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion performanceStorageConfiguration.uri!, - this.logPrefix(), - ); - await this.storage?.open(); + this.logPrefix() + ) + await this.storage?.open() } Configuration.getConfigurationSection(ConfigurationSection.uiServer) - .enabled === true && this.uiServer?.start(); + .enabled === true && this.uiServer?.start() // Start ChargingStation object instance in worker thread + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion for (const stationTemplateUrl of Configuration.getStationTemplateUrls()!) { try { - const nbStations = stationTemplateUrl.numberOfStations ?? 0; + const nbStations = stationTemplateUrl.numberOfStations ?? 0 for (let index = 1; index <= nbStations; index++) { - await this.startChargingStation(index, stationTemplateUrl); + await this.startChargingStation(index, stationTemplateUrl) } } catch (error) { console.error( chalk.red( - `Error at starting charging station with template file ${stationTemplateUrl.file}: `, + `Error at starting charging station with template file ${stationTemplateUrl.file}: ` ), - error, - ); + error + ) } } console.info( @@ -154,244 +159,246 @@ export class Bootstrap extends EventEmitter { !isNullOrUndefined(this.workerImplementation?.maxElementsPerWorker) ? ` (${this.workerImplementation?.maxElementsPerWorker} charging station(s) per worker)` : '' - }`, - ), - ); + }` + ) + ) Configuration.workerDynamicPoolInUse() && console.warn( chalk.yellow( - 'Charging stations simulator is using dynamic pool mode. This is an experimental feature with known issues.\nPlease consider using fixed pool or worker set mode instead', - ), - ); - console.info(chalk.green('Worker set/pool information:'), this.workerImplementation?.info); - this.started = true; - this.starting = false; + 'Charging stations simulator is using dynamic pool mode. This is an experimental feature with known issues.\nPlease consider using fixed pool or worker set mode instead' + ) + ) + console.info(chalk.green('Worker set/pool information:'), this.workerImplementation?.info) + this.started = true + this.starting = false } else { - console.error(chalk.red('Cannot start an already starting charging stations simulator')); + console.error(chalk.red('Cannot start an already starting charging stations simulator')) } } else { - console.error(chalk.red('Cannot start an already started charging stations simulator')); + console.error(chalk.red('Cannot start an already started charging stations simulator')) } } - public async stop(stopChargingStations = true): Promise { - if (this.started === true) { - if (this.stopping === false) { - this.stopping = true; - if (stopChargingStations === true) { + public async stop (stopChargingStations = true): Promise { + if (this.started) { + if (!this.stopping) { + this.stopping = true + if (stopChargingStations) { await this.uiServer?.sendInternalRequest( this.uiServer.buildProtocolRequest( generateUUID(), ProcedureName.STOP_CHARGING_STATION, - Constants.EMPTY_FROZEN_OBJECT, - ), - ); + Constants.EMPTY_FROZEN_OBJECT + ) + ) try { - await this.waitChargingStationsStopped(); + await this.waitChargingStationsStopped() } catch (error) { - console.error(chalk.red('Error while waiting for charging stations to stop: '), error); + console.error(chalk.red('Error while waiting for charging stations to stop: '), error) } } - await this.workerImplementation?.stop(); - delete this.workerImplementation; - this.removeAllListeners(); - await this.storage?.close(); - delete this.storage; - this.resetCounters(); - this.initializedCounters = false; - this.started = false; - this.stopping = false; + await this.workerImplementation?.stop() + delete this.workerImplementation + this.removeAllListeners() + await this.storage?.close() + delete this.storage + this.resetCounters() + this.initializedCounters = false + this.started = false + this.stopping = false } else { - console.error(chalk.red('Cannot stop an already stopping charging stations simulator')); + console.error(chalk.red('Cannot stop an already stopping charging stations simulator')) } } else { - console.error(chalk.red('Cannot stop an already stopped charging stations simulator')); + console.error(chalk.red('Cannot stop an already stopped charging stations simulator')) } } - public async restart(stopChargingStations?: boolean): Promise { - await this.stop(stopChargingStations); + public async restart (stopChargingStations?: boolean): Promise { + await this.stop(stopChargingStations) Configuration.getConfigurationSection(ConfigurationSection.uiServer) - .enabled === false && this.uiServer?.stop(); - await this.start(); + .enabled === false && this.uiServer?.stop() + await this.start() } - private async waitChargingStationsStopped(): Promise { - return new Promise((resolve, reject) => { + private async waitChargingStationsStopped (): Promise { + return await new Promise((resolve, reject) => { const waitTimeout = setTimeout(() => { const message = `Timeout ${formatDurationMilliSeconds( - Constants.STOP_CHARGING_STATIONS_TIMEOUT, - )} reached at stopping charging stations`; - console.warn(chalk.yellow(message)); - reject(new Error(message)); - }, Constants.STOP_CHARGING_STATIONS_TIMEOUT); + Constants.STOP_CHARGING_STATIONS_TIMEOUT + )} reached at stopping charging stations` + console.warn(chalk.yellow(message)) + reject(new Error(message)) + }, Constants.STOP_CHARGING_STATIONS_TIMEOUT) waitChargingStationEvents( this, ChargingStationWorkerMessageEvents.stopped, - this.numberOfChargingStations, + this.numberOfChargingStations ) .then(() => { - resolve('Charging stations stopped'); + resolve('Charging stations stopped') }) .catch(reject) .finally(() => { - clearTimeout(waitTimeout); - }); - }); + clearTimeout(waitTimeout) + }) + }) } - private initializeWorkerImplementation(workerConfiguration: WorkerConfiguration): void { - let elementsPerWorker: number | undefined; + private initializeWorkerImplementation (workerConfiguration: WorkerConfiguration): void { + let elementsPerWorker: number | undefined switch (workerConfiguration?.elementsPerWorker) { case 'auto': elementsPerWorker = this.numberOfChargingStations > availableParallelism() ? Math.round(this.numberOfChargingStations / (availableParallelism() * 1.5)) - : 1; - break; + : 1 + break case 'all': - elementsPerWorker = this.numberOfChargingStations; - break; + elementsPerWorker = this.numberOfChargingStations + break } this.workerImplementation = WorkerFactory.getWorkerImplementation( join( dirname(fileURLToPath(import.meta.url)), - `ChargingStationWorker${extname(fileURLToPath(import.meta.url))}`, + `ChargingStationWorker${extname(fileURLToPath(import.meta.url))}` ), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion workerConfiguration.processType!, { workerStartDelay: workerConfiguration.startDelay, elementStartDelay: workerConfiguration.elementStartDelay, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion poolMaxSize: workerConfiguration.poolMaxSize!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion poolMinSize: workerConfiguration.poolMinSize!, elementsPerWorker: elementsPerWorker ?? (workerConfiguration.elementsPerWorker as number), poolOptions: { messageHandler: this.messageHandler.bind(this) as (message: unknown) => void, - workerOptions: { resourceLimits: workerConfiguration.resourceLimits }, - }, - }, - ); + workerOptions: { resourceLimits: workerConfiguration.resourceLimits } + } + } + ) } - private messageHandler( - msg: ChargingStationWorkerMessage, + private messageHandler ( + msg: ChargingStationWorkerMessage ): void { // logger.debug( // `${this.logPrefix()} ${moduleName}.messageHandler: Worker channel message received: ${JSON.stringify( // msg, // undefined, - // 2, - // )}`, - // ); + // 2 + // )}` + // ) try { switch (msg.event) { case ChargingStationWorkerMessageEvents.started: - this.emit(ChargingStationWorkerMessageEvents.started, msg.data as ChargingStationData); - break; + this.emit(ChargingStationWorkerMessageEvents.started, msg.data as ChargingStationData) + break case ChargingStationWorkerMessageEvents.stopped: - this.emit(ChargingStationWorkerMessageEvents.stopped, msg.data as ChargingStationData); - break; + this.emit(ChargingStationWorkerMessageEvents.stopped, msg.data as ChargingStationData) + break case ChargingStationWorkerMessageEvents.updated: - this.emit(ChargingStationWorkerMessageEvents.updated, msg.data as ChargingStationData); - break; + this.emit(ChargingStationWorkerMessageEvents.updated, msg.data as ChargingStationData) + break case ChargingStationWorkerMessageEvents.performanceStatistics: this.emit( ChargingStationWorkerMessageEvents.performanceStatistics, - msg.data as Statistics, - ); - break; + msg.data as Statistics + ) + break case ChargingStationWorkerMessageEvents.startWorkerElementError: logger.error( `${this.logPrefix()} ${moduleName}.messageHandler: Error occured while starting worker element:`, - msg.data, - ); - this.emit(ChargingStationWorkerMessageEvents.startWorkerElementError, msg.data); - break; + msg.data + ) + this.emit(ChargingStationWorkerMessageEvents.startWorkerElementError, msg.data) + break case ChargingStationWorkerMessageEvents.startedWorkerElement: - break; + break default: throw new BaseError( `Unknown charging station worker event: '${ msg.event - }' received with data: ${JSON.stringify(msg.data, undefined, 2)}`, - ); + }' received with data: ${JSON.stringify(msg.data, undefined, 2)}` + ) } } catch (error) { logger.error( `${this.logPrefix()} ${moduleName}.messageHandler: Error occurred while handling '${ msg.event }' event:`, - error, - ); + error + ) } } - private workerEventStarted = (data: ChargingStationData) => { - this.uiServer?.chargingStations.set(data.stationInfo.hashId, data); - ++this.numberOfStartedChargingStations; + private readonly workerEventStarted = (data: ChargingStationData): void => { + this.uiServer?.chargingStations.set(data.stationInfo.hashId, data) + ++this.numberOfStartedChargingStations logger.info( `${this.logPrefix()} ${moduleName}.workerEventStarted: Charging station ${ data.stationInfo.chargingStationId } (hashId: ${data.stationInfo.hashId}) started (${ this.numberOfStartedChargingStations - } started from ${this.numberOfChargingStations})`, - ); - }; + } started from ${this.numberOfChargingStations})` + ) + } - private workerEventStopped = (data: ChargingStationData) => { - this.uiServer?.chargingStations.set(data.stationInfo.hashId, data); - --this.numberOfStartedChargingStations; + private readonly workerEventStopped = (data: ChargingStationData): void => { + this.uiServer?.chargingStations.set(data.stationInfo.hashId, data) + --this.numberOfStartedChargingStations logger.info( `${this.logPrefix()} ${moduleName}.workerEventStopped: Charging station ${ data.stationInfo.chargingStationId } (hashId: ${data.stationInfo.hashId}) stopped (${ this.numberOfStartedChargingStations - } started from ${this.numberOfChargingStations})`, - ); - }; + } started from ${this.numberOfChargingStations})` + ) + } - private workerEventUpdated = (data: ChargingStationData) => { - this.uiServer?.chargingStations.set(data.stationInfo.hashId, data); - }; + private readonly workerEventUpdated = (data: ChargingStationData): void => { + this.uiServer?.chargingStations.set(data.stationInfo.hashId, data) + } - private workerEventPerformanceStatistics = (data: Statistics) => { - this.storage?.storePerformanceStatistics(data) as void; - }; + private readonly workerEventPerformanceStatistics = (data: Statistics): void => { + this.storage?.storePerformanceStatistics(data) as undefined + } - private initializeCounters() { - if (this.initializedCounters === false) { - this.resetCounters(); - const stationTemplateUrls = Configuration.getStationTemplateUrls()!; + private initializeCounters (): void { + if (!this.initializedCounters) { + this.resetCounters() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const stationTemplateUrls = Configuration.getStationTemplateUrls()! if (isNotEmptyArray(stationTemplateUrls)) { - this.numberOfChargingStationTemplates = stationTemplateUrls.length; + this.numberOfChargingStationTemplates = stationTemplateUrls.length for (const stationTemplateUrl of stationTemplateUrls) { - this.numberOfChargingStations += stationTemplateUrl.numberOfStations ?? 0; + this.numberOfChargingStations += stationTemplateUrl.numberOfStations ?? 0 } } else { console.warn( - chalk.yellow("'stationTemplateUrls' not defined or empty in configuration, exiting"), - ); - exit(exitCodes.missingChargingStationsConfiguration); + chalk.yellow("'stationTemplateUrls' not defined or empty in configuration, exiting") + ) + exit(exitCodes.missingChargingStationsConfiguration) } if (this.numberOfChargingStations === 0) { - console.warn( - chalk.yellow('No charging station template enabled in configuration, exiting'), - ); - exit(exitCodes.noChargingStationTemplates); + console.warn(chalk.yellow('No charging station template enabled in configuration, exiting')) + exit(exitCodes.noChargingStationTemplates) } - this.initializedCounters = true; + this.initializedCounters = true } } - private resetCounters(): void { - this.numberOfChargingStationTemplates = 0; - this.numberOfChargingStations = 0; - this.numberOfStartedChargingStations = 0; + private resetCounters (): void { + this.numberOfChargingStationTemplates = 0 + this.numberOfChargingStations = 0 + this.numberOfStartedChargingStations = 0 } - private async startChargingStation( + private async startChargingStation ( index: number, - stationTemplateUrl: StationTemplateUrl, + stationTemplateUrl: StationTemplateUrl ): Promise { await this.workerImplementation?.addElement({ index, @@ -399,32 +406,32 @@ export class Bootstrap extends EventEmitter { dirname(fileURLToPath(import.meta.url)), 'assets', 'station-templates', - stationTemplateUrl.file, - ), - }); + stationTemplateUrl.file + ) + }) } - private gracefulShutdown(): void { + private gracefulShutdown (): void { this.stop() .then(() => { - console.info(`${chalk.green('Graceful shutdown')}`); - this.uiServer?.stop(); + console.info(`${chalk.green('Graceful shutdown')}`) + this.uiServer?.stop() // stop() asks for charging stations to stop by default this.waitChargingStationsStopped() .then(() => { - exit(exitCodes.succeeded); + exit(exitCodes.succeeded) }) .catch(() => { - exit(exitCodes.gracefulShutdownError); - }); + exit(exitCodes.gracefulShutdownError) + }) }) .catch((error) => { - console.error(chalk.red('Error while shutdowning charging stations simulator: '), error); - exit(exitCodes.gracefulShutdownError); - }); + console.error(chalk.red('Error while shutdowning charging stations simulator: '), error) + exit(exitCodes.gracefulShutdownError) + }) } - private logPrefix = (): string => { - return logPrefix(' Bootstrap |'); - }; + private readonly logPrefix = (): string => { + return logPrefix(' Bootstrap |') + } } diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 892a6704..6df137f8 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -1,24 +1,24 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { createHash } from 'node:crypto'; -import { EventEmitter } from 'node:events'; -import { type FSWatcher, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { URL } from 'node:url'; -import { parentPort } from 'node:worker_threads'; - -import { millisecondsToSeconds, secondsToMilliseconds } from 'date-fns'; -import merge from 'just-merge'; -import { type RawData, WebSocket } from 'ws'; - -import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator.js'; -import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel.js'; +import { createHash } from 'node:crypto' +import { EventEmitter } from 'node:events' +import { type FSWatcher, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { URL } from 'node:url' +import { parentPort } from 'node:worker_threads' + +import { millisecondsToSeconds, secondsToMilliseconds } from 'date-fns' +import merge from 'just-merge' +import { type RawData, WebSocket } from 'ws' + +import { AutomaticTransactionGenerator } from './AutomaticTransactionGenerator.js' +import { ChargingStationWorkerBroadcastChannel } from './broadcast-channel/ChargingStationWorkerBroadcastChannel.js' import { addConfigurationKey, deleteConfigurationKey, getConfigurationKey, - setConfigurationKeyValue, -} from './ConfigurationKeyUtils.js'; + setConfigurationKeyValue +} from './ConfigurationKeyUtils.js' import { buildConnectorsMap, checkChargingStation, @@ -42,9 +42,9 @@ import { initializeConnectorsMapStatus, propagateSerialNumber, stationTemplateToStationInfo, - warnTemplateKeysDeprecation, -} from './Helpers.js'; -import { IdTagsCache } from './IdTagsCache.js'; + warnTemplateKeysDeprecation +} from './Helpers.js' +import { IdTagsCache } from './IdTagsCache.js' import { OCPP16IncomingRequestService, OCPP16RequestService, @@ -58,11 +58,11 @@ import { buildStatusNotificationRequest, buildTransactionEndMeterValue, getMessageTypeString, - sendAndSetConnectorStatus, -} from './ocpp/index.js'; -import { SharedLRUCache } from './SharedLRUCache.js'; -import { BaseError, OCPPError } from '../exception/index.js'; -import { PerformanceStatistics } from '../performance/index.js'; + sendAndSetConnectorStatus +} from './ocpp/index.js' +import { SharedLRUCache } from './SharedLRUCache.js' +import { BaseError, OCPPError } from '../exception/index.js' +import { PerformanceStatistics } from '../performance/index.js' import { type AutomaticTransactionGeneratorConfiguration, AvailabilityType, @@ -108,16 +108,16 @@ import { type Status, type StatusNotificationRequest, type StatusNotificationResponse, - StopTransactionReason, + type StopTransactionReason, type StopTransactionRequest, type StopTransactionResponse, SupervisionUrlDistribution, SupportedFeatureProfiles, - Voltage, + type Voltage, type WSError, WebSocketCloseEventStatusCode, - type WsOptions, -} from '../types/index.js'; + type WsOptions +} from '../types/index.js' import { ACElectricUtils, AsyncLock, @@ -151,353 +151,362 @@ import { roundTo, secureRandom, sleep, - watchJsonFile, -} from '../utils/index.js'; + watchJsonFile +} from '../utils/index.js' export class ChargingStation extends EventEmitter { - public readonly index: number; - public readonly templateFile: string; - public stationInfo!: ChargingStationInfo; - public started: boolean; - public starting: boolean; - public idTagsCache: IdTagsCache; - public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined; - public ocppConfiguration!: ChargingStationOcppConfiguration | undefined; - public wsConnection: WebSocket | null; - public readonly connectors: Map; - public readonly evses: Map; - public readonly requests: Map; - public performanceStatistics!: PerformanceStatistics | undefined; - public heartbeatSetInterval?: NodeJS.Timeout; - public ocppRequestService!: OCPPRequestService; - public bootNotificationRequest!: BootNotificationRequest; - public bootNotificationResponse!: BootNotificationResponse | undefined; - public powerDivider!: number; - private stopping: boolean; - private configurationFile!: string; - private configurationFileHash!: string; - private connectorsConfigurationHash!: string; - private evsesConfigurationHash!: string; - private automaticTransactionGeneratorConfiguration?: AutomaticTransactionGeneratorConfiguration; - private ocppIncomingRequestService!: OCPPIncomingRequestService; - private readonly messageBuffer: Set; - private configuredSupervisionUrl!: URL; - private autoReconnectRetryCount: number; - private templateFileWatcher!: FSWatcher | undefined; - private templateFileHash!: string; - private readonly sharedLRUCache: SharedLRUCache; - private webSocketPingSetInterval?: NodeJS.Timeout; - private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel; - private flushMessageBufferSetInterval?: NodeJS.Timeout; - - constructor(index: number, templateFile: string) { - super(); - this.started = false; - this.starting = false; - this.stopping = false; - this.wsConnection = null; - this.autoReconnectRetryCount = 0; - this.index = index; - this.templateFile = templateFile; - this.connectors = new Map(); - this.evses = new Map(); - this.requests = new Map(); - this.messageBuffer = new Set(); - this.sharedLRUCache = SharedLRUCache.getInstance(); - this.idTagsCache = IdTagsCache.getInstance(); - this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this); + public readonly index: number + public readonly templateFile: string + public stationInfo!: ChargingStationInfo + public started: boolean + public starting: boolean + public idTagsCache: IdTagsCache + public automaticTransactionGenerator!: AutomaticTransactionGenerator | undefined + public ocppConfiguration!: ChargingStationOcppConfiguration | undefined + public wsConnection: WebSocket | null + public readonly connectors: Map + public readonly evses: Map + public readonly requests: Map + public performanceStatistics!: PerformanceStatistics | undefined + public heartbeatSetInterval?: NodeJS.Timeout + public ocppRequestService!: OCPPRequestService + public bootNotificationRequest!: BootNotificationRequest + public bootNotificationResponse!: BootNotificationResponse | undefined + public powerDivider!: number + private stopping: boolean + private configurationFile!: string + private configurationFileHash!: string + private connectorsConfigurationHash!: string + private evsesConfigurationHash!: string + private automaticTransactionGeneratorConfiguration?: AutomaticTransactionGeneratorConfiguration + private ocppIncomingRequestService!: OCPPIncomingRequestService + private readonly messageBuffer: Set + private configuredSupervisionUrl!: URL + private autoReconnectRetryCount: number + private templateFileWatcher!: FSWatcher | undefined + private templateFileHash!: string + private readonly sharedLRUCache: SharedLRUCache + private webSocketPingSetInterval?: NodeJS.Timeout + private readonly chargingStationWorkerBroadcastChannel: ChargingStationWorkerBroadcastChannel + private flushMessageBufferSetInterval?: NodeJS.Timeout + + constructor (index: number, templateFile: string) { + super() + this.started = false + this.starting = false + this.stopping = false + this.wsConnection = null + this.autoReconnectRetryCount = 0 + this.index = index + this.templateFile = templateFile + this.connectors = new Map() + this.evses = new Map() + this.requests = new Map() + this.messageBuffer = new Set() + this.sharedLRUCache = SharedLRUCache.getInstance() + this.idTagsCache = IdTagsCache.getInstance() + this.chargingStationWorkerBroadcastChannel = new ChargingStationWorkerBroadcastChannel(this) this.on(ChargingStationEvents.started, () => { - parentPort?.postMessage(buildStartedMessage(this)); - }); + parentPort?.postMessage(buildStartedMessage(this)) + }) this.on(ChargingStationEvents.stopped, () => { - parentPort?.postMessage(buildStoppedMessage(this)); - }); + parentPort?.postMessage(buildStoppedMessage(this)) + }) this.on(ChargingStationEvents.updated, () => { - parentPort?.postMessage(buildUpdatedMessage(this)); - }); + parentPort?.postMessage(buildUpdatedMessage(this)) + }) - this.initialize(); + this.initialize() } - public get hasEvses(): boolean { - return this.connectors.size === 0 && this.evses.size > 0; + public get hasEvses (): boolean { + return this.connectors.size === 0 && this.evses.size > 0 } - private get wsConnectionUrl(): URL { + private get wsConnectionUrl (): URL { return new URL( `${ this.stationInfo?.supervisionUrlOcppConfiguration === true && isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion isNotEmptyString(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)?.value) - ? getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)!.value + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)!.value : this.configuredSupervisionUrl.href - }/${this.stationInfo.chargingStationId}`, - ); + }/${this.stationInfo.chargingStationId}` + ) } public logPrefix = (): string => { if (isNotEmptyString(this?.stationInfo?.chargingStationId)) { - return logPrefix(` ${this?.stationInfo?.chargingStationId} |`); + return logPrefix(` ${this?.stationInfo?.chargingStationId} |`) } - let stationTemplate: ChargingStationTemplate | undefined; + let stationTemplate: ChargingStationTemplate | undefined try { stationTemplate = JSON.parse( - readFileSync(this.templateFile, 'utf8'), - ) as ChargingStationTemplate; + readFileSync(this.templateFile, 'utf8') + ) as ChargingStationTemplate } catch { - stationTemplate = undefined; + stationTemplate = undefined } - return logPrefix(` ${getChargingStationId(this.index, stationTemplate)} |`); - }; + return logPrefix(` ${getChargingStationId(this.index, stationTemplate)} |`) + } - public hasIdTags(): boolean { - return isNotEmptyArray(this.idTagsCache.getIdTags(getIdTagsFile(this.stationInfo)!)); + public hasIdTags (): boolean { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return isNotEmptyArray(this.idTagsCache.getIdTags(getIdTagsFile(this.stationInfo)!)) } - public getNumberOfPhases(stationInfo?: ChargingStationInfo): number { - const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo; + public getNumberOfPhases (stationInfo?: ChargingStationInfo): number { + const localStationInfo: ChargingStationInfo = stationInfo ?? this.stationInfo switch (this.getCurrentOutType(stationInfo)) { case CurrentType.AC: - return localStationInfo.numberOfPhases ?? 3; + return localStationInfo.numberOfPhases ?? 3 case CurrentType.DC: - return 0; + return 0 } } - public isWebSocketConnectionOpened(): boolean { - return this?.wsConnection?.readyState === WebSocket.OPEN; + public isWebSocketConnectionOpened (): boolean { + return this?.wsConnection?.readyState === WebSocket.OPEN } - public inUnknownState(): boolean { - return isNullOrUndefined(this?.bootNotificationResponse?.status); + public inUnknownState (): boolean { + return isNullOrUndefined(this?.bootNotificationResponse?.status) } - public inPendingState(): boolean { - return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.PENDING; + public inPendingState (): boolean { + return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.PENDING } - public inAcceptedState(): boolean { - return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.ACCEPTED; + public inAcceptedState (): boolean { + return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.ACCEPTED } - public inRejectedState(): boolean { - return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.REJECTED; + public inRejectedState (): boolean { + return this?.bootNotificationResponse?.status === RegistrationStatusEnumType.REJECTED } - public isRegistered(): boolean { - return ( - this.inUnknownState() === false && - (this.inAcceptedState() === true || this.inPendingState() === true) - ); + public isRegistered (): boolean { + return !this.inUnknownState() && (this.inAcceptedState() || this.inPendingState()) } - public isChargingStationAvailable(): boolean { - return this.getConnectorStatus(0)?.availability === AvailabilityType.Operative; + public isChargingStationAvailable (): boolean { + return this.getConnectorStatus(0)?.availability === AvailabilityType.Operative } - public hasConnector(connectorId: number): boolean { + public hasConnector (connectorId: number): boolean { if (this.hasEvses) { for (const evseStatus of this.evses.values()) { if (evseStatus.connectors.has(connectorId)) { - return true; + return true } } - return false; + return false } - return this.connectors.has(connectorId); + return this.connectors.has(connectorId) } - public isConnectorAvailable(connectorId: number): boolean { + public isConnectorAvailable (connectorId: number): boolean { return ( connectorId > 0 && this.getConnectorStatus(connectorId)?.availability === AvailabilityType.Operative - ); + ) } - public getNumberOfConnectors(): number { + public getNumberOfConnectors (): number { if (this.hasEvses) { - let numberOfConnectors = 0; + let numberOfConnectors = 0 for (const [evseId, evseStatus] of this.evses) { if (evseId > 0) { - numberOfConnectors += evseStatus.connectors.size; + numberOfConnectors += evseStatus.connectors.size } } - return numberOfConnectors; + return numberOfConnectors } - return this.connectors.has(0) ? this.connectors.size - 1 : this.connectors.size; + return this.connectors.has(0) ? this.connectors.size - 1 : this.connectors.size } - public getNumberOfEvses(): number { - return this.evses.has(0) ? this.evses.size - 1 : this.evses.size; + public getNumberOfEvses (): number { + return this.evses.has(0) ? this.evses.size - 1 : this.evses.size } - public getConnectorStatus(connectorId: number): ConnectorStatus | undefined { + 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 evseStatus.connectors.get(connectorId) } } - return undefined; + return undefined } - return this.connectors.get(connectorId); + return this.connectors.get(connectorId) } - public getConnectorMaximumAvailablePower(connectorId: number): number { - let connectorAmperageLimitationPowerLimit: number | undefined; + public getConnectorMaximumAvailablePower (connectorId: number): number { + let connectorAmperageLimitationPowerLimit: number | undefined if ( !isNullOrUndefined(this.getAmperageLimitation()) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.getAmperageLimitation()! < this.stationInfo.maximumAmperage! ) { connectorAmperageLimitationPowerLimit = (this.stationInfo?.currentOutType === CurrentType.AC ? ACElectricUtils.powerTotal( - this.getNumberOfPhases(), - this.stationInfo.voltageOut!, - this.getAmperageLimitation()! * - (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()), - ) - : DCElectricUtils.power(this.stationInfo.voltageOut!, this.getAmperageLimitation()!)) / - this.powerDivider; + this.getNumberOfPhases(), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.stationInfo.voltageOut!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.getAmperageLimitation()! * + (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()) + ) + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + DCElectricUtils.power(this.stationInfo.voltageOut!, this.getAmperageLimitation()!)) / + this.powerDivider } - const connectorMaximumPower = this.stationInfo.maximumPower! / this.powerDivider; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorMaximumPower = this.stationInfo.maximumPower! / this.powerDivider const connectorChargingProfilesPowerLimit = - getChargingStationConnectorChargingProfilesPowerLimit(this, connectorId); + getChargingStationConnectorChargingProfilesPowerLimit(this, connectorId) return min( isNaN(connectorMaximumPower) ? Infinity : connectorMaximumPower, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion isNaN(connectorAmperageLimitationPowerLimit!) ? Infinity - : connectorAmperageLimitationPowerLimit!, - isNaN(connectorChargingProfilesPowerLimit!) ? Infinity : connectorChargingProfilesPowerLimit!, - ); + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connectorAmperageLimitationPowerLimit!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + isNaN(connectorChargingProfilesPowerLimit!) ? Infinity : connectorChargingProfilesPowerLimit! + ) } - public getTransactionIdTag(transactionId: number): string | undefined { + public getTransactionIdTag (transactionId: number): string | undefined { if (this.hasEvses) { for (const evseStatus of this.evses.values()) { for (const connectorStatus of evseStatus.connectors.values()) { if (connectorStatus.transactionId === transactionId) { - return connectorStatus.transactionIdTag; + return connectorStatus.transactionIdTag } } } } else { for (const connectorId of this.connectors.keys()) { if (this.getConnectorStatus(connectorId)?.transactionId === transactionId) { - return this.getConnectorStatus(connectorId)?.transactionIdTag; + return this.getConnectorStatus(connectorId)?.transactionIdTag } } } } - public getNumberOfRunningTransactions(): number { - let numberOfRunningTransactions = 0; + public getNumberOfRunningTransactions (): number { + let numberOfRunningTransactions = 0 if (this.hasEvses) { for (const [evseId, evseStatus] of this.evses) { if (evseId === 0) { - continue; + continue } for (const connectorStatus of evseStatus.connectors.values()) { if (connectorStatus.transactionStarted === true) { - ++numberOfRunningTransactions; + ++numberOfRunningTransactions } } } } else { for (const connectorId of this.connectors.keys()) { if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) { - ++numberOfRunningTransactions; + ++numberOfRunningTransactions } } } - return numberOfRunningTransactions; + return numberOfRunningTransactions } - public getConnectorIdByTransactionId(transactionId: number): number | undefined { + public getConnectorIdByTransactionId (transactionId: number): number | undefined { if (this.hasEvses) { for (const evseStatus of this.evses.values()) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { if (connectorStatus.transactionId === transactionId) { - return connectorId; + return connectorId } } } } else { for (const connectorId of this.connectors.keys()) { if (this.getConnectorStatus(connectorId)?.transactionId === transactionId) { - return connectorId; + return connectorId } } } } - public getEnergyActiveImportRegisterByTransactionId( + public getEnergyActiveImportRegisterByTransactionId ( transactionId: number, - rounded = false, + rounded = false ): number { return this.getEnergyActiveImportRegister( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.getConnectorStatus(this.getConnectorIdByTransactionId(transactionId)!)!, - rounded, - ); + rounded + ) } - public getEnergyActiveImportRegisterByConnectorId(connectorId: number, rounded = false): number { - return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId)!, rounded); + public getEnergyActiveImportRegisterByConnectorId (connectorId: number, rounded = false): number { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.getEnergyActiveImportRegister(this.getConnectorStatus(connectorId)!, rounded) } - public getAuthorizeRemoteTxRequests(): boolean { + public getAuthorizeRemoteTxRequests (): boolean { const authorizeRemoteTxRequests = getConfigurationKey( this, - StandardParametersKey.AuthorizeRemoteTxRequests, - ); + StandardParametersKey.AuthorizeRemoteTxRequests + ) return authorizeRemoteTxRequests !== undefined ? convertToBoolean(authorizeRemoteTxRequests.value) - : false; + : false } - public getLocalAuthListEnabled(): boolean { + public getLocalAuthListEnabled (): boolean { const localAuthListEnabled = getConfigurationKey( this, - StandardParametersKey.LocalAuthListEnabled, - ); - return localAuthListEnabled !== undefined - ? convertToBoolean(localAuthListEnabled.value) - : false; + StandardParametersKey.LocalAuthListEnabled + ) + return localAuthListEnabled !== undefined ? convertToBoolean(localAuthListEnabled.value) : false } - public getHeartbeatInterval(): number { - const HeartbeatInterval = getConfigurationKey(this, StandardParametersKey.HeartbeatInterval); + public getHeartbeatInterval (): number { + const HeartbeatInterval = getConfigurationKey(this, StandardParametersKey.HeartbeatInterval) if (HeartbeatInterval !== undefined) { - return secondsToMilliseconds(convertToInt(HeartbeatInterval.value)); + return secondsToMilliseconds(convertToInt(HeartbeatInterval.value)) } - const HeartBeatInterval = getConfigurationKey(this, StandardParametersKey.HeartBeatInterval); + const HeartBeatInterval = getConfigurationKey(this, StandardParametersKey.HeartBeatInterval) if (HeartBeatInterval !== undefined) { - return secondsToMilliseconds(convertToInt(HeartBeatInterval.value)); + return secondsToMilliseconds(convertToInt(HeartBeatInterval.value)) } this.stationInfo?.autoRegister === false && logger.warn( `${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${ Constants.DEFAULT_HEARTBEAT_INTERVAL - }`, - ); - return Constants.DEFAULT_HEARTBEAT_INTERVAL; + }` + ) + return Constants.DEFAULT_HEARTBEAT_INTERVAL } - public setSupervisionUrl(url: string): void { + public setSupervisionUrl (url: string): void { if ( this.stationInfo?.supervisionUrlOcppConfiguration === true && isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) ) { - setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey!, url); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + setConfigurationKeyValue(this, this.stationInfo.supervisionUrlOcppKey!, url) } else { - this.stationInfo.supervisionUrls = url; - this.saveStationInfo(); - this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl(); + this.stationInfo.supervisionUrls = url + this.saveStationInfo() + this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl() } } - public startHeartbeat(): void { + public startHeartbeat (): void { if (this.getHeartbeatInterval() > 0 && this.heartbeatSetInterval === undefined) { this.heartbeatSetInterval = setInterval(() => { this.ocppRequestService @@ -505,118 +514,118 @@ export class ChargingStation extends EventEmitter { .catch((error) => { logger.error( `${this.logPrefix()} Error while sending '${RequestCommand.HEARTBEAT}':`, - error, - ); - }); - }, this.getHeartbeatInterval()); + error + ) + }) + }, this.getHeartbeatInterval()) logger.info( `${this.logPrefix()} Heartbeat started every ${formatDurationMilliSeconds( - this.getHeartbeatInterval(), - )}`, - ); + this.getHeartbeatInterval() + )}` + ) } else if (this.heartbeatSetInterval !== undefined) { logger.info( `${this.logPrefix()} Heartbeat already started every ${formatDurationMilliSeconds( - this.getHeartbeatInterval(), - )}`, - ); + this.getHeartbeatInterval() + )}` + ) } else { logger.error( - `${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval()}, not starting the heartbeat`, - ); + `${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval()}, not starting the heartbeat` + ) } } - public restartHeartbeat(): void { + public restartHeartbeat (): void { // Stop heartbeat - this.stopHeartbeat(); + this.stopHeartbeat() // Start heartbeat - this.startHeartbeat(); + this.startHeartbeat() } - public restartWebSocketPing(): void { + public restartWebSocketPing (): void { // Stop WebSocket ping - this.stopWebSocketPing(); + this.stopWebSocketPing() // Start WebSocket ping - this.startWebSocketPing(); + this.startWebSocketPing() } - public startMeterValues(connectorId: number, interval: number): void { + public startMeterValues (connectorId: number, interval: number): void { if (connectorId === 0) { - logger.error( - `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId}`, - ); - return; + logger.error(`${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId}`) + return } - if (!this.getConnectorStatus(connectorId)) { + if (this.getConnectorStatus(connectorId) == null) { logger.error( `${this.logPrefix()} Trying to start MeterValues on non existing connector id - ${connectorId}`, - ); - return; + ${connectorId}` + ) + return } if (this.getConnectorStatus(connectorId)?.transactionStarted === false) { logger.error( - `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction started`, - ); - return; + `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction started` + ) + return } else if ( this.getConnectorStatus(connectorId)?.transactionStarted === true && isNullOrUndefined(this.getConnectorStatus(connectorId)?.transactionId) ) { logger.error( - `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id`, - ); - return; + `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id` + ) + return } if (interval > 0) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.getConnectorStatus(connectorId)!.transactionSetInterval = setInterval(() => { const meterValue = buildMeterValue( this, connectorId, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.getConnectorStatus(connectorId)!.transactionId!, - interval, - ); + interval + ) this.ocppRequestService .requestHandler( - this, - RequestCommand.METER_VALUES, - { - connectorId, - transactionId: this.getConnectorStatus(connectorId)?.transactionId, - meterValue: [meterValue], - }, - ) + this, + RequestCommand.METER_VALUES, + { + connectorId, + transactionId: this.getConnectorStatus(connectorId)?.transactionId, + meterValue: [meterValue] + } + ) .catch((error) => { logger.error( `${this.logPrefix()} Error while sending '${RequestCommand.METER_VALUES}':`, - error, - ); - }); - }, interval); + error + ) + }) + }, interval) } else { logger.error( `${this.logPrefix()} Charging station ${ StandardParametersKey.MeterValueSampleInterval - } configuration set to ${interval}, not sending MeterValues`, - ); + } configuration set to ${interval}, not sending MeterValues` + ) } } - public stopMeterValues(connectorId: number) { + public stopMeterValues (connectorId: number): void { if (this.getConnectorStatus(connectorId)?.transactionSetInterval !== undefined) { - clearInterval(this.getConnectorStatus(connectorId)?.transactionSetInterval); + clearInterval(this.getConnectorStatus(connectorId)?.transactionSetInterval) } } - public start(): void { - if (this.started === false) { - if (this.starting === false) { - this.starting = true; + public start (): void { + if (!this.started) { + if (!this.starting) { + this.starting = true if (this.stationInfo?.enableStatistics === true) { - this.performanceStatistics?.start(); + this.performanceStatistics?.start() } - this.openWSConnection(); + this.openWSConnection() // Monitor charging station template file this.templateFileWatcher = watchJsonFile( this.templateFile, @@ -629,219 +638,224 @@ export class ChargingStation extends EventEmitter { logger.debug( `${this.logPrefix()} ${FileType.ChargingStationTemplate} ${ this.templateFile - } file have changed, reload`, - ); - this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash); + } file have changed, reload` + ) + this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash) // Initialize - this.initialize(); - this.idTagsCache.deleteIdTags(getIdTagsFile(this.stationInfo)!); + this.initialize() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.idTagsCache.deleteIdTags(getIdTagsFile(this.stationInfo)!) // Restart the ATG - this.stopAutomaticTransactionGenerator(); - delete this.automaticTransactionGeneratorConfiguration; - if (this.getAutomaticTransactionGeneratorConfiguration().enable === true) { - this.startAutomaticTransactionGenerator(); + this.stopAutomaticTransactionGenerator() + delete this.automaticTransactionGeneratorConfiguration + if (this.getAutomaticTransactionGeneratorConfiguration().enable) { + this.startAutomaticTransactionGenerator() } if (this.stationInfo?.enableStatistics === true) { - this.performanceStatistics?.restart(); + this.performanceStatistics?.restart() } else { - this.performanceStatistics?.stop(); + this.performanceStatistics?.stop() } // FIXME?: restart heartbeat and WebSocket ping when their interval values have changed } catch (error) { logger.error( `${this.logPrefix()} ${FileType.ChargingStationTemplate} file monitoring error:`, - error, - ); + error + ) } } - }, - ); - this.started = true; - this.emit(ChargingStationEvents.started); - this.starting = false; + } + ) + this.started = true + this.emit(ChargingStationEvents.started) + this.starting = false } else { - logger.warn(`${this.logPrefix()} Charging station is already starting...`); + logger.warn(`${this.logPrefix()} Charging station is already starting...`) } } else { - logger.warn(`${this.logPrefix()} Charging station is already started...`); + logger.warn(`${this.logPrefix()} Charging station is already started...`) } } - public async stop(reason?: StopTransactionReason, stopTransactions?: boolean): Promise { - if (this.started === true) { - if (this.stopping === false) { - this.stopping = true; - await this.stopMessageSequence(reason, stopTransactions); - this.closeWSConnection(); + public async stop (reason?: StopTransactionReason, stopTransactions?: boolean): Promise { + if (this.started) { + if (!this.stopping) { + this.stopping = true + await this.stopMessageSequence(reason, stopTransactions) + this.closeWSConnection() if (this.stationInfo?.enableStatistics === true) { - this.performanceStatistics?.stop(); + this.performanceStatistics?.stop() } - this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash); - this.templateFileWatcher?.close(); - this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash); - delete this.bootNotificationResponse; - this.started = false; - this.saveConfiguration(); - this.emit(ChargingStationEvents.stopped); - this.stopping = false; + this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash) + this.templateFileWatcher?.close() + this.sharedLRUCache.deleteChargingStationTemplate(this.templateFileHash) + delete this.bootNotificationResponse + this.started = false + this.saveConfiguration() + this.emit(ChargingStationEvents.stopped) + this.stopping = false } else { - logger.warn(`${this.logPrefix()} Charging station is already stopping...`); + logger.warn(`${this.logPrefix()} Charging station is already stopping...`) } } else { - logger.warn(`${this.logPrefix()} Charging station is already stopped...`); + logger.warn(`${this.logPrefix()} Charging station is already stopped...`) } } - public async reset(reason?: StopTransactionReason): Promise { - await this.stop(reason); - await sleep(this.stationInfo.resetTime!); - this.initialize(); - this.start(); + public async reset (reason?: StopTransactionReason): Promise { + await this.stop(reason) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await sleep(this.stationInfo.resetTime!) + this.initialize() + this.start() } - public saveOcppConfiguration(): void { + public saveOcppConfiguration (): void { if (this.stationInfo?.ocppPersistentConfiguration === true) { - this.saveConfiguration(); + this.saveConfiguration() } } - public bufferMessage(message: string): void { - this.messageBuffer.add(message); - this.setIntervalFlushMessageBuffer(); + public bufferMessage (message: string): void { + this.messageBuffer.add(message) + this.setIntervalFlushMessageBuffer() } - public openWSConnection( + public openWSConnection ( options?: WsOptions, - params?: { closeOpened?: boolean; terminateOpened?: boolean }, + params?: { closeOpened?: boolean, terminateOpened?: boolean } ): void { options = { handshakeTimeout: secondsToMilliseconds(this.getConnectionTimeout()), ...this.stationInfo?.wsOptions, - ...options, - }; - params = { ...{ closeOpened: false, terminateOpened: false }, ...params }; + ...options + } + params = { ...{ closeOpened: false, terminateOpened: false }, ...params } if (!checkChargingStation(this, this.logPrefix())) { - return; + return } if ( !isNullOrUndefined(this.stationInfo.supervisionUser) && !isNullOrUndefined(this.stationInfo.supervisionPassword) ) { - options.auth = `${this.stationInfo.supervisionUser}:${this.stationInfo.supervisionPassword}`; + options.auth = `${this.stationInfo.supervisionUser}:${this.stationInfo.supervisionPassword}` } - if (params?.closeOpened) { - this.closeWSConnection(); + if (params?.closeOpened === true) { + this.closeWSConnection() } - if (params?.terminateOpened) { - this.terminateWSConnection(); + if (params?.terminateOpened === true) { + this.terminateWSConnection() } - if (this.isWebSocketConnectionOpened() === true) { + if (this.isWebSocketConnectionOpened()) { logger.warn( - `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} is already opened`, - ); - return; + `${this.logPrefix()} OCPP connection to URL ${this.wsConnectionUrl.toString()} is already opened` + ) + return } logger.info( - `${this.logPrefix()} Open OCPP connection to URL ${this.wsConnectionUrl.toString()}`, - ); + `${this.logPrefix()} Open OCPP connection to URL ${this.wsConnectionUrl.toString()}` + ) this.wsConnection = new WebSocket( this.wsConnectionUrl, `ocpp${this.stationInfo?.ocppVersion}`, - options, - ); + options + ) // Handle WebSocket message this.wsConnection.on( 'message', - this.onMessage.bind(this) as (this: WebSocket, data: RawData, isBinary: boolean) => void, - ); + this.onMessage.bind(this) as (this: WebSocket, data: RawData, isBinary: boolean) => void + ) // Handle WebSocket error this.wsConnection.on( 'error', - this.onError.bind(this) as (this: WebSocket, error: Error) => void, - ); + this.onError.bind(this) as (this: WebSocket, error: Error) => void + ) // Handle WebSocket close this.wsConnection.on( 'close', - this.onClose.bind(this) as (this: WebSocket, code: number, reason: Buffer) => void, - ); + this.onClose.bind(this) as (this: WebSocket, code: number, reason: Buffer) => void + ) // Handle WebSocket open - this.wsConnection.on('open', this.onOpen.bind(this) as (this: WebSocket) => void); + this.wsConnection.on('open', this.onOpen.bind(this) as (this: WebSocket) => void) // Handle WebSocket ping - this.wsConnection.on('ping', this.onPing.bind(this) as (this: WebSocket, data: Buffer) => void); + this.wsConnection.on('ping', this.onPing.bind(this) as (this: WebSocket, data: Buffer) => void) // Handle WebSocket pong - this.wsConnection.on('pong', this.onPong.bind(this) as (this: WebSocket, data: Buffer) => void); + this.wsConnection.on('pong', this.onPong.bind(this) as (this: WebSocket, data: Buffer) => void) } - public closeWSConnection(): void { - if (this.isWebSocketConnectionOpened() === true) { - this.wsConnection?.close(); - this.wsConnection = null; + public closeWSConnection (): void { + if (this.isWebSocketConnectionOpened()) { + this.wsConnection?.close() + this.wsConnection = null } } - public getAutomaticTransactionGeneratorConfiguration(): AutomaticTransactionGeneratorConfiguration { + public getAutomaticTransactionGeneratorConfiguration (): AutomaticTransactionGeneratorConfiguration { if (isNullOrUndefined(this.automaticTransactionGeneratorConfiguration)) { let automaticTransactionGeneratorConfiguration: - | AutomaticTransactionGeneratorConfiguration - | undefined; - const stationTemplate = this.getTemplateFromFile(); - const stationConfiguration = this.getConfigurationFromFile(); + | AutomaticTransactionGeneratorConfiguration + | undefined + const stationTemplate = this.getTemplateFromFile() + const stationConfiguration = this.getConfigurationFromFile() if ( this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration === true && stationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash && - stationConfiguration?.automaticTransactionGenerator + stationConfiguration?.automaticTransactionGenerator != null ) { automaticTransactionGeneratorConfiguration = - stationConfiguration?.automaticTransactionGenerator; + stationConfiguration?.automaticTransactionGenerator } else { - automaticTransactionGeneratorConfiguration = stationTemplate?.AutomaticTransactionGenerator; + automaticTransactionGeneratorConfiguration = stationTemplate?.AutomaticTransactionGenerator } this.automaticTransactionGeneratorConfiguration = { ...Constants.DEFAULT_ATG_CONFIGURATION, - ...automaticTransactionGeneratorConfiguration, - }; + ...automaticTransactionGeneratorConfiguration + } } - return this.automaticTransactionGeneratorConfiguration!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.automaticTransactionGeneratorConfiguration! } - public getAutomaticTransactionGeneratorStatuses(): Status[] | undefined { - return this.getConfigurationFromFile()?.automaticTransactionGeneratorStatuses; + public getAutomaticTransactionGeneratorStatuses (): Status[] | undefined { + return this.getConfigurationFromFile()?.automaticTransactionGeneratorStatuses } - public startAutomaticTransactionGenerator(connectorIds?: number[]): void { - this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this); + public startAutomaticTransactionGenerator (connectorIds?: number[]): void { + this.automaticTransactionGenerator = AutomaticTransactionGenerator.getInstance(this) if (isNotEmptyArray(connectorIds)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion for (const connectorId of connectorIds!) { - this.automaticTransactionGenerator?.startConnector(connectorId); + this.automaticTransactionGenerator?.startConnector(connectorId) } } else { - this.automaticTransactionGenerator?.start(); + this.automaticTransactionGenerator?.start() } - this.saveAutomaticTransactionGeneratorConfiguration(); - this.emit(ChargingStationEvents.updated); + this.saveAutomaticTransactionGeneratorConfiguration() + this.emit(ChargingStationEvents.updated) } - public stopAutomaticTransactionGenerator(connectorIds?: number[]): void { + public stopAutomaticTransactionGenerator (connectorIds?: number[]): void { if (isNotEmptyArray(connectorIds)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion for (const connectorId of connectorIds!) { - this.automaticTransactionGenerator?.stopConnector(connectorId); + this.automaticTransactionGenerator?.stopConnector(connectorId) } } else { - this.automaticTransactionGenerator?.stop(); + this.automaticTransactionGenerator?.stop() } - this.saveAutomaticTransactionGeneratorConfiguration(); - this.emit(ChargingStationEvents.updated); + this.saveAutomaticTransactionGeneratorConfiguration() + this.emit(ChargingStationEvents.updated) } - public async stopTransactionOnConnector( + public async stopTransactionOnConnector ( connectorId: number, - reason?: StopTransactionReason, + reason?: StopTransactionReason ): Promise { - const transactionId = this.getConnectorStatus(connectorId)?.transactionId; + const transactionId = this.getConnectorStatus(connectorId)?.transactionId if ( this.stationInfo?.beginEndMeterValues === true && this.stationInfo?.ocppStrictCompliance === true && @@ -850,60 +864,64 @@ export class ChargingStation extends EventEmitter { const transactionEndMeterValue = buildTransactionEndMeterValue( this, connectorId, - this.getEnergyActiveImportRegisterByTransactionId(transactionId!), - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.getEnergyActiveImportRegisterByTransactionId(transactionId!) + ) await this.ocppRequestService.requestHandler( this, RequestCommand.METER_VALUES, { connectorId, transactionId, - meterValue: [transactionEndMeterValue], - }, - ); + meterValue: [transactionEndMeterValue] + } + ) } - return this.ocppRequestService.requestHandler( - this, - RequestCommand.STOP_TRANSACTION, - { - transactionId, - meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId!, true), - ...(isNullOrUndefined(reason) && { reason }), - }, - ); + return await this.ocppRequestService.requestHandler< + StopTransactionRequest, + StopTransactionResponse + >(this, RequestCommand.STOP_TRANSACTION, { + transactionId, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + meterStop: this.getEnergyActiveImportRegisterByTransactionId(transactionId!, true), + ...(isNullOrUndefined(reason) && { reason }) + }) } - public getReserveConnectorZeroSupported(): boolean { + public getReserveConnectorZeroSupported (): boolean { return convertToBoolean( - getConfigurationKey(this, StandardParametersKey.ReserveConnectorZeroSupported)!.value, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + getConfigurationKey(this, StandardParametersKey.ReserveConnectorZeroSupported)!.value + ) } - public async addReservation(reservation: Reservation): Promise { - const reservationFound = this.getReservationBy('reservationId', reservation.reservationId); + public async addReservation (reservation: Reservation): Promise { + const reservationFound = this.getReservationBy('reservationId', reservation.reservationId) if (reservationFound !== undefined) { - await this.removeReservation(reservationFound, ReservationTerminationReason.REPLACE_EXISTING); + await this.removeReservation(reservationFound, ReservationTerminationReason.REPLACE_EXISTING) } - this.getConnectorStatus(reservation.connectorId)!.reservation = reservation; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.getConnectorStatus(reservation.connectorId)!.reservation = reservation await sendAndSetConnectorStatus( this, reservation.connectorId, ConnectorStatusEnum.Reserved, undefined, - { send: reservation.connectorId !== 0 }, - ); + { send: reservation.connectorId !== 0 } + ) } - public async removeReservation( + public async removeReservation ( reservation: Reservation, - reason: ReservationTerminationReason, + reason: ReservationTerminationReason ): Promise { - const connector = this.getConnectorStatus(reservation.connectorId)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connector = this.getConnectorStatus(reservation.connectorId)! switch (reason) { case ReservationTerminationReason.CONNECTOR_STATE_CHANGED: case ReservationTerminationReason.TRANSACTION_STARTED: - delete connector.reservation; - break; + delete connector.reservation + break case ReservationTerminationReason.RESERVATION_CANCELED: case ReservationTerminationReason.REPLACE_EXISTING: case ReservationTerminationReason.EXPIRED: @@ -912,285 +930,295 @@ export class ChargingStation extends EventEmitter { reservation.connectorId, ConnectorStatusEnum.Available, undefined, - { send: reservation.connectorId !== 0 }, - ); - delete connector.reservation; - break; + { send: reservation.connectorId !== 0 } + ) + delete connector.reservation + break default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new BaseError(`Unknown reservation termination reason '${reason}'`); + throw new BaseError(`Unknown reservation termination reason '${reason}'`) } } - public getReservationBy( + public getReservationBy ( filterKey: ReservationKey, - value: number | string, + value: number | string ): Reservation | undefined { if (this.hasEvses) { for (const evseStatus of this.evses.values()) { for (const connectorStatus of evseStatus.connectors.values()) { if (connectorStatus?.reservation?.[filterKey] === value) { - return connectorStatus.reservation; + return connectorStatus.reservation } } } } else { for (const connectorStatus of this.connectors.values()) { if (connectorStatus?.reservation?.[filterKey] === value) { - return connectorStatus.reservation; + return connectorStatus.reservation } } } } - public isConnectorReservable( + public isConnectorReservable ( reservationId: number, idTag?: string, - connectorId?: number, + connectorId?: number ): boolean { - const reservation = this.getReservationBy('reservationId', reservationId); - const reservationExists = !isUndefined(reservation) && !hasReservationExpired(reservation!); + const reservation = this.getReservationBy('reservationId', reservationId) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const reservationExists = !isUndefined(reservation) && !hasReservationExpired(reservation!) if (arguments.length === 1) { - return !reservationExists; + return !reservationExists } else if (arguments.length > 1) { const userReservation = !isUndefined(idTag) - ? this.getReservationBy('idTag', idTag!) - : undefined; + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.getReservationBy('idTag', idTag!) + : undefined const userReservationExists = - !isUndefined(userReservation) && !hasReservationExpired(userReservation!); - const notConnectorZero = isUndefined(connectorId) ? true : connectorId! > 0; - const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + !isUndefined(userReservation) && !hasReservationExpired(userReservation!) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const notConnectorZero = isUndefined(connectorId) ? true : connectorId! > 0 + const freeConnectorsAvailable = this.getNumberOfReservableConnectors() > 0 return ( !reservationExists && !userReservationExists && notConnectorZero && freeConnectorsAvailable - ); + ) } - return false; + return false } - private setIntervalFlushMessageBuffer(): void { + private setIntervalFlushMessageBuffer (): void { if (this.flushMessageBufferSetInterval === undefined) { this.flushMessageBufferSetInterval = setInterval(() => { - if (this.isWebSocketConnectionOpened() === true && this.inAcceptedState() === true) { - this.flushMessageBuffer(); + if (this.isWebSocketConnectionOpened() && this.inAcceptedState()) { + this.flushMessageBuffer() } if (this.messageBuffer.size === 0) { - this.clearIntervalFlushMessageBuffer(); + this.clearIntervalFlushMessageBuffer() } - }, Constants.DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL); + }, Constants.DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL) } } - private clearIntervalFlushMessageBuffer() { + private clearIntervalFlushMessageBuffer (): void { if (this.flushMessageBufferSetInterval !== undefined) { - clearInterval(this.flushMessageBufferSetInterval); - delete this.flushMessageBufferSetInterval; + clearInterval(this.flushMessageBufferSetInterval) + delete this.flushMessageBufferSetInterval } } - private getNumberOfReservableConnectors(): number { - let numberOfReservableConnectors = 0; + private getNumberOfReservableConnectors (): number { + let numberOfReservableConnectors = 0 if (this.hasEvses) { for (const evseStatus of this.evses.values()) { - numberOfReservableConnectors += getNumberOfReservableConnectors(evseStatus.connectors); + numberOfReservableConnectors += getNumberOfReservableConnectors(evseStatus.connectors) } } else { - numberOfReservableConnectors = getNumberOfReservableConnectors(this.connectors); + numberOfReservableConnectors = getNumberOfReservableConnectors(this.connectors) } - return numberOfReservableConnectors - this.getNumberOfReservationsOnConnectorZero(); + return numberOfReservableConnectors - this.getNumberOfReservationsOnConnectorZero() } - private getNumberOfReservationsOnConnectorZero(): number { + private getNumberOfReservationsOnConnectorZero (): number { if ( - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - (this.hasEvses && this.evses.get(0)?.connectors.get(0)?.reservation) || - (!this.hasEvses && this.connectors.get(0)?.reservation) + (this.hasEvses && this.evses.get(0)?.connectors.get(0)?.reservation != null) || + (!this.hasEvses && this.connectors.get(0)?.reservation != null) ) { - return 1; + return 1 } - return 0; + return 0 } - private flushMessageBuffer(): void { + private flushMessageBuffer (): void { if (this.messageBuffer.size > 0) { for (const message of this.messageBuffer.values()) { - let beginId: string | undefined; - let commandName: RequestCommand | undefined; - const [messageType] = JSON.parse(message) as OutgoingRequest | Response | ErrorResponse; - const isRequest = messageType === MessageType.CALL_MESSAGE; + let beginId: string | undefined + let commandName: RequestCommand | undefined + const [messageType] = JSON.parse(message) as OutgoingRequest | Response | ErrorResponse + const isRequest = messageType === MessageType.CALL_MESSAGE if (isRequest) { - [, , commandName] = JSON.parse(message) as OutgoingRequest; - beginId = PerformanceStatistics.beginMeasure(commandName); + [, , commandName] = JSON.parse(message) as OutgoingRequest + beginId = PerformanceStatistics.beginMeasure(commandName) } this.wsConnection?.send(message, (error?: Error) => { - isRequest && PerformanceStatistics.endMeasure(commandName!, beginId!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + isRequest && PerformanceStatistics.endMeasure(commandName!, beginId!) if (isNullOrUndefined(error)) { logger.debug( `${this.logPrefix()} >> Buffered ${getMessageTypeString( - messageType, - )} OCPP message sent '${JSON.stringify(message)}'`, - ); - this.messageBuffer.delete(message); + messageType + )} OCPP message sent '${JSON.stringify(message)}'` + ) + this.messageBuffer.delete(message) } else { logger.debug( `${this.logPrefix()} >> Buffered ${getMessageTypeString( - messageType, + messageType )} OCPP message '${JSON.stringify(message)}' send failed:`, - error, - ); + error + ) } - }); + }) } } } - private getTemplateFromFile(): ChargingStationTemplate | undefined { - let template: ChargingStationTemplate | undefined; + private getTemplateFromFile (): ChargingStationTemplate | undefined { + let template: ChargingStationTemplate | undefined try { if (this.sharedLRUCache.hasChargingStationTemplate(this.templateFileHash)) { - template = this.sharedLRUCache.getChargingStationTemplate(this.templateFileHash); + template = this.sharedLRUCache.getChargingStationTemplate(this.templateFileHash) } else { - const measureId = `${FileType.ChargingStationTemplate} read`; - const beginId = PerformanceStatistics.beginMeasure(measureId); - template = JSON.parse(readFileSync(this.templateFile, 'utf8')) as ChargingStationTemplate; - PerformanceStatistics.endMeasure(measureId, beginId); + const measureId = `${FileType.ChargingStationTemplate} read` + const beginId = PerformanceStatistics.beginMeasure(measureId) + template = JSON.parse(readFileSync(this.templateFile, 'utf8')) as ChargingStationTemplate + PerformanceStatistics.endMeasure(measureId, beginId) template.templateHash = createHash(Constants.DEFAULT_HASH_ALGORITHM) .update(JSON.stringify(template)) - .digest('hex'); - this.sharedLRUCache.setChargingStationTemplate(template); - this.templateFileHash = template.templateHash; + .digest('hex') + this.sharedLRUCache.setChargingStationTemplate(template) + this.templateFileHash = template.templateHash } } catch (error) { handleFileException( this.templateFile, FileType.ChargingStationTemplate, error as NodeJS.ErrnoException, - this.logPrefix(), - ); - } - return template; - } - - private getStationInfoFromTemplate(): ChargingStationInfo { - const stationTemplate: ChargingStationTemplate = this.getTemplateFromFile()!; - checkTemplate(stationTemplate, this.logPrefix(), this.templateFile); - const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation, this); - warnTemplateKeysDeprecationOnce(stationTemplate, this.logPrefix(), this.templateFile); - if (stationTemplate?.Connectors) { - checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile); - } - const stationInfo: ChargingStationInfo = stationTemplateToStationInfo(stationTemplate); - stationInfo.hashId = getHashId(this.index, stationTemplate); - stationInfo.chargingStationId = getChargingStationId(this.index, stationTemplate); - stationInfo.ocppVersion = stationTemplate?.ocppVersion ?? OCPPVersion.VERSION_16; - createSerialNumber(stationTemplate, stationInfo); - stationInfo.voltageOut = this.getVoltageOut(stationInfo); + this.logPrefix() + ) + } + return template + } + + private getStationInfoFromTemplate (): ChargingStationInfo { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const stationTemplate: ChargingStationTemplate = this.getTemplateFromFile()! + checkTemplate(stationTemplate, this.logPrefix(), this.templateFile) + const warnTemplateKeysDeprecationOnce = once(warnTemplateKeysDeprecation, this) + warnTemplateKeysDeprecationOnce(stationTemplate, this.logPrefix(), this.templateFile) + if (stationTemplate?.Connectors != null) { + checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile) + } + const stationInfo: ChargingStationInfo = stationTemplateToStationInfo(stationTemplate) + stationInfo.hashId = getHashId(this.index, stationTemplate) + stationInfo.chargingStationId = getChargingStationId(this.index, stationTemplate) + stationInfo.ocppVersion = stationTemplate?.ocppVersion ?? OCPPVersion.VERSION_16 + createSerialNumber(stationTemplate, stationInfo) + stationInfo.voltageOut = this.getVoltageOut(stationInfo) if (isNotEmptyArray(stationTemplate?.power)) { - stationTemplate.power = stationTemplate.power as number[]; - const powerArrayRandomIndex = Math.floor(secureRandom() * stationTemplate.power.length); + stationTemplate.power = stationTemplate.power as number[] + const powerArrayRandomIndex = Math.floor(secureRandom() * stationTemplate.power.length) stationInfo.maximumPower = stationTemplate?.powerUnit === PowerUnits.KILO_WATT ? stationTemplate.power[powerArrayRandomIndex] * 1000 - : stationTemplate.power[powerArrayRandomIndex]; + : stationTemplate.power[powerArrayRandomIndex] } else { - stationTemplate.power = stationTemplate?.power as number; + stationTemplate.power = stationTemplate?.power as number stationInfo.maximumPower = stationTemplate?.powerUnit === PowerUnits.KILO_WATT ? stationTemplate.power * 1000 - : stationTemplate.power; + : stationTemplate.power } - stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo); + stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo) stationInfo.firmwareVersionPattern = - stationTemplate?.firmwareVersionPattern ?? Constants.SEMVER_PATTERN; + stationTemplate?.firmwareVersionPattern ?? Constants.SEMVER_PATTERN if ( isNotEmptyString(stationInfo.firmwareVersion) && - new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion!) === false + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + !new RegExp(stationInfo.firmwareVersionPattern).test(stationInfo.firmwareVersion!) ) { logger.warn( `${this.logPrefix()} Firmware version '${stationInfo.firmwareVersion}' in template file ${ this.templateFile - } does not match firmware version pattern '${stationInfo.firmwareVersionPattern}'`, - ); + } does not match firmware version pattern '${stationInfo.firmwareVersionPattern}'` + ) } stationInfo.firmwareUpgrade = merge( { versionUpgrade: { - step: 1, + step: 1 }, - reset: true, + reset: true }, - stationTemplate?.firmwareUpgrade ?? {}, - ); + stationTemplate?.firmwareUpgrade ?? {} + ) stationInfo.resetTime = !isNullOrUndefined(stationTemplate?.resetTime) - ? secondsToMilliseconds(stationTemplate.resetTime!) - : Constants.CHARGING_STATION_DEFAULT_RESET_TIME; - return stationInfo; + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + secondsToMilliseconds(stationTemplate.resetTime!) + : Constants.CHARGING_STATION_DEFAULT_RESET_TIME + return stationInfo } - private getStationInfoFromFile( - stationInfoPersistentConfiguration = true, + private getStationInfoFromFile ( + stationInfoPersistentConfiguration = true ): ChargingStationInfo | undefined { - let stationInfo: ChargingStationInfo | undefined; - if (stationInfoPersistentConfiguration === true) { - stationInfo = this.getConfigurationFromFile()?.stationInfo; - if (stationInfo) { - delete stationInfo?.infoHash; + let stationInfo: ChargingStationInfo | undefined + if (stationInfoPersistentConfiguration) { + stationInfo = this.getConfigurationFromFile()?.stationInfo + if (stationInfo != null) { + delete stationInfo?.infoHash } } - return stationInfo; + return stationInfo } - private getStationInfo(): ChargingStationInfo { - const defaultStationInfo = Constants.DEFAULT_STATION_INFO; - const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate(); + private getStationInfo (): ChargingStationInfo { + const defaultStationInfo = Constants.DEFAULT_STATION_INFO + const stationInfoFromTemplate: ChargingStationInfo = this.getStationInfoFromTemplate() const stationInfoFromFile: ChargingStationInfo | undefined = this.getStationInfoFromFile( - stationInfoFromTemplate?.stationInfoPersistentConfiguration, - ); + stationInfoFromTemplate?.stationInfoPersistentConfiguration + ) // Priority: // 1. charging station info from template // 2. charging station info from configuration file if (stationInfoFromFile?.templateHash === stationInfoFromTemplate.templateHash) { - return { ...defaultStationInfo, ...stationInfoFromFile! }; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return { ...defaultStationInfo, ...stationInfoFromFile! } } - stationInfoFromFile && + stationInfoFromFile != null && propagateSerialNumber( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.getTemplateFromFile()!, stationInfoFromFile, - stationInfoFromTemplate, - ); - return { ...defaultStationInfo, ...stationInfoFromTemplate }; + stationInfoFromTemplate + ) + return { ...defaultStationInfo, ...stationInfoFromTemplate } } - private saveStationInfo(): void { + private saveStationInfo (): void { if (this.stationInfo?.stationInfoPersistentConfiguration === true) { - this.saveConfiguration(); + this.saveConfiguration() } } - private handleUnsupportedVersion(version: OCPPVersion | undefined) { - const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}`; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new BaseError(errorMsg); + private handleUnsupportedVersion (version: OCPPVersion | undefined): void { + const errorMsg = `Unsupported protocol version '${version}' configured in template file ${this.templateFile}` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) } - private initialize(): void { - const stationTemplate = this.getTemplateFromFile()!; - checkTemplate(stationTemplate, this.logPrefix(), this.templateFile); + private initialize (): void { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const stationTemplate = this.getTemplateFromFile()! + checkTemplate(stationTemplate, this.logPrefix(), this.templateFile) this.configurationFile = join( dirname(this.templateFile.replace('station-templates', 'configurations')), - `${getHashId(this.index, stationTemplate)}.json`, - ); - const stationConfiguration = this.getConfigurationFromFile(); + `${getHashId(this.index, stationTemplate)}.json` + ) + const stationConfiguration = this.getConfigurationFromFile() if ( stationConfiguration?.stationInfo?.templateHash === stationTemplate?.templateHash && // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - (stationConfiguration?.connectorsStatus || stationConfiguration?.evsesStatus) + (stationConfiguration?.connectorsStatus != null || stationConfiguration?.evsesStatus != null) ) { - checkConfiguration(stationConfiguration, this.logPrefix(), this.configurationFile); - this.initializeConnectorsOrEvsesFromFile(stationConfiguration); + checkConfiguration(stationConfiguration, this.logPrefix(), this.configurationFile) + this.initializeConnectorsOrEvsesFromFile(stationConfiguration) } else { - this.initializeConnectorsOrEvsesFromTemplate(stationTemplate); + this.initializeConnectorsOrEvsesFromTemplate(stationTemplate) } - this.stationInfo = this.getStationInfo(); + this.stationInfo = this.getStationInfo() if ( this.stationInfo.firmwareStatus === FirmwareStatus.Installing && isNotEmptyString(this.stationInfo.firmwareVersion) && @@ -1198,108 +1226,121 @@ export class ChargingStation extends EventEmitter { ) { const patternGroup: number | undefined = this.stationInfo.firmwareUpgrade?.versionUpgrade?.patternGroup ?? - this.stationInfo.firmwareVersion?.split('.').length; + this.stationInfo.firmwareVersion?.split('.').length + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const match = new RegExp(this.stationInfo.firmwareVersionPattern!) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion .exec(this.stationInfo.firmwareVersion!) - ?.slice(1, patternGroup! + 1); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ?.slice(1, patternGroup! + 1) if (!isNullOrUndefined(match)) { - const patchLevelIndex = match!.length - 1; - match![patchLevelIndex] = ( - convertToInt(match![patchLevelIndex]) + - this.stationInfo.firmwareUpgrade!.versionUpgrade!.step! - ).toString(); - this.stationInfo.firmwareVersion = match!.join('.'); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const patchLevelIndex = match!.length - 1 + // prettier-ignore + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + match![patchLevelIndex] = (convertToInt(match![patchLevelIndex]) + + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.stationInfo.firmwareUpgrade!.versionUpgrade!.step!).toString() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.stationInfo.firmwareVersion = match!.join('.') } } - this.saveStationInfo(); - this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl(); + this.saveStationInfo() + this.configuredSupervisionUrl = this.getConfiguredSupervisionUrl() if (this.stationInfo?.enableStatistics === true) { this.performanceStatistics = PerformanceStatistics.getInstance( this.stationInfo.hashId, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.stationInfo.chargingStationId!, - this.configuredSupervisionUrl, - ); + this.configuredSupervisionUrl + ) } - this.bootNotificationRequest = createBootNotificationRequest(this.stationInfo); - this.powerDivider = this.getPowerDivider(); + this.bootNotificationRequest = createBootNotificationRequest(this.stationInfo) + this.powerDivider = this.getPowerDivider() // OCPP configuration - this.ocppConfiguration = this.getOcppConfiguration(); - this.initializeOcppConfiguration(); - this.initializeOcppServices(); + this.ocppConfiguration = this.getOcppConfiguration() + this.initializeOcppConfiguration() + this.initializeOcppServices() this.once(ChargingStationEvents.accepted, () => { this.startMessageSequence().catch((error) => { - logger.error(`${this.logPrefix()} Error while starting the message sequence:`, error); - }); - }); + logger.error(`${this.logPrefix()} Error while starting the message sequence:`, error) + }) + }) if (this.stationInfo?.autoRegister === true) { this.bootNotificationResponse = { currentTime: new Date(), interval: millisecondsToSeconds(this.getHeartbeatInterval()), - status: RegistrationStatusEnumType.ACCEPTED, - }; + status: RegistrationStatusEnumType.ACCEPTED + } } } - private initializeOcppServices(): void { - const ocppVersion = this.stationInfo?.ocppVersion; + private initializeOcppServices (): void { + const ocppVersion = this.stationInfo?.ocppVersion switch (ocppVersion) { case OCPPVersion.VERSION_16: this.ocppIncomingRequestService = - OCPP16IncomingRequestService.getInstance(); + OCPP16IncomingRequestService.getInstance() this.ocppRequestService = OCPP16RequestService.getInstance( - OCPP16ResponseService.getInstance(), - ); - break; + OCPP16ResponseService.getInstance() + ) + break case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: this.ocppIncomingRequestService = - OCPP20IncomingRequestService.getInstance(); + OCPP20IncomingRequestService.getInstance() this.ocppRequestService = OCPP20RequestService.getInstance( - OCPP20ResponseService.getInstance(), - ); - break; + OCPP20ResponseService.getInstance() + ) + break default: - this.handleUnsupportedVersion(ocppVersion); - break; + this.handleUnsupportedVersion(ocppVersion) + break } } - private initializeOcppConfiguration(): void { + private initializeOcppConfiguration (): void { if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.HeartbeatInterval))) { - addConfigurationKey(this, StandardParametersKey.HeartbeatInterval, '0'); + addConfigurationKey(this, StandardParametersKey.HeartbeatInterval, '0') } if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.HeartBeatInterval))) { - addConfigurationKey(this, StandardParametersKey.HeartBeatInterval, '0', { visible: false }); + addConfigurationKey(this, StandardParametersKey.HeartBeatInterval, '0', { visible: false }) } if ( this.stationInfo?.supervisionUrlOcppConfiguration === true && isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion isNullOrUndefined(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)) ) { addConfigurationKey( this, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.stationInfo.supervisionUrlOcppKey!, this.configuredSupervisionUrl.href, - { reboot: true }, - ); + { reboot: true } + ) } else if ( this.stationInfo?.supervisionUrlOcppConfiguration === false && isNotEmptyString(this.stationInfo?.supervisionUrlOcppKey) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion !isNullOrUndefined(getConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!)) ) { - deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!, { save: false }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + deleteConfigurationKey(this, this.stationInfo.supervisionUrlOcppKey!, { save: false }) } if ( isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion isNullOrUndefined(getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!)) ) { addConfigurationKey( this, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.stationInfo.amperageLimitationOcppKey!, - ( - this.stationInfo.maximumAmperage! * getAmperageLimitationUnitDivider(this.stationInfo) - ).toString(), - ); + // prettier-ignore + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (this.stationInfo.maximumAmperage! * getAmperageLimitationUnitDivider(this.stationInfo)).toString() + ) } if ( isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles)) @@ -1307,341 +1348,351 @@ export class ChargingStation extends EventEmitter { addConfigurationKey( this, StandardParametersKey.SupportedFeatureProfiles, - `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.FirmwareManagement},${SupportedFeatureProfiles.LocalAuthListManagement},${SupportedFeatureProfiles.SmartCharging},${SupportedFeatureProfiles.RemoteTrigger}`, - ); + `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.FirmwareManagement},${SupportedFeatureProfiles.LocalAuthListManagement},${SupportedFeatureProfiles.SmartCharging},${SupportedFeatureProfiles.RemoteTrigger}` + ) } addConfigurationKey( this, StandardParametersKey.NumberOfConnectors, this.getNumberOfConnectors().toString(), { readonly: true }, - { overwrite: true }, - ); + { overwrite: true } + ) if ( isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.MeterValuesSampledData)) ) { addConfigurationKey( this, StandardParametersKey.MeterValuesSampledData, - MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, - ); + MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + ) } if ( isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.ConnectorPhaseRotation)) ) { - const connectorsPhaseRotation: string[] = []; + const connectorsPhaseRotation: string[] = [] if (this.hasEvses) { for (const evseStatus of this.evses.values()) { for (const connectorId of evseStatus.connectors.keys()) { connectorsPhaseRotation.push( - getPhaseRotationValue(connectorId, this.getNumberOfPhases())!, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + getPhaseRotationValue(connectorId, this.getNumberOfPhases())! + ) } } } else { for (const connectorId of this.connectors.keys()) { connectorsPhaseRotation.push( - getPhaseRotationValue(connectorId, this.getNumberOfPhases())!, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + getPhaseRotationValue(connectorId, this.getNumberOfPhases())! + ) } } addConfigurationKey( this, StandardParametersKey.ConnectorPhaseRotation, - connectorsPhaseRotation.toString(), - ); + connectorsPhaseRotation.toString() + ) } if ( isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests)) ) { - addConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests, 'true'); + addConfigurationKey(this, StandardParametersKey.AuthorizeRemoteTxRequests, 'true') } if ( isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled)) && getConfigurationKey(this, StandardParametersKey.SupportedFeatureProfiles)?.value?.includes( - SupportedFeatureProfiles.LocalAuthListManagement, - ) + SupportedFeatureProfiles.LocalAuthListManagement + ) === true ) { - addConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled, 'false'); + addConfigurationKey(this, StandardParametersKey.LocalAuthListEnabled, 'false') } if (isNullOrUndefined(getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut))) { addConfigurationKey( this, StandardParametersKey.ConnectionTimeOut, - Constants.DEFAULT_CONNECTION_TIMEOUT.toString(), - ); + Constants.DEFAULT_CONNECTION_TIMEOUT.toString() + ) } - this.saveOcppConfiguration(); + this.saveOcppConfiguration() } - private initializeConnectorsOrEvsesFromFile(configuration: ChargingStationConfiguration): void { - if (configuration?.connectorsStatus && !configuration?.evsesStatus) { + private initializeConnectorsOrEvsesFromFile (configuration: ChargingStationConfiguration): void { + if (configuration?.connectorsStatus != null && configuration?.evsesStatus == null) { for (const [connectorId, connectorStatus] of configuration.connectorsStatus.entries()) { - this.connectors.set(connectorId, cloneObject(connectorStatus)); + this.connectors.set(connectorId, cloneObject(connectorStatus)) } - } else if (configuration?.evsesStatus && !configuration?.connectorsStatus) { + } else if (configuration?.evsesStatus != null && configuration?.connectorsStatus == null) { for (const [evseId, evseStatusConfiguration] of configuration.evsesStatus.entries()) { - const evseStatus = cloneObject(evseStatusConfiguration); - delete evseStatus.connectorsStatus; + const evseStatus = cloneObject(evseStatusConfiguration) + delete evseStatus.connectorsStatus this.evses.set(evseId, { ...(evseStatus as EvseStatus), connectors: new Map( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion evseStatusConfiguration.connectorsStatus!.map((connectorStatus, connectorId) => [ connectorId, - connectorStatus, - ]), - ), - }); + connectorStatus + ]) + ) + }) } - } else if (configuration?.evsesStatus && configuration?.connectorsStatus) { - const errorMsg = `Connectors and evses defined at the same time in configuration file ${this.configurationFile}`; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new BaseError(errorMsg); + } else if (configuration?.evsesStatus != null && configuration?.connectorsStatus != null) { + const errorMsg = `Connectors and evses defined at the same time in configuration file ${this.configurationFile}` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) } else { - const errorMsg = `No connectors or evses defined in configuration file ${this.configurationFile}`; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new BaseError(errorMsg); + const errorMsg = `No connectors or evses defined in configuration file ${this.configurationFile}` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) } } - private initializeConnectorsOrEvsesFromTemplate(stationTemplate: ChargingStationTemplate) { - if (stationTemplate?.Connectors && !stationTemplate?.Evses) { - this.initializeConnectorsFromTemplate(stationTemplate); - } else if (stationTemplate?.Evses && !stationTemplate?.Connectors) { - this.initializeEvsesFromTemplate(stationTemplate); - } else if (stationTemplate?.Evses && stationTemplate?.Connectors) { - const errorMsg = `Connectors and evses defined at the same time in template file ${this.templateFile}`; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new BaseError(errorMsg); + private initializeConnectorsOrEvsesFromTemplate (stationTemplate: ChargingStationTemplate): void { + if (stationTemplate?.Connectors != null && stationTemplate?.Evses == null) { + this.initializeConnectorsFromTemplate(stationTemplate) + } else if (stationTemplate?.Evses != null && stationTemplate?.Connectors == null) { + this.initializeEvsesFromTemplate(stationTemplate) + } else if (stationTemplate?.Evses != null && stationTemplate?.Connectors != null) { + const errorMsg = `Connectors and evses defined at the same time in template file ${this.templateFile}` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) } else { - const errorMsg = `No connectors or evses defined in template file ${this.templateFile}`; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new BaseError(errorMsg); + const errorMsg = `No connectors or evses defined in template file ${this.templateFile}` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) } } - private initializeConnectorsFromTemplate(stationTemplate: ChargingStationTemplate): void { - if (!stationTemplate?.Connectors && this.connectors.size === 0) { - const errorMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined`; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new BaseError(errorMsg); + private initializeConnectorsFromTemplate (stationTemplate: ChargingStationTemplate): void { + if (stationTemplate?.Connectors == null && this.connectors.size === 0) { + const errorMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no connectors configuration defined` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) } - if (!stationTemplate?.Connectors?.[0]) { + if (stationTemplate?.Connectors?.[0] == null) { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile - } with no connector id 0 configuration`, - ); + } with no connector id 0 configuration` + ) } - if (stationTemplate?.Connectors) { + if (stationTemplate?.Connectors != null) { const { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors } = - checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile); + checkConnectorsConfiguration(stationTemplate, this.logPrefix(), this.templateFile) const connectorsConfigHash = createHash(Constants.DEFAULT_HASH_ALGORITHM) .update( - `${JSON.stringify(stationTemplate?.Connectors)}${configuredMaxConnectors.toString()}`, + `${JSON.stringify(stationTemplate?.Connectors)}${configuredMaxConnectors.toString()}` ) - .digest('hex'); + .digest('hex') const connectorsConfigChanged = - this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash; + this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash if (this.connectors?.size === 0 || connectorsConfigChanged) { - connectorsConfigChanged && this.connectors.clear(); - this.connectorsConfigurationHash = connectorsConfigHash; + connectorsConfigChanged && this.connectors.clear() + this.connectorsConfigurationHash = connectorsConfigHash if (templateMaxConnectors > 0) { for (let connectorId = 0; connectorId <= configuredMaxConnectors; connectorId++) { if ( connectorId === 0 && - (!stationTemplate?.Connectors?.[connectorId] || - this.getUseConnectorId0(stationTemplate) === false) + (stationTemplate?.Connectors?.[connectorId] == null || + !this.getUseConnectorId0(stationTemplate)) ) { - continue; + continue } const templateConnectorId = - connectorId > 0 && stationTemplate?.randomConnectors + connectorId > 0 && stationTemplate?.randomConnectors === true ? getRandomInteger(templateMaxAvailableConnectors, 1) - : connectorId; - const connectorStatus = stationTemplate?.Connectors[templateConnectorId]; + : connectorId + const connectorStatus = stationTemplate?.Connectors[templateConnectorId] checkStationInfoConnectorStatus( templateConnectorId, connectorStatus, this.logPrefix(), - this.templateFile, - ); - this.connectors.set(connectorId, cloneObject(connectorStatus)); + this.templateFile + ) + this.connectors.set(connectorId, cloneObject(connectorStatus)) } - initializeConnectorsMapStatus(this.connectors, this.logPrefix()); - this.saveConnectorsStatus(); + initializeConnectorsMapStatus(this.connectors, this.logPrefix()) + this.saveConnectorsStatus() } else { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile - } with no connectors configuration defined, cannot create connectors`, - ); + } with no connectors configuration defined, cannot create connectors` + ) } } } else { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile - } with no connectors configuration defined, using already defined connectors`, - ); + } with no connectors configuration defined, using already defined connectors` + ) } } - private initializeEvsesFromTemplate(stationTemplate: ChargingStationTemplate): void { - if (!stationTemplate?.Evses && this.evses.size === 0) { - const errorMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new BaseError(errorMsg); + private initializeEvsesFromTemplate (stationTemplate: ChargingStationTemplate): void { + if (stationTemplate?.Evses == null && this.evses.size === 0) { + const errorMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) } - if (!stationTemplate?.Evses?.[0]) { + if (stationTemplate?.Evses?.[0] == null) { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile - } with no evse id 0 configuration`, - ); + } with no evse id 0 configuration` + ) } - if (!stationTemplate?.Evses?.[0]?.Connectors?.[0]) { + if (stationTemplate?.Evses?.[0]?.Connectors?.[0] == null) { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile - } with evse id 0 with no connector id 0 configuration`, - ); + } with evse id 0 with no connector id 0 configuration` + ) } if (Object.keys(stationTemplate?.Evses?.[0]?.Connectors as object).length > 1) { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile - } with evse id 0 with more than one connector configuration, only connector id 0 configuration will be used`, - ); + } with evse id 0 with more than one connector configuration, only connector id 0 configuration will be used` + ) } - if (stationTemplate?.Evses) { + if (stationTemplate?.Evses != null) { const evsesConfigHash = createHash(Constants.DEFAULT_HASH_ALGORITHM) .update(JSON.stringify(stationTemplate?.Evses)) - .digest('hex'); + .digest('hex') const evsesConfigChanged = - this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash; + this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash if (this.evses?.size === 0 || evsesConfigChanged) { - evsesConfigChanged && this.evses.clear(); - this.evsesConfigurationHash = evsesConfigHash; - const templateMaxEvses = getMaxNumberOfEvses(stationTemplate?.Evses); + evsesConfigChanged && this.evses.clear() + this.evsesConfigurationHash = evsesConfigHash + const templateMaxEvses = getMaxNumberOfEvses(stationTemplate?.Evses) if (templateMaxEvses > 0) { for (const evseKey in stationTemplate.Evses) { - const evseId = convertToInt(evseKey); + const evseId = convertToInt(evseKey) this.evses.set(evseId, { connectors: buildConnectorsMap( stationTemplate?.Evses[evseKey]?.Connectors, this.logPrefix(), - this.templateFile, + this.templateFile ), - availability: AvailabilityType.Operative, - }); - initializeConnectorsMapStatus(this.evses.get(evseId)!.connectors, this.logPrefix()); + availability: AvailabilityType.Operative + }) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + initializeConnectorsMapStatus(this.evses.get(evseId)!.connectors, this.logPrefix()) } - this.saveEvsesStatus(); + this.saveEvsesStatus() } else { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile - } with no evses configuration defined, cannot create evses`, - ); + } with no evses configuration defined, cannot create evses` + ) } } } else { logger.warn( `${this.logPrefix()} Charging station information from template ${ this.templateFile - } with no evses configuration defined, using already defined evses`, - ); + } with no evses configuration defined, using already defined evses` + ) } } - private getConfigurationFromFile(): ChargingStationConfiguration | undefined { - let configuration: ChargingStationConfiguration | undefined; + private getConfigurationFromFile (): ChargingStationConfiguration | undefined { + let configuration: ChargingStationConfiguration | undefined if (isNotEmptyString(this.configurationFile) && existsSync(this.configurationFile)) { try { if (this.sharedLRUCache.hasChargingStationConfiguration(this.configurationFileHash)) { configuration = this.sharedLRUCache.getChargingStationConfiguration( - this.configurationFileHash, - ); + this.configurationFileHash + ) } else { - const measureId = `${FileType.ChargingStationConfiguration} read`; - const beginId = PerformanceStatistics.beginMeasure(measureId); + const measureId = `${FileType.ChargingStationConfiguration} read` + const beginId = PerformanceStatistics.beginMeasure(measureId) configuration = JSON.parse( - readFileSync(this.configurationFile, 'utf8'), - ) as ChargingStationConfiguration; - PerformanceStatistics.endMeasure(measureId, beginId); - this.sharedLRUCache.setChargingStationConfiguration(configuration); - this.configurationFileHash = configuration.configurationHash!; + readFileSync(this.configurationFile, 'utf8') + ) as ChargingStationConfiguration + PerformanceStatistics.endMeasure(measureId, beginId) + this.sharedLRUCache.setChargingStationConfiguration(configuration) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.configurationFileHash = configuration.configurationHash! } } catch (error) { handleFileException( this.configurationFile, FileType.ChargingStationConfiguration, error as NodeJS.ErrnoException, - this.logPrefix(), - ); + this.logPrefix() + ) } } - return configuration; + return configuration } - private saveAutomaticTransactionGeneratorConfiguration(): void { + private saveAutomaticTransactionGeneratorConfiguration (): void { if (this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration === true) { - this.saveConfiguration(); + this.saveConfiguration() } } - private saveConnectorsStatus() { - this.saveConfiguration(); + private saveConnectorsStatus (): void { + this.saveConfiguration() } - private saveEvsesStatus() { - this.saveConfiguration(); + private saveEvsesStatus (): void { + this.saveConfiguration() } - private saveConfiguration(): void { + private saveConfiguration (): void { if (isNotEmptyString(this.configurationFile)) { try { if (!existsSync(dirname(this.configurationFile))) { - mkdirSync(dirname(this.configurationFile), { recursive: true }); + mkdirSync(dirname(this.configurationFile), { recursive: true }) } - let configurationData: ChargingStationConfiguration = this.getConfigurationFromFile() - ? cloneObject(this.getConfigurationFromFile()!) - : {}; - if (this.stationInfo?.stationInfoPersistentConfiguration === true && this.stationInfo) { - configurationData.stationInfo = this.stationInfo; + let configurationData: ChargingStationConfiguration = + this.getConfigurationFromFile() != null + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + cloneObject(this.getConfigurationFromFile()!) + : {} + if ( + this.stationInfo?.stationInfoPersistentConfiguration === true && + this.stationInfo != null + ) { + configurationData.stationInfo = this.stationInfo } else { - delete configurationData.stationInfo; + delete configurationData.stationInfo } if ( this.stationInfo?.ocppPersistentConfiguration === true && Array.isArray(this.ocppConfiguration?.configurationKey) ) { - configurationData.configurationKey = this.ocppConfiguration?.configurationKey; + configurationData.configurationKey = this.ocppConfiguration?.configurationKey } else { - delete configurationData.configurationKey; + delete configurationData.configurationKey } configurationData = merge( configurationData, - buildChargingStationAutomaticTransactionGeneratorConfiguration(this), - ); + buildChargingStationAutomaticTransactionGeneratorConfiguration(this) + ) if ( - !this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration || - !this.getAutomaticTransactionGeneratorConfiguration() + this.stationInfo?.automaticTransactionGeneratorPersistentConfiguration === false || + this.getAutomaticTransactionGeneratorConfiguration() == null ) { - delete configurationData.automaticTransactionGenerator; + delete configurationData.automaticTransactionGenerator } if (this.connectors.size > 0) { - configurationData.connectorsStatus = buildConnectorsStatus(this); + configurationData.connectorsStatus = buildConnectorsStatus(this) } else { - delete configurationData.connectorsStatus; + delete configurationData.connectorsStatus } if (this.evses.size > 0) { - configurationData.evsesStatus = buildEvsesStatus(this); + configurationData.evsesStatus = buildEvsesStatus(this) } else { - delete configurationData.evsesStatus; + delete configurationData.evsesStatus } - delete configurationData.configurationHash; + delete configurationData.configurationHash const configurationHash = createHash(Constants.DEFAULT_HASH_ALGORITHM) .update( JSON.stringify({ @@ -1649,259 +1700,262 @@ export class ChargingStation extends EventEmitter { configurationKey: configurationData.configurationKey, automaticTransactionGenerator: configurationData.automaticTransactionGenerator, ...(this.connectors.size > 0 && { - connectorsStatus: configurationData.connectorsStatus, + connectorsStatus: configurationData.connectorsStatus }), - ...(this.evses.size > 0 && { evsesStatus: configurationData.evsesStatus }), - } as ChargingStationConfiguration), + ...(this.evses.size > 0 && { evsesStatus: configurationData.evsesStatus }) + } satisfies ChargingStationConfiguration) ) - .digest('hex'); + .digest('hex') if (this.configurationFileHash !== configurationHash) { AsyncLock.runExclusive(AsyncLockType.configuration, () => { - configurationData.configurationHash = configurationHash; - const measureId = `${FileType.ChargingStationConfiguration} write`; - const beginId = PerformanceStatistics.beginMeasure(measureId); + configurationData.configurationHash = configurationHash + const measureId = `${FileType.ChargingStationConfiguration} write` + const beginId = PerformanceStatistics.beginMeasure(measureId) writeFileSync( this.configurationFile, JSON.stringify(configurationData, undefined, 2), - 'utf8', - ); - PerformanceStatistics.endMeasure(measureId, beginId); - this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash); - this.sharedLRUCache.setChargingStationConfiguration(configurationData); - this.configurationFileHash = configurationHash; + 'utf8' + ) + PerformanceStatistics.endMeasure(measureId, beginId) + this.sharedLRUCache.deleteChargingStationConfiguration(this.configurationFileHash) + this.sharedLRUCache.setChargingStationConfiguration(configurationData) + this.configurationFileHash = configurationHash }).catch((error) => { handleFileException( this.configurationFile, FileType.ChargingStationConfiguration, error as NodeJS.ErrnoException, - this.logPrefix(), - ); - }); + this.logPrefix() + ) + }) } else { logger.debug( `${this.logPrefix()} Not saving unchanged charging station configuration file ${ this.configurationFile - }`, - ); + }` + ) } } catch (error) { handleFileException( this.configurationFile, FileType.ChargingStationConfiguration, error as NodeJS.ErrnoException, - this.logPrefix(), - ); + this.logPrefix() + ) } } else { logger.error( - `${this.logPrefix()} Trying to save charging station configuration to undefined configuration file`, - ); + `${this.logPrefix()} Trying to save charging station configuration to undefined configuration file` + ) } } - private getOcppConfigurationFromTemplate(): ChargingStationOcppConfiguration | undefined { - return this.getTemplateFromFile()?.Configuration; + private getOcppConfigurationFromTemplate (): ChargingStationOcppConfiguration | undefined { + return this.getTemplateFromFile()?.Configuration } - private getOcppConfigurationFromFile(): ChargingStationOcppConfiguration | undefined { - const configurationKey = this.getConfigurationFromFile()?.configurationKey; + private getOcppConfigurationFromFile (): ChargingStationOcppConfiguration | undefined { + const configurationKey = this.getConfigurationFromFile()?.configurationKey if (this.stationInfo?.ocppPersistentConfiguration === true && Array.isArray(configurationKey)) { - return { configurationKey }; + return { configurationKey } } - return undefined; + return undefined } - private getOcppConfiguration(): ChargingStationOcppConfiguration | undefined { + private getOcppConfiguration (): ChargingStationOcppConfiguration | undefined { let ocppConfiguration: ChargingStationOcppConfiguration | undefined = - this.getOcppConfigurationFromFile(); - if (!ocppConfiguration) { - ocppConfiguration = this.getOcppConfigurationFromTemplate(); + this.getOcppConfigurationFromFile() + if (ocppConfiguration == null) { + ocppConfiguration = this.getOcppConfigurationFromTemplate() } - return ocppConfiguration; + return ocppConfiguration } - private async onOpen(): Promise { - if (this.isWebSocketConnectionOpened() === true) { + private async onOpen (): Promise { + if (this.isWebSocketConnectionOpened()) { logger.info( - `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} succeeded`, - ); - let registrationRetryCount = 0; - if (this.isRegistered() === false) { + `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} succeeded` + ) + let registrationRetryCount = 0 + if (!this.isRegistered()) { // Send BootNotification do { this.bootNotificationResponse = await this.ocppRequestService.requestHandler< - BootNotificationRequest, - BootNotificationResponse + BootNotificationRequest, + BootNotificationResponse >(this, RequestCommand.BOOT_NOTIFICATION, this.bootNotificationRequest, { - skipBufferingOnError: true, - }); - if (this.isRegistered() === false) { - this.stationInfo?.registrationMaxRetries !== -1 && ++registrationRetryCount; + skipBufferingOnError: true + }) + if (!this.isRegistered()) { + this.stationInfo?.registrationMaxRetries !== -1 && ++registrationRetryCount await sleep( - this?.bootNotificationResponse?.interval + this?.bootNotificationResponse?.interval != null ? secondsToMilliseconds(this.bootNotificationResponse.interval) - : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL, - ); + : Constants.DEFAULT_BOOT_NOTIFICATION_INTERVAL + ) } } while ( - this.isRegistered() === false && + !this.isRegistered() && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion (registrationRetryCount <= this.stationInfo.registrationMaxRetries! || this.stationInfo?.registrationMaxRetries === -1) - ); + ) } - if (this.isRegistered() === true) { - this.emit(ChargingStationEvents.registered); - if (this.inAcceptedState() === true) { - this.emit(ChargingStationEvents.accepted); + if (this.isRegistered()) { + this.emit(ChargingStationEvents.registered) + if (this.inAcceptedState()) { + this.emit(ChargingStationEvents.accepted) } } else { logger.error( `${this.logPrefix()} Registration failure: maximum retries reached (${registrationRetryCount}) or retry disabled (${this - .stationInfo?.registrationMaxRetries})`, - ); + .stationInfo?.registrationMaxRetries})` + ) } - this.autoReconnectRetryCount = 0; - this.emit(ChargingStationEvents.updated); + this.autoReconnectRetryCount = 0 + this.emit(ChargingStationEvents.updated) } else { logger.warn( - `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} failed`, - ); + `${this.logPrefix()} Connection to OCPP server through ${this.wsConnectionUrl.toString()} failed` + ) } } - private async onClose(code: WebSocketCloseEventStatusCode, reason: Buffer): Promise { + private async onClose (code: WebSocketCloseEventStatusCode, reason: Buffer): Promise { switch (code) { // Normal close case WebSocketCloseEventStatusCode.CLOSE_NORMAL: case WebSocketCloseEventStatusCode.CLOSE_NO_STATUS: logger.info( `${this.logPrefix()} WebSocket normally closed with status '${getWebSocketCloseEventStatusString( - code, - )}' and reason '${reason.toString()}'`, - ); - this.autoReconnectRetryCount = 0; - break; + code + )}' and reason '${reason.toString()}'` + ) + this.autoReconnectRetryCount = 0 + break // Abnormal close default: logger.error( `${this.logPrefix()} WebSocket abnormally closed with status '${getWebSocketCloseEventStatusString( - code, - )}' and reason '${reason.toString()}'`, - ); - this.started === true && (await this.reconnect()); - break; + code + )}' and reason '${reason.toString()}'` + ) + this.started && (await this.reconnect()) + break } - this.emit(ChargingStationEvents.updated); + this.emit(ChargingStationEvents.updated) } - private getCachedRequest(messageType: MessageType, messageId: string): CachedRequest | undefined { - const cachedRequest = this.requests.get(messageId); - if (Array.isArray(cachedRequest) === true) { - return cachedRequest; + private getCachedRequest (messageType: MessageType, messageId: string): CachedRequest | undefined { + const cachedRequest = this.requests.get(messageId) + if (Array.isArray(cachedRequest)) { + return cachedRequest } throw new OCPPError( ErrorType.PROTOCOL_ERROR, `Cached request for message id ${messageId} ${getMessageTypeString( - messageType, + messageType )} is not an array`, undefined, - cachedRequest, - ); + cachedRequest + ) } - private async handleIncomingMessage(request: IncomingRequest): Promise { - const [messageType, messageId, commandName, commandPayload] = request; + private async handleIncomingMessage (request: IncomingRequest): Promise { + const [messageType, messageId, commandName, commandPayload] = request if (this.stationInfo?.enableStatistics === true) { - this.performanceStatistics?.addRequestStatistic(commandName, messageType); + this.performanceStatistics?.addRequestStatistic(commandName, messageType) } logger.debug( `${this.logPrefix()} << Command '${commandName}' received request payload: ${JSON.stringify( - request, - )}`, - ); + request + )}` + ) // Process the message await this.ocppIncomingRequestService.incomingRequestHandler( this, messageId, commandName, - commandPayload, - ); - this.emit(ChargingStationEvents.updated); + commandPayload + ) + this.emit(ChargingStationEvents.updated) } - private handleResponseMessage(response: Response): void { - const [messageType, messageId, commandPayload] = response; - if (this.requests.has(messageId) === false) { + private handleResponseMessage (response: Response): void { + const [messageType, messageId, commandPayload] = response + if (!this.requests.has(messageId)) { // Error throw new OCPPError( ErrorType.INTERNAL_ERROR, `Response for unknown message id ${messageId}`, undefined, - commandPayload, - ); + commandPayload + ) } // Respond + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const [responseCallback, , requestCommandName, requestPayload] = this.getCachedRequest( messageType, - messageId, - )!; + messageId + )! logger.debug( `${this.logPrefix()} << Command '${ requestCommandName ?? Constants.UNKNOWN_COMMAND - }' received response payload: ${JSON.stringify(response)}`, - ); - responseCallback(commandPayload, requestPayload); + }' received response payload: ${JSON.stringify(response)}` + ) + responseCallback(commandPayload, requestPayload) } - private handleErrorMessage(errorResponse: ErrorResponse): void { - const [messageType, messageId, errorType, errorMessage, errorDetails] = errorResponse; - if (this.requests.has(messageId) === false) { + private handleErrorMessage (errorResponse: ErrorResponse): void { + const [messageType, messageId, errorType, errorMessage, errorDetails] = errorResponse + if (!this.requests.has(messageId)) { // Error throw new OCPPError( ErrorType.INTERNAL_ERROR, `Error response for unknown message id ${messageId}`, undefined, - { errorType, errorMessage, errorDetails }, - ); + { errorType, errorMessage, errorDetails } + ) } - const [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)! logger.debug( `${this.logPrefix()} << Command '${ requestCommandName ?? Constants.UNKNOWN_COMMAND - }' received error response payload: ${JSON.stringify(errorResponse)}`, - ); - errorCallback(new OCPPError(errorType, errorMessage, requestCommandName, errorDetails)); + }' received error response payload: ${JSON.stringify(errorResponse)}` + ) + errorCallback(new OCPPError(errorType, errorMessage, requestCommandName, errorDetails)) } - private async onMessage(data: RawData): Promise { - let request: IncomingRequest | Response | ErrorResponse | undefined; - let messageType: MessageType | undefined; - let errorMsg: string; + private async onMessage (data: RawData): Promise { + let request: IncomingRequest | Response | ErrorResponse | undefined + let messageType: MessageType | undefined + let errorMsg: string try { // eslint-disable-next-line @typescript-eslint/no-base-to-string - request = JSON.parse(data.toString()) as IncomingRequest | Response | ErrorResponse; - if (Array.isArray(request) === true) { - [messageType] = request; + request = JSON.parse(data.toString()) as IncomingRequest | Response | ErrorResponse + if (Array.isArray(request)) { + [messageType] = request // Check the type of message switch (messageType) { // Incoming Message case MessageType.CALL_MESSAGE: - await this.handleIncomingMessage(request as IncomingRequest); - break; + await this.handleIncomingMessage(request as IncomingRequest) + break // Response Message case MessageType.CALL_RESULT_MESSAGE: - this.handleResponseMessage(request as Response); - break; + this.handleResponseMessage(request as Response) + break // Error Message case MessageType.CALL_ERROR_MESSAGE: - this.handleErrorMessage(request as ErrorResponse); - break; + this.handleErrorMessage(request as ErrorResponse) + break // Unknown Message default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - errorMsg = `Wrong message type ${messageType}`; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new OCPPError(ErrorType.PROTOCOL_ERROR, errorMsg); + errorMsg = `Wrong message type ${messageType}` + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new OCPPError(ErrorType.PROTOCOL_ERROR, errorMsg) } } else { throw new OCPPError( @@ -1909,41 +1963,43 @@ export class ChargingStation extends EventEmitter { 'Incoming message is not an array', undefined, { - request, - }, - ); + request + } + ) } } catch (error) { - let commandName: IncomingRequestCommand | undefined; - let requestCommandName: RequestCommand | IncomingRequestCommand | undefined; - let errorCallback: ErrorCallback; - const [, messageId] = request!; + let commandName: IncomingRequestCommand | undefined + let requestCommandName: RequestCommand | IncomingRequestCommand | undefined + let errorCallback: ErrorCallback + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const [, messageId] = request! switch (messageType) { case MessageType.CALL_MESSAGE: - [, , commandName] = request as IncomingRequest; + [, , commandName] = request as IncomingRequest // Send error - await this.ocppRequestService.sendError(this, messageId, error as OCPPError, commandName); - break; + await this.ocppRequestService.sendError(this, messageId, error as OCPPError, commandName) + break case MessageType.CALL_RESULT_MESSAGE: case MessageType.CALL_ERROR_MESSAGE: - if (this.requests.has(messageId) === true) { - [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)!; + if (this.requests.has(messageId)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + [, errorCallback, requestCommandName] = this.getCachedRequest(messageType, messageId)! // Reject the deferred promise in case of error at response handling (rejecting an already fulfilled promise is a no-op) - errorCallback(error as OCPPError, false); + errorCallback(error as OCPPError, false) } else { // Remove the request from the cache in case of error at response handling - this.requests.delete(messageId); + this.requests.delete(messageId) } - break; + break } - if (error instanceof OCPPError === false) { + if (!(error instanceof OCPPError)) { logger.warn( `${this.logPrefix()} Error thrown at incoming OCPP command '${ commandName ?? requestCommandName ?? Constants.UNKNOWN_COMMAND // eslint-disable-next-line @typescript-eslint/no-base-to-string }' message '${data.toString()}' handling is not an OCPPError:`, - error, - ); + error + ) } logger.error( `${this.logPrefix()} Incoming OCPP command '${ @@ -1954,141 +2010,147 @@ export class ChargingStation extends EventEmitter { ? ` matching cached request '${JSON.stringify(this.requests.get(messageId))}'` : '' } processing error:`, - error, - ); + error + ) } } - private onPing(): void { - logger.debug(`${this.logPrefix()} Received a WS ping (rfc6455) from the server`); + private onPing (): void { + logger.debug(`${this.logPrefix()} Received a WS ping (rfc6455) from the server`) } - private onPong(): void { - logger.debug(`${this.logPrefix()} Received a WS pong (rfc6455) from the server`); + private onPong (): void { + logger.debug(`${this.logPrefix()} Received a WS pong (rfc6455) from the server`) } - private onError(error: WSError): void { - this.closeWSConnection(); - logger.error(`${this.logPrefix()} WebSocket error:`, error); + private onError (error: WSError): void { + this.closeWSConnection() + logger.error(`${this.logPrefix()} WebSocket error:`, error) } - private getEnergyActiveImportRegister(connectorStatus: ConnectorStatus, rounded = false): number { + private getEnergyActiveImportRegister (connectorStatus: ConnectorStatus, rounded = false): number { if (this.stationInfo?.meteringPerTransaction === true) { return ( - (rounded === true - ? Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue!) + (rounded + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + Math.round(connectorStatus.transactionEnergyActiveImportRegisterValue!) : connectorStatus?.transactionEnergyActiveImportRegisterValue) ?? 0 - ); + ) } return ( - (rounded === true - ? Math.round(connectorStatus.energyActiveImportRegisterValue!) + (rounded + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + Math.round(connectorStatus.energyActiveImportRegisterValue!) : connectorStatus?.energyActiveImportRegisterValue) ?? 0 - ); + ) } - private getUseConnectorId0(stationTemplate?: ChargingStationTemplate): boolean { - return stationTemplate?.useConnectorId0 ?? true; + private getUseConnectorId0 (stationTemplate?: ChargingStationTemplate): boolean { + return stationTemplate?.useConnectorId0 ?? true } - private async stopRunningTransactions(reason?: StopTransactionReason): Promise { + private async stopRunningTransactions (reason?: StopTransactionReason): Promise { if (this.hasEvses) { for (const [evseId, evseStatus] of this.evses) { if (evseId === 0) { - continue; + continue } for (const [connectorId, connectorStatus] of evseStatus.connectors) { if (connectorStatus.transactionStarted === true) { - await this.stopTransactionOnConnector(connectorId, reason); + await this.stopTransactionOnConnector(connectorId, reason) } } } } else { for (const connectorId of this.connectors.keys()) { if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted === true) { - await this.stopTransactionOnConnector(connectorId, reason); + await this.stopTransactionOnConnector(connectorId, reason) } } } } // 0 for disabling - private getConnectionTimeout(): number { + private getConnectionTimeout (): number { if (getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut) !== undefined) { return convertToInt( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion getConfigurationKey(this, StandardParametersKey.ConnectionTimeOut)!.value! ?? - Constants.DEFAULT_CONNECTION_TIMEOUT, - ); + Constants.DEFAULT_CONNECTION_TIMEOUT + ) } - return Constants.DEFAULT_CONNECTION_TIMEOUT; + return Constants.DEFAULT_CONNECTION_TIMEOUT } - private getPowerDivider(): number { - let powerDivider = this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors(); + private getPowerDivider (): number { + let powerDivider = this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors() if (this.stationInfo?.powerSharedByConnectors === true) { - powerDivider = this.getNumberOfRunningTransactions(); + powerDivider = this.getNumberOfRunningTransactions() } - return powerDivider; + return powerDivider } - private getMaximumAmperage(stationInfo?: ChargingStationInfo): number | undefined { - const maximumPower = (stationInfo ?? this.stationInfo).maximumPower!; + private getMaximumAmperage (stationInfo?: ChargingStationInfo): number | undefined { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const maximumPower = (stationInfo ?? this.stationInfo).maximumPower! switch (this.getCurrentOutType(stationInfo)) { case CurrentType.AC: return ACElectricUtils.amperagePerPhaseFromPower( this.getNumberOfPhases(stationInfo), maximumPower / (this.hasEvses ? this.getNumberOfEvses() : this.getNumberOfConnectors()), - this.getVoltageOut(stationInfo), - ); + this.getVoltageOut(stationInfo) + ) case CurrentType.DC: - return DCElectricUtils.amperage(maximumPower, this.getVoltageOut(stationInfo)); + return DCElectricUtils.amperage(maximumPower, this.getVoltageOut(stationInfo)) } } - private getCurrentOutType(stationInfo?: ChargingStationInfo): CurrentType { - return (stationInfo ?? this.stationInfo).currentOutType ?? CurrentType.AC; + private getCurrentOutType (stationInfo?: ChargingStationInfo): CurrentType { + return (stationInfo ?? this.stationInfo).currentOutType ?? CurrentType.AC } - private getVoltageOut(stationInfo?: ChargingStationInfo): Voltage { + private getVoltageOut (stationInfo?: ChargingStationInfo): Voltage { return ( (stationInfo ?? this.stationInfo).voltageOut ?? getDefaultVoltageOut(this.getCurrentOutType(stationInfo), this.logPrefix(), this.templateFile) - ); + ) } - private getAmperageLimitation(): number | undefined { + private getAmperageLimitation (): number | undefined { if ( isNotEmptyString(this.stationInfo?.amperageLimitationOcppKey) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!) !== undefined ) { return ( convertToInt( - getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!)?.value, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + getConfigurationKey(this, this.stationInfo.amperageLimitationOcppKey!)?.value ) / getAmperageLimitationUnitDivider(this.stationInfo) - ); + ) } } - private async startMessageSequence(): Promise { + private async startMessageSequence (): Promise { if (this.stationInfo?.autoRegister === true) { await this.ocppRequestService.requestHandler< - BootNotificationRequest, - BootNotificationResponse + BootNotificationRequest, + BootNotificationResponse >(this, RequestCommand.BOOT_NOTIFICATION, this.bootNotificationRequest, { - skipBufferingOnError: true, - }); + skipBufferingOnError: true + }) } // Start WebSocket ping - this.startWebSocketPing(); + this.startWebSocketPing() // Start heartbeat - this.startHeartbeat(); + this.startHeartbeat() // Initialize connectors status if (this.hasEvses) { for (const [evseId, evseStatus] of this.evses) { if (evseId > 0) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { - const connectorBootStatus = getBootConnectorStatus(this, connectorId, connectorStatus); - await sendAndSetConnectorStatus(this, connectorId, connectorBootStatus, evseId); + const connectorBootStatus = getBootConnectorStatus(this, connectorId, connectorStatus) + await sendAndSetConnectorStatus(this, connectorId, connectorBootStatus, evseId) } } } @@ -2098,50 +2160,51 @@ export class ChargingStation extends EventEmitter { const connectorBootStatus = getBootConnectorStatus( this, connectorId, - this.getConnectorStatus(connectorId)!, - ); - await sendAndSetConnectorStatus(this, connectorId, connectorBootStatus); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.getConnectorStatus(connectorId)! + ) + await sendAndSetConnectorStatus(this, connectorId, connectorBootStatus) } } } if (this.stationInfo.firmwareStatus === FirmwareStatus.Installing) { await this.ocppRequestService.requestHandler< - FirmwareStatusNotificationRequest, - FirmwareStatusNotificationResponse + FirmwareStatusNotificationRequest, + FirmwareStatusNotificationResponse >(this, RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: FirmwareStatus.Installed, - }); - this.stationInfo.firmwareStatus = FirmwareStatus.Installed; + status: FirmwareStatus.Installed + }) + this.stationInfo.firmwareStatus = FirmwareStatus.Installed } // Start the ATG - if (this.getAutomaticTransactionGeneratorConfiguration().enable === true) { - this.startAutomaticTransactionGenerator(); + if (this.getAutomaticTransactionGeneratorConfiguration().enable) { + this.startAutomaticTransactionGenerator() } - this.flushMessageBuffer(); + this.flushMessageBuffer() } - private async stopMessageSequence( + private async stopMessageSequence ( reason?: StopTransactionReason, - stopTransactions = this.stationInfo?.stopTransactionsOnStopped, + stopTransactions = this.stationInfo?.stopTransactionsOnStopped ): Promise { // Stop WebSocket ping - this.stopWebSocketPing(); + this.stopWebSocketPing() // Stop heartbeat - this.stopHeartbeat(); + this.stopHeartbeat() // Stop the ATG if (this.automaticTransactionGenerator?.started === true) { - this.stopAutomaticTransactionGenerator(); + this.stopAutomaticTransactionGenerator() } // Stop ongoing transactions - stopTransactions && (await this.stopRunningTransactions(reason)); + stopTransactions === true && (await this.stopRunningTransactions(reason)) if (this.hasEvses) { for (const [evseId, evseStatus] of this.evses) { if (evseId > 0) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { await this.ocppRequestService.requestHandler< - StatusNotificationRequest, - StatusNotificationResponse + StatusNotificationRequest, + StatusNotificationResponse >( this, RequestCommand.STATUS_NOTIFICATION, @@ -2149,10 +2212,10 @@ export class ChargingStation extends EventEmitter { this, connectorId, ConnectorStatusEnum.Unavailable, - evseId, - ), - ); - delete connectorStatus?.status; + evseId + ) + ) + delete connectorStatus?.status } } } @@ -2160,154 +2223,156 @@ export class ChargingStation extends EventEmitter { for (const connectorId of this.connectors.keys()) { if (connectorId > 0) { await this.ocppRequestService.requestHandler< - StatusNotificationRequest, - StatusNotificationResponse + StatusNotificationRequest, + StatusNotificationResponse >( this, RequestCommand.STATUS_NOTIFICATION, - buildStatusNotificationRequest(this, connectorId, ConnectorStatusEnum.Unavailable), - ); - delete this.getConnectorStatus(connectorId)?.status; + buildStatusNotificationRequest(this, connectorId, ConnectorStatusEnum.Unavailable) + ) + delete this.getConnectorStatus(connectorId)?.status } } } } - private startWebSocketPing(): void { + private startWebSocketPing (): void { const webSocketPingInterval: number = getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval) !== undefined ? convertToInt( - getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value, - ) - : 0; + getConfigurationKey(this, StandardParametersKey.WebSocketPingInterval)?.value + ) + : 0 if (webSocketPingInterval > 0 && this.webSocketPingSetInterval === undefined) { this.webSocketPingSetInterval = setInterval(() => { - if (this.isWebSocketConnectionOpened() === true) { - this.wsConnection?.ping(); + if (this.isWebSocketConnectionOpened()) { + this.wsConnection?.ping() } - }, secondsToMilliseconds(webSocketPingInterval)); + }, secondsToMilliseconds(webSocketPingInterval)) logger.info( `${this.logPrefix()} WebSocket ping started every ${formatDurationSeconds( - webSocketPingInterval, - )}`, - ); + webSocketPingInterval + )}` + ) } else if (this.webSocketPingSetInterval !== undefined) { logger.info( `${this.logPrefix()} WebSocket ping already started every ${formatDurationSeconds( - webSocketPingInterval, - )}`, - ); + webSocketPingInterval + )}` + ) } else { logger.error( - `${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval}, not starting the WebSocket ping`, - ); + `${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval}, not starting the WebSocket ping` + ) } } - private stopWebSocketPing(): void { + private stopWebSocketPing (): void { if (this.webSocketPingSetInterval !== undefined) { - clearInterval(this.webSocketPingSetInterval); - delete this.webSocketPingSetInterval; + clearInterval(this.webSocketPingSetInterval) + delete this.webSocketPingSetInterval } } - private getConfiguredSupervisionUrl(): URL { - let configuredSupervisionUrl: string; - const supervisionUrls = this.stationInfo?.supervisionUrls ?? Configuration.getSupervisionUrls(); + private getConfiguredSupervisionUrl (): URL { + let configuredSupervisionUrl: string + const supervisionUrls = this.stationInfo?.supervisionUrls ?? Configuration.getSupervisionUrls() if (isNotEmptyArray(supervisionUrls)) { - let configuredSupervisionUrlIndex: number; + let configuredSupervisionUrlIndex: number switch (Configuration.getSupervisionUrlDistribution()) { case SupervisionUrlDistribution.RANDOM: configuredSupervisionUrlIndex = Math.floor( - secureRandom() * (supervisionUrls as string[]).length, - ); - break; + secureRandom() * (supervisionUrls as string[]).length + ) + break case SupervisionUrlDistribution.ROUND_ROBIN: case SupervisionUrlDistribution.CHARGING_STATION_AFFINITY: default: - Object.values(SupervisionUrlDistribution).includes( - Configuration.getSupervisionUrlDistribution()!, - ) === false && + !Object.values(SupervisionUrlDistribution).includes( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + Configuration.getSupervisionUrlDistribution()! + ) && logger.error( // eslint-disable-next-line @typescript-eslint/no-base-to-string `${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' from values '${SupervisionUrlDistribution.toString()}', defaulting to ${ SupervisionUrlDistribution.CHARGING_STATION_AFFINITY - }`, - ); - configuredSupervisionUrlIndex = (this.index - 1) % (supervisionUrls as string[]).length; - break; + }` + ) + configuredSupervisionUrlIndex = (this.index - 1) % (supervisionUrls as string[]).length + break } - configuredSupervisionUrl = (supervisionUrls as string[])[configuredSupervisionUrlIndex]; + configuredSupervisionUrl = (supervisionUrls as string[])[configuredSupervisionUrlIndex] } else { - configuredSupervisionUrl = supervisionUrls as string; + configuredSupervisionUrl = supervisionUrls as string } if (isNotEmptyString(configuredSupervisionUrl)) { - return new URL(configuredSupervisionUrl); + return new URL(configuredSupervisionUrl) } - const errorMsg = 'No supervision url(s) configured'; - logger.error(`${this.logPrefix()} ${errorMsg}`); - throw new BaseError(`${errorMsg}`); + const errorMsg = 'No supervision url(s) configured' + logger.error(`${this.logPrefix()} ${errorMsg}`) + throw new BaseError(`${errorMsg}`) } - private stopHeartbeat(): void { + private stopHeartbeat (): void { if (this.heartbeatSetInterval !== undefined) { - clearInterval(this.heartbeatSetInterval); - delete this.heartbeatSetInterval; + clearInterval(this.heartbeatSetInterval) + delete this.heartbeatSetInterval } } - private terminateWSConnection(): void { - if (this.isWebSocketConnectionOpened() === true) { - this.wsConnection?.terminate(); - this.wsConnection = null; + private terminateWSConnection (): void { + if (this.isWebSocketConnectionOpened()) { + this.wsConnection?.terminate() + this.wsConnection = null } } - private async reconnect(): Promise { + private async reconnect (): Promise { // Stop WebSocket ping - this.stopWebSocketPing(); + this.stopWebSocketPing() // Stop heartbeat - this.stopHeartbeat(); + this.stopHeartbeat() // Stop the ATG if needed - if (this.getAutomaticTransactionGeneratorConfiguration().stopOnConnectionFailure === true) { - this.stopAutomaticTransactionGenerator(); + if (this.getAutomaticTransactionGeneratorConfiguration().stopOnConnectionFailure) { + this.stopAutomaticTransactionGenerator() } if ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.autoReconnectRetryCount < this.stationInfo.autoReconnectMaxRetries! || this.stationInfo?.autoReconnectMaxRetries === -1 ) { - ++this.autoReconnectRetryCount; + ++this.autoReconnectRetryCount const reconnectDelay = this.stationInfo?.reconnectExponentialDelay === true ? exponentialDelay(this.autoReconnectRetryCount) - : secondsToMilliseconds(this.getConnectionTimeout()); - const reconnectDelayWithdraw = 1000; + : secondsToMilliseconds(this.getConnectionTimeout()) + const reconnectDelayWithdraw = 1000 const reconnectTimeout = - reconnectDelay && reconnectDelay - reconnectDelayWithdraw > 0 + reconnectDelay != null && reconnectDelay - reconnectDelayWithdraw > 0 ? reconnectDelay - reconnectDelayWithdraw - : 0; + : 0 logger.error( `${this.logPrefix()} WebSocket connection retry in ${roundTo( reconnectDelay, - 2, - )}ms, timeout ${reconnectTimeout}ms`, - ); - await sleep(reconnectDelay); + 2 + )}ms, timeout ${reconnectTimeout}ms` + ) + await sleep(reconnectDelay) logger.error( - `${this.logPrefix()} WebSocket connection retry #${this.autoReconnectRetryCount.toString()}`, - ); + `${this.logPrefix()} WebSocket connection retry #${this.autoReconnectRetryCount.toString()}` + ) this.openWSConnection( { - handshakeTimeout: reconnectTimeout, + handshakeTimeout: reconnectTimeout }, - { closeOpened: true }, - ); + { closeOpened: true } + ) } else if (this.stationInfo?.autoReconnectMaxRetries !== -1) { logger.error( `${this.logPrefix()} WebSocket connection retries failure: maximum retries reached (${ this.autoReconnectRetryCount - }) or retries disabled (${this.stationInfo?.autoReconnectMaxRetries})`, - ); + }) or retries disabled (${this.stationInfo?.autoReconnectMaxRetries})` + ) } } } diff --git a/src/charging-station/ChargingStationWorker.ts b/src/charging-station/ChargingStationWorker.ts index c8f992f3..78d0bc8a 100644 --- a/src/charging-station/ChargingStationWorker.ts +++ b/src/charging-station/ChargingStationWorker.ts @@ -1,14 +1,14 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { parentPort } from 'node:worker_threads'; +import { parentPort } from 'node:worker_threads' -import { ThreadWorker } from 'poolifier'; +import { ThreadWorker } from 'poolifier' -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 WorkerMessage, WorkerMessageEvents } from '../worker/index.js'; +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 WorkerMessage, WorkerMessageEvents } from '../worker/index.js' /** * Creates and starts a charging station instance @@ -16,49 +16,51 @@ import { type WorkerMessage, WorkerMessageEvents } from '../worker/index.js'; * @param data - data sent to worker */ const startChargingStation = (data?: ChargingStationWorkerData): void => { - new ChargingStation(data!.index, data!.templateFile).start(); -}; + // 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 { - constructor() { + constructor () { // Add message listener to create and start charging station from the main thread parentPort?.on('message', (message: WorkerMessage) => { switch (message.event) { case WorkerMessageEvents.startWorkerElement: try { - startChargingStation(message.data); + startChargingStation(message.data) parentPort?.postMessage({ - event: WorkerMessageEvents.startedWorkerElement, - }); + 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, - }, - }); + stack: (error as Error).stack + } + }) } - break; + break default: throw new BaseError( `Unknown worker event: '${message.event}' received with data: '${JSON.stringify( message.data, undefined, - 2, - )}'`, - ); + 2 + )}'` + ) } - }); + }) } } export let chargingStationWorker: - | ChargingStationWorker - | ThreadWorker; +| ChargingStationWorker +| ThreadWorker if (Configuration.workerPoolInUse()) { - chargingStationWorker = new ThreadWorker(startChargingStation); + chargingStationWorker = new ThreadWorker(startChargingStation) } else { - chargingStationWorker = new ChargingStationWorker(); + chargingStationWorker = new ChargingStationWorker() } diff --git a/src/charging-station/ConfigurationKeyUtils.ts b/src/charging-station/ConfigurationKeyUtils.ts index c7302e70..044c0923 100644 --- a/src/charging-station/ConfigurationKeyUtils.ts +++ b/src/charging-station/ConfigurationKeyUtils.ts @@ -1,107 +1,110 @@ -import type { ChargingStation } from './ChargingStation.js'; -import type { ConfigurationKey, ConfigurationKeyType } from '../types/index.js'; -import { logger } from '../utils/index.js'; +import type { ChargingStation } from './ChargingStation.js' +import type { ConfigurationKey, ConfigurationKeyType } from '../types/index.js' +import { logger } from '../utils/index.js' interface ConfigurationKeyOptions { - readonly?: boolean; - visible?: boolean; - reboot?: boolean; + readonly?: boolean + visible?: boolean + reboot?: boolean } interface DeleteConfigurationKeyParams { - save?: boolean; - caseInsensitive?: boolean; + save?: boolean + caseInsensitive?: boolean } interface AddConfigurationKeyParams { - overwrite?: boolean; - save?: boolean; + overwrite?: boolean + save?: boolean } export const getConfigurationKey = ( chargingStation: ChargingStation, key: ConfigurationKeyType, - caseInsensitive = false, + caseInsensitive = false ): ConfigurationKey | undefined => { return chargingStation.ocppConfiguration?.configurationKey?.find((configElement) => { if (caseInsensitive) { - return configElement.key.toLowerCase() === key.toLowerCase(); + return configElement.key.toLowerCase() === key.toLowerCase() } - return configElement.key === key; - }); -}; + return configElement.key === key + }) +} export const addConfigurationKey = ( chargingStation: ChargingStation, key: ConfigurationKeyType, value: string, options?: ConfigurationKeyOptions, - params?: AddConfigurationKeyParams, + params?: AddConfigurationKeyParams ): void => { options = { ...{ readonly: false, visible: true, - reboot: false, + reboot: false }, - ...options, - }; - params = { ...{ overwrite: false, save: false }, ...params }; - let keyFound = getConfigurationKey(chargingStation, key); + ...options + } + params = { ...{ overwrite: false, save: false }, ...params } + let keyFound = getConfigurationKey(chargingStation, key) if (keyFound !== undefined && params?.overwrite === true) { deleteConfigurationKey(chargingStation, keyFound.key, { - save: false, - }); - keyFound = undefined; + save: false + }) + keyFound = undefined } if (keyFound === undefined) { chargingStation.ocppConfiguration?.configurationKey?.push({ key, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion readonly: options.readonly!, value, visible: options.visible, - reboot: options.reboot, - }); - params?.save && chargingStation.saveOcppConfiguration(); + reboot: options.reboot + }) + params?.save === true && chargingStation.saveOcppConfiguration() } else { logger.error( `${chargingStation.logPrefix()} Trying to add an already existing configuration key: %j`, - keyFound, - ); + keyFound + ) } -}; +} export const setConfigurationKeyValue = ( chargingStation: ChargingStation, key: ConfigurationKeyType, value: string, - caseInsensitive = false, + caseInsensitive = false ): void => { - const keyFound = getConfigurationKey(chargingStation, key, caseInsensitive); + const keyFound = getConfigurationKey(chargingStation, key, caseInsensitive) if (keyFound !== undefined) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingStation.ocppConfiguration!.configurationKey![ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingStation.ocppConfiguration!.configurationKey!.indexOf(keyFound) - ].value = value; - chargingStation.saveOcppConfiguration(); + ].value = value + chargingStation.saveOcppConfiguration() } else { logger.error( `${chargingStation.logPrefix()} Trying to set a value on a non existing configuration key: %j`, - { key, value }, - ); + { key, value } + ) } -}; +} export const deleteConfigurationKey = ( chargingStation: ChargingStation, key: ConfigurationKeyType, - params?: DeleteConfigurationKeyParams, + params?: DeleteConfigurationKeyParams ): ConfigurationKey[] | undefined => { - params = { ...{ save: true, caseInsensitive: false }, ...params }; - const keyFound = getConfigurationKey(chargingStation, key, params?.caseInsensitive); + params = { ...{ save: true, caseInsensitive: false }, ...params } + const keyFound = getConfigurationKey(chargingStation, key, params?.caseInsensitive) if (keyFound !== undefined) { const deletedConfigurationKey = chargingStation.ocppConfiguration?.configurationKey?.splice( chargingStation.ocppConfiguration.configurationKey.indexOf(keyFound), - 1, - ); - params?.save && chargingStation.saveOcppConfiguration(); - return deletedConfigurationKey; + 1 + ) + params?.save === true && chargingStation.saveOcppConfiguration() + return deletedConfigurationKey } -}; +} diff --git a/src/charging-station/Helpers.ts b/src/charging-station/Helpers.ts index 094205b8..cc630d33 100644 --- a/src/charging-station/Helpers.ts +++ b/src/charging-station/Helpers.ts @@ -1,10 +1,10 @@ -import { createHash, randomBytes } from 'node:crypto'; -import type { EventEmitter } from 'node:events'; -import { basename, dirname, join } from 'node:path'; -import { env } from 'node:process'; -import { fileURLToPath } from 'node:url'; +import { createHash, randomBytes } from 'node:crypto' +import type { EventEmitter } from 'node:events' +import { basename, dirname, join } from 'node:path' +import { env } from 'node:process' +import { fileURLToPath } from 'node:url' -import chalk from 'chalk'; +import chalk from 'chalk' import { type Interval, addDays, @@ -18,13 +18,13 @@ import { isDate, isPast, isWithinInterval, - toDate, -} from 'date-fns'; -import { maxTime } from 'date-fns/constants'; + toDate +} from 'date-fns' +import { maxTime } from 'date-fns/constants' -import type { ChargingStation } from './ChargingStation.js'; -import { getConfigurationKey } from './ConfigurationKeyUtils.js'; -import { BaseError } from '../exception/index.js'; +import type { ChargingStation } from './ChargingStation.js' +import { getConfigurationKey } from './ConfigurationKeyUtils.js' +import { BaseError } from '../exception/index.js' import { AmpereUnits, AvailabilityType, @@ -37,7 +37,7 @@ import { type ChargingStationConfiguration, type ChargingStationInfo, type ChargingStationTemplate, - ChargingStationWorkerMessageEvents, + type ChargingStationWorkerMessageEvents, ConnectorPhaseRotation, type ConnectorStatus, ConnectorStatusEnum, @@ -50,9 +50,9 @@ import { type Reservation, ReservationTerminationReason, StandardParametersKey, - SupportedFeatureProfiles, - Voltage, -} from '../types/index.js'; + type SupportedFeatureProfiles, + Voltage +} from '../types/index.js' import { ACElectricUtils, Constants, @@ -69,346 +69,358 @@ import { isUndefined, isValidTime, logger, - secureRandom, -} from '../utils/index.js'; + secureRandom +} from '../utils/index.js' -const moduleName = 'Helpers'; +const moduleName = 'Helpers' export const getChargingStationId = ( index: number, - stationTemplate: ChargingStationTemplate | undefined, + stationTemplate: ChargingStationTemplate | undefined ): string => { if (stationTemplate === undefined) { - return "Unknown 'chargingStationId'"; + return "Unknown 'chargingStationId'" } // In case of multiple instances: add instance index to charging station id - const instanceIndex = env.CF_INSTANCE_INDEX ?? 0; - const idSuffix = stationTemplate?.nameSuffix ?? ''; - const idStr = `000000000${index.toString()}`; - return stationTemplate?.fixedName + const instanceIndex = env.CF_INSTANCE_INDEX ?? 0 + const idSuffix = stationTemplate?.nameSuffix ?? '' + const idStr = `000000000${index.toString()}` + return stationTemplate?.fixedName != null ? stationTemplate.baseName : `${stationTemplate.baseName}-${instanceIndex.toString()}${idStr.substring( - idStr.length - 4, - )}${idSuffix}`; -}; + idStr.length - 4 + )}${idSuffix}` +} export const hasReservationExpired = (reservation: Reservation): boolean => { - return isPast(reservation.expiryDate); -}; + return isPast(reservation.expiryDate) +} export const removeExpiredReservations = async ( - chargingStation: ChargingStation, + chargingStation: ChargingStation ): Promise => { if (chargingStation.hasEvses) { for (const evseStatus of chargingStation.evses.values()) { for (const connectorStatus of evseStatus.connectors.values()) { - if (connectorStatus.reservation && hasReservationExpired(connectorStatus.reservation)) { + if ( + connectorStatus.reservation != null && + hasReservationExpired(connectorStatus.reservation) + ) { await chargingStation.removeReservation( connectorStatus.reservation, - ReservationTerminationReason.EXPIRED, - ); + ReservationTerminationReason.EXPIRED + ) } } } } else { for (const connectorStatus of chargingStation.connectors.values()) { - if (connectorStatus.reservation && hasReservationExpired(connectorStatus.reservation)) { + if ( + connectorStatus.reservation != null && + hasReservationExpired(connectorStatus.reservation) + ) { await chargingStation.removeReservation( connectorStatus.reservation, - ReservationTerminationReason.EXPIRED, - ); + ReservationTerminationReason.EXPIRED + ) } } } -}; +} export const getNumberOfReservableConnectors = ( - connectors: Map, + connectors: Map ): number => { - let numberOfReservableConnectors = 0; + let numberOfReservableConnectors = 0 for (const [connectorId, connectorStatus] of connectors) { if (connectorId === 0) { - continue; + continue } if (connectorStatus.status === ConnectorStatusEnum.Available) { - ++numberOfReservableConnectors; + ++numberOfReservableConnectors } } - return numberOfReservableConnectors; -}; + return numberOfReservableConnectors +} export const getHashId = (index: number, stationTemplate: ChargingStationTemplate): string => { const chargingStationInfo = { chargePointModel: stationTemplate.chargePointModel, chargePointVendor: stationTemplate.chargePointVendor, ...(!isUndefined(stationTemplate.chargeBoxSerialNumberPrefix) && { - chargeBoxSerialNumber: stationTemplate.chargeBoxSerialNumberPrefix, + chargeBoxSerialNumber: stationTemplate.chargeBoxSerialNumberPrefix }), ...(!isUndefined(stationTemplate.chargePointSerialNumberPrefix) && { - chargePointSerialNumber: stationTemplate.chargePointSerialNumberPrefix, + chargePointSerialNumber: stationTemplate.chargePointSerialNumberPrefix }), ...(!isUndefined(stationTemplate.meterSerialNumberPrefix) && { - meterSerialNumber: stationTemplate.meterSerialNumberPrefix, + meterSerialNumber: stationTemplate.meterSerialNumberPrefix }), ...(!isUndefined(stationTemplate.meterType) && { - meterType: stationTemplate.meterType, - }), - }; + meterType: stationTemplate.meterType + }) + } return createHash(Constants.DEFAULT_HASH_ALGORITHM) .update(`${JSON.stringify(chargingStationInfo)}${getChargingStationId(index, stationTemplate)}`) - .digest('hex'); -}; + .digest('hex') +} export const checkChargingStation = ( chargingStation: ChargingStation, - logPrefix: string, + logPrefix: string ): boolean => { - if (chargingStation.started === false && chargingStation.starting === false) { - logger.warn(`${logPrefix} charging station is stopped, cannot proceed`); - return false; + if (!chargingStation.started && !chargingStation.starting) { + logger.warn(`${logPrefix} charging station is stopped, cannot proceed`) + return false } - return true; -}; + return true +} export const getPhaseRotationValue = ( connectorId: number, - numberOfPhases: number, + numberOfPhases: number ): string | undefined => { // AC/DC if (connectorId === 0 && numberOfPhases === 0) { - return `${connectorId}.${ConnectorPhaseRotation.RST}`; + return `${connectorId}.${ConnectorPhaseRotation.RST}` } else if (connectorId > 0 && numberOfPhases === 0) { - return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`; + return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}` // AC } else if (connectorId >= 0 && numberOfPhases === 1) { - return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}`; + return `${connectorId}.${ConnectorPhaseRotation.NotApplicable}` } else if (connectorId >= 0 && numberOfPhases === 3) { - return `${connectorId}.${ConnectorPhaseRotation.RST}`; + return `${connectorId}.${ConnectorPhaseRotation.RST}` } -}; +} export const getMaxNumberOfEvses = (evses: Record): number => { - if (!evses) { - return -1; + if (evses == null) { + return -1 } - return Object.keys(evses).length; -}; + return Object.keys(evses).length +} const getMaxNumberOfConnectors = (connectors: Record): number => { - if (!connectors) { - return -1; + if (connectors == null) { + return -1 } - return Object.keys(connectors).length; -}; + return Object.keys(connectors).length +} export const getBootConnectorStatus = ( chargingStation: ChargingStation, connectorId: number, - connectorStatus: ConnectorStatus, + connectorStatus: ConnectorStatus ): ConnectorStatusEnum => { - let connectorBootStatus: ConnectorStatusEnum; + let connectorBootStatus: ConnectorStatusEnum if ( - !connectorStatus?.status && - (chargingStation.isChargingStationAvailable() === false || - chargingStation.isConnectorAvailable(connectorId) === false) + connectorStatus?.status == null && + (!chargingStation.isChargingStationAvailable() || + !chargingStation.isConnectorAvailable(connectorId)) ) { - connectorBootStatus = ConnectorStatusEnum.Unavailable; - } else if (!connectorStatus?.status && connectorStatus?.bootStatus) { + connectorBootStatus = ConnectorStatusEnum.Unavailable + } else if (connectorStatus?.status == null && connectorStatus?.bootStatus != null) { // Set boot status in template at startup - connectorBootStatus = connectorStatus?.bootStatus; - } else if (connectorStatus?.status) { + connectorBootStatus = connectorStatus?.bootStatus + } else if (connectorStatus?.status != null) { // Set previous status at startup - connectorBootStatus = connectorStatus?.status; + connectorBootStatus = connectorStatus?.status } else { // Set default status - connectorBootStatus = ConnectorStatusEnum.Available; + connectorBootStatus = ConnectorStatusEnum.Available } - return connectorBootStatus; -}; + return connectorBootStatus +} export const checkTemplate = ( stationTemplate: ChargingStationTemplate, logPrefix: string, - templateFile: string, + templateFile: string ): void => { - if (isNullOrUndefined(stationTemplate)) { - const errorMsg = `Failed to read charging station template file ${templateFile}`; - logger.error(`${logPrefix} ${errorMsg}`); - throw new BaseError(errorMsg); + if (stationTemplate == null) { + const errorMsg = `Failed to read charging station template file ${templateFile}` + logger.error(`${logPrefix} ${errorMsg}`) + throw new BaseError(errorMsg) } if (isEmptyObject(stationTemplate)) { - const errorMsg = `Empty charging station information from template file ${templateFile}`; - logger.error(`${logPrefix} ${errorMsg}`); - throw new BaseError(errorMsg); + const errorMsg = `Empty charging station information from template file ${templateFile}` + logger.error(`${logPrefix} ${errorMsg}`) + throw new BaseError(errorMsg) } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (isEmptyObject(stationTemplate.AutomaticTransactionGenerator!)) { - stationTemplate.AutomaticTransactionGenerator = Constants.DEFAULT_ATG_CONFIGURATION; + stationTemplate.AutomaticTransactionGenerator = Constants.DEFAULT_ATG_CONFIGURATION logger.warn( `${logPrefix} Empty automatic transaction generator configuration from template file ${templateFile}, set to default: %j`, - Constants.DEFAULT_ATG_CONFIGURATION, - ); + Constants.DEFAULT_ATG_CONFIGURATION + ) } if (isNullOrUndefined(stationTemplate.idTagsFile) || isEmptyString(stationTemplate.idTagsFile)) { logger.warn( - `${logPrefix} Missing id tags file in template file ${templateFile}. That can lead to issues with the Automatic Transaction Generator`, - ); + `${logPrefix} Missing id tags file in template file ${templateFile}. That can lead to issues with the Automatic Transaction Generator` + ) } -}; +} export const checkConfiguration = ( stationConfiguration: ChargingStationConfiguration | undefined, logPrefix: string, - configurationFile: string, + configurationFile: string ): void => { if (isNullOrUndefined(stationConfiguration)) { - const errorMsg = `Failed to read charging station configuration file ${configurationFile}`; - logger.error(`${logPrefix} ${errorMsg}`); - throw new BaseError(errorMsg); + const errorMsg = `Failed to read charging station configuration file ${configurationFile}` + logger.error(`${logPrefix} ${errorMsg}`) + throw new BaseError(errorMsg) } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (isEmptyObject(stationConfiguration!)) { - const errorMsg = `Empty charging station configuration from file ${configurationFile}`; - logger.error(`${logPrefix} ${errorMsg}`); - throw new BaseError(errorMsg); + const errorMsg = `Empty charging station configuration from file ${configurationFile}` + logger.error(`${logPrefix} ${errorMsg}`) + throw new BaseError(errorMsg) } -}; +} export const checkConnectorsConfiguration = ( stationTemplate: ChargingStationTemplate, logPrefix: string, - templateFile: string, + templateFile: string ): { - configuredMaxConnectors: number; - templateMaxConnectors: number; - templateMaxAvailableConnectors: number; + configuredMaxConnectors: number + templateMaxConnectors: number + templateMaxAvailableConnectors: number } => { - const configuredMaxConnectors = getConfiguredMaxNumberOfConnectors(stationTemplate); - checkConfiguredMaxConnectors(configuredMaxConnectors, logPrefix, templateFile); - const templateMaxConnectors = getMaxNumberOfConnectors(stationTemplate.Connectors!); - checkTemplateMaxConnectors(templateMaxConnectors, logPrefix, templateFile); - const templateMaxAvailableConnectors = stationTemplate.Connectors?.[0] - ? templateMaxConnectors - 1 - : templateMaxConnectors; + const configuredMaxConnectors = getConfiguredMaxNumberOfConnectors(stationTemplate) + checkConfiguredMaxConnectors(configuredMaxConnectors, logPrefix, templateFile) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const templateMaxConnectors = getMaxNumberOfConnectors(stationTemplate.Connectors!) + checkTemplateMaxConnectors(templateMaxConnectors, logPrefix, templateFile) + const templateMaxAvailableConnectors = + stationTemplate.Connectors?.[0] != null ? templateMaxConnectors - 1 : templateMaxConnectors if ( configuredMaxConnectors > templateMaxAvailableConnectors && - !stationTemplate?.randomConnectors + stationTemplate?.randomConnectors === false ) { logger.warn( - `${logPrefix} Number of connectors exceeds the number of connector configurations in template ${templateFile}, forcing random connector configurations affectation`, - ); - stationTemplate.randomConnectors = true; + `${logPrefix} Number of connectors exceeds the number of connector configurations in template ${templateFile}, forcing random connector configurations affectation` + ) + stationTemplate.randomConnectors = true } - return { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors }; -}; + return { configuredMaxConnectors, templateMaxConnectors, templateMaxAvailableConnectors } +} export const checkStationInfoConnectorStatus = ( connectorId: number, connectorStatus: ConnectorStatus, logPrefix: string, - templateFile: string, + templateFile: string ): void => { if (!isNullOrUndefined(connectorStatus?.status)) { logger.warn( - `${logPrefix} Charging station information from template ${templateFile} with connector id ${connectorId} status configuration defined, undefine it`, - ); - delete connectorStatus.status; + `${logPrefix} Charging station information from template ${templateFile} with connector id ${connectorId} status configuration defined, undefine it` + ) + delete connectorStatus.status } -}; +} export const buildConnectorsMap = ( connectors: Record, logPrefix: string, - templateFile: string, + templateFile: string ): Map => { - const connectorsMap = new Map(); + const connectorsMap = new Map() if (getMaxNumberOfConnectors(connectors) > 0) { for (const connector in connectors) { - const connectorStatus = connectors[connector]; - const connectorId = convertToInt(connector); - checkStationInfoConnectorStatus(connectorId, connectorStatus, logPrefix, templateFile); - connectorsMap.set(connectorId, cloneObject(connectorStatus)); + const connectorStatus = connectors[connector] + const connectorId = convertToInt(connector) + checkStationInfoConnectorStatus(connectorId, connectorStatus, logPrefix, templateFile) + connectorsMap.set(connectorId, cloneObject(connectorStatus)) } } else { logger.warn( - `${logPrefix} Charging station information from template ${templateFile} with no connectors, cannot build connectors map`, - ); + `${logPrefix} Charging station information from template ${templateFile} with no connectors, cannot build connectors map` + ) } - return connectorsMap; -}; + return connectorsMap +} export const initializeConnectorsMapStatus = ( connectors: Map, - logPrefix: string, + logPrefix: string ): void => { for (const connectorId of connectors.keys()) { if (connectorId > 0 && connectors.get(connectorId)?.transactionStarted === true) { logger.warn( `${logPrefix} Connector id ${connectorId} at initialization has a transaction started with id ${connectors.get( - connectorId, - )?.transactionId}`, - ); + connectorId + )?.transactionId}` + ) } if (connectorId === 0) { - connectors.get(connectorId)!.availability = AvailabilityType.Operative; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connectors.get(connectorId)!.availability = AvailabilityType.Operative if (isUndefined(connectors.get(connectorId)?.chargingProfiles)) { - connectors.get(connectorId)!.chargingProfiles = []; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connectors.get(connectorId)!.chargingProfiles = [] } } else if ( connectorId > 0 && isNullOrUndefined(connectors.get(connectorId)?.transactionStarted) ) { - initializeConnectorStatus(connectors.get(connectorId)!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + initializeConnectorStatus(connectors.get(connectorId)!) } } -}; +} export const resetConnectorStatus = (connectorStatus: ConnectorStatus): void => { connectorStatus.chargingProfiles = - connectorStatus.transactionId && isNotEmptyArray(connectorStatus.chargingProfiles) + connectorStatus.transactionId != null && isNotEmptyArray(connectorStatus.chargingProfiles) ? connectorStatus.chargingProfiles?.filter( - (chargingProfile) => chargingProfile.transactionId !== connectorStatus.transactionId, - ) - : []; - connectorStatus.idTagLocalAuthorized = false; - connectorStatus.idTagAuthorized = false; - connectorStatus.transactionRemoteStarted = false; - connectorStatus.transactionStarted = false; - delete connectorStatus?.transactionStart; - delete connectorStatus?.transactionId; - delete connectorStatus?.localAuthorizeIdTag; - delete connectorStatus?.authorizeIdTag; - delete connectorStatus?.transactionIdTag; - connectorStatus.transactionEnergyActiveImportRegisterValue = 0; - delete connectorStatus?.transactionBeginMeterValue; -}; + (chargingProfile) => chargingProfile.transactionId !== connectorStatus.transactionId + ) + : [] + connectorStatus.idTagLocalAuthorized = false + connectorStatus.idTagAuthorized = false + connectorStatus.transactionRemoteStarted = false + connectorStatus.transactionStarted = false + delete connectorStatus?.transactionStart + delete connectorStatus?.transactionId + delete connectorStatus?.localAuthorizeIdTag + delete connectorStatus?.authorizeIdTag + delete connectorStatus?.transactionIdTag + connectorStatus.transactionEnergyActiveImportRegisterValue = 0 + delete connectorStatus?.transactionBeginMeterValue +} export const createBootNotificationRequest = ( stationInfo: ChargingStationInfo, - bootReason: BootReasonEnumType = BootReasonEnumType.PowerUp, + bootReason: BootReasonEnumType = BootReasonEnumType.PowerUp ): BootNotificationRequest => { - const ocppVersion = stationInfo.ocppVersion!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const ocppVersion = stationInfo.ocppVersion! switch (ocppVersion) { case OCPPVersion.VERSION_16: return { chargePointModel: stationInfo.chargePointModel, chargePointVendor: stationInfo.chargePointVendor, ...(!isUndefined(stationInfo.chargeBoxSerialNumber) && { - chargeBoxSerialNumber: stationInfo.chargeBoxSerialNumber, + chargeBoxSerialNumber: stationInfo.chargeBoxSerialNumber }), ...(!isUndefined(stationInfo.chargePointSerialNumber) && { - chargePointSerialNumber: stationInfo.chargePointSerialNumber, + chargePointSerialNumber: stationInfo.chargePointSerialNumber }), ...(!isUndefined(stationInfo.firmwareVersion) && { - firmwareVersion: stationInfo.firmwareVersion, + firmwareVersion: stationInfo.firmwareVersion }), ...(!isUndefined(stationInfo.iccid) && { iccid: stationInfo.iccid }), ...(!isUndefined(stationInfo.imsi) && { imsi: stationInfo.imsi }), ...(!isUndefined(stationInfo.meterSerialNumber) && { - meterSerialNumber: stationInfo.meterSerialNumber, + meterSerialNumber: stationInfo.meterSerialNumber }), ...(!isUndefined(stationInfo.meterType) && { - meterType: stationInfo.meterType, - }), - } as OCPP16BootNotificationRequest; + meterType: stationInfo.meterType + }) + } satisfies OCPP16BootNotificationRequest case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: return { @@ -417,129 +429,133 @@ export const createBootNotificationRequest = ( model: stationInfo.chargePointModel, vendorName: stationInfo.chargePointVendor, ...(!isUndefined(stationInfo.firmwareVersion) && { - firmwareVersion: stationInfo.firmwareVersion, + firmwareVersion: stationInfo.firmwareVersion }), ...(!isUndefined(stationInfo.chargeBoxSerialNumber) && { - serialNumber: stationInfo.chargeBoxSerialNumber, + serialNumber: stationInfo.chargeBoxSerialNumber }), ...((!isUndefined(stationInfo.iccid) || !isUndefined(stationInfo.imsi)) && { modem: { ...(!isUndefined(stationInfo.iccid) && { iccid: stationInfo.iccid }), - ...(!isUndefined(stationInfo.imsi) && { imsi: stationInfo.imsi }), - }, - }), - }, - } as OCPP20BootNotificationRequest; + ...(!isUndefined(stationInfo.imsi) && { imsi: stationInfo.imsi }) + } + }) + } + } satisfies OCPP20BootNotificationRequest } -}; +} export const warnTemplateKeysDeprecation = ( stationTemplate: ChargingStationTemplate, logPrefix: string, - templateFile: string, -) => { - const templateKeys: { deprecatedKey: string; key?: string }[] = [ + templateFile: string +): void => { + const templateKeys: Array<{ deprecatedKey: string, key?: string }> = [ { deprecatedKey: 'supervisionUrl', key: 'supervisionUrls' }, { deprecatedKey: 'authorizationFile', key: 'idTagsFile' }, { deprecatedKey: 'payloadSchemaValidation', key: 'ocppStrictCompliance' }, - { deprecatedKey: 'mustAuthorizeAtRemoteStart', key: 'remoteAuthorization' }, - ]; + { deprecatedKey: 'mustAuthorizeAtRemoteStart', key: 'remoteAuthorization' } + ] for (const templateKey of templateKeys) { warnDeprecatedTemplateKey( stationTemplate, templateKey.deprecatedKey, logPrefix, templateFile, - !isUndefined(templateKey.key) ? `Use '${templateKey.key}' instead` : undefined, - ); - convertDeprecatedTemplateKey(stationTemplate, templateKey.deprecatedKey, templateKey.key); + !isUndefined(templateKey.key) ? `Use '${templateKey.key}' instead` : undefined + ) + convertDeprecatedTemplateKey(stationTemplate, templateKey.deprecatedKey, templateKey.key) } -}; +} export const stationTemplateToStationInfo = ( - stationTemplate: ChargingStationTemplate, + stationTemplate: ChargingStationTemplate ): ChargingStationInfo => { - stationTemplate = cloneObject(stationTemplate); - delete stationTemplate.power; - delete stationTemplate.powerUnit; - delete stationTemplate.Connectors; - delete stationTemplate.Evses; - delete stationTemplate.Configuration; - delete stationTemplate.AutomaticTransactionGenerator; - delete stationTemplate.chargeBoxSerialNumberPrefix; - delete stationTemplate.chargePointSerialNumberPrefix; - delete stationTemplate.meterSerialNumberPrefix; - return stationTemplate as ChargingStationInfo; -}; + stationTemplate = cloneObject(stationTemplate) + delete stationTemplate.power + delete stationTemplate.powerUnit + delete stationTemplate.Connectors + delete stationTemplate.Evses + delete stationTemplate.Configuration + delete stationTemplate.AutomaticTransactionGenerator + delete stationTemplate.chargeBoxSerialNumberPrefix + delete stationTemplate.chargePointSerialNumberPrefix + delete stationTemplate.meterSerialNumberPrefix + return stationTemplate as ChargingStationInfo +} export const createSerialNumber = ( stationTemplate: ChargingStationTemplate, stationInfo: ChargingStationInfo, params?: { - randomSerialNumberUpperCase?: boolean; - randomSerialNumber?: boolean; - }, + randomSerialNumberUpperCase?: boolean + randomSerialNumber?: boolean + } ): void => { - params = { ...{ randomSerialNumberUpperCase: true, randomSerialNumber: true }, ...params }; - const serialNumberSuffix = params?.randomSerialNumber - ? getRandomSerialNumberSuffix({ - upperCase: params.randomSerialNumberUpperCase, + params = { ...{ randomSerialNumberUpperCase: true, randomSerialNumber: true }, ...params } + const serialNumberSuffix = + params?.randomSerialNumber === true + ? getRandomSerialNumberSuffix({ + upperCase: params.randomSerialNumberUpperCase }) - : ''; + : '' isNotEmptyString(stationTemplate?.chargePointSerialNumberPrefix) && - (stationInfo.chargePointSerialNumber = `${stationTemplate.chargePointSerialNumberPrefix}${serialNumberSuffix}`); + (stationInfo.chargePointSerialNumber = `${stationTemplate.chargePointSerialNumberPrefix}${serialNumberSuffix}`) isNotEmptyString(stationTemplate?.chargeBoxSerialNumberPrefix) && - (stationInfo.chargeBoxSerialNumber = `${stationTemplate.chargeBoxSerialNumberPrefix}${serialNumberSuffix}`); + (stationInfo.chargeBoxSerialNumber = `${stationTemplate.chargeBoxSerialNumberPrefix}${serialNumberSuffix}`) isNotEmptyString(stationTemplate?.meterSerialNumberPrefix) && - (stationInfo.meterSerialNumber = `${stationTemplate.meterSerialNumberPrefix}${serialNumberSuffix}`); -}; + (stationInfo.meterSerialNumber = `${stationTemplate.meterSerialNumberPrefix}${serialNumberSuffix}`) +} export const propagateSerialNumber = ( stationTemplate: ChargingStationTemplate, stationInfoSrc: ChargingStationInfo, - stationInfoDst: ChargingStationInfo, -) => { - if (!stationInfoSrc || !stationTemplate) { + stationInfoDst: ChargingStationInfo +): void => { + if (stationInfoSrc == null || stationTemplate == null) { throw new BaseError( - 'Missing charging station template or existing configuration to propagate serial number', - ); + 'Missing charging station template or existing configuration to propagate serial number' + ) } - stationTemplate?.chargePointSerialNumberPrefix && stationInfoSrc?.chargePointSerialNumber + stationTemplate?.chargePointSerialNumberPrefix != null && + stationInfoSrc?.chargePointSerialNumber != null ? (stationInfoDst.chargePointSerialNumber = stationInfoSrc.chargePointSerialNumber) - : stationInfoDst?.chargePointSerialNumber && delete stationInfoDst.chargePointSerialNumber; - stationTemplate?.chargeBoxSerialNumberPrefix && stationInfoSrc?.chargeBoxSerialNumber + : stationInfoDst?.chargePointSerialNumber != null && + delete stationInfoDst.chargePointSerialNumber + stationTemplate?.chargeBoxSerialNumberPrefix != null && + stationInfoSrc?.chargeBoxSerialNumber != null ? (stationInfoDst.chargeBoxSerialNumber = stationInfoSrc.chargeBoxSerialNumber) - : stationInfoDst?.chargeBoxSerialNumber && delete stationInfoDst.chargeBoxSerialNumber; - stationTemplate?.meterSerialNumberPrefix && stationInfoSrc?.meterSerialNumber + : stationInfoDst?.chargeBoxSerialNumber != null && delete stationInfoDst.chargeBoxSerialNumber + stationTemplate?.meterSerialNumberPrefix != null && stationInfoSrc?.meterSerialNumber != null ? (stationInfoDst.meterSerialNumber = stationInfoSrc.meterSerialNumber) - : stationInfoDst?.meterSerialNumber && delete stationInfoDst.meterSerialNumber; -}; + : stationInfoDst?.meterSerialNumber != null && delete stationInfoDst.meterSerialNumber +} export const hasFeatureProfile = ( chargingStation: ChargingStation, - featureProfile: SupportedFeatureProfiles, + featureProfile: SupportedFeatureProfiles ): boolean | undefined => { return getConfigurationKey( chargingStation, - StandardParametersKey.SupportedFeatureProfiles, - )?.value?.includes(featureProfile); -}; + StandardParametersKey.SupportedFeatureProfiles + )?.value?.includes(featureProfile) +} export const getAmperageLimitationUnitDivider = (stationInfo: ChargingStationInfo): number => { - let unitDivider = 1; + let unitDivider = 1 switch (stationInfo.amperageLimitationUnit) { case AmpereUnits.DECI_AMPERE: - unitDivider = 10; - break; + unitDivider = 10 + break case AmpereUnits.CENTI_AMPERE: - unitDivider = 100; - break; + unitDivider = 100 + break case AmpereUnits.MILLI_AMPERE: - unitDivider = 1000; - break; + unitDivider = 1000 + break } - return unitDivider; -}; + return unitDivider +} /** * Gets the connector cloned charging profiles applying a power limitation @@ -551,214 +567,221 @@ export const getAmperageLimitationUnitDivider = (stationInfo: ChargingStationInf */ export const getConnectorChargingProfiles = ( chargingStation: ChargingStation, - connectorId: number, -) => { + connectorId: number +): ChargingProfile[] => { return cloneObject( (chargingStation.getConnectorStatus(connectorId)?.chargingProfiles ?? []) .sort((a, b) => b.stackLevel - a.stackLevel) .concat( (chargingStation.getConnectorStatus(0)?.chargingProfiles ?? []).sort( - (a, b) => b.stackLevel - a.stackLevel, - ), - ), - ); -}; + (a, b) => b.stackLevel - a.stackLevel + ) + ) + ) +} export const getChargingStationConnectorChargingProfilesPowerLimit = ( chargingStation: ChargingStation, - connectorId: number, + connectorId: number ): number | undefined => { - let limit: number | undefined, chargingProfile: ChargingProfile | undefined; + let limit: number | undefined, chargingProfile: ChargingProfile | undefined // Get charging profiles sorted by connector id then stack level - const chargingProfiles = getConnectorChargingProfiles(chargingStation, connectorId); + const chargingProfiles = getConnectorChargingProfiles(chargingStation, connectorId) if (isNotEmptyArray(chargingProfiles)) { const result = getLimitFromChargingProfiles( chargingStation, connectorId, chargingProfiles, - chargingStation.logPrefix(), - ); + chargingStation.logPrefix() + ) if (!isNullOrUndefined(result)) { - limit = result?.limit; - chargingProfile = result?.chargingProfile; + limit = result?.limit + chargingProfile = result?.chargingProfile switch (chargingStation.stationInfo?.currentOutType) { case CurrentType.AC: limit = chargingProfile?.chargingSchedule?.chargingRateUnit === ChargingRateUnitType.WATT ? limit : ACElectricUtils.powerTotal( - chargingStation.getNumberOfPhases(), - chargingStation.stationInfo.voltageOut!, - limit!, - ); - break; + chargingStation.getNumberOfPhases(), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo.voltageOut!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + limit! + ) + break case CurrentType.DC: limit = chargingProfile?.chargingSchedule?.chargingRateUnit === ChargingRateUnitType.WATT ? limit - : DCElectricUtils.power(chargingStation.stationInfo.voltageOut!, limit!); + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + DCElectricUtils.power(chargingStation.stationInfo.voltageOut!, limit!) } const connectorMaximumPower = - chargingStation.stationInfo.maximumPower! / chargingStation.powerDivider; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo.maximumPower! / chargingStation.powerDivider + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (limit! > connectorMaximumPower) { logger.error( `${chargingStation.logPrefix()} ${moduleName}.getChargingStationConnectorChargingProfilesPowerLimit: Charging profile id ${chargingProfile?.chargingProfileId} limit ${limit} is greater than connector id ${connectorId} maximum ${connectorMaximumPower}: %j`, - result, - ); - limit = connectorMaximumPower; + result + ) + limit = connectorMaximumPower } } } - return limit; -}; + return limit +} export const getDefaultVoltageOut = ( currentType: CurrentType, logPrefix: string, - templateFile: string, + templateFile: string ): Voltage => { - const errorMsg = `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out`; - let defaultVoltageOut: number; + const errorMsg = `Unknown ${currentType} currentOutType in template file ${templateFile}, cannot define default voltage out` + let defaultVoltageOut: number switch (currentType) { case CurrentType.AC: - defaultVoltageOut = Voltage.VOLTAGE_230; - break; + defaultVoltageOut = Voltage.VOLTAGE_230 + break case CurrentType.DC: - defaultVoltageOut = Voltage.VOLTAGE_400; - break; + defaultVoltageOut = Voltage.VOLTAGE_400 + break default: - logger.error(`${logPrefix} ${errorMsg}`); - throw new BaseError(errorMsg); + logger.error(`${logPrefix} ${errorMsg}`) + throw new BaseError(errorMsg) } - return defaultVoltageOut; -}; + return defaultVoltageOut +} export const getIdTagsFile = (stationInfo: ChargingStationInfo): string | undefined => { - return ( - stationInfo.idTagsFile && - join(dirname(fileURLToPath(import.meta.url)), 'assets', basename(stationInfo.idTagsFile)) - ); -}; + return stationInfo.idTagsFile != null + ? join(dirname(fileURLToPath(import.meta.url)), 'assets', basename(stationInfo.idTagsFile)) + : undefined +} export const waitChargingStationEvents = async ( emitter: EventEmitter, event: ChargingStationWorkerMessageEvents, - eventsToWait: number, + eventsToWait: number ): Promise => { - return new Promise((resolve) => { - let events = 0; + return await new Promise((resolve) => { + let events = 0 if (eventsToWait === 0) { - resolve(events); - return; + resolve(events) + return } emitter.on(event, () => { - ++events; + ++events if (events === eventsToWait) { - resolve(events); + resolve(events) } - }); - }); -}; + }) + }) +} const getConfiguredMaxNumberOfConnectors = (stationTemplate: ChargingStationTemplate): number => { - let configuredMaxNumberOfConnectors = 0; - if (isNotEmptyArray(stationTemplate.numberOfConnectors) === true) { - const numberOfConnectors = stationTemplate.numberOfConnectors as number[]; + let configuredMaxNumberOfConnectors = 0 + if (isNotEmptyArray(stationTemplate.numberOfConnectors)) { + const numberOfConnectors = stationTemplate.numberOfConnectors as number[] + configuredMaxNumberOfConnectors = + numberOfConnectors[Math.floor(secureRandom() * numberOfConnectors.length)] + } else if (!isUndefined(stationTemplate.numberOfConnectors)) { + configuredMaxNumberOfConnectors = stationTemplate.numberOfConnectors as number + } else if (stationTemplate.Connectors != null && stationTemplate.Evses == null) { configuredMaxNumberOfConnectors = - numberOfConnectors[Math.floor(secureRandom() * numberOfConnectors.length)]; - } else if (isUndefined(stationTemplate.numberOfConnectors) === false) { - configuredMaxNumberOfConnectors = stationTemplate.numberOfConnectors as number; - } else if (stationTemplate.Connectors && !stationTemplate.Evses) { - configuredMaxNumberOfConnectors = stationTemplate.Connectors?.[0] - ? getMaxNumberOfConnectors(stationTemplate.Connectors) - 1 - : getMaxNumberOfConnectors(stationTemplate.Connectors); - } else if (stationTemplate.Evses && !stationTemplate.Connectors) { + stationTemplate.Connectors?.[0] != null + ? getMaxNumberOfConnectors(stationTemplate.Connectors) - 1 + : getMaxNumberOfConnectors(stationTemplate.Connectors) + } else if (stationTemplate.Evses != null && stationTemplate.Connectors == null) { for (const evse in stationTemplate.Evses) { if (evse === '0') { - continue; + continue } configuredMaxNumberOfConnectors += getMaxNumberOfConnectors( - stationTemplate.Evses[evse].Connectors, - ); + stationTemplate.Evses[evse].Connectors + ) } } - return configuredMaxNumberOfConnectors; -}; + return configuredMaxNumberOfConnectors +} const checkConfiguredMaxConnectors = ( configuredMaxConnectors: number, logPrefix: string, - templateFile: string, + templateFile: string ): void => { if (configuredMaxConnectors <= 0) { logger.warn( - `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors`, - ); + `${logPrefix} Charging station information from template ${templateFile} with ${configuredMaxConnectors} connectors` + ) } -}; +} const checkTemplateMaxConnectors = ( templateMaxConnectors: number, logPrefix: string, - templateFile: string, + templateFile: string ): void => { if (templateMaxConnectors === 0) { logger.warn( - `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration`, - ); + `${logPrefix} Charging station information from template ${templateFile} with empty connectors configuration` + ) } else if (templateMaxConnectors < 0) { logger.error( - `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined`, - ); + `${logPrefix} Charging station information from template ${templateFile} with no connectors configuration defined` + ) } -}; +} const initializeConnectorStatus = (connectorStatus: ConnectorStatus): void => { - connectorStatus.availability = AvailabilityType.Operative; - connectorStatus.idTagLocalAuthorized = false; - connectorStatus.idTagAuthorized = false; - connectorStatus.transactionRemoteStarted = false; - connectorStatus.transactionStarted = false; - connectorStatus.energyActiveImportRegisterValue = 0; - connectorStatus.transactionEnergyActiveImportRegisterValue = 0; + connectorStatus.availability = AvailabilityType.Operative + connectorStatus.idTagLocalAuthorized = false + connectorStatus.idTagAuthorized = false + connectorStatus.transactionRemoteStarted = false + connectorStatus.transactionStarted = false + connectorStatus.energyActiveImportRegisterValue = 0 + connectorStatus.transactionEnergyActiveImportRegisterValue = 0 if (isUndefined(connectorStatus.chargingProfiles)) { - connectorStatus.chargingProfiles = []; + connectorStatus.chargingProfiles = [] } -}; +} const warnDeprecatedTemplateKey = ( template: ChargingStationTemplate, key: string, logPrefix: string, templateFile: string, - logMsgToAppend = '', + logMsgToAppend = '' ): void => { if (!isUndefined(template?.[key as keyof ChargingStationTemplate])) { const logMsg = `Deprecated template key '${key}' usage in file '${templateFile}'${ isNotEmptyString(logMsgToAppend) ? `. ${logMsgToAppend}` : '' - }`; - logger.warn(`${logPrefix} ${logMsg}`); - console.warn(`${chalk.green(logPrefix)} ${chalk.yellow(logMsg)}`); + }` + logger.warn(`${logPrefix} ${logMsg}`) + console.warn(`${chalk.green(logPrefix)} ${chalk.yellow(logMsg)}`) } -}; +} const convertDeprecatedTemplateKey = ( template: ChargingStationTemplate, deprecatedKey: string, - key?: string, + key?: string ): void => { if (!isUndefined(template?.[deprecatedKey as keyof ChargingStationTemplate])) { if (!isUndefined(key)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion (template as unknown as Record)[key!] = - template[deprecatedKey as keyof ChargingStationTemplate]; + template[deprecatedKey as keyof ChargingStationTemplate] } - delete template[deprecatedKey as keyof ChargingStationTemplate]; + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete + delete template[deprecatedKey as keyof ChargingStationTemplate] } -}; +} interface ChargingProfilesLimit { - limit: number; - chargingProfile: ChargingProfile; + limit: number + chargingProfile: ChargingProfile } /** @@ -774,231 +797,246 @@ const getLimitFromChargingProfiles = ( chargingStation: ChargingStation, connectorId: number, chargingProfiles: ChargingProfile[], - logPrefix: string, + logPrefix: string ): ChargingProfilesLimit | undefined => { - const debugLogMsg = `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j`; - const currentDate = new Date(); - const connectorStatus = chargingStation.getConnectorStatus(connectorId)!; + const debugLogMsg = `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Matching charging profile found for power limitation: %j` + const currentDate = new Date() + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorStatus = chargingStation.getConnectorStatus(connectorId)! for (const chargingProfile of chargingProfiles) { - const chargingSchedule = chargingProfile.chargingSchedule; - if (isNullOrUndefined(chargingSchedule?.startSchedule) && connectorStatus?.transactionStarted) { + const chargingSchedule = chargingProfile.chargingSchedule + if ( + isNullOrUndefined(chargingSchedule?.startSchedule) && + connectorStatus?.transactionStarted === true + ) { logger.debug( - `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} has no startSchedule defined. Trying to set it to the connector current transaction start date`, - ); + `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} has no startSchedule defined. Trying to set it to the connector current transaction start date` + ) // OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction - chargingSchedule.startSchedule = connectorStatus?.transactionStart; + chargingSchedule.startSchedule = connectorStatus?.transactionStart } if ( !isNullOrUndefined(chargingSchedule?.startSchedule) && !isDate(chargingSchedule?.startSchedule) ) { logger.warn( - `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} startSchedule property is not a Date instance. Trying to convert it to a Date instance`, - ); - chargingSchedule.startSchedule = convertToDate(chargingSchedule?.startSchedule)!; + `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} startSchedule property is not a Date instance. Trying to convert it to a Date instance` + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingSchedule.startSchedule = convertToDate(chargingSchedule?.startSchedule)! } if ( !isNullOrUndefined(chargingSchedule?.startSchedule) && isNullOrUndefined(chargingSchedule?.duration) ) { logger.debug( - `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} has no duration defined and will be set to the maximum time allowed`, - ); + `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} has no duration defined and will be set to the maximum time allowed` + ) // OCPP specifies that if duration is not defined, it should be infinite - chargingSchedule.duration = differenceInSeconds(maxTime, chargingSchedule.startSchedule!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingSchedule.duration = differenceInSeconds(maxTime, chargingSchedule.startSchedule!) } if (!prepareChargingProfileKind(connectorStatus, chargingProfile, currentDate, logPrefix)) { - continue; + continue } if (!canProceedChargingProfile(chargingProfile, currentDate, logPrefix)) { - continue; + continue } // Check if the charging profile is active if ( isWithinInterval(currentDate, { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion start: chargingSchedule.startSchedule!, - end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!) }) ) { if (isNotEmptyArray(chargingSchedule.chargingSchedulePeriod)) { const chargingSchedulePeriodCompareFn = ( a: ChargingSchedulePeriod, - b: ChargingSchedulePeriod, - ) => a.startPeriod - b.startPeriod; + b: ChargingSchedulePeriod + ): number => a.startPeriod - b.startPeriod if ( !isArraySorted( chargingSchedule.chargingSchedulePeriod, - chargingSchedulePeriodCompareFn, + chargingSchedulePeriodCompareFn ) ) { logger.warn( - `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} schedule periods are not sorted by start period`, - ); - chargingSchedule.chargingSchedulePeriod.sort(chargingSchedulePeriodCompareFn); + `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} schedule periods are not sorted by start period` + ) + chargingSchedule.chargingSchedulePeriod.sort(chargingSchedulePeriodCompareFn) } // Check if the first schedule period startPeriod property is equal to 0 if (chargingSchedule.chargingSchedulePeriod[0].startPeriod !== 0) { logger.error( - `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} first schedule period start period ${chargingSchedule.chargingSchedulePeriod[0].startPeriod} is not equal to 0`, - ); - continue; + `${logPrefix} ${moduleName}.getLimitFromChargingProfiles: Charging profile id ${chargingProfile.chargingProfileId} first schedule period start period ${chargingSchedule.chargingSchedulePeriod[0].startPeriod} is not equal to 0` + ) + continue } // Handle only one schedule period if (chargingSchedule.chargingSchedulePeriod.length === 1) { const result: ChargingProfilesLimit = { limit: chargingSchedule.chargingSchedulePeriod[0].limit, - chargingProfile, - }; - logger.debug(debugLogMsg, result); - return result; + chargingProfile + } + logger.debug(debugLogMsg, result) + return result } - let previousChargingSchedulePeriod: ChargingSchedulePeriod | undefined; + let previousChargingSchedulePeriod: ChargingSchedulePeriod | undefined // Search for the right schedule period for (const [ index, - chargingSchedulePeriod, + chargingSchedulePeriod ] of chargingSchedule.chargingSchedulePeriod.entries()) { // Find the right schedule period if ( isAfter( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion addSeconds(chargingSchedule.startSchedule!, chargingSchedulePeriod.startPeriod), - currentDate, + currentDate ) ) { // Found the schedule period: previous is the correct one const result: ChargingProfilesLimit = { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion limit: previousChargingSchedulePeriod!.limit, - chargingProfile, - }; - logger.debug(debugLogMsg, result); - return result; + chargingProfile + } + logger.debug(debugLogMsg, result) + return result } // Keep a reference to previous one - previousChargingSchedulePeriod = chargingSchedulePeriod; + previousChargingSchedulePeriod = chargingSchedulePeriod // Handle the last schedule period within the charging profile duration if ( index === chargingSchedule.chargingSchedulePeriod.length - 1 || (index < chargingSchedule.chargingSchedulePeriod.length - 1 && differenceInSeconds( addSeconds( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingSchedule.startSchedule!, - chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod, + chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod ), - chargingSchedule.startSchedule!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingSchedule.startSchedule! + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ) > chargingSchedule.duration!) ) { const result: ChargingProfilesLimit = { limit: previousChargingSchedulePeriod.limit, - chargingProfile, - }; - logger.debug(debugLogMsg, result); - return result; + chargingProfile + } + logger.debug(debugLogMsg, result) + return result } } } } } -}; +} export const prepareChargingProfileKind = ( connectorStatus: ConnectorStatus, chargingProfile: ChargingProfile, currentDate: Date, - logPrefix: string, + logPrefix: string ): boolean => { switch (chargingProfile.chargingProfileKind) { case ChargingProfileKindType.RECURRING: if (!canProceedRecurringChargingProfile(chargingProfile, logPrefix)) { - return false; + return false } - prepareRecurringChargingProfile(chargingProfile, currentDate, logPrefix); - break; + prepareRecurringChargingProfile(chargingProfile, currentDate, logPrefix) + break case ChargingProfileKindType.RELATIVE: if (!isNullOrUndefined(chargingProfile.chargingSchedule.startSchedule)) { logger.warn( - `${logPrefix} ${moduleName}.prepareChargingProfileKind: Relative charging profile id ${chargingProfile.chargingProfileId} has a startSchedule property defined. It will be ignored or used if the connector has a transaction started`, - ); - delete chargingProfile.chargingSchedule.startSchedule; + `${logPrefix} ${moduleName}.prepareChargingProfileKind: Relative charging profile id ${chargingProfile.chargingProfileId} has a startSchedule property defined. It will be ignored or used if the connector has a transaction started` + ) + delete chargingProfile.chargingSchedule.startSchedule } - if (connectorStatus?.transactionStarted) { - chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart; + if (connectorStatus?.transactionStarted === true) { + chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart } // FIXME: Handle relative charging profile duration - break; + break } - return true; -}; + return true +} export const canProceedChargingProfile = ( chargingProfile: ChargingProfile, currentDate: Date, - logPrefix: string, + logPrefix: string ): boolean => { if ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion (isValidTime(chargingProfile.validFrom) && isBefore(currentDate, chargingProfile.validFrom!)) || + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion (isValidTime(chargingProfile.validTo) && isAfter(currentDate, chargingProfile.validTo!)) ) { logger.debug( `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${ chargingProfile.chargingProfileId - } is not valid for the current date ${currentDate.toISOString()}`, - ); - return false; + } is not valid for the current date ${currentDate.toISOString()}` + ) + return false } if ( isNullOrUndefined(chargingProfile.chargingSchedule.startSchedule) || isNullOrUndefined(chargingProfile.chargingSchedule.duration) ) { logger.error( - `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has no startSchedule or duration defined`, - ); - return false; + `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has no startSchedule or duration defined` + ) + return false } if ( !isNullOrUndefined(chargingProfile.chargingSchedule.startSchedule) && !isValidTime(chargingProfile.chargingSchedule.startSchedule) ) { logger.error( - `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has an invalid startSchedule date defined`, - ); - return false; + `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has an invalid startSchedule date defined` + ) + return false } if ( !isNullOrUndefined(chargingProfile.chargingSchedule.duration) && !Number.isSafeInteger(chargingProfile.chargingSchedule.duration) ) { logger.error( - `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has non integer duration defined`, - ); - return false; + `${logPrefix} ${moduleName}.canProceedChargingProfile: Charging profile id ${chargingProfile.chargingProfileId} has non integer duration defined` + ) + return false } - return true; -}; + return true +} const canProceedRecurringChargingProfile = ( chargingProfile: ChargingProfile, - logPrefix: string, + logPrefix: string ): boolean => { if ( chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING && isNullOrUndefined(chargingProfile.recurrencyKind) ) { logger.error( - `${logPrefix} ${moduleName}.canProceedRecurringChargingProfile: Recurring charging profile id ${chargingProfile.chargingProfileId} has no recurrencyKind defined`, - ); - return false; + `${logPrefix} ${moduleName}.canProceedRecurringChargingProfile: Recurring charging profile id ${chargingProfile.chargingProfileId} has no recurrencyKind defined` + ) + return false } if ( chargingProfile.chargingProfileKind === ChargingProfileKindType.RECURRING && isNullOrUndefined(chargingProfile.chargingSchedule.startSchedule) ) { logger.error( - `${logPrefix} ${moduleName}.canProceedRecurringChargingProfile: Recurring charging profile id ${chargingProfile.chargingProfileId} has no startSchedule defined`, - ); - return false; + `${logPrefix} ${moduleName}.canProceedRecurringChargingProfile: Recurring charging profile id ${chargingProfile.chargingProfileId} has no startSchedule defined` + ) + return false } - return true; -}; + return true +} /** * Adjust recurring charging profile startSchedule to the current recurrency time interval if needed @@ -1010,77 +1048,84 @@ const canProceedRecurringChargingProfile = ( const prepareRecurringChargingProfile = ( chargingProfile: ChargingProfile, currentDate: Date, - logPrefix: string, + logPrefix: string ): boolean => { - const chargingSchedule = chargingProfile.chargingSchedule; - let recurringIntervalTranslated = false; - let recurringInterval: Interval; + const chargingSchedule = chargingProfile.chargingSchedule + let recurringIntervalTranslated = false + let recurringInterval: Interval switch (chargingProfile.recurrencyKind) { case RecurrencyKindType.DAILY: recurringInterval = { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion start: chargingSchedule.startSchedule!, - end: addDays(chargingSchedule.startSchedule!, 1), - }; - checkRecurringChargingProfileDuration(chargingProfile, recurringInterval, logPrefix); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + end: addDays(chargingSchedule.startSchedule!, 1) + } + checkRecurringChargingProfileDuration(chargingProfile, recurringInterval, logPrefix) if ( !isWithinInterval(currentDate, recurringInterval) && isBefore(recurringInterval.end, currentDate) ) { chargingSchedule.startSchedule = addDays( recurringInterval.start, - differenceInDays(currentDate, recurringInterval.start), - ); + differenceInDays(currentDate, recurringInterval.start) + ) recurringInterval = { start: chargingSchedule.startSchedule, - end: addDays(chargingSchedule.startSchedule, 1), - }; - recurringIntervalTranslated = true; + end: addDays(chargingSchedule.startSchedule, 1) + } + recurringIntervalTranslated = true } - break; + break case RecurrencyKindType.WEEKLY: recurringInterval = { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion start: chargingSchedule.startSchedule!, - end: addWeeks(chargingSchedule.startSchedule!, 1), - }; - checkRecurringChargingProfileDuration(chargingProfile, recurringInterval, logPrefix); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + end: addWeeks(chargingSchedule.startSchedule!, 1) + } + checkRecurringChargingProfileDuration(chargingProfile, recurringInterval, logPrefix) if ( !isWithinInterval(currentDate, recurringInterval) && isBefore(recurringInterval.end, currentDate) ) { chargingSchedule.startSchedule = addWeeks( recurringInterval.start, - differenceInWeeks(currentDate, recurringInterval.start), - ); + differenceInWeeks(currentDate, recurringInterval.start) + ) recurringInterval = { start: chargingSchedule.startSchedule, - end: addWeeks(chargingSchedule.startSchedule, 1), - }; - recurringIntervalTranslated = true; + end: addWeeks(chargingSchedule.startSchedule, 1) + } + recurringIntervalTranslated = true } - break; + break default: logger.error( - `${logPrefix} ${moduleName}.prepareRecurringChargingProfile: Recurring ${chargingProfile.recurrencyKind} charging profile id ${chargingProfile.chargingProfileId} is not supported`, - ); + `${logPrefix} ${moduleName}.prepareRecurringChargingProfile: Recurring ${chargingProfile.recurrencyKind} charging profile id ${chargingProfile.chargingProfileId} is not supported` + ) } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (recurringIntervalTranslated && !isWithinInterval(currentDate, recurringInterval!)) { logger.error( `${logPrefix} ${moduleName}.prepareRecurringChargingProfile: Recurring ${ chargingProfile.recurrencyKind } charging profile id ${chargingProfile.chargingProfileId} recurrency time interval [${toDate( - recurringInterval!.start, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + recurringInterval!.start ).toISOString()}, ${toDate( - recurringInterval!.end, - ).toISOString()}] has not been properly translated to current date ${currentDate.toISOString()} `, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + recurringInterval!.end + ).toISOString()}] has not been properly translated to current date ${currentDate.toISOString()} ` + ) } - return recurringIntervalTranslated; -}; + return recurringIntervalTranslated +} const checkRecurringChargingProfileDuration = ( chargingProfile: ChargingProfile, interval: Interval, - logPrefix: string, + logPrefix: string ): void => { if (isNullOrUndefined(chargingProfile.chargingSchedule.duration)) { logger.warn( @@ -1090,11 +1135,12 @@ const checkRecurringChargingProfileDuration = ( chargingProfile.chargingProfileId } duration is not defined, set it to the recurrency time interval duration ${differenceInSeconds( interval.end, - interval.start, - )}`, - ); - chargingProfile.chargingSchedule.duration = differenceInSeconds(interval.end, interval.start); + interval.start + )}` + ) + chargingProfile.chargingSchedule.duration = differenceInSeconds(interval.end, interval.start) } else if ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingProfile.chargingSchedule.duration! > differenceInSeconds(interval.end, interval.start) ) { logger.warn( @@ -1104,20 +1150,20 @@ const checkRecurringChargingProfileDuration = ( chargingProfile.chargingSchedule.duration } is greater than the recurrency time interval duration ${differenceInSeconds( interval.end, - interval.start, - )}`, - ); - chargingProfile.chargingSchedule.duration = differenceInSeconds(interval.end, interval.start); + interval.start + )}` + ) + chargingProfile.chargingSchedule.duration = differenceInSeconds(interval.end, interval.start) } -}; +} const getRandomSerialNumberSuffix = (params?: { - randomBytesLength?: number; - upperCase?: boolean; + randomBytesLength?: number + upperCase?: boolean }): string => { - const randomSerialNumberSuffix = randomBytes(params?.randomBytesLength ?? 16).toString('hex'); - if (params?.upperCase) { - return randomSerialNumberSuffix.toUpperCase(); + const randomSerialNumberSuffix = randomBytes(params?.randomBytesLength ?? 16).toString('hex') + if (params?.upperCase === true) { + return randomSerialNumberSuffix.toUpperCase() } - return randomSerialNumberSuffix; -}; + return randomSerialNumberSuffix +} diff --git a/src/charging-station/IdTagsCache.ts b/src/charging-station/IdTagsCache.ts index 3e259fff..b50c0620 100644 --- a/src/charging-station/IdTagsCache.ts +++ b/src/charging-station/IdTagsCache.ts @@ -1,37 +1,37 @@ -import { type FSWatcher, readFileSync } from 'node:fs'; +import { type FSWatcher, readFileSync } from 'node:fs' -import type { ChargingStation } from './ChargingStation.js'; -import { getIdTagsFile } from './Helpers.js'; -import { FileType, IdTagDistribution } from '../types/index.js'; +import type { ChargingStation } from './ChargingStation.js' +import { getIdTagsFile } from './Helpers.js' +import { FileType, IdTagDistribution } from '../types/index.js' import { handleFileException, isNotEmptyString, logPrefix, logger, secureRandom, - watchJsonFile, -} from '../utils/index.js'; + watchJsonFile +} from '../utils/index.js' interface IdTagsCacheValueType { - idTags: string[]; - idTagsFileWatcher: FSWatcher | undefined; + idTags: string[] + idTagsFileWatcher: FSWatcher | undefined } export class IdTagsCache { - private static instance: IdTagsCache | null = null; - private readonly idTagsCaches: Map; - private readonly idTagsCachesAddressableIndexes: Map; + private static instance: IdTagsCache | null = null + private readonly idTagsCaches: Map + private readonly idTagsCachesAddressableIndexes: Map - private constructor() { - this.idTagsCaches = new Map(); - this.idTagsCachesAddressableIndexes = new Map(); + private constructor () { + this.idTagsCaches = new Map() + this.idTagsCachesAddressableIndexes = new Map() } - public static getInstance(): IdTagsCache { + public static getInstance (): IdTagsCache { if (IdTagsCache.instance === null) { - IdTagsCache.instance = new IdTagsCache(); + IdTagsCache.instance = new IdTagsCache() } - return IdTagsCache.instance; + return IdTagsCache.instance } /** @@ -43,22 +43,23 @@ export class IdTagsCache { * @param connectorId - * @returns */ - public getIdTag( + public getIdTag ( distribution: IdTagDistribution, chargingStation: ChargingStation, - connectorId: number, + connectorId: number ): string { - const hashId = chargingStation.stationInfo.hashId; - const idTagsFile = getIdTagsFile(chargingStation.stationInfo)!; + const hashId = chargingStation.stationInfo.hashId + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const idTagsFile = getIdTagsFile(chargingStation.stationInfo)! switch (distribution) { case IdTagDistribution.RANDOM: - return this.getRandomIdTag(hashId, idTagsFile); + return this.getRandomIdTag(hashId, idTagsFile) case IdTagDistribution.ROUND_ROBIN: - return this.getRoundRobinIdTag(hashId, idTagsFile); + return this.getRoundRobinIdTag(hashId, idTagsFile) case IdTagDistribution.CONNECTOR_AFFINITY: - return this.getConnectorAffinityIdTag(chargingStation, connectorId); + return this.getConnectorAffinityIdTag(chargingStation, connectorId) default: - return this.getRoundRobinIdTag(hashId, idTagsFile); + return this.getRoundRobinIdTag(hashId, idTagsFile) } } @@ -69,58 +70,64 @@ export class IdTagsCache { * @param file - * @returns */ - public getIdTags(file: string): string[] | undefined { - if (this.hasIdTagsCache(file) === false) { - this.setIdTagsCache(file, this.getIdTagsFromFile(file)); + public getIdTags (file: string): string[] | undefined { + if (!this.hasIdTagsCache(file)) { + this.setIdTagsCache(file, this.getIdTagsFromFile(file)) } - return this.getIdTagsCache(file); + return this.getIdTagsCache(file) } - public deleteIdTags(file: string): boolean { - return this.deleteIdTagsCache(file) && this.deleteIdTagsCacheIndexes(file); + public deleteIdTags (file: string): boolean { + return this.deleteIdTagsCache(file) && this.deleteIdTagsCacheIndexes(file) } - private getRandomIdTag(hashId: string, file: string): string { - const idTags = this.getIdTags(file)!; - const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId); + private getRandomIdTag (hashId: string, file: string): string { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const idTags = this.getIdTags(file)! + const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId) this.idTagsCachesAddressableIndexes.set( addressableKey, - Math.floor(secureRandom() * idTags.length), - ); - return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)!]; + Math.floor(secureRandom() * idTags.length) + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)!] } - private getRoundRobinIdTag(hashId: string, file: string): string { - const idTags = this.getIdTags(file)!; - const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId); - const idTagIndex = this.idTagsCachesAddressableIndexes.get(addressableKey) ?? 0; - const idTag = idTags[idTagIndex]; + private getRoundRobinIdTag (hashId: string, file: string): string { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const idTags = this.getIdTags(file)! + const addressableKey = this.getIdTagsCacheIndexesAddressableKey(file, hashId) + const idTagIndex = this.idTagsCachesAddressableIndexes.get(addressableKey) ?? 0 + const idTag = idTags[idTagIndex] this.idTagsCachesAddressableIndexes.set( addressableKey, - idTagIndex === idTags.length - 1 ? 0 : idTagIndex + 1, - ); - return idTag; + idTagIndex === idTags.length - 1 ? 0 : idTagIndex + 1 + ) + return idTag } - private getConnectorAffinityIdTag(chargingStation: ChargingStation, connectorId: number): string { - const file = getIdTagsFile(chargingStation.stationInfo)!; - const idTags = this.getIdTags(file)!; + private getConnectorAffinityIdTag (chargingStation: ChargingStation, connectorId: number): string { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const file = getIdTagsFile(chargingStation.stationInfo)! + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const idTags = this.getIdTags(file)! const addressableKey = this.getIdTagsCacheIndexesAddressableKey( file, - chargingStation.stationInfo.hashId, - ); + chargingStation.stationInfo.hashId + ) this.idTagsCachesAddressableIndexes.set( addressableKey, - (chargingStation.index - 1 + (connectorId - 1)) % idTags.length, - ); - return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)!]; + (chargingStation.index - 1 + (connectorId - 1)) % idTags.length + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return idTags[this.idTagsCachesAddressableIndexes.get(addressableKey)!] } - private hasIdTagsCache(file: string): boolean { - return this.idTagsCaches.has(file); + private hasIdTagsCache (file: string): boolean { + return this.idTagsCaches.has(file) } - private setIdTagsCache(file: string, idTags: string[]) { + private setIdTagsCache (file: string, idTags: string[]): Map { return this.idTagsCaches.set(file, { idTags, idTagsFileWatcher: watchJsonFile( @@ -132,10 +139,10 @@ export class IdTagsCache { if (isNotEmptyString(filename) && event === 'change') { try { logger.debug( - `${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload`, - ); - this.deleteIdTagsCache(file); - this.deleteIdTagsCacheIndexes(file); + `${this.logPrefix(file)} ${FileType.Authorization} file have changed, reload` + ) + this.deleteIdTagsCache(file) + this.deleteIdTagsCacheIndexes(file) } catch (error) { handleFileException( file, @@ -143,56 +150,56 @@ export class IdTagsCache { error as NodeJS.ErrnoException, this.logPrefix(file), { - throwError: false, - }, - ); + throwError: false + } + ) } } - }, - ), - }); + } + ) + }) } - private getIdTagsCache(file: string): string[] | undefined { - return this.idTagsCaches.get(file)?.idTags; + private getIdTagsCache (file: string): string[] | undefined { + return this.idTagsCaches.get(file)?.idTags } - private deleteIdTagsCache(file: string): boolean { - this.idTagsCaches.get(file)?.idTagsFileWatcher?.close(); - return this.idTagsCaches.delete(file); + private deleteIdTagsCache (file: string): boolean { + this.idTagsCaches.get(file)?.idTagsFileWatcher?.close() + return this.idTagsCaches.delete(file) } - private deleteIdTagsCacheIndexes(file: string): boolean { - const deleted: boolean[] = []; + private deleteIdTagsCacheIndexes (file: string): boolean { + const deleted: boolean[] = [] for (const [key] of this.idTagsCachesAddressableIndexes) { if (key.startsWith(file)) { - deleted.push(this.idTagsCachesAddressableIndexes.delete(key)); + deleted.push(this.idTagsCachesAddressableIndexes.delete(key)) } } - return !deleted.some((value) => value === false); + return !deleted.some((value) => !value) } - private getIdTagsCacheIndexesAddressableKey(prefix: string, uid: string): string { - return `${prefix}${uid}`; + private getIdTagsCacheIndexesAddressableKey (prefix: string, uid: string): string { + return `${prefix}${uid}` } - private getIdTagsFromFile(file: string): string[] { + private getIdTagsFromFile (file: string): string[] { if (isNotEmptyString(file)) { try { - return JSON.parse(readFileSync(file, 'utf8')) as string[]; + return JSON.parse(readFileSync(file, 'utf8')) as string[] } catch (error) { handleFileException( file, FileType.Authorization, error as NodeJS.ErrnoException, - this.logPrefix(file), - ); + this.logPrefix(file) + ) } } - return []; + return [] } - private logPrefix = (file: string): string => { - return logPrefix(` Id tags cache for id tags file '${file}' |`); - }; + private readonly logPrefix = (file: string): string => { + return logPrefix(` Id tags cache for id tags file '${file}' |`) + } } diff --git a/src/charging-station/SharedLRUCache.ts b/src/charging-station/SharedLRUCache.ts index b2dacec8..6985e707 100644 --- a/src/charging-station/SharedLRUCache.ts +++ b/src/charging-station/SharedLRUCache.ts @@ -1,127 +1,131 @@ -import { LRUMapWithDelete as LRUCache } from 'mnemonist'; +import { LRUMapWithDelete as LRUCache } from 'mnemonist' -import { Bootstrap } from './Bootstrap.js'; -import type { ChargingStationConfiguration, ChargingStationTemplate } from '../types/index.js'; +import { Bootstrap } from './Bootstrap.js' +import type { ChargingStationConfiguration, ChargingStationTemplate } from '../types/index.js' import { isEmptyObject, isNotEmptyArray, isNotEmptyString, - isNullOrUndefined, -} from '../utils/index.js'; + isNullOrUndefined +} from '../utils/index.js' enum CacheType { chargingStationTemplate = 'chargingStationTemplate', - chargingStationConfiguration = 'chargingStationConfiguration', + chargingStationConfiguration = 'chargingStationConfiguration' } -type CacheValueType = ChargingStationTemplate | ChargingStationConfiguration; +type CacheValueType = ChargingStationTemplate | ChargingStationConfiguration export class SharedLRUCache { - private static instance: SharedLRUCache | null = null; - private readonly lruCache: LRUCache; + private static instance: SharedLRUCache | null = null + private readonly lruCache: LRUCache - private constructor() { + private constructor () { this.lruCache = new LRUCache( Bootstrap.getInstance().numberOfChargingStationTemplates + - Bootstrap.getInstance().numberOfChargingStations, - ); + Bootstrap.getInstance().numberOfChargingStations + ) } - public static getInstance(): SharedLRUCache { + public static getInstance (): SharedLRUCache { if (SharedLRUCache.instance === null) { - SharedLRUCache.instance = new SharedLRUCache(); + SharedLRUCache.instance = new SharedLRUCache() } - return SharedLRUCache.instance; + return SharedLRUCache.instance } - public hasChargingStationConfiguration(chargingStationConfigurationHash: string): boolean { - return this.has(this.getChargingStationConfigurationKey(chargingStationConfigurationHash)); + public hasChargingStationConfiguration (chargingStationConfigurationHash: string): boolean { + return this.has(this.getChargingStationConfigurationKey(chargingStationConfigurationHash)) } - public setChargingStationConfiguration( - chargingStationConfiguration: ChargingStationConfiguration, + public setChargingStationConfiguration ( + chargingStationConfiguration: ChargingStationConfiguration ): void { if (this.isChargingStationConfigurationCacheable(chargingStationConfiguration)) { this.set( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.getChargingStationConfigurationKey(chargingStationConfiguration.configurationHash!), - chargingStationConfiguration, - ); + chargingStationConfiguration + ) } } - public getChargingStationConfiguration( - chargingStationConfigurationHash: string, + public getChargingStationConfiguration ( + chargingStationConfigurationHash: string ): ChargingStationConfiguration { return this.get( - this.getChargingStationConfigurationKey(chargingStationConfigurationHash), - ) as ChargingStationConfiguration; + this.getChargingStationConfigurationKey(chargingStationConfigurationHash) + ) as ChargingStationConfiguration } - public deleteChargingStationConfiguration(chargingStationConfigurationHash: string): void { - this.delete(this.getChargingStationConfigurationKey(chargingStationConfigurationHash)); + public deleteChargingStationConfiguration (chargingStationConfigurationHash: string): void { + this.delete(this.getChargingStationConfigurationKey(chargingStationConfigurationHash)) } - public hasChargingStationTemplate(chargingStationTemplateHash: string): boolean { - return this.has(this.getChargingStationTemplateKey(chargingStationTemplateHash)); + public hasChargingStationTemplate (chargingStationTemplateHash: string): boolean { + return this.has(this.getChargingStationTemplateKey(chargingStationTemplateHash)) } - public setChargingStationTemplate(chargingStationTemplate: ChargingStationTemplate): void { + public setChargingStationTemplate (chargingStationTemplate: ChargingStationTemplate): void { this.set( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.getChargingStationTemplateKey(chargingStationTemplate.templateHash!), - chargingStationTemplate, - ); + chargingStationTemplate + ) } - public getChargingStationTemplate(chargingStationTemplateHash: string): ChargingStationTemplate { + public getChargingStationTemplate (chargingStationTemplateHash: string): ChargingStationTemplate { return this.get( - this.getChargingStationTemplateKey(chargingStationTemplateHash), - ) as ChargingStationTemplate; + this.getChargingStationTemplateKey(chargingStationTemplateHash) + ) as ChargingStationTemplate } - public deleteChargingStationTemplate(chargingStationTemplateHash: string): void { - this.delete(this.getChargingStationTemplateKey(chargingStationTemplateHash)); + public deleteChargingStationTemplate (chargingStationTemplateHash: string): void { + this.delete(this.getChargingStationTemplateKey(chargingStationTemplateHash)) } - public clear(): void { - this.lruCache.clear(); + public clear (): void { + this.lruCache.clear() } - private getChargingStationConfigurationKey(hash: string): string { - return `${CacheType.chargingStationConfiguration}${hash}`; + private getChargingStationConfigurationKey (hash: string): string { + return `${CacheType.chargingStationConfiguration}${hash}` } - private getChargingStationTemplateKey(hash: string): string { - return `${CacheType.chargingStationTemplate}${hash}`; + private getChargingStationTemplateKey (hash: string): string { + return `${CacheType.chargingStationTemplate}${hash}` } - private has(key: string): boolean { - return this.lruCache.has(key); + private has (key: string): boolean { + return this.lruCache.has(key) } - private get(key: string): CacheValueType | undefined { - return this.lruCache.get(key); + private get (key: string): CacheValueType | undefined { + return this.lruCache.get(key) } - private set(key: string, value: CacheValueType): void { - this.lruCache.set(key, value); + private set (key: string, value: CacheValueType): void { + this.lruCache.set(key, value) } - private delete(key: string): void { - this.lruCache.delete(key); + private delete (key: string): void { + this.lruCache.delete(key) } - private isChargingStationConfigurationCacheable( - chargingStationConfiguration: ChargingStationConfiguration, + private isChargingStationConfigurationCacheable ( + chargingStationConfiguration: ChargingStationConfiguration ): boolean { return ( - isNullOrUndefined(chargingStationConfiguration?.configurationKey) === false && - isNullOrUndefined(chargingStationConfiguration?.stationInfo) === false && - isNullOrUndefined(chargingStationConfiguration?.automaticTransactionGenerator) === false && - isNullOrUndefined(chargingStationConfiguration?.configurationHash) === false && - isNotEmptyArray(chargingStationConfiguration?.configurationKey) === true && - isEmptyObject(chargingStationConfiguration.stationInfo!) === false && - isEmptyObject(chargingStationConfiguration.automaticTransactionGenerator!) === false && - isNotEmptyString(chargingStationConfiguration?.configurationHash) === true - ); + !isNullOrUndefined(chargingStationConfiguration?.configurationKey) && + !isNullOrUndefined(chargingStationConfiguration?.stationInfo) && + !isNullOrUndefined(chargingStationConfiguration?.automaticTransactionGenerator) && + !isNullOrUndefined(chargingStationConfiguration?.configurationHash) && + isNotEmptyArray(chargingStationConfiguration?.configurationKey) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + !isEmptyObject(chargingStationConfiguration.stationInfo!) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + !isEmptyObject(chargingStationConfiguration.automaticTransactionGenerator!) && + isNotEmptyString(chargingStationConfiguration?.configurationHash) + ) } } diff --git a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts index 5de17e1a..87f693f8 100644 --- a/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/ChargingStationWorkerBroadcastChannel.ts @@ -1,7 +1,7 @@ -import { secondsToMilliseconds } from 'date-fns'; +import { secondsToMilliseconds } from 'date-fns' -import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'; -import { BaseError, type OCPPError } from '../../exception/index.js'; +import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js' +import { BaseError, type OCPPError } from '../../exception/index.js' import { AuthorizationStatus, type AuthorizeRequest, @@ -35,20 +35,20 @@ import { type StatusNotificationRequest, type StatusNotificationResponse, type StopTransactionRequest, - type StopTransactionResponse, -} from '../../types/index.js'; + type StopTransactionResponse +} from '../../types/index.js' import { Constants, convertToInt, isEmptyObject, isNullOrUndefined, - logger, -} from '../../utils/index.js'; -import type { ChargingStation } from '../ChargingStation.js'; -import { getConfigurationKey } from '../ConfigurationKeyUtils.js'; -import { buildMeterValue } from '../ocpp/index.js'; + logger +} from '../../utils/index.js' +import type { ChargingStation } from '../ChargingStation.js' +import { getConfigurationKey } from '../ConfigurationKeyUtils.js' +import { buildMeterValue } from '../ocpp/index.js' -const moduleName = 'ChargingStationWorkerBroadcastChannel'; +const moduleName = 'ChargingStationWorkerBroadcastChannel' type CommandResponse = | EmptyObject @@ -57,138 +57,149 @@ type CommandResponse = | AuthorizeResponse | BootNotificationResponse | HeartbeatResponse - | DataTransferResponse; + | DataTransferResponse type CommandHandler = ( - requestPayload?: BroadcastChannelRequestPayload, -) => Promise | void; + requestPayload?: BroadcastChannelRequestPayload + // eslint-disable-next-line @typescript-eslint/no-invalid-void-type +) => Promise | void export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChannel { - private readonly commandHandlers: Map; - private readonly chargingStation: ChargingStation; + private readonly commandHandlers: Map + private readonly chargingStation: ChargingStation - constructor(chargingStation: ChargingStation) { - super(); + constructor (chargingStation: ChargingStation) { + super() const requestParams: RequestParams = { - throwError: true, - }; + throwError: true + } this.commandHandlers = new Map([ - [BroadcastChannelProcedureName.START_CHARGING_STATION, () => this.chargingStation.start()], + [ + BroadcastChannelProcedureName.START_CHARGING_STATION, + () => { + this.chargingStation.start() + } + ], [ BroadcastChannelProcedureName.STOP_CHARGING_STATION, - async () => this.chargingStation.stop(), + async () => { + await this.chargingStation.stop() + } ], [ BroadcastChannelProcedureName.OPEN_CONNECTION, - () => this.chargingStation.openWSConnection(), + () => { + this.chargingStation.openWSConnection() + } ], [ BroadcastChannelProcedureName.CLOSE_CONNECTION, - () => this.chargingStation.closeWSConnection(), + () => { + this.chargingStation.closeWSConnection() + } ], [ BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR, - (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.startAutomaticTransactionGenerator(requestPayload?.connectorIds), + (requestPayload?: BroadcastChannelRequestPayload) => { + this.chargingStation.startAutomaticTransactionGenerator(requestPayload?.connectorIds) + } ], [ BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR, - (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.stopAutomaticTransactionGenerator(requestPayload?.connectorIds), + (requestPayload?: BroadcastChannelRequestPayload) => { + this.chargingStation.stopAutomaticTransactionGenerator(requestPayload?.connectorIds) + } ], [ BroadcastChannelProcedureName.SET_SUPERVISION_URL, - (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.setSupervisionUrl(requestPayload?.url as string), + (requestPayload?: BroadcastChannelRequestPayload) => { + this.chargingStation.setSupervisionUrl(requestPayload?.url as string) + } ], [ BroadcastChannelProcedureName.START_TRANSACTION, async (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.ocppRequestService.requestHandler< - StartTransactionRequest, - StartTransactionResponse - >(this.chargingStation, RequestCommand.START_TRANSACTION, requestPayload, requestParams), + await this.chargingStation.ocppRequestService.requestHandler< + StartTransactionRequest, + StartTransactionResponse + >(this.chargingStation, RequestCommand.START_TRANSACTION, requestPayload, requestParams) ], [ BroadcastChannelProcedureName.STOP_TRANSACTION, async (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.ocppRequestService.requestHandler< - StopTransactionRequest, - StartTransactionResponse + await this.chargingStation.ocppRequestService.requestHandler< + StopTransactionRequest, + StartTransactionResponse >( this.chargingStation, RequestCommand.STOP_TRANSACTION, { meterStop: this.chargingStation.getEnergyActiveImportRegisterByTransactionId( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion requestPayload!.transactionId!, - true, + true ), - ...requestPayload, + ...requestPayload }, - requestParams, - ), + requestParams + ) ], [ BroadcastChannelProcedureName.AUTHORIZE, async (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.ocppRequestService.requestHandler< - AuthorizeRequest, - AuthorizeResponse - >(this.chargingStation, RequestCommand.AUTHORIZE, requestPayload, requestParams), + await this.chargingStation.ocppRequestService.requestHandler< + AuthorizeRequest, + AuthorizeResponse + >(this.chargingStation, RequestCommand.AUTHORIZE, requestPayload, requestParams) ], [ BroadcastChannelProcedureName.BOOT_NOTIFICATION, async (requestPayload?: BroadcastChannelRequestPayload) => { this.chargingStation.bootNotificationResponse = await this.chargingStation.ocppRequestService.requestHandler< - BootNotificationRequest, - BootNotificationResponse + BootNotificationRequest, + BootNotificationResponse >( this.chargingStation, RequestCommand.BOOT_NOTIFICATION, { ...this.chargingStation.bootNotificationRequest, - ...requestPayload, + ...requestPayload }, { skipBufferingOnError: true, - throwError: true, - }, - ); - return this.chargingStation.bootNotificationResponse; - }, + throwError: true + } + ) + return this.chargingStation.bootNotificationResponse + } ], [ BroadcastChannelProcedureName.STATUS_NOTIFICATION, async (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.ocppRequestService.requestHandler< - StatusNotificationRequest, - StatusNotificationResponse - >( - this.chargingStation, - RequestCommand.STATUS_NOTIFICATION, - requestPayload, - requestParams, - ), + await this.chargingStation.ocppRequestService.requestHandler< + StatusNotificationRequest, + StatusNotificationResponse + >(this.chargingStation, RequestCommand.STATUS_NOTIFICATION, requestPayload, requestParams) ], [ BroadcastChannelProcedureName.HEARTBEAT, async (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.ocppRequestService.requestHandler< - HeartbeatRequest, - HeartbeatResponse - >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload, requestParams), + await this.chargingStation.ocppRequestService.requestHandler< + HeartbeatRequest, + HeartbeatResponse + >(this.chargingStation, RequestCommand.HEARTBEAT, requestPayload, requestParams) ], [ BroadcastChannelProcedureName.METER_VALUES, async (requestPayload?: BroadcastChannelRequestPayload) => { const configuredMeterValueSampleInterval = getConfigurationKey( chargingStation, - StandardParametersKey.MeterValueSampleInterval, - ); - return this.chargingStation.ocppRequestService.requestHandler< - MeterValuesRequest, - MeterValuesResponse + StandardParametersKey.MeterValueSampleInterval + ) + return await this.chargingStation.ocppRequestService.requestHandler< + MeterValuesRequest, + MeterValuesResponse >( this.chargingStation, RequestCommand.METER_VALUES, @@ -196,171 +207,177 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne meterValue: [ buildMeterValue( this.chargingStation, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion requestPayload!.connectorId!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.chargingStation.getConnectorStatus(requestPayload!.connectorId!)! .transactionId!, configuredMeterValueSampleInterval !== undefined ? secondsToMilliseconds(convertToInt(configuredMeterValueSampleInterval.value)) - : Constants.DEFAULT_METER_VALUES_INTERVAL, - ), + : Constants.DEFAULT_METER_VALUES_INTERVAL + ) ], - ...requestPayload, + ...requestPayload }, - requestParams, - ); - }, + requestParams + ) + } ], [ BroadcastChannelProcedureName.DATA_TRANSFER, async (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.ocppRequestService.requestHandler< - DataTransferRequest, - DataTransferResponse - >(this.chargingStation, RequestCommand.DATA_TRANSFER, requestPayload, requestParams), + await this.chargingStation.ocppRequestService.requestHandler< + DataTransferRequest, + DataTransferResponse + >(this.chargingStation, RequestCommand.DATA_TRANSFER, requestPayload, requestParams) ], [ BroadcastChannelProcedureName.DIAGNOSTICS_STATUS_NOTIFICATION, async (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.ocppRequestService.requestHandler< - DiagnosticsStatusNotificationRequest, - DiagnosticsStatusNotificationResponse + await this.chargingStation.ocppRequestService.requestHandler< + DiagnosticsStatusNotificationRequest, + DiagnosticsStatusNotificationResponse >( this.chargingStation, RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, requestPayload, - requestParams, - ), + requestParams + ) ], [ BroadcastChannelProcedureName.FIRMWARE_STATUS_NOTIFICATION, async (requestPayload?: BroadcastChannelRequestPayload) => - this.chargingStation.ocppRequestService.requestHandler< - FirmwareStatusNotificationRequest, - FirmwareStatusNotificationResponse + await this.chargingStation.ocppRequestService.requestHandler< + FirmwareStatusNotificationRequest, + FirmwareStatusNotificationResponse >( this.chargingStation, RequestCommand.FIRMWARE_STATUS_NOTIFICATION, requestPayload, - requestParams, - ), - ], - ]); - this.chargingStation = chargingStation; - this.onmessage = this.requestHandler.bind(this) as (message: unknown) => void; - this.onmessageerror = this.messageErrorHandler.bind(this) as (message: unknown) => void; + requestParams + ) + ] + ]) + this.chargingStation = chargingStation + this.onmessage = this.requestHandler.bind(this) as (message: unknown) => void + this.onmessageerror = this.messageErrorHandler.bind(this) as (message: unknown) => void } - private async requestHandler(messageEvent: MessageEvent): Promise { - const validatedMessageEvent = this.validateMessageEvent(messageEvent); + private async requestHandler (messageEvent: MessageEvent): Promise { + const validatedMessageEvent = this.validateMessageEvent(messageEvent) if (validatedMessageEvent === false) { - return; + return } - if (this.isResponse(validatedMessageEvent.data) === true) { - return; + if (this.isResponse(validatedMessageEvent.data)) { + return } - const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest; + const [uuid, command, requestPayload] = validatedMessageEvent.data as BroadcastChannelRequest if ( !isNullOrUndefined(requestPayload.hashIds) && requestPayload.hashIds?.includes(this.chargingStation.stationInfo.hashId) === false ) { - return; + return } if (!isNullOrUndefined(requestPayload.hashId)) { logger.error( - `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead`, - ); - return; + `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: 'hashId' field usage in PDU is deprecated, use 'hashIds' array instead` + ) + return } - let responsePayload: BroadcastChannelResponsePayload | undefined; - let commandResponse: CommandResponse | void | undefined; + let responsePayload: BroadcastChannelResponsePayload | undefined + let commandResponse: CommandResponse | undefined try { - commandResponse = await this.commandHandler(command, requestPayload); - if (isNullOrUndefined(commandResponse) || isEmptyObject(commandResponse as CommandResponse)) { + commandResponse = await this.commandHandler(command, requestPayload) + if (isNullOrUndefined(commandResponse) || isEmptyObject(commandResponse)) { responsePayload = { hashId: this.chargingStation.stationInfo.hashId, - status: ResponseStatus.SUCCESS, - }; + status: ResponseStatus.SUCCESS + } } else { responsePayload = this.commandResponseToResponsePayload( command, requestPayload, - commandResponse as CommandResponse, - ); + commandResponse + ) } } catch (error) { logger.error( `${this.chargingStation.logPrefix()} ${moduleName}.requestHandler: Handle request error:`, - error, - ); + error + ) responsePayload = { hashId: this.chargingStation.stationInfo.hashId, status: ResponseStatus.FAILURE, command, requestPayload, - commandResponse: commandResponse as CommandResponse, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + commandResponse: commandResponse!, errorMessage: (error as OCPPError).message, errorStack: (error as OCPPError).stack, - errorDetails: (error as OCPPError).details, - }; + errorDetails: (error as OCPPError).details + } } finally { - this.sendResponse([uuid, responsePayload!]); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.sendResponse([uuid, responsePayload!]) } } - private messageErrorHandler(messageEvent: MessageEvent): void { + private messageErrorHandler (messageEvent: MessageEvent): void { logger.error( `${this.chargingStation.logPrefix()} ${moduleName}.messageErrorHandler: Error at handling message:`, - messageEvent, - ); + messageEvent + ) } - private async commandHandler( + private async commandHandler ( command: BroadcastChannelProcedureName, - requestPayload: BroadcastChannelRequestPayload, - ): Promise { - if (this.commandHandlers.has(command) === true) { - this.cleanRequestPayload(command, requestPayload); - return this.commandHandlers.get(command)!(requestPayload); + requestPayload: BroadcastChannelRequestPayload + // eslint-disable-next-line @typescript-eslint/no-invalid-void-type + ): Promise { + if (this.commandHandlers.has(command)) { + this.cleanRequestPayload(command, requestPayload) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return await this.commandHandlers.get(command)!(requestPayload) } - throw new BaseError(`Unknown worker broadcast channel command: '${command}'`); + throw new BaseError(`Unknown worker broadcast channel command: '${command}'`) } - private cleanRequestPayload( + private cleanRequestPayload ( command: BroadcastChannelProcedureName, - requestPayload: BroadcastChannelRequestPayload, + requestPayload: BroadcastChannelRequestPayload ): void { - delete requestPayload.hashId; - delete requestPayload.hashIds; - [ + delete requestPayload.hashId + delete requestPayload.hashIds + ![ BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR, - BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR, - ].includes(command) === false && delete requestPayload.connectorIds; + BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR + ].includes(command) && delete requestPayload.connectorIds } - private commandResponseToResponsePayload( + private commandResponseToResponsePayload ( command: BroadcastChannelProcedureName, requestPayload: BroadcastChannelRequestPayload, - commandResponse: CommandResponse, + commandResponse: CommandResponse ): BroadcastChannelResponsePayload { - const responseStatus = this.commandResponseToResponseStatus(command, commandResponse); + const responseStatus = this.commandResponseToResponseStatus(command, commandResponse) if (responseStatus === ResponseStatus.SUCCESS) { return { hashId: this.chargingStation.stationInfo.hashId, - status: responseStatus, - }; + status: responseStatus + } } return { hashId: this.chargingStation.stationInfo.hashId, status: responseStatus, command, requestPayload, - commandResponse, - }; + commandResponse + } } - private commandResponseToResponseStatus( + private commandResponseToResponseStatus ( command: BroadcastChannelProcedureName, - commandResponse: CommandResponse, + commandResponse: CommandResponse ): ResponseStatus { switch (command) { case BroadcastChannelProcedureName.START_TRANSACTION: @@ -374,32 +391,32 @@ export class ChargingStationWorkerBroadcastChannel extends WorkerBroadcastChanne | AuthorizeResponse )?.idTagInfo?.status === AuthorizationStatus.ACCEPTED ) { - return ResponseStatus.SUCCESS; + return ResponseStatus.SUCCESS } - return ResponseStatus.FAILURE; + return ResponseStatus.FAILURE case BroadcastChannelProcedureName.BOOT_NOTIFICATION: if (commandResponse?.status === RegistrationStatusEnumType.ACCEPTED) { - return ResponseStatus.SUCCESS; + return ResponseStatus.SUCCESS } - return ResponseStatus.FAILURE; + return ResponseStatus.FAILURE case BroadcastChannelProcedureName.DATA_TRANSFER: if (commandResponse?.status === DataTransferStatus.ACCEPTED) { - return ResponseStatus.SUCCESS; + return ResponseStatus.SUCCESS } - return ResponseStatus.FAILURE; + return ResponseStatus.FAILURE case BroadcastChannelProcedureName.STATUS_NOTIFICATION: case BroadcastChannelProcedureName.METER_VALUES: - if (isEmptyObject(commandResponse) === true) { - return ResponseStatus.SUCCESS; + if (isEmptyObject(commandResponse)) { + return ResponseStatus.SUCCESS } - return ResponseStatus.FAILURE; + return ResponseStatus.FAILURE case BroadcastChannelProcedureName.HEARTBEAT: if ('currentTime' in commandResponse) { - return ResponseStatus.SUCCESS; + return ResponseStatus.SUCCESS } - return ResponseStatus.FAILURE; + return ResponseStatus.FAILURE default: - return ResponseStatus.FAILURE; + return ResponseStatus.FAILURE } } } diff --git a/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts index 62588e0a..57b4bc78 100644 --- a/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/UIServiceWorkerBroadcastChannel.ts @@ -1,79 +1,82 @@ -import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js'; +import { WorkerBroadcastChannel } from './WorkerBroadcastChannel.js' import { type BroadcastChannelResponse, type BroadcastChannelResponsePayload, type MessageEvent, type ResponsePayload, - ResponseStatus, -} from '../../types/index.js'; -import { isNullOrUndefined, logger } from '../../utils/index.js'; -import type { AbstractUIService } from '../ui-server/ui-services/AbstractUIService.js'; + ResponseStatus +} from '../../types/index.js' +import { isNullOrUndefined, logger } from '../../utils/index.js' +import type { AbstractUIService } from '../ui-server/ui-services/AbstractUIService.js' -const moduleName = 'UIServiceWorkerBroadcastChannel'; +const moduleName = 'UIServiceWorkerBroadcastChannel' interface Responses { - responsesExpected: number; - responsesReceived: number; - responses: BroadcastChannelResponsePayload[]; + responsesExpected: number + responsesReceived: number + responses: BroadcastChannelResponsePayload[] } export class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChannel { - private readonly uiService: AbstractUIService; - private readonly responses: Map; + private readonly uiService: AbstractUIService + private readonly responses: Map - constructor(uiService: AbstractUIService) { - super(); - this.uiService = uiService; - this.onmessage = this.responseHandler.bind(this) as (message: unknown) => void; - this.onmessageerror = this.messageErrorHandler.bind(this) as (message: unknown) => void; - this.responses = new Map(); + constructor (uiService: AbstractUIService) { + super() + this.uiService = uiService + this.onmessage = this.responseHandler.bind(this) as (message: unknown) => void + this.onmessageerror = this.messageErrorHandler.bind(this) as (message: unknown) => void + this.responses = new Map() } - private responseHandler(messageEvent: MessageEvent): void { - const validatedMessageEvent = this.validateMessageEvent(messageEvent); + private responseHandler (messageEvent: MessageEvent): void { + const validatedMessageEvent = this.validateMessageEvent(messageEvent) if (validatedMessageEvent === false) { - return; + return } - if (this.isRequest(validatedMessageEvent.data) === true) { - return; + if (this.isRequest(validatedMessageEvent.data)) { + return } - const [uuid, responsePayload] = validatedMessageEvent.data as BroadcastChannelResponse; - if (this.responses.has(uuid) === false) { + const [uuid, responsePayload] = validatedMessageEvent.data as BroadcastChannelResponse + if (!this.responses.has(uuid)) { this.responses.set(uuid, { responsesExpected: this.uiService.getBroadcastChannelExpectedResponses(uuid), responsesReceived: 1, - responses: [responsePayload], - }); + responses: [responsePayload] + }) } else if ( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.responses.get(uuid)!.responsesReceived <= this.responses.get(uuid)!.responsesExpected ) { - ++this.responses.get(uuid)!.responsesReceived; - this.responses.get(uuid)?.responses.push(responsePayload); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.responses.get(uuid)!.responsesReceived + this.responses.get(uuid)?.responses.push(responsePayload) } if ( this.responses.get(uuid)?.responsesReceived === this.responses.get(uuid)?.responsesExpected ) { - this.uiService.sendResponse(uuid, this.buildResponsePayload(uuid)); - this.responses.delete(uuid); - this.uiService.deleteBroadcastChannelRequest(uuid); + this.uiService.sendResponse(uuid, this.buildResponsePayload(uuid)) + this.responses.delete(uuid) + this.uiService.deleteBroadcastChannelRequest(uuid) } } - private buildResponsePayload(uuid: string): ResponsePayload { + private buildResponsePayload (uuid: string): ResponsePayload { const responsesStatus = this.responses .get(uuid) ?.responses.every(({ status }) => status === ResponseStatus.SUCCESS) === true ? ResponseStatus.SUCCESS - : ResponseStatus.FAILURE; + : ResponseStatus.FAILURE return { status: responsesStatus, hashIdsSucceeded: this.responses .get(uuid) ?.responses.map(({ status, hashId }) => { if (hashId !== undefined && status === ResponseStatus.SUCCESS) { - return hashId; + return hashId } + return undefined }) .filter((hashId) => !isNullOrUndefined(hashId)) as string[], ...(responsesStatus === ResponseStatus.FAILURE && { @@ -81,28 +84,30 @@ export class UIServiceWorkerBroadcastChannel extends WorkerBroadcastChannel { .get(uuid) ?.responses.map(({ status, hashId }) => { if (hashId !== undefined && status === ResponseStatus.FAILURE) { - return hashId; + return hashId } + return undefined }) - .filter((hashId) => !isNullOrUndefined(hashId)) as string[], + .filter((hashId) => !isNullOrUndefined(hashId)) as string[] }), ...(responsesStatus === ResponseStatus.FAILURE && { responsesFailed: this.responses .get(uuid) ?.responses.map((response) => { if (response !== undefined && response.status === ResponseStatus.FAILURE) { - return response; + return response } + return undefined }) - .filter((response) => !isNullOrUndefined(response)) as BroadcastChannelResponsePayload[], - }), - }; + .filter((response) => !isNullOrUndefined(response)) as BroadcastChannelResponsePayload[] + }) + } } - private messageErrorHandler(messageEvent: MessageEvent): void { + private messageErrorHandler (messageEvent: MessageEvent): void { logger.error( `${this.uiService.logPrefix(moduleName, 'messageErrorHandler')} Error at handling message:`, - messageEvent, - ); + messageEvent + ) } } diff --git a/src/charging-station/broadcast-channel/WorkerBroadcastChannel.ts b/src/charging-station/broadcast-channel/WorkerBroadcastChannel.ts index dbd83c5b..dfd0c3e5 100644 --- a/src/charging-station/broadcast-channel/WorkerBroadcastChannel.ts +++ b/src/charging-station/broadcast-channel/WorkerBroadcastChannel.ts @@ -1,59 +1,59 @@ -import { BroadcastChannel } from 'node:worker_threads'; +import { BroadcastChannel } from 'node:worker_threads' import type { BroadcastChannelRequest, BroadcastChannelResponse, JsonType, - MessageEvent, -} from '../../types/index.js'; -import { logPrefix, logger, validateUUID } from '../../utils/index.js'; + MessageEvent +} from '../../types/index.js' +import { logPrefix, logger, validateUUID } from '../../utils/index.js' -const moduleName = 'WorkerBroadcastChannel'; +const moduleName = 'WorkerBroadcastChannel' export abstract class WorkerBroadcastChannel extends BroadcastChannel { - protected constructor() { - super('worker'); + protected constructor () { + super('worker') } - public sendRequest(request: BroadcastChannelRequest): void { - this.postMessage(request); + public sendRequest (request: BroadcastChannelRequest): void { + this.postMessage(request) } - protected sendResponse(response: BroadcastChannelResponse): void { - this.postMessage(response); + protected sendResponse (response: BroadcastChannelResponse): void { + this.postMessage(response) } - protected isRequest(message: JsonType[]): boolean { - return Array.isArray(message) === true && message.length === 3; + protected isRequest (message: JsonType[]): boolean { + return Array.isArray(message) && message.length === 3 } - protected isResponse(message: JsonType[]): boolean { - return Array.isArray(message) === true && message.length === 2; + protected isResponse (message: JsonType[]): boolean { + return Array.isArray(message) && message.length === 2 } - protected validateMessageEvent(messageEvent: MessageEvent): MessageEvent | false { - if (Array.isArray(messageEvent.data) === false) { + protected validateMessageEvent (messageEvent: MessageEvent): MessageEvent | false { + if (!Array.isArray(messageEvent.data)) { logger.error( `${this.logPrefix( moduleName, - 'validateMessageEvent', - )} Worker broadcast channel protocol message event data is not an array`, - ); - return false; + 'validateMessageEvent' + )} Worker broadcast channel protocol message event data is not an array` + ) + return false } - if (validateUUID(messageEvent.data[0]) === false) { + if (!validateUUID(messageEvent.data[0])) { logger.error( `${this.logPrefix( moduleName, - 'validateMessageEvent', - )} Worker broadcast channel protocol message event data UUID field is invalid`, - ); - return false; + 'validateMessageEvent' + )} Worker broadcast channel protocol message event data UUID field is invalid` + ) + return false } - return messageEvent; + return messageEvent } - private logPrefix = (modName: string, methodName: string): string => { - return logPrefix(` Worker Broadcast Channel | ${modName}.${methodName}:`); - }; + private readonly logPrefix = (modName: string, methodName: string): string => { + return logPrefix(` Worker Broadcast Channel | ${modName}.${methodName}:`) + } } diff --git a/src/charging-station/index.ts b/src/charging-station/index.ts index d03a5f77..8d8a83c3 100644 --- a/src/charging-station/index.ts +++ b/src/charging-station/index.ts @@ -1,10 +1,10 @@ -export { Bootstrap } from './Bootstrap.js'; -export type { ChargingStation } from './ChargingStation.js'; +export { Bootstrap } from './Bootstrap.js' +export type { ChargingStation } from './ChargingStation.js' export { addConfigurationKey, getConfigurationKey, - setConfigurationKeyValue, -} from './ConfigurationKeyUtils.js'; + setConfigurationKeyValue +} from './ConfigurationKeyUtils.js' export { canProceedChargingProfile, checkChargingStation, @@ -14,5 +14,5 @@ export { hasReservationExpired, prepareChargingProfileKind, removeExpiredReservations, - resetConnectorStatus, -} from './Helpers.js'; + resetConnectorStatus +} from './Helpers.js' diff --git a/src/charging-station/ocpp/1.6/OCPP16Constants.ts b/src/charging-station/ocpp/1.6/OCPP16Constants.ts index 3d1ab3c8..b4a98ed0 100644 --- a/src/charging-station/ocpp/1.6/OCPP16Constants.ts +++ b/src/charging-station/ocpp/1.6/OCPP16Constants.ts @@ -1,23 +1,23 @@ -import { type ConnectorStatusTransition, OCPP16ChargePointStatus } from '../../../types/index.js'; -import { OCPPConstants } from '../OCPPConstants.js'; +import { type ConnectorStatusTransition, OCPP16ChargePointStatus } from '../../../types/index.js' +import { OCPPConstants } from '../OCPPConstants.js' export class OCPP16Constants extends OCPPConstants { static readonly ChargePointStatusChargingStationTransitions: Readonly< - ConnectorStatusTransition[] + ConnectorStatusTransition[] > = Object.freeze([ - { to: OCPP16ChargePointStatus.Available }, - // { from: OCPP16ChargePointStatus.Available, to: OCPP16ChargePointStatus.Available }, - { from: OCPP16ChargePointStatus.Available, to: OCPP16ChargePointStatus.Unavailable }, - { from: OCPP16ChargePointStatus.Available, to: OCPP16ChargePointStatus.Faulted }, - { to: OCPP16ChargePointStatus.Unavailable }, - { from: OCPP16ChargePointStatus.Unavailable, to: OCPP16ChargePointStatus.Available }, - // { from: OCPP16ChargePointStatus.Unavailable, to: OCPP16ChargePointStatus.Unavailable }, - { from: OCPP16ChargePointStatus.Unavailable, to: OCPP16ChargePointStatus.Faulted }, - { to: OCPP16ChargePointStatus.Faulted }, - { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Available }, - { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Unavailable }, + { to: OCPP16ChargePointStatus.Available }, + // { from: OCPP16ChargePointStatus.Available, to: OCPP16ChargePointStatus.Available }, + { from: OCPP16ChargePointStatus.Available, to: OCPP16ChargePointStatus.Unavailable }, + { from: OCPP16ChargePointStatus.Available, to: OCPP16ChargePointStatus.Faulted }, + { to: OCPP16ChargePointStatus.Unavailable }, + { from: OCPP16ChargePointStatus.Unavailable, to: OCPP16ChargePointStatus.Available }, + // { from: OCPP16ChargePointStatus.Unavailable, to: OCPP16ChargePointStatus.Unavailable }, + { from: OCPP16ChargePointStatus.Unavailable, to: OCPP16ChargePointStatus.Faulted }, + { to: OCPP16ChargePointStatus.Faulted }, + { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Available }, + { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Unavailable } // { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Faulted }, - ]); + ]) static readonly ChargePointStatusConnectorTransitions: Readonly = Object.freeze([ @@ -109,7 +109,7 @@ export class OCPP16Constants extends OCPPConstants { { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.SuspendedEVSE }, { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Finishing }, { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Reserved }, - { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Unavailable }, + { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Unavailable } // { from: OCPP16ChargePointStatus.Faulted, to: OCPP16ChargePointStatus.Faulted }, - ]); + ]) } diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index f1ea435a..656d2cb3 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -1,23 +1,23 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { createWriteStream, readdirSync } from 'node:fs'; -import { dirname, join, resolve } from 'node:path'; -import { URL, fileURLToPath } from 'node:url'; +import { createWriteStream, readdirSync } from 'node:fs' +import { dirname, join, resolve } from 'node:path' +import { URL, fileURLToPath } from 'node:url' -import type { JSONSchemaType } from 'ajv'; -import { Client, type FTPResponse } from 'basic-ftp'; +import type { JSONSchemaType } from 'ajv' +import { Client, type FTPResponse } from 'basic-ftp' import { type Interval, addSeconds, differenceInSeconds, isDate, - secondsToMilliseconds, -} from 'date-fns'; -import { maxTime } from 'date-fns/constants'; -import { create } from 'tar'; + secondsToMilliseconds +} from 'date-fns' +import { maxTime } from 'date-fns/constants' +import { create } from 'tar' -import { OCPP16Constants } from './OCPP16Constants.js'; -import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'; +import { OCPP16Constants } from './OCPP16Constants.js' +import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' import { type ChargingStation, canProceedChargingProfile, @@ -26,9 +26,9 @@ import { getConnectorChargingProfiles, prepareChargingProfileKind, removeExpiredReservations, - setConfigurationKeyValue, -} from '../../../charging-station/index.js'; -import { OCPPError } from '../../../exception/index.js'; + setConfigurationKeyValue +} from '../../../charging-station/index.js' +import { OCPPError } from '../../../exception/index.js' import { type ChangeConfigurationRequest, type ChangeConfigurationResponse, @@ -94,8 +94,8 @@ import { type SetChargingProfileRequest, type SetChargingProfileResponse, type UnlockConnectorRequest, - type UnlockConnectorResponse, -} from '../../../types/index.js'; + type UnlockConnectorResponse +} from '../../../types/index.js' import { Constants, convertToDate, @@ -108,246 +108,249 @@ import { isNullOrUndefined, isUndefined, logger, - sleep, -} from '../../../utils/index.js'; -import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'; + sleep +} from '../../../utils/index.js' +import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js' -const moduleName = 'OCPP16IncomingRequestService'; +const moduleName = 'OCPP16IncomingRequestService' export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { - protected jsonSchemas: Map>; - private incomingRequestHandlers: Map; + protected jsonSchemas: Map> + private readonly incomingRequestHandlers: Map< + OCPP16IncomingRequestCommand, + IncomingRequestHandler + > - public constructor() { + public constructor () { // if (new.target?.name === moduleName) { - // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); + // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`) // } - super(OCPPVersion.VERSION_16); + super(OCPPVersion.VERSION_16) this.incomingRequestHandlers = new Map([ [ OCPP16IncomingRequestCommand.RESET, - this.handleRequestReset.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestReset.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CLEAR_CACHE, - this.handleRequestClearCache.bind(this) as IncomingRequestHandler, + this.handleRequestClearCache.bind(this) as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, - this.handleRequestUnlockConnector.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestUnlockConnector.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.GET_CONFIGURATION, - this.handleRequestGetConfiguration.bind(this) as IncomingRequestHandler, + this.handleRequestGetConfiguration.bind(this) as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, - this.handleRequestChangeConfiguration.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestChangeConfiguration.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - this.handleRequestGetCompositeSchedule.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestGetCompositeSchedule.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - this.handleRequestSetChargingProfile.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestSetChargingProfile.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - this.handleRequestClearChargingProfile.bind(this) as IncomingRequestHandler, + this.handleRequestClearChargingProfile.bind(this) as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, - this.handleRequestChangeAvailability.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestChangeAvailability.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, - this.handleRequestRemoteStartTransaction.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestRemoteStartTransaction.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, - this.handleRequestRemoteStopTransaction.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestRemoteStopTransaction.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - this.handleRequestGetDiagnostics.bind(this) as IncomingRequestHandler, + this.handleRequestGetDiagnostics.bind(this) as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - this.handleRequestTriggerMessage.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestTriggerMessage.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.DATA_TRANSFER, - this.handleRequestDataTransfer.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestDataTransfer.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - this.handleRequestUpdateFirmware.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestUpdateFirmware.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.RESERVE_NOW, - this.handleRequestReserveNow.bind(this) as unknown as IncomingRequestHandler, + this.handleRequestReserveNow.bind(this) as unknown as IncomingRequestHandler ], [ OCPP16IncomingRequestCommand.CANCEL_RESERVATION, - this.handleRequestCancelReservation.bind(this) as unknown as IncomingRequestHandler, - ], - ]); + this.handleRequestCancelReservation.bind(this) as unknown as IncomingRequestHandler + ] + ]) this.jsonSchemas = new Map>([ [ OCPP16IncomingRequestCommand.RESET, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/Reset.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CLEAR_CACHE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ClearCache.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/UnlockConnector.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_CONFIGURATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetConfiguration.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ChangeConfiguration.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetDiagnostics.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetCompositeSchedule.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/SetChargingProfile.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ClearChargingProfile.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ChangeAvailability.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/RemoteStartTransaction.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/RemoteStopTransaction.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/TriggerMessage.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.DATA_TRANSFER, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/DataTransfer.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/UpdateFirmware.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.RESERVE_NOW, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ReserveNow.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CANCEL_RESERVATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/CancelReservation.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.validatePayload = this.validatePayload.bind(this) as ( chargingStation: ChargingStation, commandName: OCPP16IncomingRequestCommand, - commandPayload: JsonType, - ) => boolean; + commandPayload: JsonType + ) => boolean } public async incomingRequestHandler( chargingStation: ChargingStation, messageId: string, commandName: OCPP16IncomingRequestCommand, - commandPayload: ReqType, + commandPayload: ReqType ): Promise { - let response: ResType; + let response: ResType if ( chargingStation.stationInfo?.ocppStrictCompliance === true && - chargingStation.inPendingState() === true && + chargingStation.inPendingState() && (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION || commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION) ) { @@ -356,35 +359,36 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { `${commandName} cannot be issued to handle request PDU ${JSON.stringify( commandPayload, undefined, - 2, + 2 )} while the charging station is in pending state on the central server`, commandName, - commandPayload, - ); + commandPayload + ) } if ( - chargingStation.isRegistered() === true || + chargingStation.isRegistered() || (chargingStation.stationInfo?.ocppStrictCompliance === false && - chargingStation.inUnknownState() === true) + chargingStation.inUnknownState()) ) { if ( - this.incomingRequestHandlers.has(commandName) === true && - OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true + this.incomingRequestHandlers.has(commandName) && + OCPP16ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) ) { try { - this.validatePayload(chargingStation, commandName, commandPayload); + this.validatePayload(chargingStation, commandName, commandPayload) // Call the method to build the response + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion response = (await this.incomingRequestHandlers.get(commandName)!( chargingStation, - commandPayload, - )) as ResType; + commandPayload + )) as ResType } catch (error) { // Log logger.error( `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`, - error, - ); - throw error; + error + ) + throw error } } else { // Throw exception @@ -393,11 +397,11 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { `${commandName} is not implemented to handle request PDU ${JSON.stringify( commandPayload, undefined, - 2, + 2 )}`, commandName, - commandPayload, - ); + commandPayload + ) } } else { throw new OCPPError( @@ -405,152 +409,156 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { `${commandName} cannot be issued to handle request PDU ${JSON.stringify( commandPayload, undefined, - 2, + 2 )} while the charging station is not registered on the central server.`, commandName, - commandPayload, - ); + commandPayload + ) } // Send the built response await chargingStation.ocppRequestService.sendResponse( chargingStation, messageId, response, - commandName, - ); + commandName + ) } - private validatePayload( + private validatePayload ( chargingStation: ChargingStation, commandName: OCPP16IncomingRequestCommand, - commandPayload: JsonType, + commandPayload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName) === true) { + if (this.jsonSchemas.has(commandName)) { return this.validateIncomingRequestPayload( chargingStation, commandName, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.jsonSchemas.get(commandName)!, - commandPayload, - ); + commandPayload + ) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`, - ); - return false; + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + ) + return false } // Simulate charging station restart - private handleRequestReset( + private handleRequestReset ( chargingStation: ChargingStation, - commandPayload: ResetRequest, + commandPayload: ResetRequest ): GenericResponse { - const { type } = commandPayload; + const { type } = commandPayload chargingStation .reset(`${type}Reset` as OCPP16StopTransactionReason) - .catch(Constants.EMPTY_FUNCTION); + .catch(Constants.EMPTY_FUNCTION) logger.info( `${chargingStation.logPrefix()} ${type} reset command received, simulating it. The station will be back online in ${formatDurationMilliSeconds( - chargingStation.stationInfo.resetTime!, - )}`, - ); - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo.resetTime! + )}` + ) + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED } - private async handleRequestUnlockConnector( + private async handleRequestUnlockConnector ( chargingStation: ChargingStation, - commandPayload: UnlockConnectorRequest, + commandPayload: UnlockConnectorRequest ): Promise { - const { connectorId } = commandPayload; - if (chargingStation.hasConnector(connectorId) === false) { + const { connectorId } = commandPayload + if (!chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED; + `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED } if (connectorId === 0) { - logger.error(`${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId}`); - return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED; + logger.error(`${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId}`) + return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED } if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) { const stopResponse = await chargingStation.stopTransactionOnConnector( connectorId, - OCPP16StopTransactionReason.UNLOCK_COMMAND, - ); + OCPP16StopTransactionReason.UNLOCK_COMMAND + ) if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { - return OCPP16Constants.OCPP_RESPONSE_UNLOCKED; + return OCPP16Constants.OCPP_RESPONSE_UNLOCKED } - return OCPP16Constants.OCPP_RESPONSE_UNLOCK_FAILED; + return OCPP16Constants.OCPP_RESPONSE_UNLOCK_FAILED } await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Available, - ); - return OCPP16Constants.OCPP_RESPONSE_UNLOCKED; + OCPP16ChargePointStatus.Available + ) + return OCPP16Constants.OCPP_RESPONSE_UNLOCKED } - private handleRequestGetConfiguration( + private handleRequestGetConfiguration ( chargingStation: ChargingStation, - commandPayload: GetConfigurationRequest, + commandPayload: GetConfigurationRequest ): GetConfigurationResponse { - const { key } = commandPayload; - const configurationKey: OCPPConfigurationKey[] = []; - const unknownKey: string[] = []; - if (isUndefined(key) === true) { + const { key } = commandPayload + const configurationKey: OCPPConfigurationKey[] = [] + const unknownKey: string[] = [] + if (isUndefined(key)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion for (const configuration of chargingStation.ocppConfiguration!.configurationKey!) { - if (isUndefined(configuration.visible) === true) { - configuration.visible = true; + if (isUndefined(configuration.visible)) { + configuration.visible = true } if (configuration.visible === false) { - continue; + continue } configurationKey.push({ key: configuration.key, readonly: configuration.readonly, - value: configuration.value, - }); + value: configuration.value + }) } - } else if (isNotEmptyArray(key) === true) { + } else if (isNotEmptyArray(key)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion for (const k of key!) { - const keyFound = getConfigurationKey(chargingStation, k, true); + const keyFound = getConfigurationKey(chargingStation, k, true) if (keyFound !== undefined) { - if (isUndefined(keyFound.visible) === true) { - keyFound.visible = true; + if (isUndefined(keyFound.visible)) { + keyFound.visible = true } if (keyFound.visible === false) { - continue; + continue } configurationKey.push({ key: keyFound.key, readonly: keyFound.readonly, - value: keyFound.value, - }); + value: keyFound.value + }) } else { - unknownKey.push(k); + unknownKey.push(k) } } } return { configurationKey, - unknownKey, - }; + unknownKey + } } - private handleRequestChangeConfiguration( + private handleRequestChangeConfiguration ( chargingStation: ChargingStation, - commandPayload: ChangeConfigurationRequest, + commandPayload: ChangeConfigurationRequest ): ChangeConfigurationResponse { - const { key, value } = commandPayload; - const keyToChange = getConfigurationKey(chargingStation, key, true); + const { key, value } = commandPayload + const keyToChange = getConfigurationKey(chargingStation, key, true) if (keyToChange?.readonly === true) { - return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REJECTED } else if (keyToChange?.readonly === false) { - let valueChanged = false; + let valueChanged = false if (keyToChange.value !== value) { - setConfigurationKeyValue(chargingStation, key, value, true); - valueChanged = true; + setConfigurationKeyValue(chargingStation, key, value, true) + valueChanged = true } - let triggerHeartbeatRestart = false; + let triggerHeartbeatRestart = false if ( (keyToChange.key as OCPP16StandardParametersKey) === OCPP16StandardParametersKey.HeartBeatInterval && @@ -559,9 +567,9 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { setConfigurationKeyValue( chargingStation, OCPP16StandardParametersKey.HeartbeatInterval, - value, - ); - triggerHeartbeatRestart = true; + value + ) + triggerHeartbeatRestart = true } if ( (keyToChange.key as OCPP16StandardParametersKey) === @@ -571,74 +579,74 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { setConfigurationKeyValue( chargingStation, OCPP16StandardParametersKey.HeartBeatInterval, - value, - ); - triggerHeartbeatRestart = true; + value + ) + triggerHeartbeatRestart = true } if (triggerHeartbeatRestart) { - chargingStation.restartHeartbeat(); + chargingStation.restartHeartbeat() } if ( (keyToChange.key as OCPP16StandardParametersKey) === OCPP16StandardParametersKey.WebSocketPingInterval && valueChanged ) { - chargingStation.restartWebSocketPing(); + chargingStation.restartWebSocketPing() } - if (keyToChange.reboot) { - return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED; + if (keyToChange.reboot === true) { + return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED } - return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED; + return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_ACCEPTED } - return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED; + return OCPP16Constants.OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED } - private handleRequestSetChargingProfile( + private handleRequestSetChargingProfile ( chargingStation: ChargingStation, - commandPayload: SetChargingProfileRequest, + commandPayload: SetChargingProfileRequest ): SetChargingProfileResponse { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.SmartCharging, - OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, - ) === false + OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE + ) ) { - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED; + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED } - const { connectorId, csChargingProfiles } = commandPayload; - if (chargingStation.hasConnector(connectorId) === false) { + const { connectorId, csChargingProfiles } = commandPayload + if (!chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED } if ( csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && connectorId !== 0 ) { - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED } if ( csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE && connectorId === 0 ) { logger.error( - `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId}`, - ); - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId}` + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED } - const connectorStatus = chargingStation.getConnectorStatus(connectorId); + const connectorStatus = chargingStation.getConnectorStatus(connectorId) if ( csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE && connectorId > 0 && connectorStatus?.transactionStarted === false ) { logger.error( - `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} without a started transaction`, - ); - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} without a started transaction` + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED } if ( csChargingProfiles.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE && @@ -649,82 +657,83 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { logger.error( `${chargingStation.logPrefix()} Trying to set transaction charging profile(s) on connector ${connectorId} with a different transaction id ${ csChargingProfiles.transactionId - } than the started transaction id ${connectorStatus?.transactionId}`, - ); - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; + } than the started transaction id ${connectorStatus?.transactionId}` + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED } - OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, csChargingProfiles); + OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, csChargingProfiles) logger.debug( `${chargingStation.logPrefix()} Charging profile(s) set on connector id ${connectorId}: %j`, - csChargingProfiles, - ); - return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED; + csChargingProfiles + ) + return OCPP16Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED } - private handleRequestGetCompositeSchedule( + private handleRequestGetCompositeSchedule ( chargingStation: ChargingStation, - commandPayload: OCPP16GetCompositeScheduleRequest, + commandPayload: OCPP16GetCompositeScheduleRequest ): OCPP16GetCompositeScheduleResponse { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.SmartCharging, - OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, - ) === false + OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE + ) ) { - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - const { connectorId, duration, chargingRateUnit } = commandPayload; - if (chargingStation.hasConnector(connectorId) === false) { + const { connectorId, duration, chargingRateUnit } = commandPayload + if (!chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_RESPONSE_REJECTED } if (connectorId === 0) { logger.error( - `${chargingStation.logPrefix()} Get composite schedule on connector id ${connectorId} is not yet supported`, - ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Get composite schedule on connector id ${connectorId} is not yet supported` + ) + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - if (chargingRateUnit) { + if (chargingRateUnit != null) { logger.warn( - `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done`, - ); + `${chargingStation.logPrefix()} Get composite schedule with a specified rate unit is not yet supported, no conversion will be done` + ) } - const connectorStatus = chargingStation.getConnectorStatus(connectorId)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorStatus = chargingStation.getConnectorStatus(connectorId)! if ( isEmptyArray( - connectorStatus?.chargingProfiles && - isEmptyArray(chargingStation.getConnectorStatus(0)?.chargingProfiles), + connectorStatus?.chargingProfiles != null && + isEmptyArray(chargingStation.getConnectorStatus(0)?.chargingProfiles) ) ) { - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - const currentDate = new Date(); + const currentDate = new Date() const compositeScheduleInterval: Interval = { start: currentDate, - end: addSeconds(currentDate, duration), - }; + end: addSeconds(currentDate, duration) + } // Get charging profiles sorted by connector id then stack level const chargingProfiles: OCPP16ChargingProfile[] = getConnectorChargingProfiles( chargingStation, - connectorId, - ); - let previousCompositeSchedule: OCPP16ChargingSchedule | undefined; - let compositeSchedule: OCPP16ChargingSchedule | undefined; + connectorId + ) + let previousCompositeSchedule: OCPP16ChargingSchedule | undefined + let compositeSchedule: OCPP16ChargingSchedule | undefined for (const chargingProfile of chargingProfiles) { if ( isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && - connectorStatus?.transactionStarted + connectorStatus?.transactionStarted === true ) { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ chargingProfile.chargingProfileId - } has no startSchedule defined. Trying to set it to the connector current transaction start date`, - ); + } has no startSchedule defined. Trying to set it to the connector current transaction start date` + ) // OCPP specifies that if startSchedule is not defined, it should be relative to start of the connector transaction - chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart; + chargingProfile.chargingSchedule.startSchedule = connectorStatus?.transactionStart } if ( !isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && @@ -733,11 +742,12 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { logger.warn( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ chargingProfile.chargingProfileId - } startSchedule property is not a Date instance. Trying to convert it to a Date instance`, - ); + } startSchedule property is not a Date instance. Trying to convert it to a Date instance` + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingProfile.chargingSchedule.startSchedule = convertToDate( - chargingProfile.chargingSchedule?.startSchedule, - )!; + chargingProfile.chargingSchedule?.startSchedule + )! } if ( !isNullOrUndefined(chargingProfile.chargingSchedule?.startSchedule) && @@ -746,89 +756,94 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { logger.debug( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetCompositeSchedule: Charging profile id ${ chargingProfile.chargingProfileId - } has no duration defined and will be set to the maximum time allowed`, - ); + } has no duration defined and will be set to the maximum time allowed` + ) // OCPP specifies that if duration is not defined, it should be infinite chargingProfile.chargingSchedule.duration = differenceInSeconds( maxTime, - chargingProfile.chargingSchedule.startSchedule!, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingProfile.chargingSchedule.startSchedule! + ) } if ( !prepareChargingProfileKind( connectorStatus, chargingProfile, compositeScheduleInterval.start as Date, - chargingStation.logPrefix(), + chargingStation.logPrefix() ) ) { - continue; + continue } if ( !canProceedChargingProfile( chargingProfile, compositeScheduleInterval.start as Date, - chargingStation.logPrefix(), + chargingStation.logPrefix() ) ) { - continue; + continue } compositeSchedule = OCPP16ServiceUtils.composeChargingSchedules( previousCompositeSchedule, chargingProfile.chargingSchedule, - compositeScheduleInterval, - ); - previousCompositeSchedule = compositeSchedule; + compositeScheduleInterval + ) + previousCompositeSchedule = compositeSchedule } - if (compositeSchedule) { + if (compositeSchedule != null) { return { status: GenericStatus.Accepted, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion scheduleStart: compositeSchedule.startSchedule!, connectorId, - chargingSchedule: compositeSchedule, - }; + chargingSchedule: compositeSchedule + } } - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - private handleRequestClearChargingProfile( + private handleRequestClearChargingProfile ( chargingStation: ChargingStation, - commandPayload: OCPP16ClearChargingProfileRequest, + commandPayload: OCPP16ClearChargingProfileRequest ): OCPP16ClearChargingProfileResponse { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.SmartCharging, - OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, - ) === false + OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE + ) ) { - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN } - const { connectorId } = commandPayload; - if (chargingStation.hasConnector(connectorId!) === false) { + const { connectorId } = commandPayload + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (!chargingStation.hasConnector(connectorId!)) { logger.error( - `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; + `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN } - const connectorStatus = chargingStation.getConnectorStatus(connectorId!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorStatus = chargingStation.getConnectorStatus(connectorId!) if (!isNullOrUndefined(connectorId) && isNotEmptyArray(connectorStatus?.chargingProfiles)) { - connectorStatus!.chargingProfiles = []; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connectorStatus!.chargingProfiles = [] logger.debug( - `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED; + `${chargingStation.logPrefix()} Charging profile(s) cleared on connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED } if (isNullOrUndefined(connectorId)) { - let clearedCP = false; + let clearedCP = false if (chargingStation.hasEvses) { for (const evseStatus of chargingStation.evses.values()) { for (const status of evseStatus.connectors.values()) { clearedCP = OCPP16ServiceUtils.clearChargingProfiles( chargingStation, commandPayload, - status.chargingProfiles, - ); + status.chargingProfiles + ) } } } else { @@ -836,238 +851,240 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { clearedCP = OCPP16ServiceUtils.clearChargingProfiles( chargingStation, commandPayload, - chargingStation.getConnectorStatus(id)?.chargingProfiles, - ); + chargingStation.getConnectorStatus(id)?.chargingProfiles + ) } } if (clearedCP) { - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED; + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED } } - return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; + return OCPP16Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN } - private async handleRequestChangeAvailability( + private async handleRequestChangeAvailability ( chargingStation: ChargingStation, - commandPayload: OCPP16ChangeAvailabilityRequest, + commandPayload: OCPP16ChangeAvailabilityRequest ): Promise { - const { connectorId, type } = commandPayload; - if (chargingStation.hasConnector(connectorId) === false) { + const { connectorId, type } = commandPayload + if (!chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId}`, - ); - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId}` + ) + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED } const chargePointStatus: OCPP16ChargePointStatus = type === OCPP16AvailabilityType.Operative ? OCPP16ChargePointStatus.Available - : OCPP16ChargePointStatus.Unavailable; + : OCPP16ChargePointStatus.Unavailable if (connectorId === 0) { - let response: OCPP16ChangeAvailabilityResponse; + let response: OCPP16ChangeAvailabilityResponse if (chargingStation.hasEvses) { for (const evseStatus of chargingStation.evses.values()) { response = await OCPP16ServiceUtils.changeAvailability( chargingStation, [...evseStatus.connectors.keys()], chargePointStatus, - type, - ); + type + ) } } else { response = await OCPP16ServiceUtils.changeAvailability( chargingStation, [...chargingStation.connectors.keys()], chargePointStatus, - type, - ); + type + ) } - return response!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return response! } else if ( connectorId > 0 && - (chargingStation.isChargingStationAvailable() === true || - (chargingStation.isChargingStationAvailable() === false && + (chargingStation.isChargingStationAvailable() || + (!chargingStation.isChargingStationAvailable() && type === OCPP16AvailabilityType.Inoperative)) ) { if (chargingStation.getConnectorStatus(connectorId)?.transactionStarted === true) { - chargingStation.getConnectorStatus(connectorId)!.availability = type; - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.availability = type + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED } - chargingStation.getConnectorStatus(connectorId)!.availability = type; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.availability = type await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - chargePointStatus, - ); - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; + chargePointStatus + ) + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED } - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED } - private async handleRequestRemoteStartTransaction( + private async handleRequestRemoteStartTransaction ( chargingStation: ChargingStation, - commandPayload: RemoteStartTransactionRequest, + commandPayload: RemoteStartTransactionRequest ): Promise { - const { connectorId: transactionConnectorId, idTag, chargingProfile } = commandPayload; - if (chargingStation.hasConnector(transactionConnectorId) === false) { - return this.notifyRemoteStartTransactionRejected( + const { connectorId: transactionConnectorId, idTag, chargingProfile } = commandPayload + if (!chargingStation.hasConnector(transactionConnectorId)) { + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } if ( !chargingStation.isChargingStationAvailable() || !chargingStation.isConnectorAvailable(transactionConnectorId) ) { - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } const remoteStartTransactionLogMsg = ` ${chargingStation.logPrefix()} Transaction remotely STARTED on ${ chargingStation.stationInfo.chargingStationId - }#${transactionConnectorId} for idTag '${idTag}'`; + }#${transactionConnectorId} for idTag '${idTag}'` await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, transactionConnectorId, - OCPP16ChargePointStatus.Preparing, - ); - const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId)!; + OCPP16ChargePointStatus.Preparing + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorStatus = chargingStation.getConnectorStatus(transactionConnectorId)! // Authorization check required if ( - chargingStation.getAuthorizeRemoteTxRequests() === true && + chargingStation.getAuthorizeRemoteTxRequests() && (await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, transactionConnectorId, idTag)) ) { // Authorization successful, start transaction if ( - (chargingProfile && + (chargingProfile != null && this.setRemoteStartTransactionChargingProfile( chargingStation, transactionConnectorId, - chargingProfile, - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - ) === true) || - !chargingProfile + chargingProfile + )) || + chargingProfile == null ) { - connectorStatus.transactionRemoteStarted = true; + connectorStatus.transactionRemoteStarted = true if ( ( await chargingStation.ocppRequestService.requestHandler< - OCPP16StartTransactionRequest, - OCPP16StartTransactionResponse + OCPP16StartTransactionRequest, + OCPP16StartTransactionResponse >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { connectorId: transactionConnectorId, - idTag, + idTag }) ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED ) { - logger.debug(remoteStartTransactionLogMsg); - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; + logger.debug(remoteStartTransactionLogMsg) + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED } - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } // No authorization check required, start transaction if ( - (chargingProfile && + (chargingProfile != null && this.setRemoteStartTransactionChargingProfile( chargingStation, transactionConnectorId, - chargingProfile, - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - ) === true) || - !chargingProfile + chargingProfile + )) || + chargingProfile == null ) { - connectorStatus.transactionRemoteStarted = true; + connectorStatus.transactionRemoteStarted = true if ( ( await chargingStation.ocppRequestService.requestHandler< - OCPP16StartTransactionRequest, - OCPP16StartTransactionResponse + OCPP16StartTransactionRequest, + OCPP16StartTransactionResponse >(chargingStation, OCPP16RequestCommand.START_TRANSACTION, { connectorId: transactionConnectorId, - idTag, + idTag }) ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED ) { - logger.debug(remoteStartTransactionLogMsg); - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; + logger.debug(remoteStartTransactionLogMsg) + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED } - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } - return this.notifyRemoteStartTransactionRejected( + return await this.notifyRemoteStartTransactionRejected( chargingStation, transactionConnectorId, - idTag, - ); + idTag + ) } - private async notifyRemoteStartTransactionRejected( + private async notifyRemoteStartTransactionRejected ( chargingStation: ChargingStation, connectorId: number, - idTag: string, + idTag: string ): Promise { - const connectorStatus = chargingStation.getConnectorStatus(connectorId); + const connectorStatus = chargingStation.getConnectorStatus(connectorId) if (connectorStatus?.status !== OCPP16ChargePointStatus.Available) { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Available, - ); + OCPP16ChargePointStatus.Available + ) } logger.warn( - `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId}, idTag '${idTag}', availability '${connectorStatus?.availability}', status '${connectorStatus?.status}'`, - ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId}, idTag '${idTag}', availability '${connectorStatus?.availability}', status '${connectorStatus?.status}'` + ) + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - private setRemoteStartTransactionChargingProfile( + private setRemoteStartTransactionChargingProfile ( chargingStation: ChargingStation, connectorId: number, - chargingProfile: OCPP16ChargingProfile, + chargingProfile: OCPP16ChargingProfile ): boolean { if (chargingProfile?.chargingProfilePurpose === OCPP16ChargingProfilePurposeType.TX_PROFILE) { - OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, chargingProfile); + OCPP16ServiceUtils.setChargingProfile(chargingStation, connectorId, chargingProfile) logger.debug( `${chargingStation.logPrefix()} Charging profile(s) set at remote start transaction on connector id ${connectorId}: %j`, - chargingProfile, - ); - return true; + chargingProfile + ) + return true } logger.warn( `${chargingStation.logPrefix()} Not allowed to set ${ chargingProfile.chargingProfilePurpose - } charging profile(s) at remote start transaction`, - ); - return false; + } charging profile(s) at remote start transaction` + ) + return false } - private async handleRequestRemoteStopTransaction( + private async handleRequestRemoteStopTransaction ( chargingStation: ChargingStation, - commandPayload: RemoteStopTransactionRequest, + commandPayload: RemoteStopTransactionRequest ): Promise { - const { transactionId } = commandPayload; + const { transactionId } = commandPayload if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { if (evseId > 0) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { if (connectorStatus.transactionId === transactionId) { - return OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId); + return await OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId) } } } @@ -1078,64 +1095,65 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { connectorId > 0 && chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId ) { - return OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId); + return await OCPP16ServiceUtils.remoteStopTransaction(chargingStation, connectorId) } } } logger.warn( - `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id ${transactionId}`, - ); - return OCPP16Constants.OCPP_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Trying to remote stop a non existing transaction with id ${transactionId}` + ) + return OCPP16Constants.OCPP_RESPONSE_REJECTED } - private handleRequestUpdateFirmware( + private handleRequestUpdateFirmware ( chargingStation: ChargingStation, - commandPayload: OCPP16UpdateFirmwareRequest, + commandPayload: OCPP16UpdateFirmwareRequest ): OCPP16UpdateFirmwareResponse { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.FirmwareManagement, - OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, - ) === false + OCPP16IncomingRequestCommand.UPDATE_FIRMWARE + ) ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported`, - ); - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: feature profile not supported` + ) + return OCPP16Constants.OCPP_RESPONSE_EMPTY } - let { retrieveDate } = commandPayload; + let { retrieveDate } = commandPayload if ( !isNullOrUndefined(chargingStation.stationInfo.firmwareStatus) && chargingStation.stationInfo.firmwareStatus !== OCPP16FirmwareStatus.Installed ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress`, - ); - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + `${chargingStation.logPrefix()} ${moduleName}.handleRequestUpdateFirmware: Cannot simulate firmware update: firmware update is already in progress` + ) + return OCPP16Constants.OCPP_RESPONSE_EMPTY } - retrieveDate = convertToDate(retrieveDate)!; - const now = Date.now(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + retrieveDate = convertToDate(retrieveDate)! + const now = Date.now() if (retrieveDate?.getTime() <= now) { - this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION); + this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION) } else { setTimeout( () => { - this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION); + this.updateFirmwareSimulation(chargingStation).catch(Constants.EMPTY_FUNCTION) }, - retrieveDate?.getTime() - now, - ); + retrieveDate?.getTime() - now + ) } - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + return OCPP16Constants.OCPP_RESPONSE_EMPTY } - private async updateFirmwareSimulation( + private async updateFirmwareSimulation ( chargingStation: ChargingStation, maxDelay = 30, - minDelay = 15, + minDelay = 15 ): Promise { - if (checkChargingStation(chargingStation, chargingStation.logPrefix()) === false) { - return; + if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) { + return } if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { @@ -1145,8 +1163,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } } } @@ -1160,55 +1178,55 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } } } await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: OCPP16FirmwareStatus.Downloading, - }); - chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading; + status: OCPP16FirmwareStatus.Downloading + }) + chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloading if ( chargingStation.stationInfo?.firmwareUpgrade?.failureStatus === OCPP16FirmwareStatus.DownloadFailed ) { - await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))) await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus, - }); + status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus + }) chargingStation.stationInfo.firmwareStatus = - chargingStation.stationInfo?.firmwareUpgrade?.failureStatus; - return; + chargingStation.stationInfo?.firmwareUpgrade?.failureStatus + return } - await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))) await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: OCPP16FirmwareStatus.Downloaded, - }); - chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded; - let wasTransactionsStarted = false; - let transactionsStarted: boolean; + status: OCPP16FirmwareStatus.Downloaded + }) + chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Downloaded + let wasTransactionsStarted = false + let transactionsStarted: boolean do { - const runningTransactions = chargingStation.getNumberOfRunningTransactions(); + const runningTransactions = chargingStation.getNumberOfRunningTransactions() if (runningTransactions > 0) { - const waitTime = secondsToMilliseconds(15); + const waitTime = secondsToMilliseconds(15) logger.debug( `${chargingStation.logPrefix()} ${moduleName}.updateFirmwareSimulation: ${runningTransactions} transaction(s) in progress, waiting ${formatDurationMilliSeconds( - waitTime, - )} before continuing firmware update simulation`, - ); - await sleep(waitTime); - transactionsStarted = true; - wasTransactionsStarted = true; + waitTime + )} before continuing firmware update simulation` + ) + await sleep(waitTime) + transactionsStarted = true + wasTransactionsStarted = true } else { if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { @@ -1218,8 +1236,8 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } } } @@ -1234,191 +1252,193 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } } } - transactionsStarted = false; + transactionsStarted = false } - } while (transactionsStarted); + } while (transactionsStarted) !wasTransactionsStarted && - (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))); - if (checkChargingStation(chargingStation, chargingStation.logPrefix()) === false) { - return; + (await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay)))) + if (!checkChargingStation(chargingStation, chargingStation.logPrefix())) { + return } await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: OCPP16FirmwareStatus.Installing, - }); - chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing; + status: OCPP16FirmwareStatus.Installing + }) + chargingStation.stationInfo.firmwareStatus = OCPP16FirmwareStatus.Installing if ( chargingStation.stationInfo?.firmwareUpgrade?.failureStatus === OCPP16FirmwareStatus.InstallationFailed ) { - await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))) await chargingStation.ocppRequestService.requestHandler< - OCPP16FirmwareStatusNotificationRequest, - OCPP16FirmwareStatusNotificationResponse + OCPP16FirmwareStatusNotificationRequest, + OCPP16FirmwareStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, { - status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus, - }); + status: chargingStation.stationInfo?.firmwareUpgrade?.failureStatus + }) chargingStation.stationInfo.firmwareStatus = - chargingStation.stationInfo?.firmwareUpgrade?.failureStatus; - return; + chargingStation.stationInfo?.firmwareUpgrade?.failureStatus + return } if (chargingStation.stationInfo?.firmwareUpgrade?.reset === true) { - await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))); - await chargingStation.reset(OCPP16StopTransactionReason.REBOOT); + await sleep(secondsToMilliseconds(getRandomInteger(maxDelay, minDelay))) + await chargingStation.reset(OCPP16StopTransactionReason.REBOOT) } } - private async handleRequestGetDiagnostics( + private async handleRequestGetDiagnostics ( chargingStation: ChargingStation, - commandPayload: GetDiagnosticsRequest, + commandPayload: GetDiagnosticsRequest ): Promise { if ( - OCPP16ServiceUtils.checkFeatureProfile( + !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.FirmwareManagement, - OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - ) === false + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ) ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported`, - ); - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Cannot get diagnostics: feature profile not supported` + ) + return OCPP16Constants.OCPP_RESPONSE_EMPTY } - const { location } = commandPayload; - const uri = new URL(location); + const { location } = commandPayload + const uri = new URL(location) if (uri.protocol.startsWith('ftp:')) { - let ftpClient: Client | undefined; + let ftpClient: Client | undefined try { const logFiles = readdirSync(resolve(dirname(fileURLToPath(import.meta.url)), '../')) .filter((file) => file.endsWith('.log')) - .map((file) => join('./', file)); - const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz`; - create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive)); - ftpClient = new Client(); + .map((file) => join('./', file)) + const diagnosticsArchive = `${chargingStation.stationInfo.chargingStationId}_logs.tar.gz` + create({ gzip: true }, logFiles).pipe(createWriteStream(diagnosticsArchive)) + ftpClient = new Client() const accessResponse = await ftpClient.access({ host: uri.host, ...(isNotEmptyString(uri.port) && { port: convertToInt(uri.port) }), ...(isNotEmptyString(uri.username) && { user: uri.username }), - ...(isNotEmptyString(uri.password) && { password: uri.password }), - }); - let uploadResponse: FTPResponse | undefined; + ...(isNotEmptyString(uri.password) && { password: uri.password }) + }) + let uploadResponse: FTPResponse | undefined if (accessResponse.code === 220) { ftpClient.trackProgress((info) => { logger.info( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: ${ info.bytes / 1024 - } bytes transferred from diagnostics archive ${info.name}`, - ); + } bytes transferred from diagnostics archive ${info.name}` + ) chargingStation.ocppRequestService .requestHandler< - OCPP16DiagnosticsStatusNotificationRequest, - OCPP16DiagnosticsStatusNotificationResponse - >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { - status: OCPP16DiagnosticsStatus.Uploading, - }) + OCPP16DiagnosticsStatusNotificationRequest, + OCPP16DiagnosticsStatusNotificationResponse + >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { + status: OCPP16DiagnosticsStatus.Uploading + }) .catch((error) => { logger.error( `${chargingStation.logPrefix()} ${moduleName}.handleRequestGetDiagnostics: Error while sending '${ OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION }'`, - error, - ); - }); - }); + error + ) + }) + }) uploadResponse = await ftpClient.uploadFrom( join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), diagnosticsArchive), - `${uri.pathname}${diagnosticsArchive}`, - ); + `${uri.pathname}${diagnosticsArchive}` + ) if (uploadResponse.code === 226) { await chargingStation.ocppRequestService.requestHandler< - OCPP16DiagnosticsStatusNotificationRequest, - OCPP16DiagnosticsStatusNotificationResponse + OCPP16DiagnosticsStatusNotificationRequest, + OCPP16DiagnosticsStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { - status: OCPP16DiagnosticsStatus.Uploaded, - }); - if (ftpClient) { - ftpClient.close(); + status: OCPP16DiagnosticsStatus.Uploaded + }) + if (ftpClient != null) { + ftpClient.close() } - return { fileName: diagnosticsArchive }; + return { fileName: diagnosticsArchive } } throw new OCPPError( ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code}${ - uploadResponse?.code && `|${uploadResponse?.code}` + uploadResponse?.code != null && `|${uploadResponse?.code}` }`, - OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - ); + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ) } throw new OCPPError( ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code}${ - uploadResponse?.code && `|${uploadResponse?.code}` + uploadResponse?.code != null && `|${uploadResponse?.code}` }`, - OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, - ); + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ) } catch (error) { await chargingStation.ocppRequestService.requestHandler< - OCPP16DiagnosticsStatusNotificationRequest, - OCPP16DiagnosticsStatusNotificationResponse + OCPP16DiagnosticsStatusNotificationRequest, + OCPP16DiagnosticsStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { - status: OCPP16DiagnosticsStatus.UploadFailed, - }); - if (ftpClient) { - ftpClient.close(); + status: OCPP16DiagnosticsStatus.UploadFailed + }) + if (ftpClient != null) { + ftpClient.close() } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, error as Error, - { errorResponse: OCPP16Constants.OCPP_RESPONSE_EMPTY }, - )!; + { errorResponse: OCPP16Constants.OCPP_RESPONSE_EMPTY } + )! } } else { logger.error( `${chargingStation.logPrefix()} Unsupported protocol ${ uri.protocol - } to transfer the diagnostic logs archive`, - ); + } to transfer the diagnostic logs archive` + ) await chargingStation.ocppRequestService.requestHandler< - OCPP16DiagnosticsStatusNotificationRequest, - OCPP16DiagnosticsStatusNotificationResponse + OCPP16DiagnosticsStatusNotificationRequest, + OCPP16DiagnosticsStatusNotificationResponse >(chargingStation, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, { - status: OCPP16DiagnosticsStatus.UploadFailed, - }); - return OCPP16Constants.OCPP_RESPONSE_EMPTY; + status: OCPP16DiagnosticsStatus.UploadFailed + }) + return OCPP16Constants.OCPP_RESPONSE_EMPTY } } - private handleRequestTriggerMessage( + private handleRequestTriggerMessage ( chargingStation: ChargingStation, - commandPayload: OCPP16TriggerMessageRequest, + commandPayload: OCPP16TriggerMessageRequest ): OCPP16TriggerMessageResponse { - const { requestedMessage, connectorId } = commandPayload; + const { requestedMessage, connectorId } = commandPayload if ( !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.RemoteTrigger, - OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, + OCPP16IncomingRequestCommand.TRIGGER_MESSAGE ) || !OCPP16ServiceUtils.isMessageTriggerSupported(chargingStation, requestedMessage) ) { - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED; + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED } if ( !OCPP16ServiceUtils.isConnectorIdValid( chargingStation, OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, - connectorId!, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connectorId! ) ) { - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED } try { switch (requestedMessage) { @@ -1426,233 +1446,240 @@ export class OCPP16IncomingRequestService extends OCPPIncomingRequestService { setTimeout(() => { chargingStation.ocppRequestService .requestHandler( - chargingStation, - OCPP16RequestCommand.BOOT_NOTIFICATION, - chargingStation.bootNotificationRequest, - { skipBufferingOnError: true, triggerMessage: true }, - ) + chargingStation, + OCPP16RequestCommand.BOOT_NOTIFICATION, + chargingStation.bootNotificationRequest, + { skipBufferingOnError: true, triggerMessage: true } + ) .then((response) => { - chargingStation.bootNotificationResponse = response; + chargingStation.bootNotificationResponse = response }) - .catch(Constants.EMPTY_FUNCTION); - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY); - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; + .catch(Constants.EMPTY_FUNCTION) + }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED case OCPP16MessageTrigger.Heartbeat: setTimeout(() => { chargingStation.ocppRequestService .requestHandler( - chargingStation, - OCPP16RequestCommand.HEARTBEAT, - undefined, - { - triggerMessage: true, - }, - ) - .catch(Constants.EMPTY_FUNCTION); - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY); - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; + chargingStation, + OCPP16RequestCommand.HEARTBEAT, + undefined, + { + triggerMessage: true + } + ) + .catch(Constants.EMPTY_FUNCTION) + }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED case OCPP16MessageTrigger.StatusNotification: setTimeout(() => { if (!isNullOrUndefined(connectorId)) { chargingStation.ocppRequestService .requestHandler( - chargingStation, - OCPP16RequestCommand.STATUS_NOTIFICATION, - { - connectorId, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: chargingStation.getConnectorStatus(connectorId!)?.status, - }, - { - triggerMessage: true, - }, - ) - .catch(Constants.EMPTY_FUNCTION); + chargingStation, + OCPP16RequestCommand.STATUS_NOTIFICATION, + { + connectorId, + errorCode: OCPP16ChargePointErrorCode.NO_ERROR, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + status: chargingStation.getConnectorStatus(connectorId!)?.status + }, + { + triggerMessage: true + } + ) + .catch(Constants.EMPTY_FUNCTION) } else { - // eslint-disable-next-line no-lonely-if if (chargingStation.hasEvses) { for (const evseStatus of chargingStation.evses.values()) { for (const [id, connectorStatus] of evseStatus.connectors) { chargingStation.ocppRequestService .requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse - >( - chargingStation, - OCPP16RequestCommand.STATUS_NOTIFICATION, - { - connectorId: id, - errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: connectorStatus.status, - }, - { - triggerMessage: true, - }, - ) - .catch(Constants.EMPTY_FUNCTION); - } - } - } else { - for (const id of chargingStation.connectors.keys()) { - chargingStation.ocppRequestService - .requestHandler< - OCPP16StatusNotificationRequest, - OCPP16StatusNotificationResponse + OCPP16StatusNotificationRequest, + OCPP16StatusNotificationResponse >( chargingStation, OCPP16RequestCommand.STATUS_NOTIFICATION, { connectorId: id, errorCode: OCPP16ChargePointErrorCode.NO_ERROR, - status: chargingStation.getConnectorStatus(id)?.status, + status: connectorStatus.status }, { - triggerMessage: true, - }, + triggerMessage: true + } ) - .catch(Constants.EMPTY_FUNCTION); + .catch(Constants.EMPTY_FUNCTION) + } + } + } else { + for (const id of chargingStation.connectors.keys()) { + chargingStation.ocppRequestService + .requestHandler< + OCPP16StatusNotificationRequest, + OCPP16StatusNotificationResponse + >( + chargingStation, + OCPP16RequestCommand.STATUS_NOTIFICATION, + { + connectorId: id, + errorCode: OCPP16ChargePointErrorCode.NO_ERROR, + status: chargingStation.getConnectorStatus(id)?.status + }, + { + triggerMessage: true + } + ) + .catch(Constants.EMPTY_FUNCTION) } } } - }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY); - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; + }, OCPP16Constants.OCPP_TRIGGER_MESSAGE_DELAY) + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED default: - return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED; + return OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED } } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, error as Error, - { errorResponse: OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }, - )!; + { errorResponse: OCPP16Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED } + )! } } - private handleRequestDataTransfer( + private handleRequestDataTransfer ( chargingStation: ChargingStation, - commandPayload: OCPP16DataTransferRequest, + commandPayload: OCPP16DataTransferRequest ): OCPP16DataTransferResponse { - const { vendorId } = commandPayload; + const { vendorId } = commandPayload try { if (Object.values(OCPP16DataTransferVendorId).includes(vendorId)) { - return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED; + return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED } - return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID; + return OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.DATA_TRANSFER, error as Error, - { errorResponse: OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED }, - )!; + { errorResponse: OCPP16Constants.OCPP_DATA_TRANSFER_RESPONSE_REJECTED } + )! } } - private async handleRequestReserveNow( + private async handleRequestReserveNow ( chargingStation: ChargingStation, - commandPayload: OCPP16ReserveNowRequest, + commandPayload: OCPP16ReserveNowRequest ): Promise { if ( !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.Reservation, - OCPP16IncomingRequestCommand.RESERVE_NOW, + OCPP16IncomingRequestCommand.RESERVE_NOW ) ) { - return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } - const { reservationId, idTag, connectorId } = commandPayload; - let response: OCPP16ReserveNowResponse; + const { reservationId, idTag, connectorId } = commandPayload + let response: OCPP16ReserveNowResponse try { if (connectorId > 0 && !chargingStation.isConnectorAvailable(connectorId)) { - return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } if (connectorId === 0 && !chargingStation.getReserveConnectorZeroSupported()) { - return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } if (!(await OCPP16ServiceUtils.isIdTagAuthorized(chargingStation, connectorId, idTag))) { - return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_RESERVATION_RESPONSE_REJECTED } - await removeExpiredReservations(chargingStation); + await removeExpiredReservations(chargingStation) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion switch (chargingStation.getConnectorStatus(connectorId)!.status) { case OCPP16ChargePointStatus.Faulted: - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED + break case OCPP16ChargePointStatus.Preparing: case OCPP16ChargePointStatus.Charging: case OCPP16ChargePointStatus.SuspendedEV: case OCPP16ChargePointStatus.SuspendedEVSE: case OCPP16ChargePointStatus.Finishing: - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED + break case OCPP16ChargePointStatus.Unavailable: - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_UNAVAILABLE + break case OCPP16ChargePointStatus.Reserved: if (!chargingStation.isConnectorReservable(reservationId, idTag, connectorId)) { - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED + break } // eslint-disable-next-line no-fallthrough default: if (!chargingStation.isConnectorReservable(reservationId, idTag)) { - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED; - break; + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_OCCUPIED + break } await chargingStation.addReservation({ id: commandPayload.reservationId, - ...commandPayload, - }); - response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_ACCEPTED; - break; + ...commandPayload + }) + response = OCPP16Constants.OCPP_RESERVATION_RESPONSE_ACCEPTED + break } - return response; + return response } catch (error) { - chargingStation.getConnectorStatus(connectorId)!.status = OCPP16ChargePointStatus.Available; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.status = OCPP16ChargePointStatus.Available + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.RESERVE_NOW, error as Error, - { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED }, - )!; + { errorResponse: OCPP16Constants.OCPP_RESERVATION_RESPONSE_FAULTED } + )! } } - private async handleRequestCancelReservation( + private async handleRequestCancelReservation ( chargingStation: ChargingStation, - commandPayload: OCPP16CancelReservationRequest, + commandPayload: OCPP16CancelReservationRequest ): Promise { if ( !OCPP16ServiceUtils.checkFeatureProfile( chargingStation, OCPP16SupportedFeatureProfiles.Reservation, - OCPP16IncomingRequestCommand.CANCEL_RESERVATION, + OCPP16IncomingRequestCommand.CANCEL_RESERVATION ) ) { - return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED; + return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED } try { - const { reservationId } = commandPayload; - const reservation = chargingStation.getReservationBy('reservationId', reservationId); + const { reservationId } = commandPayload + const reservation = chargingStation.getReservationBy('reservationId', reservationId) if (isUndefined(reservation)) { logger.debug( - `${chargingStation.logPrefix()} Reservation with id ${reservationId} does not exist on charging station`, - ); - return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED; + `${chargingStation.logPrefix()} Reservation with id ${reservationId} does not exist on charging station` + ) + return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED } await chargingStation.removeReservation( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion reservation!, - ReservationTerminationReason.RESERVATION_CANCELED, - ); - return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED; + ReservationTerminationReason.RESERVATION_CANCELED + ) + return OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED } catch (error) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.handleIncomingRequestError( chargingStation, OCPP16IncomingRequestCommand.CANCEL_RESERVATION, error as Error, - { errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED }, - )!; + { errorResponse: OCPP16Constants.OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED } + )! } } } diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index 2b0375a2..37e7019c 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -1,11 +1,11 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import type { JSONSchemaType } from 'ajv'; +import type { JSONSchemaType } from 'ajv' -import { OCPP16Constants } from './OCPP16Constants.js'; -import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'; -import type { ChargingStation } from '../../../charging-station/index.js'; -import { OCPPError } from '../../../exception/index.js'; +import { OCPP16Constants } from './OCPP16Constants.js' +import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' +import type { ChargingStation } from '../../../charging-station/index.js' +import { OCPPError } from '../../../exception/index.js' import { ErrorType, type JsonObject, @@ -23,144 +23,144 @@ import { type OCPP16StatusNotificationRequest, type OCPP16StopTransactionRequest, OCPPVersion, - type RequestParams, -} from '../../../types/index.js'; -import { Constants, generateUUID } from '../../../utils/index.js'; -import { OCPPRequestService } from '../OCPPRequestService.js'; -import type { OCPPResponseService } from '../OCPPResponseService.js'; + type RequestParams +} from '../../../types/index.js' +import { Constants, generateUUID } from '../../../utils/index.js' +import { OCPPRequestService } from '../OCPPRequestService.js' +import type { OCPPResponseService } from '../OCPPResponseService.js' -const moduleName = 'OCPP16RequestService'; +const moduleName = 'OCPP16RequestService' export class OCPP16RequestService extends OCPPRequestService { - protected jsonSchemas: Map>; + protected jsonSchemas: Map> - public constructor(ocppResponseService: OCPPResponseService) { + public constructor (ocppResponseService: OCPPResponseService) { // if (new.target?.name === moduleName) { - // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); + // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`) // } - super(OCPPVersion.VERSION_16, ocppResponseService); + super(OCPPVersion.VERSION_16, ocppResponseService) this.jsonSchemas = new Map>([ [ OCPP16RequestCommand.AUTHORIZE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/Authorize.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.BOOT_NOTIFICATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/BootNotification.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotification.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.HEARTBEAT, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/Heartbeat.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.METER_VALUES, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/MeterValues.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.STATUS_NOTIFICATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/StatusNotification.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.START_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/StartTransaction.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.STOP_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/StopTransaction.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.DATA_TRANSFER, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/DataTransfer.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotification.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.buildRequestPayload = this.buildRequestPayload.bind(this) as ( chargingStation: ChargingStation, commandName: OCPP16RequestCommand, - commandParams?: JsonType, - ) => Request; + commandParams?: JsonType + ) => Request } public async requestHandler( chargingStation: ChargingStation, commandName: OCPP16RequestCommand, commandParams?: JsonType, - params?: RequestParams, + params?: RequestParams ): Promise { // FIXME?: add sanity checks on charging station availability, connector availability, connector status, etc. - if (OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true) { + if (OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName)) { return (await this.sendMessage( chargingStation, generateUUID(), this.buildRequestPayload(chargingStation, commandName, commandParams), commandName, - params, - )) as ResponseType; + params + )) as ResponseType } // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). throw new OCPPError( ErrorType.NOT_SUPPORTED, `Unsupported OCPP command '${commandName}'`, commandName, - commandParams, - ); + commandParams + ) } private buildRequestPayload( chargingStation: ChargingStation, commandName: OCPP16RequestCommand, - commandParams?: JsonType, + commandParams?: JsonType ): Request { - let connectorId: number | undefined; - let energyActiveImportRegister: number; - commandParams = commandParams as JsonObject; + let connectorId: number | undefined + let energyActiveImportRegister: number + commandParams = commandParams as JsonObject switch (commandName) { case OCPP16RequestCommand.BOOT_NOTIFICATION: case OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION: @@ -168,61 +168,65 @@ export class OCPP16RequestService extends OCPPRequestService { case OCPP16RequestCommand.METER_VALUES: case OCPP16RequestCommand.STATUS_NOTIFICATION: case OCPP16RequestCommand.DATA_TRANSFER: - return commandParams as unknown as Request; + return commandParams as unknown as Request case OCPP16RequestCommand.AUTHORIZE: return { idTag: Constants.DEFAULT_IDTAG, - ...commandParams, - } as unknown as Request; + ...commandParams + } as unknown as Request case OCPP16RequestCommand.HEARTBEAT: - return OCPP16Constants.OCPP_REQUEST_EMPTY as unknown as Request; + return OCPP16Constants.OCPP_REQUEST_EMPTY as unknown as Request case OCPP16RequestCommand.START_TRANSACTION: return { idTag: Constants.DEFAULT_IDTAG, meterStart: chargingStation.getEnergyActiveImportRegisterByConnectorId( commandParams?.connectorId as number, - true, + true ), timestamp: new Date(), ...(OCPP16ServiceUtils.hasReservation( chargingStation, commandParams?.connectorId as number, - commandParams?.idTag as string, + commandParams?.idTag as string ) && { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion reservationId: chargingStation.getReservationBy( 'connectorId', chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved ? 0 - : (commandParams?.connectorId as number), - )!.reservationId, + : (commandParams?.connectorId as number) + )!.reservationId }), - ...commandParams, - } as unknown as Request; + ...commandParams + } as unknown as Request case OCPP16RequestCommand.STOP_TRANSACTION: - chargingStation.stationInfo?.transactionDataMeterValues && + chargingStation.stationInfo?.transactionDataMeterValues === true && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion (connectorId = chargingStation.getConnectorIdByTransactionId( - commandParams?.transactionId as number, - )!); + commandParams?.transactionId as number + )!) energyActiveImportRegister = chargingStation.getEnergyActiveImportRegisterByTransactionId( commandParams?.transactionId as number, - true, - ); + true + ) return { idTag: chargingStation.getTransactionIdTag(commandParams?.transactionId as number), meterStop: energyActiveImportRegister, timestamp: new Date(), - ...(chargingStation.stationInfo?.transactionDataMeterValues && { + ...(chargingStation.stationInfo?.transactionDataMeterValues === true && { transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingStation.getConnectorStatus(connectorId!)!.transactionBeginMeterValue!, OCPP16ServiceUtils.buildTransactionEndMeterValue( chargingStation, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion connectorId!, - energyActiveImportRegister, - ), - ), + energyActiveImportRegister + ) + ) }), - ...commandParams, - } as unknown as Request; + ...commandParams + } as unknown as Request default: // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). throw new OCPPError( @@ -230,8 +234,8 @@ export class OCPP16RequestService extends OCPPRequestService { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `Unsupported OCPP command '${commandName}'`, commandName, - commandParams, - ); + commandParams + ) } } } diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 1e64e74b..37c05637 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -1,17 +1,17 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import type { JSONSchemaType } from 'ajv'; -import { secondsToMilliseconds } from 'date-fns'; +import type { JSONSchemaType } from 'ajv' +import { secondsToMilliseconds } from 'date-fns' -import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js'; +import { OCPP16ServiceUtils } from './OCPP16ServiceUtils.js' import { type ChargingStation, addConfigurationKey, getConfigurationKey, hasReservationExpired, - resetConnectorStatus, -} from '../../../charging-station/index.js'; -import { OCPPError } from '../../../exception/index.js'; + resetConnectorStatus +} from '../../../charging-station/index.js' +import { OCPPError } from '../../../exception/index.js' import { type ChangeConfigurationResponse, ErrorType, @@ -49,307 +49,305 @@ import { ReservationTerminationReason, type ResponseHandler, type SetChargingProfileResponse, - type UnlockConnectorResponse, -} from '../../../types/index.js'; -import { Constants, convertToInt, isNullOrUndefined, logger } from '../../../utils/index.js'; -import { OCPPResponseService } from '../OCPPResponseService.js'; + type UnlockConnectorResponse +} from '../../../types/index.js' +import { Constants, convertToInt, isNullOrUndefined, logger } from '../../../utils/index.js' +import { OCPPResponseService } from '../OCPPResponseService.js' -const moduleName = 'OCPP16ResponseService'; +const moduleName = 'OCPP16ResponseService' export class OCPP16ResponseService extends OCPPResponseService { public jsonIncomingRequestResponseSchemas: Map< - OCPP16IncomingRequestCommand, - JSONSchemaType - >; + OCPP16IncomingRequestCommand, + JSONSchemaType + > - private responseHandlers: Map; - private jsonSchemas: Map>; + private readonly responseHandlers: Map + private readonly jsonSchemas: Map> - public constructor() { + public constructor () { // if (new.target?.name === moduleName) { - // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); + // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`) // } - super(OCPPVersion.VERSION_16); + super(OCPPVersion.VERSION_16) this.responseHandlers = new Map([ [ OCPP16RequestCommand.BOOT_NOTIFICATION, - this.handleResponseBootNotification.bind(this) as ResponseHandler, + this.handleResponseBootNotification.bind(this) as ResponseHandler ], [OCPP16RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this) as ResponseHandler], [OCPP16RequestCommand.AUTHORIZE, this.handleResponseAuthorize.bind(this) as ResponseHandler], [ OCPP16RequestCommand.START_TRANSACTION, - this.handleResponseStartTransaction.bind(this) as ResponseHandler, + this.handleResponseStartTransaction.bind(this) as ResponseHandler ], [ OCPP16RequestCommand.STOP_TRANSACTION, - this.handleResponseStopTransaction.bind(this) as ResponseHandler, + this.handleResponseStopTransaction.bind(this) as ResponseHandler ], [ OCPP16RequestCommand.STATUS_NOTIFICATION, - this.emptyResponseHandler.bind(this) as ResponseHandler, + this.emptyResponseHandler.bind(this) as ResponseHandler ], [OCPP16RequestCommand.METER_VALUES, this.emptyResponseHandler.bind(this) as ResponseHandler], [ OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, - this.emptyResponseHandler.bind(this) as ResponseHandler, + this.emptyResponseHandler.bind(this) as ResponseHandler ], [OCPP16RequestCommand.DATA_TRANSFER, this.emptyResponseHandler.bind(this) as ResponseHandler], [ OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, - this.emptyResponseHandler.bind(this) as ResponseHandler, - ], - ]); + this.emptyResponseHandler.bind(this) as ResponseHandler + ] + ]) this.jsonSchemas = new Map>([ [ OCPP16RequestCommand.BOOT_NOTIFICATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/BootNotificationResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.HEARTBEAT, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/HeartbeatResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.AUTHORIZE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/AuthorizeResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.START_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/StartTransactionResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.STOP_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/StopTransactionResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.STATUS_NOTIFICATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/StatusNotificationResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.METER_VALUES, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/MeterValuesResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/DiagnosticsStatusNotificationResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.DATA_TRANSFER, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16RequestCommand.FIRMWARE_STATUS_NOTIFICATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/FirmwareStatusNotificationResponse.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.jsonIncomingRequestResponseSchemas = new Map([ [ OCPP16IncomingRequestCommand.RESET, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ResetResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CLEAR_CACHE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ClearCacheResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ChangeAvailabilityResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/UnlockConnectorResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_CONFIGURATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetConfigurationResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ChangeConfigurationResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_COMPOSITE_SCHEDULE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetCompositeScheduleResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/SetChargingProfileResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ClearChargingProfileResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/RemoteStartTransactionResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/RemoteStopTransactionResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/GetDiagnosticsResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/TriggerMessageResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.DATA_TRANSFER, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/DataTransferResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.UPDATE_FIRMWARE, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/UpdateFirmwareResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.RESERVE_NOW, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/ReserveNowResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP16IncomingRequestCommand.CANCEL_RESERVATION, OCPP16ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/1.6/CancelReservationResponse.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.validatePayload = this.validatePayload.bind(this) as ( chargingStation: ChargingStation, commandName: OCPP16RequestCommand, - payload: JsonType, - ) => boolean; + payload: JsonType + ) => boolean } public async responseHandler( chargingStation: ChargingStation, commandName: OCPP16RequestCommand, payload: ResType, - requestPayload: ReqType, + requestPayload: ReqType ): Promise { - if ( - chargingStation.isRegistered() === true || - commandName === OCPP16RequestCommand.BOOT_NOTIFICATION - ) { + if (chargingStation.isRegistered() || commandName === OCPP16RequestCommand.BOOT_NOTIFICATION) { if ( - this.responseHandlers.has(commandName) === true && - OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true + this.responseHandlers.has(commandName) && + OCPP16ServiceUtils.isRequestCommandSupported(chargingStation, commandName) ) { try { - this.validatePayload(chargingStation, commandName, payload); - await this.responseHandlers.get(commandName)!(chargingStation, payload, requestPayload); + this.validatePayload(chargingStation, commandName, payload) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await this.responseHandlers.get(commandName)!(chargingStation, payload, requestPayload) } catch (error) { logger.error( `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`, - error, - ); - throw error; + error + ) + throw error } } else { // Throw exception @@ -358,11 +356,11 @@ export class OCPP16ResponseService extends OCPPResponseService { `${commandName} is not implemented to handle response PDU ${JSON.stringify( payload, undefined, - 2, + 2 )}`, commandName, - payload, - ); + payload + ) } } else { throw new OCPPError( @@ -370,36 +368,37 @@ export class OCPP16ResponseService extends OCPPResponseService { `${commandName} cannot be issued to handle response PDU ${JSON.stringify( payload, undefined, - 2, + 2 )} while the charging station is not registered on the central server.`, commandName, - payload, - ); + payload + ) } } - private validatePayload( + private validatePayload ( chargingStation: ChargingStation, commandName: OCPP16RequestCommand, - payload: JsonType, + payload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName) === true) { + if (this.jsonSchemas.has(commandName)) { return this.validateResponsePayload( chargingStation, commandName, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.jsonSchemas.get(commandName)!, - payload, - ); + payload + ) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`, - ); - return false; + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + ) + return false } - private handleResponseBootNotification( + private handleResponseBootNotification ( chargingStation: ChargingStation, - payload: OCPP16BootNotificationResponse, + payload: OCPP16BootNotificationResponse ): void { if (payload.status === RegistrationStatusEnumType.ACCEPTED) { addConfigurationKey( @@ -407,45 +406,45 @@ export class OCPP16ResponseService extends OCPPResponseService { OCPP16StandardParametersKey.HeartbeatInterval, payload.interval.toString(), {}, - { overwrite: true, save: true }, - ); + { overwrite: true, save: true } + ) addConfigurationKey( chargingStation, OCPP16StandardParametersKey.HeartBeatInterval, payload.interval.toString(), { visible: false }, - { overwrite: true, save: true }, - ); - OCPP16ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval); + { overwrite: true, save: true } + ) + OCPP16ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval) } if (Object.values(RegistrationStatusEnumType).includes(payload.status)) { const logMsg = `${chargingStation.logPrefix()} Charging station in '${ payload.status - }' state on the central server`; + }' state on the central server` payload.status === RegistrationStatusEnumType.REJECTED ? logger.warn(logMsg) - : logger.info(logMsg); + : logger.info(logMsg) } else { logger.error( `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`, - payload, - ); + payload + ) } } - private handleResponseAuthorize( + private handleResponseAuthorize ( chargingStation: ChargingStation, payload: OCPP16AuthorizeResponse, - requestPayload: OCPP16AuthorizeRequest, + requestPayload: OCPP16AuthorizeRequest ): void { - let authorizeConnectorId: number | undefined; + let authorizeConnectorId: number | undefined if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { if (evseId > 0) { for (const [connectorId, connectorStatus] of evseStatus.connectors) { if (connectorStatus?.authorizeIdTag === requestPayload.idTag) { - authorizeConnectorId = connectorId; - break; + authorizeConnectorId = connectorId + break } } } @@ -456,104 +455,106 @@ export class OCPP16ResponseService extends OCPPResponseService { connectorId > 0 && chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag ) { - authorizeConnectorId = connectorId; - break; + authorizeConnectorId = connectorId + break } } } - const authorizeConnectorStatus = chargingStation.getConnectorStatus(authorizeConnectorId!); - const authorizeConnectorIdDefined = !isNullOrUndefined(authorizeConnectorId); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const authorizeConnectorStatus = chargingStation.getConnectorStatus(authorizeConnectorId!) + const authorizeConnectorIdDefined = !isNullOrUndefined(authorizeConnectorId) if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { if (authorizeConnectorIdDefined) { - // authorizeConnectorStatus!.authorizeIdTag = requestPayload.idTag; - authorizeConnectorStatus!.idTagAuthorized = true; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + authorizeConnectorStatus!.idTagAuthorized = true } logger.debug( `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${ authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : '' - }`, - ); + }` + ) } else { if (authorizeConnectorIdDefined) { - authorizeConnectorStatus!.idTagAuthorized = false; - delete authorizeConnectorStatus?.authorizeIdTag; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + authorizeConnectorStatus!.idTagAuthorized = false + delete authorizeConnectorStatus?.authorizeIdTag } logger.debug( `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${ payload.idTagInfo.status - }'${authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''}`, - ); + }'${authorizeConnectorIdDefined ? ` on connector id ${authorizeConnectorId}` : ''}` + ) } } - private async handleResponseStartTransaction( + private async handleResponseStartTransaction ( chargingStation: ChargingStation, payload: OCPP16StartTransactionResponse, - requestPayload: OCPP16StartTransactionRequest, + requestPayload: OCPP16StartTransactionRequest ): Promise { - const { connectorId } = requestPayload; - if (connectorId === 0 || chargingStation.hasConnector(connectorId) === false) { + const { connectorId } = requestPayload + if (connectorId === 0 || !chargingStation.hasConnector(connectorId)) { logger.error( - `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${connectorId}`, - ); - return; + `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${connectorId}` + ) + return } - const connectorStatus = chargingStation.getConnectorStatus(connectorId); + const connectorStatus = chargingStation.getConnectorStatus(connectorId) if ( connectorStatus?.transactionRemoteStarted === true && - chargingStation.getAuthorizeRemoteTxRequests() === true && - chargingStation.getLocalAuthListEnabled() === true && - chargingStation.hasIdTags() === true && + chargingStation.getAuthorizeRemoteTxRequests() && + chargingStation.getLocalAuthListEnabled() && + chargingStation.hasIdTags() && connectorStatus?.idTagLocalAuthorized === false ) { logger.error( - `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${connectorStatus?.localAuthorizeIdTag} on connector id ${connectorId}`, - ); - await this.resetConnectorOnStartTransactionError(chargingStation, connectorId); - return; + `${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${connectorStatus?.localAuthorizeIdTag} on connector id ${connectorId}` + ) + await this.resetConnectorOnStartTransactionError(chargingStation, connectorId) + return } if ( connectorStatus?.transactionRemoteStarted === true && - chargingStation.getAuthorizeRemoteTxRequests() === true && + chargingStation.getAuthorizeRemoteTxRequests() && chargingStation.stationInfo?.remoteAuthorization === true && connectorStatus?.idTagLocalAuthorized === false && connectorStatus?.idTagAuthorized === false ) { logger.error( - `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${connectorStatus?.authorizeIdTag} on connector id ${connectorId}`, - ); - await this.resetConnectorOnStartTransactionError(chargingStation, connectorId); - return; + `${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${connectorStatus?.authorizeIdTag} on connector id ${connectorId}` + ) + await this.resetConnectorOnStartTransactionError(chargingStation, connectorId) + return } if ( - connectorStatus?.idTagAuthorized && + connectorStatus?.idTagAuthorized === true && connectorStatus?.authorizeIdTag !== requestPayload.idTag ) { logger.error( `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${ requestPayload.idTag - } different from the authorize request one ${connectorStatus?.authorizeIdTag} on connector id ${connectorId}`, - ); - await this.resetConnectorOnStartTransactionError(chargingStation, connectorId); - return; + } different from the authorize request one ${connectorStatus?.authorizeIdTag} on connector id ${connectorId}` + ) + await this.resetConnectorOnStartTransactionError(chargingStation, connectorId) + return } if ( - connectorStatus?.idTagLocalAuthorized && + connectorStatus?.idTagLocalAuthorized === true && connectorStatus?.localAuthorizeIdTag !== requestPayload.idTag ) { logger.error( `${chargingStation.logPrefix()} Trying to start a transaction with an idTag ${ requestPayload.idTag - } different from the local authorized one ${connectorStatus?.localAuthorizeIdTag} on connector id ${connectorId}`, - ); - await this.resetConnectorOnStartTransactionError(chargingStation, connectorId); - return; + } different from the local authorized one ${connectorStatus?.localAuthorizeIdTag} on connector id ${connectorId}` + ) + await this.resetConnectorOnStartTransactionError(chargingStation, connectorId) + return } if (connectorStatus?.transactionStarted === true) { logger.error( - `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${connectorId} by idTag ${connectorStatus?.transactionIdTag}`, - ); - return; + `${chargingStation.logPrefix()} Trying to start a transaction on an already used connector id ${connectorId} by idTag ${connectorStatus?.transactionIdTag}` + ) + return } if (chargingStation.hasEvses) { for (const [evseId, evseStatus] of chargingStation.evses) { @@ -561,10 +562,10 @@ export class OCPP16ResponseService extends OCPPResponseService { for (const [id, status] of evseStatus.connectors) { if (id !== connectorId && status?.transactionStarted === true) { logger.error( - `${chargingStation.logPrefix()} Trying to start a transaction on an already used evse id ${evseId} by connector id ${id} with idTag ${status?.transactionIdTag}`, - ); - await this.resetConnectorOnStartTransactionError(chargingStation, connectorId); - return; + `${chargingStation.logPrefix()} Trying to start a transaction on an already used evse id ${evseId} by connector id ${id} with idTag ${status?.transactionIdTag}` + ) + await this.resetConnectorOnStartTransactionError(chargingStation, connectorId) + return } } } @@ -575,44 +576,45 @@ export class OCPP16ResponseService extends OCPPResponseService { connectorStatus?.status !== OCPP16ChargePointStatus.Preparing ) { logger.error( - `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId} with status ${connectorStatus?.status}`, - ); - return; + `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId} with status ${connectorStatus?.status}` + ) + return } if (!Number.isSafeInteger(payload.transactionId)) { logger.warn( `${chargingStation.logPrefix()} Trying to start a transaction on connector id ${connectorId} with a non integer transaction id ${ payload.transactionId - }, converting to integer`, - ); - payload.transactionId = convertToInt(payload.transactionId); + }, converting to integer` + ) + payload.transactionId = convertToInt(payload.transactionId) } if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { - connectorStatus.transactionStarted = true; - connectorStatus.transactionStart = requestPayload.timestamp; - connectorStatus.transactionId = payload.transactionId; - connectorStatus.transactionIdTag = requestPayload.idTag; - connectorStatus.transactionEnergyActiveImportRegisterValue = 0; + connectorStatus.transactionStarted = true + connectorStatus.transactionStart = requestPayload.timestamp + connectorStatus.transactionId = payload.transactionId + connectorStatus.transactionIdTag = requestPayload.idTag + connectorStatus.transactionEnergyActiveImportRegisterValue = 0 connectorStatus.transactionBeginMeterValue = OCPP16ServiceUtils.buildTransactionBeginMeterValue( chargingStation, connectorId, - requestPayload.meterStart, - ); - if (requestPayload.reservationId) { + requestPayload.meterStart + ) + if (requestPayload.reservationId != null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const reservation = chargingStation.getReservationBy( 'reservationId', - requestPayload.reservationId, - )!; + requestPayload.reservationId + )! if (reservation.idTag !== requestPayload.idTag) { logger.warn( `${chargingStation.logPrefix()} Reserved transaction ${ payload.transactionId } started with a different idTag ${requestPayload.idTag} than the reservation one ${ reservation.idTag - }`, - ); + }` + ) } if (hasReservationExpired(reservation)) { logger.warn( @@ -620,46 +622,46 @@ export class OCPP16ResponseService extends OCPPResponseService { payload.transactionId } started with expired reservation ${ requestPayload.reservationId - } (expiry date: ${reservation.expiryDate.toISOString()}))`, - ); + } (expiry date: ${reservation.expiryDate.toISOString()}))` + ) } await chargingStation.removeReservation( reservation, - ReservationTerminationReason.TRANSACTION_STARTED, - ); + ReservationTerminationReason.TRANSACTION_STARTED + ) } - chargingStation.stationInfo?.beginEndMeterValues && + chargingStation.stationInfo?.beginEndMeterValues === true && (await chargingStation.ocppRequestService.requestHandler< - OCPP16MeterValuesRequest, - OCPP16MeterValuesResponse + OCPP16MeterValuesRequest, + OCPP16MeterValuesResponse >(chargingStation, OCPP16RequestCommand.METER_VALUES, { connectorId, transactionId: payload.transactionId, - meterValue: [connectorStatus.transactionBeginMeterValue], - } as OCPP16MeterValuesRequest)); + meterValue: [connectorStatus.transactionBeginMeterValue] + } satisfies OCPP16MeterValuesRequest)) await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Charging, - ); + OCPP16ChargePointStatus.Charging + ) logger.info( `${chargingStation.logPrefix()} Transaction with id ${payload.transactionId} STARTED on ${ chargingStation.stationInfo.chargingStationId - }#${connectorId} for idTag '${requestPayload.idTag}'`, - ); - if (chargingStation.stationInfo.powerSharedByConnectors) { - ++chargingStation.powerDivider; + }#${connectorId} for idTag '${requestPayload.idTag}'` + ) + if (chargingStation.stationInfo.powerSharedByConnectors === true) { + ++chargingStation.powerDivider } const configuredMeterValueSampleInterval = getConfigurationKey( chargingStation, - OCPP16StandardParametersKey.MeterValueSampleInterval, - ); + OCPP16StandardParametersKey.MeterValueSampleInterval + ) chargingStation.startMeterValues( connectorId, configuredMeterValueSampleInterval !== undefined ? secondsToMilliseconds(convertToInt(configuredMeterValueSampleInterval.value)) - : Constants.DEFAULT_METER_VALUES_INTERVAL, - ); + : Constants.DEFAULT_METER_VALUES_INTERVAL + ) } else { logger.warn( `${chargingStation.logPrefix()} Starting transaction with id ${ @@ -672,94 +674,101 @@ export class OCPP16ResponseService extends OCPPResponseService { OCPP16ServiceUtils.hasReservation(chargingStation, connectorId, requestPayload.idTag) ? `, reservationId '${requestPayload.reservationId}'` : '' - }`, - ); - await this.resetConnectorOnStartTransactionError(chargingStation, connectorId); + }` + ) + await this.resetConnectorOnStartTransactionError(chargingStation, connectorId) } } - private async resetConnectorOnStartTransactionError( + private async resetConnectorOnStartTransactionError ( chargingStation: ChargingStation, - connectorId: number, + connectorId: number ): Promise { - const connectorStatus = chargingStation.getConnectorStatus(connectorId); - resetConnectorStatus(connectorStatus!); - chargingStation.stopMeterValues(connectorId); + const connectorStatus = chargingStation.getConnectorStatus(connectorId) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + resetConnectorStatus(connectorStatus!) + chargingStation.stopMeterValues(connectorId) if (connectorStatus?.status !== OCPP16ChargePointStatus.Available) { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Available, - ); + OCPP16ChargePointStatus.Available + ) } } - private async handleResponseStopTransaction( + private async handleResponseStopTransaction ( chargingStation: ChargingStation, payload: OCPP16StopTransactionResponse, - requestPayload: OCPP16StopTransactionRequest, + requestPayload: OCPP16StopTransactionRequest ): Promise { const transactionConnectorId = chargingStation.getConnectorIdByTransactionId( - requestPayload.transactionId, - ); + requestPayload.transactionId + ) if (isNullOrUndefined(transactionConnectorId)) { logger.error( `${chargingStation.logPrefix()} Trying to stop a non existing transaction with id ${ requestPayload.transactionId - }`, - ); - return; + }` + ) + return } chargingStation.stationInfo?.beginEndMeterValues === true && chargingStation.stationInfo?.ocppStrictCompliance === false && chargingStation.stationInfo?.outOfOrderEndMeterValues === true && (await chargingStation.ocppRequestService.requestHandler< - OCPP16MeterValuesRequest, - OCPP16MeterValuesResponse + OCPP16MeterValuesRequest, + OCPP16MeterValuesResponse >(chargingStation, OCPP16RequestCommand.METER_VALUES, { connectorId: transactionConnectorId, transactionId: requestPayload.transactionId, meterValue: [ OCPP16ServiceUtils.buildTransactionEndMeterValue( chargingStation, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion transactionConnectorId!, - requestPayload.meterStop, - ), - ], - })); + requestPayload.meterStop + ) + ] + })) if ( - chargingStation.isChargingStationAvailable() === false || - chargingStation.isConnectorAvailable(transactionConnectorId!) === false + !chargingStation.isChargingStationAvailable() || + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + !chargingStation.isConnectorAvailable(transactionConnectorId!) ) { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion transactionConnectorId!, - OCPP16ChargePointStatus.Unavailable, - ); + OCPP16ChargePointStatus.Unavailable + ) } else { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion transactionConnectorId!, - OCPP16ChargePointStatus.Available, - ); + OCPP16ChargePointStatus.Available + ) } - if (chargingStation.stationInfo.powerSharedByConnectors) { - chargingStation.powerDivider--; + if (chargingStation.stationInfo.powerSharedByConnectors === true) { + chargingStation.powerDivider-- } - resetConnectorStatus(chargingStation.getConnectorStatus(transactionConnectorId!)!); - chargingStation.stopMeterValues(transactionConnectorId!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + resetConnectorStatus(chargingStation.getConnectorStatus(transactionConnectorId!)!) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stopMeterValues(transactionConnectorId!) const logMsg = `${chargingStation.logPrefix()} Transaction with id ${ requestPayload.transactionId } STOPPED on ${ chargingStation.stationInfo.chargingStationId - }#${transactionConnectorId} with status '${payload.idTagInfo?.status}'`; + }#${transactionConnectorId} with status '${payload.idTagInfo?.status}'` if ( isNullOrUndefined(payload.idTagInfo) || payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED ) { - logger.info(logMsg); + logger.info(logMsg) } else { - logger.warn(logMsg); + logger.warn(logMsg) } } } diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index e4d1ebdf..c234a5ec 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -1,6 +1,6 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import type { JSONSchemaType } from 'ajv'; +import type { JSONSchemaType } from 'ajv' import { type Interval, addSeconds, @@ -8,20 +8,20 @@ import { differenceInSeconds, isAfter, isBefore, - isWithinInterval, -} from 'date-fns'; + isWithinInterval +} from 'date-fns' -import { OCPP16Constants } from './OCPP16Constants.js'; +import { OCPP16Constants } from './OCPP16Constants.js' import { type ChargingStation, hasFeatureProfile, - hasReservationExpired, -} from '../../../charging-station/index.js'; + hasReservationExpired +} from '../../../charging-station/index.js' import { type GenericResponse, type JsonType, OCPP16AuthorizationStatus, - OCPP16AvailabilityType, + type OCPP16AvailabilityType, type OCPP16ChangeAvailabilityResponse, OCPP16ChargePointStatus, type OCPP16ChargingProfile, @@ -31,137 +31,139 @@ import { type OCPP16MeterValue, OCPP16MeterValueContext, OCPP16MeterValueUnit, - OCPP16RequestCommand, + type OCPP16RequestCommand, OCPP16StandardParametersKey, OCPP16StopTransactionReason, type OCPP16SupportedFeatureProfiles, - OCPPVersion, -} from '../../../types/index.js'; -import { isNotEmptyArray, isNullOrUndefined, logger, roundTo } from '../../../utils/index.js'; -import { OCPPServiceUtils } from '../OCPPServiceUtils.js'; + OCPPVersion +} from '../../../types/index.js' +import { isNotEmptyArray, isNullOrUndefined, logger, roundTo } from '../../../utils/index.js' +import { OCPPServiceUtils } from '../OCPPServiceUtils.js' export class OCPP16ServiceUtils extends OCPPServiceUtils { - public static checkFeatureProfile( + public static checkFeatureProfile ( chargingStation: ChargingStation, featureProfile: OCPP16SupportedFeatureProfiles, - command: OCPP16RequestCommand | OCPP16IncomingRequestCommand, + command: OCPP16RequestCommand | OCPP16IncomingRequestCommand ): boolean { - if (!hasFeatureProfile(chargingStation, featureProfile)) { + if (hasFeatureProfile(chargingStation, featureProfile) === false) { logger.warn( `${chargingStation.logPrefix()} Trying to '${command}' without '${featureProfile}' feature enabled in ${ OCPP16StandardParametersKey.SupportedFeatureProfiles - } in configuration`, - ); - return false; + } in configuration` + ) + return false } - return true; + return true } - public static buildTransactionBeginMeterValue( + public static buildTransactionBeginMeterValue ( chargingStation: ChargingStation, connectorId: number, - meterStart: number, + meterStart: number ): OCPP16MeterValue { const meterValue: OCPP16MeterValue = { timestamp: new Date(), - sampledValue: [], - }; + sampledValue: [] + } // Energy.Active.Import.Register measurand (default) const sampledValueTemplate = OCPP16ServiceUtils.getSampledValueTemplate( chargingStation, - connectorId, - ); + connectorId + ) const unitDivider = - sampledValueTemplate?.unit === OCPP16MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; + sampledValueTemplate?.unit === OCPP16MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1 meterValue.sampledValue.push( OCPP16ServiceUtils.buildSampledValue( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion sampledValueTemplate!, roundTo((meterStart ?? 0) / unitDivider, 4), - OCPP16MeterValueContext.TRANSACTION_BEGIN, - ), - ); - return meterValue; + OCPP16MeterValueContext.TRANSACTION_BEGIN + ) + ) + return meterValue } - public static buildTransactionDataMeterValues( + public static buildTransactionDataMeterValues ( transactionBeginMeterValue: OCPP16MeterValue, - transactionEndMeterValue: OCPP16MeterValue, + transactionEndMeterValue: OCPP16MeterValue ): OCPP16MeterValue[] { - const meterValues: OCPP16MeterValue[] = []; - meterValues.push(transactionBeginMeterValue); - meterValues.push(transactionEndMeterValue); - return meterValues; + const meterValues: OCPP16MeterValue[] = [] + meterValues.push(transactionBeginMeterValue) + meterValues.push(transactionEndMeterValue) + return meterValues } public static remoteStopTransaction = async ( chargingStation: ChargingStation, - connectorId: number, + connectorId: number ): Promise => { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - OCPP16ChargePointStatus.Finishing, - ); + OCPP16ChargePointStatus.Finishing + ) const stopResponse = await chargingStation.stopTransactionOnConnector( connectorId, - OCPP16StopTransactionReason.REMOTE, - ); + OCPP16StopTransactionReason.REMOTE + ) if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { - return OCPP16Constants.OCPP_RESPONSE_ACCEPTED; + return OCPP16Constants.OCPP_RESPONSE_ACCEPTED } - return OCPP16Constants.OCPP_RESPONSE_REJECTED; - }; + return OCPP16Constants.OCPP_RESPONSE_REJECTED + } public static changeAvailability = async ( chargingStation: ChargingStation, connectorIds: number[], chargePointStatus: OCPP16ChargePointStatus, - availabilityType: OCPP16AvailabilityType, + availabilityType: OCPP16AvailabilityType ): Promise => { - const responses: OCPP16ChangeAvailabilityResponse[] = []; + const responses: OCPP16ChangeAvailabilityResponse[] = [] for (const connectorId of connectorIds) { let response: OCPP16ChangeAvailabilityResponse = - OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; - const connectorStatus = chargingStation.getConnectorStatus(connectorId)!; + OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorStatus = chargingStation.getConnectorStatus(connectorId)! if (connectorStatus?.transactionStarted === true) { - response = OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; + response = OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED } - connectorStatus.availability = availabilityType; + connectorStatus.availability = availabilityType if (response === OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) { await OCPP16ServiceUtils.sendAndSetConnectorStatus( chargingStation, connectorId, - chargePointStatus, - ); + chargePointStatus + ) } - responses.push(response); + responses.push(response) } if (responses.includes(OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED)) { - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED } - return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; - }; + return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED + } - public static setChargingProfile( + public static setChargingProfile ( chargingStation: ChargingStation, connectorId: number, - cp: OCPP16ChargingProfile, + cp: OCPP16ChargingProfile ): void { if (isNullOrUndefined(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { logger.error( - `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an uninitialized charging profiles array attribute, applying deferred initialization`, - ); - chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = []; + `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an uninitialized charging profiles array attribute, applying deferred initialization` + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = [] } - if ( - Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles) === false - ) { + if (!Array.isArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { logger.error( - `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an improper attribute type for the charging profiles array, applying proper type deferred initialization`, - ); - chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = []; + `${chargingStation.logPrefix()} Trying to set a charging profile on connector id ${connectorId} with an improper attribute type for the charging profiles array, applying proper type deferred initialization` + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.chargingProfiles = [] } - let cpReplaced = false; + let cpReplaced = false if (isNotEmptyArray(chargingStation.getConnectorStatus(connectorId)?.chargingProfiles)) { chargingStation .getConnectorStatus(connectorId) @@ -171,110 +173,124 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { (chargingProfile.stackLevel === cp.stackLevel && chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose) ) { - chargingStation.getConnectorStatus(connectorId)!.chargingProfiles![index] = cp; - cpReplaced = true; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.chargingProfiles![index] = cp + cpReplaced = true } - }); + }) } - !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp); + !cpReplaced && chargingStation.getConnectorStatus(connectorId)?.chargingProfiles?.push(cp) } public static clearChargingProfiles = ( chargingStation: ChargingStation, commandPayload: OCPP16ClearChargingProfileRequest, - chargingProfiles: OCPP16ChargingProfile[] | undefined, + chargingProfiles: OCPP16ChargingProfile[] | undefined ): boolean => { - const { id, chargingProfilePurpose, stackLevel } = commandPayload; - let clearedCP = false; + const { id, chargingProfilePurpose, stackLevel } = commandPayload + let clearedCP = false if (isNotEmptyArray(chargingProfiles)) { chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => { - let clearCurrentCP = false; + let clearCurrentCP = false if (chargingProfile.chargingProfileId === id) { - clearCurrentCP = true; + clearCurrentCP = true } - if (!chargingProfilePurpose && chargingProfile.stackLevel === stackLevel) { - clearCurrentCP = true; + if (chargingProfilePurpose == null && chargingProfile.stackLevel === stackLevel) { + clearCurrentCP = true } - if (!stackLevel && chargingProfile.chargingProfilePurpose === chargingProfilePurpose) { - clearCurrentCP = true; + if ( + stackLevel == null && + chargingProfile.chargingProfilePurpose === chargingProfilePurpose + ) { + clearCurrentCP = true } if ( chargingProfile.stackLevel === stackLevel && chargingProfile.chargingProfilePurpose === chargingProfilePurpose ) { - clearCurrentCP = true; + clearCurrentCP = true } if (clearCurrentCP) { - chargingProfiles.splice(index, 1); + chargingProfiles.splice(index, 1) logger.debug( `${chargingStation.logPrefix()} Matching charging profile(s) cleared: %j`, - chargingProfile, - ); - clearedCP = true; + chargingProfile + ) + clearedCP = true } - }); + }) } - return clearedCP; - }; + return clearedCP + } public static composeChargingSchedules = ( chargingScheduleHigher: OCPP16ChargingSchedule | undefined, chargingScheduleLower: OCPP16ChargingSchedule | undefined, - compositeInterval: Interval, + compositeInterval: Interval ): OCPP16ChargingSchedule | undefined => { - if (!chargingScheduleHigher && !chargingScheduleLower) { - return undefined; + if (chargingScheduleHigher == null && chargingScheduleLower == null) { + return undefined } - if (chargingScheduleHigher && !chargingScheduleLower) { - return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher, compositeInterval); + if (chargingScheduleHigher != null && chargingScheduleLower == null) { + return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher, compositeInterval) } - if (!chargingScheduleHigher && chargingScheduleLower) { - return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower, compositeInterval); + if (chargingScheduleHigher == null && chargingScheduleLower != null) { + return OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower, compositeInterval) } const compositeChargingScheduleHigher: OCPP16ChargingSchedule | undefined = - OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher!, compositeInterval); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleHigher!, compositeInterval) const compositeChargingScheduleLower: OCPP16ChargingSchedule | undefined = - OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower!, compositeInterval); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + OCPP16ServiceUtils.composeChargingSchedule(chargingScheduleLower!, compositeInterval) const compositeChargingScheduleHigherInterval: Interval = { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion start: compositeChargingScheduleHigher!.startSchedule!, end: addSeconds( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion compositeChargingScheduleHigher!.startSchedule!, - compositeChargingScheduleHigher!.duration!, - ), - }; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + compositeChargingScheduleHigher!.duration! + ) + } const compositeChargingScheduleLowerInterval: Interval = { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion start: compositeChargingScheduleLower!.startSchedule!, end: addSeconds( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion compositeChargingScheduleLower!.startSchedule!, - compositeChargingScheduleLower!.duration!, - ), - }; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + compositeChargingScheduleLower!.duration! + ) + } const higherFirst = isBefore( compositeChargingScheduleHigherInterval.start, - compositeChargingScheduleLowerInterval.start, - ); + compositeChargingScheduleLowerInterval.start + ) if ( !areIntervalsOverlapping( compositeChargingScheduleHigherInterval, - compositeChargingScheduleLowerInterval, + compositeChargingScheduleLowerInterval ) ) { return { ...compositeChargingScheduleLower, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...compositeChargingScheduleHigher!, startSchedule: higherFirst ? (compositeChargingScheduleHigherInterval.start as Date) : (compositeChargingScheduleLowerInterval.start as Date), duration: higherFirst ? differenceInSeconds( - compositeChargingScheduleLowerInterval.end, - compositeChargingScheduleHigherInterval.start, - ) + compositeChargingScheduleLowerInterval.end, + compositeChargingScheduleHigherInterval.start + ) : differenceInSeconds( - compositeChargingScheduleHigherInterval.end, - compositeChargingScheduleLowerInterval.start, - ), + compositeChargingScheduleHigherInterval.end, + compositeChargingScheduleLowerInterval.start + ), chargingSchedulePeriod: [ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...compositeChargingScheduleHigher!.chargingSchedulePeriod.map((schedulePeriod) => { return { ...schedulePeriod, @@ -283,10 +299,11 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { : schedulePeriod.startPeriod + differenceInSeconds( compositeChargingScheduleHigherInterval.start, - compositeChargingScheduleLowerInterval.start, - ), - }; + compositeChargingScheduleLowerInterval.start + ) + } }), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...compositeChargingScheduleLower!.chargingSchedulePeriod.map((schedulePeriod) => { return { ...schedulePeriod, @@ -294,30 +311,32 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { ? schedulePeriod.startPeriod + differenceInSeconds( compositeChargingScheduleLowerInterval.start, - compositeChargingScheduleHigherInterval.start, + compositeChargingScheduleHigherInterval.start ) - : 0, - }; - }), - ].sort((a, b) => a.startPeriod - b.startPeriod), - }; + : 0 + } + }) + ].sort((a, b) => a.startPeriod - b.startPeriod) + } } return { ...compositeChargingScheduleLower, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...compositeChargingScheduleHigher!, startSchedule: higherFirst ? (compositeChargingScheduleHigherInterval.start as Date) : (compositeChargingScheduleLowerInterval.start as Date), duration: higherFirst ? differenceInSeconds( - compositeChargingScheduleLowerInterval.end, - compositeChargingScheduleHigherInterval.start, - ) + compositeChargingScheduleLowerInterval.end, + compositeChargingScheduleHigherInterval.start + ) : differenceInSeconds( - compositeChargingScheduleHigherInterval.end, - compositeChargingScheduleLowerInterval.start, - ), + compositeChargingScheduleHigherInterval.end, + compositeChargingScheduleLowerInterval.start + ), chargingSchedulePeriod: [ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...compositeChargingScheduleHigher!.chargingSchedulePeriod.map((schedulePeriod) => { return { ...schedulePeriod, @@ -326,10 +345,11 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { : schedulePeriod.startPeriod + differenceInSeconds( compositeChargingScheduleHigherInterval.start, - compositeChargingScheduleLowerInterval.start, - ), - }; + compositeChargingScheduleLowerInterval.start + ) + } }), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ...compositeChargingScheduleLower!.chargingSchedulePeriod .filter((schedulePeriod, index) => { if ( @@ -337,62 +357,64 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { isWithinInterval( addSeconds( compositeChargingScheduleLowerInterval.start, - schedulePeriod.startPeriod, + schedulePeriod.startPeriod ), { start: compositeChargingScheduleLowerInterval.start, - end: compositeChargingScheduleHigherInterval.end, - }, + end: compositeChargingScheduleHigherInterval.end + } ) ) { - return false; + return false } if ( higherFirst && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion index < compositeChargingScheduleLower!.chargingSchedulePeriod.length - 1 && !isWithinInterval( addSeconds( compositeChargingScheduleLowerInterval.start, - schedulePeriod.startPeriod, + schedulePeriod.startPeriod ), { start: compositeChargingScheduleLowerInterval.start, - end: compositeChargingScheduleHigherInterval.end, - }, + end: compositeChargingScheduleHigherInterval.end + } ) && isWithinInterval( addSeconds( compositeChargingScheduleLowerInterval.start, - compositeChargingScheduleLower!.chargingSchedulePeriod[index + 1].startPeriod, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + compositeChargingScheduleLower!.chargingSchedulePeriod[index + 1].startPeriod ), { start: compositeChargingScheduleLowerInterval.start, - end: compositeChargingScheduleHigherInterval.end, - }, + end: compositeChargingScheduleHigherInterval.end + } ) ) { - return false; + return false } if ( !higherFirst && isWithinInterval( addSeconds( compositeChargingScheduleLowerInterval.start, - schedulePeriod.startPeriod, + schedulePeriod.startPeriod ), { start: compositeChargingScheduleHigherInterval.start, - end: compositeChargingScheduleLowerInterval.end, - }, + end: compositeChargingScheduleLowerInterval.end + } ) ) { - return false; + return false } - return true; + return true }) .map((schedulePeriod, index) => { if (index === 0 && schedulePeriod.startPeriod !== 0) { - schedulePeriod.startPeriod = 0; + schedulePeriod.startPeriod = 0 } return { ...schedulePeriod, @@ -400,126 +422,129 @@ export class OCPP16ServiceUtils extends OCPPServiceUtils { ? schedulePeriod.startPeriod + differenceInSeconds( compositeChargingScheduleLowerInterval.start, - compositeChargingScheduleHigherInterval.start, + compositeChargingScheduleHigherInterval.start ) - : 0, - }; - }), - ].sort((a, b) => a.startPeriod - b.startPeriod), - }; - }; + : 0 + } + }) + ].sort((a, b) => a.startPeriod - b.startPeriod) + } + } public static hasReservation = ( chargingStation: ChargingStation, connectorId: number, - idTag: string, + idTag: string ): boolean => { - const connectorReservation = chargingStation.getReservationBy('connectorId', connectorId); - const chargingStationReservation = chargingStation.getReservationBy('connectorId', 0); + const connectorReservation = chargingStation.getReservationBy('connectorId', connectorId) + const chargingStationReservation = chargingStation.getReservationBy('connectorId', 0) if ( (chargingStation.getConnectorStatus(connectorId)?.status === OCPP16ChargePointStatus.Reserved && - connectorReservation && + connectorReservation != null && !hasReservationExpired(connectorReservation) && - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing connectorReservation?.idTag === idTag) || (chargingStation.getConnectorStatus(0)?.status === OCPP16ChargePointStatus.Reserved && - chargingStationReservation && + chargingStationReservation != null && !hasReservationExpired(chargingStationReservation) && chargingStationReservation?.idTag === idTag) ) { logger.debug( `${chargingStation.logPrefix()} Connector id ${connectorId} has a valid reservation for idTag ${idTag}: %j`, - connectorReservation ?? chargingStationReservation, - ); - return true; + connectorReservation ?? chargingStationReservation + ) + return true } - return false; - }; + return false + } public static parseJsonSchemaFile( relativePath: string, moduleName?: string, - methodName?: string, + methodName?: string ): JSONSchemaType { return super.parseJsonSchemaFile( relativePath, OCPPVersion.VERSION_16, moduleName, - methodName, - ); + methodName + ) } - private static composeChargingSchedule = ( + private static readonly composeChargingSchedule = ( chargingSchedule: OCPP16ChargingSchedule, - compositeInterval: Interval, + compositeInterval: Interval ): OCPP16ChargingSchedule | undefined => { const chargingScheduleInterval: Interval = { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion start: chargingSchedule.startSchedule!, - end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!), - }; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + end: addSeconds(chargingSchedule.startSchedule!, chargingSchedule.duration!) + } if (areIntervalsOverlapping(chargingScheduleInterval, compositeInterval)) { - chargingSchedule.chargingSchedulePeriod.sort((a, b) => a.startPeriod - b.startPeriod); + chargingSchedule.chargingSchedulePeriod.sort((a, b) => a.startPeriod - b.startPeriod) if (isBefore(chargingScheduleInterval.start, compositeInterval.start)) { return { ...chargingSchedule, startSchedule: compositeInterval.start as Date, duration: differenceInSeconds( chargingScheduleInterval.end, - compositeInterval.start as Date, + compositeInterval.start as Date ), chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod .filter((schedulePeriod, index) => { if ( isWithinInterval( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!, - compositeInterval, + compositeInterval ) ) { - return true; + return true } if ( index < chargingSchedule.chargingSchedulePeriod.length - 1 && !isWithinInterval( addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod), - compositeInterval, + compositeInterval ) && isWithinInterval( addSeconds( chargingScheduleInterval.start, - chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod, + chargingSchedule.chargingSchedulePeriod[index + 1].startPeriod ), - compositeInterval, + compositeInterval ) ) { - return true; + return true } - return false; + return false }) .map((schedulePeriod, index) => { if (index === 0 && schedulePeriod.startPeriod !== 0) { - schedulePeriod.startPeriod = 0; + schedulePeriod.startPeriod = 0 } - return schedulePeriod; - }), - }; + return schedulePeriod + }) + } } if (isAfter(chargingScheduleInterval.end, compositeInterval.end)) { return { ...chargingSchedule, duration: differenceInSeconds( compositeInterval.end as Date, - chargingScheduleInterval.start, + chargingScheduleInterval.start ), chargingSchedulePeriod: chargingSchedule.chargingSchedulePeriod.filter((schedulePeriod) => isWithinInterval( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion addSeconds(chargingScheduleInterval.start, schedulePeriod.startPeriod)!, - compositeInterval, - ), - ), - }; + compositeInterval + ) + ) + } } - return chargingSchedule; + return chargingSchedule } - }; + } } diff --git a/src/charging-station/ocpp/2.0/OCPP20Constants.ts b/src/charging-station/ocpp/2.0/OCPP20Constants.ts index 3ae368a3..266bac11 100644 --- a/src/charging-station/ocpp/2.0/OCPP20Constants.ts +++ b/src/charging-station/ocpp/2.0/OCPP20Constants.ts @@ -1,8 +1,8 @@ import { type ConnectorStatusTransition, - OCPP20ConnectorStatusEnumType, -} from '../../../types/index.js'; -import { OCPPConstants } from '../OCPPConstants.js'; + OCPP20ConnectorStatusEnumType +} from '../../../types/index.js' +import { OCPPConstants } from '../OCPPConstants.js' export class OCPP20Constants extends OCPPConstants { static readonly ChargingStationStatusTransitions: Readonly = @@ -11,13 +11,13 @@ export class OCPP20Constants extends OCPPConstants { // { from: OCPP20ConnectorStatusEnumType.Available, to: OCPP20ConnectorStatusEnumType.Available }, { from: OCPP20ConnectorStatusEnumType.Available, - to: OCPP20ConnectorStatusEnumType.Unavailable, + to: OCPP20ConnectorStatusEnumType.Unavailable }, { from: OCPP20ConnectorStatusEnumType.Available, to: OCPP20ConnectorStatusEnumType.Faulted }, { to: OCPP20ConnectorStatusEnumType.Unavailable }, { from: OCPP20ConnectorStatusEnumType.Unavailable, - to: OCPP20ConnectorStatusEnumType.Available, + to: OCPP20ConnectorStatusEnumType.Available }, // { // from: OCPP20ConnectorStatusEnumType.Unavailable, @@ -25,16 +25,16 @@ export class OCPP20Constants extends OCPPConstants { // }, { from: OCPP20ConnectorStatusEnumType.Unavailable, - to: OCPP20ConnectorStatusEnumType.Faulted, + to: OCPP20ConnectorStatusEnumType.Faulted }, { to: OCPP20ConnectorStatusEnumType.Faulted }, { from: OCPP20ConnectorStatusEnumType.Faulted, to: OCPP20ConnectorStatusEnumType.Available }, { from: OCPP20ConnectorStatusEnumType.Faulted, - to: OCPP20ConnectorStatusEnumType.Unavailable, - }, + to: OCPP20ConnectorStatusEnumType.Unavailable + } // { from: OCPP20ConnectorStatusEnumType.Faulted, to: OCPP20ConnectorStatusEnumType.Faulted }, - ]); + ]) static readonly ConnectorStatusTransitions: Readonly = Object.freeze( [ @@ -44,7 +44,7 @@ export class OCPP20Constants extends OCPPConstants { { from: OCPP20ConnectorStatusEnumType.Available, to: OCPP20ConnectorStatusEnumType.Reserved }, { from: OCPP20ConnectorStatusEnumType.Available, - to: OCPP20ConnectorStatusEnumType.Unavailable, + to: OCPP20ConnectorStatusEnumType.Unavailable }, { from: OCPP20ConnectorStatusEnumType.Available, to: OCPP20ConnectorStatusEnumType.Faulted }, // { to: OCPP20ConnectorStatusEnumType.Occupied }, @@ -53,7 +53,7 @@ export class OCPP20Constants extends OCPPConstants { // { from: OCPP20ConnectorStatusEnumType.Occupied, to: OCPP20ConnectorStatusEnumType.Reserved }, { from: OCPP20ConnectorStatusEnumType.Occupied, - to: OCPP20ConnectorStatusEnumType.Unavailable, + to: OCPP20ConnectorStatusEnumType.Unavailable }, { from: OCPP20ConnectorStatusEnumType.Occupied, to: OCPP20ConnectorStatusEnumType.Faulted }, // { to: OCPP20ConnectorStatusEnumType.Reserved }, @@ -62,23 +62,23 @@ export class OCPP20Constants extends OCPPConstants { // { from: OCPP20ConnectorStatusEnumType.Reserved, to: OCPP20ConnectorStatusEnumType.Reserved }, { from: OCPP20ConnectorStatusEnumType.Reserved, - to: OCPP20ConnectorStatusEnumType.Unavailable, + to: OCPP20ConnectorStatusEnumType.Unavailable }, { from: OCPP20ConnectorStatusEnumType.Reserved, to: OCPP20ConnectorStatusEnumType.Faulted }, { to: OCPP20ConnectorStatusEnumType.Unavailable }, { from: OCPP20ConnectorStatusEnumType.Unavailable, - to: OCPP20ConnectorStatusEnumType.Available, + to: OCPP20ConnectorStatusEnumType.Available }, { from: OCPP20ConnectorStatusEnumType.Unavailable, - to: OCPP20ConnectorStatusEnumType.Occupied, + to: OCPP20ConnectorStatusEnumType.Occupied }, // { from: OCPP20ConnectorStatusEnumType.Unavailable, to: OCPP20ConnectorStatusEnumType.Reserved }, // { from: OCPP20ConnectorStatusEnumType.Unavailable, to: OCPP20ConnectorStatusEnumType.Unavailable }, { from: OCPP20ConnectorStatusEnumType.Unavailable, - to: OCPP20ConnectorStatusEnumType.Faulted, + to: OCPP20ConnectorStatusEnumType.Faulted }, { to: OCPP20ConnectorStatusEnumType.Faulted }, { from: OCPP20ConnectorStatusEnumType.Faulted, to: OCPP20ConnectorStatusEnumType.Available }, @@ -86,9 +86,9 @@ export class OCPP20Constants extends OCPPConstants { { from: OCPP20ConnectorStatusEnumType.Faulted, to: OCPP20ConnectorStatusEnumType.Reserved }, { from: OCPP20ConnectorStatusEnumType.Faulted, - to: OCPP20ConnectorStatusEnumType.Unavailable, - }, + to: OCPP20ConnectorStatusEnumType.Unavailable + } // { from: OCPP20ConnectorStatusEnumType.Faulted, to: OCPP20ConnectorStatusEnumType.Faulted }, - ], - ); + ] + ) } diff --git a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts index a48c7e9d..e0b8fa54 100644 --- a/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts @@ -1,62 +1,65 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import type { JSONSchemaType } from 'ajv'; +import type { JSONSchemaType } from 'ajv' -import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'; -import type { ChargingStation } from '../../../charging-station/index.js'; -import { OCPPError } from '../../../exception/index.js'; +import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js' +import type { ChargingStation } from '../../../charging-station/index.js' +import { OCPPError } from '../../../exception/index.js' import { ErrorType, type IncomingRequestHandler, type JsonType, type OCPP20ClearCacheRequest, OCPP20IncomingRequestCommand, - OCPPVersion, -} from '../../../types/index.js'; -import { logger } from '../../../utils/index.js'; -import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js'; + OCPPVersion +} from '../../../types/index.js' +import { logger } from '../../../utils/index.js' +import { OCPPIncomingRequestService } from '../OCPPIncomingRequestService.js' -const moduleName = 'OCPP20IncomingRequestService'; +const moduleName = 'OCPP20IncomingRequestService' export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { - protected jsonSchemas: Map>; - private incomingRequestHandlers: Map; + protected jsonSchemas: Map> + private readonly incomingRequestHandlers: Map< + OCPP20IncomingRequestCommand, + IncomingRequestHandler + > - public constructor() { + public constructor () { // if (new.target?.name === moduleName) { - // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); + // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`) // } - super(OCPPVersion.VERSION_20); + super(OCPPVersion.VERSION_20) this.incomingRequestHandlers = new Map([ - [OCPP20IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)], - ]); + [OCPP20IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)] + ]) this.jsonSchemas = new Map>([ [ OCPP20IncomingRequestCommand.CLEAR_CACHE, OCPP20ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/2.0/ClearCacheRequest.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.validatePayload = this.validatePayload.bind(this) as ( chargingStation: ChargingStation, commandName: OCPP20IncomingRequestCommand, - commandPayload: JsonType, - ) => boolean; + commandPayload: JsonType + ) => boolean } public async incomingRequestHandler( chargingStation: ChargingStation, messageId: string, commandName: OCPP20IncomingRequestCommand, - commandPayload: ReqType, + commandPayload: ReqType ): Promise { - let response: ResType; + let response: ResType if ( chargingStation.stationInfo?.ocppStrictCompliance === true && - chargingStation.inPendingState() === true && + chargingStation.inPendingState() && (commandName === OCPP20IncomingRequestCommand.REQUEST_START_TRANSACTION || commandName === OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION) ) { @@ -65,35 +68,36 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { `${commandName} cannot be issued to handle request PDU ${JSON.stringify( commandPayload, undefined, - 2, + 2 )} while the charging station is in pending state on the central server`, commandName, - commandPayload, - ); + commandPayload + ) } if ( - chargingStation.isRegistered() === true || + chargingStation.isRegistered() || (chargingStation.stationInfo?.ocppStrictCompliance === false && - chargingStation.inUnknownState() === true) + chargingStation.inUnknownState()) ) { if ( - this.incomingRequestHandlers.has(commandName) === true && - OCPP20ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) === true + this.incomingRequestHandlers.has(commandName) && + OCPP20ServiceUtils.isIncomingRequestCommandSupported(chargingStation, commandName) ) { try { - this.validatePayload(chargingStation, commandName, commandPayload); + this.validatePayload(chargingStation, commandName, commandPayload) // Call the method to build the response + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion response = (await this.incomingRequestHandlers.get(commandName)!( chargingStation, - commandPayload, - )) as ResType; + commandPayload + )) as ResType } catch (error) { // Log logger.error( `${chargingStation.logPrefix()} ${moduleName}.incomingRequestHandler: Handle incoming request error:`, - error, - ); - throw error; + error + ) + throw error } } else { // Throw exception @@ -102,11 +106,11 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { `${commandName} is not implemented to handle request PDU ${JSON.stringify( commandPayload, undefined, - 2, + 2 )}`, commandName, - commandPayload, - ); + commandPayload + ) } } else { throw new OCPPError( @@ -114,37 +118,38 @@ export class OCPP20IncomingRequestService extends OCPPIncomingRequestService { `${commandName} cannot be issued to handle request PDU ${JSON.stringify( commandPayload, undefined, - 2, + 2 )} while the charging station is not registered on the central server.`, commandName, - commandPayload, - ); + commandPayload + ) } // Send the built response await chargingStation.ocppRequestService.sendResponse( chargingStation, messageId, response, - commandName, - ); + commandName + ) } - private validatePayload( + private validatePayload ( chargingStation: ChargingStation, commandName: OCPP20IncomingRequestCommand, - commandPayload: JsonType, + commandPayload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName) === true) { + if (this.jsonSchemas.has(commandName)) { return this.validateIncomingRequestPayload( chargingStation, commandName, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.jsonSchemas.get(commandName)!, - commandPayload, - ); + commandPayload + ) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`, - ); - return false; + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + ) + return false } } diff --git a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts index 60b44142..a9a73028 100644 --- a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts @@ -1,11 +1,11 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import type { JSONSchemaType } from 'ajv'; +import type { JSONSchemaType } from 'ajv' -import { OCPP20Constants } from './OCPP20Constants.js'; -import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'; -import type { ChargingStation } from '../../../charging-station/index.js'; -import { OCPPError } from '../../../exception/index.js'; +import { OCPP20Constants } from './OCPP20Constants.js' +import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js' +import type { ChargingStation } from '../../../charging-station/index.js' +import { OCPPError } from '../../../exception/index.js' import { ErrorType, type JsonObject, @@ -15,96 +15,96 @@ import { OCPP20RequestCommand, type OCPP20StatusNotificationRequest, OCPPVersion, - type RequestParams, -} from '../../../types/index.js'; -import { generateUUID } from '../../../utils/index.js'; -import { OCPPRequestService } from '../OCPPRequestService.js'; -import type { OCPPResponseService } from '../OCPPResponseService.js'; + type RequestParams +} from '../../../types/index.js' +import { generateUUID } from '../../../utils/index.js' +import { OCPPRequestService } from '../OCPPRequestService.js' +import type { OCPPResponseService } from '../OCPPResponseService.js' -const moduleName = 'OCPP20RequestService'; +const moduleName = 'OCPP20RequestService' export class OCPP20RequestService extends OCPPRequestService { - protected jsonSchemas: Map>; + protected jsonSchemas: Map> - public constructor(ocppResponseService: OCPPResponseService) { + public constructor (ocppResponseService: OCPPResponseService) { // if (new.target?.name === moduleName) { - // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); + // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`) // } - super(OCPPVersion.VERSION_20, ocppResponseService); + super(OCPPVersion.VERSION_20, ocppResponseService) this.jsonSchemas = new Map>([ [ OCPP20RequestCommand.BOOT_NOTIFICATION, OCPP20ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/2.0/BootNotificationRequest.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP20RequestCommand.HEARTBEAT, OCPP20ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/2.0/HeartbeatRequest.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP20RequestCommand.STATUS_NOTIFICATION, OCPP20ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/2.0/StatusNotificationRequest.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.buildRequestPayload = this.buildRequestPayload.bind(this) as ( chargingStation: ChargingStation, commandName: OCPP20RequestCommand, - commandParams?: JsonType, - ) => Request; + commandParams?: JsonType + ) => Request } public async requestHandler( chargingStation: ChargingStation, commandName: OCPP20RequestCommand, commandParams?: JsonType, - params?: RequestParams, + params?: RequestParams ): Promise { // FIXME?: add sanity checks on charging station availability, connector availability, connector status, etc. - if (OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true) { + if (OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName)) { return (await this.sendMessage( chargingStation, generateUUID(), this.buildRequestPayload(chargingStation, commandName, commandParams), commandName, - params, - )) as ResponseType; + params + )) as ResponseType } // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). throw new OCPPError( ErrorType.NOT_SUPPORTED, `Unsupported OCPP command '${commandName}'`, commandName, - commandParams, - ); + commandParams + ) } private buildRequestPayload( chargingStation: ChargingStation, commandName: OCPP20RequestCommand, - commandParams?: JsonType, + commandParams?: JsonType ): Request { - commandParams = commandParams as JsonObject; + commandParams = commandParams as JsonObject switch (commandName) { case OCPP20RequestCommand.BOOT_NOTIFICATION: - return commandParams as unknown as Request; + return commandParams as unknown as Request case OCPP20RequestCommand.HEARTBEAT: - return OCPP20Constants.OCPP_RESPONSE_EMPTY as unknown as Request; + return OCPP20Constants.OCPP_RESPONSE_EMPTY as unknown as Request case OCPP20RequestCommand.STATUS_NOTIFICATION: return { timestamp: new Date(), - ...commandParams, - } as unknown as Request; + ...commandParams + } as unknown as Request default: // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). throw new OCPPError( @@ -112,8 +112,8 @@ export class OCPP20RequestService extends OCPPRequestService { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions `Unsupported OCPP command '${commandName}'`, commandName, - commandParams, - ); + commandParams + ) } } } diff --git a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts index 98f2afb6..c8345676 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts @@ -1,10 +1,10 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import type { JSONSchemaType } from 'ajv'; +import type { JSONSchemaType } from 'ajv' -import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js'; -import { type ChargingStation, addConfigurationKey } from '../../../charging-station/index.js'; -import { OCPPError } from '../../../exception/index.js'; +import { OCPP20ServiceUtils } from './OCPP20ServiceUtils.js' +import { type ChargingStation, addConfigurationKey } from '../../../charging-station/index.js' +import { OCPPError } from '../../../exception/index.js' import { ErrorType, type JsonType, @@ -17,104 +17,102 @@ import { type OCPP20StatusNotificationResponse, OCPPVersion, RegistrationStatusEnumType, - type ResponseHandler, -} from '../../../types/index.js'; -import { logger } from '../../../utils/index.js'; -import { OCPPResponseService } from '../OCPPResponseService.js'; + type ResponseHandler +} from '../../../types/index.js' +import { logger } from '../../../utils/index.js' +import { OCPPResponseService } from '../OCPPResponseService.js' -const moduleName = 'OCPP20ResponseService'; +const moduleName = 'OCPP20ResponseService' export class OCPP20ResponseService extends OCPPResponseService { public jsonIncomingRequestResponseSchemas: Map< - OCPP20IncomingRequestCommand, - JSONSchemaType - >; + OCPP20IncomingRequestCommand, + JSONSchemaType + > - private responseHandlers: Map; - private jsonSchemas: Map>; + private readonly responseHandlers: Map + private readonly jsonSchemas: Map> - public constructor() { + public constructor () { // if (new.target?.name === moduleName) { - // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`); + // throw new TypeError(`Cannot construct ${new.target?.name} instances directly`) // } - super(OCPPVersion.VERSION_20); + super(OCPPVersion.VERSION_20) this.responseHandlers = new Map([ [ OCPP20RequestCommand.BOOT_NOTIFICATION, - this.handleResponseBootNotification.bind(this) as ResponseHandler, + this.handleResponseBootNotification.bind(this) as ResponseHandler ], [OCPP20RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this) as ResponseHandler], [ OCPP20RequestCommand.STATUS_NOTIFICATION, - this.emptyResponseHandler.bind(this) as ResponseHandler, - ], - ]); + this.emptyResponseHandler.bind(this) as ResponseHandler + ] + ]) this.jsonSchemas = new Map>([ [ OCPP20RequestCommand.BOOT_NOTIFICATION, OCPP20ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/2.0/BootNotificationResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP20RequestCommand.HEARTBEAT, OCPP20ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/2.0/HeartbeatResponse.json', moduleName, - 'constructor', - ), + 'constructor' + ) ], [ OCPP20RequestCommand.STATUS_NOTIFICATION, OCPP20ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.jsonIncomingRequestResponseSchemas = new Map([ [ OCPP20IncomingRequestCommand.CLEAR_CACHE, OCPP20ServiceUtils.parseJsonSchemaFile( 'assets/json-schemas/ocpp/2.0/ClearCacheResponse.json', moduleName, - 'constructor', - ), - ], - ]); + 'constructor' + ) + ] + ]) this.validatePayload = this.validatePayload.bind(this) as ( chargingStation: ChargingStation, commandName: OCPP20RequestCommand, - payload: JsonType, - ) => boolean; + payload: JsonType + ) => boolean } public async responseHandler( chargingStation: ChargingStation, commandName: OCPP20RequestCommand, payload: ResType, - requestPayload: ReqType, + requestPayload: ReqType ): Promise { - if ( - chargingStation.isRegistered() === true || - commandName === OCPP20RequestCommand.BOOT_NOTIFICATION - ) { + if (chargingStation.isRegistered() || commandName === OCPP20RequestCommand.BOOT_NOTIFICATION) { if ( - this.responseHandlers.has(commandName) === true && - OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName) === true + this.responseHandlers.has(commandName) && + OCPP20ServiceUtils.isRequestCommandSupported(chargingStation, commandName) ) { try { - this.validatePayload(chargingStation, commandName, payload); - await this.responseHandlers.get(commandName)!(chargingStation, payload, requestPayload); + this.validatePayload(chargingStation, commandName, payload) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await this.responseHandlers.get(commandName)!(chargingStation, payload, requestPayload) } catch (error) { logger.error( `${chargingStation.logPrefix()} ${moduleName}.responseHandler: Handle response error:`, - error, - ); - throw error; + error + ) + throw error } } else { // Throw exception @@ -123,11 +121,11 @@ export class OCPP20ResponseService extends OCPPResponseService { `${commandName} is not implemented to handle response PDU ${JSON.stringify( payload, undefined, - 2, + 2 )}`, commandName, - payload, - ); + payload + ) } } else { throw new OCPPError( @@ -135,36 +133,37 @@ export class OCPP20ResponseService extends OCPPResponseService { `${commandName} cannot be issued to handle response PDU ${JSON.stringify( payload, undefined, - 2, + 2 )} while the charging station is not registered on the central server.`, commandName, - payload, - ); + payload + ) } } - private validatePayload( + private validatePayload ( chargingStation: ChargingStation, commandName: OCPP20RequestCommand, - payload: JsonType, + payload: JsonType ): boolean { - if (this.jsonSchemas.has(commandName) === true) { + if (this.jsonSchemas.has(commandName)) { return this.validateResponsePayload( chargingStation, commandName, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.jsonSchemas.get(commandName)!, - payload, - ); + payload + ) } logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation`, - ); - return false; + `${chargingStation.logPrefix()} ${moduleName}.validatePayload: No JSON schema found for command '${commandName}' PDU validation` + ) + return false } - private handleResponseBootNotification( + private handleResponseBootNotification ( chargingStation: ChargingStation, - payload: OCPP20BootNotificationResponse, + payload: OCPP20BootNotificationResponse ): void { if (payload.status === RegistrationStatusEnumType.ACCEPTED) { addConfigurationKey( @@ -172,22 +171,22 @@ export class OCPP20ResponseService extends OCPPResponseService { OCPP20OptionalVariableName.HeartbeatInterval, payload.interval.toString(), {}, - { overwrite: true, save: true }, - ); - OCPP20ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval); + { overwrite: true, save: true } + ) + OCPP20ServiceUtils.startHeartbeatInterval(chargingStation, payload.interval) } if (Object.values(RegistrationStatusEnumType).includes(payload.status)) { const logMsg = `${chargingStation.logPrefix()} Charging station in '${ payload.status - }' state on the central server`; + }' state on the central server` payload.status === RegistrationStatusEnumType.REJECTED ? logger.warn(logMsg) - : logger.info(logMsg); + : logger.info(logMsg) } else { logger.error( `${chargingStation.logPrefix()} Charging station boot notification response received: %j with undefined registration status`, - payload, - ); + payload + ) } } } diff --git a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts index eb4d099b..43d3e2f6 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ServiceUtils.ts @@ -1,21 +1,21 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import type { JSONSchemaType } from 'ajv'; +import type { JSONSchemaType } from 'ajv' -import { type JsonType, OCPPVersion } from '../../../types/index.js'; -import { OCPPServiceUtils } from '../OCPPServiceUtils.js'; +import { type JsonType, OCPPVersion } from '../../../types/index.js' +import { OCPPServiceUtils } from '../OCPPServiceUtils.js' export class OCPP20ServiceUtils extends OCPPServiceUtils { public static parseJsonSchemaFile( relativePath: string, moduleName?: string, - methodName?: string, + methodName?: string ): JSONSchemaType { return super.parseJsonSchemaFile( relativePath, OCPPVersion.VERSION_20, moduleName, - methodName, - ); + methodName + ) } } diff --git a/src/charging-station/ocpp/OCPPConstants.ts b/src/charging-station/ocpp/OCPPConstants.ts index 8f5f98d6..40807c88 100644 --- a/src/charging-station/ocpp/OCPPConstants.ts +++ b/src/charging-station/ocpp/OCPPConstants.ts @@ -8,137 +8,138 @@ import { MeterValueMeasurand, ReservationStatus, TriggerMessageStatus, - UnlockStatus, -} from '../../types/index.js'; -import { Constants } from '../../utils/index.js'; + UnlockStatus +} from '../../types/index.js' +import { Constants } from '../../utils/index.js' +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class OCPPConstants { - static readonly OCPP_WEBSOCKET_TIMEOUT = 60000; // Ms - static readonly OCPP_TRIGGER_MESSAGE_DELAY = 500; // Ms + static readonly OCPP_WEBSOCKET_TIMEOUT = 60000 // Ms + static readonly OCPP_TRIGGER_MESSAGE_DELAY = 500 // Ms static readonly OCPP_MEASURANDS_SUPPORTED = Object.freeze([ MeterValueMeasurand.STATE_OF_CHARGE, MeterValueMeasurand.VOLTAGE, MeterValueMeasurand.POWER_ACTIVE_IMPORT, MeterValueMeasurand.CURRENT_IMPORT, - MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, - ]); + MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + ]) - static readonly OCPP_REQUEST_EMPTY = Constants.EMPTY_FROZEN_OBJECT; - static readonly OCPP_RESPONSE_EMPTY = Constants.EMPTY_FROZEN_OBJECT; - static readonly OCPP_RESPONSE_ACCEPTED = Object.freeze({ status: GenericStatus.Accepted }); - static readonly OCPP_RESPONSE_REJECTED = Object.freeze({ status: GenericStatus.Rejected }); + static readonly OCPP_REQUEST_EMPTY = Constants.EMPTY_FROZEN_OBJECT + static readonly OCPP_RESPONSE_EMPTY = Constants.EMPTY_FROZEN_OBJECT + static readonly OCPP_RESPONSE_ACCEPTED = Object.freeze({ status: GenericStatus.Accepted }) + static readonly OCPP_RESPONSE_REJECTED = Object.freeze({ status: GenericStatus.Rejected }) static readonly OCPP_CONFIGURATION_RESPONSE_ACCEPTED = Object.freeze({ - status: ConfigurationStatus.ACCEPTED, - }); + status: ConfigurationStatus.ACCEPTED + }) static readonly OCPP_CONFIGURATION_RESPONSE_REJECTED = Object.freeze({ - status: ConfigurationStatus.REJECTED, - }); + status: ConfigurationStatus.REJECTED + }) static readonly OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED = Object.freeze({ - status: ConfigurationStatus.REBOOT_REQUIRED, - }); + status: ConfigurationStatus.REBOOT_REQUIRED + }) static readonly OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED = Object.freeze({ - status: ConfigurationStatus.NOT_SUPPORTED, - }); + status: ConfigurationStatus.NOT_SUPPORTED + }) static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED = Object.freeze({ - status: ChargingProfileStatus.ACCEPTED, - }); + status: ChargingProfileStatus.ACCEPTED + }) static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED = Object.freeze({ - status: ChargingProfileStatus.REJECTED, - }); + status: ChargingProfileStatus.REJECTED + }) static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED = Object.freeze({ - status: ChargingProfileStatus.NOT_SUPPORTED, - }); + status: ChargingProfileStatus.NOT_SUPPORTED + }) static readonly OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED = Object.freeze({ - status: ClearChargingProfileStatus.ACCEPTED, - }); + status: ClearChargingProfileStatus.ACCEPTED + }) static readonly OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN = Object.freeze({ - status: ClearChargingProfileStatus.UNKNOWN, - }); + status: ClearChargingProfileStatus.UNKNOWN + }) - static readonly OCPP_RESPONSE_UNLOCKED = Object.freeze({ status: UnlockStatus.UNLOCKED }); + static readonly OCPP_RESPONSE_UNLOCKED = Object.freeze({ status: UnlockStatus.UNLOCKED }) static readonly OCPP_RESPONSE_UNLOCK_FAILED = Object.freeze({ - status: UnlockStatus.UNLOCK_FAILED, - }); + status: UnlockStatus.UNLOCK_FAILED + }) static readonly OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED = Object.freeze({ - status: UnlockStatus.NOT_SUPPORTED, - }); + status: UnlockStatus.NOT_SUPPORTED + }) static readonly OCPP_AVAILABILITY_RESPONSE_ACCEPTED = Object.freeze({ - status: AvailabilityStatus.ACCEPTED, - }); + status: AvailabilityStatus.ACCEPTED + }) static readonly OCPP_AVAILABILITY_RESPONSE_REJECTED = Object.freeze({ - status: AvailabilityStatus.REJECTED, - }); + status: AvailabilityStatus.REJECTED + }) static readonly OCPP_AVAILABILITY_RESPONSE_SCHEDULED = Object.freeze({ - status: AvailabilityStatus.SCHEDULED, - }); + status: AvailabilityStatus.SCHEDULED + }) static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED = Object.freeze({ - status: TriggerMessageStatus.ACCEPTED, - }); + status: TriggerMessageStatus.ACCEPTED + }) static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED = Object.freeze({ - status: TriggerMessageStatus.REJECTED, - }); + status: TriggerMessageStatus.REJECTED + }) static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED = Object.freeze({ - status: TriggerMessageStatus.NOT_IMPLEMENTED, - }); + status: TriggerMessageStatus.NOT_IMPLEMENTED + }) static readonly OCPP_DATA_TRANSFER_RESPONSE_ACCEPTED = Object.freeze({ - status: DataTransferStatus.ACCEPTED, - }); + status: DataTransferStatus.ACCEPTED + }) static readonly OCPP_DATA_TRANSFER_RESPONSE_REJECTED = Object.freeze({ - status: DataTransferStatus.REJECTED, - }); + status: DataTransferStatus.REJECTED + }) static readonly OCPP_DATA_TRANSFER_RESPONSE_UNKNOWN_VENDOR_ID = Object.freeze({ - status: DataTransferStatus.UNKNOWN_VENDOR_ID, - }); + status: DataTransferStatus.UNKNOWN_VENDOR_ID + }) static readonly OCPP_RESERVATION_RESPONSE_ACCEPTED = Object.freeze({ - status: ReservationStatus.ACCEPTED, - }); // Reservation has been made + status: ReservationStatus.ACCEPTED + }) // Reservation has been made static readonly OCPP_RESERVATION_RESPONSE_FAULTED = Object.freeze({ - status: ReservationStatus.FAULTED, - }); // Reservation has not been made, because of connector in FAULTED state + status: ReservationStatus.FAULTED + }) // Reservation has not been made, because of connector in FAULTED state static readonly OCPP_RESERVATION_RESPONSE_OCCUPIED = Object.freeze({ - status: ReservationStatus.OCCUPIED, - }); // Reservation has not been made, because all connectors are OCCUPIED + status: ReservationStatus.OCCUPIED + }) // Reservation has not been made, because all connectors are OCCUPIED static readonly OCPP_RESERVATION_RESPONSE_REJECTED = Object.freeze({ - status: ReservationStatus.REJECTED, - }); // Reservation has not been made, because charging station is not configured to accept reservations + status: ReservationStatus.REJECTED + }) // Reservation has not been made, because charging station is not configured to accept reservations static readonly OCPP_RESERVATION_RESPONSE_UNAVAILABLE = Object.freeze({ - status: ReservationStatus.UNAVAILABLE, - }); // Reservation has not been made, because connector is in UNAVAILABLE state + status: ReservationStatus.UNAVAILABLE + }) // Reservation has not been made, because connector is in UNAVAILABLE state static readonly OCPP_CANCEL_RESERVATION_RESPONSE_ACCEPTED = Object.freeze({ - status: GenericStatus.Accepted, - }); // Reservation for id has been cancelled + status: GenericStatus.Accepted + }) // Reservation for id has been cancelled static readonly OCPP_CANCEL_RESERVATION_RESPONSE_REJECTED = Object.freeze({ - status: GenericStatus.Rejected, - }); // Reservation could not be cancelled, because there is no reservation active for id + status: GenericStatus.Rejected + }) // Reservation could not be cancelled, because there is no reservation active for id - protected constructor() { + protected constructor () { // This is intentional } } diff --git a/src/charging-station/ocpp/OCPPIncomingRequestService.ts b/src/charging-station/ocpp/OCPPIncomingRequestService.ts index 9d555b56..ba224189 100644 --- a/src/charging-station/ocpp/OCPPIncomingRequestService.ts +++ b/src/charging-station/ocpp/OCPPIncomingRequestService.ts @@ -1,85 +1,86 @@ -import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv'; -import _ajvFormats from 'ajv-formats'; +import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv' +import _ajvFormats from 'ajv-formats' -import { OCPPConstants } from './OCPPConstants.js'; -import { OCPPServiceUtils } from './OCPPServiceUtils.js'; -import { type ChargingStation, getIdTagsFile } from '../../charging-station/index.js'; -import { OCPPError } from '../../exception/index.js'; +import { OCPPConstants } from './OCPPConstants.js' +import { OCPPServiceUtils } from './OCPPServiceUtils.js' +import { type ChargingStation, getIdTagsFile } from '../../charging-station/index.js' +import { OCPPError } from '../../exception/index.js' import type { ClearCacheResponse, HandleErrorParams, IncomingRequestCommand, JsonType, - OCPPVersion, -} from '../../types/index.js'; -import { logger, setDefaultErrorParams } from '../../utils/index.js'; -type Ajv = _Ajv.default; -const Ajv = _Ajv.default; -const ajvFormats = _ajvFormats.default; + OCPPVersion +} from '../../types/index.js' +import { logger, setDefaultErrorParams } from '../../utils/index.js' +type Ajv = _Ajv.default +// eslint-disable-next-line @typescript-eslint/no-redeclare +const Ajv = _Ajv.default +const ajvFormats = _ajvFormats.default -const moduleName = 'OCPPIncomingRequestService'; +const moduleName = 'OCPPIncomingRequestService' export abstract class OCPPIncomingRequestService { - private static instance: OCPPIncomingRequestService | null = null; - private readonly version: OCPPVersion; - private readonly ajv: Ajv; - private jsonValidateFunctions: Map>; - protected abstract jsonSchemas: Map>; + private static instance: OCPPIncomingRequestService | null = null + private readonly version: OCPPVersion + private readonly ajv: Ajv + private readonly jsonValidateFunctions: Map> + protected abstract jsonSchemas: Map> - protected constructor(version: OCPPVersion) { - this.version = version; + protected constructor (version: OCPPVersion) { + this.version = version this.ajv = new Ajv({ keywords: ['javaType'], - multipleOfPrecision: 2, - }); - ajvFormats(this.ajv); - this.jsonValidateFunctions = new Map>(); + multipleOfPrecision: 2 + }) + ajvFormats(this.ajv) + this.jsonValidateFunctions = new Map>() this.incomingRequestHandler = this.incomingRequestHandler.bind(this) as < ReqType extends JsonType, // eslint-disable-next-line @typescript-eslint/no-unused-vars - ResType extends JsonType, + ResType extends JsonType >( chargingStation: ChargingStation, messageId: string, commandName: IncomingRequestCommand, - commandPayload: ReqType, - ) => Promise; + commandPayload: ReqType + ) => Promise this.validateIncomingRequestPayload = this.validateIncomingRequestPayload.bind(this) as < - T extends JsonType, + T extends JsonType >( chargingStation: ChargingStation, commandName: IncomingRequestCommand, schema: JSONSchemaType, - payload: T, - ) => boolean; + payload: T + ) => boolean } public static getInstance(this: new () => T): T { if (OCPPIncomingRequestService.instance === null) { - OCPPIncomingRequestService.instance = new this(); + OCPPIncomingRequestService.instance = new this() } - return OCPPIncomingRequestService.instance as T; + return OCPPIncomingRequestService.instance as T } protected handleIncomingRequestError( chargingStation: ChargingStation, commandName: IncomingRequestCommand, error: Error, - params: HandleErrorParams = { throwError: true, consoleOut: false }, + params: HandleErrorParams = { throwError: true, consoleOut: false } ): T | undefined { - setDefaultErrorParams(params); + setDefaultErrorParams(params) logger.error( `${chargingStation.logPrefix()} ${moduleName}.handleIncomingRequestError: Incoming request command '${commandName}' error:`, - error, - ); - if (!params?.throwError && params?.errorResponse) { - return params?.errorResponse; + error + ) + if (params?.throwError === false && params?.errorResponse != null) { + return params?.errorResponse } - if (params?.throwError && !params?.errorResponse) { - throw error; + if (params?.throwError === true && params?.errorResponse == null) { + throw error } - if (params?.throwError && params?.errorResponse) { - return params?.errorResponse; + if (params?.throwError === true && params?.errorResponse != null) { + return params?.errorResponse } } @@ -87,42 +88,44 @@ export abstract class OCPPIncomingRequestService { chargingStation: ChargingStation, commandName: IncomingRequestCommand, schema: JSONSchemaType, - payload: T, + payload: T ): boolean { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { - return true; + return true } - const validate = this.getJsonIncomingRequestValidateFunction(commandName, schema); + const validate = this.getJsonIncomingRequestValidateFunction(commandName, schema) if (validate(payload)) { - return true; + return true } logger.error( `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestPayload: Command '${commandName}' incoming request PDU is invalid: %j`, - validate.errors, - ); + validate.errors + ) throw new OCPPError( OCPPServiceUtils.ajvErrorsToErrorType(validate.errors), 'Incoming request PDU is invalid', commandName, - JSON.stringify(validate.errors, undefined, 2), - ); + JSON.stringify(validate.errors, undefined, 2) + ) } - protected handleRequestClearCache(chargingStation: ChargingStation): ClearCacheResponse { + protected handleRequestClearCache (chargingStation: ChargingStation): ClearCacheResponse { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (chargingStation.idTagsCache.deleteIdTags(getIdTagsFile(chargingStation.stationInfo)!)) { - return OCPPConstants.OCPP_RESPONSE_ACCEPTED; + return OCPPConstants.OCPP_RESPONSE_ACCEPTED } - return OCPPConstants.OCPP_RESPONSE_REJECTED; + return OCPPConstants.OCPP_RESPONSE_REJECTED } private getJsonIncomingRequestValidateFunction( commandName: IncomingRequestCommand, - schema: JSONSchemaType, - ) { - if (this.jsonValidateFunctions.has(commandName) === false) { - this.jsonValidateFunctions.set(commandName, this.ajv.compile(schema).bind(this)); + schema: JSONSchemaType + ): ValidateFunction { + if (!this.jsonValidateFunctions.has(commandName)) { + this.jsonValidateFunctions.set(commandName, this.ajv.compile(schema).bind(this)) } - return this.jsonValidateFunctions.get(commandName)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.jsonValidateFunctions.get(commandName)! } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -130,6 +133,6 @@ export abstract class OCPPIncomingRequestService { chargingStation: ChargingStation, messageId: string, commandName: IncomingRequestCommand, - commandPayload: ReqType, - ): Promise; + commandPayload: ReqType + ): Promise } diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index 8b5f0cc3..67b93afe 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -1,12 +1,12 @@ -import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv'; -import _ajvFormats from 'ajv-formats'; +import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv' +import _ajvFormats from 'ajv-formats' -import { OCPPConstants } from './OCPPConstants.js'; -import type { OCPPResponseService } from './OCPPResponseService.js'; -import { OCPPServiceUtils } from './OCPPServiceUtils.js'; -import type { ChargingStation } from '../../charging-station/index.js'; -import { OCPPError } from '../../exception/index.js'; -import { PerformanceStatistics } from '../../performance/index.js'; +import { OCPPConstants } from './OCPPConstants.js' +import type { OCPPResponseService } from './OCPPResponseService.js' +import { OCPPServiceUtils } from './OCPPServiceUtils.js' +import type { ChargingStation } from '../../charging-station/index.js' +import { OCPPError } from '../../exception/index.js' +import { PerformanceStatistics } from '../../performance/index.js' import { ChargingStationEvents, type ErrorCallback, @@ -21,117 +21,118 @@ import { type RequestParams, type Response, type ResponseCallback, - type ResponseType, -} from '../../types/index.js'; + type ResponseType +} from '../../types/index.js' import { cloneObject, formatDurationMilliSeconds, handleSendMessageError, isNullOrUndefined, - logger, -} from '../../utils/index.js'; -type Ajv = _Ajv.default; -const Ajv = _Ajv.default; -const ajvFormats = _ajvFormats.default; + logger +} from '../../utils/index.js' +type Ajv = _Ajv.default +// eslint-disable-next-line @typescript-eslint/no-redeclare +const Ajv = _Ajv.default +const ajvFormats = _ajvFormats.default -const moduleName = 'OCPPRequestService'; +const moduleName = 'OCPPRequestService' const defaultRequestParams: RequestParams = { skipBufferingOnError: false, triggerMessage: false, - throwError: false, -}; + throwError: false +} export abstract class OCPPRequestService { - private static instance: OCPPRequestService | null = null; - private readonly version: OCPPVersion; - private readonly ajv: Ajv; - private readonly ocppResponseService: OCPPResponseService; - private readonly jsonValidateFunctions: Map>; - protected abstract jsonSchemas: Map>; + private static instance: OCPPRequestService | null = null + private readonly version: OCPPVersion + private readonly ajv: Ajv + private readonly ocppResponseService: OCPPResponseService + private readonly jsonValidateFunctions: Map> + protected abstract jsonSchemas: Map> - protected constructor(version: OCPPVersion, ocppResponseService: OCPPResponseService) { - this.version = version; + protected constructor (version: OCPPVersion, ocppResponseService: OCPPResponseService) { + this.version = version this.ajv = new Ajv({ keywords: ['javaType'], - multipleOfPrecision: 2, - }); - ajvFormats(this.ajv); - this.jsonValidateFunctions = new Map>(); - this.ocppResponseService = ocppResponseService; + multipleOfPrecision: 2 + }) + ajvFormats(this.ajv) + this.jsonValidateFunctions = new Map>() + this.ocppResponseService = ocppResponseService this.requestHandler = this.requestHandler.bind(this) as < // eslint-disable-next-line @typescript-eslint/no-unused-vars ReqType extends JsonType, - ResType extends JsonType, + ResType extends JsonType >( chargingStation: ChargingStation, commandName: RequestCommand, commandParams?: JsonType, - params?: RequestParams, - ) => Promise; + params?: RequestParams + ) => Promise this.sendMessage = this.sendMessage.bind(this) as ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType, commandName: RequestCommand, - params?: RequestParams, - ) => Promise; + params?: RequestParams + ) => Promise this.sendResponse = this.sendResponse.bind(this) as ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType, - commandName: IncomingRequestCommand, - ) => Promise; + commandName: IncomingRequestCommand + ) => Promise this.sendError = this.sendError.bind(this) as ( chargingStation: ChargingStation, messageId: string, ocppError: OCPPError, - commandName: RequestCommand | IncomingRequestCommand, - ) => Promise; + commandName: RequestCommand | IncomingRequestCommand + ) => Promise this.internalSendMessage = this.internalSendMessage.bind(this) as ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType | OCPPError, messageType: MessageType, commandName: RequestCommand | IncomingRequestCommand, - params?: RequestParams, - ) => Promise; + params?: RequestParams + ) => Promise this.buildMessageToSend = this.buildMessageToSend.bind(this) as ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType | OCPPError, messageType: MessageType, - commandName: RequestCommand | IncomingRequestCommand, - ) => string; + commandName: RequestCommand | IncomingRequestCommand + ) => string this.validateRequestPayload = this.validateRequestPayload.bind(this) as ( chargingStation: ChargingStation, commandName: RequestCommand | IncomingRequestCommand, - payload: T, - ) => boolean; + payload: T + ) => boolean this.validateIncomingRequestResponsePayload = this.validateIncomingRequestResponsePayload.bind( - this, + this ) as ( chargingStation: ChargingStation, commandName: RequestCommand | IncomingRequestCommand, - payload: T, - ) => boolean; + payload: T + ) => boolean } public static getInstance( this: new (ocppResponseService: OCPPResponseService) => T, - ocppResponseService: OCPPResponseService, + ocppResponseService: OCPPResponseService ): T { if (OCPPRequestService.instance === null) { - OCPPRequestService.instance = new this(ocppResponseService); + OCPPRequestService.instance = new this(ocppResponseService) } - return OCPPRequestService.instance as T; + return OCPPRequestService.instance as T } - public async sendResponse( + public async sendResponse ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType, - commandName: IncomingRequestCommand, + commandName: IncomingRequestCommand ): Promise { try { // Send response message @@ -140,21 +141,21 @@ export abstract class OCPPRequestService { messageId, messagePayload, MessageType.CALL_RESULT_MESSAGE, - commandName, - ); + commandName + ) } catch (error) { handleSendMessageError(chargingStation, commandName, error as Error, { - throwError: true, - }); - return null; + throwError: true + }) + return null } } - public async sendError( + public async sendError ( chargingStation: ChargingStation, messageId: string, ocppError: OCPPError, - commandName: RequestCommand | IncomingRequestCommand, + commandName: RequestCommand | IncomingRequestCommand ): Promise { try { // Send error message @@ -163,25 +164,25 @@ export abstract class OCPPRequestService { messageId, ocppError, MessageType.CALL_ERROR_MESSAGE, - commandName, - ); + commandName + ) } catch (error) { - handleSendMessageError(chargingStation, commandName, error as Error); - return null; + handleSendMessageError(chargingStation, commandName, error as Error) + return null } } - protected async sendMessage( + protected async sendMessage ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType, commandName: RequestCommand, - params?: RequestParams, + params?: RequestParams ): Promise { params = { ...defaultRequestParams, - ...params, - }; + ...params + } try { return await this.internalSendMessage( chargingStation, @@ -189,140 +190,142 @@ export abstract class OCPPRequestService { messagePayload, MessageType.CALL_MESSAGE, commandName, - params, - ); + params + ) } catch (error) { handleSendMessageError(chargingStation, commandName, error as Error, { - throwError: params.throwError, - }); - return null; + throwError: params.throwError + }) + return null } } private validateRequestPayload( chargingStation: ChargingStation, commandName: RequestCommand | IncomingRequestCommand, - payload: T, + payload: T ): boolean { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { - return true; + return true } - if (this.jsonSchemas.has(commandName as RequestCommand) === false) { + if (!this.jsonSchemas.has(commandName as RequestCommand)) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: No JSON schema found for command '${commandName}' PDU validation`, - ); - return true; + `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: No JSON schema found for command '${commandName}' PDU validation` + ) + return true } - const validate = this.getJsonRequestValidateFunction(commandName as RequestCommand); - payload = cloneObject(payload); - OCPPServiceUtils.convertDateToISOString(payload); + const validate = this.getJsonRequestValidateFunction(commandName as RequestCommand) + payload = cloneObject(payload) + OCPPServiceUtils.convertDateToISOString(payload) if (validate(payload)) { - return true; + return true } logger.error( `${chargingStation.logPrefix()} ${moduleName}.validateRequestPayload: Command '${commandName}' request PDU is invalid: %j`, - validate.errors, - ); + validate.errors + ) // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). throw new OCPPError( OCPPServiceUtils.ajvErrorsToErrorType(validate.errors), 'Request PDU is invalid', commandName, - JSON.stringify(validate.errors, undefined, 2), - ); + JSON.stringify(validate.errors, undefined, 2) + ) } - private getJsonRequestValidateFunction(commandName: RequestCommand) { - if (this.jsonValidateFunctions.has(commandName) === false) { + private getJsonRequestValidateFunction( + commandName: RequestCommand + ): ValidateFunction { + if (!this.jsonValidateFunctions.has(commandName)) { this.jsonValidateFunctions.set( commandName, - this.ajv.compile(this.jsonSchemas.get(commandName)!).bind(this), - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.ajv.compile(this.jsonSchemas.get(commandName)!).bind(this) + ) } - return this.jsonValidateFunctions.get(commandName)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.jsonValidateFunctions.get(commandName)! } private validateIncomingRequestResponsePayload( chargingStation: ChargingStation, commandName: RequestCommand | IncomingRequestCommand, - payload: T, + payload: T ): boolean { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { - return true; + return true } if ( - this.ocppResponseService.jsonIncomingRequestResponseSchemas.has( - commandName as IncomingRequestCommand, - ) === false + !this.ocppResponseService.jsonIncomingRequestResponseSchemas.has( + commandName as IncomingRequestCommand + ) ) { logger.warn( - `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema found for command '${commandName}' PDU validation`, - ); - return true; + `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: No JSON schema found for command '${commandName}' PDU validation` + ) + return true } const validate = this.getJsonRequestResponseValidateFunction( - commandName as IncomingRequestCommand, - ); - payload = cloneObject(payload); - OCPPServiceUtils.convertDateToISOString(payload); + commandName as IncomingRequestCommand + ) + payload = cloneObject(payload) + OCPPServiceUtils.convertDateToISOString(payload) if (validate(payload)) { - return true; + return true } logger.error( `${chargingStation.logPrefix()} ${moduleName}.validateIncomingRequestResponsePayload: Command '${commandName}' reponse PDU is invalid: %j`, - validate.errors, - ); + validate.errors + ) // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError(). throw new OCPPError( OCPPServiceUtils.ajvErrorsToErrorType(validate.errors), 'Response PDU is invalid', commandName, - JSON.stringify(validate.errors, undefined, 2), - ); + JSON.stringify(validate.errors, undefined, 2) + ) } private getJsonRequestResponseValidateFunction( - commandName: IncomingRequestCommand, - ) { - if ( - this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.has(commandName) === - false - ) { + commandName: IncomingRequestCommand + ): ValidateFunction { + if (!this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.has(commandName)) { this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.set( commandName, this.ajv + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion .compile(this.ocppResponseService.jsonIncomingRequestResponseSchemas.get(commandName)!) - .bind(this), - ); + .bind(this) + ) } - return this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.get(commandName)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.ocppResponseService.jsonIncomingRequestResponseValidateFunctions.get(commandName)! } - private async internalSendMessage( + private async internalSendMessage ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType | OCPPError, messageType: MessageType, commandName: RequestCommand | IncomingRequestCommand, - params?: RequestParams, + params?: RequestParams ): Promise { params = { ...defaultRequestParams, - ...params, - }; + ...params + } if ( - (chargingStation.inUnknownState() === true && - commandName === RequestCommand.BOOT_NOTIFICATION) || + (chargingStation.inUnknownState() && commandName === RequestCommand.BOOT_NOTIFICATION) || (chargingStation.stationInfo?.ocppStrictCompliance === false && - chargingStation.inUnknownState() === true) || - chargingStation.inAcceptedState() === true || - (chargingStation.inPendingState() === true && + chargingStation.inUnknownState()) || + chargingStation.inAcceptedState() || + (chargingStation.inPendingState() && (params.triggerMessage === true || messageType === MessageType.CALL_RESULT_MESSAGE)) ) { // eslint-disable-next-line @typescript-eslint/no-this-alias - const self = this; + const self = this // Send a message through wsConnection - return new Promise((resolve, reject) => { + return await new Promise((resolve, reject) => { /** * Function that will receive the request's response * @@ -333,8 +336,8 @@ export abstract class OCPPRequestService { if (chargingStation.stationInfo?.enableStatistics === true) { chargingStation.performanceStatistics?.addRequestStatistic( commandName, - MessageType.CALL_RESULT_MESSAGE, - ); + MessageType.CALL_RESULT_MESSAGE + ) } // Handle the request's response self.ocppResponseService @@ -342,17 +345,17 @@ export abstract class OCPPRequestService { chargingStation, commandName as RequestCommand, payload, - requestPayload, + requestPayload ) .then(() => { - resolve(payload); + resolve(payload) }) .catch(reject) .finally(() => { - chargingStation.requests.delete(messageId); - chargingStation.emit(ChargingStationEvents.updated); - }); - }; + chargingStation.requests.delete(messageId) + chargingStation.emit(ChargingStationEvents.updated) + }) + } /** * Function that will receive the request's error response @@ -361,28 +364,28 @@ export abstract class OCPPRequestService { * @param requestStatistic - */ const errorCallback = (ocppError: OCPPError, requestStatistic = true): void => { - if (requestStatistic === true && chargingStation.stationInfo?.enableStatistics === true) { + if (requestStatistic && chargingStation.stationInfo?.enableStatistics === true) { chargingStation.performanceStatistics?.addRequestStatistic( commandName, - MessageType.CALL_ERROR_MESSAGE, - ); + MessageType.CALL_ERROR_MESSAGE + ) } logger.error( `${chargingStation.logPrefix()} Error occurred at ${OCPPServiceUtils.getMessageTypeString( - messageType, + messageType )} command ${commandName} with PDU %j:`, messagePayload, - ocppError, - ); - chargingStation.requests.delete(messageId); - chargingStation.emit(ChargingStationEvents.updated); - reject(ocppError); - }; + ocppError + ) + chargingStation.requests.delete(messageId) + chargingStation.emit(ChargingStationEvents.updated) + reject(ocppError) + } const handleSendError = (ocppError: OCPPError): void => { if (params?.skipBufferingOnError === false) { // Buffer - chargingStation.bufferMessage(messageToSend); + chargingStation.bufferMessage(messageToSend) if (messageType === MessageType.CALL_MESSAGE) { this.cacheRequestPromise( chargingStation, @@ -390,55 +393,55 @@ export abstract class OCPPRequestService { messagePayload as JsonType, commandName, responseCallback, - errorCallback, - ); + errorCallback + ) } } else if ( params?.skipBufferingOnError === true && messageType === MessageType.CALL_MESSAGE ) { // Remove request from the cache - chargingStation.requests.delete(messageId); + chargingStation.requests.delete(messageId) } - return reject(ocppError); - }; + reject(ocppError) + } if (chargingStation.stationInfo?.enableStatistics === true) { - chargingStation.performanceStatistics?.addRequestStatistic(commandName, messageType); + chargingStation.performanceStatistics?.addRequestStatistic(commandName, messageType) } const messageToSend = this.buildMessageToSend( chargingStation, messageId, messagePayload, messageType, - commandName, - ); + commandName + ) // Check if wsConnection opened - if (chargingStation.isWebSocketConnectionOpened() === true) { - const beginId = PerformanceStatistics.beginMeasure(commandName); + if (chargingStation.isWebSocketConnectionOpened()) { + const beginId = PerformanceStatistics.beginMeasure(commandName) const sendTimeout = setTimeout(() => { - return handleSendError( + handleSendError( new OCPPError( ErrorType.GENERIC_ERROR, `Timeout ${formatDurationMilliSeconds( - OCPPConstants.OCPP_WEBSOCKET_TIMEOUT, + OCPPConstants.OCPP_WEBSOCKET_TIMEOUT )} reached for ${ params?.skipBufferingOnError === false ? '' : 'non ' }buffered message id '${messageId}' with content '${messageToSend}'`, commandName, - (messagePayload as OCPPError).details, - ), - ); - }, OCPPConstants.OCPP_WEBSOCKET_TIMEOUT); + (messagePayload as OCPPError).details + ) + ) + }, OCPPConstants.OCPP_WEBSOCKET_TIMEOUT) chargingStation.wsConnection?.send(messageToSend, (error?: Error) => { - PerformanceStatistics.endMeasure(commandName, beginId); - clearTimeout(sendTimeout); + PerformanceStatistics.endMeasure(commandName, beginId) + clearTimeout(sendTimeout) if (isNullOrUndefined(error)) { logger.debug( `${chargingStation.logPrefix()} >> Command '${commandName}' sent ${OCPPServiceUtils.getMessageTypeString( - messageType, - )} payload: ${messageToSend}`, - ); + messageType + )} payload: ${messageToSend}` + ) if (messageType === MessageType.CALL_MESSAGE) { this.cacheRequestPromise( chargingStation, @@ -446,77 +449,77 @@ export abstract class OCPPRequestService { messagePayload as JsonType, commandName, responseCallback, - errorCallback, - ); + errorCallback + ) } else { // Resolve response - return resolve(messagePayload); + resolve(messagePayload) } - } else if (error) { - return handleSendError( + } else if (error != null) { + handleSendError( new OCPPError( ErrorType.GENERIC_ERROR, `WebSocket errored for ${ params?.skipBufferingOnError === false ? '' : 'non ' }buffered message id '${messageId}' with content '${messageToSend}'`, commandName, - { name: error.name, message: error.message, stack: error.stack }, - ), - ); + { name: error.name, message: error.message, stack: error.stack } + ) + ) } - }); + }) } else { - return handleSendError( + handleSendError( new OCPPError( ErrorType.GENERIC_ERROR, `WebSocket closed for ${ params?.skipBufferingOnError === false ? '' : 'non ' }buffered message id '${messageId}' with content '${messageToSend}'`, commandName, - (messagePayload as OCPPError).details, - ), - ); + (messagePayload as OCPPError).details + ) + ) } - }); + }) } throw new OCPPError( ErrorType.SECURITY_ERROR, `Cannot send command ${commandName} PDU when the charging station is in ${chargingStation?.bootNotificationResponse?.status} state on the central server`, - commandName, - ); + commandName + ) } - private buildMessageToSend( + private buildMessageToSend ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType | OCPPError, messageType: MessageType, - commandName: RequestCommand | IncomingRequestCommand, + commandName: RequestCommand | IncomingRequestCommand ): string { - let messageToSend: string; + let messageToSend: string // Type of message switch (messageType) { // Request case MessageType.CALL_MESSAGE: // Build request - this.validateRequestPayload(chargingStation, commandName, messagePayload as JsonType); + this.validateRequestPayload(chargingStation, commandName, messagePayload as JsonType) messageToSend = JSON.stringify([ messageType, messageId, commandName, - messagePayload, - ] as OutgoingRequest); - break; + messagePayload + ] as OutgoingRequest) + break // Response case MessageType.CALL_RESULT_MESSAGE: // Build response this.validateIncomingRequestResponsePayload( chargingStation, commandName, - messagePayload as JsonType, - ); - messageToSend = JSON.stringify([messageType, messageId, messagePayload] as Response); - break; + messagePayload as JsonType + ) + messageToSend = JSON.stringify([messageType, messageId, messagePayload] as Response) + break // Error Message case MessageType.CALL_ERROR_MESSAGE: // Build Error Message @@ -526,28 +529,28 @@ export abstract class OCPPRequestService { (messagePayload as OCPPError).code, (messagePayload as OCPPError).message, (messagePayload as OCPPError).details ?? { - command: (messagePayload as OCPPError).command ?? commandName, - }, - ] as ErrorResponse); - break; + command: (messagePayload as OCPPError).command ?? commandName + } + ] as ErrorResponse) + break } - return messageToSend; + return messageToSend } - private cacheRequestPromise( + private cacheRequestPromise ( chargingStation: ChargingStation, messageId: string, messagePayload: JsonType, commandName: RequestCommand | IncomingRequestCommand, responseCallback: ResponseCallback, - errorCallback: ErrorCallback, + errorCallback: ErrorCallback ): void { chargingStation.requests.set(messageId, [ responseCallback, errorCallback, commandName, - messagePayload, - ]); + messagePayload + ]) } // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -556,6 +559,6 @@ export abstract class OCPPRequestService { commandName: RequestCommand, // FIXME: should be ReqType commandParams?: JsonType, - params?: RequestParams, - ): Promise; + params?: RequestParams + ): Promise } diff --git a/src/charging-station/ocpp/OCPPResponseService.ts b/src/charging-station/ocpp/OCPPResponseService.ts index a80bc2b7..738aa5ae 100644 --- a/src/charging-station/ocpp/OCPPResponseService.ts +++ b/src/charging-station/ocpp/OCPPResponseService.ts @@ -1,118 +1,120 @@ -import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv'; -import _ajvFormats from 'ajv-formats'; +import _Ajv, { type JSONSchemaType, type ValidateFunction } from 'ajv' +import _ajvFormats from 'ajv-formats' -import { OCPPServiceUtils } from './OCPPServiceUtils.js'; -import type { ChargingStation } from '../../charging-station/index.js'; -import { OCPPError } from '../../exception/index.js'; +import { OCPPServiceUtils } from './OCPPServiceUtils.js' +import type { ChargingStation } from '../../charging-station/index.js' +import { OCPPError } from '../../exception/index.js' import type { IncomingRequestCommand, JsonType, OCPPVersion, - RequestCommand, -} from '../../types/index.js'; -import { logger } from '../../utils/index.js'; -type Ajv = _Ajv.default; -const Ajv = _Ajv.default; -const ajvFormats = _ajvFormats.default; + RequestCommand +} from '../../types/index.js' +import { logger } from '../../utils/index.js' +type Ajv = _Ajv.default +// eslint-disable-next-line @typescript-eslint/no-redeclare +const Ajv = _Ajv.default +const ajvFormats = _ajvFormats.default -const moduleName = 'OCPPResponseService'; +const moduleName = 'OCPPResponseService' export abstract class OCPPResponseService { - private static instance: OCPPResponseService | null = null; + private static instance: OCPPResponseService | null = null public jsonIncomingRequestResponseValidateFunctions: Map< - IncomingRequestCommand, - ValidateFunction - >; + IncomingRequestCommand, + ValidateFunction + > - private readonly version: OCPPVersion; - private readonly ajv: Ajv; - private jsonRequestValidateFunctions: Map>; + private readonly version: OCPPVersion + private readonly ajv: Ajv + private readonly jsonRequestValidateFunctions: Map> public abstract jsonIncomingRequestResponseSchemas: Map< - IncomingRequestCommand, - JSONSchemaType - >; + IncomingRequestCommand, + JSONSchemaType + > - protected constructor(version: OCPPVersion) { - this.version = version; + protected constructor (version: OCPPVersion) { + this.version = version this.ajv = new Ajv({ keywords: ['javaType'], - multipleOfPrecision: 2, - }); - ajvFormats(this.ajv); - this.jsonRequestValidateFunctions = new Map>(); + multipleOfPrecision: 2 + }) + ajvFormats(this.ajv) + this.jsonRequestValidateFunctions = new Map>() this.jsonIncomingRequestResponseValidateFunctions = new Map< - IncomingRequestCommand, - ValidateFunction - >(); + IncomingRequestCommand, + ValidateFunction + >() this.responseHandler = this.responseHandler.bind(this) as < ReqType extends JsonType, - ResType extends JsonType, + ResType extends JsonType >( chargingStation: ChargingStation, commandName: RequestCommand, payload: ResType, - requestPayload: ReqType, - ) => Promise; + requestPayload: ReqType + ) => Promise this.validateResponsePayload = this.validateResponsePayload.bind(this) as ( chargingStation: ChargingStation, commandName: RequestCommand, schema: JSONSchemaType, - payload: T, - ) => boolean; + payload: T + ) => boolean } public static getInstance(this: new () => T): T { if (OCPPResponseService.instance === null) { - OCPPResponseService.instance = new this(); + OCPPResponseService.instance = new this() } - return OCPPResponseService.instance as T; + return OCPPResponseService.instance as T } protected validateResponsePayload( chargingStation: ChargingStation, commandName: RequestCommand, schema: JSONSchemaType, - payload: T, + payload: T ): boolean { if (chargingStation.stationInfo?.ocppStrictCompliance === false) { - return true; + return true } - const validate = this.getJsonRequestValidateFunction(commandName, schema); + const validate = this.getJsonRequestValidateFunction(commandName, schema) if (validate(payload)) { - return true; + return true } logger.error( `${chargingStation.logPrefix()} ${moduleName}.validateResponsePayload: Command '${commandName}' response PDU is invalid: %j`, - validate.errors, - ); + validate.errors + ) throw new OCPPError( OCPPServiceUtils.ajvErrorsToErrorType(validate.errors), 'Response PDU is invalid', commandName, - JSON.stringify(validate.errors, undefined, 2), - ); + JSON.stringify(validate.errors, undefined, 2) + ) } - protected emptyResponseHandler() { + protected emptyResponseHandler (): void { /* This is intentional */ } private getJsonRequestValidateFunction( commandName: RequestCommand, - schema: JSONSchemaType, - ) { - if (this.jsonRequestValidateFunctions.has(commandName) === false) { - this.jsonRequestValidateFunctions.set(commandName, this.ajv.compile(schema).bind(this)); + schema: JSONSchemaType + ): ValidateFunction { + if (!this.jsonRequestValidateFunctions.has(commandName)) { + this.jsonRequestValidateFunctions.set(commandName, this.ajv.compile(schema).bind(this)) } - return this.jsonRequestValidateFunctions.get(commandName)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.jsonRequestValidateFunctions.get(commandName)! } public abstract responseHandler( chargingStation: ChargingStation, commandName: RequestCommand, payload: ResType, - requestPayload: ReqType, - ): Promise; + requestPayload: ReqType + ): Promise } diff --git a/src/charging-station/ocpp/OCPPServiceUtils.ts b/src/charging-station/ocpp/OCPPServiceUtils.ts index 56961521..b0225063 100644 --- a/src/charging-station/ocpp/OCPPServiceUtils.ts +++ b/src/charging-station/ocpp/OCPPServiceUtils.ts @@ -1,19 +1,19 @@ -import { readFileSync } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' -import type { DefinedError, ErrorObject, JSONSchemaType } from 'ajv'; -import { isDate } from 'date-fns'; +import type { DefinedError, ErrorObject, JSONSchemaType } from 'ajv' +import { isDate } from 'date-fns' -import { OCPP16Constants } from './1.6/OCPP16Constants.js'; -import { OCPP20Constants } from './2.0/OCPP20Constants.js'; -import { OCPPConstants } from './OCPPConstants.js'; +import { OCPP16Constants } from './1.6/OCPP16Constants.js' +import { OCPP20Constants } from './2.0/OCPP20Constants.js' +import { OCPPConstants } from './OCPPConstants.js' import { type ChargingStation, getConfigurationKey, - getIdTagsFile, -} from '../../charging-station/index.js'; -import { BaseError, OCPPError } from '../../exception/index.js'; + getIdTagsFile +} from '../../charging-station/index.js' +import { BaseError, OCPPError } from '../../exception/index.js' import { AuthorizationStatus, type AuthorizeRequest, @@ -45,8 +45,8 @@ import { type SampledValueTemplate, StandardParametersKey, type StatusNotificationRequest, - type StatusNotificationResponse, -} from '../../types/index.js'; + type StatusNotificationResponse +} from '../../types/index.js' import { ACElectricUtils, Constants, @@ -65,226 +65,228 @@ import { logger, max, min, - roundTo, -} from '../../utils/index.js'; + roundTo +} from '../../utils/index.js' export const getMessageTypeString = (messageType: MessageType): string => { switch (messageType) { case MessageType.CALL_MESSAGE: - return 'request'; + return 'request' case MessageType.CALL_RESULT_MESSAGE: - return 'response'; + return 'response' case MessageType.CALL_ERROR_MESSAGE: - return 'error'; + return 'error' default: - return 'unknown'; + return 'unknown' } -}; +} export const buildStatusNotificationRequest = ( chargingStation: ChargingStation, connectorId: number, status: ConnectorStatusEnum, - evseId?: number, + evseId?: number ): StatusNotificationRequest => { switch (chargingStation.stationInfo?.ocppVersion) { case OCPPVersion.VERSION_16: return { connectorId, status, - errorCode: ChargePointErrorCode.NO_ERROR, - } as OCPP16StatusNotificationRequest; + errorCode: ChargePointErrorCode.NO_ERROR + } satisfies OCPP16StatusNotificationRequest case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: return { timestamp: new Date(), connectorStatus: status, connectorId, - evseId, - } as OCPP20StatusNotificationRequest; + evseId + } satisfies OCPP20StatusNotificationRequest default: - throw new BaseError('Cannot build status notification payload: OCPP version not supported'); + throw new BaseError('Cannot build status notification payload: OCPP version not supported') } -}; +} export const isIdTagAuthorized = async ( chargingStation: ChargingStation, connectorId: number, - idTag: string, + idTag: string ): Promise => { if ( !chargingStation.getLocalAuthListEnabled() && - !chargingStation.stationInfo?.remoteAuthorization + chargingStation.stationInfo?.remoteAuthorization === false ) { logger.warn( - `${chargingStation.logPrefix()} The charging station expects to authorize RFID tags but nor local authorization nor remote authorization are enabled. Misbehavior may occur`, - ); + `${chargingStation.logPrefix()} The charging station expects to authorize RFID tags but nor local authorization nor remote authorization are enabled. Misbehavior may occur` + ) } - if ( - chargingStation.getLocalAuthListEnabled() === true && - isIdTagLocalAuthorized(chargingStation, idTag) - ) { - const connectorStatus: ConnectorStatus = chargingStation.getConnectorStatus(connectorId)!; - connectorStatus.localAuthorizeIdTag = idTag; - connectorStatus.idTagLocalAuthorized = true; - return true; - } else if (chargingStation.stationInfo?.remoteAuthorization) { - return await isIdTagRemoteAuthorized(chargingStation, connectorId, idTag); + if (chargingStation.getLocalAuthListEnabled() && isIdTagLocalAuthorized(chargingStation, idTag)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const connectorStatus: ConnectorStatus = chargingStation.getConnectorStatus(connectorId)! + connectorStatus.localAuthorizeIdTag = idTag + connectorStatus.idTagLocalAuthorized = true + return true + } else if (chargingStation.stationInfo?.remoteAuthorization === true) { + return await isIdTagRemoteAuthorized(chargingStation, connectorId, idTag) } - return false; -}; + return false +} const isIdTagLocalAuthorized = (chargingStation: ChargingStation, idTag: string): boolean => { return ( - chargingStation.hasIdTags() === true && + chargingStation.hasIdTags() && isNotEmptyString( chargingStation.idTagsCache + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion .getIdTags(getIdTagsFile(chargingStation.stationInfo)!) - ?.find((tag) => tag === idTag), + ?.find((tag) => tag === idTag) ) - ); -}; + ) +} const isIdTagRemoteAuthorized = async ( chargingStation: ChargingStation, connectorId: number, - idTag: string, + idTag: string ): Promise => { - chargingStation.getConnectorStatus(connectorId)!.authorizeIdTag = idTag; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.authorizeIdTag = idTag return ( ( await chargingStation.ocppRequestService.requestHandler( chargingStation, RequestCommand.AUTHORIZE, { - idTag, - }, + idTag + } ) )?.idTagInfo?.status === AuthorizationStatus.ACCEPTED - ); -}; + ) +} export const sendAndSetConnectorStatus = async ( chargingStation: ChargingStation, connectorId: number, status: ConnectorStatusEnum, evseId?: number, - options?: { send: boolean }, + options?: { send: boolean } ): Promise => { - options = { send: true, ...options }; + options = { send: true, ...options } if (options.send) { - checkConnectorStatusTransition(chargingStation, connectorId, status); + checkConnectorStatusTransition(chargingStation, connectorId, status) await chargingStation.ocppRequestService.requestHandler< - StatusNotificationRequest, - StatusNotificationResponse + StatusNotificationRequest, + StatusNotificationResponse >( chargingStation, RequestCommand.STATUS_NOTIFICATION, - buildStatusNotificationRequest(chargingStation, connectorId, status, evseId), - ); + buildStatusNotificationRequest(chargingStation, connectorId, status, evseId) + ) } - chargingStation.getConnectorStatus(connectorId)!.status = status; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.status = status chargingStation.emit(ChargingStationEvents.connectorStatusChanged, { connectorId, - ...chargingStation.getConnectorStatus(connectorId), - }); -}; + ...chargingStation.getConnectorStatus(connectorId) + }) +} const checkConnectorStatusTransition = ( chargingStation: ChargingStation, connectorId: number, - status: ConnectorStatusEnum, + status: ConnectorStatusEnum ): boolean => { - const fromStatus = chargingStation.getConnectorStatus(connectorId)!.status; - let transitionAllowed = false; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const fromStatus = chargingStation.getConnectorStatus(connectorId)!.status + let transitionAllowed = false switch (chargingStation.stationInfo?.ocppVersion) { case OCPPVersion.VERSION_16: if ( (connectorId === 0 && OCPP16Constants.ChargePointStatusChargingStationTransitions.findIndex( - (transition) => transition.from === fromStatus && transition.to === status, + (transition) => transition.from === fromStatus && transition.to === status ) !== -1) || (connectorId > 0 && OCPP16Constants.ChargePointStatusConnectorTransitions.findIndex( - (transition) => transition.from === fromStatus && transition.to === status, + (transition) => transition.from === fromStatus && transition.to === status ) !== -1) ) { - transitionAllowed = true; + transitionAllowed = true } - break; + break case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: if ( (connectorId === 0 && OCPP20Constants.ChargingStationStatusTransitions.findIndex( - (transition) => transition.from === fromStatus && transition.to === status, + (transition) => transition.from === fromStatus && transition.to === status ) !== -1) || (connectorId > 0 && OCPP20Constants.ConnectorStatusTransitions.findIndex( - (transition) => transition.from === fromStatus && transition.to === status, + (transition) => transition.from === fromStatus && transition.to === status ) !== -1) ) { - transitionAllowed = true; + transitionAllowed = true } - break; + break default: throw new BaseError( - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - `Cannot check connector status transition: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported`, - ); + `Cannot check connector status transition: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported` + ) } - if (transitionAllowed === false) { + if (!transitionAllowed) { logger.warn( `${chargingStation.logPrefix()} OCPP ${chargingStation.stationInfo ?.ocppVersion} connector id ${connectorId} status transition from '${ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingStation.getConnectorStatus(connectorId)!.status - }' to '${status}' is not allowed`, - ); + }' to '${status}' is not allowed` + ) } - return transitionAllowed; -}; + return transitionAllowed +} export const buildMeterValue = ( chargingStation: ChargingStation, connectorId: number, transactionId: number, interval: number, - debug = false, + debug = false ): MeterValue => { - const connector = chargingStation.getConnectorStatus(connectorId); - let meterValue: MeterValue; - let socSampledValueTemplate: SampledValueTemplate | undefined; - let voltageSampledValueTemplate: SampledValueTemplate | undefined; - let powerSampledValueTemplate: SampledValueTemplate | undefined; - let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}; - let currentSampledValueTemplate: SampledValueTemplate | undefined; - let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}; - let energySampledValueTemplate: SampledValueTemplate | undefined; + const connector = chargingStation.getConnectorStatus(connectorId) + let meterValue: MeterValue + let socSampledValueTemplate: SampledValueTemplate | undefined + let voltageSampledValueTemplate: SampledValueTemplate | undefined + let powerSampledValueTemplate: SampledValueTemplate | undefined + let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {} + let currentSampledValueTemplate: SampledValueTemplate | undefined + let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {} + let energySampledValueTemplate: SampledValueTemplate | undefined switch (chargingStation.stationInfo?.ocppVersion) { case OCPPVersion.VERSION_16: meterValue = { timestamp: new Date(), - sampledValue: [], - }; + sampledValue: [] + } // SoC measurand socSampledValueTemplate = getSampledValueTemplate( chargingStation, connectorId, - MeterValueMeasurand.STATE_OF_CHARGE, - ); - if (socSampledValueTemplate) { - const socMaximumValue = 100; - const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0; + MeterValueMeasurand.STATE_OF_CHARGE + ) + if (socSampledValueTemplate != null) { + const socMaximumValue = 100 + const socMinimumValue = socSampledValueTemplate.minimumValue ?? 0 const socSampledValueTemplateValue = isNotEmptyString(socSampledValueTemplate.value) ? getRandomFloatFluctuatedRounded( - parseInt(socSampledValueTemplate.value), - socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : getRandomInteger(socMaximumValue, socMinimumValue); + parseInt(socSampledValueTemplate.value), + socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : getRandomInteger(socMaximumValue, socMinimumValue) meterValue.sampledValue.push( - buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue), - ); - const sampledValuesIndex = meterValue.sampledValue.length - 1; + buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue) + ) + const sampledValuesIndex = meterValue.sampledValue.length - 1 if ( convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > socMaximumValue || convertToInt(meterValue.sampledValue[sampledValuesIndex].value) < socMinimumValue || @@ -296,114 +298,117 @@ export const buildMeterValue = ( MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${socMinimumValue}/${ meterValue.sampledValue[sampledValuesIndex].value - }/${socMaximumValue}`, - ); + }/${socMaximumValue}` + ) } } // Voltage measurand voltageSampledValueTemplate = getSampledValueTemplate( chargingStation, connectorId, - MeterValueMeasurand.VOLTAGE, - ); - if (voltageSampledValueTemplate) { + MeterValueMeasurand.VOLTAGE + ) + if (voltageSampledValueTemplate != null) { const voltageSampledValueTemplateValue = isNotEmptyString(voltageSampledValueTemplate.value) ? parseInt(voltageSampledValueTemplate.value) - : chargingStation.stationInfo.voltageOut!; + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo.voltageOut! const fluctuationPercent = - voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; + voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT const voltageMeasurandValue = getRandomFloatFluctuatedRounded( voltageSampledValueTemplateValue, - fluctuationPercent, - ); + fluctuationPercent + ) if ( chargingStation.getNumberOfPhases() !== 3 || (chargingStation.getNumberOfPhases() === 3 && - chargingStation.stationInfo?.mainVoltageMeterValues) + chargingStation.stationInfo?.mainVoltageMeterValues === true) ) { meterValue.sampledValue.push( - buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue), - ); + buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue) + ) } for ( let phase = 1; chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases(); phase++ ) { - const phaseLineToNeutralValue = `L${phase}-N`; + const phaseLineToNeutralValue = `L${phase}-N` const voltagePhaseLineToNeutralSampledValueTemplate = getSampledValueTemplate( chargingStation, connectorId, MeterValueMeasurand.VOLTAGE, - phaseLineToNeutralValue as MeterValuePhase, - ); - let voltagePhaseLineToNeutralMeasurandValue: number | undefined; - if (voltagePhaseLineToNeutralSampledValueTemplate) { + phaseLineToNeutralValue as MeterValuePhase + ) + let voltagePhaseLineToNeutralMeasurandValue: number | undefined + if (voltagePhaseLineToNeutralSampledValueTemplate != null) { const voltagePhaseLineToNeutralSampledValueTemplateValue = isNotEmptyString( - voltagePhaseLineToNeutralSampledValueTemplate.value, + voltagePhaseLineToNeutralSampledValueTemplate.value ) ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value) - : chargingStation.stationInfo.voltageOut!; + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo.voltageOut! const fluctuationPhaseToNeutralPercent = voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT; + Constants.DEFAULT_FLUCTUATION_PERCENT voltagePhaseLineToNeutralMeasurandValue = getRandomFloatFluctuatedRounded( voltagePhaseLineToNeutralSampledValueTemplateValue, - fluctuationPhaseToNeutralPercent, - ); + fluctuationPhaseToNeutralPercent + ) } meterValue.sampledValue.push( buildSampledValue( voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate, voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue, undefined, - phaseLineToNeutralValue as MeterValuePhase, - ), - ); - if (chargingStation.stationInfo?.phaseLineToLineVoltageMeterValues) { + phaseLineToNeutralValue as MeterValuePhase + ) + ) + if (chargingStation.stationInfo?.phaseLineToLineVoltageMeterValues === true) { const phaseLineToLineValue = `L${phase}-L${ (phase + 1) % chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % chargingStation.getNumberOfPhases() : chargingStation.getNumberOfPhases() - }`; + }` const voltagePhaseLineToLineValueRounded = roundTo( Math.sqrt(chargingStation.getNumberOfPhases()) * + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion chargingStation.stationInfo.voltageOut!, - 2, - ); + 2 + ) const voltagePhaseLineToLineSampledValueTemplate = getSampledValueTemplate( chargingStation, connectorId, MeterValueMeasurand.VOLTAGE, - phaseLineToLineValue as MeterValuePhase, - ); - let voltagePhaseLineToLineMeasurandValue: number | undefined; - if (voltagePhaseLineToLineSampledValueTemplate) { + phaseLineToLineValue as MeterValuePhase + ) + let voltagePhaseLineToLineMeasurandValue: number | undefined + if (voltagePhaseLineToLineSampledValueTemplate != null) { const voltagePhaseLineToLineSampledValueTemplateValue = isNotEmptyString( - voltagePhaseLineToLineSampledValueTemplate.value, + voltagePhaseLineToLineSampledValueTemplate.value ) ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value) - : voltagePhaseLineToLineValueRounded; + : voltagePhaseLineToLineValueRounded const fluctuationPhaseLineToLinePercent = voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT; + Constants.DEFAULT_FLUCTUATION_PERCENT voltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded( voltagePhaseLineToLineSampledValueTemplateValue, - fluctuationPhaseLineToLinePercent, - ); + fluctuationPhaseLineToLinePercent + ) } const defaultVoltagePhaseLineToLineMeasurandValue = getRandomFloatFluctuatedRounded( voltagePhaseLineToLineValueRounded, - fluctuationPercent, - ); + fluctuationPercent + ) meterValue.sampledValue.push( buildSampledValue( voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate, voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue, undefined, - phaseLineToLineValue as MeterValuePhase, - ), - ); + phaseLineToLineValue as MeterValuePhase + ) + ) } } } @@ -411,206 +416,208 @@ export const buildMeterValue = ( powerSampledValueTemplate = getSampledValueTemplate( chargingStation, connectorId, - MeterValueMeasurand.POWER_ACTIVE_IMPORT, - ); + MeterValueMeasurand.POWER_ACTIVE_IMPORT + ) if (chargingStation.getNumberOfPhases() === 3) { powerPerPhaseSampledValueTemplates = { L1: getSampledValueTemplate( chargingStation, connectorId, MeterValueMeasurand.POWER_ACTIVE_IMPORT, - MeterValuePhase.L1_N, + MeterValuePhase.L1_N ), L2: getSampledValueTemplate( chargingStation, connectorId, MeterValueMeasurand.POWER_ACTIVE_IMPORT, - MeterValuePhase.L2_N, + MeterValuePhase.L2_N ), L3: getSampledValueTemplate( chargingStation, connectorId, MeterValueMeasurand.POWER_ACTIVE_IMPORT, - MeterValuePhase.L3_N, - ), - }; + MeterValuePhase.L3_N + ) + } } - if (powerSampledValueTemplate) { - checkMeasurandPowerDivider(chargingStation, powerSampledValueTemplate.measurand!); + if (powerSampledValueTemplate != null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + checkMeasurandPowerDivider(chargingStation, powerSampledValueTemplate.measurand!) const errMsg = `MeterValues measurand ${ powerSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${ chargingStation.templateFile }, cannot calculate ${ powerSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - } measurand value`; - const powerMeasurandValues: MeasurandValues = {} as MeasurandValues; - const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1; + } measurand value` + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const powerMeasurandValues: MeasurandValues = {} as MeasurandValues + const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1 const connectorMaximumAvailablePower = - chargingStation.getConnectorMaximumAvailablePower(connectorId); - const connectorMaximumPower = Math.round(connectorMaximumAvailablePower); + chargingStation.getConnectorMaximumAvailablePower(connectorId) + const connectorMaximumPower = Math.round(connectorMaximumAvailablePower) const connectorMaximumPowerPerPhase = Math.round( - connectorMaximumAvailablePower / chargingStation.getNumberOfPhases(), - ); - const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue ?? 0); + connectorMaximumAvailablePower / chargingStation.getNumberOfPhases() + ) + const connectorMinimumPower = Math.round(powerSampledValueTemplate.minimumValue ?? 0) const connectorMinimumPowerPerPhase = Math.round( - connectorMinimumPower / chargingStation.getNumberOfPhases(), - ); + connectorMinimumPower / chargingStation.getNumberOfPhases() + ) switch (chargingStation.stationInfo?.currentOutType) { case CurrentType.AC: if (chargingStation.getNumberOfPhases() === 3) { const defaultFluctuatedPowerPerPhase = isNotEmptyString( - powerSampledValueTemplate.value, + powerSampledValueTemplate.value ) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - powerSampledValueTemplate.value, - connectorMaximumPower / unitDivider, - connectorMinimumPower / unitDivider, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + powerSampledValueTemplate.value, + connectorMaximumPower / unitDivider, + connectorMinimumPower / unitDivider, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumPower / unitDivider, - }, - ) / chargingStation.getNumberOfPhases(), - powerSampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : undefined; + fallbackValue: connectorMinimumPower / unitDivider + } + ) / chargingStation.getNumberOfPhases(), + powerSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : undefined const phase1FluctuatedValue = isNotEmptyString( - powerPerPhaseSampledValueTemplates.L1?.value, + powerPerPhaseSampledValueTemplates.L1?.value ) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - powerPerPhaseSampledValueTemplates.L1?.value, - connectorMaximumPowerPerPhase / unitDivider, - connectorMinimumPowerPerPhase / unitDivider, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + powerPerPhaseSampledValueTemplates.L1?.value, + connectorMaximumPowerPerPhase / unitDivider, + connectorMinimumPowerPerPhase / unitDivider, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumPowerPerPhase / unitDivider, - }, - ), - powerPerPhaseSampledValueTemplates.L1?.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : undefined; + fallbackValue: connectorMinimumPowerPerPhase / unitDivider + } + ), + powerPerPhaseSampledValueTemplates.L1?.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : undefined const phase2FluctuatedValue = isNotEmptyString( - powerPerPhaseSampledValueTemplates.L2?.value, + powerPerPhaseSampledValueTemplates.L2?.value ) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - powerPerPhaseSampledValueTemplates.L2?.value, - connectorMaximumPowerPerPhase / unitDivider, - connectorMinimumPowerPerPhase / unitDivider, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + powerPerPhaseSampledValueTemplates.L2?.value, + connectorMaximumPowerPerPhase / unitDivider, + connectorMinimumPowerPerPhase / unitDivider, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumPowerPerPhase / unitDivider, - }, - ), - powerPerPhaseSampledValueTemplates.L2?.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : undefined; + fallbackValue: connectorMinimumPowerPerPhase / unitDivider + } + ), + powerPerPhaseSampledValueTemplates.L2?.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : undefined const phase3FluctuatedValue = isNotEmptyString( - powerPerPhaseSampledValueTemplates.L3?.value, + powerPerPhaseSampledValueTemplates.L3?.value ) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - powerPerPhaseSampledValueTemplates.L3?.value, - connectorMaximumPowerPerPhase / unitDivider, - connectorMinimumPowerPerPhase / unitDivider, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + powerPerPhaseSampledValueTemplates.L3?.value, + connectorMaximumPowerPerPhase / unitDivider, + connectorMinimumPowerPerPhase / unitDivider, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumPowerPerPhase / unitDivider, - }, - ), - powerPerPhaseSampledValueTemplates.L3?.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : undefined; + fallbackValue: connectorMinimumPowerPerPhase / unitDivider + } + ), + powerPerPhaseSampledValueTemplates.L3?.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : undefined powerMeasurandValues.L1 = phase1FluctuatedValue ?? defaultFluctuatedPowerPerPhase ?? getRandomFloatRounded( connectorMaximumPowerPerPhase / unitDivider, - connectorMinimumPowerPerPhase / unitDivider, - ); + connectorMinimumPowerPerPhase / unitDivider + ) powerMeasurandValues.L2 = phase2FluctuatedValue ?? defaultFluctuatedPowerPerPhase ?? getRandomFloatRounded( connectorMaximumPowerPerPhase / unitDivider, - connectorMinimumPowerPerPhase / unitDivider, - ); + connectorMinimumPowerPerPhase / unitDivider + ) powerMeasurandValues.L3 = phase3FluctuatedValue ?? defaultFluctuatedPowerPerPhase ?? getRandomFloatRounded( connectorMaximumPowerPerPhase / unitDivider, - connectorMinimumPowerPerPhase / unitDivider, - ); + connectorMinimumPowerPerPhase / unitDivider + ) } else { powerMeasurandValues.L1 = isNotEmptyString(powerSampledValueTemplate.value) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - powerSampledValueTemplate.value, - connectorMaximumPower / unitDivider, - connectorMinimumPower / unitDivider, - { - limitationEnabled: - chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumPower / unitDivider, - }, - ), - powerSampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : getRandomFloatRounded( - connectorMaximumPower / unitDivider, - connectorMinimumPower / unitDivider, - ); - powerMeasurandValues.L2 = 0; - powerMeasurandValues.L3 = 0; - } - powerMeasurandValues.allPhases = roundTo( - powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3, - 2, - ); - break; - case CurrentType.DC: - powerMeasurandValues.allPhases = isNotEmptyString(powerSampledValueTemplate.value) - ? getRandomFloatFluctuatedRounded( getLimitFromSampledValueTemplateCustomValue( powerSampledValueTemplate.value, connectorMaximumPower / unitDivider, connectorMinimumPower / unitDivider, { limitationEnabled: - chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumPower / unitDivider, - }, + chargingStation.stationInfo?.customValueLimitationMeterValues, + fallbackValue: connectorMinimumPower / unitDivider + } ), powerSampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, + Constants.DEFAULT_FLUCTUATION_PERCENT ) - : getRandomFloatRounded( + : getRandomFloatRounded( + connectorMaximumPower / unitDivider, + connectorMinimumPower / unitDivider + ) + powerMeasurandValues.L2 = 0 + powerMeasurandValues.L3 = 0 + } + powerMeasurandValues.allPhases = roundTo( + powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3, + 2 + ) + break + case CurrentType.DC: + powerMeasurandValues.allPhases = isNotEmptyString(powerSampledValueTemplate.value) + ? getRandomFloatFluctuatedRounded( + getLimitFromSampledValueTemplateCustomValue( + powerSampledValueTemplate.value, connectorMaximumPower / unitDivider, connectorMinimumPower / unitDivider, - ); - break; + { + limitationEnabled: + chargingStation.stationInfo?.customValueLimitationMeterValues, + fallbackValue: connectorMinimumPower / unitDivider + } + ), + powerSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : getRandomFloatRounded( + connectorMaximumPower / unitDivider, + connectorMinimumPower / unitDivider + ) + break default: - logger.error(`${chargingStation.logPrefix()} ${errMsg}`); - throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES); + logger.error(`${chargingStation.logPrefix()} ${errMsg}`) + throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES) } meterValue.sampledValue.push( - buildSampledValue(powerSampledValueTemplate, powerMeasurandValues.allPhases), - ); - const sampledValuesIndex = meterValue.sampledValue.length - 1; - const connectorMaximumPowerRounded = roundTo(connectorMaximumPower / unitDivider, 2); - const connectorMinimumPowerRounded = roundTo(connectorMinimumPower / unitDivider, 2); + buildSampledValue(powerSampledValueTemplate, powerMeasurandValues.allPhases) + ) + const sampledValuesIndex = meterValue.sampledValue.length - 1 + const connectorMaximumPowerRounded = roundTo(connectorMaximumPower / unitDivider, 2) + const connectorMinimumPowerRounded = roundTo(connectorMinimumPower / unitDivider, 2) if ( convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > connectorMaximumPowerRounded || @@ -624,15 +631,15 @@ export const buildMeterValue = ( MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumPowerRounded}/${ meterValue.sampledValue[sampledValuesIndex].value - }/${connectorMaximumPowerRounded}`, - ); + }/${connectorMaximumPowerRounded}` + ) } for ( let phase = 1; chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases(); phase++ ) { - const phaseValue = `L${phase}-N`; + const phaseValue = `L${phase}-N` meterValue.sampledValue.push( buildSampledValue( powerPerPhaseSampledValueTemplates[ @@ -640,18 +647,18 @@ export const buildMeterValue = ( ] ?? powerSampledValueTemplate, powerMeasurandValues[`L${phase}` as keyof MeasurandPerPhaseSampledValueTemplates], undefined, - phaseValue as MeterValuePhase, - ), - ); - const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1; + phaseValue as MeterValuePhase + ) + ) + const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1 const connectorMaximumPowerPerPhaseRounded = roundTo( connectorMaximumPowerPerPhase / unitDivider, - 2, - ); + 2 + ) const connectorMinimumPowerPerPhaseRounded = roundTo( connectorMinimumPowerPerPhase / unitDivider, - 2, - ); + 2 + ) if ( convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > connectorMaximumPowerPerPhaseRounded || @@ -667,8 +674,8 @@ export const buildMeterValue = ( meterValue.sampledValue[sampledValuesPerPhaseIndex].phase }, connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumPowerPerPhaseRounded}/${ meterValue.sampledValue[sampledValuesPerPhaseIndex].value - }/${connectorMaximumPowerPerPhaseRounded}`, - ); + }/${connectorMaximumPowerPerPhaseRounded}` + ) } } } @@ -676,192 +683,196 @@ export const buildMeterValue = ( currentSampledValueTemplate = getSampledValueTemplate( chargingStation, connectorId, - MeterValueMeasurand.CURRENT_IMPORT, - ); + MeterValueMeasurand.CURRENT_IMPORT + ) if (chargingStation.getNumberOfPhases() === 3) { currentPerPhaseSampledValueTemplates = { L1: getSampledValueTemplate( chargingStation, connectorId, MeterValueMeasurand.CURRENT_IMPORT, - MeterValuePhase.L1, + MeterValuePhase.L1 ), L2: getSampledValueTemplate( chargingStation, connectorId, MeterValueMeasurand.CURRENT_IMPORT, - MeterValuePhase.L2, + MeterValuePhase.L2 ), L3: getSampledValueTemplate( chargingStation, connectorId, MeterValueMeasurand.CURRENT_IMPORT, - MeterValuePhase.L3, - ), - }; + MeterValuePhase.L3 + ) + } } - if (currentSampledValueTemplate) { - checkMeasurandPowerDivider(chargingStation, currentSampledValueTemplate.measurand!); + if (currentSampledValueTemplate != null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + checkMeasurandPowerDivider(chargingStation, currentSampledValueTemplate.measurand!) const errMsg = `MeterValues measurand ${ currentSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: Unknown ${chargingStation.stationInfo?.currentOutType} currentOutType in template file ${ chargingStation.templateFile }, cannot calculate ${ currentSampledValueTemplate.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - } measurand value`; - const currentMeasurandValues: MeasurandValues = {} as MeasurandValues; + } measurand value` + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + const currentMeasurandValues: MeasurandValues = {} as MeasurandValues const connectorMaximumAvailablePower = - chargingStation.getConnectorMaximumAvailablePower(connectorId); - const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0; - let connectorMaximumAmperage: number; + chargingStation.getConnectorMaximumAvailablePower(connectorId) + const connectorMinimumAmperage = currentSampledValueTemplate.minimumValue ?? 0 + let connectorMaximumAmperage: number switch (chargingStation.stationInfo?.currentOutType) { case CurrentType.AC: connectorMaximumAmperage = ACElectricUtils.amperagePerPhaseFromPower( chargingStation.getNumberOfPhases(), connectorMaximumAvailablePower, - chargingStation.stationInfo.voltageOut!, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo.voltageOut! + ) if (chargingStation.getNumberOfPhases() === 3) { const defaultFluctuatedAmperagePerPhase = isNotEmptyString( - currentSampledValueTemplate.value, + currentSampledValueTemplate.value ) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - currentSampledValueTemplate.value, - connectorMaximumAmperage, - connectorMinimumAmperage, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + currentSampledValueTemplate.value, + connectorMaximumAmperage, + connectorMinimumAmperage, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumAmperage, - }, - ), - currentSampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : undefined; + fallbackValue: connectorMinimumAmperage + } + ), + currentSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : undefined const phase1FluctuatedValue = isNotEmptyString( - currentPerPhaseSampledValueTemplates.L1?.value, + currentPerPhaseSampledValueTemplates.L1?.value ) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - currentPerPhaseSampledValueTemplates.L1?.value, - connectorMaximumAmperage, - connectorMinimumAmperage, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + currentPerPhaseSampledValueTemplates.L1?.value, + connectorMaximumAmperage, + connectorMinimumAmperage, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumAmperage, - }, - ), - currentPerPhaseSampledValueTemplates.L1?.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : undefined; + fallbackValue: connectorMinimumAmperage + } + ), + currentPerPhaseSampledValueTemplates.L1?.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : undefined const phase2FluctuatedValue = isNotEmptyString( - currentPerPhaseSampledValueTemplates.L2?.value, + currentPerPhaseSampledValueTemplates.L2?.value ) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - currentPerPhaseSampledValueTemplates.L2?.value, - connectorMaximumAmperage, - connectorMinimumAmperage, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + currentPerPhaseSampledValueTemplates.L2?.value, + connectorMaximumAmperage, + connectorMinimumAmperage, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumAmperage, - }, - ), - currentPerPhaseSampledValueTemplates.L2?.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : undefined; + fallbackValue: connectorMinimumAmperage + } + ), + currentPerPhaseSampledValueTemplates.L2?.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : undefined const phase3FluctuatedValue = isNotEmptyString( - currentPerPhaseSampledValueTemplates.L3?.value, + currentPerPhaseSampledValueTemplates.L3?.value ) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - currentPerPhaseSampledValueTemplates.L3?.value, - connectorMaximumAmperage, - connectorMinimumAmperage, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + currentPerPhaseSampledValueTemplates.L3?.value, + connectorMaximumAmperage, + connectorMinimumAmperage, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumAmperage, - }, - ), - currentPerPhaseSampledValueTemplates.L3?.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : undefined; + fallbackValue: connectorMinimumAmperage + } + ), + currentPerPhaseSampledValueTemplates.L3?.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : undefined currentMeasurandValues.L1 = phase1FluctuatedValue ?? defaultFluctuatedAmperagePerPhase ?? - getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); + getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage) currentMeasurandValues.L2 = phase2FluctuatedValue ?? defaultFluctuatedAmperagePerPhase ?? - getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); + getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage) currentMeasurandValues.L3 = phase3FluctuatedValue ?? defaultFluctuatedAmperagePerPhase ?? - getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); + getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage) } else { currentMeasurandValues.L1 = isNotEmptyString(currentSampledValueTemplate.value) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - currentSampledValueTemplate.value, - connectorMaximumAmperage, - connectorMinimumAmperage, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + currentSampledValueTemplate.value, + connectorMaximumAmperage, + connectorMinimumAmperage, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumAmperage, - }, - ), - currentSampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); - currentMeasurandValues.L2 = 0; - currentMeasurandValues.L3 = 0; + fallbackValue: connectorMinimumAmperage + } + ), + currentSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage) + currentMeasurandValues.L2 = 0 + currentMeasurandValues.L3 = 0 } currentMeasurandValues.allPhases = roundTo( (currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / chargingStation.getNumberOfPhases(), - 2, - ); - break; + 2 + ) + break case CurrentType.DC: connectorMaximumAmperage = DCElectricUtils.amperage( connectorMaximumAvailablePower, - chargingStation.stationInfo.voltageOut!, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.stationInfo.voltageOut! + ) currentMeasurandValues.allPhases = isNotEmptyString(currentSampledValueTemplate.value) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - currentSampledValueTemplate.value, - connectorMaximumAmperage, - connectorMinimumAmperage, - { - limitationEnabled: + getLimitFromSampledValueTemplateCustomValue( + currentSampledValueTemplate.value, + connectorMaximumAmperage, + connectorMinimumAmperage, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumAmperage, - }, - ), - currentSampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage); - break; + fallbackValue: connectorMinimumAmperage + } + ), + currentSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : getRandomFloatRounded(connectorMaximumAmperage, connectorMinimumAmperage) + break default: - logger.error(`${chargingStation.logPrefix()} ${errMsg}`); - throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES); + logger.error(`${chargingStation.logPrefix()} ${errMsg}`) + throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES) } meterValue.sampledValue.push( - buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases), - ); - const sampledValuesIndex = meterValue.sampledValue.length - 1; + buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases) + ) + const sampledValuesIndex = meterValue.sampledValue.length - 1 if ( convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > connectorMaximumAmperage || @@ -875,15 +886,15 @@ export const buildMeterValue = ( MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumAmperage}/${ meterValue.sampledValue[sampledValuesIndex].value - }/${connectorMaximumAmperage}`, - ); + }/${connectorMaximumAmperage}` + ) } for ( let phase = 1; chargingStation.getNumberOfPhases() === 3 && phase <= chargingStation.getNumberOfPhases(); phase++ ) { - const phaseValue = `L${phase}`; + const phaseValue = `L${phase}` meterValue.sampledValue.push( buildSampledValue( currentPerPhaseSampledValueTemplates[ @@ -891,10 +902,10 @@ export const buildMeterValue = ( ] ?? currentSampledValueTemplate, currentMeasurandValues[phaseValue as keyof MeasurandPerPhaseSampledValueTemplates], undefined, - phaseValue as MeterValuePhase, - ), - ); - const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1; + phaseValue as MeterValuePhase + ) + ) + const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1 if ( convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > connectorMaximumAmperage || @@ -910,56 +921,60 @@ export const buildMeterValue = ( meterValue.sampledValue[sampledValuesPerPhaseIndex].phase }, connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumAmperage}/${ meterValue.sampledValue[sampledValuesPerPhaseIndex].value - }/${connectorMaximumAmperage}`, - ); + }/${connectorMaximumAmperage}` + ) } } } // Energy.Active.Import.Register measurand (default) - energySampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId); - if (energySampledValueTemplate) { - checkMeasurandPowerDivider(chargingStation, energySampledValueTemplate.measurand!); + energySampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId) + if (energySampledValueTemplate != null) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + checkMeasurandPowerDivider(chargingStation, energySampledValueTemplate.measurand!) const unitDivider = - energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; + energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1 const connectorMaximumAvailablePower = - chargingStation.getConnectorMaximumAvailablePower(connectorId); + chargingStation.getConnectorMaximumAvailablePower(connectorId) const connectorMaximumEnergyRounded = roundTo( (connectorMaximumAvailablePower * interval) / (3600 * 1000), - 2, - ); + 2 + ) const connectorMinimumEnergyRounded = roundTo( energySampledValueTemplate.minimumValue ?? 0, - 2, - ); + 2 + ) const energyValueRounded = isNotEmptyString(energySampledValueTemplate.value) ? getRandomFloatFluctuatedRounded( - getLimitFromSampledValueTemplateCustomValue( - energySampledValueTemplate.value, - connectorMaximumEnergyRounded, - connectorMinimumEnergyRounded, - { - limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, - fallbackValue: connectorMinimumEnergyRounded, - unitMultiplier: unitDivider, - }, - ), - energySampledValueTemplate.fluctuationPercent ?? - Constants.DEFAULT_FLUCTUATION_PERCENT, - ) - : getRandomFloatRounded(connectorMaximumEnergyRounded, connectorMinimumEnergyRounded); + getLimitFromSampledValueTemplateCustomValue( + energySampledValueTemplate.value, + connectorMaximumEnergyRounded, + connectorMinimumEnergyRounded, + { + limitationEnabled: chargingStation.stationInfo?.customValueLimitationMeterValues, + fallbackValue: connectorMinimumEnergyRounded, + unitMultiplier: unitDivider + } + ), + energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT + ) + : getRandomFloatRounded(connectorMaximumEnergyRounded, connectorMinimumEnergyRounded) // Persist previous value on connector - if (connector) { + if (connector != null) { if ( - isNullOrUndefined(connector.energyActiveImportRegisterValue) === false && + !isNullOrUndefined(connector.energyActiveImportRegisterValue) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion connector.energyActiveImportRegisterValue! >= 0 && - isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) === false && + !isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion connector.transactionEnergyActiveImportRegisterValue! >= 0 ) { - connector.energyActiveImportRegisterValue! += energyValueRounded; - connector.transactionEnergyActiveImportRegisterValue! += energyValueRounded; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connector.energyActiveImportRegisterValue! += energyValueRounded + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + connector.transactionEnergyActiveImportRegisterValue! += energyValueRounded } else { - connector.energyActiveImportRegisterValue = 0; - connector.transactionEnergyActiveImportRegisterValue = 0; + connector.energyActiveImportRegisterValue = 0 + connector.transactionEnergyActiveImportRegisterValue = 0 } } meterValue.sampledValue.push( @@ -968,11 +983,11 @@ export const buildMeterValue = ( roundTo( chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) / unitDivider, - 2, - ), - ), - ); - const sampledValuesIndex = meterValue.sampledValue.length - 1; + 2 + ) + ) + ) + const sampledValuesIndex = meterValue.sampledValue.length - 1 if ( energyValueRounded > connectorMaximumEnergyRounded || energyValueRounded < connectorMinimumEnergyRounded || @@ -982,358 +997,362 @@ export const buildMeterValue = ( `${chargingStation.logPrefix()} MeterValues measurand ${ meterValue.sampledValue[sampledValuesIndex].measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumEnergyRounded}/${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${interval}ms`, - ); + }: connector id ${connectorId}, transaction id ${connector?.transactionId}, value: ${connectorMinimumEnergyRounded}/${energyValueRounded}/${connectorMaximumEnergyRounded}, duration: ${interval}ms` + ) } } - return meterValue; + return meterValue case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: default: throw new BaseError( - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - `Cannot build meterValue: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported`, - ); + `Cannot build meterValue: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported` + ) } -}; +} export const buildTransactionEndMeterValue = ( chargingStation: ChargingStation, connectorId: number, - meterStop: number, + meterStop: number ): MeterValue => { - let meterValue: MeterValue; - let sampledValueTemplate: SampledValueTemplate | undefined; - let unitDivider: number; + let meterValue: MeterValue + let sampledValueTemplate: SampledValueTemplate | undefined + let unitDivider: number switch (chargingStation.stationInfo?.ocppVersion) { case OCPPVersion.VERSION_16: meterValue = { timestamp: new Date(), - sampledValue: [], - }; + sampledValue: [] + } // Energy.Active.Import.Register measurand (default) - sampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId); - unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; + sampledValueTemplate = getSampledValueTemplate(chargingStation, connectorId) + unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1 meterValue.sampledValue.push( buildSampledValue( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion sampledValueTemplate!, roundTo((meterStop ?? 0) / unitDivider, 4), - MeterValueContext.TRANSACTION_END, - ), - ); - return meterValue; + MeterValueContext.TRANSACTION_END + ) + ) + return meterValue case OCPPVersion.VERSION_20: case OCPPVersion.VERSION_201: default: throw new BaseError( - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - `Cannot build meterValue: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported`, - ); + `Cannot build meterValue: OCPP version ${chargingStation.stationInfo?.ocppVersion} not supported` + ) } -}; +} const checkMeasurandPowerDivider = ( chargingStation: ChargingStation, - measurandType: MeterValueMeasurand, + measurandType: MeterValueMeasurand ): void => { if (isUndefined(chargingStation.powerDivider)) { const errMsg = `MeterValues measurand ${ measurandType ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: powerDivider is undefined`; - logger.error(`${chargingStation.logPrefix()} ${errMsg}`); - throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES); + }: powerDivider is undefined` + logger.error(`${chargingStation.logPrefix()} ${errMsg}`) + throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES) } else if (chargingStation?.powerDivider <= 0) { const errMsg = `MeterValues measurand ${ measurandType ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - }: powerDivider have zero or below value ${chargingStation.powerDivider}`; - logger.error(`${chargingStation.logPrefix()} ${errMsg}`); - throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES); + }: powerDivider have zero or below value ${chargingStation.powerDivider}` + logger.error(`${chargingStation.logPrefix()} ${errMsg}`) + throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES) } -}; +} const getLimitFromSampledValueTemplateCustomValue = ( value: string | undefined, maxLimit: number, minLimit: number, - options?: { limitationEnabled?: boolean; fallbackValue?: number; unitMultiplier?: number }, + options?: { limitationEnabled?: boolean, fallbackValue?: number, unitMultiplier?: number } ): number => { options = { ...{ limitationEnabled: false, unitMultiplier: 1, - fallbackValue: 0, + fallbackValue: 0 }, - ...options, - }; - const parsedValue = parseInt(value ?? ''); - if (options?.limitationEnabled) { + ...options + } + const parsedValue = parseInt(value ?? '') + if (options?.limitationEnabled === true) { return max( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion min((!isNaN(parsedValue) ? parsedValue : Infinity) * options.unitMultiplier!, maxLimit), - minLimit, - ); + minLimit + ) } - return (!isNaN(parsedValue) ? parsedValue : options.fallbackValue!) * options.unitMultiplier!; -}; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return (!isNaN(parsedValue) ? parsedValue : options.fallbackValue!) * options.unitMultiplier! +} const getSampledValueTemplate = ( chargingStation: ChargingStation, connectorId: number, measurand: MeterValueMeasurand = MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, - phase?: MeterValuePhase, + phase?: MeterValuePhase ): SampledValueTemplate | undefined => { - const onPhaseStr = phase ? `on phase ${phase} ` : ''; - if (OCPPConstants.OCPP_MEASURANDS_SUPPORTED.includes(measurand) === false) { + const onPhaseStr = phase != null ? `on phase ${phase} ` : '' + if (!OCPPConstants.OCPP_MEASURANDS_SUPPORTED.includes(measurand)) { logger.warn( - `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`, - ); - return; + `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}` + ) + return } if ( measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER && getConfigurationKey( chargingStation, - StandardParametersKey.MeterValuesSampledData, + StandardParametersKey.MeterValuesSampledData )?.value?.includes(measurand) === false ) { logger.debug( `${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId} not found in '${ StandardParametersKey.MeterValuesSampledData - }' OCPP parameter`, - ); - return; + }' OCPP parameter` + ) + return } const sampledValueTemplates: SampledValueTemplate[] = - chargingStation.getConnectorStatus(connectorId)!.MeterValues; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + chargingStation.getConnectorStatus(connectorId)!.MeterValues for ( let index = 0; - isNotEmptyArray(sampledValueTemplates) === true && index < sampledValueTemplates.length; + isNotEmptyArray(sampledValueTemplates) && index < sampledValueTemplates.length; index++ ) { if ( - OCPPConstants.OCPP_MEASURANDS_SUPPORTED.includes( - sampledValueTemplates[index]?.measurand ?? - MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, - ) === false + !OCPPConstants.OCPP_MEASURANDS_SUPPORTED.includes( + sampledValueTemplates[index]?.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + ) ) { logger.warn( - `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`, - ); + `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}` + ) } else if ( - phase && + phase != null && sampledValueTemplates[index]?.phase === phase && sampledValueTemplates[index]?.measurand === measurand && getConfigurationKey( chargingStation, - StandardParametersKey.MeterValuesSampledData, + StandardParametersKey.MeterValuesSampledData )?.value?.includes(measurand) === true ) { - return sampledValueTemplates[index]; + return sampledValueTemplates[index] } else if ( - !phase && - !sampledValueTemplates[index]?.phase && + phase == null && + sampledValueTemplates[index]?.phase == null && sampledValueTemplates[index]?.measurand === measurand && getConfigurationKey( chargingStation, - StandardParametersKey.MeterValuesSampledData, + StandardParametersKey.MeterValuesSampledData )?.value?.includes(measurand) === true ) { - return sampledValueTemplates[index]; + return sampledValueTemplates[index] } else if ( measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER && - (!sampledValueTemplates[index]?.measurand || + (sampledValueTemplates[index]?.measurand == null || sampledValueTemplates[index]?.measurand === measurand) ) { - return sampledValueTemplates[index]; + return sampledValueTemplates[index] } } if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) { - const errorMsg = `Missing MeterValues for default measurand '${measurand}' in template on connector id ${connectorId}`; - logger.error(`${chargingStation.logPrefix()} ${errorMsg}`); - throw new BaseError(errorMsg); + const errorMsg = `Missing MeterValues for default measurand '${measurand}' in template on connector id ${connectorId}` + logger.error(`${chargingStation.logPrefix()} ${errorMsg}`) + throw new BaseError(errorMsg) } logger.debug( - `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}`, - ); -}; + `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}` + ) +} const buildSampledValue = ( sampledValueTemplate: SampledValueTemplate, value: number, context?: MeterValueContext, - phase?: MeterValuePhase, + phase?: MeterValuePhase ): SampledValue => { - const sampledValueContext = context ?? sampledValueTemplate?.context; + const sampledValueContext = context ?? sampledValueTemplate?.context const sampledValueLocation = - sampledValueTemplate?.location ?? getMeasurandDefaultLocation(sampledValueTemplate.measurand!); - const sampledValuePhase = phase ?? sampledValueTemplate?.phase; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + sampledValueTemplate?.location ?? getMeasurandDefaultLocation(sampledValueTemplate.measurand!) + const sampledValuePhase = phase ?? sampledValueTemplate?.phase + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return { ...(!isNullOrUndefined(sampledValueTemplate.unit) && { - unit: sampledValueTemplate.unit, + unit: sampledValueTemplate.unit }), ...(!isNullOrUndefined(sampledValueContext) && { context: sampledValueContext }), ...(!isNullOrUndefined(sampledValueTemplate.measurand) && { - measurand: sampledValueTemplate.measurand, + measurand: sampledValueTemplate.measurand }), ...(!isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }), ...(!isNullOrUndefined(value) && { value: value.toString() }), - ...(!isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }), - } as SampledValue; -}; + ...(!isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }) + } as SampledValue +} const getMeasurandDefaultLocation = ( - measurandType: MeterValueMeasurand, + measurandType: MeterValueMeasurand ): MeterValueLocation | undefined => { switch (measurandType) { case MeterValueMeasurand.STATE_OF_CHARGE: - return MeterValueLocation.EV; + return MeterValueLocation.EV } -}; +} // const getMeasurandDefaultUnit = ( -// measurandType: MeterValueMeasurand, +// measurandType: MeterValueMeasurand // ): MeterValueUnit | undefined => { // switch (measurandType) { // case MeterValueMeasurand.CURRENT_EXPORT: // case MeterValueMeasurand.CURRENT_IMPORT: // case MeterValueMeasurand.CURRENT_OFFERED: -// return MeterValueUnit.AMP; +// return MeterValueUnit.AMP // case MeterValueMeasurand.ENERGY_ACTIVE_EXPORT_REGISTER: // case MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER: -// return MeterValueUnit.WATT_HOUR; +// return MeterValueUnit.WATT_HOUR // case MeterValueMeasurand.POWER_ACTIVE_EXPORT: // case MeterValueMeasurand.POWER_ACTIVE_IMPORT: // case MeterValueMeasurand.POWER_OFFERED: -// return MeterValueUnit.WATT; +// return MeterValueUnit.WATT // case MeterValueMeasurand.STATE_OF_CHARGE: -// return MeterValueUnit.PERCENT; +// return MeterValueUnit.PERCENT // case MeterValueMeasurand.VOLTAGE: -// return MeterValueUnit.VOLT; +// return MeterValueUnit.VOLT // } -// }; +// } +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class OCPPServiceUtils { - public static getMessageTypeString = getMessageTypeString; - public static sendAndSetConnectorStatus = sendAndSetConnectorStatus; - public static isIdTagAuthorized = isIdTagAuthorized; - public static buildTransactionEndMeterValue = buildTransactionEndMeterValue; - protected static getSampledValueTemplate = getSampledValueTemplate; - protected static buildSampledValue = buildSampledValue; + public static getMessageTypeString = getMessageTypeString + public static sendAndSetConnectorStatus = sendAndSetConnectorStatus + public static isIdTagAuthorized = isIdTagAuthorized + public static buildTransactionEndMeterValue = buildTransactionEndMeterValue + protected static getSampledValueTemplate = getSampledValueTemplate + protected static buildSampledValue = buildSampledValue - protected constructor() { + protected constructor () { // This is intentional } - public static ajvErrorsToErrorType(errors: ErrorObject[] | null | undefined): ErrorType { - if (isNotEmptyArray(errors) === true) { + public static ajvErrorsToErrorType (errors: ErrorObject[] | null | undefined): ErrorType { + if (isNotEmptyArray(errors)) { for (const error of errors as DefinedError[]) { switch (error.keyword) { case 'type': - return ErrorType.TYPE_CONSTRAINT_VIOLATION; + return ErrorType.TYPE_CONSTRAINT_VIOLATION case 'dependencies': case 'required': - return ErrorType.OCCURRENCE_CONSTRAINT_VIOLATION; + return ErrorType.OCCURRENCE_CONSTRAINT_VIOLATION case 'pattern': case 'format': - return ErrorType.PROPERTY_CONSTRAINT_VIOLATION; + return ErrorType.PROPERTY_CONSTRAINT_VIOLATION } } } - return ErrorType.FORMAT_VIOLATION; + return ErrorType.FORMAT_VIOLATION } - public static isRequestCommandSupported( + public static isRequestCommandSupported ( chargingStation: ChargingStation, - command: RequestCommand, + command: RequestCommand ): boolean { - const isRequestCommand = Object.values(RequestCommand).includes(command); + const isRequestCommand = Object.values(RequestCommand).includes(command) if ( - isRequestCommand === true && - !chargingStation.stationInfo?.commandsSupport?.outgoingCommands + isRequestCommand && + chargingStation.stationInfo?.commandsSupport?.outgoingCommands == null ) { - return true; + return true } else if ( - isRequestCommand === true && - chargingStation.stationInfo?.commandsSupport?.outgoingCommands?.[command] + isRequestCommand && + chargingStation.stationInfo?.commandsSupport?.outgoingCommands?.[command] != null ) { - return chargingStation.stationInfo?.commandsSupport?.outgoingCommands[command]; + return chargingStation.stationInfo?.commandsSupport?.outgoingCommands[command] } - logger.error(`${chargingStation.logPrefix()} Unknown outgoing OCPP command '${command}'`); - return false; + logger.error(`${chargingStation.logPrefix()} Unknown outgoing OCPP command '${command}'`) + return false } - public static isIncomingRequestCommandSupported( + public static isIncomingRequestCommandSupported ( chargingStation: ChargingStation, - command: IncomingRequestCommand, + command: IncomingRequestCommand ): boolean { const isIncomingRequestCommand = - Object.values(IncomingRequestCommand).includes(command); + Object.values(IncomingRequestCommand).includes(command) if ( - isIncomingRequestCommand === true && - !chargingStation.stationInfo?.commandsSupport?.incomingCommands + isIncomingRequestCommand && + chargingStation.stationInfo?.commandsSupport?.incomingCommands == null ) { - return true; + return true } else if ( - isIncomingRequestCommand === true && - chargingStation.stationInfo?.commandsSupport?.incomingCommands?.[command] + isIncomingRequestCommand && + chargingStation.stationInfo?.commandsSupport?.incomingCommands?.[command] != null ) { - return chargingStation.stationInfo?.commandsSupport?.incomingCommands[command]; + return chargingStation.stationInfo?.commandsSupport?.incomingCommands[command] } - logger.error(`${chargingStation.logPrefix()} Unknown incoming OCPP command '${command}'`); - return false; + logger.error(`${chargingStation.logPrefix()} Unknown incoming OCPP command '${command}'`) + return false } - public static isMessageTriggerSupported( + public static isMessageTriggerSupported ( chargingStation: ChargingStation, - messageTrigger: MessageTrigger, + messageTrigger: MessageTrigger ): boolean { - const isMessageTrigger = Object.values(MessageTrigger).includes(messageTrigger); - if (isMessageTrigger === true && !chargingStation.stationInfo?.messageTriggerSupport) { - return true; + const isMessageTrigger = Object.values(MessageTrigger).includes(messageTrigger) + if (isMessageTrigger && chargingStation.stationInfo?.messageTriggerSupport == null) { + return true } else if ( - isMessageTrigger === true && - chargingStation.stationInfo?.messageTriggerSupport?.[messageTrigger] + isMessageTrigger && + chargingStation.stationInfo?.messageTriggerSupport?.[messageTrigger] != null ) { - return chargingStation.stationInfo?.messageTriggerSupport[messageTrigger]; + return chargingStation.stationInfo?.messageTriggerSupport[messageTrigger] } logger.error( - `${chargingStation.logPrefix()} Unknown incoming OCPP message trigger '${messageTrigger}'`, - ); - return false; + `${chargingStation.logPrefix()} Unknown incoming OCPP message trigger '${messageTrigger}'` + ) + return false } - public static isConnectorIdValid( + public static isConnectorIdValid ( chargingStation: ChargingStation, ocppCommand: IncomingRequestCommand, - connectorId: number, + connectorId: number ): boolean { if (connectorId < 0) { logger.error( - `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector id ${connectorId}`, - ); - return false; + `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector id ${connectorId}` + ) + return false } - return true; + return true } public static convertDateToISOString(obj: T): void { for (const key in obj) { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-non-null-assertion if (isDate(obj![key])) { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - (obj![key] as string) = (obj![key] as Date).toISOString(); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-non-null-assertion + (obj![key] as string) = (obj![key] as Date).toISOString() + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-non-null-assertion } else if (obj![key] !== null && typeof obj![key] === 'object') { - // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion - OCPPServiceUtils.convertDateToISOString(obj![key] as T); + // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-non-null-assertion + OCPPServiceUtils.convertDateToISOString(obj![key] as T) } } } - public static startHeartbeatInterval(chargingStation: ChargingStation, interval: number): void { + public static startHeartbeatInterval (chargingStation: ChargingStation, interval: number): void { if (chargingStation.heartbeatSetInterval === undefined) { - chargingStation.startHeartbeat(); + chargingStation.startHeartbeat() } else if (chargingStation.getHeartbeatInterval() !== interval) { - chargingStation.restartHeartbeat(); + chargingStation.restartHeartbeat() } } @@ -1341,32 +1360,33 @@ export class OCPPServiceUtils { relativePath: string, ocppVersion: OCPPVersion, moduleName?: string, - methodName?: string, + methodName?: string ): JSONSchemaType { - const filePath = join(dirname(fileURLToPath(import.meta.url)), relativePath); + const filePath = join(dirname(fileURLToPath(import.meta.url)), relativePath) try { - return JSON.parse(readFileSync(filePath, 'utf8')) as JSONSchemaType; + return JSON.parse(readFileSync(filePath, 'utf8')) as JSONSchemaType } catch (error) { handleFileException( filePath, FileType.JsonSchema, error as NodeJS.ErrnoException, OCPPServiceUtils.logPrefix(ocppVersion, moduleName, methodName), - { throwError: false }, - ); - return {} as JSONSchemaType; + { throwError: false } + ) + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + return {} as JSONSchemaType } } - private static logPrefix = ( + private static readonly logPrefix = ( ocppVersion: OCPPVersion, moduleName?: string, - methodName?: string, + methodName?: string ): string => { const logMsg = isNotEmptyString(moduleName) && isNotEmptyString(methodName) ? ` OCPP ${ocppVersion} | ${moduleName}.${methodName}:` - : ` OCPP ${ocppVersion} |`; - return logPrefix(logMsg); - }; + : ` OCPP ${ocppVersion} |` + return logPrefix(logMsg) + } } diff --git a/src/charging-station/ocpp/index.ts b/src/charging-station/ocpp/index.ts index da1b751e..61e092d8 100644 --- a/src/charging-station/ocpp/index.ts +++ b/src/charging-station/ocpp/index.ts @@ -1,16 +1,16 @@ -export { OCPP16IncomingRequestService } from './1.6/OCPP16IncomingRequestService.js'; -export { OCPP16RequestService } from './1.6/OCPP16RequestService.js'; -export { OCPP16ResponseService } from './1.6/OCPP16ResponseService.js'; -export { OCPP20IncomingRequestService } from './2.0/OCPP20IncomingRequestService.js'; -export { OCPP20RequestService } from './2.0/OCPP20RequestService.js'; -export { OCPP20ResponseService } from './2.0/OCPP20ResponseService.js'; -export { OCPPIncomingRequestService } from './OCPPIncomingRequestService.js'; -export { OCPPRequestService } from './OCPPRequestService.js'; +export { OCPP16IncomingRequestService } from './1.6/OCPP16IncomingRequestService.js' +export { OCPP16RequestService } from './1.6/OCPP16RequestService.js' +export { OCPP16ResponseService } from './1.6/OCPP16ResponseService.js' +export { OCPP20IncomingRequestService } from './2.0/OCPP20IncomingRequestService.js' +export { OCPP20RequestService } from './2.0/OCPP20RequestService.js' +export { OCPP20ResponseService } from './2.0/OCPP20ResponseService.js' +export { OCPPIncomingRequestService } from './OCPPIncomingRequestService.js' +export { OCPPRequestService } from './OCPPRequestService.js' export { buildMeterValue, buildStatusNotificationRequest, buildTransactionEndMeterValue, getMessageTypeString, isIdTagAuthorized, - sendAndSetConnectorStatus, -} from './OCPPServiceUtils.js'; + sendAndSetConnectorStatus +} from './OCPPServiceUtils.js' diff --git a/src/charging-station/ui-server/AbstractUIServer.ts b/src/charging-station/ui-server/AbstractUIServer.ts index c6cc741b..78f04683 100644 --- a/src/charging-station/ui-server/AbstractUIServer.ts +++ b/src/charging-station/ui-server/AbstractUIServer.ts @@ -1,11 +1,11 @@ -import { type IncomingMessage, Server, type ServerResponse } from 'node:http'; -import { type Http2Server, createServer } from 'node:http2'; +import { type IncomingMessage, Server, type ServerResponse } from 'node:http' +import { type Http2Server, createServer } from 'node:http2' -import type { WebSocket } from 'ws'; +import type { WebSocket } from 'ws' -import type { AbstractUIService } from './ui-services/AbstractUIService.js'; -import { UIServiceFactory } from './ui-services/UIServiceFactory.js'; -import { BaseError } from '../../exception/index.js'; +import type { AbstractUIService } from './ui-services/AbstractUIService.js' +import { UIServiceFactory } from './ui-services/UIServiceFactory.js' +import { BaseError } from '../../exception/index.js' import { ApplicationProtocolVersion, AuthenticationType, @@ -16,116 +16,112 @@ import { ProtocolVersion, type RequestPayload, type ResponsePayload, - type UIServerConfiguration, -} from '../../types/index.js'; + type UIServerConfiguration +} from '../../types/index.js' export abstract class AbstractUIServer { - public readonly chargingStations: Map; - protected readonly httpServer: Server | Http2Server; - protected readonly responseHandlers: Map; - protected readonly uiServices: Map; + public readonly chargingStations: Map + protected readonly httpServer: Server | Http2Server + protected readonly responseHandlers: Map + protected readonly uiServices: Map - public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) { - this.chargingStations = new Map(); + public constructor (protected readonly uiServerConfiguration: UIServerConfiguration) { + this.chargingStations = new Map() switch (this.uiServerConfiguration.version) { case ApplicationProtocolVersion.VERSION_11: - this.httpServer = new Server(); - break; + this.httpServer = new Server() + break case ApplicationProtocolVersion.VERSION_20: - this.httpServer = createServer(); - break; + this.httpServer = createServer() + break default: throw new BaseError( - `Unsupported application protocol version ${this.uiServerConfiguration.version}`, - ); + `Unsupported application protocol version ${this.uiServerConfiguration.version}` + ) } - this.responseHandlers = new Map(); - this.uiServices = new Map(); + this.responseHandlers = new Map() + this.uiServices = new Map() } - public buildProtocolRequest( + public buildProtocolRequest ( id: string, procedureName: ProcedureName, - requestPayload: RequestPayload, + requestPayload: RequestPayload ): ProtocolRequest { - return [id, procedureName, requestPayload]; + return [id, procedureName, requestPayload] } - public buildProtocolResponse(id: string, responsePayload: ResponsePayload): ProtocolResponse { - return [id, responsePayload]; + public buildProtocolResponse (id: string, responsePayload: ResponsePayload): ProtocolResponse { + return [id, responsePayload] } - public stop(): void { - this.stopHttpServer(); - this.chargingStations.clear(); + public stop (): void { + this.stopHttpServer() + this.chargingStations.clear() } - public async sendInternalRequest(request: ProtocolRequest): Promise { - const protocolVersion = ProtocolVersion['0.0.1']; - this.registerProtocolVersionUIService(protocolVersion); - return this.uiServices + public async sendInternalRequest (request: ProtocolRequest): Promise { + const protocolVersion = ProtocolVersion['0.0.1'] + this.registerProtocolVersionUIService(protocolVersion) + return await (this.uiServices .get(protocolVersion) - ?.requestHandler(request) as Promise; + ?.requestHandler(request) as Promise) } - public hasResponseHandler(id: string): boolean { - return this.responseHandlers.has(id); + public hasResponseHandler (id: string): boolean { + return this.responseHandlers.has(id) } - protected startHttpServer(): void { - if (this.httpServer.listening === false) { - this.httpServer.listen(this.uiServerConfiguration.options); + protected startHttpServer (): void { + if (!this.httpServer.listening) { + this.httpServer.listen(this.uiServerConfiguration.options) } } - protected registerProtocolVersionUIService(version: ProtocolVersion): void { - if (this.uiServices.has(version) === false) { - this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this)); + protected registerProtocolVersionUIService (version: ProtocolVersion): void { + if (!this.uiServices.has(version)) { + this.uiServices.set(version, UIServiceFactory.getUIServiceImplementation(version, this)) } } - protected authenticate(req: IncomingMessage, next: (err?: Error) => void): void { - if (this.isBasicAuthEnabled() === true) { - if (this.isValidBasicAuth(req) === false) { - next(new BaseError('Unauthorized')); + protected authenticate (req: IncomingMessage, next: (err?: Error) => void): void { + if (this.isBasicAuthEnabled()) { + if (!this.isValidBasicAuth(req)) { + next(new BaseError('Unauthorized')) } - next(); + next() } - next(); + next() } - private stopHttpServer(): void { - if (this.httpServer.listening === true) { - this.httpServer.close(); + private stopHttpServer (): void { + if (this.httpServer.listening) { + this.httpServer.close() } } - private isBasicAuthEnabled(): boolean { + private isBasicAuthEnabled (): boolean { return ( this.uiServerConfiguration.authentication?.enabled === true && this.uiServerConfiguration.authentication?.type === AuthenticationType.BASIC_AUTH - ); + ) } - private isValidBasicAuth(req: IncomingMessage): boolean { - const authorizationHeader = req.headers.authorization ?? ''; - const authorizationToken = authorizationHeader.split(/\s+/).pop() ?? ''; - const authentication = Buffer.from(authorizationToken, 'base64').toString(); - const authenticationParts = authentication.split(/:/); - const username = authenticationParts.shift(); - const password = authenticationParts.join(':'); + private isValidBasicAuth (req: IncomingMessage): boolean { + const authorizationHeader = req.headers.authorization ?? '' + const authorizationToken = authorizationHeader.split(/\s+/).pop() ?? '' + const authentication = Buffer.from(authorizationToken, 'base64').toString() + const authenticationParts = authentication.split(/:/) + const username = authenticationParts.shift() + const password = authenticationParts.join(':') return ( this.uiServerConfiguration.authentication?.username === username && this.uiServerConfiguration.authentication?.password === password - ); + ) } - public abstract start(): void; - public abstract sendRequest(request: ProtocolRequest): void; - public abstract sendResponse(response: ProtocolResponse): void; - public abstract logPrefix( - moduleName?: string, - methodName?: string, - prefixSuffix?: string, - ): string; + public abstract start (): void + public abstract sendRequest (request: ProtocolRequest): void + public abstract sendResponse (response: ProtocolResponse): void + public abstract logPrefix (moduleName?: string, methodName?: string, prefixSuffix?: string): string } diff --git a/src/charging-station/ui-server/UIHttpServer.ts b/src/charging-station/ui-server/UIHttpServer.ts index eeab416d..ce1a6ed8 100644 --- a/src/charging-station/ui-server/UIHttpServer.ts +++ b/src/charging-station/ui-server/UIHttpServer.ts @@ -1,10 +1,10 @@ -import type { IncomingMessage, RequestListener, ServerResponse } from 'node:http'; +import type { IncomingMessage, RequestListener, ServerResponse } from 'node:http' -import { StatusCodes } from 'http-status-codes'; +import { StatusCodes } from 'http-status-codes' -import { AbstractUIServer } from './AbstractUIServer.js'; -import { UIServerUtils } from './UIServerUtils.js'; -import { BaseError } from '../../exception/index.js'; +import { AbstractUIServer } from './AbstractUIServer.js' +import { UIServerUtils } from './UIServerUtils.js' +import { BaseError } from '../../exception/index.js' import { ApplicationProtocolVersion, type ProcedureName, @@ -14,155 +14,148 @@ import { type ProtocolVersion, type RequestPayload, ResponseStatus, - type UIServerConfiguration, -} from '../../types/index.js'; -import { - Constants, - generateUUID, - isNotEmptyString, - isNullOrUndefined, - logPrefix, - logger, -} from '../../utils/index.js'; + type UIServerConfiguration +} from '../../types/index.js' +import { Constants, generateUUID, isNotEmptyString, logPrefix, logger } from '../../utils/index.js' -const moduleName = 'UIHttpServer'; +const moduleName = 'UIHttpServer' enum HttpMethods { GET = 'GET', PUT = 'PUT', POST = 'POST', - PATCH = 'PATCH', + PATCH = 'PATCH' } export class UIHttpServer extends AbstractUIServer { - public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) { - super(uiServerConfiguration); + public constructor (protected readonly uiServerConfiguration: UIServerConfiguration) { + super(uiServerConfiguration) } - public start(): void { - this.httpServer.on('request', this.requestListener.bind(this) as RequestListener); - this.startHttpServer(); + public start (): void { + this.httpServer.on('request', this.requestListener.bind(this) as RequestListener) + this.startHttpServer() } - public sendRequest(request: ProtocolRequest): void { + public sendRequest (request: ProtocolRequest): void { switch (this.uiServerConfiguration.version) { case ApplicationProtocolVersion.VERSION_20: - this.httpServer.emit('request', request); - break; + this.httpServer.emit('request', request) + break } } - public sendResponse(response: ProtocolResponse): void { - const [uuid, payload] = response; + public sendResponse (response: ProtocolResponse): void { + const [uuid, payload] = response try { - if (this.hasResponseHandler(uuid) === true) { - const res = this.responseHandlers.get(uuid) as ServerResponse; + if (this.hasResponseHandler(uuid)) { + const res = this.responseHandlers.get(uuid) as ServerResponse res .writeHead(this.responseStatusToStatusCode(payload.status), { - 'Content-Type': 'application/json', + 'Content-Type': 'application/json' }) - .end(JSON.stringify(payload)); + .end(JSON.stringify(payload)) } else { logger.error( - `${this.logPrefix(moduleName, 'sendResponse')} Response for unknown request id: ${uuid}`, - ); + `${this.logPrefix(moduleName, 'sendResponse')} Response for unknown request id: ${uuid}` + ) } } catch (error) { logger.error( `${this.logPrefix(moduleName, 'sendResponse')} Error at sending response id '${uuid}':`, - error, - ); + error + ) } finally { - this.responseHandlers.delete(uuid); + this.responseHandlers.delete(uuid) } } public logPrefix = (modName?: string, methodName?: string, prefixSuffix?: string): string => { - const logMsgPrefix = prefixSuffix ? `UI HTTP Server ${prefixSuffix}` : 'UI HTTP Server'; + const logMsgPrefix = prefixSuffix != null ? `UI HTTP Server ${prefixSuffix}` : 'UI HTTP Server' const logMsg = isNotEmptyString(modName) && isNotEmptyString(methodName) ? ` ${logMsgPrefix} | ${modName}.${methodName}:` - : ` ${logMsgPrefix} |`; - return logPrefix(logMsg); - }; + : ` ${logMsgPrefix} |` + return logPrefix(logMsg) + } - private requestListener(req: IncomingMessage, res: ServerResponse): void { + private requestListener (req: IncomingMessage, res: ServerResponse): void { this.authenticate(req, (err) => { - if (err) { + if (err != null) { res .writeHead(StatusCodes.UNAUTHORIZED, { 'Content-Type': 'text/plain', - 'WWW-Authenticate': 'Basic realm=users', + 'WWW-Authenticate': 'Basic realm=users' }) .end(`${StatusCodes.UNAUTHORIZED} Unauthorized`) - .destroy(); - req.destroy(); + .destroy() + req.destroy() } - }); + }) // Expected request URL pathname: /ui/:version/:procedureName const [protocol, version, procedureName] = req.url?.split('/').slice(1) as [ Protocol, ProtocolVersion, - ProcedureName, - ]; - const uuid = generateUUID(); - this.responseHandlers.set(uuid, res); + ProcedureName + ] + const uuid = generateUUID() + this.responseHandlers.set(uuid, res) try { - const fullProtocol = `${protocol}${version}`; - if (UIServerUtils.isProtocolAndVersionSupported(fullProtocol) === false) { - throw new BaseError(`Unsupported UI protocol version: '${fullProtocol}'`); + const fullProtocol = `${protocol}${version}` + if (!UIServerUtils.isProtocolAndVersionSupported(fullProtocol)) { + throw new BaseError(`Unsupported UI protocol version: '${fullProtocol}'`) } - this.registerProtocolVersionUIService(version); + this.registerProtocolVersionUIService(version) req.on('error', (error) => { logger.error( `${this.logPrefix(moduleName, 'requestListener.req.onerror')} Error on HTTP request:`, - error, - ); - }); + error + ) + }) if (req.method === HttpMethods.POST) { - const bodyBuffer: Uint8Array[] = []; + const bodyBuffer: Uint8Array[] = [] req .on('data', (chunk: Uint8Array) => { - bodyBuffer.push(chunk); + bodyBuffer.push(chunk) }) .on('end', () => { - const body = JSON.parse(Buffer.concat(bodyBuffer).toString()) as RequestPayload; + const body = JSON.parse(Buffer.concat(bodyBuffer).toString()) as RequestPayload this.uiServices .get(version) ?.requestHandler( this.buildProtocolRequest( uuid, procedureName, - body ?? Constants.EMPTY_FROZEN_OBJECT, - ), + body ?? Constants.EMPTY_FROZEN_OBJECT + ) ) .then((protocolResponse?: ProtocolResponse) => { - if (!isNullOrUndefined(protocolResponse)) { - this.sendResponse(protocolResponse!); + if (protocolResponse != null) { + this.sendResponse(protocolResponse) } }) - .catch(Constants.EMPTY_FUNCTION); - }); + .catch(Constants.EMPTY_FUNCTION) + }) } else { - throw new BaseError(`Unsupported HTTP method: '${req.method}'`); + throw new BaseError(`Unsupported HTTP method: '${req.method}'`) } } catch (error) { logger.error( `${this.logPrefix(moduleName, 'requestListener')} Handle HTTP request error:`, - error, - ); - this.sendResponse(this.buildProtocolResponse(uuid, { status: ResponseStatus.FAILURE })); + error + ) + this.sendResponse(this.buildProtocolResponse(uuid, { status: ResponseStatus.FAILURE })) } } - private responseStatusToStatusCode(status: ResponseStatus): StatusCodes { + private responseStatusToStatusCode (status: ResponseStatus): StatusCodes { switch (status) { case ResponseStatus.SUCCESS: - return StatusCodes.OK; + return StatusCodes.OK case ResponseStatus.FAILURE: - return StatusCodes.BAD_REQUEST; + return StatusCodes.BAD_REQUEST default: - return StatusCodes.INTERNAL_SERVER_ERROR; + return StatusCodes.INTERNAL_SERVER_ERROR } } } diff --git a/src/charging-station/ui-server/UIServerFactory.ts b/src/charging-station/ui-server/UIServerFactory.ts index b192ff35..fe2b36d2 100644 --- a/src/charging-station/ui-server/UIServerFactory.ts +++ b/src/charging-station/ui-server/UIServerFactory.ts @@ -1,50 +1,52 @@ -import chalk from 'chalk'; +import chalk from 'chalk' -import type { AbstractUIServer } from './AbstractUIServer.js'; -import { UIHttpServer } from './UIHttpServer.js'; -import { UIServerUtils } from './UIServerUtils.js'; -import { UIWebSocketServer } from './UIWebSocketServer.js'; +import type { AbstractUIServer } from './AbstractUIServer.js' +import { UIHttpServer } from './UIHttpServer.js' +import { UIServerUtils } from './UIServerUtils.js' +import { UIWebSocketServer } from './UIWebSocketServer.js' import { ApplicationProtocol, ApplicationProtocolVersion, - type UIServerConfiguration, -} from '../../types/index.js'; + type UIServerConfiguration +} from '../../types/index.js' +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class UIServerFactory { - private constructor() { + private constructor () { // This is intentional } - public static getUIServerImplementation( - uiServerConfiguration: UIServerConfiguration, + public static getUIServerImplementation ( + uiServerConfiguration: UIServerConfiguration ): AbstractUIServer | undefined { - if (UIServerUtils.isLoopback(uiServerConfiguration.options!.host!) === false) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (!UIServerUtils.isLoopback(uiServerConfiguration.options!.host!)) { console.warn( chalk.yellow( - 'Loopback address not detected in UI server configuration. This is not recommended.', - ), - ); + 'Loopback address not detected in UI server configuration. This is not recommended.' + ) + ) } uiServerConfiguration = { version: ApplicationProtocolVersion.VERSION_11, - ...uiServerConfiguration, - }; + ...uiServerConfiguration + } if ( uiServerConfiguration.type === ApplicationProtocol.WS && uiServerConfiguration.version !== ApplicationProtocolVersion.VERSION_11 ) { console.warn( chalk.yellow( - `Only version ${ApplicationProtocolVersion.VERSION_11} is supported for WebSocket UI server. Falling back to version ${ApplicationProtocolVersion.VERSION_11}.`, - ), - ); - uiServerConfiguration.version = ApplicationProtocolVersion.VERSION_11; + `Only version ${ApplicationProtocolVersion.VERSION_11} is supported for WebSocket UI server. Falling back to version ${ApplicationProtocolVersion.VERSION_11}.` + ) + ) + uiServerConfiguration.version = ApplicationProtocolVersion.VERSION_11 } switch (uiServerConfiguration.type) { case ApplicationProtocol.WS: - return new UIWebSocketServer(uiServerConfiguration); + return new UIWebSocketServer(uiServerConfiguration) case ApplicationProtocol.HTTP: - return new UIHttpServer(uiServerConfiguration); + return new UIHttpServer(uiServerConfiguration) } } } diff --git a/src/charging-station/ui-server/UIServerUtils.ts b/src/charging-station/ui-server/UIServerUtils.ts index e35a1f15..2056c209 100644 --- a/src/charging-station/ui-server/UIServerUtils.ts +++ b/src/charging-station/ui-server/UIServerUtils.ts @@ -1,60 +1,60 @@ -import type { IncomingMessage } from 'node:http'; +import type { IncomingMessage } from 'node:http' -import { Protocol, ProtocolVersion } from '../../types/index.js'; -import { logPrefix, logger } from '../../utils/index.js'; +import { Protocol, ProtocolVersion } from '../../types/index.js' +import { logPrefix, logger } from '../../utils/index.js' +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class UIServerUtils { - private constructor() { + private constructor () { // This is intentional } public static handleProtocols = ( protocols: Set, - // eslint-disable-next-line @typescript-eslint/no-unused-vars - request: IncomingMessage, + request: IncomingMessage ): string | false => { - let protocol: Protocol | undefined; - let version: ProtocolVersion | undefined; + let protocol: Protocol | undefined + let version: ProtocolVersion | undefined if (protocols.size === 0) { - return false; + return false } for (const fullProtocol of protocols) { - if (UIServerUtils.isProtocolAndVersionSupported(fullProtocol) === true) { - return fullProtocol; + if (UIServerUtils.isProtocolAndVersionSupported(fullProtocol)) { + return fullProtocol } } logger.error( `${logPrefix( - ' UI WebSocket Server |', - )} Unsupported protocol: '${protocol}' or protocol version: '${version}'`, - ); - return false; - }; + ' UI WebSocket Server |' + )} Unsupported protocol: '${protocol}' or protocol version: '${version}'` + ) + return false + } public static isProtocolAndVersionSupported = (protocolStr: string): boolean => { - const [protocol, version] = UIServerUtils.getProtocolAndVersion(protocolStr); + const [protocol, version] = UIServerUtils.getProtocolAndVersion(protocolStr) return ( - Object.values(Protocol).includes(protocol) === true && - Object.values(ProtocolVersion).includes(version) === true - ); - }; + Object.values(Protocol).includes(protocol) && Object.values(ProtocolVersion).includes(version) + ) + } public static getProtocolAndVersion = (protocolStr: string): [Protocol, ProtocolVersion] => { - const protocolIndex = protocolStr.indexOf(Protocol.UI); + const protocolIndex = protocolStr.indexOf(Protocol.UI) const protocol = protocolStr.substring( protocolIndex, - protocolIndex + Protocol.UI.length, - ) as Protocol; - const version = protocolStr.substring(protocolIndex + Protocol.UI.length) as ProtocolVersion; - return [protocol, version]; - }; + protocolIndex + Protocol.UI.length + ) as Protocol + const version = protocolStr.substring(protocolIndex + Protocol.UI.length) as ProtocolVersion + return [protocol, version] + } - public static isLoopback(address: string): boolean { + public static isLoopback (address: string): boolean { + // eslint-disable-next-line prefer-regex-literals const isLoopbackRegExp = new RegExp( // eslint-disable-next-line no-useless-escape /^localhost$|^127(?:\.\d+){0,2}\.\d+$|^(?:0*\:)*?:?0*1$/, - 'i', - ); - return isLoopbackRegExp.test(address); + 'i' + ) + return isLoopbackRegExp.test(address) } } diff --git a/src/charging-station/ui-server/UIWebSocketServer.ts b/src/charging-station/ui-server/UIWebSocketServer.ts index 42e4b3c2..097533e0 100644 --- a/src/charging-station/ui-server/UIWebSocketServer.ts +++ b/src/charging-station/ui-server/UIWebSocketServer.ts @@ -1,219 +1,215 @@ -import type { IncomingMessage } from 'node:http'; -import type { Duplex } from 'node:stream'; +import type { IncomingMessage } from 'node:http' +import type { Duplex } from 'node:stream' -import { StatusCodes } from 'http-status-codes'; -import { type RawData, WebSocket, WebSocketServer } from 'ws'; +import { StatusCodes } from 'http-status-codes' +import { type RawData, WebSocket, WebSocketServer } from 'ws' -import { AbstractUIServer } from './AbstractUIServer.js'; -import { UIServerUtils } from './UIServerUtils.js'; +import { AbstractUIServer } from './AbstractUIServer.js' +import { UIServerUtils } from './UIServerUtils.js' import { type ProtocolRequest, type ProtocolResponse, type UIServerConfiguration, - WebSocketCloseEventStatusCode, -} from '../../types/index.js'; + WebSocketCloseEventStatusCode +} from '../../types/index.js' import { Constants, getWebSocketCloseEventStatusString, isNotEmptyString, - isNullOrUndefined, logPrefix, logger, - validateUUID, -} from '../../utils/index.js'; + validateUUID +} from '../../utils/index.js' -const moduleName = 'UIWebSocketServer'; +const moduleName = 'UIWebSocketServer' export class UIWebSocketServer extends AbstractUIServer { - private readonly webSocketServer: WebSocketServer; + private readonly webSocketServer: WebSocketServer - public constructor(protected readonly uiServerConfiguration: UIServerConfiguration) { - super(uiServerConfiguration); + public constructor (protected readonly uiServerConfiguration: UIServerConfiguration) { + super(uiServerConfiguration) this.webSocketServer = new WebSocketServer({ handleProtocols: UIServerUtils.handleProtocols, - noServer: true, - }); + noServer: true + }) } - public start(): void { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.webSocketServer.on('connection', (ws: WebSocket, req: IncomingMessage): void => { - if (UIServerUtils.isProtocolAndVersionSupported(ws.protocol) === false) { + public start (): void { + this.webSocketServer.on('connection', (ws: WebSocket, _req: IncomingMessage): void => { + if (!UIServerUtils.isProtocolAndVersionSupported(ws.protocol)) { logger.error( `${this.logPrefix( moduleName, - 'start.server.onconnection', - )} Unsupported UI protocol version: '${ws.protocol}'`, - ); - ws.close(WebSocketCloseEventStatusCode.CLOSE_PROTOCOL_ERROR); + 'start.server.onconnection' + )} Unsupported UI protocol version: '${ws.protocol}'` + ) + ws.close(WebSocketCloseEventStatusCode.CLOSE_PROTOCOL_ERROR) } - const [, version] = UIServerUtils.getProtocolAndVersion(ws.protocol); - this.registerProtocolVersionUIService(version); + const [, version] = UIServerUtils.getProtocolAndVersion(ws.protocol) + this.registerProtocolVersionUIService(version) ws.on('message', (rawData) => { - const request = this.validateRawDataRequest(rawData); + const request = this.validateRawDataRequest(rawData) if (request === false) { - ws.close(WebSocketCloseEventStatusCode.CLOSE_INVALID_PAYLOAD); - return; + ws.close(WebSocketCloseEventStatusCode.CLOSE_INVALID_PAYLOAD) + return } - const [requestId] = request as ProtocolRequest; - this.responseHandlers.set(requestId, ws); + const [requestId] = request as ProtocolRequest + this.responseHandlers.set(requestId, ws) this.uiServices .get(version) ?.requestHandler(request) .then((protocolResponse?: ProtocolResponse) => { - if (!isNullOrUndefined(protocolResponse)) { - this.sendResponse(protocolResponse!); + if (protocolResponse != null) { + this.sendResponse(protocolResponse) } }) - .catch(Constants.EMPTY_FUNCTION); - }); + .catch(Constants.EMPTY_FUNCTION) + }) ws.on('error', (error) => { - logger.error(`${this.logPrefix(moduleName, 'start.ws.onerror')} WebSocket error:`, error); - }); + logger.error(`${this.logPrefix(moduleName, 'start.ws.onerror')} WebSocket error:`, error) + }) ws.on('close', (code, reason) => { logger.debug( `${this.logPrefix( moduleName, - 'start.ws.onclose', + 'start.ws.onclose' )} WebSocket closed: '${getWebSocketCloseEventStatusString( - code, - )}' - '${reason.toString()}'`, - ); - }); - }); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - this.httpServer.on('connect', (req: IncomingMessage, socket: Duplex, head: Buffer) => { + code + )}' - '${reason.toString()}'` + ) + }) + }) + this.httpServer.on('connect', (req: IncomingMessage, socket: Duplex, _head: Buffer) => { if (req.headers?.connection !== 'Upgrade' || req.headers?.upgrade !== 'websocket') { - socket.write(`HTTP/1.1 ${StatusCodes.BAD_REQUEST} Bad Request\r\n\r\n`); - socket.destroy(); + socket.write(`HTTP/1.1 ${StatusCodes.BAD_REQUEST} Bad Request\r\n\r\n`) + socket.destroy() } - }); + }) this.httpServer.on('upgrade', (req: IncomingMessage, socket: Duplex, head: Buffer): void => { this.authenticate(req, (err) => { - if (err) { - socket.write(`HTTP/1.1 ${StatusCodes.UNAUTHORIZED} Unauthorized\r\n\r\n`); - socket.destroy(); - return; + if (err != null) { + socket.write(`HTTP/1.1 ${StatusCodes.UNAUTHORIZED} Unauthorized\r\n\r\n`) + socket.destroy() + return } try { this.webSocketServer.handleUpgrade(req, socket, head, (ws: WebSocket) => { - this.webSocketServer.emit('connection', ws, req); - }); + this.webSocketServer.emit('connection', ws, req) + }) } catch (error) { logger.error( `${this.logPrefix( moduleName, - 'start.httpServer.on.upgrade', + 'start.httpServer.on.upgrade' )} Error at handling connection upgrade:`, - error, - ); + error + ) } - }); - }); - this.startHttpServer(); + }) + }) + this.startHttpServer() } - public sendRequest(request: ProtocolRequest): void { - this.broadcastToClients(JSON.stringify(request)); + public sendRequest (request: ProtocolRequest): void { + this.broadcastToClients(JSON.stringify(request)) } - public sendResponse(response: ProtocolResponse): void { - const responseId = response?.[0]; + public sendResponse (response: ProtocolResponse): void { + const responseId = response?.[0] try { if (this.hasResponseHandler(responseId)) { - const ws = this.responseHandlers.get(responseId) as WebSocket; + const ws = this.responseHandlers.get(responseId) as WebSocket if (ws?.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify(response)); + ws.send(JSON.stringify(response)) } else { logger.error( `${this.logPrefix( moduleName, - 'sendResponse', - )} Error at sending response id '${responseId}', WebSocket is not open: ${ws?.readyState}`, - ); + 'sendResponse' + )} Error at sending response id '${responseId}', WebSocket is not open: ${ws?.readyState}` + ) } } else { logger.error( `${this.logPrefix( moduleName, - 'sendResponse', - )} Response for unknown request id: ${responseId}`, - ); + 'sendResponse' + )} Response for unknown request id: ${responseId}` + ) } } catch (error) { logger.error( `${this.logPrefix( moduleName, - 'sendResponse', + 'sendResponse' )} Error at sending response id '${responseId}':`, - error, - ); + error + ) } finally { - this.responseHandlers.delete(responseId); + this.responseHandlers.delete(responseId) } } public logPrefix = (modName?: string, methodName?: string, prefixSuffix?: string): string => { - const logMsgPrefix = prefixSuffix - ? `UI WebSocket Server ${prefixSuffix}` - : 'UI WebSocket Server'; + const logMsgPrefix = + prefixSuffix != null ? `UI WebSocket Server ${prefixSuffix}` : 'UI WebSocket Server' const logMsg = isNotEmptyString(modName) && isNotEmptyString(methodName) ? ` ${logMsgPrefix} | ${modName}.${methodName}:` - : ` ${logMsgPrefix} |`; - return logPrefix(logMsg); - }; + : ` ${logMsgPrefix} |` + return logPrefix(logMsg) + } - private broadcastToClients(message: string): void { + private broadcastToClients (message: string): void { for (const client of this.webSocketServer.clients) { if (client?.readyState === WebSocket.OPEN) { - client.send(message); + client.send(message) } } } - private validateRawDataRequest(rawData: RawData): ProtocolRequest | false { + private validateRawDataRequest (rawData: RawData): ProtocolRequest | false { // logger.debug( // `${this.logPrefix( // moduleName, - // 'validateRawDataRequest', + // 'validateRawDataRequest' // // eslint-disable-next-line @typescript-eslint/no-base-to-string - // )} Raw data received in string format: ${rawData.toString()}`, - // ); + // )} Raw data received in string format: ${rawData.toString()}` + // ) // eslint-disable-next-line @typescript-eslint/no-base-to-string - const request = JSON.parse(rawData.toString()) as ProtocolRequest; + const request = JSON.parse(rawData.toString()) as ProtocolRequest - if (Array.isArray(request) === false) { + if (!Array.isArray(request)) { logger.error( `${this.logPrefix( moduleName, - 'validateRawDataRequest', + 'validateRawDataRequest' )} UI protocol request is not an array:`, - request, - ); - return false; + request + ) + return false } if (request.length !== 3) { logger.error( `${this.logPrefix(moduleName, 'validateRawDataRequest')} UI protocol request is malformed:`, - request, - ); - return false; + request + ) + return false } - if (validateUUID(request?.[0]) === false) { + if (!validateUUID(request?.[0])) { logger.error( `${this.logPrefix( moduleName, - 'validateRawDataRequest', + 'validateRawDataRequest' )} UI protocol request UUID field is invalid:`, - request, - ); - return false; + request + ) + return false } - return request; + return request } } diff --git a/src/charging-station/ui-server/ui-services/AbstractUIService.ts b/src/charging-station/ui-server/ui-services/AbstractUIService.ts index 8eb450a8..448734ba 100644 --- a/src/charging-station/ui-server/ui-services/AbstractUIService.ts +++ b/src/charging-station/ui-server/ui-services/AbstractUIService.ts @@ -1,7 +1,8 @@ -import { BaseError, type OCPPError } from '../../../exception/index.js'; +import { BaseError, type OCPPError } from '../../../exception/index.js' import { BroadcastChannelProcedureName, type BroadcastChannelRequestPayload, + type JsonType, ProcedureName, type ProtocolRequest, type ProtocolRequestHandler, @@ -9,19 +10,19 @@ import { type ProtocolVersion, type RequestPayload, type ResponsePayload, - ResponseStatus, -} from '../../../types/index.js'; -import { isNotEmptyArray, isNullOrUndefined, logger } from '../../../utils/index.js'; -import { Bootstrap } from '../../Bootstrap.js'; -import { UIServiceWorkerBroadcastChannel } from '../../broadcast-channel/UIServiceWorkerBroadcastChannel.js'; -import type { AbstractUIServer } from '../AbstractUIServer.js'; + ResponseStatus +} from '../../../types/index.js' +import { isNotEmptyArray, isNullOrUndefined, logger } from '../../../utils/index.js' +import { Bootstrap } from '../../Bootstrap.js' +import { UIServiceWorkerBroadcastChannel } from '../../broadcast-channel/UIServiceWorkerBroadcastChannel.js' +import type { AbstractUIServer } from '../AbstractUIServer.js' -const moduleName = 'AbstractUIService'; +const moduleName = 'AbstractUIService' export abstract class AbstractUIService { protected static readonly ProcedureNameToBroadCastChannelProcedureNameMapping = new Map< - ProcedureName, - BroadcastChannelProcedureName + ProcedureName, + BroadcastChannelProcedureName >([ [ProcedureName.START_CHARGING_STATION, BroadcastChannelProcedureName.START_CHARGING_STATION], [ProcedureName.STOP_CHARGING_STATION, BroadcastChannelProcedureName.STOP_CHARGING_STATION], @@ -29,11 +30,11 @@ export abstract class AbstractUIService { [ProcedureName.OPEN_CONNECTION, BroadcastChannelProcedureName.OPEN_CONNECTION], [ ProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR, - BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR, + BroadcastChannelProcedureName.START_AUTOMATIC_TRANSACTION_GENERATOR ], [ ProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR, - BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR, + BroadcastChannelProcedureName.STOP_AUTOMATIC_TRANSACTION_GENERATOR ], [ProcedureName.SET_SUPERVISION_URL, BroadcastChannelProcedureName.SET_SUPERVISION_URL], [ProcedureName.START_TRANSACTION, BroadcastChannelProcedureName.START_TRANSACTION], @@ -46,59 +47,56 @@ export abstract class AbstractUIService { [ProcedureName.DATA_TRANSFER, BroadcastChannelProcedureName.DATA_TRANSFER], [ ProcedureName.DIAGNOSTICS_STATUS_NOTIFICATION, - BroadcastChannelProcedureName.DIAGNOSTICS_STATUS_NOTIFICATION, + BroadcastChannelProcedureName.DIAGNOSTICS_STATUS_NOTIFICATION ], [ ProcedureName.FIRMWARE_STATUS_NOTIFICATION, - BroadcastChannelProcedureName.FIRMWARE_STATUS_NOTIFICATION, - ], - ]); - - protected readonly requestHandlers: Map; - private readonly version: ProtocolVersion; - private readonly uiServer: AbstractUIServer; - private readonly uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel; - private readonly broadcastChannelRequests: Map; - - constructor(uiServer: AbstractUIServer, version: ProtocolVersion) { - this.uiServer = uiServer; - this.version = version; + BroadcastChannelProcedureName.FIRMWARE_STATUS_NOTIFICATION + ] + ]) + + protected readonly requestHandlers: Map + private readonly version: ProtocolVersion + private readonly uiServer: AbstractUIServer + private readonly uiServiceWorkerBroadcastChannel: UIServiceWorkerBroadcastChannel + private readonly broadcastChannelRequests: Map + + constructor (uiServer: AbstractUIServer, version: ProtocolVersion) { + this.uiServer = uiServer + this.version = version this.requestHandlers = new Map([ [ProcedureName.LIST_CHARGING_STATIONS, this.handleListChargingStations.bind(this)], [ProcedureName.START_SIMULATOR, this.handleStartSimulator.bind(this)], - [ProcedureName.STOP_SIMULATOR, this.handleStopSimulator.bind(this)], - ]); - this.uiServiceWorkerBroadcastChannel = new UIServiceWorkerBroadcastChannel(this); - this.broadcastChannelRequests = new Map(); + [ProcedureName.STOP_SIMULATOR, this.handleStopSimulator.bind(this)] + ]) + this.uiServiceWorkerBroadcastChannel = new UIServiceWorkerBroadcastChannel(this) + this.broadcastChannelRequests = new Map() } - public async requestHandler(request: ProtocolRequest): Promise { - let messageId: string | undefined; - let command: ProcedureName | undefined; - let requestPayload: RequestPayload | undefined; - let responsePayload: ResponsePayload | undefined; + public async requestHandler (request: ProtocolRequest): Promise { + let messageId: string | undefined + let command: ProcedureName | undefined + let requestPayload: RequestPayload | undefined + let responsePayload: ResponsePayload | undefined try { - [messageId, command, requestPayload] = request; + [messageId, command, requestPayload] = request - if (this.requestHandlers.has(command) === false) { + if (!this.requestHandlers.has(command)) { throw new BaseError( `${command} is not implemented to handle message payload ${JSON.stringify( requestPayload, undefined, - 2, - )}`, - ); + 2 + )}` + ) } // Call the request handler to build the response payload - responsePayload = await this.requestHandlers.get(command)!( - messageId, - command, - requestPayload, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + responsePayload = await this.requestHandlers.get(command)!(messageId, command, requestPayload) } catch (error) { // Log - logger.error(`${this.logPrefix(moduleName, 'requestHandler')} Handle request error:`, error); + logger.error(`${this.logPrefix(moduleName, 'requestHandler')} Handle request error:`, error) responsePayload = { hashIds: requestPayload?.hashIds, status: ResponseStatus.FAILURE, @@ -107,110 +105,114 @@ export abstract class AbstractUIService { responsePayload, errorMessage: (error as OCPPError).message, errorStack: (error as OCPPError).stack, - errorDetails: (error as OCPPError).details, - }; + errorDetails: (error as OCPPError).details + } } if (!isNullOrUndefined(responsePayload)) { - return this.uiServer.buildProtocolResponse(messageId!, responsePayload!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.uiServer.buildProtocolResponse(messageId!, responsePayload!) } } - // public sendRequest( + // public sendRequest ( // messageId: string, // procedureName: ProcedureName, - // requestPayload: RequestPayload, + // requestPayload: RequestPayload // ): void { // this.uiServer.sendRequest( - // this.uiServer.buildProtocolRequest(messageId, procedureName, requestPayload), - // ); + // this.uiServer.buildProtocolRequest(messageId, procedureName, requestPayload) + // ) // } - public sendResponse(messageId: string, responsePayload: ResponsePayload): void { + public sendResponse (messageId: string, responsePayload: ResponsePayload): void { if (this.uiServer.hasResponseHandler(messageId)) { - this.uiServer.sendResponse(this.uiServer.buildProtocolResponse(messageId, responsePayload)); + this.uiServer.sendResponse(this.uiServer.buildProtocolResponse(messageId, responsePayload)) } } public logPrefix = (modName: string, methodName: string): string => { - return this.uiServer.logPrefix(modName, methodName, this.version); - }; + return this.uiServer.logPrefix(modName, methodName, this.version) + } - public deleteBroadcastChannelRequest(uuid: string): void { - this.broadcastChannelRequests.delete(uuid); + public deleteBroadcastChannelRequest (uuid: string): void { + this.broadcastChannelRequests.delete(uuid) } - public getBroadcastChannelExpectedResponses(uuid: string): number { - return this.broadcastChannelRequests.get(uuid)!; + public getBroadcastChannelExpectedResponses (uuid: string): number { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return this.broadcastChannelRequests.get(uuid)! } - protected handleProtocolRequest( + protected handleProtocolRequest ( uuid: string, procedureName: ProcedureName, - payload: RequestPayload, + payload: RequestPayload ): void { this.sendBroadcastChannelRequest( uuid, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion AbstractUIService.ProcedureNameToBroadCastChannelProcedureNameMapping.get(procedureName)!, - payload, - ); + payload + ) } - private sendBroadcastChannelRequest( + private sendBroadcastChannelRequest ( uuid: string, procedureName: BroadcastChannelProcedureName, - payload: BroadcastChannelRequestPayload, + payload: BroadcastChannelRequestPayload ): void { if (isNotEmptyArray(payload.hashIds)) { payload.hashIds = payload.hashIds ?.map((hashId) => { - if (hashId !== undefined && this.uiServer.chargingStations.has(hashId) === true) { - return hashId; + if (hashId !== undefined && this.uiServer.chargingStations.has(hashId)) { + return hashId } logger.warn( `${this.logPrefix( moduleName, - 'sendBroadcastChannelRequest', - )} Charging station with hashId '${hashId}' not found`, - ); + 'sendBroadcastChannelRequest' + )} Charging station with hashId '${hashId}' not found` + ) + return undefined }) - ?.filter((hashId) => !isNullOrUndefined(hashId)) as string[]; + ?.filter((hashId) => !isNullOrUndefined(hashId)) as string[] } else { - delete payload.hashIds; + delete payload.hashIds } const expectedNumberOfResponses = Array.isArray(payload.hashIds) ? payload.hashIds.length - : this.uiServer.chargingStations.size; + : this.uiServer.chargingStations.size if (expectedNumberOfResponses === 0) { throw new BaseError( - 'hashIds array in the request payload does not contain any valid charging station hashId', - ); + 'hashIds array in the request payload does not contain any valid charging station hashId' + ) } - this.uiServiceWorkerBroadcastChannel.sendRequest([uuid, procedureName, payload]); - this.broadcastChannelRequests.set(uuid, expectedNumberOfResponses); + this.uiServiceWorkerBroadcastChannel.sendRequest([uuid, procedureName, payload]) + this.broadcastChannelRequests.set(uuid, expectedNumberOfResponses) } - private handleListChargingStations(): ResponsePayload { + private handleListChargingStations (): ResponsePayload { return { status: ResponseStatus.SUCCESS, - chargingStations: [...this.uiServer.chargingStations.values()], - } as ResponsePayload; + chargingStations: [...this.uiServer.chargingStations.values()] as JsonType[] + } satisfies ResponsePayload } - private async handleStartSimulator(): Promise { + private async handleStartSimulator (): Promise { try { - await Bootstrap.getInstance().start(); - return { status: ResponseStatus.SUCCESS }; + await Bootstrap.getInstance().start() + return { status: ResponseStatus.SUCCESS } } catch { - return { status: ResponseStatus.FAILURE }; + return { status: ResponseStatus.FAILURE } } } - private async handleStopSimulator(): Promise { + private async handleStopSimulator (): Promise { try { - await Bootstrap.getInstance().stop(); - return { status: ResponseStatus.SUCCESS }; + await Bootstrap.getInstance().stop() + return { status: ResponseStatus.SUCCESS } } catch { - return { status: ResponseStatus.FAILURE }; + return { status: ResponseStatus.FAILURE } } } } diff --git a/src/charging-station/ui-server/ui-services/UIService001.ts b/src/charging-station/ui-server/ui-services/UIService001.ts index 142f97ef..674f8985 100644 --- a/src/charging-station/ui-server/ui-services/UIService001.ts +++ b/src/charging-station/ui-server/ui-services/UIService001.ts @@ -1,15 +1,15 @@ -import { AbstractUIService } from './AbstractUIService.js'; -import { type ProtocolRequestHandler, ProtocolVersion } from '../../../types/index.js'; -import type { AbstractUIServer } from '../AbstractUIServer.js'; +import { AbstractUIService } from './AbstractUIService.js' +import { type ProtocolRequestHandler, ProtocolVersion } from '../../../types/index.js' +import type { AbstractUIServer } from '../AbstractUIServer.js' export class UIService001 extends AbstractUIService { - constructor(uiServer: AbstractUIServer) { - super(uiServer, ProtocolVersion['0.0.1']); + constructor (uiServer: AbstractUIServer) { + super(uiServer, ProtocolVersion['0.0.1']) for (const procedureName of AbstractUIService.ProcedureNameToBroadCastChannelProcedureNameMapping.keys()) { this.requestHandlers.set( procedureName, - this.handleProtocolRequest.bind(this) as ProtocolRequestHandler, - ); + this.handleProtocolRequest.bind(this) as ProtocolRequestHandler + ) } } } diff --git a/src/charging-station/ui-server/ui-services/UIServiceFactory.ts b/src/charging-station/ui-server/ui-services/UIServiceFactory.ts index 6b7a0ee2..81fc2201 100644 --- a/src/charging-station/ui-server/ui-services/UIServiceFactory.ts +++ b/src/charging-station/ui-server/ui-services/UIServiceFactory.ts @@ -1,20 +1,21 @@ -import type { AbstractUIService } from './AbstractUIService.js'; -import { UIService001 } from './UIService001.js'; -import { ProtocolVersion } from '../../../types/index.js'; -import type { AbstractUIServer } from '../AbstractUIServer.js'; +import type { AbstractUIService } from './AbstractUIService.js' +import { UIService001 } from './UIService001.js' +import { ProtocolVersion } from '../../../types/index.js' +import type { AbstractUIServer } from '../AbstractUIServer.js' +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class UIServiceFactory { - private constructor() { + private constructor () { // This is intentional } - public static getUIServiceImplementation( + public static getUIServiceImplementation ( version: ProtocolVersion, - uiServer: AbstractUIServer, + uiServer: AbstractUIServer ): AbstractUIService { switch (version) { case ProtocolVersion['0.0.1']: - return new UIService001(uiServer); + return new UIService001(uiServer) } } } diff --git a/src/exception/BaseError.ts b/src/exception/BaseError.ts index 2163f6ee..6b9ea70f 100644 --- a/src/exception/BaseError.ts +++ b/src/exception/BaseError.ts @@ -1,14 +1,14 @@ export class BaseError extends Error { - public constructor(message?: string) { - super(message); - this.name = new.target.name; - Object.setPrototypeOf(this, new.target.prototype); + public constructor (message?: string) { + super(message) + this.name = new.target.name + Object.setPrototypeOf(this, new.target.prototype) typeof Error.captureStackTrace === 'function' ? Error.captureStackTrace(this, this.constructor) - : this.createStack(); + : this.createStack() } - private createStack(): void { - this.stack = new Error().stack; + private createStack (): void { + this.stack = new Error().stack } } diff --git a/src/exception/OCPPError.ts b/src/exception/OCPPError.ts index 4fb9ea16..d6347652 100644 --- a/src/exception/OCPPError.ts +++ b/src/exception/OCPPError.ts @@ -1,30 +1,29 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { BaseError } from './BaseError.js'; +import { BaseError } from './BaseError.js' import { - ErrorType, + type ErrorType, type IncomingRequestCommand, type JsonType, - type RequestCommand, -} from '../types/index.js'; -import { Constants } from '../utils/index.js'; + type RequestCommand +} from '../types/index.js' +import { Constants } from '../utils/index.js' export class OCPPError extends BaseError { - code: ErrorType; - command?: RequestCommand | IncomingRequestCommand; - details?: JsonType; + code: ErrorType + command?: RequestCommand | IncomingRequestCommand + details?: JsonType - constructor( + constructor ( code: ErrorType, message: string, command?: RequestCommand | IncomingRequestCommand, - details?: JsonType, + details?: JsonType ) { - super(message); + super(message) - this.code = code; - this.command = - command ?? (Constants.UNKNOWN_COMMAND as RequestCommand | IncomingRequestCommand); - this.details = details; + this.code = code + this.command = command ?? (Constants.UNKNOWN_COMMAND as RequestCommand | IncomingRequestCommand) + this.details = details } } diff --git a/src/exception/index.ts b/src/exception/index.ts index 027f9857..0916eecb 100644 --- a/src/exception/index.ts +++ b/src/exception/index.ts @@ -1,2 +1,2 @@ -export { BaseError } from './BaseError.js'; -export { OCPPError } from './OCPPError.js'; +export { BaseError } from './BaseError.js' +export { OCPPError } from './OCPPError.js' diff --git a/src/performance/PerformanceStatistics.ts b/src/performance/PerformanceStatistics.ts index 1246e1b2..2d557eaa 100644 --- a/src/performance/PerformanceStatistics.ts +++ b/src/performance/PerformanceStatistics.ts @@ -1,10 +1,10 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { type PerformanceEntry, PerformanceObserver, performance } from 'node:perf_hooks'; -import type { URL } from 'node:url'; -import { parentPort } from 'node:worker_threads'; +import { type PerformanceEntry, PerformanceObserver, performance } from 'node:perf_hooks' +import type { URL } from 'node:url' +import { parentPort } from 'node:worker_threads' -import { secondsToMilliseconds } from 'date-fns'; +import { secondsToMilliseconds } from 'date-fns' import { ConfigurationSection, @@ -14,8 +14,8 @@ import { type RequestCommand, type Statistics, type StorageConfiguration, - type TimestampedData, -} from '../types/index.js'; + type TimestampedData +} from '../types/index.js' import { CircularArray, Configuration, @@ -32,246 +32,263 @@ import { median, min, nthPercentile, - stdDeviation, -} from '../utils/index.js'; + stdDeviation +} from '../utils/index.js' export class PerformanceStatistics { private static readonly instances: Map = new Map< - string, - PerformanceStatistics - >(); + string, + PerformanceStatistics + >() - private readonly objId: string; - private readonly objName: string; - private performanceObserver!: PerformanceObserver; - private readonly statistics: Statistics; - private displayInterval?: NodeJS.Timeout; + private readonly objId: string + private readonly objName: string + private performanceObserver!: PerformanceObserver + private readonly statistics: Statistics + private displayInterval?: NodeJS.Timeout - private constructor(objId: string, objName: string, uri: URL) { - this.objId = objId; - this.objName = objName; - this.initializePerformanceObserver(); + private constructor (objId: string, objName: string, uri: URL) { + this.objId = objId + this.objName = objName + this.initializePerformanceObserver() this.statistics = { id: this.objId ?? 'Object id not specified', name: this.objName ?? 'Object name not specified', uri: uri.toString(), createdAt: new Date(), - statisticsData: new Map(), - }; + statisticsData: new Map() + } } - public static getInstance( + public static getInstance ( objId: string, objName: string, - uri: URL, + uri: URL ): PerformanceStatistics | undefined { if (!PerformanceStatistics.instances.has(objId)) { - PerformanceStatistics.instances.set(objId, new PerformanceStatistics(objId, objName, uri)); + PerformanceStatistics.instances.set(objId, new PerformanceStatistics(objId, objName, uri)) } - return PerformanceStatistics.instances.get(objId); + return PerformanceStatistics.instances.get(objId) } - public static beginMeasure(id: string): string { - const markId = `${id.charAt(0).toUpperCase()}${id.slice(1)}~${generateUUID()}`; - performance.mark(markId); - return markId; + public static beginMeasure (id: string): string { + const markId = `${id.charAt(0).toUpperCase()}${id.slice(1)}~${generateUUID()}` + performance.mark(markId) + return markId } - public static endMeasure(name: string, markId: string): void { + public static endMeasure (name: string, markId: string): void { try { - performance.measure(name, markId); + performance.measure(name, markId) } catch (error) { if (error instanceof Error && error.message.includes('performance mark has not been set')) { /** Ignore */ } else { - throw error; + throw error } } - performance.clearMarks(markId); - performance.clearMeasures(name); + performance.clearMarks(markId) + performance.clearMeasures(name) } - public addRequestStatistic( + public addRequestStatistic ( command: RequestCommand | IncomingRequestCommand, - messageType: MessageType, + messageType: MessageType ): void { switch (messageType) { case MessageType.CALL_MESSAGE: if ( this.statistics.statisticsData.has(command) && - this.statistics.statisticsData.get(command)?.requestCount + this.statistics.statisticsData.get(command)?.requestCount != null ) { - ++this.statistics.statisticsData.get(command)!.requestCount!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.statistics.statisticsData.get(command)!.requestCount! } else { this.statistics.statisticsData.set(command, { ...this.statistics.statisticsData.get(command), - requestCount: 1, - }); + requestCount: 1 + }) } - break; + break case MessageType.CALL_RESULT_MESSAGE: if ( this.statistics.statisticsData.has(command) && - this.statistics.statisticsData.get(command)?.responseCount + this.statistics.statisticsData.get(command)?.responseCount != null ) { - ++this.statistics.statisticsData.get(command)!.responseCount!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.statistics.statisticsData.get(command)!.responseCount! } else { this.statistics.statisticsData.set(command, { ...this.statistics.statisticsData.get(command), - responseCount: 1, - }); + responseCount: 1 + }) } - break; + break case MessageType.CALL_ERROR_MESSAGE: if ( this.statistics.statisticsData.has(command) && - this.statistics.statisticsData.get(command)?.errorCount + this.statistics.statisticsData.get(command)?.errorCount != null ) { - ++this.statistics.statisticsData.get(command)!.errorCount!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ++this.statistics.statisticsData.get(command)!.errorCount! } else { this.statistics.statisticsData.set(command, { ...this.statistics.statisticsData.get(command), - errorCount: 1, - }); + errorCount: 1 + }) } - break; + break default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - logger.error(`${this.logPrefix()} wrong message type ${messageType}`); - break; + logger.error(`${this.logPrefix()} wrong message type ${messageType}`) + break } } - public start(): void { - this.startLogStatisticsInterval(); + public start (): void { + this.startLogStatisticsInterval() const performanceStorageConfiguration = Configuration.getConfigurationSection( - ConfigurationSection.performanceStorage, - ); - if (performanceStorageConfiguration.enabled) { + ConfigurationSection.performanceStorage + ) + if (performanceStorageConfiguration.enabled === true) { logger.info( `${this.logPrefix()} storage enabled: type ${performanceStorageConfiguration.type}, uri: ${ performanceStorageConfiguration.uri - }`, - ); + }` + ) } } - public stop(): void { - this.stopLogStatisticsInterval(); - performance.clearMarks(); - performance.clearMeasures(); - this.performanceObserver?.disconnect(); + public stop (): void { + this.stopLogStatisticsInterval() + performance.clearMarks() + performance.clearMeasures() + this.performanceObserver?.disconnect() } - public restart(): void { - this.stop(); - this.start(); + public restart (): void { + this.stop() + this.start() } - private initializePerformanceObserver(): void { + private initializePerformanceObserver (): void { this.performanceObserver = new PerformanceObserver((performanceObserverList) => { - const lastPerformanceEntry = performanceObserverList.getEntries()[0]; + const lastPerformanceEntry = performanceObserverList.getEntries()[0] // logger.debug( // `${this.logPrefix()} '${lastPerformanceEntry.name}' performance entry: %j`, - // lastPerformanceEntry, - // ); - this.addPerformanceEntryToStatistics(lastPerformanceEntry); - }); - this.performanceObserver.observe({ entryTypes: ['measure'] }); + // lastPerformanceEntry + // ) + this.addPerformanceEntryToStatistics(lastPerformanceEntry) + }) + this.performanceObserver.observe({ entryTypes: ['measure'] }) } - private logStatistics(): void { + private logStatistics (): void { logger.info(`${this.logPrefix()}`, { ...this.statistics, - statisticsData: JSONStringifyWithMapSupport(this.statistics.statisticsData), - }); + statisticsData: JSONStringifyWithMapSupport(this.statistics.statisticsData) + }) } - private startLogStatisticsInterval(): void { + private startLogStatisticsInterval (): void { const logConfiguration = Configuration.getConfigurationSection( - ConfigurationSection.log, - ); - const logStatisticsInterval = logConfiguration.enabled - ? logConfiguration.statisticsInterval! - : 0; - if (logStatisticsInterval > 0 && !this.displayInterval) { + ConfigurationSection.log + ) + const logStatisticsInterval = + logConfiguration.enabled === true + ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + logConfiguration.statisticsInterval! + : 0 + if (logStatisticsInterval > 0 && this.displayInterval == null) { this.displayInterval = setInterval(() => { - this.logStatistics(); - }, secondsToMilliseconds(logStatisticsInterval)); + this.logStatistics() + }, secondsToMilliseconds(logStatisticsInterval)) logger.info( - `${this.logPrefix()} logged every ${formatDurationSeconds(logStatisticsInterval)}`, - ); - } else if (this.displayInterval) { + `${this.logPrefix()} logged every ${formatDurationSeconds(logStatisticsInterval)}` + ) + } else if (this.displayInterval != null) { logger.info( - `${this.logPrefix()} already logged every ${formatDurationSeconds(logStatisticsInterval)}`, - ); - } else if (logConfiguration.enabled) { + `${this.logPrefix()} already logged every ${formatDurationSeconds(logStatisticsInterval)}` + ) + } else if (logConfiguration.enabled === true) { logger.info( - `${this.logPrefix()} log interval is set to ${logStatisticsInterval}. Not logging statistics`, - ); + `${this.logPrefix()} log interval is set to ${logStatisticsInterval}. Not logging statistics` + ) } } - private stopLogStatisticsInterval(): void { - if (this.displayInterval) { - clearInterval(this.displayInterval); - delete this.displayInterval; + private stopLogStatisticsInterval (): void { + if (this.displayInterval != null) { + clearInterval(this.displayInterval) + delete this.displayInterval } } - private addPerformanceEntryToStatistics(entry: PerformanceEntry): void { + private addPerformanceEntryToStatistics (entry: PerformanceEntry): void { // Initialize command statistics if (!this.statistics.statisticsData.has(entry.name)) { - this.statistics.statisticsData.set(entry.name, {}); + this.statistics.statisticsData.set(entry.name, {}) } // Update current statistics + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.statistics.statisticsData.get(entry.name)!.timeMeasurementCount = - (this.statistics.statisticsData.get(entry.name)?.timeMeasurementCount ?? 0) + 1; - this.statistics.statisticsData.get(entry.name)!.currentTimeMeasurement = entry.duration; + (this.statistics.statisticsData.get(entry.name)?.timeMeasurementCount ?? 0) + 1 + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.statistics.statisticsData.get(entry.name)!.currentTimeMeasurement = entry.duration + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.statistics.statisticsData.get(entry.name)!.minTimeMeasurement = min( entry.duration, - this.statistics.statisticsData.get(entry.name)?.minTimeMeasurement ?? Infinity, - ); + this.statistics.statisticsData.get(entry.name)?.minTimeMeasurement ?? Infinity + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.statistics.statisticsData.get(entry.name)!.maxTimeMeasurement = max( entry.duration, - this.statistics.statisticsData.get(entry.name)?.maxTimeMeasurement ?? -Infinity, - ); + this.statistics.statisticsData.get(entry.name)?.maxTimeMeasurement ?? -Infinity + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.statistics.statisticsData.get(entry.name)!.totalTimeMeasurement = - (this.statistics.statisticsData.get(entry.name)?.totalTimeMeasurement ?? 0) + entry.duration; + (this.statistics.statisticsData.get(entry.name)?.totalTimeMeasurement ?? 0) + entry.duration this.statistics.statisticsData.get(entry.name)?.measurementTimeSeries instanceof CircularArray ? this.statistics.statisticsData - .get(entry.name) - ?.measurementTimeSeries?.push({ timestamp: entry.startTime, value: entry.duration }) - : (this.statistics.statisticsData.get(entry.name)!.measurementTimeSeries = + .get(entry.name) + ?.measurementTimeSeries?.push({ timestamp: entry.startTime, value: entry.duration }) + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (this.statistics.statisticsData.get(entry.name)!.measurementTimeSeries = new CircularArray(Constants.DEFAULT_CIRCULAR_BUFFER_CAPACITY, { timestamp: entry.startTime, - value: entry.duration, - })); + value: entry.duration + })) const timeMeasurementValues = extractTimeSeriesValues( - this.statistics.statisticsData.get(entry.name)!.measurementTimeSeries!, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.statistics.statisticsData.get(entry.name)!.measurementTimeSeries! + ) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.statistics.statisticsData.get(entry.name)!.avgTimeMeasurement = - average(timeMeasurementValues); + average(timeMeasurementValues) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.statistics.statisticsData.get(entry.name)!.medTimeMeasurement = - median(timeMeasurementValues); + median(timeMeasurementValues) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.statistics.statisticsData.get(entry.name)!.ninetyFiveThPercentileTimeMeasurement = - nthPercentile(timeMeasurementValues, 95); + nthPercentile(timeMeasurementValues, 95) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.statistics.statisticsData.get(entry.name)!.stdDevTimeMeasurement = stdDeviation( timeMeasurementValues, - this.statistics.statisticsData.get(entry.name)!.avgTimeMeasurement, - ); - this.statistics.updatedAt = new Date(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.statistics.statisticsData.get(entry.name)!.avgTimeMeasurement + ) + this.statistics.updatedAt = new Date() if ( Configuration.getConfigurationSection( - ConfigurationSection.performanceStorage, - ).enabled + ConfigurationSection.performanceStorage + ).enabled === true ) { - parentPort?.postMessage(buildPerformanceStatisticsMessage(this.statistics)); + parentPort?.postMessage(buildPerformanceStatisticsMessage(this.statistics)) } } - private logPrefix = (): string => { - return logPrefix(` ${this.objName} | Performance statistics`); - }; + private readonly logPrefix = (): string => { + return logPrefix(` ${this.objName} | Performance statistics`) + } } diff --git a/src/performance/index.ts b/src/performance/index.ts index d4d47596..1cbed378 100644 --- a/src/performance/index.ts +++ b/src/performance/index.ts @@ -1,3 +1,3 @@ -export { PerformanceStatistics } from './PerformanceStatistics.js'; -export { type Storage } from './storage/Storage.js'; -export { StorageFactory } from './storage/StorageFactory.js'; +export { PerformanceStatistics } from './PerformanceStatistics.js' +export { type Storage } from './storage/Storage.js' +export { StorageFactory } from './storage/StorageFactory.js' diff --git a/src/performance/storage/JsonFileStorage.ts b/src/performance/storage/JsonFileStorage.ts index ff119959..3fe5e58e 100644 --- a/src/performance/storage/JsonFileStorage.ts +++ b/src/performance/storage/JsonFileStorage.ts @@ -1,90 +1,90 @@ // Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { closeSync, existsSync, mkdirSync, openSync, writeSync } from 'node:fs'; -import { dirname } from 'node:path'; +import { closeSync, existsSync, mkdirSync, openSync, writeSync } from 'node:fs' +import { dirname } from 'node:path' -import { Storage } from './Storage.js'; -import { BaseError } from '../../exception/index.js'; -import { FileType, type Statistics } from '../../types/index.js'; +import { Storage } from './Storage.js' +import { BaseError } from '../../exception/index.js' +import { FileType, type Statistics } from '../../types/index.js' import { AsyncLock, AsyncLockType, JSONStringifyWithMapSupport, - handleFileException, - isNullOrUndefined, -} from '../../utils/index.js'; + handleFileException +} from '../../utils/index.js' export class JsonFileStorage extends Storage { - private static performanceRecords: Map; + private static performanceRecords: Map - private fd?: number; + private fd?: number - constructor(storageUri: string, logPrefix: string) { - super(storageUri, logPrefix); - this.dbName = this.storageUri.pathname; + constructor (storageUri: string, logPrefix: string) { + super(storageUri, logPrefix) + this.dbName = this.storageUri.pathname } - public storePerformanceStatistics(performanceStatistics: Statistics): void { - this.checkPerformanceRecordsFile(); - JsonFileStorage.performanceRecords.set(performanceStatistics.id, performanceStatistics); + public storePerformanceStatistics (performanceStatistics: Statistics): void { + 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), 0, - 'utf8', - ); + 'utf8' + ) }).catch((error) => { handleFileException( this.dbName, FileType.PerformanceRecords, error as NodeJS.ErrnoException, - this.logPrefix, - ); - }); + this.logPrefix + ) + }) } - public open(): void { - JsonFileStorage.performanceRecords = new Map(); + public open (): void { + JsonFileStorage.performanceRecords = new Map() try { - if (isNullOrUndefined(this?.fd)) { + if (this?.fd == null) { if (!existsSync(dirname(this.dbName))) { - mkdirSync(dirname(this.dbName), { recursive: true }); + mkdirSync(dirname(this.dbName), { recursive: true }) } - this.fd = openSync(this.dbName, 'w'); + this.fd = openSync(this.dbName, 'w') } } catch (error) { handleFileException( this.dbName, FileType.PerformanceRecords, error as NodeJS.ErrnoException, - this.logPrefix, - ); + this.logPrefix + ) } } - public close(): void { - JsonFileStorage.performanceRecords.clear(); + public close (): void { + JsonFileStorage.performanceRecords.clear() try { - if (this?.fd) { - closeSync(this.fd); - delete this?.fd; + if (this?.fd != null) { + closeSync(this.fd) + delete this?.fd } } catch (error) { handleFileException( this.dbName, FileType.PerformanceRecords, error as NodeJS.ErrnoException, - this.logPrefix, - ); + this.logPrefix + ) } } - private checkPerformanceRecordsFile(): void { - if (!this?.fd) { + private checkPerformanceRecordsFile (): void { + if (this?.fd == null) { throw new BaseError( - `${this.logPrefix} Performance records '${this.dbName}' file descriptor not found`, - ); + `${this.logPrefix} Performance records '${this.dbName}' file descriptor not found` + ) } } } diff --git a/src/performance/storage/MikroOrmStorage.ts b/src/performance/storage/MikroOrmStorage.ts index 8d2cffca..4e2091a5 100644 --- a/src/performance/storage/MikroOrmStorage.ts +++ b/src/performance/storage/MikroOrmStorage.ts @@ -1,92 +1,91 @@ // Copyright Jerome Benoit. 2021-2023. All Rights Reserved. import { - Configuration, - Connection, + type Configuration, + type Connection, type IDatabaseDriver, MikroORM, - type Options, -} from '@mikro-orm/core'; -import { TsMorphMetadataProvider } from '@mikro-orm/reflection'; + type Options +} from '@mikro-orm/core' +import { TsMorphMetadataProvider } from '@mikro-orm/reflection' -import { Storage } from './Storage.js'; +import { Storage } from './Storage.js' import { type MikroOrmDbType, PerformanceData, PerformanceRecord, type Statistics, - StorageType, -} from '../../types/index.js'; -import { Constants } from '../../utils/index.js'; + StorageType +} from '../../types/index.js' +import { Constants } from '../../utils/index.js' export class MikroOrmStorage extends Storage { - private storageType: StorageType; - private orm?: MikroORM; + private readonly storageType: StorageType + private orm?: MikroORM - constructor(storageUri: string, logPrefix: string, storageType: StorageType) { - super(storageUri, logPrefix); - this.storageType = storageType; - this.dbName = this.getDBName(); + constructor (storageUri: string, logPrefix: string, storageType: StorageType) { + super(storageUri, logPrefix) + this.storageType = storageType + this.dbName = this.getDBName() } - // eslint-disable-next-line @typescript-eslint/no-unused-vars - public async storePerformanceStatistics(performanceStatistics: Statistics): Promise { + public async storePerformanceStatistics (performanceStatistics: Statistics): Promise { try { - const performanceRecord = new PerformanceRecord(); - await this.orm?.em.persistAndFlush(performanceRecord); + const performanceRecord = new PerformanceRecord() + await this.orm?.em.persistAndFlush(performanceRecord) } catch (error) { - this.handleDBError(this.storageType, error as Error, Constants.PERFORMANCE_RECORDS_TABLE); + this.handleDBError(this.storageType, error as Error, Constants.PERFORMANCE_RECORDS_TABLE) } } - public async open(): Promise { + public async open (): Promise { try { - if (!this?.orm) { - this.orm = await MikroORM.init(this.getOptions(), true); + if (this?.orm == null) { + this.orm = await MikroORM.init(this.getOptions(), true) } } catch (error) { - this.handleDBError(this.storageType, error as Error); + this.handleDBError(this.storageType, error as Error) } } - public async close(): Promise { + public async close (): Promise { try { - if (this?.orm) { - await this.orm.close(); - delete this?.orm; + if (this?.orm != null) { + await this.orm.close() + delete this?.orm } } catch (error) { - this.handleDBError(this.storageType, error as Error); + this.handleDBError(this.storageType, error as Error) } } - private getDBName(): string { + private getDBName (): string { if (this.storageType === StorageType.SQLITE) { - return `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`; + return `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db` } return ( this.storageUri.pathname.replace(/(?:^\/)|(?:\/$)/g, '') ?? Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME - ); + ) } - private getOptions(): - | Configuration> - | Options> { + private getOptions (): + | Configuration> + | Options> { return { metadataProvider: TsMorphMetadataProvider, entities: [PerformanceRecord, PerformanceData], type: this.storageType as MikroOrmDbType, - clientUrl: this.getClientUrl(), - }; + clientUrl: this.getClientUrl() + } } - private getClientUrl(): string | undefined { + private getClientUrl (): string | undefined { switch (this.storageType) { case StorageType.SQLITE: case StorageType.MARIA_DB: case StorageType.MYSQL: - return this.storageUri.toString(); + return this.storageUri.toString() } } } diff --git a/src/performance/storage/MongoDBStorage.ts b/src/performance/storage/MongoDBStorage.ts index e27b33c4..70256fc9 100644 --- a/src/performance/storage/MongoDBStorage.ts +++ b/src/performance/storage/MongoDBStorage.ts @@ -1,73 +1,73 @@ // Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { MongoClient } from 'mongodb'; +import { MongoClient } from 'mongodb' -import { Storage } from './Storage.js'; -import { BaseError } from '../../exception/index.js'; -import { type Statistics, StorageType } from '../../types/index.js'; -import { Constants } from '../../utils/index.js'; +import { Storage } from './Storage.js' +import { BaseError } from '../../exception/index.js' +import { type Statistics, StorageType } from '../../types/index.js' +import { Constants } from '../../utils/index.js' export class MongoDBStorage extends Storage { - private readonly client?: MongoClient; - private connected: boolean; + private readonly client?: MongoClient + private connected: boolean - constructor(storageUri: string, logPrefix: string) { - super(storageUri, logPrefix); - this.client = new MongoClient(this.storageUri.toString()); - this.connected = false; + constructor (storageUri: string, logPrefix: string) { + super(storageUri, logPrefix) + this.client = new MongoClient(this.storageUri.toString()) + this.connected = false this.dbName = this.storageUri.pathname.replace(/(?:^\/)|(?:\/$)/g, '') ?? - Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME; + Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME } - public async storePerformanceStatistics(performanceStatistics: Statistics): Promise { + public async storePerformanceStatistics (performanceStatistics: Statistics): Promise { try { - this.checkDBConnection(); + this.checkDBConnection() await this.client ?.db(this.dbName) .collection(Constants.PERFORMANCE_RECORDS_TABLE) - .replaceOne({ id: performanceStatistics.id }, performanceStatistics, { upsert: true }); + .replaceOne({ id: performanceStatistics.id }, performanceStatistics, { upsert: true }) } catch (error) { - this.handleDBError(StorageType.MONGO_DB, error as Error, Constants.PERFORMANCE_RECORDS_TABLE); + this.handleDBError(StorageType.MONGO_DB, error as Error, Constants.PERFORMANCE_RECORDS_TABLE) } } - public async open(): Promise { + public async open (): Promise { try { - if (!this.connected && this?.client) { - await this.client.connect(); - this.connected = true; + if (!this.connected && this?.client != null) { + await this.client.connect() + this.connected = true } } catch (error) { - this.handleDBError(StorageType.MONGO_DB, error as Error); + this.handleDBError(StorageType.MONGO_DB, error as Error) } } - public async close(): Promise { + public async close (): Promise { try { - if (this.connected && this?.client) { - await this.client.close(); - this.connected = false; + if (this.connected && this?.client != null) { + await this.client.close() + this.connected = false } } catch (error) { - this.handleDBError(StorageType.MONGO_DB, error as Error); + this.handleDBError(StorageType.MONGO_DB, error as Error) } } - private checkDBConnection() { - if (!this?.client) { + private checkDBConnection (): void { + if (this?.client == null) { throw new BaseError( `${this.logPrefix} ${this.getDBNameFromStorageType( - StorageType.MONGO_DB, - )} client initialization failed while trying to issue a request`, - ); + StorageType.MONGO_DB + )} client initialization failed while trying to issue a request` + ) } if (!this.connected) { throw new BaseError( `${this.logPrefix} ${this.getDBNameFromStorageType( - StorageType.MONGO_DB, - )} connection not opened while trying to issue a request`, - ); + StorageType.MONGO_DB + )} connection not opened while trying to issue a request` + ) } } } diff --git a/src/performance/storage/Storage.ts b/src/performance/storage/Storage.ts index 5723dca8..8ee2a701 100644 --- a/src/performance/storage/Storage.ts +++ b/src/performance/storage/Storage.ts @@ -1,62 +1,61 @@ // Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { URL } from 'node:url'; +import { URL } from 'node:url' import { DBName, type EmptyObject, type HandleErrorParams, type Statistics, - StorageType, -} from '../../types/index.js'; -import { isNullOrUndefined, logger, setDefaultErrorParams } from '../../utils/index.js'; + StorageType +} from '../../types/index.js' +import { logger, setDefaultErrorParams } from '../../utils/index.js' export abstract class Storage { - protected readonly storageUri: URL; - protected readonly logPrefix: string; - protected dbName!: string; + protected readonly storageUri: URL + protected readonly logPrefix: string + protected dbName!: string - constructor(storageUri: string, logPrefix: string) { - this.storageUri = new URL(storageUri); - this.logPrefix = logPrefix; + constructor (storageUri: string, logPrefix: string) { + this.storageUri = new URL(storageUri) + this.logPrefix = logPrefix } - protected handleDBError( + protected handleDBError ( type: StorageType, error: Error, table?: string, - params: HandleErrorParams = { throwError: false, consoleOut: false }, + params: HandleErrorParams = { throwError: false, consoleOut: false } ): void { - setDefaultErrorParams(params, { throwError: false, consoleOut: false }); - const inTableOrCollectionStr = - (!isNullOrUndefined(table) || !table) && ` in table or collection '${table}'`; + setDefaultErrorParams(params, { throwError: false, consoleOut: false }) + const inTableOrCollectionStr = table != null && ` in table or collection '${table}'` logger.error( `${this.logPrefix} ${this.getDBNameFromStorageType(type)} error '${ error.message }'${inTableOrCollectionStr}:`, - error, - ); - if (params?.throwError) { - throw error; + error + ) + if (params?.throwError === true) { + throw error } } - protected getDBNameFromStorageType(type: StorageType): DBName | undefined { + protected getDBNameFromStorageType (type: StorageType): DBName | undefined { switch (type) { case StorageType.MARIA_DB: - return DBName.MARIA_DB; + return DBName.MARIA_DB case StorageType.MONGO_DB: - return DBName.MONGO_DB; + return DBName.MONGO_DB case StorageType.MYSQL: - return DBName.MYSQL; + return DBName.MYSQL case StorageType.SQLITE: - return DBName.SQLITE; + return DBName.SQLITE } } - public abstract open(): void | Promise; - public abstract close(): void | Promise; - public abstract storePerformanceStatistics( - performanceStatistics: Statistics, - ): void | Promise; + public abstract open (): void | Promise + public abstract close (): void | Promise + public abstract storePerformanceStatistics ( + performanceStatistics: Statistics + ): void | Promise } diff --git a/src/performance/storage/StorageFactory.ts b/src/performance/storage/StorageFactory.ts index 51a0de50..f5cfcbef 100644 --- a/src/performance/storage/StorageFactory.ts +++ b/src/performance/storage/StorageFactory.ts @@ -1,39 +1,40 @@ // Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { JsonFileStorage } from './JsonFileStorage.js'; +import { JsonFileStorage } from './JsonFileStorage.js' // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { MikroOrmStorage } from './MikroOrmStorage.js'; -import { MongoDBStorage } from './MongoDBStorage.js'; -import type { Storage } from './Storage.js'; -import { BaseError } from '../../exception/index.js'; -import { StorageType } from '../../types/index.js'; +import { MikroOrmStorage } from './MikroOrmStorage.js' +import { MongoDBStorage } from './MongoDBStorage.js' +import type { Storage } from './Storage.js' +import { BaseError } from '../../exception/index.js' +import { StorageType } from '../../types/index.js' +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class StorageFactory { - private constructor() { + private constructor () { // This is intentional } - public static getStorage( + public static getStorage ( type: StorageType, connectionUri: string, - logPrefix: string, + logPrefix: string ): Storage | undefined { - let storageInstance: Storage; + let storageInstance: Storage switch (type) { case StorageType.JSON_FILE: - storageInstance = new JsonFileStorage(connectionUri, logPrefix); - break; + storageInstance = new JsonFileStorage(connectionUri, logPrefix) + break case StorageType.MONGO_DB: - storageInstance = new MongoDBStorage(connectionUri, logPrefix); - break; + storageInstance = new MongoDBStorage(connectionUri, logPrefix) + break // case StorageType.MYSQL: // case StorageType.MARIA_DB: // case StorageType.SQLITE: - // storageInstance = new MikroOrmStorage(connectionUri, logPrefix, type); - // break; + // storageInstance = new MikroOrmStorage(connectionUri, logPrefix, type) + // break default: - throw new BaseError(`${logPrefix} Unknown storage type: ${type}`); + throw new BaseError(`${logPrefix} Unknown storage type: ${type}`) } - return storageInstance; + return storageInstance } } diff --git a/src/scripts/deleteChargingStations.cjs b/src/scripts/deleteChargingStations.cjs index beffb18c..0244d1b5 100755 --- a/src/scripts/deleteChargingStations.cjs +++ b/src/scripts/deleteChargingStations.cjs @@ -1,8 +1,8 @@ #!/usr/bin/env node -const fs = require('node:fs'); +const fs = require('node:fs') -const { MongoClient } = require('mongodb'); +const { MongoClient } = require('mongodb') // This script deletes charging stations // Filter charging stations by id pattern @@ -11,23 +11,24 @@ const { MongoClient } = require('mongodb'); // Delete these charging stations all at once // Config -const config = JSON.parse(fs.readFileSync('scriptConfig.json', 'utf8')); +const config = JSON.parse(fs.readFileSync('scriptConfig.json', 'utf8')) // Mongo Connection and Query if (config?.mongoConnectionString) { - MongoClient.connect(config.mongoConnectionString, async function (err, client) { - const db = client.db(); + // eslint-disable-next-line n/handle-callback-err + MongoClient.connect(config.mongoConnectionString, async function (_err, client) { + const db = client.db() for await (const tenantID of config.tenantIDs) { const response = await db .collection(`${tenantID}.chargingstations`) - .deleteMany({ _id: { $regex: config.idPattern } }); + .deleteMany({ _id: { $regex: config.idPattern } }) console.info( response.deletedCount, `Charging Stations with id = %${config.idPattern}% deleted. TenantID =`, - tenantID, - ); + tenantID + ) } - client.close(); - }); + client.close() + }) } diff --git a/src/scripts/setCSPublicFlag.cjs b/src/scripts/setCSPublicFlag.cjs index 10dc9d11..b7718634 100755 --- a/src/scripts/setCSPublicFlag.cjs +++ b/src/scripts/setCSPublicFlag.cjs @@ -1,8 +1,8 @@ #!/usr/bin/env node -const fs = require('node:fs'); +const fs = require('node:fs') -const { MongoClient } = require('mongodb'); +const { MongoClient } = require('mongodb') // This script sets charging stations public or private // Filter charging stations by id pattern @@ -12,23 +12,24 @@ const { MongoClient } = require('mongodb'); // set public = true // Config -const config = JSON.parse(fs.readFileSync('scriptConfig.json', 'utf8')); +const config = JSON.parse(fs.readFileSync('scriptConfig.json', 'utf8')) // Mongo Connection and Query if (config?.mongoConnectionString) { - MongoClient.connect(config.mongoConnectionString, async function (err, client) { - const db = client.db(); + // eslint-disable-next-line n/handle-callback-err + MongoClient.connect(config.mongoConnectionString, async function (_err, client) { + const db = client.db() for await (const tenantID of config.tenantIDs) { const response = await db .collection(`${tenantID}.chargingstations`) - .updateMany({ _id: { $regex: config.idPattern } }, { $set: { public: config.publicFlag } }); + .updateMany({ _id: { $regex: config.idPattern } }, { $set: { public: config.publicFlag } }) console.info( response.modifiedCount, `Charging Stations with id = %${config.idPattern}% updated. TenantID =`, - tenantID, - ); + tenantID + ) } - client.close(); - }); + client.close() + }) } diff --git a/src/start.ts b/src/start.ts index 8af14b50..8c56e635 100644 --- a/src/start.ts +++ b/src/start.ts @@ -1,11 +1,11 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import chalk from 'chalk'; +import chalk from 'chalk' -import { Bootstrap } from './charging-station/index.js'; +import { Bootstrap } from './charging-station/index.js' try { - await Bootstrap.getInstance().start(); + await Bootstrap.getInstance().start() } catch (error) { - console.error(chalk.red('Startup error: '), error); + console.error(chalk.red('Startup error: '), error) } diff --git a/src/types/AutomaticTransactionGenerator.ts b/src/types/AutomaticTransactionGenerator.ts index 2ae3abd7..9a29fb7b 100644 --- a/src/types/AutomaticTransactionGenerator.ts +++ b/src/types/AutomaticTransactionGenerator.ts @@ -1,42 +1,42 @@ export enum IdTagDistribution { RANDOM = 'random', ROUND_ROBIN = 'round-robin', - CONNECTOR_AFFINITY = 'connector-affinity', + CONNECTOR_AFFINITY = 'connector-affinity' } export interface AutomaticTransactionGeneratorConfiguration { - enable: boolean; - minDuration: number; - maxDuration: number; - minDelayBetweenTwoTransactions: number; - maxDelayBetweenTwoTransactions: number; - probabilityOfStart: number; - stopAfterHours: number; - stopOnConnectionFailure: boolean; - requireAuthorize?: boolean; - idTagDistribution?: IdTagDistribution; + enable: boolean + minDuration: number + maxDuration: number + minDelayBetweenTwoTransactions: number + maxDelayBetweenTwoTransactions: number + probabilityOfStart: number + stopAfterHours: number + stopOnConnectionFailure: boolean + requireAuthorize?: boolean + idTagDistribution?: IdTagDistribution } export interface Status { - start: boolean; - startDate?: Date; - lastRunDate?: Date; - stopDate?: Date; - stoppedDate?: Date; - authorizeRequests?: number; - acceptedAuthorizeRequests?: number; - rejectedAuthorizeRequests?: number; - startTransactionRequests?: number; - acceptedStartTransactionRequests?: number; - rejectedStartTransactionRequests?: number; - stopTransactionRequests?: number; - acceptedStopTransactionRequests?: number; - rejectedStopTransactionRequests?: number; - skippedConsecutiveTransactions?: number; - skippedTransactions?: number; + start: boolean + startDate?: Date + lastRunDate?: Date + stopDate?: Date + stoppedDate?: Date + authorizeRequests?: number + acceptedAuthorizeRequests?: number + rejectedAuthorizeRequests?: number + startTransactionRequests?: number + acceptedStartTransactionRequests?: number + rejectedStartTransactionRequests?: number + stopTransactionRequests?: number + acceptedStopTransactionRequests?: number + rejectedStopTransactionRequests?: number + skippedConsecutiveTransactions?: number + skippedTransactions?: number } export interface ChargingStationAutomaticTransactionGeneratorConfiguration { - automaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration; - automaticTransactionGeneratorStatuses?: Status[]; + automaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration + automaticTransactionGeneratorStatuses?: Status[] } diff --git a/src/types/ChargingStationConfiguration.ts b/src/types/ChargingStationConfiguration.ts index 82db82d0..bb42adff 100644 --- a/src/types/ChargingStationConfiguration.ts +++ b/src/types/ChargingStationConfiguration.ts @@ -1,25 +1,25 @@ -import type { ChargingStationAutomaticTransactionGeneratorConfiguration } from './AutomaticTransactionGenerator.js'; -import type { ChargingStationInfoConfiguration } from './ChargingStationInfo.js'; -import type { ChargingStationOcppConfiguration } from './ChargingStationOcppConfiguration.js'; -import type { ConnectorStatus } from './ConnectorStatus.js'; -import type { EvseStatus } from './Evse.js'; +import type { ChargingStationAutomaticTransactionGeneratorConfiguration } from './AutomaticTransactionGenerator.js' +import type { ChargingStationInfoConfiguration } from './ChargingStationInfo.js' +import type { ChargingStationOcppConfiguration } from './ChargingStationOcppConfiguration.js' +import type { ConnectorStatus } from './ConnectorStatus.js' +import type { EvseStatus } from './Evse.js' interface ConnectorsConfiguration { - connectorsStatus?: ConnectorStatus[]; + connectorsStatus?: ConnectorStatus[] } export type EvseStatusConfiguration = Omit & { - connectorsStatus?: ConnectorStatus[]; -}; + connectorsStatus?: ConnectorStatus[] +} interface EvsesConfiguration { - evsesStatus?: EvseStatusConfiguration[]; + evsesStatus?: EvseStatusConfiguration[] } export type ChargingStationConfiguration = ChargingStationInfoConfiguration & - ChargingStationOcppConfiguration & - ChargingStationAutomaticTransactionGeneratorConfiguration & - ConnectorsConfiguration & - EvsesConfiguration & { - configurationHash?: string; - }; +ChargingStationOcppConfiguration & +ChargingStationAutomaticTransactionGeneratorConfiguration & +ConnectorsConfiguration & +EvsesConfiguration & { + configurationHash?: string +} diff --git a/src/types/ChargingStationEvents.ts b/src/types/ChargingStationEvents.ts index f7980b47..5a0fc8fd 100644 --- a/src/types/ChargingStationEvents.ts +++ b/src/types/ChargingStationEvents.ts @@ -4,5 +4,5 @@ export enum ChargingStationEvents { registered = 'registered', accepted = 'accepted', updated = 'updated', - connectorStatusChanged = 'connectorStatusChanged', + connectorStatusChanged = 'connectorStatusChanged' } diff --git a/src/types/ChargingStationInfo.ts b/src/types/ChargingStationInfo.ts index 9e8da907..0f4fe97d 100644 --- a/src/types/ChargingStationInfo.ts +++ b/src/types/ChargingStationInfo.ts @@ -1,30 +1,30 @@ -import type { ChargingStationTemplate } from './ChargingStationTemplate.js'; -import type { FirmwareStatus } from './ocpp/Requests.js'; +import type { ChargingStationTemplate } from './ChargingStationTemplate.js' +import type { FirmwareStatus } from './ocpp/Requests.js' export type ChargingStationInfo = Omit< - ChargingStationTemplate, - | 'AutomaticTransactionGenerator' - | 'Configuration' - | 'Connectors' - | 'Evses' - | 'power' - | 'powerUnit' - | 'chargeBoxSerialNumberPrefix' - | 'chargePointSerialNumberPrefix' - | 'meterSerialNumberPrefix' +ChargingStationTemplate, +| 'AutomaticTransactionGenerator' +| 'Configuration' +| 'Connectors' +| 'Evses' +| 'power' +| 'powerUnit' +| 'chargeBoxSerialNumberPrefix' +| 'chargePointSerialNumberPrefix' +| 'meterSerialNumberPrefix' > & { - hashId: string; + hashId: string /** @deprecated Use hashId instead */ - infoHash?: string; - chargingStationId?: string; - chargeBoxSerialNumber?: string; - chargePointSerialNumber?: string; - meterSerialNumber?: string; - maximumPower?: number; // Always in Watt - maximumAmperage?: number; // Always in Ampere - firmwareStatus?: FirmwareStatus; -}; + infoHash?: string + chargingStationId?: string + chargeBoxSerialNumber?: string + chargePointSerialNumber?: string + meterSerialNumber?: string + maximumPower?: number // Always in Watt + maximumAmperage?: number // Always in Ampere + firmwareStatus?: FirmwareStatus +} export interface ChargingStationInfoConfiguration { - stationInfo?: ChargingStationInfo; + stationInfo?: ChargingStationInfo } diff --git a/src/types/ChargingStationOcppConfiguration.ts b/src/types/ChargingStationOcppConfiguration.ts index e9932701..2ea2c53e 100644 --- a/src/types/ChargingStationOcppConfiguration.ts +++ b/src/types/ChargingStationOcppConfiguration.ts @@ -1,10 +1,10 @@ -import type { OCPPConfigurationKey } from './ocpp/Configuration.js'; +import type { OCPPConfigurationKey } from './ocpp/Configuration.js' export type ConfigurationKey = OCPPConfigurationKey & { - visible?: boolean; - reboot?: boolean; -}; + visible?: boolean + reboot?: boolean +} export interface ChargingStationOcppConfiguration { - configurationKey?: ConfigurationKey[]; + configurationKey?: ConfigurationKey[] } diff --git a/src/types/ChargingStationTemplate.ts b/src/types/ChargingStationTemplate.ts index 9e9df95f..cb358844 100644 --- a/src/types/ChargingStationTemplate.ts +++ b/src/types/ChargingStationTemplate.ts @@ -1,58 +1,58 @@ -import type { ClientRequestArgs } from 'node:http'; +import type { ClientRequestArgs } from 'node:http' -import type { ClientOptions } from 'ws'; +import type { ClientOptions } from 'ws' -import type { AutomaticTransactionGeneratorConfiguration } from './AutomaticTransactionGenerator.js'; -import type { ChargingStationOcppConfiguration } from './ChargingStationOcppConfiguration.js'; -import type { ConnectorStatus } from './ConnectorStatus.js'; -import type { EvseTemplate } from './Evse.js'; -import type { OCPPProtocol } from './ocpp/OCPPProtocol.js'; -import type { OCPPVersion } from './ocpp/OCPPVersion.js'; +import type { AutomaticTransactionGeneratorConfiguration } from './AutomaticTransactionGenerator.js' +import type { ChargingStationOcppConfiguration } from './ChargingStationOcppConfiguration.js' +import type { ConnectorStatus } from './ConnectorStatus.js' +import type { EvseTemplate } from './Evse.js' +import type { OCPPProtocol } from './ocpp/OCPPProtocol.js' +import type { OCPPVersion } from './ocpp/OCPPVersion.js' import type { FirmwareStatus, IncomingRequestCommand, MessageTrigger, - RequestCommand, -} from './ocpp/Requests.js'; + RequestCommand +} from './ocpp/Requests.js' export enum CurrentType { AC = 'AC', - DC = 'DC', + DC = 'DC' } export enum PowerUnits { WATT = 'W', - KILO_WATT = 'kW', + KILO_WATT = 'kW' } export enum AmpereUnits { MILLI_AMPERE = 'mA', CENTI_AMPERE = 'cA', DECI_AMPERE = 'dA', - AMPERE = 'A', + AMPERE = 'A' } export enum Voltage { VOLTAGE_110 = 110, VOLTAGE_230 = 230, VOLTAGE_400 = 400, - VOLTAGE_800 = 800, + VOLTAGE_800 = 800 } -export type WsOptions = ClientOptions & ClientRequestArgs; +export type WsOptions = ClientOptions & ClientRequestArgs export interface FirmwareUpgrade { versionUpgrade?: { - patternGroup?: number; - step?: number; - }; - reset?: boolean; - failureStatus?: FirmwareStatus; + patternGroup?: number + step?: number + } + reset?: boolean + failureStatus?: FirmwareStatus } interface CommandsSupport { - incomingCommands: Record; - outgoingCommands?: Record; + incomingCommands: Record + outgoingCommands?: Record } enum x509CertificateType { @@ -61,73 +61,73 @@ enum x509CertificateType { CSMSRootCertificate = 'CSMSRootCertificate', ManufacturerRootCertificate = 'ManufacturerRootCertificate', ChargingStationCertificate = 'ChargingStationCertificate', - V2GCertificate = 'V2GCertificate', + V2GCertificate = 'V2GCertificate' } export interface ChargingStationTemplate { - templateHash?: string; - supervisionUrls?: string | string[]; - supervisionUrlOcppConfiguration?: boolean; - supervisionUrlOcppKey?: string; - supervisionUser?: string; - supervisionPassword?: string; - ocppVersion?: OCPPVersion; - ocppProtocol?: OCPPProtocol; - ocppStrictCompliance?: boolean; - ocppPersistentConfiguration?: boolean; - stationInfoPersistentConfiguration?: boolean; - automaticTransactionGeneratorPersistentConfiguration?: boolean; - wsOptions?: WsOptions; - idTagsFile?: string; - baseName: string; - nameSuffix?: string; - fixedName?: boolean; - chargePointModel: string; - chargePointVendor: string; - chargePointSerialNumberPrefix?: string; - chargeBoxSerialNumberPrefix?: string; - firmwareVersionPattern?: string; - firmwareVersion?: string; - firmwareUpgrade?: FirmwareUpgrade; - iccid?: string; - imsi?: string; - meterSerialNumberPrefix?: string; - meterType?: string; - power?: number | number[]; - powerUnit?: PowerUnits; - powerSharedByConnectors?: boolean; - currentOutType?: CurrentType; - voltageOut?: Voltage; - numberOfPhases?: number; - numberOfConnectors?: number | number[]; - useConnectorId0?: boolean; - randomConnectors?: boolean; - resetTime?: number; - autoRegister?: boolean; - autoReconnectMaxRetries?: number; - reconnectExponentialDelay?: boolean; - registrationMaxRetries?: number; - enableStatistics?: boolean; - remoteAuthorization?: boolean; + templateHash?: string + supervisionUrls?: string | string[] + supervisionUrlOcppConfiguration?: boolean + supervisionUrlOcppKey?: string + supervisionUser?: string + supervisionPassword?: string + ocppVersion?: OCPPVersion + ocppProtocol?: OCPPProtocol + ocppStrictCompliance?: boolean + ocppPersistentConfiguration?: boolean + stationInfoPersistentConfiguration?: boolean + automaticTransactionGeneratorPersistentConfiguration?: boolean + wsOptions?: WsOptions + idTagsFile?: string + baseName: string + nameSuffix?: string + fixedName?: boolean + chargePointModel: string + chargePointVendor: string + chargePointSerialNumberPrefix?: string + chargeBoxSerialNumberPrefix?: string + firmwareVersionPattern?: string + firmwareVersion?: string + firmwareUpgrade?: FirmwareUpgrade + iccid?: string + imsi?: string + meterSerialNumberPrefix?: string + meterType?: string + power?: number | number[] + powerUnit?: PowerUnits + powerSharedByConnectors?: boolean + currentOutType?: CurrentType + voltageOut?: Voltage + numberOfPhases?: number + numberOfConnectors?: number | number[] + useConnectorId0?: boolean + randomConnectors?: boolean + resetTime?: number + autoRegister?: boolean + autoReconnectMaxRetries?: number + reconnectExponentialDelay?: boolean + registrationMaxRetries?: number + enableStatistics?: boolean + remoteAuthorization?: boolean /** @deprecated Replaced by remoteAuthorization */ - mustAuthorizeAtRemoteStart?: boolean; + mustAuthorizeAtRemoteStart?: boolean /** @deprecated Replaced by ocppStrictCompliance */ - payloadSchemaValidation?: boolean; - amperageLimitationOcppKey?: string; - amperageLimitationUnit?: AmpereUnits; - beginEndMeterValues?: boolean; - outOfOrderEndMeterValues?: boolean; - meteringPerTransaction?: boolean; - transactionDataMeterValues?: boolean; - stopTransactionsOnStopped?: boolean; - mainVoltageMeterValues?: boolean; - phaseLineToLineVoltageMeterValues?: boolean; - customValueLimitationMeterValues?: boolean; - commandsSupport?: CommandsSupport; - messageTriggerSupport?: Record; - Configuration?: ChargingStationOcppConfiguration; - AutomaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration; - Evses?: Record; - Connectors?: Record; - x509Certificates?: Record; + payloadSchemaValidation?: boolean + amperageLimitationOcppKey?: string + amperageLimitationUnit?: AmpereUnits + beginEndMeterValues?: boolean + outOfOrderEndMeterValues?: boolean + meteringPerTransaction?: boolean + transactionDataMeterValues?: boolean + stopTransactionsOnStopped?: boolean + mainVoltageMeterValues?: boolean + phaseLineToLineVoltageMeterValues?: boolean + customValueLimitationMeterValues?: boolean + commandsSupport?: CommandsSupport + messageTriggerSupport?: Record + Configuration?: ChargingStationOcppConfiguration + AutomaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration + Evses?: Record + Connectors?: Record + x509Certificates?: Record } diff --git a/src/types/ChargingStationWorker.ts b/src/types/ChargingStationWorker.ts index 5cd73160..5b123079 100644 --- a/src/types/ChargingStationWorker.ts +++ b/src/types/ChargingStationWorker.ts @@ -1,64 +1,65 @@ -import type { WebSocket } from 'ws'; +import type { WebSocket } from 'ws' -import type { ChargingStationAutomaticTransactionGeneratorConfiguration } from './AutomaticTransactionGenerator.js'; -import { ChargingStationEvents } from './ChargingStationEvents.js'; -import type { ChargingStationInfo } from './ChargingStationInfo.js'; -import type { ChargingStationOcppConfiguration } from './ChargingStationOcppConfiguration.js'; -import type { ConnectorStatus } from './ConnectorStatus.js'; -import type { EvseStatus } from './Evse.js'; -import type { JsonObject } from './JsonType.js'; -import type { BootNotificationResponse } from './ocpp/Responses.js'; -import type { Statistics } from './Statistics.js'; -import { type WorkerData, type WorkerMessage, WorkerMessageEvents } from '../worker/index.js'; +import type { ChargingStationAutomaticTransactionGeneratorConfiguration } from './AutomaticTransactionGenerator.js' +import { ChargingStationEvents } from './ChargingStationEvents.js' +import type { ChargingStationInfo } from './ChargingStationInfo.js' +import type { ChargingStationOcppConfiguration } from './ChargingStationOcppConfiguration.js' +import type { ConnectorStatus } from './ConnectorStatus.js' +import type { EvseStatus } from './Evse.js' +import type { JsonObject } from './JsonType.js' +import type { BootNotificationResponse } from './ocpp/Responses.js' +import type { Statistics } from './Statistics.js' +import { type WorkerData, type WorkerMessage, WorkerMessageEvents } from '../worker/index.js' interface ChargingStationWorkerOptions extends JsonObject { - elementStartDelay?: number; + elementStartDelay?: number } export interface ChargingStationWorkerData extends WorkerData { - index: number; - templateFile: string; - chargingStationWorkerOptions?: ChargingStationWorkerOptions; + index: number + templateFile: string + chargingStationWorkerOptions?: ChargingStationWorkerOptions } export type EvseStatusWorkerType = Omit & { - connectors?: ConnectorStatus[]; -}; + connectors?: ConnectorStatus[] +} export interface ChargingStationData extends WorkerData { - started: boolean; - stationInfo: ChargingStationInfo; - connectors: ConnectorStatus[]; - evses: EvseStatusWorkerType[]; - ocppConfiguration: ChargingStationOcppConfiguration; + started: boolean + stationInfo: ChargingStationInfo + connectors: ConnectorStatus[] + evses: EvseStatusWorkerType[] + ocppConfiguration: ChargingStationOcppConfiguration wsState?: - | typeof WebSocket.CONNECTING - | typeof WebSocket.OPEN - | typeof WebSocket.CLOSING - | typeof WebSocket.CLOSED; - bootNotificationResponse?: BootNotificationResponse; - automaticTransactionGenerator?: ChargingStationAutomaticTransactionGeneratorConfiguration; + | typeof WebSocket.CONNECTING + | typeof WebSocket.OPEN + | typeof WebSocket.CLOSING + | typeof WebSocket.CLOSED + bootNotificationResponse?: BootNotificationResponse + automaticTransactionGenerator?: ChargingStationAutomaticTransactionGeneratorConfiguration } enum ChargingStationMessageEvents { - performanceStatistics = 'performanceStatistics', + performanceStatistics = 'performanceStatistics' } export const ChargingStationWorkerMessageEvents = { ...WorkerMessageEvents, ...ChargingStationEvents, - ...ChargingStationMessageEvents, -} as const; + ...ChargingStationMessageEvents +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare export type ChargingStationWorkerMessageEvents = | WorkerMessageEvents | ChargingStationEvents - | ChargingStationMessageEvents; + | ChargingStationMessageEvents -export type ChargingStationWorkerMessageData = ChargingStationData | Statistics; +export type ChargingStationWorkerMessageData = ChargingStationData | Statistics export type ChargingStationWorkerMessage = Omit< - WorkerMessage, - 'event' +WorkerMessage, +'event' > & { - event: ChargingStationWorkerMessageEvents; -}; + event: ChargingStationWorkerMessageEvents +} diff --git a/src/types/ConfigurationData.ts b/src/types/ConfigurationData.ts index 4b636cda..fe8b8481 100644 --- a/src/types/ConfigurationData.ts +++ b/src/types/ConfigurationData.ts @@ -1,123 +1,123 @@ -import type { ListenOptions } from 'node:net'; -import type { ResourceLimits } from 'node:worker_threads'; +import type { ListenOptions } from 'node:net' +import type { ResourceLimits } from 'node:worker_threads' -import type { WorkerChoiceStrategy } from 'poolifier'; +import type { WorkerChoiceStrategy } from 'poolifier' -import type { StorageType } from './Storage.js'; -import type { ApplicationProtocol, AuthenticationType } from './UIProtocol.js'; -import type { WorkerProcessType } from '../worker/index.js'; +import type { StorageType } from './Storage.js' +import type { ApplicationProtocol, AuthenticationType } from './UIProtocol.js' +import type { WorkerProcessType } from '../worker/index.js' -type ServerOptions = ListenOptions; +type ServerOptions = ListenOptions export enum ConfigurationSection { log = 'log', performanceStorage = 'performanceStorage', worker = 'worker', - uiServer = 'uiServer', + uiServer = 'uiServer' } export enum SupervisionUrlDistribution { ROUND_ROBIN = 'round-robin', RANDOM = 'random', - CHARGING_STATION_AFFINITY = 'charging-station-affinity', + CHARGING_STATION_AFFINITY = 'charging-station-affinity' } export interface StationTemplateUrl { - file: string; - numberOfStations: number; + file: string + numberOfStations: number } export interface LogConfiguration { - enabled?: boolean; - file?: string; - errorFile?: string; - statisticsInterval?: number; - level?: string; - console?: boolean; - format?: string; - rotate?: boolean; - maxFiles?: string | number; - maxSize?: string | number; + enabled?: boolean + file?: string + errorFile?: string + statisticsInterval?: number + level?: string + console?: boolean + format?: string + rotate?: boolean + maxFiles?: string | number + maxSize?: string | number } export enum ApplicationProtocolVersion { VERSION_11 = 1.1, - VERSION_20 = 2.0, + VERSION_20 = 2.0 } export interface UIServerConfiguration { - enabled?: boolean; - type?: ApplicationProtocol; - version?: ApplicationProtocolVersion; - options?: ServerOptions; + enabled?: boolean + type?: ApplicationProtocol + version?: ApplicationProtocolVersion + options?: ServerOptions authentication?: { - enabled: boolean; - type: AuthenticationType; - username?: string; - password?: string; - }; + enabled: boolean + type: AuthenticationType + username?: string + password?: string + } } export interface StorageConfiguration { - enabled?: boolean; - type?: StorageType; - uri?: string; + enabled?: boolean + type?: StorageType + uri?: string } -export type ElementsPerWorkerType = number | 'auto' | 'all'; +export type ElementsPerWorkerType = number | 'auto' | 'all' export interface WorkerConfiguration { - processType?: WorkerProcessType; - startDelay?: number; - elementsPerWorker?: ElementsPerWorkerType; - elementStartDelay?: number; - poolMinSize?: number; - poolMaxSize?: number; - resourceLimits?: ResourceLimits; + processType?: WorkerProcessType + startDelay?: number + elementsPerWorker?: ElementsPerWorkerType + elementStartDelay?: number + poolMinSize?: number + poolMaxSize?: number + resourceLimits?: ResourceLimits } export interface ConfigurationData { - supervisionUrls?: string | string[]; - supervisionUrlDistribution?: SupervisionUrlDistribution; - stationTemplateUrls: StationTemplateUrl[]; - log?: LogConfiguration; - worker?: WorkerConfiguration; - uiServer?: UIServerConfiguration; - performanceStorage?: StorageConfiguration; + supervisionUrls?: string | string[] + supervisionUrlDistribution?: SupervisionUrlDistribution + stationTemplateUrls: StationTemplateUrl[] + log?: LogConfiguration + worker?: WorkerConfiguration + uiServer?: UIServerConfiguration + performanceStorage?: StorageConfiguration /** @deprecated Moved to charging station template */ - autoReconnectMaxRetries?: number; + autoReconnectMaxRetries?: number /** @deprecated Moved to worker configuration section. */ - workerProcess?: WorkerProcessType; + workerProcess?: WorkerProcessType /** @deprecated Moved to worker configuration section. */ - workerStartDelay?: number; + workerStartDelay?: number /** @deprecated Moved to worker configuration section. */ - elementStartDelay?: number; + elementStartDelay?: number /** @deprecated Moved to worker configuration section. */ - workerPoolMinSize?: number; + workerPoolMinSize?: number /** @deprecated Moved to worker configuration section. */ - workerPoolMaxSize?: number; + workerPoolMaxSize?: number /** @deprecated Moved to worker configuration section. */ - workerPoolStrategy?: WorkerChoiceStrategy; + workerPoolStrategy?: WorkerChoiceStrategy /** @deprecated Moved to worker configuration section. */ - chargingStationsPerWorker?: number; + chargingStationsPerWorker?: number /** @deprecated Moved to log configuration section. */ - logStatisticsInterval?: number; + logStatisticsInterval?: number /** @deprecated Moved to log configuration section. */ - logEnabled?: boolean; + logEnabled?: boolean /** @deprecated Moved to log configuration section. */ - logConsole?: boolean; + logConsole?: boolean /** @deprecated Moved to log configuration section. */ - logFormat?: string; + logFormat?: string /** @deprecated Moved to log configuration section. */ - logLevel?: string; + logLevel?: string /** @deprecated Moved to log configuration section. */ - logRotate?: boolean; + logRotate?: boolean /** @deprecated Moved to log configuration section. */ - logMaxFiles?: number | string; + logMaxFiles?: number | string /** @deprecated Moved to log configuration section. */ - logMaxSize?: number | string; + logMaxSize?: number | string /** @deprecated Moved to log configuration section. */ - logFile?: string; + logFile?: string /** @deprecated Moved to log configuration section. */ - logErrorFile?: string; + logErrorFile?: string } diff --git a/src/types/ConnectorStatus.ts b/src/types/ConnectorStatus.ts index 61c8a208..26f54334 100644 --- a/src/types/ConnectorStatus.ts +++ b/src/types/ConnectorStatus.ts @@ -1,28 +1,28 @@ -import type { SampledValueTemplate } from './MeasurandPerPhaseSampledValueTemplates.js'; -import type { ChargingProfile } from './ocpp/ChargingProfile.js'; -import type { ConnectorStatusEnum } from './ocpp/ConnectorStatusEnum.js'; -import type { MeterValue } from './ocpp/MeterValues.js'; -import type { AvailabilityType } from './ocpp/Requests.js'; -import type { Reservation } from './ocpp/Reservation.js'; +import type { SampledValueTemplate } from './MeasurandPerPhaseSampledValueTemplates.js' +import type { ChargingProfile } from './ocpp/ChargingProfile.js' +import type { ConnectorStatusEnum } from './ocpp/ConnectorStatusEnum.js' +import type { MeterValue } from './ocpp/MeterValues.js' +import type { AvailabilityType } from './ocpp/Requests.js' +import type { Reservation } from './ocpp/Reservation.js' export interface ConnectorStatus { - availability: AvailabilityType; - bootStatus?: ConnectorStatusEnum; - status?: ConnectorStatusEnum; - MeterValues: SampledValueTemplate[]; - authorizeIdTag?: string; - idTagAuthorized?: boolean; - localAuthorizeIdTag?: string; - idTagLocalAuthorized?: boolean; - transactionRemoteStarted?: boolean; - transactionStarted?: boolean; - transactionStart?: Date; - transactionId?: number; - transactionSetInterval?: NodeJS.Timeout; - transactionIdTag?: string; - energyActiveImportRegisterValue?: number; // In Wh - transactionEnergyActiveImportRegisterValue?: number; // In Wh - transactionBeginMeterValue?: MeterValue; - chargingProfiles?: ChargingProfile[]; - reservation?: Reservation; + availability: AvailabilityType + bootStatus?: ConnectorStatusEnum + status?: ConnectorStatusEnum + MeterValues: SampledValueTemplate[] + authorizeIdTag?: string + idTagAuthorized?: boolean + localAuthorizeIdTag?: string + idTagLocalAuthorized?: boolean + transactionRemoteStarted?: boolean + transactionStarted?: boolean + transactionStart?: Date + transactionId?: number + transactionSetInterval?: NodeJS.Timeout + transactionIdTag?: string + energyActiveImportRegisterValue?: number // In Wh + transactionEnergyActiveImportRegisterValue?: number // In Wh + transactionBeginMeterValue?: MeterValue + chargingProfiles?: ChargingProfile[] + reservation?: Reservation } diff --git a/src/types/EmptyObject.ts b/src/types/EmptyObject.ts index 253e9645..41bdfc0b 100644 --- a/src/types/EmptyObject.ts +++ b/src/types/EmptyObject.ts @@ -1 +1 @@ -export type EmptyObject = Record; +export type EmptyObject = Record diff --git a/src/types/Error.ts b/src/types/Error.ts index 46f7569c..34bad1ad 100644 --- a/src/types/Error.ts +++ b/src/types/Error.ts @@ -1,7 +1,7 @@ -import type { JsonType } from './JsonType.js'; +import type { JsonType } from './JsonType.js' export interface HandleErrorParams { - throwError?: boolean; - consoleOut?: boolean; - errorResponse?: T; + throwError?: boolean + consoleOut?: boolean + errorResponse?: T } diff --git a/src/types/Evse.ts b/src/types/Evse.ts index aaf2f664..8fb09e48 100644 --- a/src/types/Evse.ts +++ b/src/types/Evse.ts @@ -1,11 +1,11 @@ -import type { ConnectorStatus } from './ConnectorStatus.js'; -import type { AvailabilityType } from './ocpp/Requests.js'; +import type { ConnectorStatus } from './ConnectorStatus.js' +import type { AvailabilityType } from './ocpp/Requests.js' export interface EvseTemplate { - Connectors: Record; + Connectors: Record } export interface EvseStatus { - connectors: Map; - availability: AvailabilityType; + connectors: Map + availability: AvailabilityType } diff --git a/src/types/FileType.ts b/src/types/FileType.ts index 3781b7d9..2b6bc380 100644 --- a/src/types/FileType.ts +++ b/src/types/FileType.ts @@ -4,5 +4,5 @@ export enum FileType { ChargingStationConfiguration = 'charging station configuration', ChargingStationTemplate = 'charging station template', PerformanceRecords = 'performance records', - JsonSchema = 'json schema', + JsonSchema = 'json schema' } diff --git a/src/types/JsonType.ts b/src/types/JsonType.ts index 256f0f42..17db232c 100644 --- a/src/types/JsonType.ts +++ b/src/types/JsonType.ts @@ -1,7 +1,7 @@ -type JsonPrimitive = string | number | boolean | Date | null; +type JsonPrimitive = string | number | boolean | Date | null export type JsonObject = { - [key in string]?: JsonType; -}; + [key in string]?: JsonType +} -export type JsonType = JsonPrimitive | JsonType[] | JsonObject; +export type JsonType = JsonPrimitive | JsonType[] | JsonObject diff --git a/src/types/MeasurandPerPhaseSampledValueTemplates.ts b/src/types/MeasurandPerPhaseSampledValueTemplates.ts index 95a73b64..5da56b48 100644 --- a/src/types/MeasurandPerPhaseSampledValueTemplates.ts +++ b/src/types/MeasurandPerPhaseSampledValueTemplates.ts @@ -1,12 +1,12 @@ -import type { SampledValue } from './ocpp/MeterValues.js'; +import type { SampledValue } from './ocpp/MeterValues.js' export type SampledValueTemplate = SampledValue & { - fluctuationPercent?: number; - minimumValue?: number; -}; + fluctuationPercent?: number + minimumValue?: number +} export interface MeasurandPerPhaseSampledValueTemplates { - L1?: SampledValueTemplate; - L2?: SampledValueTemplate; - L3?: SampledValueTemplate; + L1?: SampledValueTemplate + L2?: SampledValueTemplate + L3?: SampledValueTemplate } diff --git a/src/types/MeasurandValues.ts b/src/types/MeasurandValues.ts index f907cc80..833efa82 100644 --- a/src/types/MeasurandValues.ts +++ b/src/types/MeasurandValues.ts @@ -1,6 +1,6 @@ export interface MeasurandValues { - L1: number; - L2: number; - L3: number; - allPhases: number; + L1: number + L2: number + L3: number + allPhases: number } diff --git a/src/types/Statistics.ts b/src/types/Statistics.ts index a7abb3f8..9bb614ee 100644 --- a/src/types/Statistics.ts +++ b/src/types/Statistics.ts @@ -1,33 +1,33 @@ -import type { IncomingRequestCommand, RequestCommand } from './ocpp/Requests.js'; -import type { CircularArray } from '../utils/index.js'; -import type { WorkerData } from '../worker/index.js'; +import type { IncomingRequestCommand, RequestCommand } from './ocpp/Requests.js' +import type { CircularArray } from '../utils/index.js' +import type { WorkerData } from '../worker/index.js' export interface TimestampedData { - timestamp: number; - value: number; + timestamp: number + value: number } export type StatisticsData = Partial<{ - requestCount: number; - responseCount: number; - errorCount: number; - timeMeasurementCount: number; - measurementTimeSeries: CircularArray; - currentTimeMeasurement: number; - minTimeMeasurement: number; - maxTimeMeasurement: number; - totalTimeMeasurement: number; - avgTimeMeasurement: number; - medTimeMeasurement: number; - ninetyFiveThPercentileTimeMeasurement: number; - stdDevTimeMeasurement: number; -}>; + requestCount: number + responseCount: number + errorCount: number + timeMeasurementCount: number + measurementTimeSeries: CircularArray + currentTimeMeasurement: number + minTimeMeasurement: number + maxTimeMeasurement: number + totalTimeMeasurement: number + avgTimeMeasurement: number + medTimeMeasurement: number + ninetyFiveThPercentileTimeMeasurement: number + stdDevTimeMeasurement: number +}> export type Statistics = { - id: string; - name: string; - uri: string; - createdAt: Date; - updatedAt?: Date; - statisticsData: Map; -} & WorkerData; + id: string + name: string + uri: string + createdAt: Date + updatedAt?: Date + statisticsData: Map +} & WorkerData diff --git a/src/types/Storage.ts b/src/types/Storage.ts index afb29e82..3301f5ca 100644 --- a/src/types/Storage.ts +++ b/src/types/Storage.ts @@ -1,18 +1,18 @@ -import type { Configuration } from '@mikro-orm/core'; +import type { Configuration } from '@mikro-orm/core' -export type MikroOrmDbType = keyof typeof Configuration.PLATFORMS; +export type MikroOrmDbType = keyof typeof Configuration.PLATFORMS export enum StorageType { JSON_FILE = 'jsonfile', MONGO_DB = 'mongodb', MYSQL = 'mysql', MARIA_DB = 'mariadb', - SQLITE = 'sqlite', + SQLITE = 'sqlite' } export enum DBName { MONGO_DB = 'MongoDB', MYSQL = 'MySQL', MARIA_DB = 'MariaDB', - SQLITE = 'SQLite', + SQLITE = 'SQLite' } diff --git a/src/types/UIProtocol.ts b/src/types/UIProtocol.ts index 69312eef..f44475d8 100644 --- a/src/types/UIProtocol.ts +++ b/src/types/UIProtocol.ts @@ -1,31 +1,31 @@ -import type { JsonObject } from './JsonType.js'; -import type { BroadcastChannelResponsePayload } from './WorkerBroadcastChannel.js'; +import type { JsonObject } from './JsonType.js' +import type { BroadcastChannelResponsePayload } from './WorkerBroadcastChannel.js' export enum Protocol { - UI = 'ui', + UI = 'ui' } export enum ApplicationProtocol { HTTP = 'http', - WS = 'ws', + WS = 'ws' } export enum AuthenticationType { - BASIC_AUTH = 'basic-auth', + BASIC_AUTH = 'basic-auth' } export enum ProtocolVersion { - '0.0.1' = '0.0.1', + '0.0.1' = '0.0.1' } -export type ProtocolRequest = [string, ProcedureName, RequestPayload]; -export type ProtocolResponse = [string, ResponsePayload]; +export type ProtocolRequest = [string, ProcedureName, RequestPayload] +export type ProtocolResponse = [string, ResponsePayload] export type ProtocolRequestHandler = ( uuid?: string, procedureName?: ProcedureName, - payload?: RequestPayload, -) => undefined | Promise | ResponsePayload | Promise; + payload?: RequestPayload +) => undefined | Promise | ResponsePayload | Promise export enum ProcedureName { START_SIMULATOR = 'startSimulator', @@ -47,22 +47,22 @@ export enum ProcedureName { METER_VALUES = 'meterValues', DATA_TRANSFER = 'dataTransfer', DIAGNOSTICS_STATUS_NOTIFICATION = 'diagnosticsStatusNotification', - FIRMWARE_STATUS_NOTIFICATION = 'firmwareStatusNotification', + FIRMWARE_STATUS_NOTIFICATION = 'firmwareStatusNotification' } export interface RequestPayload extends JsonObject { - hashIds?: string[]; - connectorIds?: number[]; + hashIds?: string[] + connectorIds?: number[] } export enum ResponseStatus { SUCCESS = 'success', - FAILURE = 'failure', + FAILURE = 'failure' } export interface ResponsePayload extends JsonObject { - status: ResponseStatus; - hashIdsSucceeded?: string[]; - hashIdsFailed?: string[]; - responsesFailed?: BroadcastChannelResponsePayload[]; + status: ResponseStatus + hashIdsSucceeded?: string[] + hashIdsFailed?: string[] + responsesFailed?: BroadcastChannelResponsePayload[] } diff --git a/src/types/WebSocket.ts b/src/types/WebSocket.ts index 7d028afe..2f7269aa 100644 --- a/src/types/WebSocket.ts +++ b/src/types/WebSocket.ts @@ -15,8 +15,8 @@ export const WebSocketCloseEventStatusString: Record { - hashId: string; + hashId: string } export interface MessageEvent { - data: BroadcastChannelRequest | BroadcastChannelResponse; + data: BroadcastChannelRequest | BroadcastChannelResponse } diff --git a/src/types/index.ts b/src/types/index.ts index 4d8b534e..3b1f799c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -9,15 +9,15 @@ export { ProtocolVersion, type RequestPayload, type ResponsePayload, - ResponseStatus, -} from './UIProtocol.js'; + ResponseStatus +} from './UIProtocol.js' export { type AutomaticTransactionGeneratorConfiguration, type ChargingStationAutomaticTransactionGeneratorConfiguration, IdTagDistribution, - type Status, -} from './AutomaticTransactionGenerator.js'; -export { type GenericResponse, GenericStatus, RegistrationStatusEnumType } from './ocpp/Common.js'; + type Status +} from './AutomaticTransactionGenerator.js' +export { type GenericResponse, GenericStatus, RegistrationStatusEnumType } from './ocpp/Common.js' export { AvailabilityType, type BootNotificationRequest, @@ -38,8 +38,8 @@ export { type RequestParams, type ResponseCallback, type ResponseType, - type StatusNotificationRequest, -} from './ocpp/Requests.js'; + type StatusNotificationRequest +} from './ocpp/Requests.js' export { AvailabilityStatus, type BootNotificationResponse, @@ -59,8 +59,8 @@ export { type ResponseHandler, type StatusNotificationResponse, TriggerMessageStatus, - UnlockStatus, -} from './ocpp/Responses.js'; + UnlockStatus +} from './ocpp/Responses.js' export { AuthorizationStatus, type AuthorizeRequest, @@ -69,17 +69,17 @@ export { type StartTransactionResponse, StopTransactionReason, type StopTransactionRequest, - type StopTransactionResponse, -} from './ocpp/Transaction.js'; -export { BootReasonEnumType, OCPP20ConnectorStatusEnumType } from './ocpp/2.0/Common.js'; + type StopTransactionResponse +} from './ocpp/Transaction.js' +export { BootReasonEnumType, OCPP20ConnectorStatusEnumType } from './ocpp/2.0/Common.js' export { BroadcastChannelProcedureName, type BroadcastChannelRequest, type BroadcastChannelRequestPayload, type BroadcastChannelResponse, type BroadcastChannelResponsePayload, - type MessageEvent, -} from './WorkerBroadcastChannel.js'; + type MessageEvent +} from './WorkerBroadcastChannel.js' export { type ChangeConfigurationRequest, type GetConfigurationRequest, @@ -108,8 +108,8 @@ export { type RemoteStopTransactionRequest, type ResetRequest, type SetChargingProfileRequest, - type UnlockConnectorRequest, -} from './ocpp/1.6/Requests.js'; + type UnlockConnectorRequest +} from './ocpp/1.6/Requests.js' export { type ChangeConfigurationResponse, type GetConfigurationResponse, @@ -128,33 +128,33 @@ export { type OCPP16TriggerMessageResponse, type OCPP16UpdateFirmwareResponse, type SetChargingProfileResponse, - type UnlockConnectorResponse, -} from './ocpp/1.6/Responses.js'; -export { ChargePointErrorCode } from './ocpp/ChargePointErrorCode.js'; + type UnlockConnectorResponse +} from './ocpp/1.6/Responses.js' +export { ChargePointErrorCode } from './ocpp/ChargePointErrorCode.js' export { type ChargingProfile, ChargingProfileKindType, ChargingRateUnitType, type ChargingSchedulePeriod, - RecurrencyKindType, -} from './ocpp/ChargingProfile.js'; + RecurrencyKindType +} from './ocpp/ChargingProfile.js' export type { ChargingStationConfiguration, - EvseStatusConfiguration, -} from './ChargingStationConfiguration.js'; + EvseStatusConfiguration +} from './ChargingStationConfiguration.js' export { type ChargingStationData, type ChargingStationWorkerData, type ChargingStationWorkerMessage, type ChargingStationWorkerMessageData, ChargingStationWorkerMessageEvents, - type EvseStatusWorkerType, -} from './ChargingStationWorker.js'; -export type { ChargingStationInfo } from './ChargingStationInfo.js'; + type EvseStatusWorkerType +} from './ChargingStationWorker.js' +export type { ChargingStationInfo } from './ChargingStationInfo.js' export type { ChargingStationOcppConfiguration, - ConfigurationKey, -} from './ChargingStationOcppConfiguration.js'; + ConfigurationKey +} from './ChargingStationOcppConfiguration.js' export { AmpereUnits, type ChargingStationTemplate, @@ -162,8 +162,8 @@ export { type FirmwareUpgrade, PowerUnits, Voltage, - type WsOptions, -} from './ChargingStationTemplate.js'; + type WsOptions +} from './ChargingStationTemplate.js' export { ApplicationProtocolVersion, type ConfigurationData, @@ -174,31 +174,31 @@ export { type StorageConfiguration, SupervisionUrlDistribution, type UIServerConfiguration, - type WorkerConfiguration, -} from './ConfigurationData.js'; + type WorkerConfiguration +} from './ConfigurationData.js' export { type ConfigurationKeyType, ConnectorPhaseRotation, type OCPPConfigurationKey, StandardParametersKey, SupportedFeatureProfiles, - VendorParametersKey, -} from './ocpp/Configuration.js'; -export type { ConnectorStatus } from './ConnectorStatus.js'; -export { ConnectorStatusEnum, type ConnectorStatusTransition } from './ocpp/ConnectorStatusEnum.js'; -export { DBName, type MikroOrmDbType, StorageType } from './Storage.js'; -export type { EmptyObject } from './EmptyObject.js'; -export { ErrorType } from './ocpp/ErrorType.js'; -export type { EvseTemplate, EvseStatus } from './Evse.js'; -export { FileType } from './FileType.js'; -export type { HandleErrorParams } from './Error.js'; -export type { JsonObject, JsonType } from './JsonType.js'; + VendorParametersKey +} from './ocpp/Configuration.js' +export type { ConnectorStatus } from './ConnectorStatus.js' +export { ConnectorStatusEnum, type ConnectorStatusTransition } from './ocpp/ConnectorStatusEnum.js' +export { DBName, type MikroOrmDbType, StorageType } from './Storage.js' +export type { EmptyObject } from './EmptyObject.js' +export { ErrorType } from './ocpp/ErrorType.js' +export type { EvseTemplate, EvseStatus } from './Evse.js' +export { FileType } from './FileType.js' +export type { HandleErrorParams } from './Error.js' +export type { JsonObject, JsonType } from './JsonType.js' export type { MeasurandPerPhaseSampledValueTemplates, - SampledValueTemplate, -} from './MeasurandPerPhaseSampledValueTemplates.js'; -export type { MeasurandValues } from './MeasurandValues.js'; -export { MessageType } from './ocpp/MessageType.js'; + SampledValueTemplate +} from './MeasurandPerPhaseSampledValueTemplates.js' +export type { MeasurandValues } from './MeasurandValues.js' +export { MessageType } from './ocpp/MessageType.js' export { type MeterValue, MeterValueContext, @@ -206,8 +206,8 @@ export { MeterValueMeasurand, MeterValuePhase, MeterValueUnit, - type SampledValue, -} from './ocpp/MeterValues.js'; + type SampledValue +} from './ocpp/MeterValues.js' export { type OCPP16MeterValue, OCPP16MeterValueContext, @@ -217,8 +217,8 @@ export { OCPP16MeterValueUnit, type OCPP16MeterValuesRequest, type OCPP16MeterValuesResponse, - type OCPP16SampledValue, -} from './ocpp/1.6/MeterValues.js'; + type OCPP16SampledValue +} from './ocpp/1.6/MeterValues.js' export { OCPP16AuthorizationStatus, type OCPP16AuthorizeRequest, @@ -227,49 +227,49 @@ export { type OCPP16StartTransactionResponse, OCPP16StopTransactionReason, type OCPP16StopTransactionRequest, - type OCPP16StopTransactionResponse, -} from './ocpp/1.6/Transaction.js'; -export { OCPP16ChargePointErrorCode } from './ocpp/1.6/ChargePointErrorCode.js'; -export { OCPP16ChargePointStatus } from './ocpp/1.6/ChargePointStatus.js'; + type OCPP16StopTransactionResponse +} from './ocpp/1.6/Transaction.js' +export { OCPP16ChargePointErrorCode } from './ocpp/1.6/ChargePointErrorCode.js' +export { OCPP16ChargePointStatus } from './ocpp/1.6/ChargePointStatus.js' export { type OCPP16ChargingProfile, OCPP16ChargingProfilePurposeType, OCPP16ChargingRateUnitType, type OCPP16ChargingSchedule, - type OCPP16ChargingSchedulePeriod, -} from './ocpp/1.6/ChargingProfile.js'; + type OCPP16ChargingSchedulePeriod +} from './ocpp/1.6/ChargingProfile.js' export { OCPP16StandardParametersKey, - OCPP16SupportedFeatureProfiles, -} from './ocpp/1.6/Configuration.js'; -export { OCPP16DiagnosticsStatus } from './ocpp/1.6/DiagnosticsStatus.js'; + OCPP16SupportedFeatureProfiles +} from './ocpp/1.6/Configuration.js' +export { OCPP16DiagnosticsStatus } from './ocpp/1.6/DiagnosticsStatus.js' export { type OCPP20BootNotificationRequest, type OCPP20ClearCacheRequest, type OCPP20HeartbeatRequest, OCPP20IncomingRequestCommand, OCPP20RequestCommand, - type OCPP20StatusNotificationRequest, -} from './ocpp/2.0/Requests.js'; + type OCPP20StatusNotificationRequest +} from './ocpp/2.0/Requests.js' export type { OCPP20BootNotificationResponse, OCPP20ClearCacheResponse, OCPP20HeartbeatResponse, - OCPP20StatusNotificationResponse, -} from './ocpp/2.0/Responses.js'; -export { OCPP20OptionalVariableName } from './ocpp/2.0/Variables.js'; -export { OCPPVersion } from './ocpp/OCPPVersion.js'; -export { PerformanceData } from './orm/entities/PerformanceData.js'; -export { PerformanceRecord } from './orm/entities/PerformanceRecord.js'; -export type { Statistics, TimestampedData } from './Statistics.js'; + OCPP20StatusNotificationResponse +} from './ocpp/2.0/Responses.js' +export { OCPP20OptionalVariableName } from './ocpp/2.0/Variables.js' +export { OCPPVersion } from './ocpp/OCPPVersion.js' +export { PerformanceData } from './orm/entities/PerformanceData.js' +export { PerformanceRecord } from './orm/entities/PerformanceRecord.js' +export type { Statistics, TimestampedData } from './Statistics.js' export { type WSError, WebSocketCloseEventStatusCode, - WebSocketCloseEventStatusString, -} from './WebSocket.js'; + WebSocketCloseEventStatusString +} from './WebSocket.js' export { type Reservation, type ReservationKey, - ReservationTerminationReason, -} from './ocpp/Reservation.js'; -export { ChargingStationEvents } from './ChargingStationEvents.js'; + ReservationTerminationReason +} from './ocpp/Reservation.js' +export { ChargingStationEvents } from './ChargingStationEvents.js' diff --git a/src/types/ocpp/1.6/ChargePointErrorCode.ts b/src/types/ocpp/1.6/ChargePointErrorCode.ts index 4255cd05..db06d0c9 100644 --- a/src/types/ocpp/1.6/ChargePointErrorCode.ts +++ b/src/types/ocpp/1.6/ChargePointErrorCode.ts @@ -14,5 +14,5 @@ export enum OCPP16ChargePointErrorCode { READER_FAILURE = 'ReaderFailure', RESET_FAILURE = 'ResetFailure', UNDER_VOLTAGE = 'UnderVoltage', - WEAK_SIGNAL = 'WeakSignal', + WEAK_SIGNAL = 'WeakSignal' } diff --git a/src/types/ocpp/1.6/ChargePointStatus.ts b/src/types/ocpp/1.6/ChargePointStatus.ts index 4cef943e..ef7467bd 100644 --- a/src/types/ocpp/1.6/ChargePointStatus.ts +++ b/src/types/ocpp/1.6/ChargePointStatus.ts @@ -7,5 +7,5 @@ export enum OCPP16ChargePointStatus { Finishing = 'Finishing', Reserved = 'Reserved', Unavailable = 'Unavailable', - Faulted = 'Faulted', + Faulted = 'Faulted' } diff --git a/src/types/ocpp/1.6/ChargingProfile.ts b/src/types/ocpp/1.6/ChargingProfile.ts index 89cf8e4d..744696c5 100644 --- a/src/types/ocpp/1.6/ChargingProfile.ts +++ b/src/types/ocpp/1.6/ChargingProfile.ts @@ -1,49 +1,49 @@ -import type { JsonObject } from '../../JsonType.js'; +import type { JsonObject } from '../../JsonType.js' export interface OCPP16ChargingProfile extends JsonObject { - chargingProfileId: number; - transactionId?: number; - stackLevel: number; - chargingProfilePurpose: OCPP16ChargingProfilePurposeType; - chargingProfileKind: OCPP16ChargingProfileKindType; - recurrencyKind?: OCPP16RecurrencyKindType; - validFrom?: Date; - validTo?: Date; - chargingSchedule: OCPP16ChargingSchedule; + chargingProfileId: number + transactionId?: number + stackLevel: number + chargingProfilePurpose: OCPP16ChargingProfilePurposeType + chargingProfileKind: OCPP16ChargingProfileKindType + recurrencyKind?: OCPP16RecurrencyKindType + validFrom?: Date + validTo?: Date + chargingSchedule: OCPP16ChargingSchedule } export interface OCPP16ChargingSchedule extends JsonObject { - startSchedule?: Date; - duration?: number; - chargingRateUnit: OCPP16ChargingRateUnitType; - chargingSchedulePeriod: OCPP16ChargingSchedulePeriod[]; - minChargeRate?: number; + startSchedule?: Date + duration?: number + chargingRateUnit: OCPP16ChargingRateUnitType + chargingSchedulePeriod: OCPP16ChargingSchedulePeriod[] + minChargeRate?: number } export interface OCPP16ChargingSchedulePeriod extends JsonObject { - startPeriod: number; - limit: number; - numberPhases?: number; + startPeriod: number + limit: number + numberPhases?: number } export enum OCPP16ChargingRateUnitType { WATT = 'W', - AMPERE = 'A', + AMPERE = 'A' } export enum OCPP16ChargingProfileKindType { ABSOLUTE = 'Absolute', RECURRING = 'Recurring', - RELATIVE = 'Relative', + RELATIVE = 'Relative' } export enum OCPP16ChargingProfilePurposeType { CHARGE_POINT_MAX_PROFILE = 'ChargePointMaxProfile', TX_DEFAULT_PROFILE = 'TxDefaultProfile', - TX_PROFILE = 'TxProfile', + TX_PROFILE = 'TxProfile' } export enum OCPP16RecurrencyKindType { DAILY = 'Daily', - WEEKLY = 'Weekly', + WEEKLY = 'Weekly' } diff --git a/src/types/ocpp/1.6/Configuration.ts b/src/types/ocpp/1.6/Configuration.ts index 1bd92376..a5888d93 100644 --- a/src/types/ocpp/1.6/Configuration.ts +++ b/src/types/ocpp/1.6/Configuration.ts @@ -4,7 +4,7 @@ export enum OCPP16SupportedFeatureProfiles { LocalAuthListManagement = 'LocalAuthListManagement', Reservation = 'Reservation', SmartCharging = 'SmartCharging', - RemoteTrigger = 'RemoteTrigger', + RemoteTrigger = 'RemoteTrigger' } export enum OCPP16StandardParametersKey { @@ -51,9 +51,9 @@ export enum OCPP16StandardParametersKey { ChargingScheduleAllowedChargingRateUnit = 'ChargingScheduleAllowedChargingRateUnit', ChargingScheduleMaxPeriods = 'ChargingScheduleMaxPeriods', ConnectorSwitch3to1PhaseSupported = 'ConnectorSwitch3to1PhaseSupported', - MaxChargingProfilesInstalled = 'MaxChargingProfilesInstalled', + MaxChargingProfilesInstalled = 'MaxChargingProfilesInstalled' } export enum OCPP16VendorParametersKey { - ConnectionUrl = 'ConnectionUrl', + ConnectionUrl = 'ConnectionUrl' } diff --git a/src/types/ocpp/1.6/DiagnosticsStatus.ts b/src/types/ocpp/1.6/DiagnosticsStatus.ts index c8c2c890..c9feccea 100644 --- a/src/types/ocpp/1.6/DiagnosticsStatus.ts +++ b/src/types/ocpp/1.6/DiagnosticsStatus.ts @@ -2,5 +2,5 @@ export enum OCPP16DiagnosticsStatus { Idle = 'Idle', Uploaded = 'Uploaded', UploadFailed = 'UploadFailed', - Uploading = 'Uploading', + Uploading = 'Uploading' } diff --git a/src/types/ocpp/1.6/MeterValues.ts b/src/types/ocpp/1.6/MeterValues.ts index e8d14ef9..92cac7c3 100644 --- a/src/types/ocpp/1.6/MeterValues.ts +++ b/src/types/ocpp/1.6/MeterValues.ts @@ -1,5 +1,5 @@ -import type { EmptyObject } from '../../EmptyObject.js'; -import type { JsonObject } from '../../JsonType.js'; +import type { EmptyObject } from '../../EmptyObject.js' +import type { JsonObject } from '../../JsonType.js' export enum OCPP16MeterValueUnit { WATT_HOUR = 'Wh', @@ -17,7 +17,7 @@ export enum OCPP16MeterValueUnit { TEMP_CELSIUS = 'Celsius', TEMP_FAHRENHEIT = 'Fahrenheit', TEMP_KELVIN = 'K', - PERCENT = 'Percent', + PERCENT = 'Percent' } export enum OCPP16MeterValueContext { @@ -28,7 +28,7 @@ export enum OCPP16MeterValueContext { SAMPLE_PERIODIC = 'Sample.Periodic', TRANSACTION_BEGIN = 'Transaction.Begin', TRANSACTION_END = 'Transaction.End', - TRIGGER = 'Trigger', + TRIGGER = 'Trigger' } export enum OCPP16MeterValueMeasurand { @@ -53,7 +53,7 @@ export enum OCPP16MeterValueMeasurand { FAN_RPM = 'RPM', STATE_OF_CHARGE = 'SoC', TEMPERATURE = 'Temperature', - VOLTAGE = 'Voltage', + VOLTAGE = 'Voltage' } export enum OCPP16MeterValueLocation { @@ -61,7 +61,7 @@ export enum OCPP16MeterValueLocation { CABLE = 'Cable', EV = 'EV', INLET = 'Inlet', - OUTLET = 'Outlet', + OUTLET = 'Outlet' } export enum OCPP16MeterValuePhase { @@ -74,33 +74,33 @@ export enum OCPP16MeterValuePhase { L3_N = 'L3-N', L1_L2 = 'L1-L2', L2_L3 = 'L2-L3', - L3_L1 = 'L3-L1', + L3_L1 = 'L3-L1' } enum OCPP16MeterValueFormat { RAW = 'Raw', - SIGNED_DATA = 'SignedData', + SIGNED_DATA = 'SignedData' } export interface OCPP16SampledValue extends JsonObject { - value: string; - unit?: OCPP16MeterValueUnit; - context?: OCPP16MeterValueContext; - measurand?: OCPP16MeterValueMeasurand; - phase?: OCPP16MeterValuePhase; - location?: OCPP16MeterValueLocation; - format?: OCPP16MeterValueFormat; + value: string + unit?: OCPP16MeterValueUnit + context?: OCPP16MeterValueContext + measurand?: OCPP16MeterValueMeasurand + phase?: OCPP16MeterValuePhase + location?: OCPP16MeterValueLocation + format?: OCPP16MeterValueFormat } export interface OCPP16MeterValue extends JsonObject { - timestamp: Date; - sampledValue: OCPP16SampledValue[]; + timestamp: Date + sampledValue: OCPP16SampledValue[] } export interface OCPP16MeterValuesRequest extends JsonObject { - connectorId: number; - transactionId?: number; - meterValue: OCPP16MeterValue[]; + connectorId: number + transactionId?: number + meterValue: OCPP16MeterValue[] } -export type OCPP16MeterValuesResponse = EmptyObject; +export type OCPP16MeterValuesResponse = EmptyObject diff --git a/src/types/ocpp/1.6/Requests.ts b/src/types/ocpp/1.6/Requests.ts index 9c3ece54..32d9757f 100644 --- a/src/types/ocpp/1.6/Requests.ts +++ b/src/types/ocpp/1.6/Requests.ts @@ -1,14 +1,14 @@ -import type { OCPP16ChargePointErrorCode } from './ChargePointErrorCode.js'; -import type { OCPP16ChargePointStatus } from './ChargePointStatus.js'; +import type { OCPP16ChargePointErrorCode } from './ChargePointErrorCode.js' +import type { OCPP16ChargePointStatus } from './ChargePointStatus.js' import type { OCPP16ChargingProfile, OCPP16ChargingProfilePurposeType, - OCPP16ChargingRateUnitType, -} from './ChargingProfile.js'; -import type { OCPP16StandardParametersKey, OCPP16VendorParametersKey } from './Configuration.js'; -import type { OCPP16DiagnosticsStatus } from './DiagnosticsStatus.js'; -import type { EmptyObject } from '../../EmptyObject.js'; -import type { JsonObject } from '../../JsonType.js'; + OCPP16ChargingRateUnitType +} from './ChargingProfile.js' +import type { OCPP16StandardParametersKey, OCPP16VendorParametersKey } from './Configuration.js' +import type { OCPP16DiagnosticsStatus } from './DiagnosticsStatus.js' +import type { EmptyObject } from '../../EmptyObject.js' +import type { JsonObject } from '../../JsonType.js' export enum OCPP16RequestCommand { BOOT_NOTIFICATION = 'BootNotification', @@ -20,7 +20,7 @@ export enum OCPP16RequestCommand { METER_VALUES = 'MeterValues', DIAGNOSTICS_STATUS_NOTIFICATION = 'DiagnosticsStatusNotification', FIRMWARE_STATUS_NOTIFICATION = 'FirmwareStatusNotification', - DATA_TRANSFER = 'DataTransfer', + DATA_TRANSFER = 'DataTransfer' } export enum OCPP16IncomingRequestCommand { @@ -40,102 +40,102 @@ export enum OCPP16IncomingRequestCommand { DATA_TRANSFER = 'DataTransfer', UPDATE_FIRMWARE = 'UpdateFirmware', RESERVE_NOW = 'ReserveNow', - CANCEL_RESERVATION = 'CancelReservation', + CANCEL_RESERVATION = 'CancelReservation' } -export type OCPP16HeartbeatRequest = EmptyObject; +export type OCPP16HeartbeatRequest = EmptyObject export interface OCPP16BootNotificationRequest extends JsonObject { - chargePointVendor: string; - chargePointModel: string; - chargePointSerialNumber?: string; - chargeBoxSerialNumber?: string; - firmwareVersion?: string; - iccid?: string; - imsi?: string; - meterType?: string; - meterSerialNumber?: string; + chargePointVendor: string + chargePointModel: string + chargePointSerialNumber?: string + chargeBoxSerialNumber?: string + firmwareVersion?: string + iccid?: string + imsi?: string + meterType?: string + meterSerialNumber?: string } export interface OCPP16StatusNotificationRequest extends JsonObject { - connectorId: number; - errorCode: OCPP16ChargePointErrorCode; - status: OCPP16ChargePointStatus; - info?: string; - timestamp?: Date; - vendorId?: string; - vendorErrorCode?: string; + connectorId: number + errorCode: OCPP16ChargePointErrorCode + status: OCPP16ChargePointStatus + info?: string + timestamp?: Date + vendorId?: string + vendorErrorCode?: string } -export type OCPP16ClearCacheRequest = EmptyObject; +export type OCPP16ClearCacheRequest = EmptyObject -type OCPP16ConfigurationKey = string | OCPP16StandardParametersKey | OCPP16VendorParametersKey; +type OCPP16ConfigurationKey = string | OCPP16StandardParametersKey | OCPP16VendorParametersKey export interface ChangeConfigurationRequest extends JsonObject { - key: OCPP16ConfigurationKey; - value: string; + key: OCPP16ConfigurationKey + value: string } export interface RemoteStartTransactionRequest extends JsonObject { - connectorId: number; - idTag: string; - chargingProfile?: OCPP16ChargingProfile; + connectorId: number + idTag: string + chargingProfile?: OCPP16ChargingProfile } export interface RemoteStopTransactionRequest extends JsonObject { - transactionId: number; + transactionId: number } export interface UnlockConnectorRequest extends JsonObject { - connectorId: number; + connectorId: number } export interface GetConfigurationRequest extends JsonObject { - key?: OCPP16ConfigurationKey[]; + key?: OCPP16ConfigurationKey[] } enum ResetType { HARD = 'Hard', - SOFT = 'Soft', + SOFT = 'Soft' } export interface ResetRequest extends JsonObject { - type: ResetType; + type: ResetType } export interface OCPP16GetCompositeScheduleRequest extends JsonObject { - connectorId: number; - duration: number; - chargingRateUnit?: OCPP16ChargingRateUnitType; + connectorId: number + duration: number + chargingRateUnit?: OCPP16ChargingRateUnitType } export interface SetChargingProfileRequest extends JsonObject { - connectorId: number; - csChargingProfiles: OCPP16ChargingProfile; + connectorId: number + csChargingProfiles: OCPP16ChargingProfile } export enum OCPP16AvailabilityType { Inoperative = 'Inoperative', - Operative = 'Operative', + Operative = 'Operative' } export interface OCPP16ChangeAvailabilityRequest extends JsonObject { - connectorId: number; - type: OCPP16AvailabilityType; + connectorId: number + type: OCPP16AvailabilityType } export interface OCPP16ClearChargingProfileRequest extends JsonObject { - id?: number; - connectorId?: number; - chargingProfilePurpose?: OCPP16ChargingProfilePurposeType; - stackLevel?: number; + id?: number + connectorId?: number + chargingProfilePurpose?: OCPP16ChargingProfilePurposeType + stackLevel?: number } export interface OCPP16UpdateFirmwareRequest extends JsonObject { - location: string; - retrieveDate: Date; - retries?: number; - retryInterval?: number; + location: string + retrieveDate: Date + retries?: number + retryInterval?: number } export enum OCPP16FirmwareStatus { @@ -145,23 +145,23 @@ export enum OCPP16FirmwareStatus { Idle = 'Idle', InstallationFailed = 'InstallationFailed', Installing = 'Installing', - Installed = 'Installed', + Installed = 'Installed' } export type OCPP16FirmwareStatusNotificationRequest = { - status: OCPP16FirmwareStatus; -} & JsonObject; + status: OCPP16FirmwareStatus +} & JsonObject export interface GetDiagnosticsRequest extends JsonObject { - location: string; - retries?: number; - retryInterval?: number; - startTime?: Date; - stopTime?: Date; + location: string + retries?: number + retryInterval?: number + startTime?: Date + stopTime?: Date } export interface OCPP16DiagnosticsStatusNotificationRequest extends JsonObject { - status: OCPP16DiagnosticsStatus; + status: OCPP16DiagnosticsStatus } export enum OCPP16MessageTrigger { @@ -170,30 +170,30 @@ export enum OCPP16MessageTrigger { FirmwareStatusNotification = 'FirmwareStatusNotification', Heartbeat = 'Heartbeat', MeterValues = 'MeterValues', - StatusNotification = 'StatusNotification', + StatusNotification = 'StatusNotification' } export interface OCPP16TriggerMessageRequest extends JsonObject { - requestedMessage: OCPP16MessageTrigger; - connectorId?: number; + requestedMessage: OCPP16MessageTrigger + connectorId?: number } export enum OCPP16DataTransferVendorId {} export interface OCPP16DataTransferRequest extends JsonObject { - vendorId: string; - messageId?: string; - data?: string; + vendorId: string + messageId?: string + data?: string } export interface OCPP16ReserveNowRequest extends JsonObject { - connectorId: number; - expiryDate: Date; - idTag: string; - parentIdTag?: string; - reservationId: number; + connectorId: number + expiryDate: Date + idTag: string + parentIdTag?: string + reservationId: number } export interface OCPP16CancelReservationRequest extends JsonObject { - reservationId: number; + reservationId: number } diff --git a/src/types/ocpp/1.6/Responses.ts b/src/types/ocpp/1.6/Responses.ts index d813eac0..b8c36ed2 100644 --- a/src/types/ocpp/1.6/Responses.ts +++ b/src/types/ocpp/1.6/Responses.ts @@ -1,113 +1,113 @@ -import type { OCPP16ChargingSchedule } from './ChargingProfile.js'; -import type { EmptyObject } from '../../EmptyObject.js'; -import type { JsonObject } from '../../JsonType.js'; -import type { GenericStatus, RegistrationStatusEnumType } from '../Common.js'; -import type { OCPPConfigurationKey } from '../Configuration.js'; +import type { OCPP16ChargingSchedule } from './ChargingProfile.js' +import type { EmptyObject } from '../../EmptyObject.js' +import type { JsonObject } from '../../JsonType.js' +import type { GenericStatus, RegistrationStatusEnumType } from '../Common.js' +import type { OCPPConfigurationKey } from '../Configuration.js' export interface OCPP16HeartbeatResponse extends JsonObject { - currentTime: Date; + currentTime: Date } export enum OCPP16UnlockStatus { UNLOCKED = 'Unlocked', UNLOCK_FAILED = 'UnlockFailed', - NOT_SUPPORTED = 'NotSupported', + NOT_SUPPORTED = 'NotSupported' } export interface UnlockConnectorResponse extends JsonObject { - status: OCPP16UnlockStatus; + status: OCPP16UnlockStatus } export enum OCPP16ConfigurationStatus { ACCEPTED = 'Accepted', REJECTED = 'Rejected', REBOOT_REQUIRED = 'RebootRequired', - NOT_SUPPORTED = 'NotSupported', + NOT_SUPPORTED = 'NotSupported' } export interface ChangeConfigurationResponse extends JsonObject { - status: OCPP16ConfigurationStatus; + status: OCPP16ConfigurationStatus } export interface OCPP16BootNotificationResponse extends JsonObject { - status: RegistrationStatusEnumType; - currentTime: Date; - interval: number; + status: RegistrationStatusEnumType + currentTime: Date + interval: number } -export type OCPP16StatusNotificationResponse = EmptyObject; +export type OCPP16StatusNotificationResponse = EmptyObject export interface GetConfigurationResponse extends JsonObject { - configurationKey: OCPPConfigurationKey[]; - unknownKey: string[]; + configurationKey: OCPPConfigurationKey[] + unknownKey: string[] } export enum OCPP16ChargingProfileStatus { ACCEPTED = 'Accepted', REJECTED = 'Rejected', - NOT_SUPPORTED = 'NotSupported', + NOT_SUPPORTED = 'NotSupported' } export interface OCPP16GetCompositeScheduleResponse extends JsonObject { - status: GenericStatus; - connectorId?: number; - scheduleStart?: Date; - chargingSchedule?: OCPP16ChargingSchedule; + status: GenericStatus + connectorId?: number + scheduleStart?: Date + chargingSchedule?: OCPP16ChargingSchedule } export interface SetChargingProfileResponse extends JsonObject { - status: OCPP16ChargingProfileStatus; + status: OCPP16ChargingProfileStatus } export enum OCPP16AvailabilityStatus { ACCEPTED = 'Accepted', REJECTED = 'Rejected', - SCHEDULED = 'Scheduled', + SCHEDULED = 'Scheduled' } export interface OCPP16ChangeAvailabilityResponse extends JsonObject { - status: OCPP16AvailabilityStatus; + status: OCPP16AvailabilityStatus } export enum OCPP16ClearChargingProfileStatus { ACCEPTED = 'Accepted', - UNKNOWN = 'Unknown', + UNKNOWN = 'Unknown' } export interface OCPP16ClearChargingProfileResponse extends JsonObject { - status: OCPP16ClearChargingProfileStatus; + status: OCPP16ClearChargingProfileStatus } -export type OCPP16UpdateFirmwareResponse = EmptyObject; +export type OCPP16UpdateFirmwareResponse = EmptyObject -export type OCPP16FirmwareStatusNotificationResponse = EmptyObject; +export type OCPP16FirmwareStatusNotificationResponse = EmptyObject export interface GetDiagnosticsResponse extends JsonObject { - fileName?: string; + fileName?: string } -export type OCPP16DiagnosticsStatusNotificationResponse = EmptyObject; +export type OCPP16DiagnosticsStatusNotificationResponse = EmptyObject export enum OCPP16TriggerMessageStatus { ACCEPTED = 'Accepted', REJECTED = 'Rejected', - NOT_IMPLEMENTED = 'NotImplemented', + NOT_IMPLEMENTED = 'NotImplemented' } export interface OCPP16TriggerMessageResponse extends JsonObject { - status: OCPP16TriggerMessageStatus; + status: OCPP16TriggerMessageStatus } export enum OCPP16DataTransferStatus { ACCEPTED = 'Accepted', REJECTED = 'Rejected', UNKNOWN_MESSAGE_ID = 'UnknownMessageId', - UNKNOWN_VENDOR_ID = 'UnknownVendorId', + UNKNOWN_VENDOR_ID = 'UnknownVendorId' } export interface OCPP16DataTransferResponse extends JsonObject { - status: OCPP16DataTransferStatus; - data?: string; + status: OCPP16DataTransferStatus + data?: string } export enum OCPP16ReservationStatus { @@ -116,9 +116,9 @@ export enum OCPP16ReservationStatus { OCCUPIED = 'Occupied', REJECTED = 'Rejected', UNAVAILABLE = 'Unavailable', - NOT_SUPPORTED = 'NotSupported', + NOT_SUPPORTED = 'NotSupported' } export interface OCPP16ReserveNowResponse extends JsonObject { - status: OCPP16ReservationStatus; + status: OCPP16ReservationStatus } diff --git a/src/types/ocpp/1.6/Transaction.ts b/src/types/ocpp/1.6/Transaction.ts index 49cabf2e..2e6c7289 100644 --- a/src/types/ocpp/1.6/Transaction.ts +++ b/src/types/ocpp/1.6/Transaction.ts @@ -1,5 +1,5 @@ -import type { OCPP16MeterValue } from './MeterValues.js'; -import type { JsonObject } from '../../JsonType.js'; +import type { OCPP16MeterValue } from './MeterValues.js' +import type { JsonObject } from '../../JsonType.js' export enum OCPP16StopTransactionReason { EMERGENCY_STOP = 'EmergencyStop', @@ -12,7 +12,7 @@ export enum OCPP16StopTransactionReason { REMOTE = 'Remote', SOFT_RESET = 'SoftReset', UNLOCK_COMMAND = 'UnlockCommand', - DE_AUTHORIZED = 'DeAuthorized', + DE_AUTHORIZED = 'DeAuthorized' } export enum OCPP16AuthorizationStatus { @@ -20,45 +20,45 @@ export enum OCPP16AuthorizationStatus { BLOCKED = 'Blocked', EXPIRED = 'Expired', INVALID = 'Invalid', - CONCURRENT_TX = 'ConcurrentTx', + CONCURRENT_TX = 'ConcurrentTx' } interface IdTagInfo extends JsonObject { - status: OCPP16AuthorizationStatus; - parentIdTag?: string; - expiryDate?: Date; + status: OCPP16AuthorizationStatus + parentIdTag?: string + expiryDate?: Date } export interface OCPP16AuthorizeRequest extends JsonObject { - idTag: string; + idTag: string } export interface OCPP16AuthorizeResponse extends JsonObject { - idTagInfo: IdTagInfo; + idTagInfo: IdTagInfo } export interface OCPP16StartTransactionRequest extends JsonObject { - connectorId: number; - idTag: string; - meterStart: number; - timestamp: Date; - reservationId?: number; + connectorId: number + idTag: string + meterStart: number + timestamp: Date + reservationId?: number } export interface OCPP16StartTransactionResponse extends JsonObject { - idTagInfo: IdTagInfo; - transactionId: number; + idTagInfo: IdTagInfo + transactionId: number } export interface OCPP16StopTransactionRequest extends JsonObject { - idTag?: string; - meterStop: number; - timestamp: Date; - transactionId: number; - reason?: OCPP16StopTransactionReason; - transactionData?: OCPP16MeterValue[]; + idTag?: string + meterStop: number + timestamp: Date + transactionId: number + reason?: OCPP16StopTransactionReason + transactionData?: OCPP16MeterValue[] } export interface OCPP16StopTransactionResponse extends JsonObject { - idTagInfo?: IdTagInfo; + idTagInfo?: IdTagInfo } diff --git a/src/types/ocpp/2.0/Common.ts b/src/types/ocpp/2.0/Common.ts index 9898ca3c..9d13b2a8 100644 --- a/src/types/ocpp/2.0/Common.ts +++ b/src/types/ocpp/2.0/Common.ts @@ -1,17 +1,15 @@ -import type { JsonObject } from '../../JsonType.js'; -import type { GenericStatus } from '../Common.js'; +import type { JsonObject } from '../../JsonType.js' +import type { GenericStatus } from '../Common.js' export enum DataEnumType { - // eslint-disable-next-line id-blacklist string = 'string', decimal = 'decimal', integer = 'integer', dateTime = 'dateTime', - // eslint-disable-next-line id-blacklist boolean = 'boolean', OptionList = 'OptionList', SequenceList = 'SequenceList', - MemberList = 'MemberList', + MemberList = 'MemberList' } export enum BootReasonEnumType { @@ -23,12 +21,12 @@ export enum BootReasonEnumType { ScheduledReset = 'ScheduledReset', Triggered = 'Triggered', Unknown = 'Unknown', - Watchdog = 'Watchdog', + Watchdog = 'Watchdog' } export enum OperationalStatusEnumType { Operative = 'Operative', - Inoperative = 'Inoperative', + Inoperative = 'Inoperative' } export enum OCPP20ConnectorStatusEnumType { @@ -36,15 +34,15 @@ export enum OCPP20ConnectorStatusEnumType { Occupied = 'Occupied', Reserved = 'Reserved', Unavailable = 'Unavailable', - Faulted = 'Faulted', + Faulted = 'Faulted' } -export type GenericStatusEnumType = GenericStatus; +export type GenericStatusEnumType = GenericStatus export enum HashAlgorithmEnumType { SHA256 = 'SHA256', SHA384 = 'SHA384', - SHA512 = 'SHA512', + SHA512 = 'SHA512' } export enum GetCertificateIdUseEnumType { @@ -52,77 +50,77 @@ export enum GetCertificateIdUseEnumType { MORootCertificate = 'MORootCertificate', CSMSRootCertificate = 'CSMSRootCertificate', V2GCertificateChain = 'V2GCertificateChain', - ManufacturerRootCertificate = 'ManufacturerRootCertificate', + ManufacturerRootCertificate = 'ManufacturerRootCertificate' } export enum GetCertificateStatusEnumType { Accepted = 'Accepted', - Failed = 'Failed', + Failed = 'Failed' } export enum GetInstalledCertificateStatusEnumType { Accepted = 'Accepted', - NotFound = 'NotFound', + NotFound = 'NotFound' } export enum InstallCertificateStatusEnumType { Accepted = 'Accepted', Rejected = 'Rejected', - Failed = 'Failed', + Failed = 'Failed' } export enum InstallCertificateUseEnumType { V2GRootCertificate = 'V2GRootCertificate', MORootCertificate = 'MORootCertificate', CSMSRootCertificate = 'CSMSRootCertificate', - ManufacturerRootCertificate = 'ManufacturerRootCertificate', + ManufacturerRootCertificate = 'ManufacturerRootCertificate' } export enum DeleteCertificateStatusEnumType { Accepted = 'Accepted', Failed = 'Failed', - NotFound = 'NotFound', + NotFound = 'NotFound' } export enum CertificateActionEnumType { Install = 'Install', - Update = 'Update', + Update = 'Update' } export enum CertificateSigningUseEnumType { ChargingStationCertificate = 'ChargingStationCertificate', - V2GCertificate = 'V2GCertificate', + V2GCertificate = 'V2GCertificate' } -export type CertificateSignedStatusEnumType = GenericStatusEnumType; +export type CertificateSignedStatusEnumType = GenericStatusEnumType export type CertificateHashDataType = { - hashAlgorithm: HashAlgorithmEnumType; - issuerNameHash: string; - issuerKeyHash: string; - serialNumber: string; -} & JsonObject; + hashAlgorithm: HashAlgorithmEnumType + issuerNameHash: string + issuerKeyHash: string + serialNumber: string +} & JsonObject export type CertificateHashDataChainType = { - certificateType: GetCertificateIdUseEnumType; - certificateHashData: CertificateHashDataType; - childCertificateHashData?: CertificateHashDataType; -} & JsonObject; + certificateType: GetCertificateIdUseEnumType + certificateHashData: CertificateHashDataType + childCertificateHashData?: CertificateHashDataType +} & JsonObject export type OCSPRequestDataType = { - hashAlgorithm: HashAlgorithmEnumType; - issuerNameHash: string; - issuerKeyHash: string; - serialNumber: string; - responderURL: string; -} & JsonObject; + hashAlgorithm: HashAlgorithmEnumType + issuerNameHash: string + issuerKeyHash: string + serialNumber: string + responderURL: string +} & JsonObject export type StatusInfoType = { - reasonCode: string; - additionalInfo?: string; -} & JsonObject; + reasonCode: string + additionalInfo?: string +} & JsonObject export type EVSEType = { - id: number; - connectorId?: string; -} & JsonObject; + id: number + connectorId?: string +} & JsonObject diff --git a/src/types/ocpp/2.0/Requests.ts b/src/types/ocpp/2.0/Requests.ts index b91edc9d..ef7a5161 100644 --- a/src/types/ocpp/2.0/Requests.ts +++ b/src/types/ocpp/2.0/Requests.ts @@ -1,58 +1,58 @@ import type { BootReasonEnumType, InstallCertificateUseEnumType, - OCPP20ConnectorStatusEnumType, -} from './Common.js'; -import type { OCPP20SetVariableDataType } from './Variables.js'; -import type { EmptyObject } from '../../EmptyObject.js'; -import type { JsonObject } from '../../JsonType.js'; + OCPP20ConnectorStatusEnumType +} from './Common.js' +import type { OCPP20SetVariableDataType } from './Variables.js' +import type { EmptyObject } from '../../EmptyObject.js' +import type { JsonObject } from '../../JsonType.js' export enum OCPP20RequestCommand { BOOT_NOTIFICATION = 'BootNotification', HEARTBEAT = 'Heartbeat', - STATUS_NOTIFICATION = 'StatusNotification', + STATUS_NOTIFICATION = 'StatusNotification' } export enum OCPP20IncomingRequestCommand { CLEAR_CACHE = 'ClearCache', REQUEST_START_TRANSACTION = 'RequestStartTransaction', - REQUEST_STOP_TRANSACTION = 'RequestStopTransaction', + REQUEST_STOP_TRANSACTION = 'RequestStopTransaction' } type ModemType = { - iccid?: string; - imsi?: string; -} & JsonObject; + iccid?: string + imsi?: string +} & JsonObject type ChargingStationType = { - serialNumber?: string; - model: string; - vendorName: string; - firmwareVersion?: string; - modem?: ModemType; -} & JsonObject; + serialNumber?: string + model: string + vendorName: string + firmwareVersion?: string + modem?: ModemType +} & JsonObject export type OCPP20BootNotificationRequest = { - reason: BootReasonEnumType; - chargingStation: ChargingStationType; -} & JsonObject; + reason: BootReasonEnumType + chargingStation: ChargingStationType +} & JsonObject -export type OCPP20HeartbeatRequest = EmptyObject; +export type OCPP20HeartbeatRequest = EmptyObject -export type OCPP20ClearCacheRequest = EmptyObject; +export type OCPP20ClearCacheRequest = EmptyObject export type OCPP20StatusNotificationRequest = { - timestamp: Date; - connectorStatus: OCPP20ConnectorStatusEnumType; - evseId: number; - connectorId: number; -} & JsonObject; + timestamp: Date + connectorStatus: OCPP20ConnectorStatusEnumType + evseId: number + connectorId: number +} & JsonObject export type OCPP20SetVariablesRequest = { - setVariableData: OCPP20SetVariableDataType[]; -} & JsonObject; + setVariableData: OCPP20SetVariableDataType[] +} & JsonObject export type OCPP20InstallCertificateRequest = { - certificateType: InstallCertificateUseEnumType; - certificate: string; -} & JsonObject; + certificateType: InstallCertificateUseEnumType + certificate: string +} & JsonObject diff --git a/src/types/ocpp/2.0/Responses.ts b/src/types/ocpp/2.0/Responses.ts index 38fdda41..dbeb1e3f 100644 --- a/src/types/ocpp/2.0/Responses.ts +++ b/src/types/ocpp/2.0/Responses.ts @@ -1,36 +1,36 @@ import type { GenericStatusEnumType, InstallCertificateStatusEnumType, - StatusInfoType, -} from './Common.js'; -import type { OCPP20SetVariableResultType } from './Variables.js'; -import type { EmptyObject } from '../../EmptyObject.js'; -import type { JsonObject } from '../../JsonType.js'; -import type { RegistrationStatusEnumType } from '../Common.js'; + StatusInfoType +} from './Common.js' +import type { OCPP20SetVariableResultType } from './Variables.js' +import type { EmptyObject } from '../../EmptyObject.js' +import type { JsonObject } from '../../JsonType.js' +import type { RegistrationStatusEnumType } from '../Common.js' export type OCPP20BootNotificationResponse = { - currentTime: Date; - status: RegistrationStatusEnumType; - interval: number; - statusInfo?: StatusInfoType; -} & JsonObject; + currentTime: Date + status: RegistrationStatusEnumType + interval: number + statusInfo?: StatusInfoType +} & JsonObject export type OCPP20HeartbeatResponse = { - currentTime: Date; -} & JsonObject; + currentTime: Date +} & JsonObject export type OCPP20ClearCacheResponse = { - status: GenericStatusEnumType; - statusInfo?: StatusInfoType; -} & JsonObject; + status: GenericStatusEnumType + statusInfo?: StatusInfoType +} & JsonObject -export type OCPP20StatusNotificationResponse = EmptyObject; +export type OCPP20StatusNotificationResponse = EmptyObject export type OCPP20SetVariablesResponse = { - setVariableResult: OCPP20SetVariableResultType[]; -} & JsonObject; + setVariableResult: OCPP20SetVariableResultType[] +} & JsonObject export type OCPP20InstallCertificateResponse = { - status: InstallCertificateStatusEnumType; - statusInfo?: StatusInfoType; -} & JsonObject; + status: InstallCertificateStatusEnumType + statusInfo?: StatusInfoType +} & JsonObject diff --git a/src/types/ocpp/2.0/Variables.ts b/src/types/ocpp/2.0/Variables.ts index 87075cbd..b5cd53e5 100644 --- a/src/types/ocpp/2.0/Variables.ts +++ b/src/types/ocpp/2.0/Variables.ts @@ -1,5 +1,5 @@ -import type { EVSEType, StatusInfoType } from './Common.js'; -import type { JsonObject } from '../../JsonType.js'; +import type { EVSEType, StatusInfoType } from './Common.js' +import type { JsonObject } from '../../JsonType.js' enum OCPP20ComponentName { AlignedDataCtrlr = 'AlignedDataCtrlr', @@ -19,7 +19,7 @@ enum OCPP20ComponentName { SecurityCtrlr = 'SecurityCtrlr', SmartChargingCtrlr = 'SmartChargingCtrlr', TariffCostCtrlr = 'TariffCostCtrlr', - TxCtrlr = 'TxCtrlr', + TxCtrlr = 'TxCtrlr' } export enum OCPP20RequiredVariableName { @@ -50,48 +50,48 @@ export enum OCPP20RequiredVariableName { TxEndedMeasurands = 'TxEndedMeasurands', TxStartedMeasurands = 'TxStartedMeasurands', TxUpdatedMeasurands = 'TxUpdatedMeasurands', - TxUpdatedInterval = 'TxUpdatedInterval', + TxUpdatedInterval = 'TxUpdatedInterval' } export enum OCPP20OptionalVariableName { HeartbeatInterval = 'HeartbeatInterval', - WebSocketPingInterval = 'WebSocketPingInterval', + WebSocketPingInterval = 'WebSocketPingInterval' } export enum OCPP20VendorVariableName { - ConnectionUrl = 'ConnectionUrl', + ConnectionUrl = 'ConnectionUrl' } enum AttributeEnumType { Actual = 'Actual', Target = 'Target', MinSet = 'MinSet', - MaxSet = 'MaxSet', + MaxSet = 'MaxSet' } type ComponentType = { - name: string | OCPP20ComponentName; - instance?: string; - evse?: EVSEType; -} & JsonObject; + name: string | OCPP20ComponentName + instance?: string + evse?: EVSEType +} & JsonObject type VariableName = | string | OCPP20RequiredVariableName | OCPP20OptionalVariableName - | OCPP20VendorVariableName; + | OCPP20VendorVariableName type VariableType = { - name: VariableName; - instance?: string; -} & JsonObject; + name: VariableName + instance?: string +} & JsonObject export type OCPP20SetVariableDataType = { - attributeType?: AttributeEnumType; - attributeValue: string; - component: ComponentType; - variable: VariableType; -} & JsonObject; + attributeType?: AttributeEnumType + attributeValue: string + component: ComponentType + variable: VariableType +} & JsonObject enum SetVariableStatusEnumType { Accepted = 'Accepted', @@ -99,18 +99,18 @@ enum SetVariableStatusEnumType { UnknownComponent = 'UnknownComponent', UnknownVariable = 'UnknownVariable', NotSupportedAttributeType = 'NotSupportedAttributeType', - RebootRequired = 'RebootRequired', + RebootRequired = 'RebootRequired' } export type OCPP20SetVariableResultType = { - attributeType?: AttributeEnumType; - attributeStatus: SetVariableStatusEnumType; - component: ComponentType; - variable: VariableType; - attributeStatusInfo?: StatusInfoType; -} & JsonObject; + attributeType?: AttributeEnumType + attributeStatus: SetVariableStatusEnumType + component: ComponentType + variable: VariableType + attributeStatusInfo?: StatusInfoType +} & JsonObject export type OCPP20ComponentVariableType = { - component: ComponentType; - variable?: VariableType; -} & JsonObject; + component: ComponentType + variable?: VariableType +} & JsonObject diff --git a/src/types/ocpp/ChargePointErrorCode.ts b/src/types/ocpp/ChargePointErrorCode.ts index 5fcbee45..9c29390e 100644 --- a/src/types/ocpp/ChargePointErrorCode.ts +++ b/src/types/ocpp/ChargePointErrorCode.ts @@ -1,6 +1,7 @@ -import { OCPP16ChargePointErrorCode } from './1.6/ChargePointErrorCode.js'; +import { OCPP16ChargePointErrorCode } from './1.6/ChargePointErrorCode.js' export const ChargePointErrorCode = { - ...OCPP16ChargePointErrorCode, -} as const; -export type ChargePointErrorCode = OCPP16ChargePointErrorCode; + ...OCPP16ChargePointErrorCode +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type ChargePointErrorCode = OCPP16ChargePointErrorCode diff --git a/src/types/ocpp/ChargingProfile.ts b/src/types/ocpp/ChargingProfile.ts index 6033ad95..efc25b90 100644 --- a/src/types/ocpp/ChargingProfile.ts +++ b/src/types/ocpp/ChargingProfile.ts @@ -3,24 +3,27 @@ import { OCPP16ChargingProfileKindType, OCPP16ChargingRateUnitType, type OCPP16ChargingSchedulePeriod, - OCPP16RecurrencyKindType, -} from './1.6/ChargingProfile.js'; + OCPP16RecurrencyKindType +} from './1.6/ChargingProfile.js' -export type ChargingProfile = OCPP16ChargingProfile; +export type ChargingProfile = OCPP16ChargingProfile -export type ChargingSchedulePeriod = OCPP16ChargingSchedulePeriod; +export type ChargingSchedulePeriod = OCPP16ChargingSchedulePeriod export const ChargingProfileKindType = { - ...OCPP16ChargingProfileKindType, -} as const; -export type ChargingProfileKindType = OCPP16ChargingProfileKindType; + ...OCPP16ChargingProfileKindType +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type ChargingProfileKindType = OCPP16ChargingProfileKindType export const RecurrencyKindType = { - ...OCPP16RecurrencyKindType, -} as const; -export type RecurrencyKindType = OCPP16RecurrencyKindType; + ...OCPP16RecurrencyKindType +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type RecurrencyKindType = OCPP16RecurrencyKindType export const ChargingRateUnitType = { - ...OCPP16ChargingRateUnitType, -} as const; -export type ChargingRateUnitType = OCPP16ChargingRateUnitType; + ...OCPP16ChargingRateUnitType +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type ChargingRateUnitType = OCPP16ChargingRateUnitType diff --git a/src/types/ocpp/Common.ts b/src/types/ocpp/Common.ts index a1be9be0..55b92f10 100644 --- a/src/types/ocpp/Common.ts +++ b/src/types/ocpp/Common.ts @@ -1,16 +1,16 @@ -import type { JsonObject } from '../JsonType.js'; +import type { JsonObject } from '../JsonType.js' export enum GenericStatus { Accepted = 'Accepted', - Rejected = 'Rejected', + Rejected = 'Rejected' } export interface GenericResponse extends JsonObject { - status: GenericStatus; + status: GenericStatus } export enum RegistrationStatusEnumType { ACCEPTED = 'Accepted', PENDING = 'Pending', - REJECTED = 'Rejected', + REJECTED = 'Rejected' } diff --git a/src/types/ocpp/Configuration.ts b/src/types/ocpp/Configuration.ts index 263ff004..ac8a0945 100644 --- a/src/types/ocpp/Configuration.ts +++ b/src/types/ocpp/Configuration.ts @@ -1,32 +1,35 @@ import { OCPP16StandardParametersKey, OCPP16SupportedFeatureProfiles, - OCPP16VendorParametersKey, -} from './1.6/Configuration.js'; + OCPP16VendorParametersKey +} from './1.6/Configuration.js' import { OCPP20OptionalVariableName, OCPP20RequiredVariableName, - OCPP20VendorVariableName, -} from './2.0/Variables.js'; -import type { JsonObject } from '../JsonType.js'; + OCPP20VendorVariableName +} from './2.0/Variables.js' +import type { JsonObject } from '../JsonType.js' export const StandardParametersKey = { ...OCPP16StandardParametersKey, ...OCPP20RequiredVariableName, - ...OCPP20OptionalVariableName, -} as const; -export type StandardParametersKey = OCPP16StandardParametersKey; + ...OCPP20OptionalVariableName +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type StandardParametersKey = OCPP16StandardParametersKey export const VendorParametersKey = { ...OCPP16VendorParametersKey, - ...OCPP20VendorVariableName, -} as const; -export type VendorParametersKey = OCPP16VendorParametersKey; + ...OCPP20VendorVariableName +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type VendorParametersKey = OCPP16VendorParametersKey export const SupportedFeatureProfiles = { - ...OCPP16SupportedFeatureProfiles, -} as const; -export type SupportedFeatureProfiles = OCPP16SupportedFeatureProfiles; + ...OCPP16SupportedFeatureProfiles +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type SupportedFeatureProfiles = OCPP16SupportedFeatureProfiles export enum ConnectorPhaseRotation { NotApplicable = 'NotApplicable', @@ -36,13 +39,13 @@ export enum ConnectorPhaseRotation { SRT = 'SRT', STR = 'STR', TRS = 'TRS', - TSR = 'TSR', + TSR = 'TSR' } -export type ConfigurationKeyType = string | StandardParametersKey | VendorParametersKey; +export type ConfigurationKeyType = string | StandardParametersKey | VendorParametersKey export type OCPPConfigurationKey = { - key: ConfigurationKeyType; - readonly: boolean; - value?: string; -} & JsonObject; + key: ConfigurationKeyType + readonly: boolean + value?: string +} & JsonObject diff --git a/src/types/ocpp/ConnectorStatusEnum.ts b/src/types/ocpp/ConnectorStatusEnum.ts index 9ed66fa5..9c4d2469 100644 --- a/src/types/ocpp/ConnectorStatusEnum.ts +++ b/src/types/ocpp/ConnectorStatusEnum.ts @@ -1,13 +1,14 @@ -import { OCPP16ChargePointStatus } from './1.6/ChargePointStatus.js'; -import { OCPP20ConnectorStatusEnumType } from './2.0/Common.js'; +import { OCPP16ChargePointStatus } from './1.6/ChargePointStatus.js' +import { OCPP20ConnectorStatusEnumType } from './2.0/Common.js' export const ConnectorStatusEnum = { ...OCPP16ChargePointStatus, - ...OCPP20ConnectorStatusEnumType, -} as const; -export type ConnectorStatusEnum = OCPP16ChargePointStatus | OCPP20ConnectorStatusEnumType; + ...OCPP20ConnectorStatusEnumType +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type ConnectorStatusEnum = OCPP16ChargePointStatus | OCPP20ConnectorStatusEnumType export type ConnectorStatusTransition = Readonly<{ - from?: ConnectorStatusEnum; - to: ConnectorStatusEnum; -}>; + from?: ConnectorStatusEnum + to: ConnectorStatusEnum +}> diff --git a/src/types/ocpp/ErrorType.ts b/src/types/ocpp/ErrorType.ts index e8762380..d6d8357f 100644 --- a/src/types/ocpp/ErrorType.ts +++ b/src/types/ocpp/ErrorType.ts @@ -19,5 +19,5 @@ export enum ErrorType { // Payload for Action is syntactically correct but at least one of the fields violates data type constraints (e.g. "somestring" = 12) TYPE_CONSTRAINT_VIOLATION = 'TypeConstraintViolation', // Any other error not covered by the previous ones - GENERIC_ERROR = 'GenericError', + GENERIC_ERROR = 'GenericError' } diff --git a/src/types/ocpp/MessageType.ts b/src/types/ocpp/MessageType.ts index 1346ee82..972ed474 100644 --- a/src/types/ocpp/MessageType.ts +++ b/src/types/ocpp/MessageType.ts @@ -1,5 +1,5 @@ export enum MessageType { CALL_MESSAGE = 2, // Caller to Callee CALL_RESULT_MESSAGE = 3, // Callee to Caller - CALL_ERROR_MESSAGE = 4, // Callee to Caller + CALL_ERROR_MESSAGE = 4 // Callee to Caller } diff --git a/src/types/ocpp/MeterValues.ts b/src/types/ocpp/MeterValues.ts index 9b6a3537..aec3d19e 100644 --- a/src/types/ocpp/MeterValues.ts +++ b/src/types/ocpp/MeterValues.ts @@ -5,34 +5,39 @@ import { OCPP16MeterValueMeasurand, OCPP16MeterValuePhase, OCPP16MeterValueUnit, - type OCPP16SampledValue, -} from './1.6/MeterValues.js'; + type OCPP16SampledValue +} from './1.6/MeterValues.js' export const MeterValueUnit = { - ...OCPP16MeterValueUnit, -} as const; -export type MeterValueUnit = OCPP16MeterValueUnit; + ...OCPP16MeterValueUnit +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type MeterValueUnit = OCPP16MeterValueUnit export const MeterValueContext = { - ...OCPP16MeterValueContext, -} as const; -export type MeterValueContext = OCPP16MeterValueContext; + ...OCPP16MeterValueContext +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type MeterValueContext = OCPP16MeterValueContext export const MeterValueMeasurand = { - ...OCPP16MeterValueMeasurand, -} as const; -export type MeterValueMeasurand = OCPP16MeterValueMeasurand; + ...OCPP16MeterValueMeasurand +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type MeterValueMeasurand = OCPP16MeterValueMeasurand export const MeterValueLocation = { - ...OCPP16MeterValueLocation, -} as const; -export type MeterValueLocation = OCPP16MeterValueLocation; + ...OCPP16MeterValueLocation +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type MeterValueLocation = OCPP16MeterValueLocation export const MeterValuePhase = { - ...OCPP16MeterValuePhase, -} as const; -export type MeterValuePhase = OCPP16MeterValuePhase; + ...OCPP16MeterValuePhase +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type MeterValuePhase = OCPP16MeterValuePhase -export type SampledValue = OCPP16SampledValue; +export type SampledValue = OCPP16SampledValue -export type MeterValue = OCPP16MeterValue; +export type MeterValue = OCPP16MeterValue diff --git a/src/types/ocpp/OCPPProtocol.ts b/src/types/ocpp/OCPPProtocol.ts index 1df59397..3342d81d 100644 --- a/src/types/ocpp/OCPPProtocol.ts +++ b/src/types/ocpp/OCPPProtocol.ts @@ -1,3 +1,3 @@ export enum OCPPProtocol { - JSON = 'json', + JSON = 'json' } diff --git a/src/types/ocpp/OCPPVersion.ts b/src/types/ocpp/OCPPVersion.ts index a19fc280..37690b5e 100644 --- a/src/types/ocpp/OCPPVersion.ts +++ b/src/types/ocpp/OCPPVersion.ts @@ -1,5 +1,5 @@ export enum OCPPVersion { VERSION_16 = '1.6', VERSION_20 = '2.0', - VERSION_201 = '2.0.1', + VERSION_201 = '2.0.1' } diff --git a/src/types/ocpp/Requests.ts b/src/types/ocpp/Requests.ts index 0d60daf5..c718f1e0 100644 --- a/src/types/ocpp/Requests.ts +++ b/src/types/ocpp/Requests.ts @@ -1,5 +1,5 @@ -import { OCPP16DiagnosticsStatus } from './1.6/DiagnosticsStatus.js'; -import type { OCPP16MeterValuesRequest } from './1.6/MeterValues.js'; +import { OCPP16DiagnosticsStatus } from './1.6/DiagnosticsStatus.js' +import type { OCPP16MeterValuesRequest } from './1.6/MeterValues.js' import { OCPP16AvailabilityType, type OCPP16BootNotificationRequest, @@ -13,97 +13,103 @@ import { OCPP16MessageTrigger, OCPP16RequestCommand, type OCPP16ReserveNowRequest, - type OCPP16StatusNotificationRequest, -} from './1.6/Requests.js'; -import { OperationalStatusEnumType } from './2.0/Common.js'; + type OCPP16StatusNotificationRequest +} from './1.6/Requests.js' +import { OperationalStatusEnumType } from './2.0/Common.js' import { type OCPP20BootNotificationRequest, OCPP20IncomingRequestCommand, OCPP20RequestCommand, - type OCPP20StatusNotificationRequest, -} from './2.0/Requests.js'; -import type { MessageType } from './MessageType.js'; -import type { ChargingStation } from '../../charging-station/index.js'; -import type { OCPPError } from '../../exception/index.js'; -import type { JsonType } from '../JsonType.js'; + type OCPP20StatusNotificationRequest +} from './2.0/Requests.js' +import type { MessageType } from './MessageType.js' +import type { ChargingStation } from '../../charging-station/index.js' +import type { OCPPError } from '../../exception/index.js' +import type { JsonType } from '../JsonType.js' export const RequestCommand = { ...OCPP16RequestCommand, - ...OCPP20RequestCommand, -} as const; -export type RequestCommand = OCPP16RequestCommand | OCPP20RequestCommand; + ...OCPP20RequestCommand +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type RequestCommand = OCPP16RequestCommand | OCPP20RequestCommand -export type OutgoingRequest = [MessageType.CALL_MESSAGE, string, RequestCommand, JsonType]; +export type OutgoingRequest = [MessageType.CALL_MESSAGE, string, RequestCommand, JsonType] export interface RequestParams { - skipBufferingOnError?: boolean; - triggerMessage?: boolean; - throwError?: boolean; + skipBufferingOnError?: boolean + triggerMessage?: boolean + throwError?: boolean } export const IncomingRequestCommand = { ...OCPP16IncomingRequestCommand, - ...OCPP20IncomingRequestCommand, -} as const; -export type IncomingRequestCommand = OCPP16IncomingRequestCommand | OCPP20IncomingRequestCommand; + ...OCPP20IncomingRequestCommand +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type IncomingRequestCommand = OCPP16IncomingRequestCommand | OCPP20IncomingRequestCommand -export type IncomingRequest = [MessageType.CALL_MESSAGE, string, IncomingRequestCommand, JsonType]; +export type IncomingRequest = [MessageType.CALL_MESSAGE, string, IncomingRequestCommand, JsonType] export type IncomingRequestHandler = ( chargingStation: ChargingStation, - commandPayload: JsonType, -) => JsonType | Promise; + commandPayload: JsonType +) => JsonType | Promise -export type ResponseCallback = (payload: JsonType, requestPayload: JsonType) => void; +export type ResponseCallback = (payload: JsonType, requestPayload: JsonType) => void -export type ErrorCallback = (ocppError: OCPPError, requestStatistic?: boolean) => void; +export type ErrorCallback = (ocppError: OCPPError, requestStatistic?: boolean) => void export type CachedRequest = [ ResponseCallback, ErrorCallback, RequestCommand | IncomingRequestCommand, - JsonType, -]; + JsonType +] export const MessageTrigger = { - ...OCPP16MessageTrigger, -} as const; -export type MessageTrigger = OCPP16MessageTrigger; + ...OCPP16MessageTrigger +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type MessageTrigger = OCPP16MessageTrigger -export type BootNotificationRequest = OCPP16BootNotificationRequest | OCPP20BootNotificationRequest; +export type BootNotificationRequest = OCPP16BootNotificationRequest | OCPP20BootNotificationRequest -export type HeartbeatRequest = OCPP16HeartbeatRequest; +export type HeartbeatRequest = OCPP16HeartbeatRequest export type StatusNotificationRequest = | OCPP16StatusNotificationRequest - | OCPP20StatusNotificationRequest; + | OCPP20StatusNotificationRequest -export type MeterValuesRequest = OCPP16MeterValuesRequest; +export type MeterValuesRequest = OCPP16MeterValuesRequest -export type DataTransferRequest = OCPP16DataTransferRequest; +export type DataTransferRequest = OCPP16DataTransferRequest -export type DiagnosticsStatusNotificationRequest = OCPP16DiagnosticsStatusNotificationRequest; +export type DiagnosticsStatusNotificationRequest = OCPP16DiagnosticsStatusNotificationRequest -export type FirmwareStatusNotificationRequest = OCPP16FirmwareStatusNotificationRequest; +export type FirmwareStatusNotificationRequest = OCPP16FirmwareStatusNotificationRequest export const AvailabilityType = { ...OCPP16AvailabilityType, - ...OperationalStatusEnumType, -} as const; -export type AvailabilityType = OCPP16AvailabilityType | OperationalStatusEnumType; + ...OperationalStatusEnumType +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type AvailabilityType = OCPP16AvailabilityType | OperationalStatusEnumType export const DiagnosticsStatus = { - ...OCPP16DiagnosticsStatus, -} as const; -export type DiagnosticsStatus = OCPP16DiagnosticsStatus; + ...OCPP16DiagnosticsStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type DiagnosticsStatus = OCPP16DiagnosticsStatus export const FirmwareStatus = { - ...OCPP16FirmwareStatus, -} as const; -export type FirmwareStatus = OCPP16FirmwareStatus; + ...OCPP16FirmwareStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type FirmwareStatus = OCPP16FirmwareStatus -export type ResponseType = JsonType | OCPPError; +export type ResponseType = JsonType | OCPPError -export type ReserveNowRequest = OCPP16ReserveNowRequest; +export type ReserveNowRequest = OCPP16ReserveNowRequest -export type CancelReservationRequest = OCPP16CancelReservationRequest; +export type CancelReservationRequest = OCPP16CancelReservationRequest diff --git a/src/types/ocpp/Reservation.ts b/src/types/ocpp/Reservation.ts index 8d783a26..5f15838a 100644 --- a/src/types/ocpp/Reservation.ts +++ b/src/types/ocpp/Reservation.ts @@ -1,13 +1,13 @@ -import type { OCPP16ReserveNowRequest } from './1.6/Requests.js'; +import type { OCPP16ReserveNowRequest } from './1.6/Requests.js' -export type Reservation = OCPP16ReserveNowRequest; +export type Reservation = OCPP16ReserveNowRequest -export type ReservationKey = keyof Reservation; +export type ReservationKey = keyof Reservation export enum ReservationTerminationReason { EXPIRED = 'Expired', TRANSACTION_STARTED = 'TransactionStarted', CONNECTOR_STATE_CHANGED = 'ConnectorStateChanged', RESERVATION_CANCELED = 'ReservationCanceled', - REPLACE_EXISTING = 'ReplaceExisting', + REPLACE_EXISTING = 'ReplaceExisting' } diff --git a/src/types/ocpp/Responses.ts b/src/types/ocpp/Responses.ts index e00302ac..9983ee8a 100644 --- a/src/types/ocpp/Responses.ts +++ b/src/types/ocpp/Responses.ts @@ -1,4 +1,4 @@ -import type { OCPP16MeterValuesResponse } from './1.6/MeterValues.js'; +import type { OCPP16MeterValuesResponse } from './1.6/MeterValues.js' import { OCPP16AvailabilityStatus, type OCPP16BootNotificationResponse, @@ -13,86 +13,95 @@ import { OCPP16ReservationStatus, type OCPP16StatusNotificationResponse, OCPP16TriggerMessageStatus, - OCPP16UnlockStatus, -} from './1.6/Responses.js'; -import type { OCPP20BootNotificationResponse, OCPP20ClearCacheResponse } from './2.0/Responses.js'; -import { type GenericResponse, GenericStatus } from './Common.js'; -import type { ErrorType } from './ErrorType.js'; -import type { MessageType } from './MessageType.js'; -import type { ChargingStation } from '../../charging-station/index.js'; -import type { JsonType } from '../JsonType.js'; + OCPP16UnlockStatus +} from './1.6/Responses.js' +import type { OCPP20BootNotificationResponse, OCPP20ClearCacheResponse } from './2.0/Responses.js' +import { type GenericResponse, GenericStatus } from './Common.js' +import type { ErrorType } from './ErrorType.js' +import type { MessageType } from './MessageType.js' +import type { ChargingStation } from '../../charging-station/index.js' +import type { JsonType } from '../JsonType.js' -export type Response = [MessageType.CALL_RESULT_MESSAGE, string, JsonType]; +export type Response = [MessageType.CALL_RESULT_MESSAGE, string, JsonType] -export type ErrorResponse = [MessageType.CALL_ERROR_MESSAGE, string, ErrorType, string, JsonType]; +export type ErrorResponse = [MessageType.CALL_ERROR_MESSAGE, string, ErrorType, string, JsonType] export type ResponseHandler = ( chargingStation: ChargingStation, payload: JsonType, - requestPayload?: JsonType, -) => void | Promise; + requestPayload?: JsonType +) => void | Promise export type BootNotificationResponse = | OCPP16BootNotificationResponse - | OCPP20BootNotificationResponse; + | OCPP20BootNotificationResponse -export type HeartbeatResponse = OCPP16HeartbeatResponse; +export type HeartbeatResponse = OCPP16HeartbeatResponse -export type ClearCacheResponse = GenericResponse | OCPP20ClearCacheResponse; +export type ClearCacheResponse = GenericResponse | OCPP20ClearCacheResponse -export type StatusNotificationResponse = OCPP16StatusNotificationResponse; +export type StatusNotificationResponse = OCPP16StatusNotificationResponse -export type MeterValuesResponse = OCPP16MeterValuesResponse; +export type MeterValuesResponse = OCPP16MeterValuesResponse -export type DataTransferResponse = OCPP16DataTransferResponse; +export type DataTransferResponse = OCPP16DataTransferResponse -export type DiagnosticsStatusNotificationResponse = OCPP16DiagnosticsStatusNotificationResponse; +export type DiagnosticsStatusNotificationResponse = OCPP16DiagnosticsStatusNotificationResponse -export type FirmwareStatusNotificationResponse = OCPP16FirmwareStatusNotificationResponse; +export type FirmwareStatusNotificationResponse = OCPP16FirmwareStatusNotificationResponse export const AvailabilityStatus = { - ...OCPP16AvailabilityStatus, -} as const; -export type AvailabilityStatus = OCPP16AvailabilityStatus; + ...OCPP16AvailabilityStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type AvailabilityStatus = OCPP16AvailabilityStatus export const ChargingProfileStatus = { - ...OCPP16ChargingProfileStatus, -} as const; -export type ChargingProfileStatus = OCPP16ChargingProfileStatus; + ...OCPP16ChargingProfileStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type ChargingProfileStatus = OCPP16ChargingProfileStatus export const ClearChargingProfileStatus = { - ...OCPP16ClearChargingProfileStatus, -} as const; -export type ClearChargingProfileStatus = OCPP16ClearChargingProfileStatus; + ...OCPP16ClearChargingProfileStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type ClearChargingProfileStatus = OCPP16ClearChargingProfileStatus export const ConfigurationStatus = { - ...OCPP16ConfigurationStatus, -} as const; -export type ConfigurationStatus = OCPP16ConfigurationStatus; + ...OCPP16ConfigurationStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type ConfigurationStatus = OCPP16ConfigurationStatus export const UnlockStatus = { - ...OCPP16UnlockStatus, -} as const; -export type UnlockStatus = OCPP16UnlockStatus; + ...OCPP16UnlockStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type UnlockStatus = OCPP16UnlockStatus export const TriggerMessageStatus = { - ...OCPP16TriggerMessageStatus, -} as const; -export type TriggerMessageStatus = OCPP16TriggerMessageStatus; + ...OCPP16TriggerMessageStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type TriggerMessageStatus = OCPP16TriggerMessageStatus export const DataTransferStatus = { - ...OCPP16DataTransferStatus, -} as const; -export type DataTransferStatus = OCPP16DataTransferStatus; + ...OCPP16DataTransferStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type DataTransferStatus = OCPP16DataTransferStatus -export type ReservationStatus = OCPP16ReservationStatus; +export type ReservationStatus = OCPP16ReservationStatus +// eslint-disable-next-line @typescript-eslint/no-redeclare export const ReservationStatus = { - ...OCPP16ReservationStatus, -} as const; + ...OCPP16ReservationStatus +} as const -export type CancelReservationStatus = GenericStatus; +export type CancelReservationStatus = GenericStatus +// eslint-disable-next-line @typescript-eslint/no-redeclare export const CancelReservationStatus = { - ...GenericStatus, -} as const; + ...GenericStatus +} as const -export type CancelReservationResponse = GenericResponse; +export type CancelReservationResponse = GenericResponse diff --git a/src/types/ocpp/Transaction.ts b/src/types/ocpp/Transaction.ts index 2a01b004..75034efc 100644 --- a/src/types/ocpp/Transaction.ts +++ b/src/types/ocpp/Transaction.ts @@ -6,27 +6,29 @@ import { type OCPP16StartTransactionResponse, OCPP16StopTransactionReason, type OCPP16StopTransactionRequest, - type OCPP16StopTransactionResponse, -} from './1.6/Transaction.js'; + type OCPP16StopTransactionResponse +} from './1.6/Transaction.js' export const AuthorizationStatus = { - ...OCPP16AuthorizationStatus, -} as const; -export type AuthorizationStatus = OCPP16AuthorizationStatus; + ...OCPP16AuthorizationStatus +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type AuthorizationStatus = OCPP16AuthorizationStatus -export type AuthorizeRequest = OCPP16AuthorizeRequest; +export type AuthorizeRequest = OCPP16AuthorizeRequest -export type AuthorizeResponse = OCPP16AuthorizeResponse; +export type AuthorizeResponse = OCPP16AuthorizeResponse export const StopTransactionReason = { - ...OCPP16StopTransactionReason, -} as const; -export type StopTransactionReason = OCPP16StopTransactionReason; + ...OCPP16StopTransactionReason +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type StopTransactionReason = OCPP16StopTransactionReason -export type StartTransactionRequest = OCPP16StartTransactionRequest; +export type StartTransactionRequest = OCPP16StartTransactionRequest -export type StartTransactionResponse = OCPP16StartTransactionResponse; +export type StartTransactionResponse = OCPP16StartTransactionResponse -export type StopTransactionRequest = OCPP16StopTransactionRequest; +export type StopTransactionRequest = OCPP16StopTransactionRequest -export type StopTransactionResponse = OCPP16StopTransactionResponse; +export type StopTransactionResponse = OCPP16StopTransactionResponse diff --git a/src/types/orm/entities/PerformanceData.ts b/src/types/orm/entities/PerformanceData.ts index 3b046a91..7774a96c 100644 --- a/src/types/orm/entities/PerformanceData.ts +++ b/src/types/orm/entities/PerformanceData.ts @@ -1,8 +1,8 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core'; +import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core' // eslint-disable-next-line @typescript-eslint/no-unused-vars -import type { PerformanceRecord } from './PerformanceRecord.js'; +import type { PerformanceRecord } from './PerformanceRecord.js' @Entity() export class PerformanceData { diff --git a/src/types/orm/entities/PerformanceRecord.ts b/src/types/orm/entities/PerformanceRecord.ts index 58bfacad..c752e099 100644 --- a/src/types/orm/entities/PerformanceRecord.ts +++ b/src/types/orm/entities/PerformanceRecord.ts @@ -1,8 +1,8 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'; +import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core' // eslint-disable-next-line @typescript-eslint/no-unused-vars -import type { PerformanceData } from './PerformanceData.js'; +import type { PerformanceData } from './PerformanceData.js' @Entity() export class PerformanceRecord { diff --git a/src/utils/AsyncLock.ts b/src/utils/AsyncLock.ts index 3f89759d..c6b35b84 100644 --- a/src/utils/AsyncLock.ts +++ b/src/utils/AsyncLock.ts @@ -1,62 +1,64 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { Queue } from 'mnemonist'; +import { Queue } from 'mnemonist' -import { Constants } from './Constants.js'; +import { Constants } from './Constants.js' export enum AsyncLockType { configuration = 'configuration', - performance = 'performance', + performance = 'performance' } -type ResolveType = (value: void | PromiseLike) => void; +type ResolveType = (value: void | PromiseLike) => void export class AsyncLock { - private static readonly asyncLocks = new Map(); - private acquired: boolean; - private readonly resolveQueue: Queue; + private static readonly asyncLocks = new Map() + private acquired: boolean + private readonly resolveQueue: Queue - private constructor() { - this.acquired = false; - this.resolveQueue = new Queue(); + private constructor () { + this.acquired = false + this.resolveQueue = new Queue() } public static async runExclusive(type: AsyncLockType, fn: () => T | Promise): Promise { - return AsyncLock.acquire(type) + return await AsyncLock.acquire(type) .then(fn) .finally(() => { - AsyncLock.release(type).catch(Constants.EMPTY_FUNCTION); - }); + AsyncLock.release(type).catch(Constants.EMPTY_FUNCTION) + }) } - private static async acquire(type: AsyncLockType): Promise { - const asyncLock = AsyncLock.getAsyncLock(type); + private static async acquire (type: AsyncLockType): Promise { + const asyncLock = AsyncLock.getAsyncLock(type) if (!asyncLock.acquired) { - asyncLock.acquired = true; - return; + asyncLock.acquired = true + return } - return new Promise((resolve) => { - asyncLock.resolveQueue.enqueue(resolve); - }); + await new Promise((resolve) => { + asyncLock.resolveQueue.enqueue(resolve) + }) } - private static async release(type: AsyncLockType): Promise { - const asyncLock = AsyncLock.getAsyncLock(type); + private static async release (type: AsyncLockType): Promise { + const asyncLock = AsyncLock.getAsyncLock(type) if (asyncLock.resolveQueue.size === 0 && asyncLock.acquired) { - asyncLock.acquired = false; - return; + asyncLock.acquired = false + return } - const queuedResolve = asyncLock.resolveQueue.dequeue()!; - return new Promise((resolve) => { - queuedResolve(); - resolve(); - }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const queuedResolve = asyncLock.resolveQueue.dequeue()! + await new Promise((resolve) => { + queuedResolve() + resolve() + }) } - private static getAsyncLock(type: AsyncLockType): AsyncLock { + private static getAsyncLock (type: AsyncLockType): AsyncLock { if (!AsyncLock.asyncLocks.has(type)) { - AsyncLock.asyncLocks.set(type, new AsyncLock()); + AsyncLock.asyncLocks.set(type, new AsyncLock()) } - return AsyncLock.asyncLocks.get(type)!; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return AsyncLock.asyncLocks.get(type)! } } diff --git a/src/utils/ChargingStationConfigurationUtils.ts b/src/utils/ChargingStationConfigurationUtils.ts index b18922a4..b25aba8c 100644 --- a/src/utils/ChargingStationConfigurationUtils.ts +++ b/src/utils/ChargingStationConfigurationUtils.ts @@ -1,60 +1,60 @@ -import { isNullOrUndefined } from './Utils.js'; -import type { ChargingStation } from '../charging-station/index.js'; +import { isNullOrUndefined } from './Utils.js' +import type { ChargingStation } from '../charging-station/index.js' import type { ChargingStationAutomaticTransactionGeneratorConfiguration, ConnectorStatus, EvseStatusConfiguration, - EvseStatusWorkerType, -} from '../types/index.js'; + EvseStatusWorkerType +} from '../types/index.js' export const buildChargingStationAutomaticTransactionGeneratorConfiguration = ( - chargingStation: ChargingStation, + chargingStation: ChargingStation ): ChargingStationAutomaticTransactionGeneratorConfiguration => { return { automaticTransactionGenerator: chargingStation.getAutomaticTransactionGeneratorConfiguration(), ...(!isNullOrUndefined(chargingStation.automaticTransactionGenerator?.connectorsStatus) && { automaticTransactionGeneratorStatuses: [ - ...chargingStation.automaticTransactionGenerator!.connectorsStatus.values(), - ], - }), - }; -}; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ...chargingStation.automaticTransactionGenerator!.connectorsStatus.values() + ] + }) + } +} export const buildConnectorsStatus = (chargingStation: ChargingStation): ConnectorStatus[] => { return [...chargingStation.connectors.values()].map( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest, - ); -}; + ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest + ) +} export const enum OutputFormat { configuration = 'configuration', - worker = 'worker', + worker = 'worker' } export const buildEvsesStatus = ( chargingStation: ChargingStation, - outputFormat: OutputFormat = OutputFormat.configuration, -): (EvseStatusWorkerType | EvseStatusConfiguration)[] => { + outputFormat: OutputFormat = OutputFormat.configuration +): Array => { + // eslint-disable-next-line array-callback-return return [...chargingStation.evses.values()].map((evseStatus) => { const connectorsStatus = [...evseStatus.connectors.values()].map( - // eslint-disable-next-line @typescript-eslint/no-unused-vars - ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest, - ); - let status: EvseStatusConfiguration; + ({ transactionSetInterval, ...connectorStatusRest }) => connectorStatusRest + ) + let status: EvseStatusConfiguration switch (outputFormat) { case OutputFormat.worker: return { ...evseStatus, - connectors: connectorsStatus, - }; + connectors: connectorsStatus + } case OutputFormat.configuration: status = { ...evseStatus, - connectorsStatus, - }; - delete (status as EvseStatusWorkerType).connectors; - return status; + connectorsStatus + } + delete (status as EvseStatusWorkerType).connectors + return status } - }); -}; + }) +} diff --git a/src/utils/CircularArray.ts b/src/utils/CircularArray.ts index 4fafe528..3a1a89c9 100644 --- a/src/utils/CircularArray.ts +++ b/src/utils/CircularArray.ts @@ -1,96 +1,96 @@ // Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -export const DEFAULT_CIRCULAR_ARRAY_SIZE = 1024; +export const DEFAULT_CIRCULAR_ARRAY_SIZE = 1024 /** * Array with a maximum length and shifting items when full. */ export class CircularArray extends Array { - public size: number; + public size: number - constructor(size: number = DEFAULT_CIRCULAR_ARRAY_SIZE, ...items: T[]) { - super(); - this.checkSize(size); - this.size = size; + constructor (size: number = DEFAULT_CIRCULAR_ARRAY_SIZE, ...items: T[]) { + super() + this.checkSize(size) + this.size = size if (arguments.length > 1) { - this.push(...items); + this.push(...items) } } - public push(...items: T[]): number { - const length = super.push(...items); + public push (...items: T[]): number { + const length = super.push(...items) if (length > this.size) { - super.splice(0, length - this.size); + super.splice(0, length - this.size) } - return this.length; + return this.length } - public unshift(...items: T[]): number { - const length = super.unshift(...items); + public unshift (...items: T[]): number { + const length = super.unshift(...items) if (length > this.size) { - super.splice(this.size, items.length); + super.splice(this.size, items.length) } - return this.length; + return this.length } - public concat(...items: (T | ConcatArray)[]): CircularArray { - const concatenatedCircularArray = super.concat(items as T[]) as CircularArray; - concatenatedCircularArray.size = this.size; + public concat (...items: Array>): CircularArray { + const concatenatedCircularArray = super.concat(items as T[]) as CircularArray + concatenatedCircularArray.size = this.size if (concatenatedCircularArray.length > concatenatedCircularArray.size) { concatenatedCircularArray.splice( 0, - concatenatedCircularArray.length - concatenatedCircularArray.size, - ); + concatenatedCircularArray.length - concatenatedCircularArray.size + ) } - return concatenatedCircularArray; + return concatenatedCircularArray } - public splice(start: number, deleteCount?: number, ...items: T[]): CircularArray { - let itemsRemoved: T[] = []; + public splice (start: number, deleteCount?: number, ...items: T[]): CircularArray { + let itemsRemoved: T[] = [] if (arguments.length >= 3 && deleteCount !== undefined) { - itemsRemoved = super.splice(start, deleteCount, ...items); + itemsRemoved = super.splice(start, deleteCount, ...items) if (this.length > this.size) { - const itemsOverflowing = super.splice(0, this.length - this.size); + const itemsOverflowing = super.splice(0, this.length - this.size) itemsRemoved = new CircularArray( itemsRemoved.length + itemsOverflowing.length, ...itemsRemoved, - ...itemsOverflowing, - ); + ...itemsOverflowing + ) } } else if (arguments.length === 2) { - itemsRemoved = super.splice(start, deleteCount); + itemsRemoved = super.splice(start, deleteCount) } else { - itemsRemoved = super.splice(start); + itemsRemoved = super.splice(start) } - return itemsRemoved as CircularArray; + return itemsRemoved as CircularArray } - public resize(size: number): void { - this.checkSize(size); + public resize (size: number): void { + this.checkSize(size) if (size === 0) { - this.length = 0; + this.length = 0 } else if (size < this.size) { for (let i = size; i < this.size; i++) { - super.pop(); + super.pop() } } - this.size = size; + this.size = size } - public empty(): boolean { - return this.length === 0; + public empty (): boolean { + return this.length === 0 } - public full(): boolean { - return this.length === this.size; + public full (): boolean { + return this.length === this.size } - private checkSize(size: number): void { + private checkSize (size: number): void { if (!Number.isSafeInteger(size)) { - throw new TypeError(`Invalid circular array size: ${size} is not a safe integer`); + throw new TypeError(`Invalid circular array size: ${size} is not a safe integer`) } if (size < 0) { - throw new RangeError(`Invalid circular array size: ${size} < 0`); + throw new RangeError(`Invalid circular array size: ${size} < 0`) } } } diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index 1c2e208f..021bce02 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -1,10 +1,10 @@ -import { type FSWatcher, readFileSync, watch } from 'node:fs'; -import { dirname, join } from 'node:path'; -import { env } from 'node:process'; -import { fileURLToPath } from 'node:url'; +import { type FSWatcher, readFileSync, watch } from 'node:fs' +import { dirname, join } from 'node:path' +import { env } from 'node:process' +import { fileURLToPath } from 'node:url' -import chalk from 'chalk'; -import merge from 'just-merge'; +import chalk from 'chalk' +import merge from 'just-merge' import { buildPerformanceUriFilePath, @@ -12,10 +12,10 @@ import { checkWorkerProcessType, getDefaultPerformanceStorageUri, handleFileException, - logPrefix, -} from './ConfigurationUtils.js'; -import { Constants } from './Constants.js'; -import { hasOwnProp, isCFEnvironment, isUndefined, once } from './Utils.js'; + logPrefix +} from './ConfigurationUtils.js' +import { Constants } from './Constants.js' +import { hasOwnProp, isCFEnvironment, isUndefined, once } from './Utils.js' import { ApplicationProtocol, type ConfigurationData, @@ -27,177 +27,180 @@ import { StorageType, SupervisionUrlDistribution, type UIServerConfiguration, - type WorkerConfiguration, -} from '../types/index.js'; + type WorkerConfiguration +} from '../types/index.js' import { DEFAULT_ELEMENT_START_DELAY, DEFAULT_POOL_MAX_SIZE, DEFAULT_POOL_MIN_SIZE, DEFAULT_WORKER_START_DELAY, - WorkerProcessType, -} from '../worker/index.js'; + WorkerProcessType +} from '../worker/index.js' type ConfigurationSectionType = | LogConfiguration | StorageConfiguration | WorkerConfiguration - | UIServerConfiguration; + | UIServerConfiguration +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class Configuration { - public static configurationChangeCallback: () => Promise; + public static configurationChangeCallback: () => Promise - private static configurationFile = join( + private static readonly configurationFile = join( dirname(fileURLToPath(import.meta.url)), 'assets', - 'config.json', - ); + 'config.json' + ) - private static configurationFileReloading = false; - private static configurationData?: ConfigurationData; - private static configurationFileWatcher?: FSWatcher; - private static configurationSectionCache = new Map< - ConfigurationSection, - ConfigurationSectionType + private static configurationFileReloading = false + private static configurationData?: ConfigurationData + private static configurationFileWatcher?: FSWatcher + private static readonly configurationSectionCache = new Map< + ConfigurationSection, + ConfigurationSectionType >([ [ConfigurationSection.log, Configuration.buildLogSection()], [ConfigurationSection.performanceStorage, Configuration.buildPerformanceStorageSection()], [ConfigurationSection.worker, Configuration.buildWorkerSection()], - [ConfigurationSection.uiServer, Configuration.buildUIServerSection()], - ]); + [ConfigurationSection.uiServer, Configuration.buildUIServerSection()] + ]) - private constructor() { + private constructor () { // This is intentional } public static getConfigurationSection( - sectionName: ConfigurationSection, + sectionName: ConfigurationSection ): T { if (!Configuration.isConfigurationSectionCached(sectionName)) { - Configuration.cacheConfigurationSection(sectionName); + Configuration.cacheConfigurationSection(sectionName) } - return Configuration.configurationSectionCache.get(sectionName) as T; + return Configuration.configurationSectionCache.get(sectionName) as T } - public static getStationTemplateUrls(): StationTemplateUrl[] | undefined { + public static getStationTemplateUrls (): StationTemplateUrl[] | undefined { const checkDeprecatedConfigurationKeysOnce = once( Configuration.checkDeprecatedConfigurationKeys.bind(Configuration), - Configuration, - ); - checkDeprecatedConfigurationKeysOnce(); - return Configuration.getConfigurationData()?.stationTemplateUrls; + Configuration + ) + checkDeprecatedConfigurationKeysOnce() + return Configuration.getConfigurationData()?.stationTemplateUrls } - public static getSupervisionUrls(): string | string[] | undefined { + public static getSupervisionUrls (): string | string[] | undefined { if ( !isUndefined( - Configuration.getConfigurationData()?.['supervisionURLs' as keyof ConfigurationData], + Configuration.getConfigurationData()?.['supervisionURLs' as keyof ConfigurationData] ) ) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion Configuration.getConfigurationData()!.supervisionUrls = Configuration.getConfigurationData()![ 'supervisionURLs' as keyof ConfigurationData - ] as string | string[]; + ] as string | string[] } - return Configuration.getConfigurationData()?.supervisionUrls; + return Configuration.getConfigurationData()?.supervisionUrls } - public static getSupervisionUrlDistribution(): SupervisionUrlDistribution | undefined { + public static getSupervisionUrlDistribution (): SupervisionUrlDistribution | undefined { return hasOwnProp(Configuration.getConfigurationData(), 'supervisionUrlDistribution') ? Configuration.getConfigurationData()?.supervisionUrlDistribution - : SupervisionUrlDistribution.ROUND_ROBIN; + : SupervisionUrlDistribution.ROUND_ROBIN } - public static workerPoolInUse(): boolean { + public static workerPoolInUse (): boolean { return [WorkerProcessType.dynamicPool, WorkerProcessType.fixedPool].includes( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion Configuration.getConfigurationSection(ConfigurationSection.worker) - .processType!, - ); + .processType! + ) } - public static workerDynamicPoolInUse(): boolean { + public static workerDynamicPoolInUse (): boolean { return ( Configuration.getConfigurationSection(ConfigurationSection.worker) .processType === WorkerProcessType.dynamicPool - ); + ) } - private static isConfigurationSectionCached(sectionName: ConfigurationSection): boolean { - return Configuration.configurationSectionCache.has(sectionName); + private static isConfigurationSectionCached (sectionName: ConfigurationSection): boolean { + return Configuration.configurationSectionCache.has(sectionName) } - private static cacheConfigurationSection(sectionName: ConfigurationSection): void { + private static cacheConfigurationSection (sectionName: ConfigurationSection): void { switch (sectionName) { case ConfigurationSection.log: - Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection()); - break; + Configuration.configurationSectionCache.set(sectionName, Configuration.buildLogSection()) + break case ConfigurationSection.performanceStorage: Configuration.configurationSectionCache.set( sectionName, - Configuration.buildPerformanceStorageSection(), - ); - break; + Configuration.buildPerformanceStorageSection() + ) + break case ConfigurationSection.worker: - Configuration.configurationSectionCache.set( - sectionName, - Configuration.buildWorkerSection(), - ); - break; + Configuration.configurationSectionCache.set(sectionName, Configuration.buildWorkerSection()) + break case ConfigurationSection.uiServer: Configuration.configurationSectionCache.set( sectionName, - Configuration.buildUIServerSection(), - ); - break; + Configuration.buildUIServerSection() + ) + break default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new Error(`Unknown configuration section '${sectionName}'`); + throw new Error(`Unknown configuration section '${sectionName}'`) } } - private static buildUIServerSection(): UIServerConfiguration { + private static buildUIServerSection (): UIServerConfiguration { let uiServerConfiguration: UIServerConfiguration = { enabled: false, type: ApplicationProtocol.WS, options: { host: Constants.DEFAULT_UI_SERVER_HOST, - port: Constants.DEFAULT_UI_SERVER_PORT, - }, - }; + port: Constants.DEFAULT_UI_SERVER_PORT + } + } if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.uiServer)) { uiServerConfiguration = merge( uiServerConfiguration, - Configuration.getConfigurationData()!.uiServer!, - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + Configuration.getConfigurationData()!.uiServer! + ) } - if (isCFEnvironment() === true) { - delete uiServerConfiguration.options?.host; - uiServerConfiguration.options!.port = parseInt(env.PORT!); + if (isCFEnvironment()) { + delete uiServerConfiguration.options?.host + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + uiServerConfiguration.options!.port = parseInt(env.PORT!) } - return uiServerConfiguration; + return uiServerConfiguration } - private static buildPerformanceStorageSection(): StorageConfiguration { + private static buildPerformanceStorageSection (): StorageConfiguration { let storageConfiguration: StorageConfiguration = { enabled: false, type: StorageType.JSON_FILE, - uri: getDefaultPerformanceStorageUri(StorageType.JSON_FILE), - }; + uri: getDefaultPerformanceStorageUri(StorageType.JSON_FILE) + } if (hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.performanceStorage)) { storageConfiguration = { ...storageConfiguration, ...Configuration.getConfigurationData()?.performanceStorage, ...(Configuration.getConfigurationData()?.performanceStorage?.type === StorageType.JSON_FILE && - Configuration.getConfigurationData()?.performanceStorage?.uri && { - uri: buildPerformanceUriFilePath( - new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname, - ), - }), - }; + Configuration.getConfigurationData()?.performanceStorage?.uri != null && { + uri: buildPerformanceUriFilePath( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + new URL(Configuration.getConfigurationData()!.performanceStorage!.uri!).pathname + ) + }) + } } - return storageConfiguration; + return storageConfiguration } - private static buildLogSection(): LogConfiguration { + private static buildLogSection (): LogConfiguration { const defaultLogConfiguration: LogConfiguration = { enabled: true, file: 'logs/combined.log', @@ -205,304 +208,307 @@ export class Configuration { statisticsInterval: Constants.DEFAULT_LOG_STATISTICS_INTERVAL, level: 'info', format: 'simple', - rotate: true, - }; + rotate: true + } const deprecatedLogConfiguration: LogConfiguration = { ...(hasOwnProp(Configuration.getConfigurationData(), 'logEnabled') && { - enabled: Configuration.getConfigurationData()?.logEnabled, + enabled: Configuration.getConfigurationData()?.logEnabled }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logFile') && { - file: Configuration.getConfigurationData()?.logFile, + file: Configuration.getConfigurationData()?.logFile }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logErrorFile') && { - errorFile: Configuration.getConfigurationData()?.logErrorFile, + errorFile: Configuration.getConfigurationData()?.logErrorFile }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logStatisticsInterval') && { - statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval, + statisticsInterval: Configuration.getConfigurationData()?.logStatisticsInterval }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logLevel') && { - level: Configuration.getConfigurationData()?.logLevel, + level: Configuration.getConfigurationData()?.logLevel }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logConsole') && { - console: Configuration.getConfigurationData()?.logConsole, + console: Configuration.getConfigurationData()?.logConsole }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logFormat') && { - format: Configuration.getConfigurationData()?.logFormat, + format: Configuration.getConfigurationData()?.logFormat }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logRotate') && { - rotate: Configuration.getConfigurationData()?.logRotate, + rotate: Configuration.getConfigurationData()?.logRotate }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxFiles') && { - maxFiles: Configuration.getConfigurationData()?.logMaxFiles, + maxFiles: Configuration.getConfigurationData()?.logMaxFiles }), ...(hasOwnProp(Configuration.getConfigurationData(), 'logMaxSize') && { - maxSize: Configuration.getConfigurationData()?.logMaxSize, - }), - }; + maxSize: Configuration.getConfigurationData()?.logMaxSize + }) + } const logConfiguration: LogConfiguration = { ...defaultLogConfiguration, ...deprecatedLogConfiguration, ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.log) && - Configuration.getConfigurationData()?.log), - }; - return logConfiguration; + Configuration.getConfigurationData()?.log) + } + return logConfiguration } - private static buildWorkerSection(): WorkerConfiguration { + private static buildWorkerSection (): WorkerConfiguration { const defaultWorkerConfiguration: WorkerConfiguration = { processType: WorkerProcessType.workerSet, startDelay: DEFAULT_WORKER_START_DELAY, elementsPerWorker: 'auto', elementStartDelay: DEFAULT_ELEMENT_START_DELAY, poolMinSize: DEFAULT_POOL_MIN_SIZE, - poolMaxSize: DEFAULT_POOL_MAX_SIZE, - }; + poolMaxSize: DEFAULT_POOL_MAX_SIZE + } const deprecatedWorkerConfiguration: WorkerConfiguration = { ...(hasOwnProp(Configuration.getConfigurationData(), 'workerProcess') && { - processType: Configuration.getConfigurationData()?.workerProcess, + processType: Configuration.getConfigurationData()?.workerProcess }), ...(hasOwnProp(Configuration.getConfigurationData(), 'workerStartDelay') && { - startDelay: Configuration.getConfigurationData()?.workerStartDelay, + startDelay: Configuration.getConfigurationData()?.workerStartDelay }), ...(hasOwnProp(Configuration.getConfigurationData(), 'chargingStationsPerWorker') && { - elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker, + elementsPerWorker: Configuration.getConfigurationData()?.chargingStationsPerWorker }), ...(hasOwnProp(Configuration.getConfigurationData(), 'elementStartDelay') && { - elementStartDelay: Configuration.getConfigurationData()?.elementStartDelay, + elementStartDelay: Configuration.getConfigurationData()?.elementStartDelay }), ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMinSize') && { - poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize, + poolMinSize: Configuration.getConfigurationData()?.workerPoolMinSize }), ...(hasOwnProp(Configuration.getConfigurationData(), 'workerPoolMaxSize') && { - poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize, - }), - }; + poolMaxSize: Configuration.getConfigurationData()?.workerPoolMaxSize + }) + } hasOwnProp(Configuration.getConfigurationData(), 'workerPoolStrategy') && - delete Configuration.getConfigurationData()?.workerPoolStrategy; + delete Configuration.getConfigurationData()?.workerPoolStrategy const workerConfiguration: WorkerConfiguration = { ...defaultWorkerConfiguration, ...deprecatedWorkerConfiguration, ...(hasOwnProp(Configuration.getConfigurationData(), ConfigurationSection.worker) && - Configuration.getConfigurationData()?.worker), - }; - checkWorkerProcessType(workerConfiguration.processType!); - checkWorkerElementsPerWorker(workerConfiguration.elementsPerWorker); - return workerConfiguration; + Configuration.getConfigurationData()?.worker) + } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + checkWorkerProcessType(workerConfiguration.processType!) + checkWorkerElementsPerWorker(workerConfiguration.elementsPerWorker) + return workerConfiguration } - private static checkDeprecatedConfigurationKeys() { + private static checkDeprecatedConfigurationKeys (): void { // connection timeout Configuration.warnDeprecatedConfigurationKey( 'autoReconnectTimeout', undefined, - "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead", - ); + "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead" + ) Configuration.warnDeprecatedConfigurationKey( 'connectionTimeout', undefined, - "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead", - ); + "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead" + ) // connection retries Configuration.warnDeprecatedConfigurationKey( 'autoReconnectMaxRetries', undefined, - 'Use it in charging station template instead', - ); + 'Use it in charging station template instead' + ) // station template url(s) Configuration.warnDeprecatedConfigurationKey( 'stationTemplateURLs', undefined, - "Use 'stationTemplateUrls' instead", - ); + "Use 'stationTemplateUrls' instead" + ) !isUndefined( - Configuration.getConfigurationData()?.['stationTemplateURLs' as keyof ConfigurationData], + Configuration.getConfigurationData()?.['stationTemplateURLs' as keyof ConfigurationData] ) && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion (Configuration.getConfigurationData()!.stationTemplateUrls = + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion Configuration.getConfigurationData()![ 'stationTemplateURLs' as keyof ConfigurationData - ] as StationTemplateUrl[]); + ] as StationTemplateUrl[]) Configuration.getConfigurationData()?.stationTemplateUrls.forEach( (stationTemplateUrl: StationTemplateUrl) => { if (!isUndefined(stationTemplateUrl?.['numberOfStation' as keyof StationTemplateUrl])) { console.error( `${chalk.green(logPrefix())} ${chalk.red( - `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead`, - )}`, - ); + `Deprecated configuration key 'numberOfStation' usage for template file '${stationTemplateUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead` + )}` + ) } - }, - ); + } + ) // supervision url(s) Configuration.warnDeprecatedConfigurationKey( 'supervisionURLs', undefined, - "Use 'supervisionUrls' instead", - ); + "Use 'supervisionUrls' instead" + ) // supervision urls distribution Configuration.warnDeprecatedConfigurationKey( 'distributeStationToTenantEqually', undefined, - "Use 'supervisionUrlDistribution' instead", - ); + "Use 'supervisionUrlDistribution' instead" + ) Configuration.warnDeprecatedConfigurationKey( 'distributeStationsToTenantsEqually', undefined, - "Use 'supervisionUrlDistribution' instead", - ); + "Use 'supervisionUrlDistribution' instead" + ) // worker section Configuration.warnDeprecatedConfigurationKey( 'useWorkerPool', undefined, - `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead` + ) Configuration.warnDeprecatedConfigurationKey( 'workerProcess', undefined, - `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the type of worker process model instead` + ) Configuration.warnDeprecatedConfigurationKey( 'workerStartDelay', undefined, - `Use '${ConfigurationSection.worker}' section to define the worker start delay instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the worker start delay instead` + ) Configuration.warnDeprecatedConfigurationKey( 'chargingStationsPerWorker', undefined, - `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the number of element(s) per worker instead` + ) Configuration.warnDeprecatedConfigurationKey( 'elementStartDelay', undefined, - `Use '${ConfigurationSection.worker}' section to define the worker's element start delay instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the worker's element start delay instead` + ) Configuration.warnDeprecatedConfigurationKey( 'workerPoolMinSize', undefined, - `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the worker pool minimum size instead` + ) Configuration.warnDeprecatedConfigurationKey( 'workerPoolSize', undefined, - `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead` + ) Configuration.warnDeprecatedConfigurationKey( 'workerPoolMaxSize', undefined, - `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the worker pool maximum size instead` + ) Configuration.warnDeprecatedConfigurationKey( 'workerPoolStrategy', undefined, - `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead`, - ); + `Use '${ConfigurationSection.worker}' section to define the worker pool strategy instead` + ) Configuration.warnDeprecatedConfigurationKey( 'poolStrategy', ConfigurationSection.worker, - 'Not publicly exposed to end users', - ); + 'Not publicly exposed to end users' + ) if ( Configuration.getConfigurationData()?.worker?.processType === ('staticPool' as WorkerProcessType) ) { console.error( `${chalk.green(logPrefix())} ${chalk.red( - `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead`, - )}`, - ); + `Deprecated configuration 'staticPool' value usage in worker section 'processType' field. Use '${WorkerProcessType.fixedPool}' value instead` + )}` + ) } // log section Configuration.warnDeprecatedConfigurationKey( 'logEnabled', undefined, - `Use '${ConfigurationSection.log}' section to define the logging enablement instead`, - ); + `Use '${ConfigurationSection.log}' section to define the logging enablement instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logFile', undefined, - `Use '${ConfigurationSection.log}' section to define the log file instead`, - ); + `Use '${ConfigurationSection.log}' section to define the log file instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logErrorFile', undefined, - `Use '${ConfigurationSection.log}' section to define the log error file instead`, - ); + `Use '${ConfigurationSection.log}' section to define the log error file instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logConsole', undefined, - `Use '${ConfigurationSection.log}' section to define the console logging enablement instead`, - ); + `Use '${ConfigurationSection.log}' section to define the console logging enablement instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logStatisticsInterval', undefined, - `Use '${ConfigurationSection.log}' section to define the log statistics interval instead`, - ); + `Use '${ConfigurationSection.log}' section to define the log statistics interval instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logLevel', undefined, - `Use '${ConfigurationSection.log}' section to define the log level instead`, - ); + `Use '${ConfigurationSection.log}' section to define the log level instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logFormat', undefined, - `Use '${ConfigurationSection.log}' section to define the log format instead`, - ); + `Use '${ConfigurationSection.log}' section to define the log format instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logRotate', undefined, - `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead`, - ); + `Use '${ConfigurationSection.log}' section to define the log rotation enablement instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logMaxFiles', undefined, - `Use '${ConfigurationSection.log}' section to define the log maximum files instead`, - ); + `Use '${ConfigurationSection.log}' section to define the log maximum files instead` + ) Configuration.warnDeprecatedConfigurationKey( 'logMaxSize', undefined, - `Use '${ConfigurationSection.log}' section to define the log maximum size instead`, - ); + `Use '${ConfigurationSection.log}' section to define the log maximum size instead` + ) // performanceStorage section Configuration.warnDeprecatedConfigurationKey( 'URI', ConfigurationSection.performanceStorage, - "Use 'uri' instead", - ); + "Use 'uri' instead" + ) // uiServer section if (hasOwnProp(Configuration.getConfigurationData(), 'uiWebSocketServer')) { console.error( `${chalk.green(logPrefix())} ${chalk.red( - `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead`, - )}`, - ); + `Deprecated configuration section 'uiWebSocketServer' usage. Use '${ConfigurationSection.uiServer}' instead` + )}` + ) } } - private static warnDeprecatedConfigurationKey( + private static warnDeprecatedConfigurationKey ( key: string, sectionName?: string, - logMsgToAppend = '', - ) { + logMsgToAppend = '' + ): void { if ( - sectionName && + sectionName != null && !isUndefined( - Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData], + Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData] ) && !isUndefined( ( Configuration.getConfigurationData()?.[sectionName as keyof ConfigurationData] as Record< - string, - unknown + string, + unknown > - )?.[key], + )?.[key] ) ) { console.error( `${chalk.green(logPrefix())} ${chalk.red( `Deprecated configuration key '${key}' usage in section '${sectionName}'${ logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : '' - }`, - )}`, - ); + }` + )}` + ) } else if ( !isUndefined(Configuration.getConfigurationData()?.[key as keyof ConfigurationData]) ) { @@ -510,70 +516,71 @@ export class Configuration { `${chalk.green(logPrefix())} ${chalk.red( `Deprecated configuration key '${key}' usage${ logMsgToAppend.trim().length > 0 ? `. ${logMsgToAppend}` : '' - }`, - )}`, - ); + }` + )}` + ) } } - private static getConfigurationData(): ConfigurationData | undefined { - if (!Configuration.configurationData) { + private static getConfigurationData (): ConfigurationData | undefined { + if (Configuration.configurationData == null) { try { Configuration.configurationData = JSON.parse( - readFileSync(Configuration.configurationFile, 'utf8'), - ) as ConfigurationData; - if (!Configuration.configurationFileWatcher) { - Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher(); + readFileSync(Configuration.configurationFile, 'utf8') + ) as ConfigurationData + if (Configuration.configurationFileWatcher == null) { + Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher() } } catch (error) { handleFileException( Configuration.configurationFile, FileType.Configuration, error as NodeJS.ErrnoException, - logPrefix(), - ); + logPrefix() + ) } } - return Configuration.configurationData; + return Configuration.configurationData } - private static getConfigurationFileWatcher(): FSWatcher | undefined { + private static getConfigurationFileWatcher (): FSWatcher | undefined { try { return watch(Configuration.configurationFile, (event, filename): void => { if ( !Configuration.configurationFileReloading && + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion filename!.trim()!.length > 0 && event === 'change' ) { - Configuration.configurationFileReloading = true; - const consoleWarnOnce = once(console.warn, this); + Configuration.configurationFileReloading = true + const consoleWarnOnce = once(console.warn, this) consoleWarnOnce( `${chalk.green(logPrefix())} ${chalk.yellow( - `${FileType.Configuration} ${this.configurationFile} file have changed, reload`, - )}`, - ); - delete Configuration.configurationData; - Configuration.configurationSectionCache.clear(); + `${FileType.Configuration} ${this.configurationFile} file have changed, reload` + )}` + ) + delete Configuration.configurationData + Configuration.configurationSectionCache.clear() if (!isUndefined(Configuration.configurationChangeCallback)) { Configuration.configurationChangeCallback() .catch((error) => { - throw typeof error === 'string' ? new Error(error) : error; + throw typeof error === 'string' ? new Error(error) : error }) .finally(() => { - Configuration.configurationFileReloading = false; - }); + Configuration.configurationFileReloading = false + }) } else { - Configuration.configurationFileReloading = false; + Configuration.configurationFileReloading = false } } - }); + }) } catch (error) { handleFileException( Configuration.configurationFile, FileType.Configuration, error as NodeJS.ErrnoException, - logPrefix(), - ); + logPrefix() + ) } } } diff --git a/src/utils/ConfigurationUtils.ts b/src/utils/ConfigurationUtils.ts index 4e03513c..925e7c13 100644 --- a/src/utils/ConfigurationUtils.ts +++ b/src/utils/ConfigurationUtils.ts @@ -1,74 +1,74 @@ -import { dirname, join, resolve } from 'node:path'; -import { fileURLToPath } from 'node:url'; +import { dirname, join, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' -import chalk from 'chalk'; +import chalk from 'chalk' -import { Constants } from './Constants.js'; -import { isNotEmptyString, logPrefix as utilsLogPrefix } from './Utils.js'; -import { type ElementsPerWorkerType, FileType, StorageType } from '../types/index.js'; -import { WorkerProcessType } from '../worker/index.js'; +import { Constants } from './Constants.js' +import { isNotEmptyString, logPrefix as utilsLogPrefix } from './Utils.js' +import { type ElementsPerWorkerType, type FileType, StorageType } from '../types/index.js' +import { WorkerProcessType } from '../worker/index.js' export const logPrefix = (): string => { - return utilsLogPrefix(' Simulator configuration |'); -}; + return utilsLogPrefix(' Simulator configuration |') +} -export const buildPerformanceUriFilePath = (file: string) => { - return `file://${join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), file)}`; -}; +export const buildPerformanceUriFilePath = (file: string): string => { + return `file://${join(resolve(dirname(fileURLToPath(import.meta.url)), '../'), file)}` +} -export const getDefaultPerformanceStorageUri = (storageType: StorageType) => { +export const getDefaultPerformanceStorageUri = (storageType: StorageType): string => { switch (storageType) { case StorageType.JSON_FILE: return buildPerformanceUriFilePath( - `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME}`, - ); + `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME}` + ) case StorageType.SQLITE: return buildPerformanceUriFilePath( - `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`, - ); + `${Constants.DEFAULT_PERFORMANCE_DIRECTORY}/${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db` + ) default: - throw new Error(`Unsupported storage type '${storageType}'`); + throw new Error(`Unsupported storage type '${storageType}'`) } -}; +} export const handleFileException = ( file: string, fileType: FileType, error: NodeJS.ErrnoException, - logPfx: string, + logPfx: string ): void => { - const prefix = isNotEmptyString(logPfx) ? `${logPfx} ` : ''; - let logMsg: string; + const prefix = isNotEmptyString(logPfx) ? `${logPfx} ` : '' + let logMsg: string switch (error.code) { case 'ENOENT': - logMsg = `${fileType} file ${file} not found: `; - break; + logMsg = `${fileType} file ${file} not found: ` + break case 'EEXIST': - logMsg = `${fileType} file ${file} already exists: `; - break; + logMsg = `${fileType} file ${file} already exists: ` + break case 'EACCES': - logMsg = `${fileType} file ${file} access denied: `; - break; + logMsg = `${fileType} file ${file} access denied: ` + break case 'EPERM': - logMsg = `${fileType} file ${file} permission denied: `; - break; + logMsg = `${fileType} file ${file} permission denied: ` + break default: - logMsg = `${fileType} file ${file} error: `; + logMsg = `${fileType} file ${file} error: ` } - console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error); - throw error; -}; + console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error) + throw error +} export const checkWorkerProcessType = (workerProcessType: WorkerProcessType): void => { if (!Object.values(WorkerProcessType).includes(workerProcessType)) { throw new SyntaxError( - `Invalid worker process type '${workerProcessType}' defined in configuration`, - ); + `Invalid worker process type '${workerProcessType}' defined in configuration` + ) } -}; +} export const checkWorkerElementsPerWorker = ( - elementsPerWorker: ElementsPerWorkerType | undefined, + elementsPerWorker: ElementsPerWorkerType | undefined ): void => { if ( elementsPerWorker !== undefined && @@ -77,12 +77,12 @@ export const checkWorkerElementsPerWorker = ( !Number.isSafeInteger(elementsPerWorker) ) { throw new SyntaxError( - `Invalid number of elements per worker '${elementsPerWorker}' defined in configuration`, - ); + `Invalid number of elements per worker '${elementsPerWorker}' defined in configuration` + ) } if (Number.isSafeInteger(elementsPerWorker) && (elementsPerWorker as number) <= 0) { throw RangeError( - `Invalid negative or zero number of elements per worker '${elementsPerWorker}' defined in configuration`, - ); + `Invalid negative or zero number of elements per worker '${elementsPerWorker}' defined in configuration` + ) } -}; +} diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index 4dc07366..75a17c11 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -3,9 +3,10 @@ import { type ChargingStationInfo, CurrentType, OCPPVersion, - VendorParametersKey, -} from '../types/index.js'; + VendorParametersKey +} from '../types/index.js' +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class Constants { static readonly DEFAULT_STATION_INFO: Partial = Object.freeze({ enableStatistics: false, @@ -28,16 +29,16 @@ export class Constants { autoReconnectMaxRetries: -1, registrationMaxRetries: -1, reconnectExponentialDelay: false, - stopTransactionsOnStopped: true, - }); + stopTransactionsOnStopped: true + }) - static readonly DEFAULT_BOOT_NOTIFICATION_INTERVAL = 60000; // Ms - static readonly DEFAULT_HEARTBEAT_INTERVAL = 60000; // Ms - static readonly DEFAULT_METER_VALUES_INTERVAL = 60000; // Ms + static readonly DEFAULT_BOOT_NOTIFICATION_INTERVAL = 60000 // Ms + static readonly DEFAULT_HEARTBEAT_INTERVAL = 60000 // Ms + static readonly DEFAULT_METER_VALUES_INTERVAL = 60000 // Ms - static readonly CHARGING_STATION_DEFAULT_RESET_TIME = 60000; // Ms - static readonly CHARGING_STATION_ATG_AVAILABILITY_TIME = 1000; // Ms - static readonly CHARGING_STATION_ATG_INITIALIZATION_TIME = 1000; // Ms + static readonly CHARGING_STATION_DEFAULT_RESET_TIME = 60000 // Ms + static readonly CHARGING_STATION_ATG_AVAILABILITY_TIME = 1000 // Ms + static readonly CHARGING_STATION_ATG_INITIALIZATION_TIME = 1000 // Ms static readonly DEFAULT_ATG_CONFIGURATION: AutomaticTransactionGeneratorConfiguration = Object.freeze({ @@ -48,47 +49,47 @@ export class Constants { maxDelayBetweenTwoTransactions: 30, probabilityOfStart: 1, stopAfterHours: 0.25, - stopOnConnectionFailure: true, - }); + stopOnConnectionFailure: true + }) // See https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string static readonly SEMVER_PATTERN = - '^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$'; + '^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$' - static readonly DEFAULT_CIRCULAR_BUFFER_CAPACITY = 4096; + static readonly DEFAULT_CIRCULAR_BUFFER_CAPACITY = 4096 - static readonly DEFAULT_HASH_ALGORITHM = 'sha384'; + static readonly DEFAULT_HASH_ALGORITHM = 'sha384' - static readonly DEFAULT_IDTAG = '00000000'; + static readonly DEFAULT_IDTAG = '00000000' - static readonly DEFAULT_CONNECTION_TIMEOUT = 30; + static readonly DEFAULT_CONNECTION_TIMEOUT = 30 - static readonly DEFAULT_LOG_STATISTICS_INTERVAL = 60; // Seconds + static readonly DEFAULT_LOG_STATISTICS_INTERVAL = 60 // Seconds - static readonly DEFAULT_FLUCTUATION_PERCENT = 5; + static readonly DEFAULT_FLUCTUATION_PERCENT = 5 - static readonly DEFAULT_PERFORMANCE_DIRECTORY = 'performance'; - static readonly DEFAULT_PERFORMANCE_RECORDS_FILENAME = 'performanceRecords.json'; - static readonly DEFAULT_PERFORMANCE_RECORDS_DB_NAME = 'e-mobility-charging-stations-simulator'; - static readonly PERFORMANCE_RECORDS_TABLE = 'performance_records'; + static readonly DEFAULT_PERFORMANCE_DIRECTORY = 'performance' + static readonly DEFAULT_PERFORMANCE_RECORDS_FILENAME = 'performanceRecords.json' + static readonly DEFAULT_PERFORMANCE_RECORDS_DB_NAME = 'e-mobility-charging-stations-simulator' + static readonly PERFORMANCE_RECORDS_TABLE = 'performance_records' - static readonly DEFAULT_UI_SERVER_HOST = 'localhost'; - static readonly DEFAULT_UI_SERVER_PORT = 8080; + static readonly DEFAULT_UI_SERVER_HOST = 'localhost' + static readonly DEFAULT_UI_SERVER_PORT = 8080 - static readonly UNKNOWN_COMMAND = 'unknown command'; + static readonly UNKNOWN_COMMAND = 'unknown command' - static readonly MAX_RANDOM_INTEGER = 281474976710654; + static readonly MAX_RANDOM_INTEGER = 281474976710654 - static readonly STOP_CHARGING_STATIONS_TIMEOUT = 120000; // Ms + static readonly STOP_CHARGING_STATIONS_TIMEOUT = 120000 // Ms - static readonly EMPTY_FROZEN_OBJECT = Object.freeze({}); + static readonly EMPTY_FROZEN_OBJECT = Object.freeze({}) static readonly EMPTY_FUNCTION = Object.freeze(() => { /* This is intentional */ - }); + }) - static readonly DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL = 60000; // Ms + static readonly DEFAULT_MESSAGE_BUFFER_FLUSH_INTERVAL = 60000 // Ms - private constructor() { + private constructor () { // This is intentional } } diff --git a/src/utils/ElectricUtils.ts b/src/utils/ElectricUtils.ts index b0f8dff4..47c74d99 100644 --- a/src/utils/ElectricUtils.ts +++ b/src/utils/ElectricUtils.ts @@ -7,62 +7,64 @@ /** * Targeted to AC related values calculation. */ +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class ACElectricUtils { - private constructor() { + private constructor () { // This is intentional } - static powerTotal(nbOfPhases: number, V: number, Iph: number, cosPhi = 1): number { - return nbOfPhases * ACElectricUtils.powerPerPhase(V, Iph, cosPhi); + static powerTotal (nbOfPhases: number, V: number, Iph: number, cosPhi = 1): number { + return nbOfPhases * ACElectricUtils.powerPerPhase(V, Iph, cosPhi) } - static powerPerPhase(V: number, Iph: number, cosPhi = 1): number { - const powerPerPhase = V * Iph * cosPhi; + static powerPerPhase (V: number, Iph: number, cosPhi = 1): number { + const powerPerPhase = V * Iph * cosPhi if (cosPhi === 1) { - return powerPerPhase; + return powerPerPhase } - return Math.round(powerPerPhase); + return Math.round(powerPerPhase) } - static amperageTotal(nbOfPhases: number, Iph: number): number { - return nbOfPhases * Iph; + static amperageTotal (nbOfPhases: number, Iph: number): number { + return nbOfPhases * Iph } - static amperageTotalFromPower(P: number, V: number, cosPhi = 1): number { - const amperage = P / (V * cosPhi); + static amperageTotalFromPower (P: number, V: number, cosPhi = 1): number { + const amperage = P / (V * cosPhi) if (cosPhi === 1 && P % V === 0) { - return amperage; + return amperage } - return Math.round(amperage); + return Math.round(amperage) } - static amperagePerPhaseFromPower(nbOfPhases: number, P: number, V: number, cosPhi = 1): number { - const amperage = ACElectricUtils.amperageTotalFromPower(P, V, cosPhi); - const amperagePerPhase = amperage / nbOfPhases; + static amperagePerPhaseFromPower (nbOfPhases: number, P: number, V: number, cosPhi = 1): number { + const amperage = ACElectricUtils.amperageTotalFromPower(P, V, cosPhi) + const amperagePerPhase = amperage / nbOfPhases if (amperage % nbOfPhases === 0) { - return amperagePerPhase; + return amperagePerPhase } - return Math.round(amperagePerPhase); + return Math.round(amperagePerPhase) } } /** * Targeted to DC related values calculation. */ +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class DCElectricUtils { - private constructor() { + private constructor () { // This is intentional } - static power(V: number, I: number): number { - return V * I; + static power (V: number, I: number): number { + return V * I } - static amperage(P: number, V: number): number { - const amperage = P / V; + static amperage (P: number, V: number): number { + const amperage = P / V if (P % V === 0) { - return amperage; + return amperage } - return Math.round(amperage); + return Math.round(amperage) } } diff --git a/src/utils/ErrorUtils.ts b/src/utils/ErrorUtils.ts index e046f1c8..1fd0482b 100644 --- a/src/utils/ErrorUtils.ts +++ b/src/utils/ErrorUtils.ts @@ -1,98 +1,98 @@ -import process from 'node:process'; +import process from 'node:process' -import chalk from 'chalk'; +import chalk from 'chalk' -import { logger } from './Logger.js'; -import { isNotEmptyString } from './Utils.js'; -import type { ChargingStation } from '../charging-station/index.js'; +import { logger } from './Logger.js' +import { isNotEmptyString } from './Utils.js' +import type { ChargingStation } from '../charging-station/index.js' import type { EmptyObject, FileType, HandleErrorParams, IncomingRequestCommand, JsonType, - RequestCommand, -} from '../types/index.js'; + RequestCommand +} from '../types/index.js' const defaultErrorParams = { throwError: true, - consoleOut: false, -}; + consoleOut: false +} export const handleUncaughtException = (): void => { process.on('uncaughtException', (error: Error) => { - console.error(chalk.red('Uncaught exception: '), error); - }); -}; + console.error(chalk.red('Uncaught exception: '), error) + }) +} export const handleUnhandledRejection = (): void => { process.on('unhandledRejection', (reason: unknown) => { - console.error(chalk.red('Unhandled rejection: '), reason); - }); -}; + console.error(chalk.red('Unhandled rejection: '), reason) + }) +} export const handleFileException = ( file: string, fileType: FileType, error: NodeJS.ErrnoException, logPrefix: string, - params: HandleErrorParams = defaultErrorParams, + params: HandleErrorParams = defaultErrorParams ): void => { - setDefaultErrorParams(params); - const prefix = isNotEmptyString(logPrefix) ? `${logPrefix} ` : ''; - let logMsg: string; + setDefaultErrorParams(params) + const prefix = isNotEmptyString(logPrefix) ? `${logPrefix} ` : '' + let logMsg: string switch (error.code) { case 'ENOENT': - logMsg = `${fileType} file ${file} not found:`; - break; + logMsg = `${fileType} file ${file} not found:` + break case 'EEXIST': - logMsg = `${fileType} file ${file} already exists:`; - break; + logMsg = `${fileType} file ${file} already exists:` + break case 'EACCES': - logMsg = `${fileType} file ${file} access denied:`; - break; + logMsg = `${fileType} file ${file} access denied:` + break case 'EPERM': - logMsg = `${fileType} file ${file} permission denied:`; - break; + logMsg = `${fileType} file ${file} permission denied:` + break default: - logMsg = `${fileType} file ${file} error:`; + logMsg = `${fileType} file ${file} error:` } if (params?.consoleOut === true) { - logMsg = `${logMsg} `; - if (params?.throwError) { - console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error); + logMsg = `${logMsg} ` + if (params?.throwError === true) { + console.error(`${chalk.green(prefix)}${chalk.red(logMsg)}`, error) } else { - console.warn(`${chalk.green(prefix)}${chalk.yellow(logMsg)}`, error); + console.warn(`${chalk.green(prefix)}${chalk.yellow(logMsg)}`, error) } } else if (params?.consoleOut === false) { - if (params?.throwError) { - logger.error(`${prefix}${logMsg}`, error); + if (params?.throwError === true) { + logger.error(`${prefix}${logMsg}`, error) } else { - logger.warn(`${prefix}${logMsg}`, error); + logger.warn(`${prefix}${logMsg}`, error) } } - if (params?.throwError) { - throw error; + if (params?.throwError === true) { + throw error } -}; +} export const handleSendMessageError = ( chargingStation: ChargingStation, commandName: RequestCommand | IncomingRequestCommand, error: Error, - params: HandleErrorParams = { throwError: false, consoleOut: false }, + params: HandleErrorParams = { throwError: false, consoleOut: false } ): void => { - setDefaultErrorParams(params, { throwError: false, consoleOut: false }); - logger.error(`${chargingStation.logPrefix()} Request command '${commandName}' error:`, error); + setDefaultErrorParams(params, { throwError: false, consoleOut: false }) + logger.error(`${chargingStation.logPrefix()} Request command '${commandName}' error:`, error) if (params?.throwError === true) { - throw error; + throw error } -}; +} export const setDefaultErrorParams = ( params: HandleErrorParams, - defaultParams: HandleErrorParams = defaultErrorParams, + defaultParams: HandleErrorParams = defaultErrorParams ): HandleErrorParams => { - params = { ...defaultParams, ...params }; - return params; -}; + params = { ...defaultParams, ...params } + return params +} diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index 7235011c..bc8dd07c 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -1,9 +1,9 @@ -import { type FSWatcher, type WatchListener, readFileSync, watch } from 'node:fs'; +import { type FSWatcher, type WatchListener, readFileSync, watch } from 'node:fs' -import { handleFileException } from './ErrorUtils.js'; -import { logger } from './Logger.js'; -import { isNotEmptyString } from './Utils.js'; -import type { FileType, JsonType } from '../types/index.js'; +import { handleFileException } from './ErrorUtils.js' +import { logger } from './Logger.js' +import { isNotEmptyString } from './Utils.js' +import type { FileType, JsonType } from '../types/index.js' export const watchJsonFile = ( file: string, @@ -13,25 +13,26 @@ export const watchJsonFile = ( listener: WatchListener = (event, filename) => { if (isNotEmptyString(filename) && event === 'change') { try { - logger.debug(`${logPrefix} ${fileType} file ${file} have changed, reload`); - refreshedVariable && (refreshedVariable = JSON.parse(readFileSync(file, 'utf8')) as T); + logger.debug(`${logPrefix} ${fileType} file ${file} have changed, reload`) + refreshedVariable != null && + (refreshedVariable = JSON.parse(readFileSync(file, 'utf8')) as T) } catch (error) { handleFileException(file, fileType, error as NodeJS.ErrnoException, logPrefix, { - throwError: false, - }); + throwError: false + }) } } - }, + } ): FSWatcher | undefined => { if (isNotEmptyString(file)) { try { - return watch(file, listener); + return watch(file, listener) } catch (error) { handleFileException(file, fileType, error as NodeJS.ErrnoException, logPrefix, { - throwError: false, - }); + throwError: false + }) } } else { - logger.info(`${logPrefix} No ${fileType} file to watch given. Not monitoring its changes`); + logger.info(`${logPrefix} No ${fileType} file to watch given. Not monitoring its changes`) } -}; +} diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index ba5fe9eb..7d80e1ae 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -1,69 +1,74 @@ -import type { FormatWrap } from 'logform'; -import { createLogger, format, type transport } from 'winston'; -import TransportType from 'winston/lib/winston/transports/index.js'; -import DailyRotateFile from 'winston-daily-rotate-file'; +import type { FormatWrap } from 'logform' +import { createLogger, format, type transport } from 'winston' +import TransportType from 'winston/lib/winston/transports/index.js' +import DailyRotateFile from 'winston-daily-rotate-file' -import { Configuration } from './Configuration.js'; -import { insertAt } from './Utils.js'; -import { ConfigurationSection, type LogConfiguration } from '../types/index.js'; +import { Configuration } from './Configuration.js' +import { insertAt } from './Utils.js' +import { ConfigurationSection, type LogConfiguration } from '../types/index.js' const logConfiguration = Configuration.getConfigurationSection( - ConfigurationSection.log, -); -let transports: transport[]; + ConfigurationSection.log +) +let transports: transport[] if (logConfiguration.rotate === true) { - const logMaxFiles = logConfiguration.maxFiles; - const logMaxSize = logConfiguration.maxSize; + const logMaxFiles = logConfiguration.maxFiles + const logMaxSize = logConfiguration.maxSize transports = [ new DailyRotateFile({ filename: insertAt( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion logConfiguration.errorFile!, '-%DATE%', - logConfiguration.errorFile!.indexOf('.log'), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + logConfiguration.errorFile!.indexOf('.log') ), level: 'error', - ...(logMaxFiles && { maxFiles: logMaxFiles }), - ...(logMaxSize && { maxSize: logMaxSize }), + ...(logMaxFiles != null && { maxFiles: logMaxFiles }), + ...(logMaxSize != null && { maxSize: logMaxSize }) }), new DailyRotateFile({ + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion filename: insertAt(logConfiguration.file!, '-%DATE%', logConfiguration.file!.indexOf('.log')), - ...(logMaxFiles && { maxFiles: logMaxFiles }), - ...(logMaxSize && { maxSize: logMaxSize }), - }), - ]; + ...(logMaxFiles != null && { maxFiles: logMaxFiles }), + ...(logMaxSize != null && { maxSize: logMaxSize }) + }) + ] } else { transports = [ new TransportType.File({ filename: logConfiguration.errorFile, - level: 'error', + level: 'error' }), new TransportType.File({ - filename: logConfiguration.file, - }), - ]; + filename: logConfiguration.file + }) + ] } export const logger = createLogger({ - silent: !logConfiguration.enabled, + silent: logConfiguration.enabled === false, level: logConfiguration.level, format: format.combine( format.splat(), - (format[logConfiguration.format! as keyof FormatWrap] as FormatWrap)(), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (format[logConfiguration.format! as keyof FormatWrap] as FormatWrap)() ), - transports, -}); + transports +}) // // If enabled, log to the `console` with the format: // `${info.level}: ${info.message} JSON.stringify({ ...rest }) ` // -if (logConfiguration.console) { +if (logConfiguration.console === true) { logger.add( new TransportType.Console({ format: format.combine( format.splat(), - (format[logConfiguration.format! as keyof FormatWrap] as FormatWrap)(), - ), - }), - ); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (format[logConfiguration.format! as keyof FormatWrap] as FormatWrap)() + ) + }) + ) } diff --git a/src/utils/MessageChannelUtils.ts b/src/utils/MessageChannelUtils.ts index 85d0c483..842b8bfe 100644 --- a/src/utils/MessageChannelUtils.ts +++ b/src/utils/MessageChannelUtils.ts @@ -2,51 +2,51 @@ import { OutputFormat, buildChargingStationAutomaticTransactionGeneratorConfiguration, buildConnectorsStatus, - buildEvsesStatus, -} from './ChargingStationConfigurationUtils.js'; -import type { ChargingStation } from '../charging-station/index.js'; + buildEvsesStatus +} from './ChargingStationConfigurationUtils.js' +import type { ChargingStation } from '../charging-station/index.js' import { type ChargingStationData, type ChargingStationWorkerMessage, ChargingStationWorkerMessageEvents, - type Statistics, -} from '../types/index.js'; + type Statistics +} from '../types/index.js' export const buildStartedMessage = ( - chargingStation: ChargingStation, + chargingStation: ChargingStation ): ChargingStationWorkerMessage => { return { event: ChargingStationWorkerMessageEvents.started, - data: buildChargingStationDataPayload(chargingStation), - }; -}; + data: buildChargingStationDataPayload(chargingStation) + } +} export const buildStoppedMessage = ( - chargingStation: ChargingStation, + chargingStation: ChargingStation ): ChargingStationWorkerMessage => { return { event: ChargingStationWorkerMessageEvents.stopped, - data: buildChargingStationDataPayload(chargingStation), - }; -}; + data: buildChargingStationDataPayload(chargingStation) + } +} export const buildUpdatedMessage = ( - chargingStation: ChargingStation, + chargingStation: ChargingStation ): ChargingStationWorkerMessage => { return { event: ChargingStationWorkerMessageEvents.updated, - data: buildChargingStationDataPayload(chargingStation), - }; -}; + data: buildChargingStationDataPayload(chargingStation) + } +} export const buildPerformanceStatisticsMessage = ( - statistics: Statistics, + statistics: Statistics ): ChargingStationWorkerMessage => { return { event: ChargingStationWorkerMessageEvents.performanceStatistics, - data: statistics, - }; -}; + data: statistics + } +} const buildChargingStationDataPayload = (chargingStation: ChargingStation): ChargingStationData => { return { @@ -54,12 +54,13 @@ const buildChargingStationDataPayload = (chargingStation: ChargingStation): Char stationInfo: chargingStation.stationInfo, connectors: buildConnectorsStatus(chargingStation), evses: buildEvsesStatus(chargingStation, OutputFormat.worker), + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ocppConfiguration: chargingStation.ocppConfiguration!, wsState: chargingStation?.wsConnection?.readyState, bootNotificationResponse: chargingStation.bootNotificationResponse, - ...(chargingStation.automaticTransactionGenerator && { + ...(chargingStation.automaticTransactionGenerator != null && { automaticTransactionGenerator: - buildChargingStationAutomaticTransactionGeneratorConfiguration(chargingStation), - }), - }; -}; + buildChargingStationAutomaticTransactionGeneratorConfiguration(chargingStation) + }) + } +} diff --git a/src/utils/StatisticUtils.ts b/src/utils/StatisticUtils.ts index 7d2412ee..bccebbe0 100644 --- a/src/utils/StatisticUtils.ts +++ b/src/utils/StatisticUtils.ts @@ -1,4 +1,4 @@ -import { isEmptyArray, isNullOrUndefined } from './Utils.js'; +import { isEmptyArray, isNullOrUndefined } from './Utils.js' /** * Computes the average of the given data set. @@ -9,13 +9,13 @@ import { isEmptyArray, isNullOrUndefined } from './Utils.js'; */ export const average = (dataSet: number[]): number => { if (Array.isArray(dataSet) && dataSet.length === 0) { - return 0; + return 0 } if (Array.isArray(dataSet) && dataSet.length === 1) { - return dataSet[0]; + return dataSet[0] } - return dataSet.reduce((accumulator, nb) => accumulator + nb, 0) / dataSet.length; -}; + return dataSet.reduce((accumulator, nb) => accumulator + nb, 0) / dataSet.length +} /** * Computes the median of the given data set. @@ -26,43 +26,43 @@ export const average = (dataSet: number[]): number => { */ export const median = (dataSet: number[]): number => { if (isEmptyArray(dataSet)) { - return 0; + return 0 } - if (Array.isArray(dataSet) === true && dataSet.length === 1) { - return dataSet[0]; + if (Array.isArray(dataSet) && dataSet.length === 1) { + return dataSet[0] } - const sortedDataSet = dataSet.slice().sort((a, b) => a - b); + const sortedDataSet = dataSet.slice().sort((a, b) => a - b) return ( (sortedDataSet[(sortedDataSet.length - 1) >> 1] + sortedDataSet[sortedDataSet.length >> 1]) / 2 - ); -}; + ) +} // TODO: use order statistics tree https://en.wikipedia.org/wiki/Order_statistic_tree export const nthPercentile = (dataSet: number[], percentile: number): number => { if (percentile < 0 && percentile > 100) { - throw new RangeError('Percentile is not between 0 and 100'); + throw new RangeError('Percentile is not between 0 and 100') } if (isEmptyArray(dataSet)) { - return 0; + return 0 } - const sortedDataSet = dataSet.slice().sort((a, b) => a - b); + const sortedDataSet = dataSet.slice().sort((a, b) => a - b) if (percentile === 0 || sortedDataSet.length === 1) { - return sortedDataSet[0]; + return sortedDataSet[0] } if (percentile === 100) { - return sortedDataSet[sortedDataSet.length - 1]; + return sortedDataSet[sortedDataSet.length - 1] } - const percentileIndexBase = (percentile / 100) * (sortedDataSet.length - 1); - const percentileIndexInteger = Math.floor(percentileIndexBase); + const percentileIndexBase = (percentile / 100) * (sortedDataSet.length - 1) + const percentileIndexInteger = Math.floor(percentileIndexBase) if (!isNullOrUndefined(sortedDataSet[percentileIndexInteger + 1])) { return ( sortedDataSet[percentileIndexInteger] + (percentileIndexBase - percentileIndexInteger) * (sortedDataSet[percentileIndexInteger + 1] - sortedDataSet[percentileIndexInteger]) - ); + ) } - return sortedDataSet[percentileIndexInteger]; -}; + return sortedDataSet[percentileIndexInteger] +} /** * Computes the sample standard deviation of the given data set. @@ -75,16 +75,16 @@ export const nthPercentile = (dataSet: number[], percentile: number): number => */ export const stdDeviation = ( dataSet: number[], - dataSetAverage: number = average(dataSet), + dataSetAverage: number = average(dataSet) ): number => { if (isEmptyArray(dataSet)) { - return 0; + return 0 } if (Array.isArray(dataSet) && dataSet.length === 1) { - return 0; + return 0 } return Math.sqrt( dataSet.reduce((accumulator, num) => accumulator + Math.pow(num - dataSetAverage, 2), 0) / - (dataSet.length - 1), - ); -}; + (dataSet.length - 1) + ) +} diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index 872c2b04..291787d9 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -1,5 +1,5 @@ -import { getRandomValues, randomBytes, randomInt, randomUUID } from 'node:crypto'; -import { env, nextTick } from 'node:process'; +import { getRandomValues, randomBytes, randomInt, randomUUID } from 'node:crypto' +import { env, nextTick } from 'node:process' import { formatDuration, @@ -10,156 +10,158 @@ import { millisecondsToMinutes, millisecondsToSeconds, minutesToSeconds, - secondsToMilliseconds, -} from 'date-fns'; + secondsToMilliseconds +} from 'date-fns' -import { Constants } from './Constants.js'; -import { type TimestampedData, WebSocketCloseEventStatusString } from '../types/index.js'; +import { Constants } from './Constants.js' +import { type TimestampedData, WebSocketCloseEventStatusString } from '../types/index.js' export const logPrefix = (prefixString = ''): string => { - return `${new Date().toLocaleString()}${prefixString}`; -}; + return `${new Date().toLocaleString()}${prefixString}` +} export const generateUUID = (): string => { - return randomUUID(); -}; + return randomUUID() +} export const validateUUID = (uuid: string): boolean => { - return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test( - uuid, - ); -}; + return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-4[0-9a-fA-F]{3}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(uuid) +} export const sleep = async (milliSeconds: number): Promise => { - return new Promise((resolve) => setTimeout(resolve as () => void, milliSeconds)); -}; + return await new Promise((resolve) => + setTimeout(resolve as () => void, milliSeconds) + ) +} export const formatDurationMilliSeconds = (duration: number): string => { - duration = convertToInt(duration); + duration = convertToInt(duration) if (duration < 0) { - throw new RangeError('Duration cannot be negative'); + throw new RangeError('Duration cannot be negative') } - const days = Math.floor(duration / (24 * 3600 * 1000)); - const hours = Math.floor(millisecondsToHours(duration) - days * 24); + const days = Math.floor(duration / (24 * 3600 * 1000)) + const hours = Math.floor(millisecondsToHours(duration) - days * 24) const minutes = Math.floor( - millisecondsToMinutes(duration) - days * 24 * 60 - hoursToMinutes(hours), - ); + millisecondsToMinutes(duration) - days * 24 * 60 - hoursToMinutes(hours) + ) const seconds = Math.floor( millisecondsToSeconds(duration) - days * 24 * 3600 - hoursToSeconds(hours) - - minutesToSeconds(minutes), - ); + minutesToSeconds(minutes) + ) if (days === 0 && hours === 0 && minutes === 0 && seconds === 0) { - return formatDuration({ seconds }, { zero: true }); + return formatDuration({ seconds }, { zero: true }) } return formatDuration({ days, hours, minutes, - seconds, - }); -}; + seconds + }) +} export const formatDurationSeconds = (duration: number): string => { - return formatDurationMilliSeconds(secondsToMilliseconds(duration)); -}; + return formatDurationMilliSeconds(secondsToMilliseconds(duration)) +} // More efficient time validation function than the one provided by date-fns export const isValidTime = (date: unknown): boolean => { if (typeof date === 'number') { - return !isNaN(date); + return !isNaN(date) } else if (isDate(date)) { - return !isNaN(date.getTime()); + return !isNaN(date.getTime()) } - return false; -}; + return false +} export const convertToDate = ( - value: Date | string | number | null | undefined, + value: Date | string | number | null | undefined ): Date | null | undefined => { - if (isNullOrUndefined(value)) { - return value as null | undefined; + if (value == null) { + return value } if (isDate(value)) { - return value; + return value } if (isString(value) || typeof value === 'number') { - const valueToDate = new Date(value!); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const valueToDate = new Date(value) if (isNaN(valueToDate.getTime())) { - throw new Error(`Cannot convert to date: '${value!}'`); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + throw new Error(`Cannot convert to date: '${value}'`) } - return valueToDate; + return valueToDate } -}; +} export const convertToInt = (value: unknown): number => { - if (!value) { - return 0; + if (value == null) { + return 0 } - let changedValue: number = value as number; + let changedValue: number = value as number if (Number.isSafeInteger(value)) { - return value as number; + return value as number } if (typeof value === 'number') { - return Math.trunc(value); + return Math.trunc(value) } if (isString(value)) { - changedValue = parseInt(value as string); + changedValue = parseInt(value as string) } if (isNaN(changedValue)) { - throw new Error(`Cannot convert to integer: '${String(value)}'`); + throw new Error(`Cannot convert to integer: '${String(value)}'`) } - return changedValue; -}; + return changedValue +} export const convertToFloat = (value: unknown): number => { - if (!value) { - return 0; + if (value == null) { + return 0 } - let changedValue: number = value as number; + let changedValue: number = value as number if (isString(value)) { - changedValue = parseFloat(value as string); + changedValue = parseFloat(value as string) } if (isNaN(changedValue)) { - throw new Error(`Cannot convert to float: '${String(value)}'`); + throw new Error(`Cannot convert to float: '${String(value)}'`) } - return changedValue; -}; + return changedValue +} export const convertToBoolean = (value: unknown): boolean => { - let result = false; + let result = false if (value != null) { // Check the type if (typeof value === 'boolean') { - return value; + return value } else if (isString(value) && ((value as string).toLowerCase() === 'true' || value === '1')) { - result = true; + result = true } else if (typeof value === 'number' && value === 1) { - result = true; + result = true } } - return result; -}; + return result +} export const getRandomFloat = (max = Number.MAX_VALUE, min = 0): number => { if (max < min) { - throw new RangeError('Invalid interval'); + throw new RangeError('Invalid interval') } if (max - min === Infinity) { - throw new RangeError('Invalid interval'); + throw new RangeError('Invalid interval') } - return (randomBytes(4).readUInt32LE() / 0xffffffff) * (max - min) + min; -}; + return (randomBytes(4).readUInt32LE() / 0xffffffff) * (max - min) + min +} export const getRandomInteger = (max = Constants.MAX_RANDOM_INTEGER, min = 0): number => { - max = Math.floor(max); - if (!isNullOrUndefined(min) && min !== 0) { - min = Math.ceil(min); - return Math.floor(randomInt(min, max + 1)); + max = Math.floor(max) + if (min != null && min !== 0) { + min = Math.ceil(min) + return Math.floor(randomInt(min, max + 1)) } - return Math.floor(randomInt(max + 1)); -}; + return Math.floor(randomInt(max + 1)) +} /** * Rounds the given number to the given scale. @@ -170,47 +172,45 @@ export const getRandomInteger = (max = Constants.MAX_RANDOM_INTEGER, min = 0): n * @returns The rounded number. */ export const roundTo = (numberValue: number, scale: number): number => { - const roundPower = Math.pow(10, scale); - return Math.round(numberValue * roundPower * (1 + Number.EPSILON)) / roundPower; -}; + const roundPower = Math.pow(10, scale) + return Math.round(numberValue * roundPower * (1 + Number.EPSILON)) / roundPower +} export const getRandomFloatRounded = (max = Number.MAX_VALUE, min = 0, scale = 2): number => { - if (min) { - return roundTo(getRandomFloat(max, min), scale); + if (min != null && min !== 0) { + return roundTo(getRandomFloat(max, min), scale) } - return roundTo(getRandomFloat(max), scale); -}; + return roundTo(getRandomFloat(max), scale) +} export const getRandomFloatFluctuatedRounded = ( staticValue: number, fluctuationPercent: number, - scale = 2, + scale = 2 ): number => { if (fluctuationPercent < 0 || fluctuationPercent > 100) { throw new RangeError( - `Fluctuation percent must be between 0 and 100. Actual value: ${fluctuationPercent}`, - ); + `Fluctuation percent must be between 0 and 100. Actual value: ${fluctuationPercent}` + ) } if (fluctuationPercent === 0) { - return roundTo(staticValue, scale); + return roundTo(staticValue, scale) } - const fluctuationRatio = fluctuationPercent / 100; + const fluctuationRatio = fluctuationPercent / 100 return getRandomFloatRounded( staticValue * (1 + fluctuationRatio), staticValue * (1 - fluctuationRatio), - scale, - ); -}; + scale + ) +} -export const extractTimeSeriesValues = (timeSeries: Array): number[] => { - return timeSeries.map((timeSeriesItem) => timeSeriesItem.value); -}; +export const extractTimeSeriesValues = (timeSeries: TimestampedData[]): number[] => { + return timeSeries.map((timeSeriesItem) => timeSeriesItem.value) +} export const isObject = (item: unknown): boolean => { - return ( - isNullOrUndefined(item) === false && typeof item === 'object' && Array.isArray(item) === false - ); -}; + return !isNullOrUndefined(item) && typeof item === 'object' && !Array.isArray(item) +} type CloneableData = | number @@ -220,103 +220,104 @@ type CloneableData = | undefined | Date | CloneableData[] - | { [key: string]: CloneableData }; + | { [key: string]: CloneableData } -type FormatKey = (key: string) => string; +type FormatKey = (key: string) => string const deepClone = ( value: I, formatKey?: FormatKey, - refs: Map = new Map(), + refs: Map = new Map() ): O => { - const ref = refs.get(value); + const ref = refs.get(value) if (ref !== undefined) { - return ref; + return ref } if (Array.isArray(value)) { - const clone: CloneableData[] = []; - refs.set(value, clone as O); + const clone: CloneableData[] = [] + refs.set(value, clone as O) for (let i = 0; i < value.length; i++) { - clone[i] = deepClone(value[i], formatKey, refs); + clone[i] = deepClone(value[i], formatKey, refs) } - return clone as O; + return clone as O } if (value instanceof Date) { - return new Date(value.valueOf()) as O; + return new Date(value.valueOf()) as O } if (typeof value !== 'object' || value === null) { - return value as unknown as O; + return value as unknown as O } - const clone: Record = {}; - refs.set(value, clone as O); + const clone: Record = {} + refs.set(value, clone as O) for (const key of Object.keys(value)) { clone[typeof formatKey === 'function' ? formatKey(key) : key] = deepClone( value[key], formatKey, - refs, - ); + refs + ) } - return clone as O; -}; + return clone as O +} export const cloneObject = (object: T): T => { - return deepClone(object as CloneableData) as T; -}; + return deepClone(object as CloneableData) as T +} export const hasOwnProp = (object: unknown, property: PropertyKey): boolean => { - return isObject(object) && Object.hasOwn(object as object, property); -}; + return isObject(object) && Object.hasOwn(object as object, property) +} export const isCFEnvironment = (): boolean => { - return !isNullOrUndefined(env.VCAP_APPLICATION); -}; + return !isNullOrUndefined(env.VCAP_APPLICATION) +} export const isIterable = (obj: T): boolean => { - return !isNullOrUndefined(obj) ? typeof obj[Symbol.iterator as keyof T] === 'function' : false; -}; + return !isNullOrUndefined(obj) ? typeof obj[Symbol.iterator as keyof T] === 'function' : false +} const isString = (value: unknown): boolean => { - return typeof value === 'string'; -}; + return typeof value === 'string' +} export const isEmptyString = (value: unknown): boolean => { - return isNullOrUndefined(value) || (isString(value) && (value as string).trim().length === 0); -}; + return isNullOrUndefined(value) || (isString(value) && (value as string).trim().length === 0) +} export const isNotEmptyString = (value: unknown): boolean => { - return isString(value) && (value as string).trim().length > 0; -}; + return isString(value) && (value as string).trim().length > 0 +} export const isUndefined = (value: unknown): boolean => { - return value === undefined; -}; + return value === undefined +} export const isNullOrUndefined = (value: unknown): boolean => { - return value == null; -}; + return value == null +} export const isEmptyArray = (object: unknown): boolean => { - return Array.isArray(object) && object.length === 0; -}; + return Array.isArray(object) && object.length === 0 +} export const isNotEmptyArray = (object: unknown): boolean => { - return Array.isArray(object) && object.length > 0; -}; + return Array.isArray(object) && object.length > 0 +} export const isEmptyObject = (obj: object): boolean => { if (obj?.constructor !== Object) { - return false; + return false } // Iterates over the keys of an object, if // any exist, return false. + // eslint-disable-next-line no-unreachable-loop for (const _ in obj) { - return false; + return false } - return true; -}; + return true +} export const insertAt = (str: string, subStr: string, pos: number): string => - `${str.slice(0, pos)}${subStr}${str.slice(pos)}`; + `${str.slice(0, pos)}${subStr}${str.slice(pos)}` /** * Computes the retry delay in milliseconds using an exponential backoff algorithm. @@ -326,10 +327,10 @@ export const insertAt = (str: string, subStr: string, pos: number): string => * @returns delay in milliseconds */ export const exponentialDelay = (retryNumber = 0, delayFactor = 100): number => { - const delay = Math.pow(2, retryNumber) * delayFactor; - const randomSum = delay * 0.2 * secureRandom(); // 0-20% of the delay - return delay + randomSum; -}; + const delay = Math.pow(2, retryNumber) * delayFactor + const randomSum = delay * 0.2 * secureRandom() // 0-20% of the delay + return delay + randomSum +} /** * Generates a cryptographically secure random number in the [0,1[ range @@ -337,12 +338,12 @@ export const exponentialDelay = (retryNumber = 0, delayFactor = 100): number => * @returns A number in the [0,1[ range */ export const secureRandom = (): number => { - return getRandomValues(new Uint32Array(1))[0] / 0x100000000; -}; + return getRandomValues(new Uint32Array(1))[0] / 0x100000000 +} export const JSONStringifyWithMapSupport = ( - obj: Record | Record[] | Map, - space?: number, + obj: Record | Array> | Map, + space?: number ): string => { return JSON.stringify( obj, @@ -350,14 +351,14 @@ export const JSONStringifyWithMapSupport = ( if (value instanceof Map) { return { dataType: 'Map', - value: [...value], - }; + value: [...value] + } } - return value; + return value }, - space, - ); -}; + space + ) +} /** * Converts websocket error code to human readable string message @@ -367,60 +368,60 @@ export const JSONStringifyWithMapSupport = ( */ export const getWebSocketCloseEventStatusString = (code: number): string => { if (code >= 0 && code <= 999) { - return '(Unused)'; + return '(Unused)' } else if (code >= 1016) { if (code <= 1999) { - return '(For WebSocket standard)'; + return '(For WebSocket standard)' } else if (code <= 2999) { - return '(For WebSocket extensions)'; + return '(For WebSocket extensions)' } else if (code <= 3999) { - return '(For libraries and frameworks)'; + return '(For libraries and frameworks)' } else if (code <= 4999) { - return '(For applications)'; + return '(For applications)' } } if ( !isUndefined( - WebSocketCloseEventStatusString[code as keyof typeof WebSocketCloseEventStatusString], + WebSocketCloseEventStatusString[code as keyof typeof WebSocketCloseEventStatusString] ) ) { - return WebSocketCloseEventStatusString[code as keyof typeof WebSocketCloseEventStatusString]; + return WebSocketCloseEventStatusString[code as keyof typeof WebSocketCloseEventStatusString] } - return '(Unknown)'; -}; + return '(Unknown)' +} export const isArraySorted = (array: T[], compareFn: (a: T, b: T) => number): boolean => { for (let index = 0; index < array.length - 1; ++index) { if (compareFn(array[index], array[index + 1]) > 0) { - return false; + return false } } - return true; -}; + return true +} // eslint-disable-next-line @typescript-eslint/no-explicit-any export const once = ( fn: (...args: A) => R, - context: T, + context: T ): ((...args: A) => R) => { - let result: R; + let result: R return (...args: A) => { - if (fn) { - result = fn.apply(context, args); - (fn as unknown as undefined) = (context as unknown as undefined) = undefined; + if (fn != null) { + result = fn.apply(context, args) + ;(fn as unknown as undefined) = (context as unknown as undefined) = undefined } - return result; - }; -}; + return result + } +} export const min = (...args: number[]): number => - args.reduce((minimum, num) => (minimum < num ? minimum : num), Infinity); + args.reduce((minimum, num) => (minimum < num ? minimum : num), Infinity) export const max = (...args: number[]): number => - args.reduce((maximum, num) => (maximum > num ? maximum : num), -Infinity); + args.reduce((maximum, num) => (maximum > num ? maximum : num), -Infinity) export const throwErrorInNextTick = (error: Error): void => { nextTick(() => { - throw error; - }); -}; + throw error + }) +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 20d64cf5..0e727baa 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,28 +1,28 @@ -export { ACElectricUtils, DCElectricUtils } from './ElectricUtils.js'; -export { AsyncLock, AsyncLockType } from './AsyncLock.js'; +export { ACElectricUtils, DCElectricUtils } from './ElectricUtils.js' +export { AsyncLock, AsyncLockType } from './AsyncLock.js' export { OutputFormat, buildChargingStationAutomaticTransactionGeneratorConfiguration, buildConnectorsStatus, - buildEvsesStatus, -} from './ChargingStationConfigurationUtils.js'; -export { CircularArray } from './CircularArray.js'; -export { Configuration } from './Configuration.js'; -export { Constants } from './Constants.js'; + buildEvsesStatus +} from './ChargingStationConfigurationUtils.js' +export { CircularArray } from './CircularArray.js' +export { Configuration } from './Configuration.js' +export { Constants } from './Constants.js' export { handleFileException, handleUncaughtException, handleUnhandledRejection, handleSendMessageError, - setDefaultErrorParams, -} from './ErrorUtils.js'; -export { watchJsonFile } from './FileUtils.js'; + setDefaultErrorParams +} from './ErrorUtils.js' +export { watchJsonFile } from './FileUtils.js' export { buildPerformanceStatisticsMessage, buildUpdatedMessage, buildStartedMessage, - buildStoppedMessage, -} from './MessageChannelUtils.js'; + buildStoppedMessage +} from './MessageChannelUtils.js' export { JSONStringifyWithMapSupport, cloneObject, @@ -55,7 +55,7 @@ export { roundTo, secureRandom, sleep, - validateUUID, -} from './Utils.js'; -export { average, median, nthPercentile, stdDeviation } from './StatisticUtils.js'; -export { logger } from './Logger.js'; + validateUUID +} from './Utils.js' +export { average, median, nthPercentile, stdDeviation } from './StatisticUtils.js' +export { logger } from './Logger.js' diff --git a/src/worker/WorkerAbstract.ts b/src/worker/WorkerAbstract.ts index e78216de..38d76276 100644 --- a/src/worker/WorkerAbstract.ts +++ b/src/worker/WorkerAbstract.ts @@ -1,18 +1,17 @@ -import type { EventEmitterAsyncResource } from 'node:events'; -import { existsSync } from 'node:fs'; +import type { EventEmitterAsyncResource } from 'node:events' +import { existsSync } from 'node:fs' -import type { PoolInfo } from 'poolifier'; +import type { PoolInfo } from 'poolifier' -import type { SetInfo, WorkerData, WorkerOptions } from './WorkerTypes.js'; -import { defaultErrorHandler, defaultExitHandler } from './WorkerUtils.js'; +import type { SetInfo, WorkerData, WorkerOptions } from './WorkerTypes.js' export abstract class WorkerAbstract { - protected readonly workerScript: string; - protected readonly workerOptions: WorkerOptions; - public abstract readonly info: PoolInfo | SetInfo; - public abstract readonly size: number; - public abstract readonly maxElementsPerWorker: number | undefined; - public abstract readonly emitter: EventEmitterAsyncResource | undefined; + protected readonly workerScript: string + protected readonly workerOptions: WorkerOptions + public abstract readonly info: PoolInfo | SetInfo + public abstract readonly size: number + public abstract readonly maxElementsPerWorker: number | undefined + public abstract readonly emitter: EventEmitterAsyncResource | undefined /** * `WorkerAbstract` constructor. @@ -20,39 +19,35 @@ export abstract class WorkerAbstract { * @param workerScript - * @param workerOptions - */ - constructor(workerScript: string, workerOptions: WorkerOptions) { + constructor (workerScript: string, workerOptions: WorkerOptions) { if (workerScript == null) { - throw new TypeError('Worker script is not defined'); + throw new TypeError('Worker script is not defined') } if (typeof workerScript !== 'string') { - throw new TypeError('Worker script must be a string'); + throw new TypeError('Worker script must be a string') } if (workerScript.trim().length === 0) { - throw new Error('Worker script is an empty string'); + throw new Error('Worker script is an empty string') } if (!existsSync(workerScript)) { - throw new Error('Worker script file does not exist'); + throw new Error('Worker script file does not exist') } - this.workerScript = workerScript; - this.workerOptions = workerOptions; - this.workerOptions.poolOptions!.errorHandler = - this.workerOptions.poolOptions?.errorHandler ?? defaultErrorHandler; - this.workerOptions.poolOptions!.exitHandler = - this.workerOptions.poolOptions?.exitHandler ?? defaultExitHandler; + this.workerScript = workerScript + this.workerOptions = workerOptions } /** * Starts the worker pool/set. */ - public abstract start(): Promise; + public abstract start (): Promise /** * Stops the worker pool/set. */ - public abstract stop(): Promise; + public abstract stop (): Promise /** * Adds a task element to the worker pool/set. * * @param elementData - */ - public abstract addElement(elementData: T): Promise; + public abstract addElement (elementData: T): Promise } diff --git a/src/worker/WorkerConstants.ts b/src/worker/WorkerConstants.ts index 654e6f5e..40484095 100644 --- a/src/worker/WorkerConstants.ts +++ b/src/worker/WorkerConstants.ts @@ -1,18 +1,19 @@ -import { availableParallelism } from 'poolifier'; +import { availableParallelism } from 'poolifier' -import type { WorkerOptions } from './WorkerTypes.js'; +import type { WorkerOptions } from './WorkerTypes.js' +import { defaultErrorHandler, defaultExitHandler } from './WorkerUtils.js' export const EMPTY_FUNCTION = Object.freeze(() => { /* This is intentional */ -}); +}) -export const workerSetVersion = '1.0.1'; +export const workerSetVersion = '1.0.1' -export const DEFAULT_ELEMENT_START_DELAY = 0; -export const DEFAULT_WORKER_START_DELAY = 500; -export const DEFAULT_POOL_MIN_SIZE = Math.floor(availableParallelism() / 2); -export const DEFAULT_POOL_MAX_SIZE = Math.round(availableParallelism() * 1.5); -export const DEFAULT_ELEMENTS_PER_WORKER = 1; +export const DEFAULT_ELEMENT_START_DELAY = 0 +export const DEFAULT_WORKER_START_DELAY = 500 +export const DEFAULT_POOL_MIN_SIZE = Math.floor(availableParallelism() / 2) +export const DEFAULT_POOL_MAX_SIZE = Math.round(availableParallelism() * 1.5) +export const DEFAULT_ELEMENTS_PER_WORKER = 1 export const DEFAULT_WORKER_OPTIONS: WorkerOptions = Object.freeze({ workerStartDelay: DEFAULT_WORKER_START_DELAY, @@ -23,5 +24,7 @@ export const DEFAULT_WORKER_OPTIONS: WorkerOptions = Object.freeze({ poolOptions: { enableEvents: true, restartWorkerOnError: true, - }, -}); + errorHandler: defaultErrorHandler, + exitHandler: defaultExitHandler + } +}) diff --git a/src/worker/WorkerDynamicPool.ts b/src/worker/WorkerDynamicPool.ts index e27e803b..6574dcc2 100644 --- a/src/worker/WorkerDynamicPool.ts +++ b/src/worker/WorkerDynamicPool.ts @@ -1,13 +1,13 @@ -import type { EventEmitterAsyncResource } from 'node:events'; +import type { EventEmitterAsyncResource } from 'node:events' -import { DynamicThreadPool, type PoolInfo } from 'poolifier'; +import { DynamicThreadPool, type PoolInfo } from 'poolifier' -import { WorkerAbstract } from './WorkerAbstract.js'; -import type { WorkerData, WorkerOptions } from './WorkerTypes.js'; -import { randomizeDelay, sleep } from './WorkerUtils.js'; +import { WorkerAbstract } from './WorkerAbstract.js' +import type { WorkerData, WorkerOptions } from './WorkerTypes.js' +import { randomizeDelay, sleep } from './WorkerUtils.js' export class WorkerDynamicPool extends WorkerAbstract { - private readonly pool: DynamicThreadPool; + private readonly pool: DynamicThreadPool /** * Creates a new `WorkerDynamicPool`. @@ -15,47 +15,49 @@ export class WorkerDynamicPool extends WorkerAbstract { * @param workerScript - * @param workerOptions - */ - constructor(workerScript: string, workerOptions: WorkerOptions) { - super(workerScript, workerOptions); + constructor (workerScript: string, workerOptions: WorkerOptions) { + super(workerScript, workerOptions) this.pool = new DynamicThreadPool( this.workerOptions.poolMinSize, this.workerOptions.poolMaxSize, this.workerScript, - this.workerOptions.poolOptions, - ); + this.workerOptions.poolOptions + ) } - get info(): PoolInfo { - return this.pool.info; + get info (): PoolInfo { + return this.pool.info } - get size(): number { - return this.pool.info.workerNodes; + get size (): number { + return this.pool.info.workerNodes } - get maxElementsPerWorker(): number | undefined { - return undefined; + get maxElementsPerWorker (): number | undefined { + return undefined } - get emitter(): EventEmitterAsyncResource | undefined { - return this.pool?.emitter; + get emitter (): EventEmitterAsyncResource | undefined { + return this.pool?.emitter } /** @inheritDoc */ - public async start(): Promise { + public async start (): Promise { // This is intentional } /** @inheritDoc */ - public async stop(): Promise { - return this.pool.destroy(); + public async stop (): Promise { + await this.pool.destroy() } /** @inheritDoc */ - public async addElement(elementData: WorkerData): Promise { - await this.pool.execute(elementData); + public async addElement (elementData: WorkerData): Promise { + 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.elementStartDelay! > 0 && - (await sleep(randomizeDelay(this.workerOptions.elementStartDelay!))); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (await sleep(randomizeDelay(this.workerOptions.elementStartDelay!))) } } diff --git a/src/worker/WorkerFactory.ts b/src/worker/WorkerFactory.ts index 7ae5ddb9..833c939f 100644 --- a/src/worker/WorkerFactory.ts +++ b/src/worker/WorkerFactory.ts @@ -1,41 +1,42 @@ -import { isMainThread } from 'node:worker_threads'; +import { isMainThread } from 'node:worker_threads' -import type { WorkerAbstract } from './WorkerAbstract.js'; -import { DEFAULT_WORKER_OPTIONS } from './WorkerConstants.js'; -import { WorkerDynamicPool } from './WorkerDynamicPool.js'; -import { WorkerFixedPool } from './WorkerFixedPool.js'; -import { WorkerSet } from './WorkerSet.js'; -import { type WorkerData, type WorkerOptions, WorkerProcessType } from './WorkerTypes.js'; +import type { WorkerAbstract } from './WorkerAbstract.js' +import { DEFAULT_WORKER_OPTIONS } from './WorkerConstants.js' +import { WorkerDynamicPool } from './WorkerDynamicPool.js' +import { WorkerFixedPool } from './WorkerFixedPool.js' +import { WorkerSet } from './WorkerSet.js' +import { type WorkerData, type WorkerOptions, WorkerProcessType } from './WorkerTypes.js' +// eslint-disable-next-line @typescript-eslint/no-extraneous-class export class WorkerFactory { - private constructor() { + private constructor () { // This is intentional } public static getWorkerImplementation( workerScript: string, workerProcessType: WorkerProcessType, - workerOptions?: WorkerOptions, + workerOptions?: WorkerOptions ): WorkerAbstract | undefined { if (!isMainThread) { - throw new Error('Cannot get a worker implementation outside the main thread'); + throw new Error('Cannot get a worker implementation outside the main thread') } - workerOptions = { ...DEFAULT_WORKER_OPTIONS, ...workerOptions }; - let workerImplementation: WorkerAbstract; + workerOptions = { ...DEFAULT_WORKER_OPTIONS, ...workerOptions } + let workerImplementation: WorkerAbstract switch (workerProcessType) { case WorkerProcessType.workerSet: - workerImplementation = new WorkerSet(workerScript, workerOptions); - break; + workerImplementation = new WorkerSet(workerScript, workerOptions) + break case WorkerProcessType.fixedPool: - workerImplementation = new WorkerFixedPool(workerScript, workerOptions); - break; + workerImplementation = new WorkerFixedPool(workerScript, workerOptions) + break case WorkerProcessType.dynamicPool: - workerImplementation = new WorkerDynamicPool(workerScript, workerOptions); - break; + workerImplementation = new WorkerDynamicPool(workerScript, workerOptions) + break default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - throw new Error(`Worker implementation type '${workerProcessType}' not found`); + throw new Error(`Worker implementation type '${workerProcessType}' not found`) } - return workerImplementation; + return workerImplementation } } diff --git a/src/worker/WorkerFixedPool.ts b/src/worker/WorkerFixedPool.ts index 9e71c277..a2a0e759 100644 --- a/src/worker/WorkerFixedPool.ts +++ b/src/worker/WorkerFixedPool.ts @@ -1,13 +1,13 @@ -import type { EventEmitterAsyncResource } from 'node:events'; +import type { EventEmitterAsyncResource } from 'node:events' -import { FixedThreadPool, type PoolInfo } from 'poolifier'; +import { FixedThreadPool, type PoolInfo } from 'poolifier' -import { WorkerAbstract } from './WorkerAbstract.js'; -import type { WorkerData, WorkerOptions } from './WorkerTypes.js'; -import { randomizeDelay, sleep } from './WorkerUtils.js'; +import { WorkerAbstract } from './WorkerAbstract.js' +import type { WorkerData, WorkerOptions } from './WorkerTypes.js' +import { randomizeDelay, sleep } from './WorkerUtils.js' export class WorkerFixedPool extends WorkerAbstract { - private readonly pool: FixedThreadPool; + private readonly pool: FixedThreadPool /** * Creates a new `WorkerFixedPool`. @@ -15,46 +15,48 @@ export class WorkerFixedPool extends WorkerAbstract { * @param workerScript - * @param workerOptions - */ - constructor(workerScript: string, workerOptions: WorkerOptions) { - super(workerScript, workerOptions); + constructor (workerScript: string, workerOptions: WorkerOptions) { + super(workerScript, workerOptions) this.pool = new FixedThreadPool( this.workerOptions.poolMaxSize, this.workerScript, - this.workerOptions.poolOptions, - ); + this.workerOptions.poolOptions + ) } - get info(): PoolInfo { - return this.pool.info; + get info (): PoolInfo { + return this.pool.info } - get size(): number { - return this.pool.info.workerNodes; + get size (): number { + return this.pool.info.workerNodes } - get maxElementsPerWorker(): number | undefined { - return undefined; + get maxElementsPerWorker (): number | undefined { + return undefined } - get emitter(): EventEmitterAsyncResource | undefined { - return this.pool?.emitter; + get emitter (): EventEmitterAsyncResource | undefined { + return this.pool?.emitter } /** @inheritDoc */ - public async start(): Promise { + public async start (): Promise { // This is intentional } /** @inheritDoc */ - public async stop(): Promise { - return this.pool.destroy(); + public async stop (): Promise { + await this.pool.destroy() } /** @inheritDoc */ - public async addElement(elementData: WorkerData): Promise { - await this.pool.execute(elementData); + public async addElement (elementData: WorkerData): Promise { + 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.elementStartDelay! > 0 && - (await sleep(randomizeDelay(this.workerOptions.elementStartDelay!))); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (await sleep(randomizeDelay(this.workerOptions.elementStartDelay!))) } } diff --git a/src/worker/WorkerSet.ts b/src/worker/WorkerSet.ts index b33d917c..41214c8d 100644 --- a/src/worker/WorkerSet.ts +++ b/src/worker/WorkerSet.ts @@ -1,10 +1,10 @@ // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. -import { EventEmitterAsyncResource } from 'node:events'; -import { SHARE_ENV, Worker } from 'node:worker_threads'; +import { EventEmitterAsyncResource } from 'node:events' +import { SHARE_ENV, Worker } from 'node:worker_threads' -import { WorkerAbstract } from './WorkerAbstract.js'; -import { EMPTY_FUNCTION, workerSetVersion } from './WorkerConstants.js'; +import { WorkerAbstract } from './WorkerAbstract.js' +import { EMPTY_FUNCTION, workerSetVersion } from './WorkerConstants.js' import { type SetInfo, type WorkerData, @@ -12,15 +12,15 @@ import { WorkerMessageEvents, type WorkerOptions, type WorkerSetElement, - WorkerSetEvents, -} from './WorkerTypes.js'; -import { randomizeDelay, sleep } from './WorkerUtils.js'; + WorkerSetEvents +} from './WorkerTypes.js' +import { randomizeDelay, sleep } from './WorkerUtils.js' export class WorkerSet extends WorkerAbstract { - public readonly emitter: EventEmitterAsyncResource | undefined; - private readonly workerSet: Set; - private started: boolean; - private workerStartup: boolean; + public readonly emitter: EventEmitterAsyncResource | undefined + private readonly workerSet: Set + private started: boolean + private workerStartup: boolean /** * Creates a new `WorkerSet`. @@ -28,26 +28,26 @@ export class WorkerSet extends WorkerAbstract { * @param workerScript - * @param workerOptions - */ - constructor(workerScript: string, workerOptions: WorkerOptions) { - super(workerScript, workerOptions); + constructor (workerScript: string, workerOptions: WorkerOptions) { + super(workerScript, workerOptions) if (this.workerOptions.elementsPerWorker == null) { - throw new TypeError('Elements per worker is not defined'); + throw new TypeError('Elements per worker is not defined') } if (!Number.isSafeInteger(this.workerOptions.elementsPerWorker)) { - throw new TypeError('Elements per worker must be an integer'); + throw new TypeError('Elements per worker must be an integer') } if (this.workerOptions.elementsPerWorker <= 0) { - throw new RangeError('Elements per worker must be greater than zero'); + throw new RangeError('Elements per worker must be greater than zero') } - this.workerSet = new Set(); - if (this.workerOptions.poolOptions?.enableEvents) { - this.emitter = new EventEmitterAsyncResource({ name: 'workerset' }); + this.workerSet = new Set() + if (this.workerOptions.poolOptions?.enableEvents != null) { + this.emitter = new EventEmitterAsyncResource({ name: 'workerset' }) } - this.started = false; - this.workerStartup = false; + this.started = false + this.workerStartup = false } - get info(): SetInfo { + get info (): SetInfo { return { version: workerSetVersion, type: 'set', @@ -55,136 +55,147 @@ export class WorkerSet extends WorkerAbstract { size: this.size, elementsExecuting: [...this.workerSet].reduce( (accumulator, workerSetElement) => accumulator + workerSetElement.numberOfWorkerElements, - 0, + 0 ), - elementsPerWorker: this.maxElementsPerWorker!, - }; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + elementsPerWorker: this.maxElementsPerWorker! + } } - get size(): number { - return this.workerSet.size; + get size (): number { + return this.workerSet.size } - get maxElementsPerWorker(): number | undefined { - return this.workerOptions.elementsPerWorker; + get maxElementsPerWorker (): number | undefined { + return this.workerOptions.elementsPerWorker } /** @inheritDoc */ - public async start(): Promise { - this.addWorkerSetElement(); + public async start (): Promise { + this.addWorkerSetElement() // Add worker set element sequentially to optimize memory at startup + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.workerOptions.workerStartDelay! > 0 && - (await sleep(randomizeDelay(this.workerOptions.workerStartDelay!))); - this.emitter?.emit(WorkerSetEvents.started, this.info); - this.started = true; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (await sleep(randomizeDelay(this.workerOptions.workerStartDelay!))) + this.emitter?.emit(WorkerSetEvents.started, this.info) + this.started = true } /** @inheritDoc */ - public async stop(): Promise { + public async stop (): Promise { for (const workerSetElement of this.workerSet) { - const worker = workerSetElement.worker; + const worker = workerSetElement.worker const waitWorkerExit = new Promise((resolve) => { worker.once('exit', () => { - resolve(); - }); - }); - await worker.terminate(); - await waitWorkerExit; - this.emitter?.emit(WorkerSetEvents.stopped, this.info); - this.emitter?.emitDestroy(); - this.emitter?.removeAllListeners(); - this.started = false; + resolve() + }) + }) + await worker.terminate() + await waitWorkerExit + this.emitter?.emit(WorkerSetEvents.stopped, this.info) + this.emitter?.emitDestroy() + this.emitter?.removeAllListeners() + this.started = false } } /** @inheritDoc */ - public async addElement(elementData: WorkerData): Promise { + public async addElement (elementData: WorkerData): Promise { if (!this.started) { - throw new Error('Cannot add a WorkerSet element: not started'); + throw new Error('Cannot add a WorkerSet element: not started') } if (this.workerSet == null) { - throw new Error("Cannot add a WorkerSet element: 'workerSet' property does not exist"); + throw new Error("Cannot add a WorkerSet element: 'workerSet' property does not exist") } - const workerSetElement = await this.getWorkerSetElement(); + const workerSetElement = await this.getWorkerSetElement() workerSetElement.worker.postMessage({ event: WorkerMessageEvents.startWorkerElement, - data: elementData, - }); - ++workerSetElement.numberOfWorkerElements; + data: elementData + }) + ++workerSetElement.numberOfWorkerElements // Add element sequentially to optimize memory at startup + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (this.workerOptions.elementStartDelay! > 0) { - await sleep(randomizeDelay(this.workerOptions.elementStartDelay!)); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await sleep(randomizeDelay(this.workerOptions.elementStartDelay!)) } } /** * Adds a new `WorkerSetElement`. */ - private addWorkerSetElement(): WorkerSetElement { - this.workerStartup = true; + private addWorkerSetElement (): WorkerSetElement { + this.workerStartup = true const worker = new Worker(this.workerScript, { env: SHARE_ENV, - ...this.workerOptions.poolOptions?.workerOptions, - }); - worker.on('message', this.workerOptions.poolOptions?.messageHandler ?? EMPTY_FUNCTION); + ...this.workerOptions.poolOptions?.workerOptions + }) + worker.on('message', this.workerOptions.poolOptions?.messageHandler ?? EMPTY_FUNCTION) worker.on('message', (message: WorkerMessage) => { if (message.event === WorkerMessageEvents.startedWorkerElement) { - this.emitter?.emit(WorkerSetEvents.elementStarted, this.info); + this.emitter?.emit(WorkerSetEvents.elementStarted, this.info) } else if (message.event === WorkerMessageEvents.startWorkerElementError) { - this.emitter?.emit(WorkerSetEvents.elementError, message.data); + this.emitter?.emit(WorkerSetEvents.elementError, message.data) } - }); - worker.on('error', this.workerOptions.poolOptions?.errorHandler ?? EMPTY_FUNCTION); + }) + worker.on('error', this.workerOptions.poolOptions?.errorHandler ?? EMPTY_FUNCTION) worker.on('error', (error) => { - this.emitter?.emit(WorkerSetEvents.error, error); + this.emitter?.emit(WorkerSetEvents.error, error) if ( - this.workerOptions.poolOptions?.restartWorkerOnError && + this.workerOptions.poolOptions?.restartWorkerOnError === true && this.started && !this.workerStartup ) { - this.addWorkerSetElement(); + this.addWorkerSetElement() } - }); - worker.on('online', this.workerOptions.poolOptions?.onlineHandler ?? EMPTY_FUNCTION); - worker.on('exit', this.workerOptions.poolOptions?.exitHandler ?? EMPTY_FUNCTION); - worker.once('exit', () => - this.removeWorkerSetElement(this.getWorkerSetElementByWorker(worker)!), - ); - const workerSetElement: WorkerSetElement = { worker, numberOfWorkerElements: 0 }; - this.workerSet.add(workerSetElement); - this.workerStartup = false; - return workerSetElement; + }) + worker.on('online', this.workerOptions.poolOptions?.onlineHandler ?? EMPTY_FUNCTION) + worker.on('exit', this.workerOptions.poolOptions?.exitHandler ?? EMPTY_FUNCTION) + worker.once('exit', () => { + this.removeWorkerSetElement(this.getWorkerSetElementByWorker(worker)) + }) + const workerSetElement: WorkerSetElement = { worker, numberOfWorkerElements: 0 } + this.workerSet.add(workerSetElement) + this.workerStartup = false + return workerSetElement } - private removeWorkerSetElement(workerSetElement: WorkerSetElement): void { - this.workerSet.delete(workerSetElement); + private removeWorkerSetElement (workerSetElement: WorkerSetElement | undefined): void { + if (workerSetElement == null) { + return + } + this.workerSet.delete(workerSetElement) } - private async getWorkerSetElement(): Promise { - let chosenWorkerSetElement: WorkerSetElement | undefined; + private async getWorkerSetElement (): Promise { + let chosenWorkerSetElement: WorkerSetElement | undefined for (const workerSetElement of this.workerSet) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (workerSetElement.numberOfWorkerElements < this.workerOptions.elementsPerWorker!) { - chosenWorkerSetElement = workerSetElement; - break; + chosenWorkerSetElement = workerSetElement + break } } - if (!chosenWorkerSetElement) { - chosenWorkerSetElement = this.addWorkerSetElement(); + if (chosenWorkerSetElement == null) { + chosenWorkerSetElement = this.addWorkerSetElement() // Add worker set element sequentially to optimize memory at startup + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion this.workerOptions.workerStartDelay! > 0 && - (await sleep(randomizeDelay(this.workerOptions.workerStartDelay!))); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + (await sleep(randomizeDelay(this.workerOptions.workerStartDelay!))) } - return chosenWorkerSetElement; + return chosenWorkerSetElement } - private getWorkerSetElementByWorker(worker: Worker): WorkerSetElement | undefined { - let workerSetElt: WorkerSetElement | undefined; + private getWorkerSetElementByWorker (worker: Worker): WorkerSetElement | undefined { + let workerSetElt: WorkerSetElement | undefined for (const workerSetElement of this.workerSet) { if (workerSetElement.worker.threadId === worker.threadId) { - workerSetElt = workerSetElement; - break; + workerSetElt = workerSetElement + break } } - return workerSetElt; + return workerSetElt } } diff --git a/src/worker/WorkerTypes.ts b/src/worker/WorkerTypes.ts index cfbe2dac..faf327db 100644 --- a/src/worker/WorkerTypes.ts +++ b/src/worker/WorkerTypes.ts @@ -1,21 +1,21 @@ -import type { Worker } from 'node:worker_threads'; +import type { Worker } from 'node:worker_threads' -import { type PoolEvent, PoolEvents, type ThreadPoolOptions } from 'poolifier'; +import { type PoolEvent, PoolEvents, type ThreadPoolOptions } from 'poolifier' export enum WorkerProcessType { workerSet = 'workerSet', /** @experimental */ dynamicPool = 'dynamicPool', - fixedPool = 'fixedPool', + fixedPool = 'fixedPool' } export interface SetInfo { - version: string; - type: string; - worker: string; - size: number; - elementsExecuting: number; - elementsPerWorker: number; + version: string + type: string + worker: string + size: number + elementsExecuting: number + elementsPerWorker: number } export enum WorkerSetEvents { @@ -23,38 +23,39 @@ export enum WorkerSetEvents { stopped = 'stopped', error = 'error', elementStarted = 'elementStarted', - elementError = 'elementError', + elementError = 'elementError' } export const WorkerEvents = { ...PoolEvents, - ...WorkerSetEvents, -} as const; -export type WorkerEvents = PoolEvent | WorkerSetEvents; + ...WorkerSetEvents +} as const +// eslint-disable-next-line @typescript-eslint/no-redeclare +export type WorkerEvents = PoolEvent | WorkerSetEvents export interface WorkerOptions { - workerStartDelay?: number; - elementStartDelay?: number; - poolMaxSize: number; - poolMinSize: number; - elementsPerWorker?: number; - poolOptions?: ThreadPoolOptions; + workerStartDelay?: number + elementStartDelay?: number + poolMaxSize: number + poolMinSize: number + elementsPerWorker?: number + poolOptions?: ThreadPoolOptions } -export type WorkerData = Record; +export type WorkerData = Record export interface WorkerSetElement { - worker: Worker; - numberOfWorkerElements: number; + worker: Worker + numberOfWorkerElements: number } export interface WorkerMessage { - event: WorkerMessageEvents; - data: T; + event: WorkerMessageEvents + data: T } export enum WorkerMessageEvents { startWorkerElement = 'startWorkerElement', startWorkerElementError = 'startWorkerElementError', - startedWorkerElement = 'startedWorkerElement', + startedWorkerElement = 'startedWorkerElement' } diff --git a/src/worker/WorkerUtils.ts b/src/worker/WorkerUtils.ts index 776d7174..5cd19f12 100644 --- a/src/worker/WorkerUtils.ts +++ b/src/worker/WorkerUtils.ts @@ -1,31 +1,33 @@ -import { getRandomValues } from 'node:crypto'; +import { getRandomValues } from 'node:crypto' -import chalk from 'chalk'; +import chalk from 'chalk' export const sleep = async (milliSeconds: number): Promise => { - return new Promise((resolve) => setTimeout(resolve as () => void, milliSeconds)); -}; + return await new Promise((resolve) => + setTimeout(resolve as () => void, milliSeconds) + ) +} export const defaultExitHandler = (code: number): void => { if (code === 0) { - console.info(chalk.green('Worker exited successfully')); + console.info(chalk.green('Worker exited successfully')) } else if (code === 1) { - console.info(chalk.green('Worker terminated successfully')); + console.info(chalk.green('Worker terminated successfully')) } else if (code > 1) { - console.error(chalk.red(`Worker exited with exit code: ${code.toString()}`)); + console.error(chalk.red(`Worker exited with exit code: ${code.toString()}`)) } -}; +} export const defaultErrorHandler = (error: Error): void => { - console.error(chalk.red('Worker errored: '), error); -}; + console.error(chalk.red('Worker errored: '), error) +} export const randomizeDelay = (delay: number): number => { - const random = secureRandom(); - const sign = random < 0.5 ? -1 : 1; - const randomSum = delay * 0.2 * random; // 0-20% of the delay - return delay + sign * randomSum; -}; + const random = secureRandom() + const sign = random < 0.5 ? -1 : 1 + const randomSum = delay * 0.2 * random // 0-20% of the delay + return delay + sign * randomSum +} /** * Generates a cryptographically secure random number in the [0,1[ range @@ -34,5 +36,5 @@ export const randomizeDelay = (delay: number): number => { * @internal */ const secureRandom = (): number => { - return getRandomValues(new Uint32Array(1))[0] / 0x100000000; -}; + return getRandomValues(new Uint32Array(1))[0] / 0x100000000 +} diff --git a/src/worker/index.ts b/src/worker/index.ts index 32781a6e..878ef686 100644 --- a/src/worker/index.ts +++ b/src/worker/index.ts @@ -1,15 +1,15 @@ -export type { WorkerAbstract } from './WorkerAbstract.js'; +export type { WorkerAbstract } from './WorkerAbstract.js' export { DEFAULT_ELEMENT_START_DELAY, DEFAULT_POOL_MAX_SIZE, DEFAULT_POOL_MIN_SIZE, - DEFAULT_WORKER_START_DELAY, -} from './WorkerConstants.js'; -export { WorkerFactory } from './WorkerFactory.js'; + DEFAULT_WORKER_START_DELAY +} from './WorkerConstants.js' +export { WorkerFactory } from './WorkerFactory.js' export { type WorkerData, WorkerEvents, type WorkerMessage, WorkerMessageEvents, - WorkerProcessType, -} from './WorkerTypes.js'; + WorkerProcessType +} from './WorkerTypes.js' diff --git a/tests/utils/CircularArray.test.ts b/tests/utils/CircularArray.test.ts index 358f7408..74be0210 100644 --- a/tests/utils/CircularArray.test.ts +++ b/tests/utils/CircularArray.test.ts @@ -1,145 +1,147 @@ -import { describe, it } from 'node:test'; +import { describe, it } from 'node:test' -import { expect } from 'expect'; +import { expect } from 'expect' -import { CircularArray, DEFAULT_CIRCULAR_ARRAY_SIZE } from '../../src/utils/CircularArray.js'; +import { CircularArray, DEFAULT_CIRCULAR_ARRAY_SIZE } from '../../src/utils/CircularArray.js' await describe('CircularArray test suite', async () => { await it('Verify that circular array can be instantiated', () => { - const circularArray = new CircularArray(); - expect(circularArray).toBeInstanceOf(CircularArray); - }); + const circularArray = new CircularArray() + expect(circularArray).toBeInstanceOf(CircularArray) + }) await it('Verify circular array default size at instance creation', () => { - const circularArray = new CircularArray(); - expect(circularArray.size).toBe(DEFAULT_CIRCULAR_ARRAY_SIZE); - }); + const circularArray = new CircularArray() + expect(circularArray.size).toBe(DEFAULT_CIRCULAR_ARRAY_SIZE) + }) await it('Verify that circular array size can be set at instance creation', () => { - const circularArray = new CircularArray(1000); - expect(circularArray.size).toBe(1000); - }); + const circularArray = new CircularArray(1000) + expect(circularArray.size).toBe(1000) + }) await it('Verify that circular array size and items can be set at instance creation', () => { - let circularArray = new CircularArray(1000, 1, 2, 3, 4, 5); - expect(circularArray.size).toBe(1000); - expect(circularArray.length).toBe(5); - circularArray = new CircularArray(4, 1, 2, 3, 4, 5); - expect(circularArray.size).toBe(4); - expect(circularArray.length).toBe(4); - }); + let circularArray = new CircularArray(1000, 1, 2, 3, 4, 5) + expect(circularArray.size).toBe(1000) + expect(circularArray.length).toBe(5) + circularArray = new CircularArray(4, 1, 2, 3, 4, 5) + expect(circularArray.size).toBe(4) + expect(circularArray.length).toBe(4) + }) await it('Verify that circular array size is valid at instance creation', () => { expect(() => new CircularArray(0.25)).toThrow( - new TypeError('Invalid circular array size: 0.25 is not a safe integer'), - ); + new TypeError('Invalid circular array size: 0.25 is not a safe integer') + ) expect(() => new CircularArray(-1)).toThrow( - new RangeError('Invalid circular array size: -1 < 0'), - ); + new RangeError('Invalid circular array size: -1 < 0') + ) expect(() => new CircularArray(Number.MAX_SAFE_INTEGER + 1)).toThrow( new TypeError( - `Invalid circular array size: ${Number.MAX_SAFE_INTEGER + 1} is not a safe integer`, - ), - ); - }); + `Invalid circular array size: ${Number.MAX_SAFE_INTEGER + 1} is not a safe integer` + ) + ) + }) await it('Verify that circular array empty works as intended', () => { - const circularArray = new CircularArray(); - expect(circularArray.empty()).toBe(true); - }); + const circularArray = new CircularArray() + expect(circularArray.empty()).toBe(true) + }) await it('Verify that circular array full works as intended', () => { - const circularArray = new CircularArray(5, 1, 2, 3, 4, 5); - expect(circularArray.full()).toBe(true); - }); + const circularArray = new CircularArray(5, 1, 2, 3, 4, 5) + expect(circularArray.full()).toBe(true) + }) await it('Verify that circular array push works as intended', () => { - let circularArray = new CircularArray(4); - let arrayLength = circularArray.push(1, 2, 3, 4, 5); - expect(arrayLength).toBe(circularArray.size); - expect(circularArray.length).toBe(circularArray.size); - expect(circularArray).toStrictEqual(new CircularArray(4, 2, 3, 4, 5)); - arrayLength = circularArray.push(6, 7); - expect(arrayLength).toBe(circularArray.size); - expect(circularArray.length).toBe(circularArray.size); - expect(circularArray).toStrictEqual(new CircularArray(4, 4, 5, 6, 7)); - circularArray = new CircularArray(100); - arrayLength = circularArray.push(1, 2, 3, 4, 5); - expect(arrayLength).toBe(5); - expect(circularArray.size).toBe(100); - expect(circularArray.length).toBe(5); - expect(circularArray).toStrictEqual(new CircularArray(100, 1, 2, 3, 4, 5)); - }); + let circularArray = new CircularArray(4) + let arrayLength = circularArray.push(1, 2, 3, 4, 5) + expect(arrayLength).toBe(circularArray.size) + expect(circularArray.length).toBe(circularArray.size) + expect(circularArray).toStrictEqual(new CircularArray(4, 2, 3, 4, 5)) + arrayLength = circularArray.push(6, 7) + expect(arrayLength).toBe(circularArray.size) + expect(circularArray.length).toBe(circularArray.size) + expect(circularArray).toStrictEqual(new CircularArray(4, 4, 5, 6, 7)) + circularArray = new CircularArray(100) + arrayLength = circularArray.push(1, 2, 3, 4, 5) + expect(arrayLength).toBe(5) + expect(circularArray.size).toBe(100) + expect(circularArray.length).toBe(5) + expect(circularArray).toStrictEqual(new CircularArray(100, 1, 2, 3, 4, 5)) + }) await it('Verify that circular array splice works as intended', () => { - let circularArray = new CircularArray(1000, 1, 2, 3, 4, 5); - let deletedItems = circularArray.splice(2); - expect(deletedItems).toStrictEqual(new CircularArray(3, 3, 4, 5)); - expect(circularArray.length).toBe(2); - expect(circularArray).toStrictEqual(new CircularArray(1000, 1, 2)); - circularArray = new CircularArray(1000, 1, 2, 3, 4, 5); - deletedItems = circularArray.splice(2, 1); - expect(deletedItems).toStrictEqual(new CircularArray(1, 3)); - expect(circularArray.length).toBe(4); - expect(circularArray).toStrictEqual(new CircularArray(1000, 1, 2, 4, 5)); - circularArray = new CircularArray(4, 1, 2, 3, 4); - deletedItems = circularArray.splice(2, 1, 5, 6); - expect(deletedItems).toStrictEqual(new CircularArray(2, 3, 1)); - expect(circularArray.length).toBe(4); - expect(circularArray).toStrictEqual(new CircularArray(4, 2, 5, 6, 4)); - }); + let circularArray = new CircularArray(1000, 1, 2, 3, 4, 5) + let deletedItems = circularArray.splice(2) + expect(deletedItems).toStrictEqual(new CircularArray(3, 3, 4, 5)) + expect(circularArray.length).toBe(2) + expect(circularArray).toStrictEqual(new CircularArray(1000, 1, 2)) + circularArray = new CircularArray(1000, 1, 2, 3, 4, 5) + deletedItems = circularArray.splice(2, 1) + expect(deletedItems).toStrictEqual(new CircularArray(1, 3)) + expect(circularArray.length).toBe(4) + expect(circularArray).toStrictEqual(new CircularArray(1000, 1, 2, 4, 5)) + circularArray = new CircularArray(4, 1, 2, 3, 4) + deletedItems = circularArray.splice(2, 1, 5, 6) + expect(deletedItems).toStrictEqual(new CircularArray(2, 3, 1)) + expect(circularArray.length).toBe(4) + expect(circularArray).toStrictEqual(new CircularArray(4, 2, 5, 6, 4)) + }) await it('Verify that circular array concat works as intended', () => { - let circularArray = new CircularArray(5, 1, 2, 3, 4, 5); - circularArray = circularArray.concat(6, 7); - expect(circularArray.length).toBe(5); - expect(circularArray).toStrictEqual(new CircularArray(5, 3, 4, 5, 6, 7)); - circularArray = new CircularArray(1); - circularArray = circularArray.concat(6, 7); - expect(circularArray.length).toBe(1); - expect(circularArray).toStrictEqual(new CircularArray(1, 7)); - }); + let circularArray = new CircularArray(5, 1, 2, 3, 4, 5) + circularArray = circularArray.concat(6, 7) + expect(circularArray.length).toBe(5) + expect(circularArray).toStrictEqual(new CircularArray(5, 3, 4, 5, 6, 7)) + circularArray = new CircularArray(1) + circularArray = circularArray.concat(6, 7) + expect(circularArray.length).toBe(1) + expect(circularArray).toStrictEqual(new CircularArray(1, 7)) + }) await it('Verify that circular array unshift works as intended', () => { - let circularArray = new CircularArray(5, 1, 2, 3, 4, 5); - let arrayLength = circularArray.unshift(6, 7); - expect(arrayLength).toBe(5); - expect(circularArray.length).toBe(5); - expect(circularArray).toStrictEqual(new CircularArray(5, 6, 7, 1, 2, 3)); - circularArray = new CircularArray(1); - arrayLength = circularArray.unshift(6, 7); - expect(arrayLength).toBe(1); - expect(circularArray.length).toBe(1); - expect(circularArray).toStrictEqual(new CircularArray(1, 6)); - }); + let circularArray = new CircularArray(5, 1, 2, 3, 4, 5) + let arrayLength = circularArray.unshift(6, 7) + expect(arrayLength).toBe(5) + expect(circularArray.length).toBe(5) + expect(circularArray).toStrictEqual(new CircularArray(5, 6, 7, 1, 2, 3)) + circularArray = new CircularArray(1) + arrayLength = circularArray.unshift(6, 7) + expect(arrayLength).toBe(1) + expect(circularArray.length).toBe(1) + expect(circularArray).toStrictEqual(new CircularArray(1, 6)) + }) await it('Verify that circular array resize works as intended', () => { - expect(() => new CircularArray().resize(0.25)).toThrow( - new TypeError('Invalid circular array size: 0.25 is not a safe integer'), - ); - expect(() => new CircularArray().resize(-1)).toThrow( - new RangeError('Invalid circular array size: -1 < 0'), - ); - expect(() => new CircularArray().resize(Number.MAX_SAFE_INTEGER + 1)).toThrow( + expect(() => { + new CircularArray().resize(0.25) + }).toThrow(new TypeError('Invalid circular array size: 0.25 is not a safe integer')) + expect(() => { + new CircularArray().resize(-1) + }).toThrow(new RangeError('Invalid circular array size: -1 < 0')) + expect(() => { + new CircularArray().resize(Number.MAX_SAFE_INTEGER + 1) + }).toThrow( new TypeError( - `Invalid circular array size: ${Number.MAX_SAFE_INTEGER + 1} is not a safe integer`, - ), - ); - let circularArray = new CircularArray(5, 1, 2, 3, 4, 5); - circularArray.resize(0); - expect(circularArray.size).toBe(0); - expect(circularArray).toStrictEqual(new CircularArray(0)); - circularArray = new CircularArray(5, 1, 2, 3, 4, 5); - circularArray.resize(1); - expect(circularArray.size).toBe(1); - expect(circularArray).toStrictEqual(new CircularArray(1, 1)); - circularArray = new CircularArray(5, 1, 2, 3, 4, 5); - circularArray.resize(3); - expect(circularArray.size).toBe(3); - expect(circularArray).toStrictEqual(new CircularArray(3, 1, 2, 3)); - circularArray = new CircularArray(5, 1, 2, 3, 4, 5); - circularArray.resize(8); - expect(circularArray.size).toBe(8); - expect(circularArray).toStrictEqual(new CircularArray(8, 1, 2, 3, 4, 5)); - }); -}); + `Invalid circular array size: ${Number.MAX_SAFE_INTEGER + 1} is not a safe integer` + ) + ) + let circularArray = new CircularArray(5, 1, 2, 3, 4, 5) + circularArray.resize(0) + expect(circularArray.size).toBe(0) + expect(circularArray).toStrictEqual(new CircularArray(0)) + circularArray = new CircularArray(5, 1, 2, 3, 4, 5) + circularArray.resize(1) + expect(circularArray.size).toBe(1) + expect(circularArray).toStrictEqual(new CircularArray(1, 1)) + circularArray = new CircularArray(5, 1, 2, 3, 4, 5) + circularArray.resize(3) + expect(circularArray.size).toBe(3) + expect(circularArray).toStrictEqual(new CircularArray(3, 1, 2, 3)) + circularArray = new CircularArray(5, 1, 2, 3, 4, 5) + circularArray.resize(8) + expect(circularArray.size).toBe(8) + expect(circularArray).toStrictEqual(new CircularArray(8, 1, 2, 3, 4, 5)) + }) +}) diff --git a/tests/utils/ElectricUtils.test.ts b/tests/utils/ElectricUtils.test.ts index d0d3b72b..3b025b2e 100644 --- a/tests/utils/ElectricUtils.test.ts +++ b/tests/utils/ElectricUtils.test.ts @@ -1,29 +1,29 @@ -import { describe, it } from 'node:test'; +import { describe, it } from 'node:test' -import { expect } from 'expect'; +import { expect } from 'expect' -import { ACElectricUtils, DCElectricUtils } from '../../src/utils/ElectricUtils.js'; +import { ACElectricUtils, DCElectricUtils } from '../../src/utils/ElectricUtils.js' await describe('ElectricUtils test suite', async () => { await it('Verify DCElectricUtils.power()', () => { - expect(DCElectricUtils.power(230, 1)).toBe(230); - }); + expect(DCElectricUtils.power(230, 1)).toBe(230) + }) await it('Verify DCElectricUtils.amperage()', () => { - expect(DCElectricUtils.amperage(1, 230)).toBe(0); - }); + expect(DCElectricUtils.amperage(1, 230)).toBe(0) + }) await it('Verify ACElectricUtils.powerTotal()', () => { - expect(ACElectricUtils.powerTotal(3, 230, 1)).toBe(690); - }); + expect(ACElectricUtils.powerTotal(3, 230, 1)).toBe(690) + }) await it('Verify ACElectricUtils.powerPerPhase()', () => { - expect(ACElectricUtils.powerPerPhase(230, 1)).toBe(230); - }); + expect(ACElectricUtils.powerPerPhase(230, 1)).toBe(230) + }) await it('Verify ACElectricUtils.amperageTotal()', () => { - expect(ACElectricUtils.amperageTotal(3, 1)).toBe(3); - }); + expect(ACElectricUtils.amperageTotal(3, 1)).toBe(3) + }) await it('Verify ACElectricUtils.amperageTotalFromPower()', () => { - expect(ACElectricUtils.amperageTotalFromPower(690, 230)).toBe(3); - }); + expect(ACElectricUtils.amperageTotalFromPower(690, 230)).toBe(3) + }) await it('Verify ACElectricUtils.amperagePerPhaseFromPower()', () => { - expect(ACElectricUtils.amperagePerPhaseFromPower(3, 690, 230)).toBe(1); - }); -}); + expect(ACElectricUtils.amperagePerPhaseFromPower(3, 690, 230)).toBe(1) + }) +}) diff --git a/tests/utils/StatisticUtils.test.ts b/tests/utils/StatisticUtils.test.ts index a9e61b2e..78af993c 100644 --- a/tests/utils/StatisticUtils.test.ts +++ b/tests/utils/StatisticUtils.test.ts @@ -1,38 +1,38 @@ -import { describe, it } from 'node:test'; +import { describe, it } from 'node:test' -import { expect } from 'expect'; +import { expect } from 'expect' -import { average, median, nthPercentile, stdDeviation } from '../../src/utils/StatisticUtils.js'; +import { average, median, nthPercentile, stdDeviation } from '../../src/utils/StatisticUtils.js' await describe('StatisticUtils test suite', async () => { await it('Verify average()', () => { - expect(average([])).toBe(0); - expect(average([0.08])).toBe(0.08); - expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.1642857142857146); - expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.8533333333333335); - }); + expect(average([])).toBe(0) + expect(average([0.08])).toBe(0.08) + expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.1642857142857146) + expect(average([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.8533333333333335) + }) await it('Verify median()', () => { - expect(median([])).toBe(0); - expect(median([0.08])).toBe(0.08); - expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.05); - expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.535); - }); + expect(median([])).toBe(0) + expect(median([0.08])).toBe(0.08) + expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(3.05) + expect(median([0.25, 4.75, 3.05, 6.04, 1.01, 2.02])).toBe(2.535) + }) await it('Verify nthPercentile()', () => { - expect(nthPercentile([], 25)).toBe(0); - expect(nthPercentile([0.08], 50)).toBe(0.08); - const array0 = [0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03]; - expect(nthPercentile(array0, 0)).toBe(0.25); - expect(nthPercentile(array0, 50)).toBe(3.05); - expect(nthPercentile(array0, 80)).toBe(4.974); - expect(nthPercentile(array0, 85)).toBe(5.131); - expect(nthPercentile(array0, 90)).toBe(5.434); - expect(nthPercentile(array0, 95)).toBe(5.736999999999999); - expect(nthPercentile(array0, 100)).toBe(6.04); - }); + expect(nthPercentile([], 25)).toBe(0) + expect(nthPercentile([0.08], 50)).toBe(0.08) + const array0 = [0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03] + expect(nthPercentile(array0, 0)).toBe(0.25) + expect(nthPercentile(array0, 50)).toBe(3.05) + expect(nthPercentile(array0, 80)).toBe(4.974) + expect(nthPercentile(array0, 85)).toBe(5.131) + expect(nthPercentile(array0, 90)).toBe(5.434) + expect(nthPercentile(array0, 95)).toBe(5.736999999999999) + expect(nthPercentile(array0, 100)).toBe(6.04) + }) await it('Verify stdDeviation()', () => { - expect(stdDeviation([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(2.1879050645374383); - }); -}); + expect(stdDeviation([0.25, 4.75, 3.05, 6.04, 1.01, 2.02, 5.03])).toBe(2.1879050645374383) + }) +}) diff --git a/tests/utils/Utils.test.ts b/tests/utils/Utils.test.ts index 87777df3..bbb84706 100644 --- a/tests/utils/Utils.test.ts +++ b/tests/utils/Utils.test.ts @@ -1,9 +1,9 @@ -import { describe, it } from 'node:test'; +import { describe, it } from 'node:test' -import { hoursToMilliseconds, hoursToSeconds } from 'date-fns'; -import { expect } from 'expect'; +import { hoursToMilliseconds, hoursToSeconds } from 'date-fns' +import { expect } from 'expect' -import { Constants } from '../../src/utils/Constants.js'; +import { Constants } from '../../src/utils/Constants.js' import { cloneObject, convertToBoolean, @@ -34,440 +34,438 @@ import { roundTo, secureRandom, sleep, - validateUUID, -} from '../../src/utils/Utils.js'; + validateUUID +} from '../../src/utils/Utils.js' await describe('Utils test suite', async () => { await it('Verify generateUUID()/validateUUID()', () => { - const uuid = generateUUID(); - expect(uuid).toBeDefined(); - expect(uuid.length).toEqual(36); - expect(validateUUID(uuid)).toBe(true); - expect(validateUUID('abcdef00-0000-4000-0000-000000000000')).toBe(true); - expect(validateUUID('')).toBe(false); + const uuid = generateUUID() + expect(uuid).toBeDefined() + expect(uuid.length).toEqual(36) + expect(validateUUID(uuid)).toBe(true) + expect(validateUUID('abcdef00-0000-4000-0000-000000000000')).toBe(true) + expect(validateUUID('')).toBe(false) // Shall invalidate Nil UUID - expect(validateUUID('00000000-0000-0000-0000-000000000000')).toBe(false); - expect(validateUUID('987FBC9-4BED-3078-CF07A-9141BA07C9F3')).toBe(false); - }); + expect(validateUUID('00000000-0000-0000-0000-000000000000')).toBe(false) + expect(validateUUID('987FBC9-4BED-3078-CF07A-9141BA07C9F3')).toBe(false) + }) await it('Verify sleep()', async () => { - const start = performance.now(); - await sleep(1000); - const stop = performance.now(); - expect(stop - start).toBeGreaterThanOrEqual(1000); - }); + const start = performance.now() + await sleep(1000) + const stop = performance.now() + expect(stop - start).toBeGreaterThanOrEqual(1000) + }) await it('Verify formatDurationMilliSeconds()', () => { - expect(formatDurationMilliSeconds(0)).toBe('0 seconds'); - expect(formatDurationMilliSeconds(900)).toBe('0 seconds'); - expect(formatDurationMilliSeconds(1000)).toBe('1 second'); - expect(formatDurationMilliSeconds(hoursToMilliseconds(4380))).toBe('182 days 12 hours'); - }); + expect(formatDurationMilliSeconds(0)).toBe('0 seconds') + expect(formatDurationMilliSeconds(900)).toBe('0 seconds') + expect(formatDurationMilliSeconds(1000)).toBe('1 second') + expect(formatDurationMilliSeconds(hoursToMilliseconds(4380))).toBe('182 days 12 hours') + }) await it('Verify formatDurationSeconds()', () => { - expect(formatDurationSeconds(0)).toBe('0 seconds'); - expect(formatDurationSeconds(0.9)).toBe('0 seconds'); - expect(formatDurationSeconds(1)).toBe('1 second'); - expect(formatDurationSeconds(hoursToSeconds(4380))).toBe('182 days 12 hours'); - }); + expect(formatDurationSeconds(0)).toBe('0 seconds') + expect(formatDurationSeconds(0.9)).toBe('0 seconds') + expect(formatDurationSeconds(1)).toBe('1 second') + expect(formatDurationSeconds(hoursToSeconds(4380))).toBe('182 days 12 hours') + }) await it('Verify isValidTime()', () => { - expect(isValidTime(undefined)).toBe(false); - expect(isValidTime(null)).toBe(false); - expect(isValidTime('')).toBe(false); - expect(isValidTime({})).toBe(false); - expect(isValidTime([])).toBe(false); - expect(isValidTime(new Map())).toBe(false); - expect(isValidTime(new Set())).toBe(false); - expect(isValidTime(new WeakMap())).toBe(false); - expect(isValidTime(new WeakSet())).toBe(false); - expect(isValidTime(-1)).toBe(true); - expect(isValidTime(0)).toBe(true); - expect(isValidTime(1)).toBe(true); - expect(isValidTime(-0.5)).toBe(true); - expect(isValidTime(0.5)).toBe(true); - expect(isValidTime(new Date())).toBe(true); - }); + expect(isValidTime(undefined)).toBe(false) + expect(isValidTime(null)).toBe(false) + expect(isValidTime('')).toBe(false) + expect(isValidTime({})).toBe(false) + expect(isValidTime([])).toBe(false) + expect(isValidTime(new Map())).toBe(false) + expect(isValidTime(new Set())).toBe(false) + expect(isValidTime(new WeakMap())).toBe(false) + expect(isValidTime(new WeakSet())).toBe(false) + expect(isValidTime(-1)).toBe(true) + expect(isValidTime(0)).toBe(true) + expect(isValidTime(1)).toBe(true) + expect(isValidTime(-0.5)).toBe(true) + expect(isValidTime(0.5)).toBe(true) + expect(isValidTime(new Date())).toBe(true) + }) await it('Verify convertToDate()', () => { - expect(convertToDate(undefined)).toBe(undefined); - expect(convertToDate(null)).toBe(null); - expect(() => convertToDate('')).toThrow(new Error("Cannot convert to date: ''")); - expect(() => convertToDate('00:70:61')).toThrow( - new Error("Cannot convert to date: '00:70:61'"), - ); - expect(convertToDate(0)).toStrictEqual(new Date('1970-01-01T00:00:00.000Z')); - expect(convertToDate(-1)).toStrictEqual(new Date('1969-12-31T23:59:59.999Z')); - const dateStr = '2020-01-01T00:00:00.000Z'; - let date = convertToDate(dateStr); - expect(date).toBeInstanceOf(Date); - expect(date).toStrictEqual(new Date(dateStr)); - date = convertToDate(new Date(dateStr)); - expect(date).toBeInstanceOf(Date); - expect(date).toStrictEqual(new Date(dateStr)); - }); + expect(convertToDate(undefined)).toBe(undefined) + expect(convertToDate(null)).toBe(null) + expect(() => convertToDate('')).toThrow(new Error("Cannot convert to date: ''")) + expect(() => convertToDate('00:70:61')).toThrow(new Error("Cannot convert to date: '00:70:61'")) + expect(convertToDate(0)).toStrictEqual(new Date('1970-01-01T00:00:00.000Z')) + expect(convertToDate(-1)).toStrictEqual(new Date('1969-12-31T23:59:59.999Z')) + const dateStr = '2020-01-01T00:00:00.000Z' + let date = convertToDate(dateStr) + expect(date).toBeInstanceOf(Date) + expect(date).toStrictEqual(new Date(dateStr)) + date = convertToDate(new Date(dateStr)) + expect(date).toBeInstanceOf(Date) + expect(date).toStrictEqual(new Date(dateStr)) + }) await it('Verify convertToInt()', () => { - expect(convertToInt(undefined)).toBe(0); - expect(convertToInt(null)).toBe(0); - expect(convertToInt(0)).toBe(0); - const randomInteger = getRandomInteger(); - expect(convertToInt(randomInteger)).toEqual(randomInteger); - expect(convertToInt('-1')).toBe(-1); - expect(convertToInt('1')).toBe(1); - expect(convertToInt('1.1')).toBe(1); - expect(convertToInt('1.9')).toBe(1); - expect(convertToInt('1.999')).toBe(1); - expect(convertToInt(-1)).toBe(-1); - expect(convertToInt(1)).toBe(1); - expect(convertToInt(1.1)).toBe(1); - expect(convertToInt(1.9)).toBe(1); - expect(convertToInt(1.999)).toBe(1); + expect(convertToInt(undefined)).toBe(0) + expect(convertToInt(null)).toBe(0) + expect(convertToInt(0)).toBe(0) + const randomInteger = getRandomInteger() + expect(convertToInt(randomInteger)).toEqual(randomInteger) + expect(convertToInt('-1')).toBe(-1) + expect(convertToInt('1')).toBe(1) + expect(convertToInt('1.1')).toBe(1) + expect(convertToInt('1.9')).toBe(1) + expect(convertToInt('1.999')).toBe(1) + expect(convertToInt(-1)).toBe(-1) + expect(convertToInt(1)).toBe(1) + expect(convertToInt(1.1)).toBe(1) + expect(convertToInt(1.9)).toBe(1) + expect(convertToInt(1.999)).toBe(1) expect(() => { - convertToInt('NaN'); - }).toThrow("Cannot convert to integer: 'NaN'"); - }); + convertToInt('NaN') + }).toThrow("Cannot convert to integer: 'NaN'") + }) await it('Verify convertToFloat()', () => { - expect(convertToFloat(undefined)).toBe(0); - expect(convertToFloat(null)).toBe(0); - expect(convertToFloat(0)).toBe(0); - const randomFloat = getRandomFloat(); - expect(convertToFloat(randomFloat)).toEqual(randomFloat); - expect(convertToFloat('-1')).toBe(-1); - expect(convertToFloat('1')).toBe(1); - expect(convertToFloat('1.1')).toBe(1.1); - expect(convertToFloat('1.9')).toBe(1.9); - expect(convertToFloat('1.999')).toBe(1.999); - expect(convertToFloat(-1)).toBe(-1); - expect(convertToFloat(1)).toBe(1); - expect(convertToFloat(1.1)).toBe(1.1); - expect(convertToFloat(1.9)).toBe(1.9); - expect(convertToFloat(1.999)).toBe(1.999); + expect(convertToFloat(undefined)).toBe(0) + expect(convertToFloat(null)).toBe(0) + expect(convertToFloat(0)).toBe(0) + const randomFloat = getRandomFloat() + expect(convertToFloat(randomFloat)).toEqual(randomFloat) + expect(convertToFloat('-1')).toBe(-1) + expect(convertToFloat('1')).toBe(1) + expect(convertToFloat('1.1')).toBe(1.1) + expect(convertToFloat('1.9')).toBe(1.9) + expect(convertToFloat('1.999')).toBe(1.999) + expect(convertToFloat(-1)).toBe(-1) + expect(convertToFloat(1)).toBe(1) + expect(convertToFloat(1.1)).toBe(1.1) + expect(convertToFloat(1.9)).toBe(1.9) + expect(convertToFloat(1.999)).toBe(1.999) expect(() => { - convertToFloat('NaN'); - }).toThrow("Cannot convert to float: 'NaN'"); - }); + convertToFloat('NaN') + }).toThrow("Cannot convert to float: 'NaN'") + }) await it('Verify convertToBoolean()', () => { - expect(convertToBoolean(undefined)).toBe(false); - expect(convertToBoolean(null)).toBe(false); - expect(convertToBoolean('true')).toBe(true); - expect(convertToBoolean('false')).toBe(false); - expect(convertToBoolean('TRUE')).toBe(true); - expect(convertToBoolean('FALSE')).toBe(false); - expect(convertToBoolean('1')).toBe(true); - expect(convertToBoolean('0')).toBe(false); - expect(convertToBoolean(1)).toBe(true); - expect(convertToBoolean(0)).toBe(false); - expect(convertToBoolean(true)).toBe(true); - expect(convertToBoolean(false)).toBe(false); - expect(convertToBoolean('')).toBe(false); - expect(convertToBoolean('NoNBoolean')).toBe(false); - }); + expect(convertToBoolean(undefined)).toBe(false) + expect(convertToBoolean(null)).toBe(false) + expect(convertToBoolean('true')).toBe(true) + expect(convertToBoolean('false')).toBe(false) + expect(convertToBoolean('TRUE')).toBe(true) + expect(convertToBoolean('FALSE')).toBe(false) + expect(convertToBoolean('1')).toBe(true) + expect(convertToBoolean('0')).toBe(false) + expect(convertToBoolean(1)).toBe(true) + expect(convertToBoolean(0)).toBe(false) + expect(convertToBoolean(true)).toBe(true) + expect(convertToBoolean(false)).toBe(false) + expect(convertToBoolean('')).toBe(false) + expect(convertToBoolean('NoNBoolean')).toBe(false) + }) await it('Verify secureRandom()', () => { - const random = secureRandom(); - expect(typeof random === 'number').toBe(true); - expect(random).toBeGreaterThanOrEqual(0); - expect(random).toBeLessThan(1); - }); + const random = secureRandom() + expect(typeof random === 'number').toBe(true) + expect(random).toBeGreaterThanOrEqual(0) + expect(random).toBeLessThan(1) + }) await it('Verify getRandomInteger()', () => { - let randomInteger = getRandomInteger(); - expect(Number.isSafeInteger(randomInteger)).toBe(true); - expect(randomInteger).toBeGreaterThanOrEqual(0); - expect(randomInteger).toBeLessThanOrEqual(Constants.MAX_RANDOM_INTEGER); - expect(randomInteger).not.toEqual(getRandomInteger()); - randomInteger = getRandomInteger(0, -Constants.MAX_RANDOM_INTEGER); - expect(randomInteger).toBeGreaterThanOrEqual(-Constants.MAX_RANDOM_INTEGER); - expect(randomInteger).toBeLessThanOrEqual(0); + let randomInteger = getRandomInteger() + expect(Number.isSafeInteger(randomInteger)).toBe(true) + expect(randomInteger).toBeGreaterThanOrEqual(0) + expect(randomInteger).toBeLessThanOrEqual(Constants.MAX_RANDOM_INTEGER) + expect(randomInteger).not.toEqual(getRandomInteger()) + randomInteger = getRandomInteger(0, -Constants.MAX_RANDOM_INTEGER) + expect(randomInteger).toBeGreaterThanOrEqual(-Constants.MAX_RANDOM_INTEGER) + expect(randomInteger).toBeLessThanOrEqual(0) expect(() => getRandomInteger(0, 1)).toThrow( - 'The value of "max" is out of range. It must be greater than the value of "min" (1). Received 1', - ); + 'The value of "max" is out of range. It must be greater than the value of "min" (1). Received 1' + ) expect(() => getRandomInteger(-1)).toThrow( - 'The value of "max" is out of range. It must be greater than the value of "min" (0). Received 0', - ); + 'The value of "max" is out of range. It must be greater than the value of "min" (0). Received 0' + ) expect(() => getRandomInteger(Constants.MAX_RANDOM_INTEGER + 1)).toThrow( `The value of "max" is out of range. It must be <= ${ Constants.MAX_RANDOM_INTEGER + 1 - }. Received 281_474_976_710_656`, - ); - randomInteger = getRandomInteger(2, 1); - expect(randomInteger).toBeGreaterThanOrEqual(1); - expect(randomInteger).toBeLessThanOrEqual(2); - const maximum = 2.2, - minimum = 1.1; - randomInteger = getRandomInteger(maximum, minimum); - expect(randomInteger).toBeLessThanOrEqual(Math.floor(maximum)); - expect(randomInteger).toBeGreaterThanOrEqual(Math.ceil(minimum)); - }); + }. Received 281_474_976_710_656` + ) + randomInteger = getRandomInteger(2, 1) + expect(randomInteger).toBeGreaterThanOrEqual(1) + expect(randomInteger).toBeLessThanOrEqual(2) + const maximum = 2.2 + const minimum = 1.1 + randomInteger = getRandomInteger(maximum, minimum) + expect(randomInteger).toBeLessThanOrEqual(Math.floor(maximum)) + expect(randomInteger).toBeGreaterThanOrEqual(Math.ceil(minimum)) + }) await it('Verify roundTo()', () => { - expect(roundTo(0, 2)).toBe(0); - expect(roundTo(0.5, 0)).toBe(1); - expect(roundTo(0.5, 2)).toBe(0.5); - expect(roundTo(-0.5, 0)).toBe(-1); - expect(roundTo(-0.5, 2)).toBe(-0.5); - expect(roundTo(1.005, 0)).toBe(1); - expect(roundTo(1.005, 2)).toBe(1.01); - expect(roundTo(2.175, 2)).toBe(2.18); - expect(roundTo(5.015, 2)).toBe(5.02); - expect(roundTo(-1.005, 2)).toBe(-1.01); - expect(roundTo(-2.175, 2)).toBe(-2.18); - expect(roundTo(-5.015, 2)).toBe(-5.02); - }); + expect(roundTo(0, 2)).toBe(0) + expect(roundTo(0.5, 0)).toBe(1) + expect(roundTo(0.5, 2)).toBe(0.5) + expect(roundTo(-0.5, 0)).toBe(-1) + expect(roundTo(-0.5, 2)).toBe(-0.5) + expect(roundTo(1.005, 0)).toBe(1) + expect(roundTo(1.005, 2)).toBe(1.01) + expect(roundTo(2.175, 2)).toBe(2.18) + expect(roundTo(5.015, 2)).toBe(5.02) + expect(roundTo(-1.005, 2)).toBe(-1.01) + expect(roundTo(-2.175, 2)).toBe(-2.18) + expect(roundTo(-5.015, 2)).toBe(-5.02) + }) await it('Verify getRandomFloat()', () => { - let randomFloat = getRandomFloat(); - expect(typeof randomFloat === 'number').toBe(true); - expect(randomFloat).toBeGreaterThanOrEqual(0); - expect(randomFloat).toBeLessThanOrEqual(Number.MAX_VALUE); - expect(randomFloat).not.toEqual(getRandomFloat()); - expect(() => getRandomFloat(0, 1)).toThrow(new RangeError('Invalid interval')); + let randomFloat = getRandomFloat() + expect(typeof randomFloat === 'number').toBe(true) + expect(randomFloat).toBeGreaterThanOrEqual(0) + expect(randomFloat).toBeLessThanOrEqual(Number.MAX_VALUE) + expect(randomFloat).not.toEqual(getRandomFloat()) + expect(() => getRandomFloat(0, 1)).toThrow(new RangeError('Invalid interval')) expect(() => getRandomFloat(Number.MAX_VALUE, -Number.MAX_VALUE)).toThrow( - new RangeError('Invalid interval'), - ); - randomFloat = getRandomFloat(0, -Number.MAX_VALUE); - expect(randomFloat).toBeGreaterThanOrEqual(-Number.MAX_VALUE); - expect(randomFloat).toBeLessThanOrEqual(0); - }); + new RangeError('Invalid interval') + ) + randomFloat = getRandomFloat(0, -Number.MAX_VALUE) + expect(randomFloat).toBeGreaterThanOrEqual(-Number.MAX_VALUE) + expect(randomFloat).toBeLessThanOrEqual(0) + }) await it('Verify extractTimeSeriesValues()', () => { - expect(extractTimeSeriesValues([])).toEqual([]); - expect(extractTimeSeriesValues([{ timestamp: Date.now(), value: 1.1 }])).toEqual([1.1]); + expect(extractTimeSeriesValues([])).toEqual([]) + expect(extractTimeSeriesValues([{ timestamp: Date.now(), value: 1.1 }])).toEqual([1.1]) expect( extractTimeSeriesValues([ { timestamp: Date.now(), value: 1.1 }, - { timestamp: Date.now(), value: 2.2 }, - ]), - ).toEqual([1.1, 2.2]); - }); + { timestamp: Date.now(), value: 2.2 } + ]) + ).toEqual([1.1, 2.2]) + }) await it('Verify isObject()', () => { - expect(isObject('test')).toBe(false); - expect(isObject(undefined)).toBe(false); - expect(isObject(null)).toBe(false); - expect(isObject(0)).toBe(false); - expect(isObject([])).toBe(false); - expect(isObject([0, 1])).toBe(false); - expect(isObject(['0', '1'])).toBe(false); - expect(isObject({})).toBe(true); - expect(isObject({ 1: 1 })).toBe(true); - expect(isObject({ '1': '1' })).toBe(true); - expect(isObject(new Map())).toBe(true); - expect(isObject(new Set())).toBe(true); - expect(isObject(new WeakMap())).toBe(true); - expect(isObject(new WeakSet())).toBe(true); - }); + expect(isObject('test')).toBe(false) + expect(isObject(undefined)).toBe(false) + expect(isObject(null)).toBe(false) + expect(isObject(0)).toBe(false) + expect(isObject([])).toBe(false) + expect(isObject([0, 1])).toBe(false) + expect(isObject(['0', '1'])).toBe(false) + expect(isObject({})).toBe(true) + expect(isObject({ 1: 1 })).toBe(true) + expect(isObject({ 1: '1' })).toBe(true) + expect(isObject(new Map())).toBe(true) + expect(isObject(new Set())).toBe(true) + expect(isObject(new WeakMap())).toBe(true) + expect(isObject(new WeakSet())).toBe(true) + }) await it('Verify cloneObject()', () => { - const obj = { 1: 1 }; - expect(cloneObject(obj)).toStrictEqual(obj); - expect(cloneObject(obj) === obj).toBe(false); - const nestedObj = { 1: obj, 2: obj }; - expect(cloneObject(nestedObj)).toStrictEqual(nestedObj); - expect(cloneObject(nestedObj) === nestedObj).toBe(false); - const array = [1, 2]; - expect(cloneObject(array)).toStrictEqual(array); - expect(cloneObject(array) === array).toBe(false); - const objArray = [obj, obj]; - expect(cloneObject(objArray)).toStrictEqual(objArray); - expect(cloneObject(objArray) === objArray).toBe(false); - const date = new Date(); - expect(cloneObject(date)).toStrictEqual(date); - expect(cloneObject(date) === date).toBe(false); - const map = new Map([['1', '2']]); - expect(cloneObject(map)).toStrictEqual({}); - const set = new Set(['1']); - expect(cloneObject(set)).toStrictEqual({}); + const obj = { 1: 1 } + expect(cloneObject(obj)).toStrictEqual(obj) + expect(cloneObject(obj) === obj).toBe(false) + const nestedObj = { 1: obj, 2: obj } + expect(cloneObject(nestedObj)).toStrictEqual(nestedObj) + expect(cloneObject(nestedObj) === nestedObj).toBe(false) + const array = [1, 2] + expect(cloneObject(array)).toStrictEqual(array) + expect(cloneObject(array) === array).toBe(false) + const objArray = [obj, obj] + expect(cloneObject(objArray)).toStrictEqual(objArray) + expect(cloneObject(objArray) === objArray).toBe(false) + const date = new Date() + expect(cloneObject(date)).toStrictEqual(date) + expect(cloneObject(date) === date).toBe(false) + const map = new Map([['1', '2']]) + expect(cloneObject(map)).toStrictEqual({}) + const set = new Set(['1']) + expect(cloneObject(set)).toStrictEqual({}) // The URL object seems to have not enumerable properties - const url = new URL('https://domain.tld'); - expect(cloneObject(url)).toStrictEqual({}); - const weakMap = new WeakMap([[{ 1: 1 }, { 2: 2 }]]); - expect(cloneObject(weakMap)).toStrictEqual({}); - const weakSet = new WeakSet([{ 1: 1 }, { 2: 2 }]); - expect(cloneObject(weakSet)).toStrictEqual({}); - }); + const url = new URL('https://domain.tld') + expect(cloneObject(url)).toStrictEqual({}) + const weakMap = new WeakMap([[{ 1: 1 }, { 2: 2 }]]) + expect(cloneObject(weakMap)).toStrictEqual({}) + const weakSet = new WeakSet([{ 1: 1 }, { 2: 2 }]) + expect(cloneObject(weakSet)).toStrictEqual({}) + }) await it('Verify hasOwnProp()', () => { - expect(hasOwnProp('test', '')).toBe(false); - expect(hasOwnProp(undefined, '')).toBe(false); - expect(hasOwnProp(null, '')).toBe(false); - expect(hasOwnProp([], '')).toBe(false); - expect(hasOwnProp({}, '')).toBe(false); - expect(hasOwnProp({ 1: 1 }, 1)).toBe(true); - expect(hasOwnProp({ 1: 1 }, '1')).toBe(true); - expect(hasOwnProp({ 1: 1 }, 2)).toBe(false); - expect(hasOwnProp({ 1: 1 }, '2')).toBe(false); - expect(hasOwnProp({ '1': '1' }, '1')).toBe(true); - expect(hasOwnProp({ '1': '1' }, 1)).toBe(true); - expect(hasOwnProp({ '1': '1' }, '2')).toBe(false); - expect(hasOwnProp({ '1': '1' }, 2)).toBe(false); - }); + expect(hasOwnProp('test', '')).toBe(false) + expect(hasOwnProp(undefined, '')).toBe(false) + expect(hasOwnProp(null, '')).toBe(false) + expect(hasOwnProp([], '')).toBe(false) + expect(hasOwnProp({}, '')).toBe(false) + expect(hasOwnProp({ 1: 1 }, 1)).toBe(true) + expect(hasOwnProp({ 1: 1 }, '1')).toBe(true) + expect(hasOwnProp({ 1: 1 }, 2)).toBe(false) + expect(hasOwnProp({ 1: 1 }, '2')).toBe(false) + expect(hasOwnProp({ 1: '1' }, '1')).toBe(true) + expect(hasOwnProp({ 1: '1' }, 1)).toBe(true) + expect(hasOwnProp({ 1: '1' }, '2')).toBe(false) + expect(hasOwnProp({ 1: '1' }, 2)).toBe(false) + }) await it('Verify isIterable()', () => { - expect(isIterable('')).toBe(true); - expect(isIterable(' ')).toBe(true); - expect(isIterable('test')).toBe(true); - expect(isIterable(undefined)).toBe(false); - expect(isIterable(null)).toBe(false); - expect(isIterable(0)).toBe(false); - expect(isIterable([0, 1])).toBe(true); - expect(isIterable({ 1: 1 })).toBe(false); - expect(isIterable(new Map())).toBe(true); - expect(isIterable(new Set())).toBe(true); - expect(isIterable(new WeakMap())).toBe(false); - expect(isIterable(new WeakSet())).toBe(false); - }); + expect(isIterable('')).toBe(true) + expect(isIterable(' ')).toBe(true) + expect(isIterable('test')).toBe(true) + expect(isIterable(undefined)).toBe(false) + expect(isIterable(null)).toBe(false) + expect(isIterable(0)).toBe(false) + expect(isIterable([0, 1])).toBe(true) + expect(isIterable({ 1: 1 })).toBe(false) + expect(isIterable(new Map())).toBe(true) + expect(isIterable(new Set())).toBe(true) + expect(isIterable(new WeakMap())).toBe(false) + expect(isIterable(new WeakSet())).toBe(false) + }) await it('Verify isEmptyString()', () => { - expect(isEmptyString('')).toBe(true); - expect(isEmptyString(' ')).toBe(true); - expect(isEmptyString(' ')).toBe(true); - expect(isEmptyString('test')).toBe(false); - expect(isEmptyString(' test')).toBe(false); - expect(isEmptyString('test ')).toBe(false); - expect(isEmptyString(undefined)).toBe(true); - expect(isEmptyString(null)).toBe(true); - expect(isEmptyString(0)).toBe(false); - expect(isEmptyString({})).toBe(false); - expect(isEmptyString([])).toBe(false); - expect(isEmptyString(new Map())).toBe(false); - expect(isEmptyString(new Set())).toBe(false); - expect(isEmptyString(new WeakMap())).toBe(false); - expect(isEmptyString(new WeakSet())).toBe(false); - }); + expect(isEmptyString('')).toBe(true) + expect(isEmptyString(' ')).toBe(true) + expect(isEmptyString(' ')).toBe(true) + expect(isEmptyString('test')).toBe(false) + expect(isEmptyString(' test')).toBe(false) + expect(isEmptyString('test ')).toBe(false) + expect(isEmptyString(undefined)).toBe(true) + expect(isEmptyString(null)).toBe(true) + expect(isEmptyString(0)).toBe(false) + expect(isEmptyString({})).toBe(false) + expect(isEmptyString([])).toBe(false) + expect(isEmptyString(new Map())).toBe(false) + expect(isEmptyString(new Set())).toBe(false) + expect(isEmptyString(new WeakMap())).toBe(false) + expect(isEmptyString(new WeakSet())).toBe(false) + }) await it('Verify isNotEmptyString()', () => { - expect(isNotEmptyString('')).toBe(false); - expect(isNotEmptyString(' ')).toBe(false); - expect(isNotEmptyString(' ')).toBe(false); - expect(isNotEmptyString('test')).toBe(true); - expect(isNotEmptyString(' test')).toBe(true); - expect(isNotEmptyString('test ')).toBe(true); - expect(isNotEmptyString(undefined)).toBe(false); - expect(isNotEmptyString(null)).toBe(false); - expect(isNotEmptyString(0)).toBe(false); - expect(isNotEmptyString({})).toBe(false); - expect(isNotEmptyString([])).toBe(false); - expect(isNotEmptyString(new Map())).toBe(false); - expect(isNotEmptyString(new Set())).toBe(false); - expect(isNotEmptyString(new WeakMap())).toBe(false); - expect(isNotEmptyString(new WeakSet())).toBe(false); - }); + expect(isNotEmptyString('')).toBe(false) + expect(isNotEmptyString(' ')).toBe(false) + expect(isNotEmptyString(' ')).toBe(false) + expect(isNotEmptyString('test')).toBe(true) + expect(isNotEmptyString(' test')).toBe(true) + expect(isNotEmptyString('test ')).toBe(true) + expect(isNotEmptyString(undefined)).toBe(false) + expect(isNotEmptyString(null)).toBe(false) + expect(isNotEmptyString(0)).toBe(false) + expect(isNotEmptyString({})).toBe(false) + expect(isNotEmptyString([])).toBe(false) + expect(isNotEmptyString(new Map())).toBe(false) + expect(isNotEmptyString(new Set())).toBe(false) + expect(isNotEmptyString(new WeakMap())).toBe(false) + expect(isNotEmptyString(new WeakSet())).toBe(false) + }) await it('Verify isUndefined()', () => { - expect(isUndefined(undefined)).toBe(true); - expect(isUndefined(null)).toBe(false); - expect(isUndefined('')).toBe(false); - expect(isUndefined(0)).toBe(false); - expect(isUndefined({})).toBe(false); - expect(isUndefined([])).toBe(false); - expect(isUndefined(new Map())).toBe(false); - expect(isUndefined(new Set())).toBe(false); - expect(isUndefined(new WeakMap())).toBe(false); - expect(isUndefined(new WeakSet())).toBe(false); - }); + expect(isUndefined(undefined)).toBe(true) + expect(isUndefined(null)).toBe(false) + expect(isUndefined('')).toBe(false) + expect(isUndefined(0)).toBe(false) + expect(isUndefined({})).toBe(false) + expect(isUndefined([])).toBe(false) + expect(isUndefined(new Map())).toBe(false) + expect(isUndefined(new Set())).toBe(false) + expect(isUndefined(new WeakMap())).toBe(false) + expect(isUndefined(new WeakSet())).toBe(false) + }) await it('Verify isNullOrUndefined()', () => { - expect(isNullOrUndefined(undefined)).toBe(true); - expect(isNullOrUndefined(null)).toBe(true); - expect(isNullOrUndefined('')).toBe(false); - expect(isNullOrUndefined(0)).toBe(false); - expect(isNullOrUndefined({})).toBe(false); - expect(isNullOrUndefined([])).toBe(false); - expect(isNullOrUndefined(new Map())).toBe(false); - expect(isNullOrUndefined(new Set())).toBe(false); - expect(isNullOrUndefined(new WeakMap())).toBe(false); - expect(isNullOrUndefined(new WeakSet())).toBe(false); - }); + expect(isNullOrUndefined(undefined)).toBe(true) + expect(isNullOrUndefined(null)).toBe(true) + expect(isNullOrUndefined('')).toBe(false) + expect(isNullOrUndefined(0)).toBe(false) + expect(isNullOrUndefined({})).toBe(false) + expect(isNullOrUndefined([])).toBe(false) + expect(isNullOrUndefined(new Map())).toBe(false) + expect(isNullOrUndefined(new Set())).toBe(false) + expect(isNullOrUndefined(new WeakMap())).toBe(false) + expect(isNullOrUndefined(new WeakSet())).toBe(false) + }) await it('Verify isEmptyArray()', () => { - expect(isEmptyArray([])).toBe(true); - expect(isEmptyArray([1, 2])).toBe(false); - expect(isEmptyArray(['1', '2'])).toBe(false); - expect(isEmptyArray(undefined)).toBe(false); - expect(isEmptyArray(null)).toBe(false); - expect(isEmptyArray('')).toBe(false); - expect(isEmptyArray('test')).toBe(false); - expect(isEmptyArray(0)).toBe(false); - expect(isEmptyArray({})).toBe(false); - expect(isEmptyArray(new Map())).toBe(false); - expect(isEmptyArray(new Set())).toBe(false); - expect(isEmptyArray(new WeakMap())).toBe(false); - expect(isEmptyArray(new WeakSet())).toBe(false); - }); + expect(isEmptyArray([])).toBe(true) + expect(isEmptyArray([1, 2])).toBe(false) + expect(isEmptyArray(['1', '2'])).toBe(false) + expect(isEmptyArray(undefined)).toBe(false) + expect(isEmptyArray(null)).toBe(false) + expect(isEmptyArray('')).toBe(false) + expect(isEmptyArray('test')).toBe(false) + expect(isEmptyArray(0)).toBe(false) + expect(isEmptyArray({})).toBe(false) + expect(isEmptyArray(new Map())).toBe(false) + expect(isEmptyArray(new Set())).toBe(false) + expect(isEmptyArray(new WeakMap())).toBe(false) + expect(isEmptyArray(new WeakSet())).toBe(false) + }) await it('Verify isNotEmptyArray()', () => { - expect(isNotEmptyArray([])).toBe(false); - expect(isNotEmptyArray([1, 2])).toBe(true); - expect(isNotEmptyArray(['1', '2'])).toBe(true); - expect(isNotEmptyArray(undefined)).toBe(false); - expect(isNotEmptyArray(null)).toBe(false); - expect(isNotEmptyArray('')).toBe(false); - expect(isNotEmptyArray('test')).toBe(false); - expect(isNotEmptyArray(0)).toBe(false); - expect(isNotEmptyArray({})).toBe(false); - expect(isNotEmptyArray(new Map())).toBe(false); - expect(isNotEmptyArray(new Set())).toBe(false); - expect(isNotEmptyArray(new WeakMap())).toBe(false); - expect(isNotEmptyArray(new WeakSet())).toBe(false); - }); + expect(isNotEmptyArray([])).toBe(false) + expect(isNotEmptyArray([1, 2])).toBe(true) + expect(isNotEmptyArray(['1', '2'])).toBe(true) + expect(isNotEmptyArray(undefined)).toBe(false) + expect(isNotEmptyArray(null)).toBe(false) + expect(isNotEmptyArray('')).toBe(false) + expect(isNotEmptyArray('test')).toBe(false) + expect(isNotEmptyArray(0)).toBe(false) + expect(isNotEmptyArray({})).toBe(false) + expect(isNotEmptyArray(new Map())).toBe(false) + expect(isNotEmptyArray(new Set())).toBe(false) + expect(isNotEmptyArray(new WeakMap())).toBe(false) + expect(isNotEmptyArray(new WeakSet())).toBe(false) + }) await it('Verify isEmptyObject()', () => { - expect(isEmptyObject({})).toBe(true); - expect(isEmptyObject({ 1: 1, 2: 2 })).toBe(false); - expect(isEmptyObject(new Map())).toBe(false); - expect(isEmptyObject(new Set())).toBe(false); - expect(isEmptyObject(new WeakMap())).toBe(false); - expect(isEmptyObject(new WeakSet())).toBe(false); - }); + expect(isEmptyObject({})).toBe(true) + expect(isEmptyObject({ 1: 1, 2: 2 })).toBe(false) + expect(isEmptyObject(new Map())).toBe(false) + expect(isEmptyObject(new Set())).toBe(false) + expect(isEmptyObject(new WeakMap())).toBe(false) + expect(isEmptyObject(new WeakSet())).toBe(false) + }) await it('Verify isArraySorted()', () => { expect( isArraySorted([], (a, b) => { - return a - b; - }), - ).toBe(true); + return a - b + }) + ).toBe(true) expect( isArraySorted([1], (a, b) => { - return a - b; - }), - ).toBe(true); - expect(isArraySorted([1, 2, 3, 4, 5], (a, b) => a - b)).toBe(true); - expect(isArraySorted([1, 2, 3, 5, 4], (a, b) => a - b)).toBe(false); - expect(isArraySorted([2, 1, 3, 4, 5], (a, b) => a - b)).toBe(false); - }); + return a - b + }) + ).toBe(true) + expect(isArraySorted([1, 2, 3, 4, 5], (a, b) => a - b)).toBe(true) + expect(isArraySorted([1, 2, 3, 5, 4], (a, b) => a - b)).toBe(false) + expect(isArraySorted([2, 1, 3, 4, 5], (a, b) => a - b)).toBe(false) + }) await it('Verify once()', () => { - let called = 0; - const fn = () => ++called; - const onceFn = once(fn, this); - const result1 = onceFn(); - expect(called).toBe(1); - expect(result1).toBe(1); - const result2 = onceFn(); - expect(called).toBe(1); - expect(result2).toBe(1); - const result3 = onceFn(); - expect(called).toBe(1); - expect(result3).toBe(1); - }); + let called = 0 + const fn = (): number => ++called + const onceFn = once(fn, this) + const result1 = onceFn() + expect(called).toBe(1) + expect(result1).toBe(1) + const result2 = onceFn() + expect(called).toBe(1) + expect(result2).toBe(1) + const result3 = onceFn() + expect(called).toBe(1) + expect(result3).toBe(1) + }) await it('Verify min()', () => { - expect(min()).toBe(Infinity); - expect(min(0, 1)).toBe(0); - expect(min(1, 0)).toBe(0); - expect(min(0, -1)).toBe(-1); - expect(min(-1, 0)).toBe(-1); - }); + expect(min()).toBe(Infinity) + expect(min(0, 1)).toBe(0) + expect(min(1, 0)).toBe(0) + expect(min(0, -1)).toBe(-1) + expect(min(-1, 0)).toBe(-1) + }) await it('Verify max()', () => { - expect(max()).toBe(-Infinity); - expect(max(0, 1)).toBe(1); - expect(max(1, 0)).toBe(1); - expect(max(0, -1)).toBe(0); - expect(max(-1, 0)).toBe(0); - }); -}); + expect(max()).toBe(-Infinity) + expect(max(0, 1)).toBe(1) + expect(max(1, 0)).toBe(1) + expect(max(0, -1)).toBe(0) + expect(max(-1, 0)).toBe(0) + }) +}) diff --git a/ui/web/.eslintrc.cjs b/ui/web/.eslintrc.cjs index d82d3c5f..3855772d 100644 --- a/ui/web/.eslintrc.cjs +++ b/ui/web/.eslintrc.cjs @@ -1,11 +1,11 @@ -const { env } = require('node:process'); -const { defineConfig } = require('eslint-define-config'); +const { env } = require('node:process') +const { defineConfig } = require('eslint-define-config') module.exports = defineConfig({ root: true, env: { - node: true, + node: true }, plugins: ['import'], @@ -15,19 +15,19 @@ module.exports = defineConfig({ 'plugin:import/recommended', 'plugin:vue/vue3-recommended', '@vue/eslint-config-prettier', - '@vue/eslint-config-typescript/recommended', + '@vue/eslint-config-typescript/recommended' ], settings: { 'import/resolver': { typescript: { - project: './tsconfig.json', - }, - }, + project: './tsconfig.json' + } + } }, parserOptions: { - ecmaVersion: 'latest', + ecmaVersion: 'latest' }, rules: { @@ -38,9 +38,9 @@ module.exports = defineConfig({ 'sort-imports': [ 'error', { - ignoreDeclarationSort: true, - }, + ignoreDeclarationSort: true + } ], - 'import/order': 'error', - }, -}); + 'import/order': 'error' + } +}) diff --git a/ui/web/.lintstagedrc.js b/ui/web/.lintstagedrc.js index 927dd55c..a8ba217a 100644 --- a/ui/web/.lintstagedrc.js +++ b/ui/web/.lintstagedrc.js @@ -1,5 +1,5 @@ export default { '*.{.css,json,md,yml,yaml,html,js,jsx,cjs,mjs,ts,tsx,cts,mts}': 'prettier --cache --write', '*.{vue,js,jsx,cjs,mjs,ts,tsx,cts,mts}': - 'eslint . --cache --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore', -}; + 'eslint . --cache --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore' +} diff --git a/ui/web/.prettierrc.json b/ui/web/.prettierrc.json index 6414c22d..6632aaeb 100644 --- a/ui/web/.prettierrc.json +++ b/ui/web/.prettierrc.json @@ -1,5 +1,7 @@ { "$schema": "https://json.schemastore.org/prettierrc", "printWidth": 100, - "singleQuote": true + "semi": false, + "singleQuote": true, + "trailingComma": "none" } diff --git a/ui/web/src/assets/config.ts b/ui/web/src/assets/config.ts index 8d40c744..cf6f3eb2 100644 --- a/ui/web/src/assets/config.ts +++ b/ui/web/src/assets/config.ts @@ -1,11 +1,11 @@ -import type { BaseConfig } from '@/types'; +import type { BaseConfig } from '@/types' const config: BaseConfig = { uiServer: { host: 'localhost', port: 8080, - protocol: 'ui0.0.1', - }, -}; + protocol: 'ui0.0.1' + } +} -export default config; +export default config diff --git a/ui/web/src/components/Modal.vue b/ui/web/src/components/Modal.vue index ab79569a..13a4c0b2 100644 --- a/ui/web/src/components/Modal.vue +++ b/ui/web/src/components/Modal.vue @@ -8,13 +8,13 @@ diff --git a/ui/web/src/components/buttons/FlatButton.vue b/ui/web/src/components/buttons/FlatButton.vue index 1d929709..0a6d6b44 100644 --- a/ui/web/src/components/buttons/FlatButton.vue +++ b/ui/web/src/components/buttons/FlatButton.vue @@ -5,7 +5,7 @@