From e7aeea18e189dd087c8f951cf77a253e2818ae90 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 4 Mar 2022 22:34:42 +0100 Subject: [PATCH] Apply prettier formating MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- .eslintrc | 99 +- .github/ISSUE_TEMPLATE/bug_report.yml | 6 +- .github/ISSUE_TEMPLATE/feature_request.yml | 6 +- .github/workflows/ci.yml | 64 +- .github/workflows/codeql-analysis.yml | 58 +- .mocharc.json | 9 +- .nycrc.json | 4 +- .vscode/launch.json | 18 +- CODE_OF_CONDUCT.md | 22 +- README.md | 172 ++-- docker/buildspec.yml | 2 +- docker/config.json | 4 +- manifest-cf-template.yml | 26 +- mikro-orm.config-template.ts | 5 +- package-lock.json | 39 + package.json | 3 + rollup.config.js | 40 +- src/assets/authorization-tags-template.json | 5 +- src/assets/config-template.json | 4 +- .../AutomaticTransactionGenerator.ts | 214 ++++- src/charging-station/Bootstrap.ts | 83 +- src/charging-station/ChargingStation.ts | 852 ++++++++++++++---- src/charging-station/ChargingStationWorker.ts | 16 +- src/charging-station/UIWebSocketServer.ts | 21 +- .../ocpp/1.6/OCPP16IncomingRequestService.ts | 665 +++++++++++--- .../ocpp/1.6/OCPP16RequestService.ts | 704 ++++++++++++--- .../ocpp/1.6/OCPP16ResponseService.ts | 400 ++++++-- .../ocpp/1.6/OCPP16ServiceUtils.ts | 97 +- .../ocpp/OCPPIncomingRequestService.ts | 28 +- .../ocpp/OCPPRequestService.ts | 386 +++++--- .../ocpp/OCPPResponseService.ts | 16 +- .../AbstractUIService.ts | 15 +- .../ui-websocket-services/UIService001.ts | 14 +- .../ui-websocket-services/UIServiceFactory.ts | 5 +- .../ui-websocket-services/UIServiceUtils.ts | 21 +- src/exception/BaseError.ts | 2 +- src/exception/OCPPError.ts | 7 +- src/performance/PerformanceStatistics.ts | 151 +++- src/performance/storage/JsonFileStorage.ts | 44 +- src/performance/storage/MikroOrmStorage.ts | 12 +- src/performance/storage/MongoDBStorage.ts | 21 +- src/performance/storage/Storage.ts | 18 +- src/scripts/deleteChargingStations.js | 12 +- src/scripts/scriptConfig-template.json | 4 +- src/scripts/setCSPublicFlag.js | 13 +- src/start.ts | 8 +- src/types/ChargingStationTemplate.ts | 4 +- src/types/ChargingStationWorker.ts | 12 +- src/types/Storage.ts | 5 +- src/types/UIProtocol.ts | 4 +- src/types/WebSocket.ts | 41 +- src/types/Worker.ts | 5 +- src/types/ocpp/1.6/ChargePointErrorCode.ts | 2 +- src/types/ocpp/1.6/Configuration.ts | 6 +- src/types/ocpp/1.6/DiagnosticsStatus.ts | 2 +- src/types/ocpp/1.6/MeterValues.ts | 11 +- src/types/ocpp/1.6/Requests.ts | 14 +- src/types/ocpp/1.6/Responses.ts | 14 +- src/types/ocpp/1.6/Transaction.ts | 4 +- src/types/ocpp/ChargePointErrorCode.ts | 2 +- src/types/ocpp/ChargePointStatus.ts | 2 +- src/types/ocpp/Configuration.ts | 14 +- src/types/ocpp/MeterValues.ts | 11 +- src/types/ocpp/Requests.ts | 26 +- src/types/ocpp/Responses.ts | 32 +- src/types/ocpp/Transaction.ts | 8 +- src/types/orm/entities/PerformanceData.ts | 15 - src/types/orm/entities/PerformanceRecord.ts | 6 - src/utils/CircularArray.ts | 4 +- src/utils/Configuration.ts | 289 ++++-- src/utils/Constants.ts | 80 +- src/utils/FileUtils.ts | 29 +- src/utils/Logger.ts | 27 +- src/utils/Utils.ts | 37 +- src/worker/WorkerAbstract.ts | 23 +- src/worker/WorkerDynamicPool.ts | 13 +- src/worker/WorkerFactory.ts | 31 +- src/worker/WorkerSet.ts | 29 +- src/worker/WorkerStaticPool.ts | 12 +- test/robohydra/config.json | 9 +- test/robohydra/plugins/wsServer/index.js | 24 +- test/utils/CircularArrayTest.ts | 6 +- tsconfig.json | 4 +- 83 files changed, 3877 insertions(+), 1395 deletions(-) diff --git a/.eslintrc b/.eslintrc index 2153335c..6d8714d7 100644 --- a/.eslintrc +++ b/.eslintrc @@ -11,13 +11,10 @@ "plugin:import/errors", "plugin:import/warnings", "plugin:import/typescript", - "plugin:jsdoc/recommended" - ], - "plugins": [ - "import", - "jsdoc", - "@typescript-eslint" + "plugin:jsdoc/recommended", + "prettier" ], + "plugins": ["import", "jsdoc", "@typescript-eslint"], "settings": { "jsdoc": { "mode": "typescript" @@ -45,21 +42,12 @@ ], "@typescript-eslint/array-type": "off", "semi": "off", - "@typescript-eslint/semi": [ - "error", - "always" - ], - "space-before-blocks": [ - "error", - "always" - ], + "@typescript-eslint/semi": ["error", "always"], + "space-before-blocks": ["error", "always"], "@typescript-eslint/no-empty-function": [ "warn", { - "allow": [ - "arrowFunctions", - "methods" - ] + "allow": ["arrowFunctions", "methods"] } ], "@typescript-eslint/member-ordering": "error", @@ -69,10 +57,7 @@ "@typescript-eslint/no-misused-promises": "error", "curly": "error", "brace-style": "error", - "eqeqeq": [ - "error", - "always" - ], + "eqeqeq": ["error", "always"], "no-else-return": "error", "no-eq-null": "error", "no-undefined": "error", @@ -80,10 +65,7 @@ "no-lone-blocks": "error", "no-multi-spaces": "error", "no-empty": "error", - "no-return-assign": [ - "error", - "always" - ], + "no-return-assign": ["error", "always"], "no-useless-catch": "error", "no-useless-return": "error", "no-shadow": "off", // This one is generating false positive no-shadow errors on exported/const enums @@ -104,21 +86,10 @@ // "ignorePattern": "pragma|ignored" // } // ], - "eol-last": [ - "error", - "always" - ], - "consistent-this": [ - "error", - "self" - ], - "func-call-spacing": [ - "error", - "never" - ], - "keyword-spacing": [ - "error" - ], + "eol-last": ["error", "always"], + "consistent-this": ["error", "self"], + "func-call-spacing": ["error", "never"], + "keyword-spacing": ["error"], "id-blacklist": [ "error", "any", @@ -133,10 +104,7 @@ "Symbol", "symbol" ], - "linebreak-style": [ - "error", - "unix" - ], + "linebreak-style": ["error", "unix"], "max-len": [ "warn", { @@ -160,25 +128,13 @@ "asyncArrow": "always" } ], - "space-in-parens": [ - "error", - "never" - ], + "space-in-parens": ["error", "never"], "space-infix-ops": "error", "space-unary-ops": "error", - "spaced-comment": [ - "error", - "always" - ], + "spaced-comment": ["error", "always"], "switch-colon-spacing": "error", - "arrow-body-style": [ - "error", - "as-needed" - ], - "arrow-parens": [ - "error", - "always" - ], + "arrow-body-style": ["error", "as-needed"], + "arrow-parens": ["error", "always"], "arrow-spacing": "error", "no-duplicate-imports": "error", "no-var": "error", @@ -189,18 +145,10 @@ "ignoreCase": false, "ignoreDeclarationSort": false, "ignoreMemberSort": false, - "memberSyntaxSortOrder": [ - "none", - "all", - "multiple", - "single" - ] + "memberSyntaxSortOrder": ["none", "all", "multiple", "single"] } ], - "object-curly-spacing": [ - "error", - "always" - ], + "object-curly-spacing": ["error", "always"], "lines-between-class-members": [ "error", "always", @@ -208,16 +156,11 @@ "exceptAfterSingleLine": true } ], - "quotes": [ - "error", - "single" - ] + "quotes": ["error", "single"] }, "overrides": [ { - "files": [ - "*.js" - ], + "files": ["*.js"], "extends": "plugin:node/recommended", "rules": { "node/shebang": "off", diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 585fec0c..5e484fc6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -13,15 +13,13 @@ body: - type: checkboxes attributes: options: - - label: - I've searched for any related issues and avoided creating a + - label: I've searched for any related issues and avoided creating a duplicate issue. required: true - type: textarea attributes: label: Description - description: - Description of the bug. + description: Description of the bug. - type: input attributes: label: charging-stations-simulator version diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 07e10fd5..c61e58c9 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -11,15 +11,13 @@ body: - type: checkboxes attributes: options: - - label: - I've searched for any related issues and avoided creating a + - label: I've searched for any related issues and avoided creating a duplicate issue. required: true - type: textarea attributes: label: Description - description: - A clear and concise description of the feature. + description: A clear and concise description of the feature. - type: textarea attributes: label: Attachments diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e7ea8b8..e5c359c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,38 +12,38 @@ jobs: matrix: node-version: ['14.x', '16.x', '17.x'] steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 - with: - node-version: ${{ matrix.node-version }} - - name: npm install - run: npm ci - - name: npm run lint - run: npm run lint - continue-on-error: true - - name: npm run build - run: npm run build - - name: npm test - run: npm test - - name: npm run coverage - run: npm run coverage - - name: SonarCloud Scan - if: "github.repository == 'jerome-benoit/charging-stations-simulator' && matrix.node-version == '16.x'" - uses: sonarsource/sonarcloud-github-action@master - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + - uses: actions/checkout@v2 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v2 + with: + node-version: ${{ matrix.node-version }} + - name: npm install + run: npm ci + - name: npm run lint + run: npm run lint + continue-on-error: true + - name: npm run build + run: npm run build + - name: npm test + run: npm test + - name: npm run coverage + run: npm run coverage + - name: SonarCloud Scan + if: "github.repository == 'jerome-benoit/charging-stations-simulator' && matrix.node-version == '16.x'" + uses: sonarsource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} build-docker: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v1 - - name: Build docker image - run: | - cd docker - make SUBMODULES_INIT=false + - uses: actions/checkout@v2 + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + - name: Build docker image + run: | + cd docker + make SUBMODULES_INIT=false diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d234d2f4..5acc1366 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -9,14 +9,14 @@ # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # -name: "CodeQL" +name: 'CodeQL' on: push: - branches: [ master ] + branches: [master] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [master] schedule: - cron: '17 15 * * 1' @@ -32,40 +32,40 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'javascript' ] + language: ['javascript'] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - - name: Checkout repository - uses: actions/checkout@v2 + - name: Checkout repository + uses: actions/checkout@v2 - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v1 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language - #- run: | - # make bootstrap - # make release + #- run: | + # make bootstrap + # make release - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.mocharc.json b/.mocharc.json index 88465baf..6bab7faa 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -2,9 +2,7 @@ "parallel": true, "diff": true, "recursive": true, - "extension": [ - "ts" - ], + "extension": ["ts"], "package": "./package.json", "reporter": "mochawesome", "reporter-options": "reportDir=outputs/mochawesome-report,json=false", @@ -13,8 +11,5 @@ "ui": "bdd", "full-trace": true, "exit": true, - "require": [ - "ts-node/register", - "mochawesome/register" - ] + "require": ["ts-node/register", "mochawesome/register"] } diff --git a/.nycrc.json b/.nycrc.json index a83f2f91..be587719 100644 --- a/.nycrc.json +++ b/.nycrc.json @@ -1,7 +1,5 @@ { "extends": "@istanbuljs/nyc-config-typescript", "all": true, - "include": [ - "src/**/*.ts" - ] + "include": ["src/**/*.ts"] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 5c8aa82b..e8114e91 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,13 +11,8 @@ "cwd": "${workspaceFolder}", "preLaunchTask": "Build", "runtimeExecutable": "npm", - "runtimeArgs": [ - "run-script", - "start:debug" - ], - "skipFiles": [ - "/**" - ], + "runtimeArgs": ["run-script", "start:debug"], + "skipFiles": ["/**"], "stopOnEntry": true }, { @@ -26,13 +21,8 @@ "name": "Debug Simulator Development Build via npm", "cwd": "${workspaceFolder}", "runtimeExecutable": "npm", - "runtimeArgs": [ - "run-script", - "start:dev:debug" - ], - "skipFiles": [ - "/**" - ], + "runtimeArgs": ["run-script", "start:dev:debug"], + "skipFiles": ["/**"], "stopOnEntry": true } ] diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b7f75442..4bae87e2 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/README.md b/README.md index 16bad112..fb393f9b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Simple [node.js](https://nodejs.org/) program to simulate a set of charging stat ### Windows -* [Chocolatey](https://chocolatey.org/): +- [Chocolatey](https://chocolatey.org/): ```powershell choco install -y nodejs-lts @@ -16,19 +16,19 @@ choco install -y nodejs-lts ### MacOSX -* [Homebrew](https://brew.sh/): +- [Homebrew](https://brew.sh/): ```shell brew install node@14 ``` -### GNU/Linux: +### GNU/Linux: -* [NodeSource](https://github.com/nodesource/distributions) Node.js Binary Distributions for version 14.X +- [NodeSource](https://github.com/nodesource/distributions) Node.js Binary Distributions for version 14.X ## Configuration syntax -All configuration files are in the JSON standard format. +All configuration files are in the JSON standard format. The charging stations simulator's main configuration parameters must be within the `src/assets/config.json` file. A configuration template file is available at [src/assets/config-template.json](src/assets/config-template.json). @@ -36,97 +36,98 @@ All charging station templates are in the directory [src/assets/station-template A list of RFID tags must be defined for the automatic transaction generator with the default location and name `src/assets/authorization-tags.json`. A template file is available at [src/assets/authorization-tags-template.json](src/assets/authorization-tags-template.json). -The charging stations simulator have an automatic configuration files reload feature at change for: -* main configuration; -* charging station templates; -* authorization RFID tags. +The charging stations simulator have an automatic configuration files reload feature at change for: -But the modifications to test have to be done to the files in the build result directory [dist/assets](dist/assets). Once the modifications are finished, they have to be reported or copied to the matching files in the build source directory [src/assets](src/assets) to ensure they will be taken into account at next build. +- main configuration; +- charging station templates; +- authorization RFID tags. -### Main configuration +But the modifications to test have to be done to the files in the build result directory [dist/assets](dist/assets). Once the modifications are finished, they have to be reported or copied to the matching files in the build source directory [src/assets](src/assets) to ensure they will be taken into account at next build. + +### Main configuration **src/assets/config.json**: -Key | Value(s) | Default Value | Value type | Description ---- | -------| --------------| ---------- | ------------ -supervisionUrls | | [] | string \| string[] | string or array of global connection URIs to OCPP-J servers -supervisionUrlDistribution | round-robin/random/sequential | round-robin | boolean | supervision urls distribution policy to simulated charging stations -workerProcess | workerSet/staticPool/dynamicPool | workerSet | string | worker threads process type -workerStartDelay | | 500 | integer | milliseconds to wait at worker threads startup (only for workerSet threads process type) -elementStartDelay | | 0 | integer | milliseconds to wait at charging station startup -workerPoolMinSize | | 4 | integer | worker threads pool minimum number of threads -workerPoolMaxSize | | 16 | integer | worker threads pool maximum number of threads -workerPoolStrategy | ROUND_ROBIN/LESS_RECENTLY_USED/... | [poolifier](https://github.com/poolifier/poolifier) default: ROUND_ROBBIN | string | worker threads pool [poolifier](https://github.com/poolifier/poolifier) worker choice strategy -chargingStationsPerWorker | | 1 | integer | number of charging stations per worker threads for the `workerSet` process type -logStatisticsInterval | | 60 | integer | seconds between charging stations statistics output in the logs -logConsole | true/false | false | boolean | output logs on the console -logFormat | | simple | string | winston log format -logRotate | true/false | true | boolean | enable daily log files rotation -logMaxFiles | | 7 | integer | maximum number of log files to keep -logLevel | emerg/alert/crit/error/warning/notice/info/debug | info | string | winston logging level -logFile | | combined.log | string | log file relative path -logErrorFile | | error.log | string | error log file relative path -uiWebSocketServer | | { "enabled": true, "options": { "host: "localhost", "port": 8080 } } | { enabled: boolean; options: ServerOptions; } | UI WebSocket server configuration section -performanceStorage | | { "enabled": false, "type": "jsonfile", "file:///performanceRecords.json" } | { enabled: boolean; type: string; URI: string; } where type can be 'jsonfile' or 'mongodb' | performance storage configuration section -stationTemplateUrls | | {}[] | { file: string; numberOfStations: number; }[] | array of charging station templates URIs configuration section (template file name and number of stations) - -#### Worker process model: +| Key | Value(s) | Default Value | Value type | Description | +| -------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- | +| supervisionUrls | | [] | string \| string[] | string or array of global connection URIs to OCPP-J servers | +| supervisionUrlDistribution | round-robin/random/sequential | round-robin | boolean | supervision urls distribution policy to simulated charging stations | +| workerProcess | workerSet/staticPool/dynamicPool | workerSet | string | worker threads process type | +| workerStartDelay | | 500 | integer | milliseconds to wait at worker threads startup (only for workerSet threads process type) | +| elementStartDelay | | 0 | integer | milliseconds to wait at charging station startup | +| workerPoolMinSize | | 4 | integer | worker threads pool minimum number of threads | +| workerPoolMaxSize | | 16 | integer | worker threads pool maximum number of threads | +| workerPoolStrategy | ROUND_ROBIN/LESS_RECENTLY_USED/... | [poolifier](https://github.com/poolifier/poolifier) default: ROUND_ROBBIN | string | worker threads pool [poolifier](https://github.com/poolifier/poolifier) worker choice strategy | +| chargingStationsPerWorker | | 1 | integer | number of charging stations per worker threads for the `workerSet` process type | +| logStatisticsInterval | | 60 | integer | seconds between charging stations statistics output in the logs | +| logConsole | true/false | false | boolean | output logs on the console | +| logFormat | | simple | string | winston log format | +| logRotate | true/false | true | boolean | enable daily log files rotation | +| logMaxFiles | | 7 | integer | maximum number of log files to keep | +| logLevel | emerg/alert/crit/error/warning/notice/info/debug | info | string | winston logging level | +| logFile | | combined.log | string | log file relative path | +| logErrorFile | | error.log | string | error log file relative path | +| uiWebSocketServer | | { "enabled": true, "options": { "host: "localhost", "port": 8080 } } | { enabled: boolean; options: ServerOptions; } | UI WebSocket server configuration section | +| performanceStorage | | { "enabled": false, "type": "jsonfile", "file:///performanceRecords.json" } | { enabled: boolean; type: string; URI: string; } where type can be 'jsonfile' or 'mongodb' | performance storage configuration section | +| stationTemplateUrls | | {}[] | { file: string; numberOfStations: number; }[] | array of charging station templates URIs configuration section (template file name and number of stations) | + +#### Worker process model: - **workerSet**: Worker set executing each a static number (chargingStationsPerWorker) of simulated charging stations from the total - **staticPool**: - Statically sized worker pool executing a static total number of simulated charging stations + Statically sized worker pool executing a static total number of simulated charging stations - **dynamicPool**: - Dynamically sized worker pool executing a static total number of simulated charging stations + Dynamically sized worker pool executing a static total number of simulated charging stations ### Charging station template -Key | Value(s) | Default Value | Value type | Description ---- | -------| --------------| ---------- | ------------ -supervisionUrls | | '' | string\|string[] | string or array of connection URIs to OCPP-J servers. It has priority over the global configuration parameter -supervisionUser | | '' | string | basic HTTP authentication user to OCPP-J server -supervisionPassword | | '' | string | basic HTTP authentication password to OCPP-J server -supervisionUrlOcppConfiguration | true/false | false | boolean | Allow supervision URL configuration via a vendor OCPP parameter key -supervisionUrlOcppKey | | 'ConnectionUrl' | string | The vendor string that will be used as a vendor OCPP parameter key to set the supervision URL -ocppVersion | 1.6 | 1.6 | string | OCPP version -ocppProtocol | json | json | string | OCPP protocol -ocppStrictCompliance | true/false | true | boolean | Strict adherence to the OCPP version and protocol specifications -wsOptions | | {} | ClientOptions & ClientRequestArgs | [ws](https://github.com/websockets/ws) and node.js [http](https://nodejs.org/api/http.html) clients options intersection -authorizationFile | | '' | string | RFID tags list file relative to src/assets path -baseName | | '' | string | base name to build charging stations name -nameSuffix | | '' | string | name suffix to build charging stations name -fixedName | true/false | false | boolean | use the baseName as the charging stations unique name -chargePointModel | | '' | string | charging stations model -chargePointVendor | | '' | string | charging stations vendor -chargeBoxSerialNumberPrefix | | '' | string | charging stations serial number prefix -firmwareVersion | | '' | string | charging stations firmware version -power | | | float\|float[] | charging stations maximum power value(s) -powerSharedByConnectors | true/false | false | boolean | charging stations power shared by its connectors -powerUnit | W/kW | W | string | charging stations power unit -currentOutType | AC/DC | AC | string | charging stations current out type -voltageOut | | AC:230/DC:400 | integer | charging stations voltage out -numberOfPhases | 0/1/3 | AC:3/DC:0 | integer | charging stations number of phase(s) -numberOfConnectors | | | integer\|integer[] | charging stations number of connector(s) -useConnectorId0 | true/false | true | boolean | use connector id 0 definition from the template -randomConnectors | true/false | false | boolean | randomize runtime connector id affectation from the connector id definition in template -resetTime | | 60 | integer | seconds to wait before the charging stations come back at reset -autoRegister | true/false | false | boolean | set the charging station as registered at boot notification for testing purpose -autoReconnectMaxRetries | | -1 (unlimited) | integer | connection retries to the OCPP-J server -reconnectExponentialDelay | true/false | false | boolean | connection delay retry to the OCPP-J server -registrationMaxRetries | | -1 (unlimited) | integer | charging stations boot notification retries -enableStatistics | true/false | true | boolean | enable charging stations statistics -mayAuthorizeAtRemoteStart | true/false | true | boolean | always send authorize at remote start transaction when AuthorizeRemoteTxRequests is enabled -beginEndMeterValues | true/false | false | boolean | enable Transaction.{Begin,End} MeterValues -outOfOrderEndMeterValues | true/false | false | boolean | send Transaction.End MeterValues out of order. Need to relax OCPP specifications strict compliance ('ocppStrictCompliance' parameter) -meteringPerTransaction | true/false | true | boolean | enable metering history on a per transaction basis -transactionDataMeterValues | true/false | false | boolean | enable transaction data MeterValues at stop transaction -mainVoltageMeterValues | true/false | true | boolean | include charging station main voltage MeterValues on three phased charging stations -phaseLineToLineVoltageMeterValues | true/false | true | boolean | include charging station line to line voltage MeterValues on three phased charging stations -Configuration | | | ChargingStationConfiguration | charging stations OCPP parameters configuration section -AutomaticTransactionGenerator | | | AutomaticTransactionGenerator | charging stations ATG configuration section -Connectors | | | Connectors | charging stations connectors configuration section +| Key | Value(s) | Default Value | Value type | Description | +| --------------------------------- | ---------- | --------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| supervisionUrls | | '' | string\|string[] | string or array of connection URIs to OCPP-J servers. It has priority over the global configuration parameter | +| supervisionUser | | '' | string | basic HTTP authentication user to OCPP-J server | +| supervisionPassword | | '' | string | basic HTTP authentication password to OCPP-J server | +| supervisionUrlOcppConfiguration | true/false | false | boolean | Allow supervision URL configuration via a vendor OCPP parameter key | +| supervisionUrlOcppKey | | 'ConnectionUrl' | string | The vendor string that will be used as a vendor OCPP parameter key to set the supervision URL | +| ocppVersion | 1.6 | 1.6 | string | OCPP version | +| ocppProtocol | json | json | string | OCPP protocol | +| ocppStrictCompliance | true/false | true | boolean | Strict adherence to the OCPP version and protocol specifications | +| wsOptions | | {} | ClientOptions & ClientRequestArgs | [ws](https://github.com/websockets/ws) and node.js [http](https://nodejs.org/api/http.html) clients options intersection | +| authorizationFile | | '' | string | RFID tags list file relative to src/assets path | +| baseName | | '' | string | base name to build charging stations name | +| nameSuffix | | '' | string | name suffix to build charging stations name | +| fixedName | true/false | false | boolean | use the baseName as the charging stations unique name | +| chargePointModel | | '' | string | charging stations model | +| chargePointVendor | | '' | string | charging stations vendor | +| chargeBoxSerialNumberPrefix | | '' | string | charging stations serial number prefix | +| firmwareVersion | | '' | string | charging stations firmware version | +| power | | | float\|float[] | charging stations maximum power value(s) | +| powerSharedByConnectors | true/false | false | boolean | charging stations power shared by its connectors | +| powerUnit | W/kW | W | string | charging stations power unit | +| currentOutType | AC/DC | AC | string | charging stations current out type | +| voltageOut | | AC:230/DC:400 | integer | charging stations voltage out | +| numberOfPhases | 0/1/3 | AC:3/DC:0 | integer | charging stations number of phase(s) | +| numberOfConnectors | | | integer\|integer[] | charging stations number of connector(s) | +| useConnectorId0 | true/false | true | boolean | use connector id 0 definition from the template | +| randomConnectors | true/false | false | boolean | randomize runtime connector id affectation from the connector id definition in template | +| resetTime | | 60 | integer | seconds to wait before the charging stations come back at reset | +| autoRegister | true/false | false | boolean | set the charging station as registered at boot notification for testing purpose | +| autoReconnectMaxRetries | | -1 (unlimited) | integer | connection retries to the OCPP-J server | +| reconnectExponentialDelay | true/false | false | boolean | connection delay retry to the OCPP-J server | +| registrationMaxRetries | | -1 (unlimited) | integer | charging stations boot notification retries | +| enableStatistics | true/false | true | boolean | enable charging stations statistics | +| mayAuthorizeAtRemoteStart | true/false | true | boolean | always send authorize at remote start transaction when AuthorizeRemoteTxRequests is enabled | +| beginEndMeterValues | true/false | false | boolean | enable Transaction.{Begin,End} MeterValues | +| outOfOrderEndMeterValues | true/false | false | boolean | send Transaction.End MeterValues out of order. Need to relax OCPP specifications strict compliance ('ocppStrictCompliance' parameter) | +| meteringPerTransaction | true/false | true | boolean | enable metering history on a per transaction basis | +| transactionDataMeterValues | true/false | false | boolean | enable transaction data MeterValues at stop transaction | +| mainVoltageMeterValues | true/false | true | boolean | include charging station main voltage MeterValues on three phased charging stations | +| phaseLineToLineVoltageMeterValues | true/false | true | boolean | include charging station line to line voltage MeterValues on three phased charging stations | +| Configuration | | | ChargingStationConfiguration | charging stations OCPP parameters configuration section | +| AutomaticTransactionGenerator | | | AutomaticTransactionGenerator | charging stations ATG configuration section | +| Connectors | | | Connectors | charging stations connectors configuration section | #### Configuration section @@ -169,6 +170,7 @@ Connectors | | | Connectors | charging stations connectors configuration section "requireAuthorize": true } ``` + #### Connectors section ```json @@ -270,7 +272,7 @@ make SUBMODULES_INIT=true ## OCPP-J standard parameters supported -All kind of OCPP parameters are supported in a charging station template. The list here mention the standard ones also handled automatically in the simulator. +All kind of OCPP parameters are supported in a charging station template. The list here mention the standard ones also handled automatically in the simulator. ### Version 1.6 @@ -301,7 +303,7 @@ All kind of OCPP parameters are supported in a charging station template. The li #### Firmware Management Profile -- *none* +- _none_ #### Local Auth List Management Profile @@ -311,7 +313,7 @@ All kind of OCPP parameters are supported in a charging station template. The li #### Reservation Profile -- *none* +- _none_ #### Smart Charging Profile @@ -322,7 +324,7 @@ All kind of OCPP parameters are supported in a charging station template. The li #### Remote Trigger Profile -- *none* +- _none_ ## License diff --git a/docker/buildspec.yml b/docker/buildspec.yml index a4f88177..9a4c6acb 100644 --- a/docker/buildspec.yml +++ b/docker/buildspec.yml @@ -1,6 +1,6 @@ version: 0.2 parameter-store: - build_ssh_key: "/CodeBuild/emobility_build_ssh_key" + build_ssh_key: '/CodeBuild/emobility_build_ssh_key' phases: install: runtime-versions: diff --git a/docker/config.json b/docker/config.json index 292f8b62..9c4efe3a 100644 --- a/docker/config.json +++ b/docker/config.json @@ -1,7 +1,5 @@ { - "supervisionUrls": [ - "ws://server:8010/OCPP16/5c866e81a2d9593de43efdb4" - ], + "supervisionUrls": ["ws://server:8010/OCPP16/5c866e81a2d9593de43efdb4"], "distributeStationsToTenantsEqually": true, "workerProcess": "workerSet", "workerPoolMinSize": 4, diff --git a/manifest-cf-template.yml b/manifest-cf-template.yml index e5f15405..aeb21df7 100644 --- a/manifest-cf-template.yml +++ b/manifest-cf-template.yml @@ -1,14 +1,14 @@ applications: -- name: charging-stations-simulator - memory: 1G - disk_quota: 1G - instances: 1 - buildpacks: - - https://github.com/cloudfoundry/nodejs-buildpack - no-route: true - health-check-type: process - health-check-invocation-timeout: 10 - command: node -r source-map-support/register dist/start.js - env: - # OPTIMIZE_MEMORY: true - NODE_OPTIONS: --stack-trace-limit=1024 --max-old-space-size=768 + - name: charging-stations-simulator + memory: 1G + disk_quota: 1G + instances: 1 + buildpacks: + - https://github.com/cloudfoundry/nodejs-buildpack + no-route: true + health-check-type: process + health-check-invocation-timeout: 10 + command: node -r source-map-support/register dist/start.js + env: + # OPTIMIZE_MEMORY: true + NODE_OPTIONS: --stack-trace-limit=1024 --max-old-space-size=768 diff --git a/mikro-orm.config-template.ts b/mikro-orm.config-template.ts index 0c24f054..ab28c40a 100644 --- a/mikro-orm.config-template.ts +++ b/mikro-orm.config-template.ts @@ -8,5 +8,8 @@ export default { metadataProvider: TsMorphMetadataProvider, entities: [PerformanceRecord, PerformanceData], type: 'sqlite', - clientUrl: `file://${path.join(path.resolve(__dirname), `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`)}` + clientUrl: `file://${path.join( + path.resolve(__dirname), + `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db` + )}`, }; diff --git a/package-lock.json b/package-lock.json index 03b68e6a..eb81da6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,6 +43,7 @@ "clinic": "^11.1.0", "cross-env": "^7.0.3", "eslint": "^8.10.0", + "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jsdoc": "^37.9.6", "eslint-plugin-node": "^11.1.0", @@ -51,6 +52,7 @@ "mochawesome": "^7.1.1", "npm-check": "^5.9.2", "nyc": "^15.1.0", + "prettier": "^2.5.1", "release-it": "^14.12.5", "robohydra": "^0.6.9", "rollup": "^2.69.0", @@ -5851,6 +5853,18 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -13845,6 +13859,18 @@ "node": ">=0.10.0" } }, + "node_modules/prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -23767,6 +23793,13 @@ } } }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "requires": {} + }, "eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -30036,6 +30069,12 @@ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", "dev": true }, + "prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/package.json b/package.json index 0056f899..d1589d37 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "build:dev:watch": "npm run rollup -- --environment BUILD:development --watch", "lint": "cross-env TIMING=1 eslint --ext .js,.ts src", "lint:fix": "cross-env TIMING=1 eslint --fix --ext .js,.ts src", + "format": "prettier --write .", "import-sort": "npx import-sort-cli --write 'src/**/*.ts{,x}'", "test": "cross-env TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' nyc mocha test/**/*Test.ts", "coverage": "nyc report --reporter=lcov", @@ -105,6 +106,7 @@ "clinic": "^11.1.0", "cross-env": "^7.0.3", "eslint": "^8.10.0", + "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.25.4", "eslint-plugin-jsdoc": "^37.9.6", "eslint-plugin-node": "^11.1.0", @@ -113,6 +115,7 @@ "mochawesome": "^7.1.1", "npm-check": "^5.9.2", "nyc": "^15.1.0", + "prettier": "^2.5.1", "release-it": "^14.12.5", "robohydra": "^0.6.9", "rollup": "^2.69.0", diff --git a/rollup.config.js b/rollup.config.js index 3900559c..b1114284 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -10,32 +10,50 @@ const isDevelopmentBuild = process.env.BUILD === 'development'; export default { input: ['src/start.ts', 'src/charging-station/ChargingStationWorker.ts'], - output: - { + output: { dir: 'dist', format: 'cjs', exports: 'auto', sourcemap: true, preserveModules: true, preserveModulesRoot: 'src', - ...!isDevelopmentBuild && { plugins: [terser({ numWorkers: 2 })] } + ...(!isDevelopmentBuild && { plugins: [terser({ numWorkers: 2 })] }), }, - external: ['basic-ftp', 'chalk', 'crypto', 'fs', '@mikro-orm/core', '@mikro-orm/reflection', 'mongodb', 'path', 'perf_hooks', 'poolifier', 'proper-lockfile', 'reflect-metadata', 'tar', 'url', 'uuid', 'ws', 'winston-daily-rotate-file', 'winston/lib/winston/transports', 'winston', 'worker_threads'], + external: [ + 'basic-ftp', + 'chalk', + 'crypto', + 'fs', + '@mikro-orm/core', + '@mikro-orm/reflection', + 'mongodb', + 'path', + 'perf_hooks', + 'poolifier', + 'proper-lockfile', + 'reflect-metadata', + 'tar', + 'url', + 'uuid', + 'ws', + 'winston-daily-rotate-file', + 'winston/lib/winston/transports', + 'winston', + 'worker_threads', + ], plugins: [ json(), ts({ tsconfig: 'tsconfig.json', - browserslist: false + browserslist: false, }), isDevelopmentBuild && istanbul(), del({ - targets: 'dist/*' + targets: 'dist/*', }), copy({ - targets: [ - { src: 'src/assets', dest: 'dist/' } - ] + targets: [{ src: 'src/assets', dest: 'dist/' }], }), - isDevelopmentBuild && analyze() - ] + isDevelopmentBuild && analyze(), + ], }; diff --git a/src/assets/authorization-tags-template.json b/src/assets/authorization-tags-template.json index 25c39854..f4264f02 100644 --- a/src/assets/authorization-tags-template.json +++ b/src/assets/authorization-tags-template.json @@ -1,4 +1 @@ -[ - "UNDEFINED", - "BB2TBR09" -] +["UNDEFINED", "BB2TBR09"] diff --git a/src/assets/config-template.json b/src/assets/config-template.json index 4520a2d4..839750ac 100644 --- a/src/assets/config-template.json +++ b/src/assets/config-template.json @@ -1,7 +1,5 @@ { - "supervisionUrls": [ - "ws://localhost:8010/OCPP16/5be7fb271014d90008992f06" - ], + "supervisionUrls": ["ws://localhost:8010/OCPP16/5be7fb271014d90008992f06"], "supervisionUrlDistribution": "sequential", "performanceStorage": { "enabled": true, diff --git a/src/charging-station/AutomaticTransactionGenerator.ts b/src/charging-station/AutomaticTransactionGenerator.ts index 0e0982a2..b9d3f1d0 100644 --- a/src/charging-station/AutomaticTransactionGenerator.ts +++ b/src/charging-station/AutomaticTransactionGenerator.ts @@ -1,6 +1,12 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. -import { AuthorizationStatus, AuthorizeResponse, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../types/ocpp/Transaction'; +import { + AuthorizationStatus, + AuthorizeResponse, + StartTransactionResponse, + StopTransactionReason, + StopTransactionResponse, +} from '../types/ocpp/Transaction'; import type ChargingStation from './ChargingStation'; import Constants from '../utils/Constants'; @@ -10,7 +16,10 @@ import Utils from '../utils/Utils'; import logger from '../utils/Logger'; export default class AutomaticTransactionGenerator { - private static readonly instances: Map = new Map(); + private static readonly instances: Map = new Map< + string, + AutomaticTransactionGenerator + >(); public started: boolean; private readonly chargingStation: ChargingStation; private readonly connectorsStatus: Map; @@ -24,7 +33,10 @@ export default class AutomaticTransactionGenerator { public static getInstance(chargingStation: ChargingStation): AutomaticTransactionGenerator { if (!AutomaticTransactionGenerator.instances.has(chargingStation.id)) { - AutomaticTransactionGenerator.instances.set(chargingStation.id, new AutomaticTransactionGenerator(chargingStation)); + AutomaticTransactionGenerator.instances.set( + chargingStation.id, + new AutomaticTransactionGenerator(chargingStation) + ); } return AutomaticTransactionGenerator.instances.get(chargingStation.id); } @@ -48,7 +60,10 @@ export default class AutomaticTransactionGenerator { } private startConnectors(): void { - if (this.connectorsStatus?.size > 0 && this.connectorsStatus.size !== this.chargingStation.getNumberOfConnectors()) { + if ( + this.connectorsStatus?.size > 0 && + this.connectorsStatus.size !== this.chargingStation.getNumberOfConnectors() + ) { this.connectorsStatus.clear(); } for (const connectorId of this.chargingStation.connectors.keys()) { @@ -68,39 +83,69 @@ export default class AutomaticTransactionGenerator { private async internalStartConnector(connectorId: number): Promise { this.initStartConnectorStatus(connectorId); - logger.info(this.logPrefix(connectorId) + ' started on connector and will run for ' + Utils.formatDurationMilliSeconds(this.connectorsStatus.get(connectorId).stopDate.getTime() - this.connectorsStatus.get(connectorId).startDate.getTime())); + logger.info( + this.logPrefix(connectorId) + + ' started on connector and will run for ' + + Utils.formatDurationMilliSeconds( + this.connectorsStatus.get(connectorId).stopDate.getTime() - + this.connectorsStatus.get(connectorId).startDate.getTime() + ) + ); while (this.connectorsStatus.get(connectorId).start) { - if ((new Date()) > this.connectorsStatus.get(connectorId).stopDate) { + if (new Date() > this.connectorsStatus.get(connectorId).stopDate) { this.stopConnector(connectorId); break; } if (!this.chargingStation.isInAcceptedState()) { - logger.error(this.logPrefix(connectorId) + ' entered in transaction loop while the charging station is not in accepted state'); + logger.error( + this.logPrefix(connectorId) + + ' entered in transaction loop while the charging station is not in accepted state' + ); this.stopConnector(connectorId); break; } if (!this.chargingStation.isChargingStationAvailable()) { - logger.info(this.logPrefix(connectorId) + ' entered in transaction loop while the charging station is unavailable'); + logger.info( + this.logPrefix(connectorId) + + ' entered in transaction loop while the charging station is unavailable' + ); this.stopConnector(connectorId); break; } if (!this.chargingStation.isConnectorAvailable(connectorId)) { - logger.info(`${this.logPrefix(connectorId)} entered in transaction loop while the connector ${connectorId} is unavailable`); + logger.info( + `${this.logPrefix( + connectorId + )} entered in transaction loop while the connector ${connectorId} is unavailable` + ); this.stopConnector(connectorId); break; } if (!this.chargingStation?.ocppRequestService) { - logger.info(`${this.logPrefix(connectorId)} transaction loop waiting for charging station service to be initialized`); + logger.info( + `${this.logPrefix( + connectorId + )} transaction loop waiting for charging station service to be initialized` + ); do { await Utils.sleep(Constants.CHARGING_STATION_ATG_INITIALIZATION_TIME); } while (!this.chargingStation?.ocppRequestService); } - const wait = Utils.getRandomInteger(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDelayBetweenTwoTransactions, - this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDelayBetweenTwoTransactions) * 1000; - logger.info(this.logPrefix(connectorId) + ' waiting for ' + Utils.formatDurationMilliSeconds(wait)); + const wait = + Utils.getRandomInteger( + this.chargingStation.stationInfo.AutomaticTransactionGenerator + .maxDelayBetweenTwoTransactions, + this.chargingStation.stationInfo.AutomaticTransactionGenerator + .minDelayBetweenTwoTransactions + ) * 1000; + logger.info( + this.logPrefix(connectorId) + ' waiting for ' + Utils.formatDurationMilliSeconds(wait) + ); await Utils.sleep(wait); const start = Utils.secureRandom(); - if (start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart) { + if ( + start < this.chargingStation.stationInfo.AutomaticTransactionGenerator.probabilityOfStart + ) { this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions = 0; // Start transaction const startResponse = await this.startTransaction(connectorId); @@ -110,60 +155,113 @@ export default class AutomaticTransactionGenerator { this.connectorsStatus.get(connectorId).rejectedStartTransactionRequests++; } else { // Wait until end of transaction - const waitTrxEnd = Utils.getRandomInteger(this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDuration, - this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDuration) * 1000; - logger.info(this.logPrefix(connectorId) + ' transaction ' + this.chargingStation.getConnectorStatus(connectorId).transactionId.toString() + ' started and will stop in ' + Utils.formatDurationMilliSeconds(waitTrxEnd)); + const waitTrxEnd = + Utils.getRandomInteger( + this.chargingStation.stationInfo.AutomaticTransactionGenerator.maxDuration, + this.chargingStation.stationInfo.AutomaticTransactionGenerator.minDuration + ) * 1000; + logger.info( + this.logPrefix(connectorId) + + ' transaction ' + + this.chargingStation.getConnectorStatus(connectorId).transactionId.toString() + + ' started and will stop in ' + + Utils.formatDurationMilliSeconds(waitTrxEnd) + ); this.connectorsStatus.get(connectorId).acceptedStartTransactionRequests++; await Utils.sleep(waitTrxEnd); // Stop transaction - logger.info(this.logPrefix(connectorId) + ' stop transaction ' + this.chargingStation.getConnectorStatus(connectorId).transactionId.toString()); + logger.info( + this.logPrefix(connectorId) + + ' stop transaction ' + + this.chargingStation.getConnectorStatus(connectorId).transactionId.toString() + ); await this.stopTransaction(connectorId); } } else { this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions++; this.connectorsStatus.get(connectorId).skippedTransactions++; - logger.info(this.logPrefix(connectorId) + ' skipped consecutively ' + this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions.toString() + '/' + this.connectorsStatus.get(connectorId).skippedTransactions.toString() + ' transaction(s)'); + logger.info( + this.logPrefix(connectorId) + + ' skipped consecutively ' + + this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions.toString() + + '/' + + this.connectorsStatus.get(connectorId).skippedTransactions.toString() + + ' transaction(s)' + ); } this.connectorsStatus.get(connectorId).lastRunDate = new Date(); } await this.stopTransaction(connectorId); this.connectorsStatus.get(connectorId).stoppedDate = new Date(); - logger.info(this.logPrefix(connectorId) + ' stopped on connector and lasted for ' + Utils.formatDurationMilliSeconds(this.connectorsStatus.get(connectorId).stoppedDate.getTime() - this.connectorsStatus.get(connectorId).startDate.getTime())); - logger.debug(`${this.logPrefix(connectorId)} connector status %j`, this.connectorsStatus.get(connectorId)); + logger.info( + this.logPrefix(connectorId) + + ' stopped on connector and lasted for ' + + Utils.formatDurationMilliSeconds( + this.connectorsStatus.get(connectorId).stoppedDate.getTime() - + this.connectorsStatus.get(connectorId).startDate.getTime() + ) + ); + logger.debug( + `${this.logPrefix(connectorId)} connector status %j`, + this.connectorsStatus.get(connectorId) + ); } private startConnector(connectorId: number): void { // Avoid hogging the event loop with a busy loop setImmediate(() => { - this.internalStartConnector(connectorId).catch(() => { /* This is intentional */ }); + this.internalStartConnector(connectorId).catch(() => { + /* This is intentional */ + }); }); } private stopConnector(connectorId: number): void { - this.connectorsStatus.set(connectorId, { ...this.connectorsStatus.get(connectorId), start: false }); + this.connectorsStatus.set(connectorId, { + ...this.connectorsStatus.get(connectorId), + start: false, + }); } private initStartConnectorStatus(connectorId: number): void { - this.connectorsStatus.get(connectorId).authorizeRequests = this?.connectorsStatus.get(connectorId)?.authorizeRequests ?? 0; - this.connectorsStatus.get(connectorId).acceptedAuthorizeRequests = this?.connectorsStatus.get(connectorId)?.acceptedAuthorizeRequests ?? 0; - this.connectorsStatus.get(connectorId).rejectedAuthorizeRequests = this?.connectorsStatus.get(connectorId)?.rejectedAuthorizeRequests ?? 0; - this.connectorsStatus.get(connectorId).startTransactionRequests = this?.connectorsStatus.get(connectorId)?.startTransactionRequests ?? 0; - this.connectorsStatus.get(connectorId).acceptedStartTransactionRequests = this?.connectorsStatus.get(connectorId)?.acceptedStartTransactionRequests ?? 0; - this.connectorsStatus.get(connectorId).rejectedStartTransactionRequests = this?.connectorsStatus.get(connectorId)?.rejectedStartTransactionRequests ?? 0; - this.connectorsStatus.get(connectorId).stopTransactionRequests = this?.connectorsStatus.get(connectorId)?.stopTransactionRequests ?? 0; + this.connectorsStatus.get(connectorId).authorizeRequests = + this?.connectorsStatus.get(connectorId)?.authorizeRequests ?? 0; + this.connectorsStatus.get(connectorId).acceptedAuthorizeRequests = + this?.connectorsStatus.get(connectorId)?.acceptedAuthorizeRequests ?? 0; + this.connectorsStatus.get(connectorId).rejectedAuthorizeRequests = + this?.connectorsStatus.get(connectorId)?.rejectedAuthorizeRequests ?? 0; + this.connectorsStatus.get(connectorId).startTransactionRequests = + this?.connectorsStatus.get(connectorId)?.startTransactionRequests ?? 0; + this.connectorsStatus.get(connectorId).acceptedStartTransactionRequests = + this?.connectorsStatus.get(connectorId)?.acceptedStartTransactionRequests ?? 0; + this.connectorsStatus.get(connectorId).rejectedStartTransactionRequests = + this?.connectorsStatus.get(connectorId)?.rejectedStartTransactionRequests ?? 0; + this.connectorsStatus.get(connectorId).stopTransactionRequests = + this?.connectorsStatus.get(connectorId)?.stopTransactionRequests ?? 0; this.connectorsStatus.get(connectorId).skippedConsecutiveTransactions = 0; - this.connectorsStatus.get(connectorId).skippedTransactions = this?.connectorsStatus.get(connectorId)?.skippedTransactions ?? 0; - const previousRunDuration = (this?.connectorsStatus.get(connectorId)?.startDate && this?.connectorsStatus.get(connectorId)?.lastRunDate) - ? (this.connectorsStatus.get(connectorId).lastRunDate.getTime() - this.connectorsStatus.get(connectorId).startDate.getTime()) - : 0; + this.connectorsStatus.get(connectorId).skippedTransactions = + this?.connectorsStatus.get(connectorId)?.skippedTransactions ?? 0; + const previousRunDuration = + this?.connectorsStatus.get(connectorId)?.startDate && + this?.connectorsStatus.get(connectorId)?.lastRunDate + ? this.connectorsStatus.get(connectorId).lastRunDate.getTime() - + this.connectorsStatus.get(connectorId).startDate.getTime() + : 0; this.connectorsStatus.get(connectorId).startDate = new Date(); - this.connectorsStatus.get(connectorId).stopDate = new Date(this.connectorsStatus.get(connectorId).startDate.getTime() - + (this.chargingStation.stationInfo?.AutomaticTransactionGenerator?.stopAfterHours ?? Constants.CHARGING_STATION_ATG_DEFAULT_STOP_AFTER_HOURS) * 3600 * 1000 - - previousRunDuration); + this.connectorsStatus.get(connectorId).stopDate = new Date( + this.connectorsStatus.get(connectorId).startDate.getTime() + + (this.chargingStation.stationInfo?.AutomaticTransactionGenerator?.stopAfterHours ?? + Constants.CHARGING_STATION_ATG_DEFAULT_STOP_AFTER_HOURS) * + 3600 * + 1000 - + previousRunDuration + ); this.connectorsStatus.get(connectorId).start = true; } - private async startTransaction(connectorId: number): Promise { + private async startTransaction( + connectorId: number + ): Promise { const measureId = 'StartTransaction with ATG'; const beginId = PerformanceStatistics.beginMeasure(measureId); let startResponse: StartTransactionResponse; @@ -171,13 +269,19 @@ export default class AutomaticTransactionGenerator { const idTag = this.chargingStation.getRandomIdTag(); if (this.chargingStation.getAutomaticTransactionGeneratorRequireAuthorize()) { // Authorize idTag - const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(connectorId, idTag); + const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize( + connectorId, + idTag + ); this.connectorsStatus.get(connectorId).authorizeRequests++; if (authorizeResponse?.idTagInfo?.status === AuthorizationStatus.ACCEPTED) { this.connectorsStatus.get(connectorId).acceptedAuthorizeRequests++; logger.info(this.logPrefix(connectorId) + ' start transaction for idTag ' + idTag); // Start transaction - startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, idTag); + startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction( + connectorId, + idTag + ); PerformanceStatistics.endMeasure(measureId, beginId); return startResponse; } @@ -187,7 +291,10 @@ export default class AutomaticTransactionGenerator { } logger.info(this.logPrefix(connectorId) + ' start transaction for idTag ' + idTag); // Start transaction - startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction(connectorId, idTag); + startResponse = await this.chargingStation.ocppRequestService.sendStartTransaction( + connectorId, + idTag + ); PerformanceStatistics.endMeasure(measureId, beginId); return startResponse; } @@ -197,20 +304,29 @@ export default class AutomaticTransactionGenerator { return startResponse; } - private async stopTransaction(connectorId: number, reason: StopTransactionReason = StopTransactionReason.NONE): Promise { + private async stopTransaction( + connectorId: number, + reason: StopTransactionReason = StopTransactionReason.NONE + ): Promise { const measureId = 'StopTransaction with ATG'; const beginId = PerformanceStatistics.beginMeasure(measureId); let transactionId = 0; let stopResponse: StopTransactionResponse; if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted) { transactionId = this.chargingStation.getConnectorStatus(connectorId).transactionId; - stopResponse = await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, + stopResponse = await this.chargingStation.ocppRequestService.sendStopTransaction( + transactionId, this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), this.chargingStation.getTransactionIdTag(transactionId), - reason); + reason + ); this.connectorsStatus.get(connectorId).stopTransactionRequests++; } else { - logger.warn(`${this.logPrefix(connectorId)} trying to stop a not started transaction${transactionId ? ' ' + transactionId.toString() : ''}`); + logger.warn( + `${this.logPrefix(connectorId)} trying to stop a not started transaction${ + transactionId ? ' ' + transactionId.toString() : '' + }` + ); } PerformanceStatistics.endMeasure(measureId, beginId); return stopResponse; @@ -218,7 +334,13 @@ export default class AutomaticTransactionGenerator { private logPrefix(connectorId?: number): string { if (connectorId) { - return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG on connector #' + connectorId.toString() + ':'); + return Utils.logPrefix( + ' ' + + this.chargingStation.stationInfo.chargingStationId + + ' | ATG on connector #' + + connectorId.toString() + + ':' + ); } return Utils.logPrefix(' ' + this.chargingStation.stationInfo.chargingStationId + ' | ATG:'); } diff --git a/src/charging-station/Bootstrap.ts b/src/charging-station/Bootstrap.ts index fa8f73eb..d39c705c 100644 --- a/src/charging-station/Bootstrap.ts +++ b/src/charging-station/Bootstrap.ts @@ -1,6 +1,10 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. -import { ChargingStationWorkerData, ChargingStationWorkerMessage, ChargingStationWorkerMessageEvents } from '../types/ChargingStationWorker'; +import { + ChargingStationWorkerData, + ChargingStationWorkerMessage, + ChargingStationWorkerMessageEvents, +} from '../types/ChargingStationWorker'; import Configuration from '../utils/Configuration'; import { StationTemplateUrl } from '../types/ConfigurationData'; @@ -29,16 +33,23 @@ export default class Bootstrap { private constructor() { this.started = false; - this.workerScript = path.join(path.resolve(__dirname, '../'), 'charging-station', 'ChargingStationWorker.js'); + this.workerScript = path.join( + path.resolve(__dirname, '../'), + 'charging-station', + 'ChargingStationWorker.js' + ); this.initWorkerImplementation(); - Configuration.getUIWebSocketServer().enabled && (this.uiWebSocketServer = new UIWebSocketServer({ - ...Configuration.getUIWebSocketServer().options, handleProtocols: UIServiceUtils.handleProtocols - })); - Configuration.getPerformanceStorage().enabled && (this.storage = StorageFactory.getStorage( - Configuration.getPerformanceStorage().type, - Configuration.getPerformanceStorage().uri, - this.logPrefix() - )); + Configuration.getUIWebSocketServer().enabled && + (this.uiWebSocketServer = new UIWebSocketServer({ + ...Configuration.getUIWebSocketServer().options, + handleProtocols: UIServiceUtils.handleProtocols, + })); + Configuration.getPerformanceStorage().enabled && + (this.storage = StorageFactory.getStorage( + Configuration.getPerformanceStorage().type, + Configuration.getPerformanceStorage().uri, + this.logPrefix() + )); Configuration.setConfigurationChangeCallback(async () => Bootstrap.getInstance().restart()); } @@ -66,16 +77,39 @@ export default class Bootstrap { await this.startChargingStation(index, stationTemplateUrl); } } catch (error) { - console.error(chalk.red('Charging station start with template file ' + stationTemplateUrl.file + ' error '), error); + console.error( + chalk.red( + 'Charging station start with template file ' + stationTemplateUrl.file + ' error ' + ), + error + ); } } } else { console.warn(chalk.yellow('No stationTemplateUrls defined in configuration, exiting')); } if (this.numberOfChargingStations === 0) { - console.warn(chalk.yellow('No charging station template enabled in configuration, exiting')); + console.warn( + chalk.yellow('No charging station template enabled in configuration, exiting') + ); } else { - console.log(chalk.green(`Charging stations simulator ${this.version} started with ${this.numberOfChargingStations.toString()} charging station(s) and ${Utils.workerDynamicPoolInUse() ? `${Configuration.getWorkerPoolMinSize().toString()}/` : ''}${this.workerImplementation.size}${Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode${this.workerImplementation.maxElementsPerWorker ? ` (${this.workerImplementation.maxElementsPerWorker} charging station(s) per worker)` : ''}`)); + console.log( + chalk.green( + `Charging stations simulator ${ + this.version + } started with ${this.numberOfChargingStations.toString()} charging station(s) and ${ + Utils.workerDynamicPoolInUse() + ? `${Configuration.getWorkerPoolMinSize().toString()}/` + : '' + }${this.workerImplementation.size}${ + Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : '' + } worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode${ + this.workerImplementation.maxElementsPerWorker + ? ` (${this.workerImplementation.maxElementsPerWorker} charging station(s) per worker)` + : '' + }` + ) + ); } this.started = true; } catch (error) { @@ -104,7 +138,9 @@ export default class Bootstrap { } private initWorkerImplementation(): void { - this.workerImplementation = WorkerFactory.getWorkerImplementation(this.workerScript, Configuration.getWorkerProcess(), + this.workerImplementation = WorkerFactory.getWorkerImplementation( + this.workerScript, + Configuration.getWorkerProcess(), { workerStartDelay: Configuration.getWorkerStartDelay(), elementStartDelay: Configuration.getElementStartDelay(), @@ -112,7 +148,7 @@ export default class Bootstrap { poolMinSize: Configuration.getWorkerPoolMinSize(), elementsPerWorker: Configuration.getChargingStationsPerWorker(), poolOptions: { - workerChoiceStrategy: Configuration.getWorkerPoolStrategy() + workerChoiceStrategy: Configuration.getWorkerPoolStrategy(), }, messageHandler: async (msg: ChargingStationWorkerMessage) => { if (msg.id === ChargingStationWorkerMessageEvents.STARTED) { @@ -122,14 +158,23 @@ export default class Bootstrap { } else if (msg.id === ChargingStationWorkerMessageEvents.PERFORMANCE_STATISTICS) { await this.storage.storePerformanceStatistics(msg.data as unknown as Statistics); } - } - }); + }, + } + ); } - private async startChargingStation(index: number, stationTemplateUrl: StationTemplateUrl): Promise { + private async startChargingStation( + index: number, + stationTemplateUrl: StationTemplateUrl + ): Promise { const workerData: ChargingStationWorkerData = { index, - templateFile: path.join(path.resolve(__dirname, '../'), 'assets', 'station-templates', path.basename(stationTemplateUrl.file)) + templateFile: path.join( + path.resolve(__dirname, '../'), + 'assets', + 'station-templates', + path.basename(stationTemplateUrl.file) + ), }; await this.workerImplementation.addElement(workerData); this.numberOfChargingStations++; diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index f7e654f9..8d5cac1d 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -1,10 +1,28 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. -import { AvailabilityType, BootNotificationRequest, CachedRequest, IncomingRequest, IncomingRequestCommand, RequestCommand } from '../types/ocpp/Requests'; +import { + AvailabilityType, + BootNotificationRequest, + CachedRequest, + IncomingRequest, + IncomingRequestCommand, + RequestCommand, +} from '../types/ocpp/Requests'; import { BootNotificationResponse, RegistrationStatus } from '../types/ocpp/Responses'; -import ChargingStationConfiguration, { ConfigurationKey } from '../types/ChargingStationConfiguration'; -import ChargingStationTemplate, { CurrentType, PowerUnits, Voltage } from '../types/ChargingStationTemplate'; -import { ConnectorPhaseRotation, StandardParametersKey, SupportedFeatureProfiles, VendorDefaultParametersKey } from '../types/ocpp/Configuration'; +import ChargingStationConfiguration, { + ConfigurationKey, +} from '../types/ChargingStationConfiguration'; +import ChargingStationTemplate, { + CurrentType, + PowerUnits, + Voltage, +} from '../types/ChargingStationTemplate'; +import { + ConnectorPhaseRotation, + StandardParametersKey, + SupportedFeatureProfiles, + VendorDefaultParametersKey, +} from '../types/ocpp/Configuration'; import { MeterValueMeasurand, MeterValuePhase } from '../types/ocpp/MeterValues'; import { WSError, WebSocketCloseEventStatusCode } from '../types/WebSocket'; import WebSocket, { ClientOptions, Data, OPEN } from 'ws'; @@ -81,7 +99,15 @@ export default class ChargingStation { } get wsConnectionUrl(): URL { - return this.getSupervisionUrlOcppConfiguration() ? new URL(this.getConfigurationKey(this.stationInfo.supervisionUrlOcppKey ?? VendorDefaultParametersKey.ConnectionUrl).value + '/' + this.stationInfo.chargingStationId) : this.wsConfiguredConnectionUrl; + return this.getSupervisionUrlOcppConfiguration() + ? new URL( + this.getConfigurationKey( + this.stationInfo.supervisionUrlOcppKey ?? VendorDefaultParametersKey.ConnectionUrl + ).value + + '/' + + this.stationInfo.chargingStationId + ) + : this.wsConfiguredConnectionUrl; } public logPrefix(): string { @@ -102,7 +128,9 @@ export default class ChargingStation { } public getEnableStatistics(): boolean | undefined { - return !Utils.isUndefined(this.stationInfo.enableStatistics) ? this.stationInfo.enableStatistics : true; + return !Utils.isUndefined(this.stationInfo.enableStatistics) + ? this.stationInfo.enableStatistics + : true; } public getMayAuthorizeAtRemoteStart(): boolean | undefined { @@ -112,7 +140,9 @@ export default class ChargingStation { public getNumberOfPhases(): number | undefined { switch (this.getCurrentOutType()) { case CurrentType.AC: - return !Utils.isUndefined(this.stationInfo.numberOfPhases) ? this.stationInfo.numberOfPhases : 3; + return !Utils.isUndefined(this.stationInfo.numberOfPhases) + ? this.stationInfo.numberOfPhases + : 3; case CurrentType.DC: return 0; } @@ -171,7 +201,9 @@ export default class ChargingStation { } public getVoltageOut(): number | undefined { - const errMsg = `${this.logPrefix()} Unknown ${this.getCurrentOutType()} currentOutType in template file ${this.stationTemplateFile}, cannot define default voltage out`; + const errMsg = `${this.logPrefix()} Unknown ${this.getCurrentOutType()} currentOutType in template file ${ + this.stationTemplateFile + }, cannot define default voltage out`; let defaultVoltageOut: number; switch (this.getCurrentOutType()) { case CurrentType.AC: @@ -184,7 +216,9 @@ export default class ChargingStation { logger.error(errMsg); throw new Error(errMsg); } - return !Utils.isUndefined(this.stationInfo.voltageOut) ? this.stationInfo.voltageOut : defaultVoltageOut; + return !Utils.isUndefined(this.stationInfo.voltageOut) + ? this.stationInfo.voltageOut + : defaultVoltageOut; } public getTransactionIdTag(transactionId: number): string | undefined { @@ -222,7 +256,10 @@ export default class ChargingStation { public getEnergyActiveImportRegisterByTransactionId(transactionId: number): number | undefined { if (this.getMeteringPerTransaction()) { for (const connectorId of this.connectors.keys()) { - if (connectorId > 0 && this.getConnectorStatus(connectorId).transactionId === transactionId) { + if ( + connectorId > 0 && + this.getConnectorStatus(connectorId).transactionId === transactionId + ) { return this.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue; } } @@ -242,12 +279,18 @@ export default class ChargingStation { } public getAuthorizeRemoteTxRequests(): boolean { - const authorizeRemoteTxRequests = this.getConfigurationKey(StandardParametersKey.AuthorizeRemoteTxRequests); - return authorizeRemoteTxRequests ? Utils.convertToBoolean(authorizeRemoteTxRequests.value) : false; + const authorizeRemoteTxRequests = this.getConfigurationKey( + StandardParametersKey.AuthorizeRemoteTxRequests + ); + return authorizeRemoteTxRequests + ? Utils.convertToBoolean(authorizeRemoteTxRequests.value) + : false; } public getLocalAuthListEnabled(): boolean { - const localAuthListEnabled = this.getConfigurationKey(StandardParametersKey.LocalAuthListEnabled); + const localAuthListEnabled = this.getConfigurationKey( + StandardParametersKey.LocalAuthListEnabled + ); return localAuthListEnabled ? Utils.convertToBoolean(localAuthListEnabled.value) : false; } @@ -258,29 +301,70 @@ export default class ChargingStation { this.startWebSocketPing(); } - public getSampledValueTemplate(connectorId: number, measurand: MeterValueMeasurand = MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, - phase?: MeterValuePhase): SampledValueTemplate | undefined { + public getSampledValueTemplate( + connectorId: number, + measurand: MeterValueMeasurand = MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, + phase?: MeterValuePhase + ): SampledValueTemplate | undefined { const onPhaseStr = phase ? `on phase ${phase} ` : ''; if (!Constants.SUPPORTED_MEASURANDS.includes(measurand)) { - logger.warn(`${this.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`); + logger.warn( + `${this.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}` + ); return; } - if (measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER && !this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(measurand)) { - logger.debug(`${this.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId} not found in '${StandardParametersKey.MeterValuesSampledData}' OCPP parameter`); + if ( + measurand !== MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER && + !this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes( + measurand + ) + ) { + logger.debug( + `${this.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId} not found in '${ + StandardParametersKey.MeterValuesSampledData + }' OCPP parameter` + ); return; } - const sampledValueTemplates: SampledValueTemplate[] = this.getConnectorStatus(connectorId).MeterValues; - for (let index = 0; !Utils.isEmptyArray(sampledValueTemplates) && index < sampledValueTemplates.length; index++) { - if (!Constants.SUPPORTED_MEASURANDS.includes(sampledValueTemplates[index]?.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER)) { - logger.warn(`${this.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`); - } else if (phase && sampledValueTemplates[index]?.phase === phase && sampledValueTemplates[index]?.measurand === measurand - && this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(measurand)) { + const sampledValueTemplates: SampledValueTemplate[] = + this.getConnectorStatus(connectorId).MeterValues; + for ( + let index = 0; + !Utils.isEmptyArray(sampledValueTemplates) && index < sampledValueTemplates.length; + index++ + ) { + if ( + !Constants.SUPPORTED_MEASURANDS.includes( + sampledValueTemplates[index]?.measurand ?? + MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + ) + ) { + logger.warn( + `${this.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}` + ); + } else if ( + phase && + sampledValueTemplates[index]?.phase === phase && + sampledValueTemplates[index]?.measurand === measurand && + this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes( + measurand + ) + ) { return sampledValueTemplates[index]; - } else if (!phase && !sampledValueTemplates[index].phase && sampledValueTemplates[index]?.measurand === measurand - && this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(measurand)) { + } else if ( + !phase && + !sampledValueTemplates[index].phase && + sampledValueTemplates[index]?.measurand === measurand && + this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes( + measurand + ) + ) { return sampledValueTemplates[index]; - } else if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER - && (!sampledValueTemplates[index].measurand || sampledValueTemplates[index].measurand === measurand)) { + } else if ( + measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER && + (!sampledValueTemplates[index].measurand || + sampledValueTemplates[index].measurand === measurand) + ) { return sampledValueTemplates[index]; } } @@ -289,7 +373,9 @@ export default class ChargingStation { logger.error(errorMsg); throw new Error(errorMsg); } - logger.debug(`${this.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`); + logger.debug( + `${this.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}` + ); } public getAutomaticTransactionGeneratorRequireAuthorize(): boolean { @@ -297,16 +383,34 @@ export default class ChargingStation { } public startHeartbeat(): void { - if (this.getHeartbeatInterval() && this.getHeartbeatInterval() > 0 && !this.heartbeatSetInterval) { + if ( + this.getHeartbeatInterval() && + this.getHeartbeatInterval() > 0 && + !this.heartbeatSetInterval + ) { // eslint-disable-next-line @typescript-eslint/no-misused-promises this.heartbeatSetInterval = setInterval(async (): Promise => { await this.ocppRequestService.sendHeartbeat(); }, this.getHeartbeatInterval()); - logger.info(this.logPrefix() + ' Heartbeat started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); + logger.info( + this.logPrefix() + + ' Heartbeat started every ' + + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) + ); } else if (this.heartbeatSetInterval) { - logger.info(this.logPrefix() + ' Heartbeat already started every ' + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval())); + logger.info( + this.logPrefix() + + ' Heartbeat already started every ' + + Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) + ); } else { - logger.error(`${this.logPrefix()} Heartbeat interval set to ${this.getHeartbeatInterval() ? Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) : this.getHeartbeatInterval()}, not starting the heartbeat`); + logger.error( + `${this.logPrefix()} Heartbeat interval set to ${ + this.getHeartbeatInterval() + ? Utils.formatDurationMilliSeconds(this.getHeartbeatInterval()) + : this.getHeartbeatInterval() + }, not starting the heartbeat` + ); } } @@ -319,27 +423,51 @@ export default class ChargingStation { public startMeterValues(connectorId: number, interval: number): void { if (connectorId === 0) { - logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId.toString()}`); + logger.error( + `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId.toString()}` + ); return; } if (!this.getConnectorStatus(connectorId)) { - logger.error(`${this.logPrefix()} Trying to start MeterValues on non existing connector Id ${connectorId.toString()}`); + logger.error( + `${this.logPrefix()} Trying to start MeterValues on non existing connector Id ${connectorId.toString()}` + ); return; } if (!this.getConnectorStatus(connectorId)?.transactionStarted) { - logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction started`); + logger.error( + `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction started` + ); return; - } else if (this.getConnectorStatus(connectorId)?.transactionStarted && !this.getConnectorStatus(connectorId)?.transactionId) { - logger.error(`${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction id`); + } else if ( + this.getConnectorStatus(connectorId)?.transactionStarted && + !this.getConnectorStatus(connectorId)?.transactionId + ) { + logger.error( + `${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-misused-promises - this.getConnectorStatus(connectorId).transactionSetInterval = setInterval(async (): Promise => { - await this.ocppRequestService.sendMeterValues(connectorId, this.getConnectorStatus(connectorId).transactionId, interval); - }, interval); + this.getConnectorStatus(connectorId).transactionSetInterval = setInterval( + async (): Promise => { + await this.ocppRequestService.sendMeterValues( + connectorId, + this.getConnectorStatus(connectorId).transactionId, + interval + ); + }, + interval + ); } else { - logger.error(`${this.logPrefix()} Charging station ${StandardParametersKey.MeterValueSampleInterval} configuration set to ${interval ? Utils.formatDurationMilliSeconds(interval) : interval}, not sending MeterValues`); + logger.error( + `${this.logPrefix()} Charging station ${ + StandardParametersKey.MeterValueSampleInterval + } configuration set to ${ + interval ? Utils.formatDurationMilliSeconds(interval) : interval + }, not sending MeterValues` + ); } } @@ -364,7 +492,10 @@ export default class ChargingStation { this.wsConnection.on('ping', this.onPing.bind(this)); // Handle WebSocket pong this.wsConnection.on('pong', this.onPong.bind(this)); - parentPort.postMessage({ id: ChargingStationWorkerMessageEvents.STARTED, data: { id: this.stationInfo.chargingStationId } }); + parentPort.postMessage({ + id: ChargingStationWorkerMessageEvents.STARTED, + data: { id: this.stationInfo.chargingStationId }, + }); } public async stop(reason: StopTransactionReason = StopTransactionReason.NONE): Promise { @@ -372,7 +503,10 @@ export default class ChargingStation { await this.stopMessageSequence(reason); for (const connectorId of this.connectors.keys()) { if (connectorId > 0) { - await this.ocppRequestService.sendStatusNotification(connectorId, ChargePointStatus.UNAVAILABLE); + await this.ocppRequestService.sendStatusNotification( + connectorId, + ChargePointStatus.UNAVAILABLE + ); this.getConnectorStatus(connectorId).status = ChargePointStatus.UNAVAILABLE; } } @@ -383,11 +517,17 @@ export default class ChargingStation { this.performanceStatistics.stop(); } this.bootNotificationResponse = null; - parentPort.postMessage({ id: ChargingStationWorkerMessageEvents.STOPPED, data: { id: this.stationInfo.chargingStationId } }); + parentPort.postMessage({ + id: ChargingStationWorkerMessageEvents.STOPPED, + data: { id: this.stationInfo.chargingStationId }, + }); this.stopped = true; } - public getConfigurationKey(key: string | StandardParametersKey, caseInsensitive = false): ConfigurationKey | undefined { + public getConfigurationKey( + key: string | StandardParametersKey, + caseInsensitive = false + ): ConfigurationKey | undefined { return this.configuration.configurationKey.find((configElement) => { if (caseInsensitive) { return configElement.key.toLowerCase() === key.toLowerCase(); @@ -396,7 +536,15 @@ export default class ChargingStation { }); } - public addConfigurationKey(key: string | StandardParametersKey, value: string, options: { readonly?: boolean, visible?: boolean, reboot?: boolean } = { readonly: false, visible: true, reboot: false }): void { + public addConfigurationKey( + key: string | StandardParametersKey, + value: string, + options: { readonly?: boolean; visible?: boolean; reboot?: boolean } = { + readonly: false, + visible: true, + reboot: false, + } + ): void { const keyFound = this.getConfigurationKey(key); const readonly = options.readonly; const visible = options.visible; @@ -410,7 +558,10 @@ export default class ChargingStation { reboot, }); } else { - logger.error(`${this.logPrefix()} Trying to add an already existing configuration key: %j`, keyFound); + logger.error( + `${this.logPrefix()} Trying to add an already existing configuration key: %j`, + keyFound + ); } } @@ -420,20 +571,28 @@ export default class ChargingStation { const keyIndex = this.configuration.configurationKey.indexOf(keyFound); this.configuration.configurationKey[keyIndex].value = value; } else { - logger.error(`${this.logPrefix()} Trying to set a value on a non existing configuration key: %j`, { key, value }); + logger.error( + `${this.logPrefix()} Trying to set a value on a non existing configuration key: %j`, + { key, value } + ); } } public setChargingProfile(connectorId: number, cp: ChargingProfile): void { let cpReplaced = false; if (!Utils.isEmptyArray(this.getConnectorStatus(connectorId).chargingProfiles)) { - this.getConnectorStatus(connectorId).chargingProfiles?.forEach((chargingProfile: ChargingProfile, index: number) => { - if (chargingProfile.chargingProfileId === cp.chargingProfileId - || (chargingProfile.stackLevel === cp.stackLevel && chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose)) { - this.getConnectorStatus(connectorId).chargingProfiles[index] = cp; - cpReplaced = true; + this.getConnectorStatus(connectorId).chargingProfiles?.forEach( + (chargingProfile: ChargingProfile, index: number) => { + if ( + chargingProfile.chargingProfileId === cp.chargingProfileId || + (chargingProfile.stackLevel === cp.stackLevel && + chargingProfile.chargingProfilePurpose === cp.chargingProfilePurpose) + ) { + this.getConnectorStatus(connectorId).chargingProfiles[index] = cp; + cpReplaced = true; + } } - }); + ); } !cpReplaced && this.getConnectorStatus(connectorId).chargingProfiles?.push(cp); } @@ -474,7 +633,15 @@ export default class ChargingStation { // In case of multiple instances: add instance index to charging station id const instanceIndex = process.env.CF_INSTANCE_INDEX ?? 0; const idSuffix = stationTemplate.nameSuffix ?? ''; - return stationTemplate.fixedName ? stationTemplate.baseName : stationTemplate.baseName + '-' + instanceIndex.toString() + ('000000000' + this.index.toString()).substr(('000000000' + this.index.toString()).length - 4) + idSuffix; + return stationTemplate.fixedName + ? stationTemplate.baseName + : stationTemplate.baseName + + '-' + + instanceIndex.toString() + + ('000000000' + this.index.toString()).substr( + ('000000000' + this.index.toString()).length - 4 + ) + + idSuffix; } private buildStationInfo(): ChargingStationInfo { @@ -482,33 +649,51 @@ export default class ChargingStation { try { // Load template file const fileDescriptor = fs.openSync(this.stationTemplateFile, 'r'); - stationTemplateFromFile = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')) as ChargingStationTemplate; + stationTemplateFromFile = JSON.parse( + fs.readFileSync(fileDescriptor, 'utf8') + ) as ChargingStationTemplate; fs.closeSync(fileDescriptor); } catch (error) { - FileUtils.handleFileException(this.logPrefix(), 'Template', this.stationTemplateFile, error as NodeJS.ErrnoException); + FileUtils.handleFileException( + this.logPrefix(), + 'Template', + this.stationTemplateFile, + error as NodeJS.ErrnoException + ); } const chargingStationId = this.getChargingStationId(stationTemplateFromFile); // Deprecation template keys section - this.warnDeprecatedTemplateKey(stationTemplateFromFile, 'supervisionUrl', chargingStationId, 'Use \'supervisionUrls\' instead'); + this.warnDeprecatedTemplateKey( + stationTemplateFromFile, + 'supervisionUrl', + chargingStationId, + "Use 'supervisionUrls' instead" + ); this.convertDeprecatedTemplateKey(stationTemplateFromFile, 'supervisionUrl', 'supervisionUrls'); - const stationInfo: ChargingStationInfo = stationTemplateFromFile ?? {} as ChargingStationInfo; + const stationInfo: ChargingStationInfo = stationTemplateFromFile ?? ({} as ChargingStationInfo); stationInfo.wsOptions = stationTemplateFromFile?.wsOptions ?? {}; if (!Utils.isEmptyArray(stationTemplateFromFile.power)) { stationTemplateFromFile.power = stationTemplateFromFile.power as number[]; - const powerArrayRandomIndex = Math.floor(Utils.secureRandom() * stationTemplateFromFile.power.length); - stationInfo.maxPower = stationTemplateFromFile.powerUnit === PowerUnits.KILO_WATT - ? stationTemplateFromFile.power[powerArrayRandomIndex] * 1000 - : stationTemplateFromFile.power[powerArrayRandomIndex]; + const powerArrayRandomIndex = Math.floor( + Utils.secureRandom() * stationTemplateFromFile.power.length + ); + stationInfo.maxPower = + stationTemplateFromFile.powerUnit === PowerUnits.KILO_WATT + ? stationTemplateFromFile.power[powerArrayRandomIndex] * 1000 + : stationTemplateFromFile.power[powerArrayRandomIndex]; } else { stationTemplateFromFile.power = stationTemplateFromFile.power as number; - stationInfo.maxPower = stationTemplateFromFile.powerUnit === PowerUnits.KILO_WATT - ? stationTemplateFromFile.power * 1000 - : stationTemplateFromFile.power; + stationInfo.maxPower = + stationTemplateFromFile.powerUnit === PowerUnits.KILO_WATT + ? stationTemplateFromFile.power * 1000 + : stationTemplateFromFile.power; } delete stationInfo.power; delete stationInfo.powerUnit; stationInfo.chargingStationId = chargingStationId; - stationInfo.resetTime = stationTemplateFromFile.resetTime ? stationTemplateFromFile.resetTime * 1000 : Constants.CHARGING_STATION_DEFAULT_RESET_TIME; + stationInfo.resetTime = stationTemplateFromFile.resetTime + ? stationTemplateFromFile.resetTime * 1000 + : Constants.CHARGING_STATION_DEFAULT_RESET_TIME; return stationInfo; } @@ -517,7 +702,9 @@ export default class ChargingStation { } private handleUnsupportedVersion(version: OCPPVersion) { - const errMsg = `${this.logPrefix()} Unsupported protocol version '${version}' configured in template file ${this.stationTemplateFile}`; + const errMsg = `${this.logPrefix()} Unsupported protocol version '${version}' configured in template file ${ + this.stationTemplateFile + }`; logger.error(errMsg); throw new Error(errMsg); } @@ -529,37 +716,72 @@ export default class ChargingStation { this.bootNotificationRequest = { chargePointModel: this.stationInfo.chargePointModel, chargePointVendor: this.stationInfo.chargePointVendor, - ...!Utils.isUndefined(this.stationInfo.chargeBoxSerialNumberPrefix) && { chargeBoxSerialNumber: this.stationInfo.chargeBoxSerialNumberPrefix }, - ...!Utils.isUndefined(this.stationInfo.firmwareVersion) && { firmwareVersion: this.stationInfo.firmwareVersion }, + ...(!Utils.isUndefined(this.stationInfo.chargeBoxSerialNumberPrefix) && { + chargeBoxSerialNumber: this.stationInfo.chargeBoxSerialNumberPrefix, + }), + ...(!Utils.isUndefined(this.stationInfo.firmwareVersion) && { + firmwareVersion: this.stationInfo.firmwareVersion, + }), }; // Build connectors if needed const maxConnectors = this.getMaxNumberOfConnectors(); if (maxConnectors <= 0) { - logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with ${maxConnectors} connectors`); + logger.warn( + `${this.logPrefix()} Charging station template ${ + this.stationTemplateFile + } with ${maxConnectors} connectors` + ); } const templateMaxConnectors = this.getTemplateMaxNumberOfConnectors(); if (templateMaxConnectors <= 0) { - logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector configuration`); + logger.warn( + `${this.logPrefix()} Charging station template ${ + this.stationTemplateFile + } with no connector configuration` + ); } if (!this.stationInfo.Connectors[0]) { - logger.warn(`${this.logPrefix()} Charging station template ${this.stationTemplateFile} with no connector Id 0 configuration`); + logger.warn( + `${this.logPrefix()} Charging station template ${ + this.stationTemplateFile + } with no connector Id 0 configuration` + ); } // Sanity check - if (maxConnectors > (this.stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) && !this.stationInfo.randomConnectors) { - logger.warn(`${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${this.stationTemplateFile}, forcing random connector configurations affectation`); + if ( + maxConnectors > + (this.stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) && + !this.stationInfo.randomConnectors + ) { + logger.warn( + `${this.logPrefix()} Number of connectors exceeds the number of connector configurations in template ${ + this.stationTemplateFile + }, forcing random connector configurations affectation` + ); this.stationInfo.randomConnectors = true; } - const connectorsConfigHash = crypto.createHash('sha256').update(JSON.stringify(this.stationInfo.Connectors) + maxConnectors.toString()).digest('hex'); - const connectorsConfigChanged = this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash; + const connectorsConfigHash = crypto + .createHash('sha256') + .update(JSON.stringify(this.stationInfo.Connectors) + maxConnectors.toString()) + .digest('hex'); + const connectorsConfigChanged = + this.connectors?.size !== 0 && this.connectorsConfigurationHash !== connectorsConfigHash; if (this.connectors?.size === 0 || connectorsConfigChanged) { - connectorsConfigChanged && (this.connectors.clear()); + connectorsConfigChanged && this.connectors.clear(); this.connectorsConfigurationHash = connectorsConfigHash; // Add connector Id 0 let lastConnector = '0'; for (lastConnector in this.stationInfo.Connectors) { const lastConnectorId = Utils.convertToInt(lastConnector); - if (lastConnectorId === 0 && this.getUseConnectorId0() && this.stationInfo.Connectors[lastConnector]) { - this.connectors.set(lastConnectorId, Utils.cloneObject(this.stationInfo.Connectors[lastConnector])); + if ( + lastConnectorId === 0 && + this.getUseConnectorId0() && + this.stationInfo.Connectors[lastConnector] + ) { + this.connectors.set( + lastConnectorId, + Utils.cloneObject(this.stationInfo.Connectors[lastConnector]) + ); this.getConnectorStatus(lastConnectorId).availability = AvailabilityType.OPERATIVE; if (Utils.isUndefined(this.getConnectorStatus(lastConnectorId)?.chargingProfiles)) { this.getConnectorStatus(lastConnectorId).chargingProfiles = []; @@ -567,10 +789,17 @@ export default class ChargingStation { } } // Generate all connectors - if ((this.stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) > 0) { + if ( + (this.stationInfo.Connectors[0] ? templateMaxConnectors - 1 : templateMaxConnectors) > 0 + ) { for (let index = 1; index <= maxConnectors; index++) { - const randConnectorId = this.stationInfo.randomConnectors ? Utils.getRandomInteger(Utils.convertToInt(lastConnector), 1) : index; - this.connectors.set(index, Utils.cloneObject(this.stationInfo.Connectors[randConnectorId])); + const randConnectorId = this.stationInfo.randomConnectors + ? Utils.getRandomInteger(Utils.convertToInt(lastConnector), 1) + : index; + this.connectors.set( + index, + Utils.cloneObject(this.stationInfo.Connectors[randConnectorId]) + ); this.getConnectorStatus(index).availability = AvailabilityType.OPERATIVE; if (Utils.isUndefined(this.getConnectorStatus(index)?.chargingProfiles)) { this.getConnectorStatus(index).chargingProfiles = []; @@ -586,11 +815,17 @@ export default class ChargingStation { this.initializeConnectorStatus(connectorId); } } - this.wsConfiguredConnectionUrl = new URL(this.getConfiguredSupervisionUrl().href + '/' + this.stationInfo.chargingStationId); + this.wsConfiguredConnectionUrl = new URL( + this.getConfiguredSupervisionUrl().href + '/' + this.stationInfo.chargingStationId + ); switch (this.getOcppVersion()) { case OCPPVersion.VERSION_16: - this.ocppIncomingRequestService = OCPP16IncomingRequestService.getInstance(this); - this.ocppRequestService = OCPP16RequestService.getInstance(this, OCPP16ResponseService.getInstance(this)); + this.ocppIncomingRequestService = + OCPP16IncomingRequestService.getInstance(this); + this.ocppRequestService = OCPP16RequestService.getInstance( + this, + OCPP16ResponseService.getInstance(this) + ); break; default: this.handleUnsupportedVersion(this.getOcppVersion()); @@ -602,25 +837,48 @@ export default class ChargingStation { this.bootNotificationResponse = { currentTime: new Date().toISOString(), interval: this.getHeartbeatInterval() / 1000, - status: RegistrationStatus.ACCEPTED + status: RegistrationStatus.ACCEPTED, }; } this.stationInfo.powerDivider = this.getPowerDivider(); if (this.getEnableStatistics()) { - this.performanceStatistics = PerformanceStatistics.getInstance(this.id, this.stationInfo.chargingStationId, this.wsConnectionUrl); + this.performanceStatistics = PerformanceStatistics.getInstance( + this.id, + this.stationInfo.chargingStationId, + this.wsConnectionUrl + ); } } private initOcppParameters(): void { - if (this.getSupervisionUrlOcppConfiguration() && !this.getConfigurationKey(this.stationInfo.supervisionUrlOcppKey ?? VendorDefaultParametersKey.ConnectionUrl)) { - this.addConfigurationKey(VendorDefaultParametersKey.ConnectionUrl, this.getConfiguredSupervisionUrl().href, { reboot: true }); + if ( + this.getSupervisionUrlOcppConfiguration() && + !this.getConfigurationKey( + this.stationInfo.supervisionUrlOcppKey ?? VendorDefaultParametersKey.ConnectionUrl + ) + ) { + this.addConfigurationKey( + VendorDefaultParametersKey.ConnectionUrl, + this.getConfiguredSupervisionUrl().href, + { reboot: true } + ); } if (!this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles)) { - this.addConfigurationKey(StandardParametersKey.SupportedFeatureProfiles, `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.Local_Auth_List_Management},${SupportedFeatureProfiles.Smart_Charging}`); - } - this.addConfigurationKey(StandardParametersKey.NumberOfConnectors, this.getNumberOfConnectors().toString(), { readonly: true }); + this.addConfigurationKey( + StandardParametersKey.SupportedFeatureProfiles, + `${SupportedFeatureProfiles.Core},${SupportedFeatureProfiles.Local_Auth_List_Management},${SupportedFeatureProfiles.Smart_Charging}` + ); + } + this.addConfigurationKey( + StandardParametersKey.NumberOfConnectors, + this.getNumberOfConnectors().toString(), + { readonly: true } + ); if (!this.getConfigurationKey(StandardParametersKey.MeterValuesSampledData)) { - this.addConfigurationKey(StandardParametersKey.MeterValuesSampledData, MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER); + this.addConfigurationKey( + StandardParametersKey.MeterValuesSampledData, + MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + ); } if (!this.getConfigurationKey(StandardParametersKey.ConnectorPhaseRotation)) { const connectorPhaseRotation = []; @@ -630,40 +888,64 @@ export default class ChargingStation { connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.RST}`); } else if (connectorId > 0 && this.getNumberOfPhases() === 0) { connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.NotApplicable}`); - // AC + // AC } else if (connectorId > 0 && this.getNumberOfPhases() === 1) { connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.NotApplicable}`); } else if (connectorId > 0 && this.getNumberOfPhases() === 3) { connectorPhaseRotation.push(`${connectorId}.${ConnectorPhaseRotation.RST}`); } } - this.addConfigurationKey(StandardParametersKey.ConnectorPhaseRotation, connectorPhaseRotation.toString()); + this.addConfigurationKey( + StandardParametersKey.ConnectorPhaseRotation, + connectorPhaseRotation.toString() + ); } if (!this.getConfigurationKey(StandardParametersKey.AuthorizeRemoteTxRequests)) { this.addConfigurationKey(StandardParametersKey.AuthorizeRemoteTxRequests, 'true'); } - if (!this.getConfigurationKey(StandardParametersKey.LocalAuthListEnabled) - && this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles).value.includes(SupportedFeatureProfiles.Local_Auth_List_Management)) { + if ( + !this.getConfigurationKey(StandardParametersKey.LocalAuthListEnabled) && + this.getConfigurationKey(StandardParametersKey.SupportedFeatureProfiles).value.includes( + SupportedFeatureProfiles.Local_Auth_List_Management + ) + ) { this.addConfigurationKey(StandardParametersKey.LocalAuthListEnabled, 'false'); } if (!this.getConfigurationKey(StandardParametersKey.ConnectionTimeOut)) { - this.addConfigurationKey(StandardParametersKey.ConnectionTimeOut, Constants.DEFAULT_CONNECTION_TIMEOUT.toString()); + this.addConfigurationKey( + StandardParametersKey.ConnectionTimeOut, + Constants.DEFAULT_CONNECTION_TIMEOUT.toString() + ); } } private async onOpen(): Promise { - logger.info(`${this.logPrefix()} Connected to OCPP server through ${this.wsConnectionUrl.toString()}`); + logger.info( + `${this.logPrefix()} Connected to OCPP server through ${this.wsConnectionUrl.toString()}` + ); if (!this.isInAcceptedState()) { // Send BootNotification let registrationRetryCount = 0; do { - this.bootNotificationResponse = await this.ocppRequestService.sendBootNotification(this.bootNotificationRequest.chargePointModel, - this.bootNotificationRequest.chargePointVendor, this.bootNotificationRequest.chargeBoxSerialNumber, this.bootNotificationRequest.firmwareVersion); + this.bootNotificationResponse = await this.ocppRequestService.sendBootNotification( + this.bootNotificationRequest.chargePointModel, + this.bootNotificationRequest.chargePointVendor, + this.bootNotificationRequest.chargeBoxSerialNumber, + this.bootNotificationRequest.firmwareVersion + ); if (!this.isInAcceptedState()) { this.getRegistrationMaxRetries() !== -1 && registrationRetryCount++; - await Utils.sleep(this.bootNotificationResponse?.interval ? this.bootNotificationResponse.interval * 1000 : Constants.OCPP_DEFAULT_BOOT_NOTIFICATION_INTERVAL); + await Utils.sleep( + this.bootNotificationResponse?.interval + ? this.bootNotificationResponse.interval * 1000 + : Constants.OCPP_DEFAULT_BOOT_NOTIFICATION_INTERVAL + ); } - } while (!this.isInAcceptedState() && (registrationRetryCount <= this.getRegistrationMaxRetries() || this.getRegistrationMaxRetries() === -1)); + } while ( + !this.isInAcceptedState() && + (registrationRetryCount <= this.getRegistrationMaxRetries() || + this.getRegistrationMaxRetries() === -1) + ); } if (this.isInAcceptedState()) { await this.startMessageSequence(); @@ -672,7 +954,9 @@ export default class ChargingStation { this.flushMessageBuffer(); } } else { - logger.error(`${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})`); + logger.error( + `${this.logPrefix()} Registration failure: max retries reached (${this.getRegistrationMaxRetries()}) or retry disabled (${this.getRegistrationMaxRetries()})` + ); } this.autoReconnectRetryCount = 0; this.wsConnectionRestarted = false; @@ -683,20 +967,37 @@ export default class ChargingStation { // Normal close case WebSocketCloseEventStatusCode.CLOSE_NORMAL: case WebSocketCloseEventStatusCode.CLOSE_NO_STATUS: - logger.info(`${this.logPrefix()} WebSocket normally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); + logger.info( + `${this.logPrefix()} WebSocket normally closed with status '${Utils.getWebSocketCloseEventStatusString( + code + )}' and reason '${reason}'` + ); this.autoReconnectRetryCount = 0; break; // Abnormal close default: - logger.error(`${this.logPrefix()} WebSocket abnormally closed with status '${Utils.getWebSocketCloseEventStatusString(code)}' and reason '${reason}'`); + logger.error( + `${this.logPrefix()} WebSocket abnormally closed with status '${Utils.getWebSocketCloseEventStatusString( + code + )}' and reason '${reason}'` + ); await this.reconnect(code); break; } } private async onMessage(data: Data): Promise { - let [messageType, messageId, commandName, commandPayload, errorDetails]: IncomingRequest = [0, '', '' as IncomingRequestCommand, {}, {}]; - let responseCallback: (payload: JsonType | string, requestPayload: JsonType | OCPPError) => void; + let [messageType, messageId, commandName, commandPayload, errorDetails]: IncomingRequest = [ + 0, + '', + '' as IncomingRequestCommand, + {}, + {}, + ]; + let responseCallback: ( + payload: JsonType | string, + requestPayload: JsonType | OCPPError + ) => void; let rejectCallback: (error: OCPPError, requestStatistic?: boolean) => void; let requestCommandName: RequestCommand | IncomingRequestCommand; let requestPayload: JsonType | OCPPError; @@ -708,7 +1009,11 @@ export default class ChargingStation { // Parse the message [messageType, messageId, commandName, commandPayload, errorDetails] = request; } else { - throw new OCPPError(ErrorType.PROTOCOL_ERROR, 'Incoming request is not iterable', commandName); + throw new OCPPError( + ErrorType.PROTOCOL_ERROR, + 'Incoming request is not iterable', + commandName + ); } // Check the Type of message switch (messageType) { @@ -718,7 +1023,11 @@ export default class ChargingStation { this.performanceStatistics.addRequestStatistic(commandName, messageType); } // Process the call - await this.ocppIncomingRequestService.handleRequest(messageId, commandName, commandPayload); + await this.ocppIncomingRequestService.handleRequest( + messageId, + commandName, + commandPayload + ); break; // Outcome Message case MessageType.CALL_RESULT_MESSAGE: @@ -727,11 +1036,19 @@ export default class ChargingStation { if (Utils.isIterable(cachedRequest)) { [responseCallback, , , requestPayload] = cachedRequest; } else { - throw new OCPPError(ErrorType.PROTOCOL_ERROR, `Cached request for message id ${messageId} response is not iterable`, commandName); + throw new OCPPError( + ErrorType.PROTOCOL_ERROR, + `Cached request for message id ${messageId} response is not iterable`, + commandName + ); } if (!responseCallback) { // Error - throw new OCPPError(ErrorType.INTERNAL_ERROR, `Response for unknown message id ${messageId}`, commandName); + throw new OCPPError( + ErrorType.INTERNAL_ERROR, + `Response for unknown message id ${messageId}`, + commandName + ); } responseCallback(commandName, requestPayload); break; @@ -741,13 +1058,22 @@ export default class ChargingStation { if (Utils.isIterable(cachedRequest)) { [, rejectCallback, requestCommandName] = cachedRequest; } else { - throw new OCPPError(ErrorType.PROTOCOL_ERROR, `Cached request for message id ${messageId} error response is not iterable`); + throw new OCPPError( + ErrorType.PROTOCOL_ERROR, + `Cached request for message id ${messageId} error response is not iterable` + ); } if (!rejectCallback) { // Error - throw new OCPPError(ErrorType.INTERNAL_ERROR, `Error response for unknown message id ${messageId}`, requestCommandName); + throw new OCPPError( + ErrorType.INTERNAL_ERROR, + `Error response for unknown message id ${messageId}`, + requestCommandName + ); } - rejectCallback(new OCPPError(commandName, commandPayload.toString(), requestCommandName, errorDetails)); + rejectCallback( + new OCPPError(commandName, commandPayload.toString(), requestCommandName, errorDetails) + ); break; // Error default: @@ -757,9 +1083,16 @@ export default class ChargingStation { } } catch (error) { // Log - logger.error('%s Incoming OCPP message %j matching cached request %j processing error %j', this.logPrefix(), data.toString(), this.requests.get(messageId), error); + logger.error( + '%s Incoming OCPP message %j matching cached request %j processing error %j', + this.logPrefix(), + data.toString(), + this.requests.get(messageId), + error + ); // Send error - messageType === MessageType.CALL_MESSAGE && await this.ocppRequestService.sendError(messageId, error as OCPPError, commandName); + messageType === MessageType.CALL_MESSAGE && + (await this.ocppRequestService.sendError(messageId, error as OCPPError, commandName)); } } @@ -781,11 +1114,18 @@ export default class ChargingStation { } private getTemplateChargingStationConfiguration(): ChargingStationConfiguration { - return this.stationInfo.Configuration ?? {} as ChargingStationConfiguration; + return this.stationInfo.Configuration ?? ({} as ChargingStationConfiguration); } private getAuthorizationFile(): string | undefined { - return this.stationInfo.authorizationFile && path.join(path.resolve(__dirname, '../'), 'assets', path.basename(this.stationInfo.authorizationFile)); + return ( + this.stationInfo.authorizationFile && + path.join( + path.resolve(__dirname, '../'), + 'assets', + path.basename(this.stationInfo.authorizationFile) + ) + ); } private getAuthorizedTags(): string[] { @@ -798,16 +1138,27 @@ export default class ChargingStation { authorizedTags = JSON.parse(fs.readFileSync(fileDescriptor, 'utf8')) as string[]; fs.closeSync(fileDescriptor); } catch (error) { - FileUtils.handleFileException(this.logPrefix(), 'Authorization', authorizationFile, error as NodeJS.ErrnoException); + FileUtils.handleFileException( + this.logPrefix(), + 'Authorization', + authorizationFile, + error as NodeJS.ErrnoException + ); } } else { - logger.info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile); + logger.info( + this.logPrefix() + + ' No authorization file given in template file ' + + this.stationTemplateFile + ); } return authorizedTags; } private getUseConnectorId0(): boolean | undefined { - return !Utils.isUndefined(this.stationInfo.useConnectorId0) ? this.stationInfo.useConnectorId0 : true; + return !Utils.isUndefined(this.stationInfo.useConnectorId0) + ? this.stationInfo.useConnectorId0 + : true; } private getNumberOfRunningTransactions(): number { @@ -823,7 +1174,10 @@ export default class ChargingStation { // 0 for disabling private getConnectionTimeout(): number | undefined { if (this.getConfigurationKey(StandardParametersKey.ConnectionTimeOut)) { - return parseInt(this.getConfigurationKey(StandardParametersKey.ConnectionTimeOut).value) ?? Constants.DEFAULT_CONNECTION_TIMEOUT; + return ( + parseInt(this.getConfigurationKey(StandardParametersKey.ConnectionTimeOut).value) ?? + Constants.DEFAULT_CONNECTION_TIMEOUT + ); } return Constants.DEFAULT_CONNECTION_TIMEOUT; } @@ -868,15 +1222,21 @@ export default class ChargingStation { } else if (!Utils.isUndefined(this.stationInfo.numberOfConnectors)) { maxConnectors = this.stationInfo.numberOfConnectors as number; } else { - maxConnectors = this.stationInfo.Connectors[0] ? this.getTemplateMaxNumberOfConnectors() - 1 : this.getTemplateMaxNumberOfConnectors(); + maxConnectors = this.stationInfo.Connectors[0] + ? this.getTemplateMaxNumberOfConnectors() - 1 + : this.getTemplateMaxNumberOfConnectors(); } return maxConnectors; } private async startMessageSequence(): Promise { if (this.stationInfo.autoRegister) { - await this.ocppRequestService.sendBootNotification(this.bootNotificationRequest.chargePointModel, - this.bootNotificationRequest.chargePointVendor, this.bootNotificationRequest.chargeBoxSerialNumber, this.bootNotificationRequest.firmwareVersion); + await this.ocppRequestService.sendBootNotification( + this.bootNotificationRequest.chargePointModel, + this.bootNotificationRequest.chargePointVendor, + this.bootNotificationRequest.chargeBoxSerialNumber, + this.bootNotificationRequest.firmwareVersion + ); } // Start WebSocket ping this.startWebSocketPing(); @@ -886,20 +1246,42 @@ export default class ChargingStation { for (const connectorId of this.connectors.keys()) { if (connectorId === 0) { continue; - } else if (!this.stopped && !this.getConnectorStatus(connectorId)?.status && this.getConnectorStatus(connectorId)?.bootStatus) { + } else if ( + !this.stopped && + !this.getConnectorStatus(connectorId)?.status && + this.getConnectorStatus(connectorId)?.bootStatus + ) { // Send status in template at startup - await this.ocppRequestService.sendStatusNotification(connectorId, this.getConnectorStatus(connectorId).bootStatus); - this.getConnectorStatus(connectorId).status = this.getConnectorStatus(connectorId).bootStatus; - } else if (this.stopped && this.getConnectorStatus(connectorId)?.status && this.getConnectorStatus(connectorId)?.bootStatus) { + await this.ocppRequestService.sendStatusNotification( + connectorId, + this.getConnectorStatus(connectorId).bootStatus + ); + this.getConnectorStatus(connectorId).status = + this.getConnectorStatus(connectorId).bootStatus; + } else if ( + this.stopped && + this.getConnectorStatus(connectorId)?.status && + this.getConnectorStatus(connectorId)?.bootStatus + ) { // Send status in template after reset - await this.ocppRequestService.sendStatusNotification(connectorId, this.getConnectorStatus(connectorId).bootStatus); - this.getConnectorStatus(connectorId).status = this.getConnectorStatus(connectorId).bootStatus; + await this.ocppRequestService.sendStatusNotification( + connectorId, + this.getConnectorStatus(connectorId).bootStatus + ); + this.getConnectorStatus(connectorId).status = + this.getConnectorStatus(connectorId).bootStatus; } else if (!this.stopped && this.getConnectorStatus(connectorId)?.status) { // Send previous status at template reload - await this.ocppRequestService.sendStatusNotification(connectorId, this.getConnectorStatus(connectorId).status); + await this.ocppRequestService.sendStatusNotification( + connectorId, + this.getConnectorStatus(connectorId).status + ); } else { // Send default status - await this.ocppRequestService.sendStatusNotification(connectorId, ChargePointStatus.AVAILABLE); + await this.ocppRequestService.sendStatusNotification( + connectorId, + ChargePointStatus.AVAILABLE + ); this.getConnectorStatus(connectorId).status = ChargePointStatus.AVAILABLE; } } @@ -918,41 +1300,70 @@ export default class ChargingStation { } } - private async stopMessageSequence(reason: StopTransactionReason = StopTransactionReason.NONE): Promise { + private async stopMessageSequence( + reason: StopTransactionReason = StopTransactionReason.NONE + ): Promise { // Stop WebSocket ping this.stopWebSocketPing(); // Stop heartbeat this.stopHeartbeat(); // Stop the ATG - if (this.stationInfo.AutomaticTransactionGenerator.enable && - this.automaticTransactionGenerator?.started) { + if ( + this.stationInfo.AutomaticTransactionGenerator.enable && + this.automaticTransactionGenerator?.started + ) { this.automaticTransactionGenerator.stop(); } else { for (const connectorId of this.connectors.keys()) { if (connectorId > 0 && this.getConnectorStatus(connectorId)?.transactionStarted) { const transactionId = this.getConnectorStatus(connectorId).transactionId; - await this.ocppRequestService.sendStopTransaction(transactionId, this.getEnergyActiveImportRegisterByTransactionId(transactionId), - this.getTransactionIdTag(transactionId), reason); + await this.ocppRequestService.sendStopTransaction( + transactionId, + this.getEnergyActiveImportRegisterByTransactionId(transactionId), + this.getTransactionIdTag(transactionId), + reason + ); } } } } private startWebSocketPing(): void { - const webSocketPingInterval: number = this.getConfigurationKey(StandardParametersKey.WebSocketPingInterval) - ? Utils.convertToInt(this.getConfigurationKey(StandardParametersKey.WebSocketPingInterval).value) + const webSocketPingInterval: number = this.getConfigurationKey( + StandardParametersKey.WebSocketPingInterval + ) + ? Utils.convertToInt( + this.getConfigurationKey(StandardParametersKey.WebSocketPingInterval).value + ) : 0; if (webSocketPingInterval > 0 && !this.webSocketPingSetInterval) { this.webSocketPingSetInterval = setInterval(() => { if (this.isWebSocketConnectionOpened()) { - this.wsConnection.ping((): void => { /* This is intentional */ }); + this.wsConnection.ping((): void => { + /* This is intentional */ + }); } }, webSocketPingInterval * 1000); - logger.info(this.logPrefix() + ' WebSocket ping started every ' + Utils.formatDurationSeconds(webSocketPingInterval)); + logger.info( + this.logPrefix() + + ' WebSocket ping started every ' + + Utils.formatDurationSeconds(webSocketPingInterval) + ); } else if (this.webSocketPingSetInterval) { - logger.info(this.logPrefix() + ' WebSocket ping every ' + Utils.formatDurationSeconds(webSocketPingInterval) + ' already started'); + logger.info( + this.logPrefix() + + ' WebSocket ping every ' + + Utils.formatDurationSeconds(webSocketPingInterval) + + ' already started' + ); } else { - logger.error(`${this.logPrefix()} WebSocket ping interval set to ${webSocketPingInterval ? Utils.formatDurationSeconds(webSocketPingInterval) : webSocketPingInterval}, not starting the WebSocket ping`); + logger.error( + `${this.logPrefix()} WebSocket ping interval set to ${ + webSocketPingInterval + ? Utils.formatDurationSeconds(webSocketPingInterval) + : webSocketPingInterval + }, not starting the WebSocket ping` + ); } } @@ -962,13 +1373,27 @@ export default class ChargingStation { } } - private warnDeprecatedTemplateKey(template: ChargingStationTemplate, key: string, chargingStationId: string, logMsgToAppend = ''): void { + private warnDeprecatedTemplateKey( + template: ChargingStationTemplate, + key: string, + chargingStationId: string, + logMsgToAppend = '' + ): void { if (!Utils.isUndefined(template[key])) { - logger.warn(`${Utils.logPrefix(` ${chargingStationId} |`)} Deprecated template key '${key}' usage in file '${this.stationTemplateFile}'${logMsgToAppend && '. ' + logMsgToAppend}`); + const logPrefixStr = ` ${chargingStationId} |`; + logger.warn( + `${Utils.logPrefix(logPrefixStr)} Deprecated template key '${key}' usage in file '${ + this.stationTemplateFile + }'${logMsgToAppend && '. ' + logMsgToAppend}` + ); } } - private convertDeprecatedTemplateKey(template: ChargingStationTemplate, deprecatedKey: string, key: string): void { + private convertDeprecatedTemplateKey( + template: ChargingStationTemplate, + deprecatedKey: string, + key: string + ): void { if (!Utils.isUndefined(template[deprecatedKey])) { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment template[key] = template[deprecatedKey]; @@ -977,7 +1402,9 @@ export default class ChargingStation { } private getConfiguredSupervisionUrl(): URL { - const supervisionUrls = Utils.cloneObject(this.stationInfo.supervisionUrls ?? Configuration.getSupervisionUrls()); + const supervisionUrls = Utils.cloneObject( + this.stationInfo.supervisionUrls ?? Configuration.getSupervisionUrls() + ); if (!Utils.isEmptyArray(supervisionUrls)) { let urlIndex = 0; switch (Configuration.getSupervisionUrlDistribution()) { @@ -992,11 +1419,17 @@ export default class ChargingStation { if (this.index <= supervisionUrls.length) { urlIndex = this.index - 1; } else { - logger.warn(`${this.logPrefix()} No more configured supervision urls available, using the first one`); + logger.warn( + `${this.logPrefix()} No more configured supervision urls available, using the first one` + ); } break; default: - logger.error(`${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' from values '${SupervisionUrlDistribution.toString()}', defaulting to ${SupervisionUrlDistribution.ROUND_ROBIN}`); + logger.error( + `${this.logPrefix()} Unknown supervision url distribution '${Configuration.getSupervisionUrlDistribution()}' from values '${SupervisionUrlDistribution.toString()}', defaulting to ${ + SupervisionUrlDistribution.ROUND_ROBIN + }` + ); urlIndex = (this.index - 1) % supervisionUrls.length; break; } @@ -1014,7 +1447,12 @@ export default class ChargingStation { if (HeartBeatInterval) { return Utils.convertToInt(HeartBeatInterval.value) * 1000; } - !this.stationInfo.autoRegister && logger.warn(`${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${Constants.DEFAULT_HEARTBEAT_INTERVAL}`); + !this.stationInfo.autoRegister && + logger.warn( + `${this.logPrefix()} Heartbeat interval configuration key not set, using default value: ${ + Constants.DEFAULT_HEARTBEAT_INTERVAL + }` + ); return Constants.DEFAULT_HEARTBEAT_INTERVAL; } @@ -1024,9 +1462,15 @@ export default class ChargingStation { } } - private openWSConnection(options: ClientOptions & ClientRequestArgs = this.stationInfo.wsOptions, forceCloseOpened = false): void { + private openWSConnection( + options: ClientOptions & ClientRequestArgs = this.stationInfo.wsOptions, + forceCloseOpened = false + ): void { options.handshakeTimeout = options?.handshakeTimeout ?? this.getConnectionTimeout() * 1000; - if (!Utils.isNullOrUndefined(this.stationInfo.supervisionUser) && !Utils.isNullOrUndefined(this.stationInfo.supervisionPassword)) { + if ( + !Utils.isNullOrUndefined(this.stationInfo.supervisionUser) && + !Utils.isNullOrUndefined(this.stationInfo.supervisionPassword) + ) { options.auth = `${this.stationInfo.supervisionUser}:${this.stationInfo.supervisionPassword}`; } if (this.isWebSocketConnectionOpened() && forceCloseOpened) { @@ -1042,7 +1486,9 @@ export default class ChargingStation { break; } this.wsConnection = new WebSocket(this.wsConnectionUrl, protocol, options); - logger.info(this.logPrefix() + ' Open OCPP connection to URL ' + this.wsConnectionUrl.toString()); + logger.info( + this.logPrefix() + ' Open OCPP connection to URL ' + this.wsConnectionUrl.toString() + ); } private stopMeterValues(connectorId: number) { @@ -1058,7 +1504,12 @@ export default class ChargingStation { fs.watch(authorizationFile, (event, filename) => { if (filename && event === 'change') { try { - logger.debug(this.logPrefix() + ' Authorization file ' + authorizationFile + ' have changed, reload'); + logger.debug( + this.logPrefix() + + ' Authorization file ' + + authorizationFile + + ' have changed, reload' + ); // Initialize authorizedTags this.authorizedTags = this.getAuthorizedTags(); } catch (error) { @@ -1067,10 +1518,20 @@ export default class ChargingStation { } }); } catch (error) { - FileUtils.handleFileException(this.logPrefix(), 'Authorization', authorizationFile, error as NodeJS.ErrnoException); + FileUtils.handleFileException( + this.logPrefix(), + 'Authorization', + authorizationFile, + error as NodeJS.ErrnoException + ); } } else { - logger.info(this.logPrefix() + ' No authorization file given in template file ' + this.stationTemplateFile + '. Not monitoring changes'); + logger.info( + this.logPrefix() + + ' No authorization file given in template file ' + + this.stationTemplateFile + + '. Not monitoring changes' + ); } } @@ -1079,12 +1540,19 @@ export default class ChargingStation { fs.watch(this.stationTemplateFile, (event, filename): void => { if (filename && event === 'change') { try { - logger.debug(this.logPrefix() + ' Template file ' + this.stationTemplateFile + ' have changed, reload'); + logger.debug( + this.logPrefix() + + ' Template file ' + + this.stationTemplateFile + + ' have changed, reload' + ); // Initialize this.initialize(); // Restart the ATG - if (!this.stationInfo.AutomaticTransactionGenerator.enable && - this.automaticTransactionGenerator) { + if ( + !this.stationInfo.AutomaticTransactionGenerator.enable && + this.automaticTransactionGenerator + ) { this.automaticTransactionGenerator.stop(); } this.startAutomaticTransactionGenerator(); @@ -1095,17 +1563,27 @@ export default class ChargingStation { } // FIXME?: restart heartbeat and WebSocket ping when their interval values have changed } catch (error) { - logger.error(this.logPrefix() + ' Charging station template file monitoring error: %j', error); + logger.error( + this.logPrefix() + ' Charging station template file monitoring error: %j', + error + ); } } }); } catch (error) { - FileUtils.handleFileException(this.logPrefix(), 'Template', this.stationTemplateFile, error as NodeJS.ErrnoException); + FileUtils.handleFileException( + this.logPrefix(), + 'Template', + this.stationTemplateFile, + error as NodeJS.ErrnoException + ); } } private getReconnectExponentialDelay(): boolean | undefined { - return !Utils.isUndefined(this.stationInfo.reconnectExponentialDelay) ? this.stationInfo.reconnectExponentialDelay : false; + return !Utils.isUndefined(this.stationInfo.reconnectExponentialDelay) + ? this.stationInfo.reconnectExponentialDelay + : false; } private async reconnect(code: number): Promise { @@ -1114,22 +1592,45 @@ export default class ChargingStation { // Stop heartbeat this.stopHeartbeat(); // Stop the ATG if needed - if (this.stationInfo.AutomaticTransactionGenerator.enable && + if ( + this.stationInfo.AutomaticTransactionGenerator.enable && this.stationInfo.AutomaticTransactionGenerator.stopOnConnectionFailure && - this.automaticTransactionGenerator?.started) { + this.automaticTransactionGenerator?.started + ) { this.automaticTransactionGenerator.stop(); } - if (this.autoReconnectRetryCount < this.getAutoReconnectMaxRetries() || this.getAutoReconnectMaxRetries() === -1) { + if ( + this.autoReconnectRetryCount < this.getAutoReconnectMaxRetries() || + this.getAutoReconnectMaxRetries() === -1 + ) { this.autoReconnectRetryCount++; - const reconnectDelay = (this.getReconnectExponentialDelay() ? Utils.exponentialDelay(this.autoReconnectRetryCount) : this.getConnectionTimeout() * 1000); - const reconnectTimeout = (reconnectDelay - 100) > 0 && reconnectDelay; - logger.error(`${this.logPrefix()} WebSocket: connection retry in ${Utils.roundTo(reconnectDelay, 2)}ms, timeout ${reconnectTimeout}ms`); + const reconnectDelay = this.getReconnectExponentialDelay() + ? Utils.exponentialDelay(this.autoReconnectRetryCount) + : this.getConnectionTimeout() * 1000; + const reconnectTimeout = reconnectDelay - 100 > 0 && reconnectDelay; + logger.error( + `${this.logPrefix()} WebSocket: connection retry in ${Utils.roundTo( + reconnectDelay, + 2 + )}ms, timeout ${reconnectTimeout}ms` + ); await Utils.sleep(reconnectDelay); - logger.error(this.logPrefix() + ' WebSocket: reconnecting try #' + this.autoReconnectRetryCount.toString()); - this.openWSConnection({ ...this.stationInfo.wsOptions, handshakeTimeout: reconnectTimeout }, true); + logger.error( + this.logPrefix() + + ' WebSocket: reconnecting try #' + + this.autoReconnectRetryCount.toString() + ); + this.openWSConnection( + { ...this.stationInfo.wsOptions, handshakeTimeout: reconnectTimeout }, + true + ); this.wsConnectionRestarted = true; } else if (this.getAutoReconnectMaxRetries() !== -1) { - logger.error(`${this.logPrefix()} WebSocket reconnect failure: max retries reached (${this.autoReconnectRetryCount}) or retry disabled (${this.getAutoReconnectMaxRetries()})`); + logger.error( + `${this.logPrefix()} WebSocket reconnect failure: max retries reached (${ + this.autoReconnectRetryCount + }) or retry disabled (${this.getAutoReconnectMaxRetries()})` + ); } } @@ -1142,4 +1643,3 @@ export default class ChargingStation { this.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue = 0; } } - diff --git a/src/charging-station/ChargingStationWorker.ts b/src/charging-station/ChargingStationWorker.ts index cc0f6272..84ce450e 100644 --- a/src/charging-station/ChargingStationWorker.ts +++ b/src/charging-station/ChargingStationWorker.ts @@ -1,6 +1,10 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. -import { ChargingStationWorkerData, ChargingStationWorkerMessage, ChargingStationWorkerMessageEvents } from '../types/ChargingStationWorker'; +import { + ChargingStationWorkerData, + ChargingStationWorkerMessage, + ChargingStationWorkerMessageEvents, +} from '../types/ChargingStationWorker'; import { parentPort, workerData } from 'worker_threads'; import ChargingStation from './ChargingStation'; @@ -11,12 +15,18 @@ import Utils from '../utils/Utils'; // Conditionally export ThreadWorker instance for pool usage export let threadWorker: ThreadWorker; if (Utils.workerPoolInUse()) { - threadWorker = new ThreadWorker(startChargingStation, { maxInactiveTime: Constants.WORKER_POOL_MAX_INACTIVE_TIME, async: false }); + threadWorker = new ThreadWorker(startChargingStation, { + maxInactiveTime: Constants.WORKER_POOL_MAX_INACTIVE_TIME, + async: false, + }); } else { // Add message listener to start charging station from main thread addMessageListener(); if (!Utils.isUndefined(workerData)) { - startChargingStation({ index: workerData.index as number, templateFile: workerData.templateFile as string }); + startChargingStation({ + index: workerData.index as number, + templateFile: workerData.templateFile as string, + }); } } diff --git a/src/charging-station/UIWebSocketServer.ts b/src/charging-station/UIWebSocketServer.ts index 96e9a968..5ffc9e0c 100644 --- a/src/charging-station/UIWebSocketServer.ts +++ b/src/charging-station/UIWebSocketServer.ts @@ -34,9 +34,13 @@ export default class UIWebSocketServer extends Server { public start(): void { this.on('connection', (socket: WebSocket, request: IncomingMessage): void => { const protocolIndex = socket.protocol.indexOf(Protocol.UI); - const version = socket.protocol.substring(protocolIndex + Protocol.UI.length) as ProtocolVersion; + const version = socket.protocol.substring( + protocolIndex + Protocol.UI.length + ) as ProtocolVersion; if (!this.uiServices.has(version)) { - throw new BaseError(`Could not find a UI service implementation for UI protocol version ${version}`); + throw new BaseError( + `Could not find a UI service implementation for UI protocol version ${version}` + ); } // FIXME: check connection validity socket.on('message', (messageData) => { @@ -47,9 +51,16 @@ export default class UIWebSocketServer extends Server { } else { throw new BaseError('UI protocol request is not iterable'); } - this.uiServices.get(version).handleMessage(command, payload).catch(() => { - logger.error(`${this.logPrefix()} Error while handling command %s message: %j`, command, payload); - }); + this.uiServices + .get(version) + .handleMessage(command, payload) + .catch(() => { + logger.error( + `${this.logPrefix()} Error while handling command %s message: %j`, + command, + payload + ); + }); }); socket.on('error', (error) => { logger.error(`${this.logPrefix()} Error on WebSocket: %j`, error); diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index a9b79c13..ef1c7db2 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -1,10 +1,41 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. -import { ChangeAvailabilityRequest, ChangeConfigurationRequest, ClearChargingProfileRequest, GetConfigurationRequest, GetDiagnosticsRequest, MessageTrigger, OCPP16AvailabilityType, OCPP16IncomingRequestCommand, OCPP16RequestCommand, OCPP16TriggerMessageRequest, RemoteStartTransactionRequest, RemoteStopTransactionRequest, ResetRequest, SetChargingProfileRequest, UnlockConnectorRequest } from '../../../types/ocpp/1.6/Requests'; -import { ChangeAvailabilityResponse, ChangeConfigurationResponse, ClearChargingProfileResponse, GetConfigurationResponse, GetDiagnosticsResponse, OCPP16TriggerMessageResponse, SetChargingProfileResponse, UnlockConnectorResponse } from '../../../types/ocpp/1.6/Responses'; -import { ChargingProfilePurposeType, OCPP16ChargingProfile } from '../../../types/ocpp/1.6/ChargingProfile'; +import { + ChangeAvailabilityRequest, + ChangeConfigurationRequest, + ClearChargingProfileRequest, + GetConfigurationRequest, + GetDiagnosticsRequest, + MessageTrigger, + OCPP16AvailabilityType, + OCPP16IncomingRequestCommand, + OCPP16RequestCommand, + OCPP16TriggerMessageRequest, + RemoteStartTransactionRequest, + RemoteStopTransactionRequest, + ResetRequest, + SetChargingProfileRequest, + UnlockConnectorRequest, +} from '../../../types/ocpp/1.6/Requests'; +import { + ChangeAvailabilityResponse, + ChangeConfigurationResponse, + ClearChargingProfileResponse, + GetConfigurationResponse, + GetDiagnosticsResponse, + OCPP16TriggerMessageResponse, + SetChargingProfileResponse, + UnlockConnectorResponse, +} from '../../../types/ocpp/1.6/Responses'; +import { + ChargingProfilePurposeType, + OCPP16ChargingProfile, +} from '../../../types/ocpp/1.6/ChargingProfile'; import { Client, FTPResponse } from 'basic-ftp'; -import { OCPP16AuthorizationStatus, OCPP16StopTransactionReason } from '../../../types/ocpp/1.6/Transaction'; +import { + OCPP16AuthorizationStatus, + OCPP16StopTransactionReason, +} from '../../../types/ocpp/1.6/Transaction'; import type ChargingStation from '../../ChargingStation'; import Constants from '../../../utils/Constants'; @@ -39,25 +70,65 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer [OCPP16IncomingRequestCommand.RESET, this.handleRequestReset.bind(this)], [OCPP16IncomingRequestCommand.CLEAR_CACHE, this.handleRequestClearCache.bind(this)], [OCPP16IncomingRequestCommand.UNLOCK_CONNECTOR, this.handleRequestUnlockConnector.bind(this)], - [OCPP16IncomingRequestCommand.GET_CONFIGURATION, this.handleRequestGetConfiguration.bind(this)], - [OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, this.handleRequestChangeConfiguration.bind(this)], - [OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, this.handleRequestSetChargingProfile.bind(this)], - [OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, this.handleRequestClearChargingProfile.bind(this)], - [OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, this.handleRequestChangeAvailability.bind(this)], - [OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, this.handleRequestRemoteStartTransaction.bind(this)], - [OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, this.handleRequestRemoteStopTransaction.bind(this)], + [ + OCPP16IncomingRequestCommand.GET_CONFIGURATION, + this.handleRequestGetConfiguration.bind(this), + ], + [ + OCPP16IncomingRequestCommand.CHANGE_CONFIGURATION, + this.handleRequestChangeConfiguration.bind(this), + ], + [ + OCPP16IncomingRequestCommand.SET_CHARGING_PROFILE, + this.handleRequestSetChargingProfile.bind(this), + ], + [ + OCPP16IncomingRequestCommand.CLEAR_CHARGING_PROFILE, + this.handleRequestClearChargingProfile.bind(this), + ], + [ + OCPP16IncomingRequestCommand.CHANGE_AVAILABILITY, + this.handleRequestChangeAvailability.bind(this), + ], + [ + OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION, + this.handleRequestRemoteStartTransaction.bind(this), + ], + [ + OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION, + this.handleRequestRemoteStopTransaction.bind(this), + ], [OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, this.handleRequestGetDiagnostics.bind(this)], - [OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)] + [OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, this.handleRequestTriggerMessage.bind(this)], ]); } - public async handleRequest(messageId: string, commandName: OCPP16IncomingRequestCommand, commandPayload: JsonType): Promise { + public async handleRequest( + messageId: string, + commandName: OCPP16IncomingRequestCommand, + commandPayload: JsonType + ): Promise { let result: JsonType; - if (this.chargingStation.getOcppStrictCompliance() && (this.chargingStation.isInPendingState() - && (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION || commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION))) { - throw new OCPPError(ErrorType.SECURITY_ERROR, `${commandName} cannot be issued to handle request payload ${JSON.stringify(commandPayload, null, 2)} while the charging station is in pending state on the central server`, commandName); + if ( + this.chargingStation.getOcppStrictCompliance() && + this.chargingStation.isInPendingState() && + (commandName === OCPP16IncomingRequestCommand.REMOTE_START_TRANSACTION || + commandName === OCPP16IncomingRequestCommand.REMOTE_STOP_TRANSACTION) + ) { + throw new OCPPError( + ErrorType.SECURITY_ERROR, + `${commandName} cannot be issued to handle request payload ${JSON.stringify( + commandPayload, + null, + 2 + )} while the charging station is in pending state on the central server`, + commandName + ); } - if (this.chargingStation.isRegistered() || (!this.chargingStation.getOcppStrictCompliance() && this.chargingStation.isInUnknownState())) { + if ( + this.chargingStation.isRegistered() || + (!this.chargingStation.getOcppStrictCompliance() && this.chargingStation.isInUnknownState()) + ) { if (this.incomingRequestHandlers.has(commandName)) { try { // Call the method to build the result @@ -69,10 +140,26 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer } } else { // Throw exception - throw new OCPPError(ErrorType.NOT_IMPLEMENTED, `${commandName} is not implemented to handle request payload ${JSON.stringify(commandPayload, null, 2)}`, commandName); + throw new OCPPError( + ErrorType.NOT_IMPLEMENTED, + `${commandName} is not implemented to handle request payload ${JSON.stringify( + commandPayload, + null, + 2 + )}`, + commandName + ); } } else { - throw new OCPPError(ErrorType.SECURITY_ERROR, `${commandName} cannot be issued to handle request payload ${JSON.stringify(commandPayload, null, 2)} while the charging station is not registered on the central server.`, commandName); + throw new OCPPError( + ErrorType.SECURITY_ERROR, + `${commandName} cannot be issued to handle request payload ${JSON.stringify( + commandPayload, + null, + 2 + )} while the charging station is not registered on the central server.`, + commandName + ); } // Send the built result await this.chargingStation.ocppRequestService.sendResult(messageId, result, commandName); @@ -82,11 +169,19 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer private handleRequestReset(commandPayload: ResetRequest): DefaultResponse { // eslint-disable-next-line @typescript-eslint/no-misused-promises setImmediate(async (): Promise => { - await this.chargingStation.stop(commandPayload.type + 'Reset' as OCPP16StopTransactionReason); + await this.chargingStation.stop( + (commandPayload.type + 'Reset') as OCPP16StopTransactionReason + ); await Utils.sleep(this.chargingStation.stationInfo.resetTime); this.chargingStation.start(); }); - logger.info(`${this.chargingStation.logPrefix()} ${commandPayload.type} reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds(this.chargingStation.stationInfo.resetTime)}`); + logger.info( + `${this.chargingStation.logPrefix()} ${ + commandPayload.type + } reset command received, simulating it. The station will be back online in ${Utils.formatDurationMilliSeconds( + this.chargingStation.stationInfo.resetTime + )}` + ); return Constants.OCPP_RESPONSE_ACCEPTED; } @@ -94,29 +189,40 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer return Constants.OCPP_RESPONSE_ACCEPTED; } - private async handleRequestUnlockConnector(commandPayload: UnlockConnectorRequest): Promise { + private async handleRequestUnlockConnector( + commandPayload: UnlockConnectorRequest + ): Promise { const connectorId = commandPayload.connectorId; if (connectorId === 0) { - logger.error(this.chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString()); + logger.error( + this.chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString() + ); return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED; } if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted) { const transactionId = this.chargingStation.getConnectorStatus(connectorId).transactionId; - const stopResponse = await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, + const stopResponse = await this.chargingStation.ocppRequestService.sendStopTransaction( + transactionId, this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), this.chargingStation.getTransactionIdTag(transactionId), - OCPP16StopTransactionReason.UNLOCK_COMMAND); + OCPP16StopTransactionReason.UNLOCK_COMMAND + ); if (stopResponse.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { return Constants.OCPP_RESPONSE_UNLOCKED; } return Constants.OCPP_RESPONSE_UNLOCK_FAILED; } - await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE); + await this.chargingStation.ocppRequestService.sendStatusNotification( + connectorId, + OCPP16ChargePointStatus.AVAILABLE + ); this.chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE; return Constants.OCPP_RESPONSE_UNLOCKED; } - private handleRequestGetConfiguration(commandPayload: GetConfigurationRequest): GetConfigurationResponse { + private handleRequestGetConfiguration( + commandPayload: GetConfigurationRequest + ): GetConfigurationResponse { const configurationKey: OCPPConfigurationKey[] = []; const unknownKey: string[] = []; if (Utils.isEmptyArray(commandPayload.key)) { @@ -159,13 +265,25 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer }; } - private handleRequestChangeConfiguration(commandPayload: ChangeConfigurationRequest): ChangeConfigurationResponse { + private handleRequestChangeConfiguration( + commandPayload: ChangeConfigurationRequest + ): ChangeConfigurationResponse { // JSON request fields type sanity check if (!Utils.isString(commandPayload.key)) { - logger.error(`${this.chargingStation.logPrefix()} ${OCPP16RequestCommand.CHANGE_CONFIGURATION} request key field is not a string:`, commandPayload); + logger.error( + `${this.chargingStation.logPrefix()} ${ + OCPP16RequestCommand.CHANGE_CONFIGURATION + } request key field is not a string:`, + commandPayload + ); } if (!Utils.isString(commandPayload.value)) { - logger.error(`${this.chargingStation.logPrefix()} ${OCPP16RequestCommand.CHANGE_CONFIGURATION} request value field is not a string:`, commandPayload); + logger.error( + `${this.chargingStation.logPrefix()} ${ + OCPP16RequestCommand.CHANGE_CONFIGURATION + } request value field is not a string:`, + commandPayload + ); } const keyToChange = this.chargingStation.getConfigurationKey(commandPayload.key, true); if (!keyToChange) { @@ -175,17 +293,25 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer } else if (keyToChange && !keyToChange.readonly) { const keyIndex = this.chargingStation.configuration.configurationKey.indexOf(keyToChange); let valueChanged = false; - if (this.chargingStation.configuration.configurationKey[keyIndex].value !== commandPayload.value) { + if ( + this.chargingStation.configuration.configurationKey[keyIndex].value !== commandPayload.value + ) { this.chargingStation.configuration.configurationKey[keyIndex].value = commandPayload.value; valueChanged = true; } let triggerHeartbeatRestart = false; if (keyToChange.key === OCPP16StandardParametersKey.HeartBeatInterval && valueChanged) { - this.chargingStation.setConfigurationKeyValue(OCPP16StandardParametersKey.HeartbeatInterval, commandPayload.value); + this.chargingStation.setConfigurationKeyValue( + OCPP16StandardParametersKey.HeartbeatInterval, + commandPayload.value + ); triggerHeartbeatRestart = true; } if (keyToChange.key === OCPP16StandardParametersKey.HeartbeatInterval && valueChanged) { - this.chargingStation.setConfigurationKeyValue(OCPP16StandardParametersKey.HeartBeatInterval, commandPayload.value); + this.chargingStation.setConfigurationKeyValue( + OCPP16StandardParametersKey.HeartBeatInterval, + commandPayload.value + ); triggerHeartbeatRestart = true; } if (triggerHeartbeatRestart) { @@ -201,56 +327,110 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer } } - private handleRequestSetChargingProfile(commandPayload: SetChargingProfileRequest): SetChargingProfileResponse { + private handleRequestSetChargingProfile( + commandPayload: SetChargingProfileRequest + ): SetChargingProfileResponse { if (!this.chargingStation.getConnectorStatus(commandPayload.connectorId)) { - logger.error(`${this.chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${commandPayload.connectorId}`); + logger.error( + `${this.chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${ + commandPayload.connectorId + }` + ); return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } - if (commandPayload.csChargingProfiles.chargingProfilePurpose === ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && commandPayload.connectorId !== 0) { + if ( + commandPayload.csChargingProfiles.chargingProfilePurpose === + ChargingProfilePurposeType.CHARGE_POINT_MAX_PROFILE && + commandPayload.connectorId !== 0 + ) { return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } - if (commandPayload.csChargingProfiles.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE && (commandPayload.connectorId === 0 || !this.chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted)) { + if ( + commandPayload.csChargingProfiles.chargingProfilePurpose === + ChargingProfilePurposeType.TX_PROFILE && + (commandPayload.connectorId === 0 || + !this.chargingStation.getConnectorStatus(commandPayload.connectorId)?.transactionStarted) + ) { return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED; } - this.chargingStation.setChargingProfile(commandPayload.connectorId, commandPayload.csChargingProfiles); - logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); + this.chargingStation.setChargingProfile( + commandPayload.connectorId, + commandPayload.csChargingProfiles + ); + logger.debug( + `${this.chargingStation.logPrefix()} Charging profile(s) set, dump their stack: %j`, + this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles + ); return Constants.OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED; } - private handleRequestClearChargingProfile(commandPayload: ClearChargingProfileRequest): ClearChargingProfileResponse { + private handleRequestClearChargingProfile( + commandPayload: ClearChargingProfileRequest + ): ClearChargingProfileResponse { if (!this.chargingStation.getConnectorStatus(commandPayload.connectorId)) { - logger.error(`${this.chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${commandPayload.connectorId}`); + logger.error( + `${this.chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${ + commandPayload.connectorId + }` + ); return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; } - if (commandPayload.connectorId && !Utils.isEmptyArray(this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles)) { + if ( + commandPayload.connectorId && + !Utils.isEmptyArray( + this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles + ) + ) { this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles = []; - logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); + logger.debug( + `${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, + this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles + ); return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED; } if (!commandPayload.connectorId) { let clearedCP = false; for (const connectorId of this.chargingStation.connectors.keys()) { - if (!Utils.isEmptyArray(this.chargingStation.getConnectorStatus(connectorId).chargingProfiles)) { - this.chargingStation.getConnectorStatus(connectorId).chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => { - let clearCurrentCP = false; - if (chargingProfile.chargingProfileId === commandPayload.id) { - clearCurrentCP = true; - } - if (!commandPayload.chargingProfilePurpose && chargingProfile.stackLevel === commandPayload.stackLevel) { - clearCurrentCP = true; - } - if (!chargingProfile.stackLevel && chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose) { - clearCurrentCP = true; - } - if (chargingProfile.stackLevel === commandPayload.stackLevel && chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose) { - clearCurrentCP = true; - } - if (clearCurrentCP) { - this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles[index] = {} as OCPP16ChargingProfile; - logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, this.chargingStation.getConnectorStatus(commandPayload.connectorId).chargingProfiles); - clearedCP = true; - } - }); + if ( + !Utils.isEmptyArray(this.chargingStation.getConnectorStatus(connectorId).chargingProfiles) + ) { + this.chargingStation + .getConnectorStatus(connectorId) + .chargingProfiles?.forEach((chargingProfile: OCPP16ChargingProfile, index: number) => { + let clearCurrentCP = false; + if (chargingProfile.chargingProfileId === commandPayload.id) { + clearCurrentCP = true; + } + if ( + !commandPayload.chargingProfilePurpose && + chargingProfile.stackLevel === commandPayload.stackLevel + ) { + clearCurrentCP = true; + } + if ( + !chargingProfile.stackLevel && + chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose + ) { + clearCurrentCP = true; + } + if ( + chargingProfile.stackLevel === commandPayload.stackLevel && + chargingProfile.chargingProfilePurpose === commandPayload.chargingProfilePurpose + ) { + clearCurrentCP = true; + } + if (clearCurrentCP) { + this.chargingStation.getConnectorStatus( + commandPayload.connectorId + ).chargingProfiles[index] = {} as OCPP16ChargingProfile; + logger.debug( + `${this.chargingStation.logPrefix()} Charging profile(s) cleared, dump their stack: %j`, + this.chargingStation.getConnectorStatus(commandPayload.connectorId) + .chargingProfiles + ); + clearedCP = true; + } + }); } } if (clearedCP) { @@ -260,15 +440,20 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN; } - private async handleRequestChangeAvailability(commandPayload: ChangeAvailabilityRequest): Promise { + private async handleRequestChangeAvailability( + commandPayload: ChangeAvailabilityRequest + ): Promise { const connectorId: number = commandPayload.connectorId; if (!this.chargingStation.getConnectorStatus(connectorId)) { - logger.error(`${this.chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`); + logger.error( + `${this.chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}` + ); return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; } - const chargePointStatus: OCPP16ChargePointStatus = commandPayload.type === OCPP16AvailabilityType.OPERATIVE - ? OCPP16ChargePointStatus.AVAILABLE - : OCPP16ChargePointStatus.UNAVAILABLE; + const chargePointStatus: OCPP16ChargePointStatus = + commandPayload.type === OCPP16AvailabilityType.OPERATIVE + ? OCPP16ChargePointStatus.AVAILABLE + : OCPP16ChargePointStatus.UNAVAILABLE; if (connectorId === 0) { let response: ChangeAvailabilityResponse = Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; for (const id of this.chargingStation.connectors.keys()) { @@ -277,184 +462,390 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer } this.chargingStation.getConnectorStatus(id).availability = commandPayload.type; if (response === Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED) { - await this.chargingStation.ocppRequestService.sendStatusNotification(id, chargePointStatus); + await this.chargingStation.ocppRequestService.sendStatusNotification( + id, + chargePointStatus + ); this.chargingStation.getConnectorStatus(id).status = chargePointStatus; } } return response; - } else if (connectorId > 0 && (this.chargingStation.getConnectorStatus(0).availability === OCPP16AvailabilityType.OPERATIVE || (this.chargingStation.getConnectorStatus(0).availability === OCPP16AvailabilityType.INOPERATIVE && commandPayload.type === OCPP16AvailabilityType.INOPERATIVE))) { + } else if ( + connectorId > 0 && + (this.chargingStation.getConnectorStatus(0).availability === + OCPP16AvailabilityType.OPERATIVE || + (this.chargingStation.getConnectorStatus(0).availability === + OCPP16AvailabilityType.INOPERATIVE && + commandPayload.type === OCPP16AvailabilityType.INOPERATIVE)) + ) { if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted) { this.chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type; return Constants.OCPP_AVAILABILITY_RESPONSE_SCHEDULED; } this.chargingStation.getConnectorStatus(connectorId).availability = commandPayload.type; - await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, chargePointStatus); + await this.chargingStation.ocppRequestService.sendStatusNotification( + connectorId, + chargePointStatus + ); this.chargingStation.getConnectorStatus(connectorId).status = chargePointStatus; return Constants.OCPP_AVAILABILITY_RESPONSE_ACCEPTED; } return Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED; } - private async handleRequestRemoteStartTransaction(commandPayload: RemoteStartTransactionRequest): Promise { + private async handleRequestRemoteStartTransaction( + commandPayload: RemoteStartTransactionRequest + ): Promise { const transactionConnectorId: number = commandPayload.connectorId; if (transactionConnectorId) { - await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.PREPARING); - this.chargingStation.getConnectorStatus(transactionConnectorId).status = OCPP16ChargePointStatus.PREPARING; - if (this.chargingStation.isChargingStationAvailable() && this.chargingStation.isConnectorAvailable(transactionConnectorId)) { + await this.chargingStation.ocppRequestService.sendStatusNotification( + transactionConnectorId, + OCPP16ChargePointStatus.PREPARING + ); + this.chargingStation.getConnectorStatus(transactionConnectorId).status = + OCPP16ChargePointStatus.PREPARING; + if ( + this.chargingStation.isChargingStationAvailable() && + this.chargingStation.isConnectorAvailable(transactionConnectorId) + ) { // Check if authorized if (this.chargingStation.getAuthorizeRemoteTxRequests()) { let authorized = false; - if (this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags() - && this.chargingStation.authorizedTags.find((value) => value === commandPayload.idTag)) { - this.chargingStation.getConnectorStatus(transactionConnectorId).localAuthorizeIdTag = commandPayload.idTag; - this.chargingStation.getConnectorStatus(transactionConnectorId).idTagLocalAuthorized = true; + if ( + this.chargingStation.getLocalAuthListEnabled() && + this.chargingStation.hasAuthorizedTags() && + this.chargingStation.authorizedTags.find((value) => value === commandPayload.idTag) + ) { + this.chargingStation.getConnectorStatus(transactionConnectorId).localAuthorizeIdTag = + commandPayload.idTag; + this.chargingStation.getConnectorStatus(transactionConnectorId).idTagLocalAuthorized = + true; authorized = true; } else if (this.chargingStation.getMayAuthorizeAtRemoteStart()) { - const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize(transactionConnectorId, commandPayload.idTag); + const authorizeResponse = await this.chargingStation.ocppRequestService.sendAuthorize( + transactionConnectorId, + commandPayload.idTag + ); if (authorizeResponse?.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { authorized = true; } } else { - logger.warn(`${this.chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled`); + logger.warn( + `${this.chargingStation.logPrefix()} The charging station configuration expects authorize at remote start transaction but local authorization or authorize isn't enabled` + ); } if (authorized) { // Authorization successful, start transaction - if (this.setRemoteStartTransactionChargingProfile(transactionConnectorId, commandPayload.chargingProfile)) { - this.chargingStation.getConnectorStatus(transactionConnectorId).transactionRemoteStarted = true; - if ((await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag)).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { - logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); + if ( + this.setRemoteStartTransactionChargingProfile( + transactionConnectorId, + commandPayload.chargingProfile + ) + ) { + this.chargingStation.getConnectorStatus( + transactionConnectorId + ).transactionRemoteStarted = true; + if ( + ( + await this.chargingStation.ocppRequestService.sendStartTransaction( + transactionConnectorId, + commandPayload.idTag + ) + ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED + ) { + logger.debug( + this.chargingStation.logPrefix() + + ' Transaction remotely STARTED on ' + + this.chargingStation.stationInfo.chargingStationId + + '#' + + transactionConnectorId.toString() + + ' for idTag ' + + commandPayload.idTag + ); return Constants.OCPP_RESPONSE_ACCEPTED; } - return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); + return this.notifyRemoteStartTransactionRejected( + transactionConnectorId, + commandPayload.idTag + ); } - return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); + return this.notifyRemoteStartTransactionRejected( + transactionConnectorId, + commandPayload.idTag + ); } - return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); + return this.notifyRemoteStartTransactionRejected( + transactionConnectorId, + commandPayload.idTag + ); } // No authorization check required, start transaction - if (this.setRemoteStartTransactionChargingProfile(transactionConnectorId, commandPayload.chargingProfile)) { - this.chargingStation.getConnectorStatus(transactionConnectorId).transactionRemoteStarted = true; - if ((await this.chargingStation.ocppRequestService.sendStartTransaction(transactionConnectorId, commandPayload.idTag)).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { - logger.debug(this.chargingStation.logPrefix() + ' Transaction remotely STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString() + ' for idTag ' + commandPayload.idTag); + if ( + this.setRemoteStartTransactionChargingProfile( + transactionConnectorId, + commandPayload.chargingProfile + ) + ) { + this.chargingStation.getConnectorStatus(transactionConnectorId).transactionRemoteStarted = + true; + if ( + ( + await this.chargingStation.ocppRequestService.sendStartTransaction( + transactionConnectorId, + commandPayload.idTag + ) + ).idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED + ) { + logger.debug( + this.chargingStation.logPrefix() + + ' Transaction remotely STARTED on ' + + this.chargingStation.stationInfo.chargingStationId + + '#' + + transactionConnectorId.toString() + + ' for idTag ' + + commandPayload.idTag + ); return Constants.OCPP_RESPONSE_ACCEPTED; } - return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); + return this.notifyRemoteStartTransactionRejected( + transactionConnectorId, + commandPayload.idTag + ); } - return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); + return this.notifyRemoteStartTransactionRejected( + transactionConnectorId, + commandPayload.idTag + ); } - return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); + return this.notifyRemoteStartTransactionRejected( + transactionConnectorId, + commandPayload.idTag + ); } return this.notifyRemoteStartTransactionRejected(transactionConnectorId, commandPayload.idTag); } - private async notifyRemoteStartTransactionRejected(connectorId: number, idTag: string): Promise { - if (this.chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE) { - await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE); - this.chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE; + private async notifyRemoteStartTransactionRejected( + connectorId: number, + idTag: string + ): Promise { + if ( + this.chargingStation.getConnectorStatus(connectorId).status !== + OCPP16ChargePointStatus.AVAILABLE + ) { + await this.chargingStation.ocppRequestService.sendStatusNotification( + connectorId, + OCPP16ChargePointStatus.AVAILABLE + ); + this.chargingStation.getConnectorStatus(connectorId).status = + OCPP16ChargePointStatus.AVAILABLE; } - logger.warn(this.chargingStation.logPrefix() + ' Remote starting transaction REJECTED on connector Id ' + connectorId.toString() + ', idTag ' + idTag + ', availability ' + this.chargingStation.getConnectorStatus(connectorId).availability + ', status ' + this.chargingStation.getConnectorStatus(connectorId).status); + logger.warn( + this.chargingStation.logPrefix() + + ' Remote starting transaction REJECTED on connector Id ' + + connectorId.toString() + + ', idTag ' + + idTag + + ', availability ' + + this.chargingStation.getConnectorStatus(connectorId).availability + + ', status ' + + this.chargingStation.getConnectorStatus(connectorId).status + ); return Constants.OCPP_RESPONSE_REJECTED; } - private setRemoteStartTransactionChargingProfile(connectorId: number, cp: OCPP16ChargingProfile): boolean { + private setRemoteStartTransactionChargingProfile( + connectorId: number, + cp: OCPP16ChargingProfile + ): boolean { if (cp && cp.chargingProfilePurpose === ChargingProfilePurposeType.TX_PROFILE) { this.chargingStation.setChargingProfile(connectorId, cp); - logger.debug(`${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, this.chargingStation.getConnectorStatus(connectorId).chargingProfiles); + logger.debug( + `${this.chargingStation.logPrefix()} Charging profile(s) set at remote start transaction, dump their stack: %j`, + this.chargingStation.getConnectorStatus(connectorId).chargingProfiles + ); return true; } else if (cp && cp.chargingProfilePurpose !== ChargingProfilePurposeType.TX_PROFILE) { - logger.warn(`${this.chargingStation.logPrefix()} Not allowed to set ${cp.chargingProfilePurpose} charging profile(s) at remote start transaction`); + logger.warn( + `${this.chargingStation.logPrefix()} Not allowed to set ${ + cp.chargingProfilePurpose + } charging profile(s) at remote start transaction` + ); return false; } else if (!cp) { return true; } } - private async handleRequestRemoteStopTransaction(commandPayload: RemoteStopTransactionRequest): Promise { + private async handleRequestRemoteStopTransaction( + commandPayload: RemoteStopTransactionRequest + ): Promise { const transactionId = commandPayload.transactionId; for (const connectorId of this.chargingStation.connectors.keys()) { - if (connectorId > 0 && this.chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId) { - await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.FINISHING); - this.chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.FINISHING; - await this.chargingStation.ocppRequestService.sendStopTransaction(transactionId, this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), - this.chargingStation.getTransactionIdTag(transactionId)); + if ( + connectorId > 0 && + this.chargingStation.getConnectorStatus(connectorId)?.transactionId === transactionId + ) { + await this.chargingStation.ocppRequestService.sendStatusNotification( + connectorId, + OCPP16ChargePointStatus.FINISHING + ); + this.chargingStation.getConnectorStatus(connectorId).status = + OCPP16ChargePointStatus.FINISHING; + await this.chargingStation.ocppRequestService.sendStopTransaction( + transactionId, + this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId), + this.chargingStation.getTransactionIdTag(transactionId) + ); return Constants.OCPP_RESPONSE_ACCEPTED; } } - logger.info(this.chargingStation.logPrefix() + ' Trying to remote stop a non existing transaction ' + transactionId.toString()); + logger.info( + this.chargingStation.logPrefix() + + ' Trying to remote stop a non existing transaction ' + + transactionId.toString() + ); return Constants.OCPP_RESPONSE_REJECTED; } - private async handleRequestGetDiagnostics(commandPayload: GetDiagnosticsRequest): Promise { - logger.debug(this.chargingStation.logPrefix() + ' ' + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ' request received: %j', commandPayload); + private async handleRequestGetDiagnostics( + commandPayload: GetDiagnosticsRequest + ): Promise { + logger.debug( + this.chargingStation.logPrefix() + + ' ' + + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + + ' request received: %j', + commandPayload + ); const uri = new URL(commandPayload.location); if (uri.protocol.startsWith('ftp:')) { let ftpClient: Client; try { - const logFiles = fs.readdirSync(path.resolve(__dirname, '../../../../')).filter((file) => file.endsWith('.log')).map((file) => path.join('./', file)); - const diagnosticsArchive = this.chargingStation.stationInfo.chargingStationId + '_logs.tar.gz'; + const logFiles = fs + .readdirSync(path.resolve(__dirname, '../../../../')) + .filter((file) => file.endsWith('.log')) + .map((file) => path.join('./', file)); + const diagnosticsArchive = + this.chargingStation.stationInfo.chargingStationId + '_logs.tar.gz'; tar.create({ gzip: true }, logFiles).pipe(fs.createWriteStream(diagnosticsArchive)); ftpClient = new Client(); const accessResponse = await ftpClient.access({ host: uri.host, - ...(uri.port !== '') && { port: Utils.convertToInt(uri.port) }, - ...(uri.username !== '') && { user: uri.username }, - ...(uri.password !== '') && { password: uri.password }, + ...(uri.port !== '' && { port: Utils.convertToInt(uri.port) }), + ...(uri.username !== '' && { user: uri.username }), + ...(uri.password !== '' && { password: uri.password }), }); let uploadResponse: FTPResponse; if (accessResponse.code === 220) { // eslint-disable-next-line @typescript-eslint/no-misused-promises ftpClient.trackProgress(async (info) => { - logger.info(`${this.chargingStation.logPrefix()} ${info.bytes / 1024} bytes transferred from diagnostics archive ${info.name}`); - await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.Uploading); + logger.info( + `${this.chargingStation.logPrefix()} ${ + info.bytes / 1024 + } bytes transferred from diagnostics archive ${info.name}` + ); + await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification( + OCPP16DiagnosticsStatus.Uploading + ); }); - uploadResponse = await ftpClient.uploadFrom(path.join(path.resolve(__dirname, '../../../../'), diagnosticsArchive), uri.pathname + diagnosticsArchive); + uploadResponse = await ftpClient.uploadFrom( + path.join(path.resolve(__dirname, '../../../../'), diagnosticsArchive), + uri.pathname + diagnosticsArchive + ); if (uploadResponse.code === 226) { - await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.Uploaded); + await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification( + OCPP16DiagnosticsStatus.Uploaded + ); if (ftpClient) { ftpClient.close(); } return { fileName: diagnosticsArchive }; } - throw new OCPPError(ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`, OCPP16IncomingRequestCommand.GET_DIAGNOSTICS); + throw new OCPPError( + ErrorType.GENERIC_ERROR, + `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${ + uploadResponse?.code && '|' + uploadResponse?.code.toString() + }`, + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ); } - throw new OCPPError(ErrorType.GENERIC_ERROR, `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`, OCPP16IncomingRequestCommand.GET_DIAGNOSTICS); + throw new OCPPError( + ErrorType.GENERIC_ERROR, + `Diagnostics transfer failed with error code ${accessResponse.code.toString()}${ + uploadResponse?.code && '|' + uploadResponse?.code.toString() + }`, + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ); } catch (error) { - await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed); + await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification( + OCPP16DiagnosticsStatus.UploadFailed + ); if (ftpClient) { ftpClient.close(); } - return this.handleIncomingRequestError(OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, error as Error, { errorResponse: Constants.OCPP_RESPONSE_EMPTY }); + return this.handleIncomingRequestError( + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS, + error as Error, + { errorResponse: Constants.OCPP_RESPONSE_EMPTY } + ); } } else { - logger.error(`${this.chargingStation.logPrefix()} Unsupported protocol ${uri.protocol} to transfer the diagnostic logs archive`); - await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed); + logger.error( + `${this.chargingStation.logPrefix()} Unsupported protocol ${ + uri.protocol + } to transfer the diagnostic logs archive` + ); + await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification( + OCPP16DiagnosticsStatus.UploadFailed + ); return Constants.OCPP_RESPONSE_EMPTY; } } - private handleRequestTriggerMessage(commandPayload: OCPP16TriggerMessageRequest): OCPP16TriggerMessageResponse { + private handleRequestTriggerMessage( + commandPayload: OCPP16TriggerMessageRequest + ): OCPP16TriggerMessageResponse { try { switch (commandPayload.requestedMessage) { case MessageTrigger.BootNotification: setTimeout(() => { - this.chargingStation.ocppRequestService.sendBootNotification(this.chargingStation.getBootNotificationRequest().chargePointModel, - this.chargingStation.getBootNotificationRequest().chargePointVendor, this.chargingStation.getBootNotificationRequest().chargeBoxSerialNumber, - this.chargingStation.getBootNotificationRequest().firmwareVersion, this.chargingStation.getBootNotificationRequest().chargePointSerialNumber, - this.chargingStation.getBootNotificationRequest().iccid, this.chargingStation.getBootNotificationRequest().imsi, - this.chargingStation.getBootNotificationRequest().meterSerialNumber, this.chargingStation.getBootNotificationRequest().meterType, - { triggerMessage: true }).catch(() => { /* This is intentional */ }); + this.chargingStation.ocppRequestService + .sendBootNotification( + this.chargingStation.getBootNotificationRequest().chargePointModel, + this.chargingStation.getBootNotificationRequest().chargePointVendor, + this.chargingStation.getBootNotificationRequest().chargeBoxSerialNumber, + this.chargingStation.getBootNotificationRequest().firmwareVersion, + this.chargingStation.getBootNotificationRequest().chargePointSerialNumber, + this.chargingStation.getBootNotificationRequest().iccid, + this.chargingStation.getBootNotificationRequest().imsi, + this.chargingStation.getBootNotificationRequest().meterSerialNumber, + this.chargingStation.getBootNotificationRequest().meterType, + { triggerMessage: true } + ) + .catch(() => { + /* This is intentional */ + }); }, Constants.OCPP_TRIGGER_MESSAGE_DELAY); return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; case MessageTrigger.Heartbeat: setTimeout(() => { - this.chargingStation.ocppRequestService.sendHeartbeat({ triggerMessage: true }).catch(() => { /* This is intentional */ }); + this.chargingStation.ocppRequestService + .sendHeartbeat({ triggerMessage: true }) + .catch(() => { + /* This is intentional */ + }); }, Constants.OCPP_TRIGGER_MESSAGE_DELAY); return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED; default: return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED; } } catch (error) { - return this.handleIncomingRequestError(OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, error as Error, { errorResponse: Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED }); + return this.handleIncomingRequestError( + OCPP16IncomingRequestCommand.TRIGGER_MESSAGE, + error as Error, + { errorResponse: Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED } + ); } } } diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index c97ee4bf..0e50589a 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -1,10 +1,30 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. import { ACElectricUtils, DCElectricUtils } from '../../../utils/ElectricUtils'; -import { AuthorizeRequest, OCPP16AuthorizeResponse, OCPP16StartTransactionResponse, OCPP16StopTransactionReason, OCPP16StopTransactionResponse, StartTransactionRequest, StopTransactionRequest } from '../../../types/ocpp/1.6/Transaction'; +import { + AuthorizeRequest, + OCPP16AuthorizeResponse, + OCPP16StartTransactionResponse, + OCPP16StopTransactionReason, + OCPP16StopTransactionResponse, + StartTransactionRequest, + StopTransactionRequest, +} from '../../../types/ocpp/1.6/Transaction'; import { CurrentType, Voltage } from '../../../types/ChargingStationTemplate'; -import { DiagnosticsStatusNotificationRequest, HeartbeatRequest, OCPP16BootNotificationRequest, OCPP16RequestCommand, StatusNotificationRequest } from '../../../types/ocpp/1.6/Requests'; -import { MeterValueUnit, MeterValuesRequest, OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16MeterValuePhase } from '../../../types/ocpp/1.6/MeterValues'; +import { + DiagnosticsStatusNotificationRequest, + HeartbeatRequest, + OCPP16BootNotificationRequest, + OCPP16RequestCommand, + StatusNotificationRequest, +} from '../../../types/ocpp/1.6/Requests'; +import { + MeterValueUnit, + MeterValuesRequest, + OCPP16MeterValue, + OCPP16MeterValueMeasurand, + OCPP16MeterValuePhase, +} from '../../../types/ocpp/1.6/MeterValues'; import type ChargingStation from '../../ChargingStation'; import Constants from '../../../utils/Constants'; @@ -38,26 +58,42 @@ export default class OCPP16RequestService extends OCPPRequestService { await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.HEARTBEAT, params); } - public async sendBootNotification(chargePointModel: string, chargePointVendor: string, chargeBoxSerialNumber?: string, firmwareVersion?: string, - chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string, - params?: SendParams): Promise { + public async sendBootNotification( + chargePointModel: string, + chargePointVendor: string, + chargeBoxSerialNumber?: string, + firmwareVersion?: string, + chargePointSerialNumber?: string, + iccid?: string, + imsi?: string, + meterSerialNumber?: string, + meterType?: string, + params?: SendParams + ): Promise { const payload: OCPP16BootNotificationRequest = { chargePointModel, chargePointVendor, - ...!Utils.isUndefined(chargeBoxSerialNumber) && { chargeBoxSerialNumber }, - ...!Utils.isUndefined(chargePointSerialNumber) && { chargePointSerialNumber }, - ...!Utils.isUndefined(firmwareVersion) && { firmwareVersion }, - ...!Utils.isUndefined(iccid) && { iccid }, - ...!Utils.isUndefined(imsi) && { imsi }, - ...!Utils.isUndefined(meterSerialNumber) && { meterSerialNumber }, - ...!Utils.isUndefined(meterType) && { meterType } + ...(!Utils.isUndefined(chargeBoxSerialNumber) && { chargeBoxSerialNumber }), + ...(!Utils.isUndefined(chargePointSerialNumber) && { chargePointSerialNumber }), + ...(!Utils.isUndefined(firmwareVersion) && { firmwareVersion }), + ...(!Utils.isUndefined(iccid) && { iccid }), + ...(!Utils.isUndefined(imsi) && { imsi }), + ...(!Utils.isUndefined(meterSerialNumber) && { meterSerialNumber }), + ...(!Utils.isUndefined(meterType) && { meterType }), }; - return await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.BOOT_NOTIFICATION, - { ...params, skipBufferingOnError: true }) as OCPP16BootNotificationResponse; + return (await this.sendMessage( + Utils.generateUUID(), + payload, + OCPP16RequestCommand.BOOT_NOTIFICATION, + { ...params, skipBufferingOnError: true } + )) as OCPP16BootNotificationResponse; } - public async sendStatusNotification(connectorId: number, status: OCPP16ChargePointStatus, - errorCode: OCPP16ChargePointErrorCode = OCPP16ChargePointErrorCode.NO_ERROR): Promise { + public async sendStatusNotification( + connectorId: number, + status: OCPP16ChargePointStatus, + errorCode: OCPP16ChargePointErrorCode = OCPP16ChargePointErrorCode.NO_ERROR + ): Promise { const payload: StatusNotificationRequest = { connectorId, errorCode, @@ -66,26 +102,44 @@ export default class OCPP16RequestService extends OCPPRequestService { await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.STATUS_NOTIFICATION); } - public async sendAuthorize(connectorId: number, idTag?: string): Promise { + public async sendAuthorize( + connectorId: number, + idTag?: string + ): Promise { const payload: AuthorizeRequest = { - ...!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.DEFAULT_IDTAG }, + ...(!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.DEFAULT_IDTAG }), }; this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag = idTag; - return await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.AUTHORIZE) as OCPP16AuthorizeResponse; + return (await this.sendMessage( + Utils.generateUUID(), + payload, + OCPP16RequestCommand.AUTHORIZE + )) as OCPP16AuthorizeResponse; } - public async sendStartTransaction(connectorId: number, idTag?: string): Promise { + public async sendStartTransaction( + connectorId: number, + idTag?: string + ): Promise { const payload: StartTransactionRequest = { connectorId, - ...!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.DEFAULT_IDTAG }, + ...(!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.DEFAULT_IDTAG }), meterStart: this.chargingStation.getEnergyActiveImportRegisterByConnectorId(connectorId), timestamp: new Date().toISOString(), }; - return await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.START_TRANSACTION) as OCPP16StartTransactionResponse; + return (await this.sendMessage( + Utils.generateUUID(), + payload, + OCPP16RequestCommand.START_TRANSACTION + )) as OCPP16StartTransactionResponse; } - public async sendStopTransaction(transactionId: number, meterStop: number, idTag?: string, - reason: OCPP16StopTransactionReason = OCPP16StopTransactionReason.NONE): Promise { + public async sendStopTransaction( + transactionId: number, + meterStop: number, + idTag?: string, + reason: OCPP16StopTransactionReason = OCPP16StopTransactionReason.NONE + ): Promise { let connectorId: number; for (const id of this.chargingStation.connectors.keys()) { if (id > 0 && this.chargingStation.getConnectorStatus(id)?.transactionId === transactionId) { @@ -93,229 +147,595 @@ export default class OCPP16RequestService extends OCPPRequestService { break; } } - const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(this.chargingStation, connectorId, meterStop); + const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue( + this.chargingStation, + connectorId, + meterStop + ); // FIXME: should be a callback, each OCPP commands implementation must do only one job - (this.chargingStation.getBeginEndMeterValues() && this.chargingStation.getOcppStrictCompliance() && !this.chargingStation.getOutOfOrderEndMeterValues()) - && await this.sendTransactionEndMeterValues(connectorId, transactionId, transactionEndMeterValue); + this.chargingStation.getBeginEndMeterValues() && + this.chargingStation.getOcppStrictCompliance() && + !this.chargingStation.getOutOfOrderEndMeterValues() && + (await this.sendTransactionEndMeterValues( + connectorId, + transactionId, + transactionEndMeterValue + )); const payload: StopTransactionRequest = { transactionId, - ...!Utils.isUndefined(idTag) && { idTag }, + ...(!Utils.isUndefined(idTag) && { idTag }), meterStop, timestamp: new Date().toISOString(), - ...reason && { reason }, - ...this.chargingStation.getTransactionDataMeterValues() && { transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue, transactionEndMeterValue) }, + ...(reason && { reason }), + ...(this.chargingStation.getTransactionDataMeterValues() && { + transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues( + this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue, + transactionEndMeterValue + ), + }), }; - return await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.STOP_TRANSACTION) as OCPP16StartTransactionResponse; + return (await this.sendMessage( + Utils.generateUUID(), + payload, + OCPP16RequestCommand.STOP_TRANSACTION + )) as OCPP16StartTransactionResponse; } - public async sendMeterValues(connectorId: number, transactionId: number, interval: number, debug = false): Promise { + public async sendMeterValues( + connectorId: number, + transactionId: number, + interval: number, + debug = false + ): Promise { const meterValue: OCPP16MeterValue = { timestamp: new Date().toISOString(), sampledValue: [], }; const connector = this.chargingStation.getConnectorStatus(connectorId); // SoC measurand - const socSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.STATE_OF_CHARGE); + const socSampledValueTemplate = this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.STATE_OF_CHARGE + ); if (socSampledValueTemplate) { const socSampledValueTemplateValue = socSampledValueTemplate.value - ? Utils.getRandomFloatFluctuatedRounded(parseInt(socSampledValueTemplate.value), socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) + ? Utils.getRandomFloatFluctuatedRounded( + parseInt(socSampledValueTemplate.value), + socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT + ) : Utils.getRandomInteger(100); - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue)); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue) + ); const sampledValuesIndex = meterValue.sampledValue.length - 1; if (Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > 100 || debug) { - logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`); + logger.error( + `${this.chargingStation.logPrefix()} MeterValues measurand ${ + meterValue.sampledValue[sampledValuesIndex].measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + meterValue.sampledValue[sampledValuesIndex].value + }/100` + ); } } // Voltage measurand - const voltageSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE); + const voltageSampledValueTemplate = this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.VOLTAGE + ); if (voltageSampledValueTemplate) { - const voltageSampledValueTemplateValue = voltageSampledValueTemplate.value ? parseInt(voltageSampledValueTemplate.value) : this.chargingStation.getVoltageOut(); - const fluctuationPercent = voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; - const voltageMeasurandValue = Utils.getRandomFloatFluctuatedRounded(voltageSampledValueTemplateValue, fluctuationPercent); - if (this.chargingStation.getNumberOfPhases() !== 3 || (this.chargingStation.getNumberOfPhases() === 3 && this.chargingStation.getMainVoltageMeterValues())) { - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue)); + const voltageSampledValueTemplateValue = voltageSampledValueTemplate.value + ? parseInt(voltageSampledValueTemplate.value) + : this.chargingStation.getVoltageOut(); + const fluctuationPercent = + voltageSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; + const voltageMeasurandValue = Utils.getRandomFloatFluctuatedRounded( + voltageSampledValueTemplateValue, + fluctuationPercent + ); + if ( + this.chargingStation.getNumberOfPhases() !== 3 || + (this.chargingStation.getNumberOfPhases() === 3 && + this.chargingStation.getMainVoltageMeterValues()) + ) { + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue(voltageSampledValueTemplate, voltageMeasurandValue) + ); } - for (let phase = 1; this.chargingStation.getNumberOfPhases() === 3 && phase <= this.chargingStation.getNumberOfPhases(); phase++) { + for ( + let phase = 1; + this.chargingStation.getNumberOfPhases() === 3 && + phase <= this.chargingStation.getNumberOfPhases(); + phase++ + ) { const phaseLineToNeutralValue = `L${phase}-N`; - const voltagePhaseLineToNeutralSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE, - phaseLineToNeutralValue as OCPP16MeterValuePhase); + const voltagePhaseLineToNeutralSampledValueTemplate = + this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.VOLTAGE, + phaseLineToNeutralValue as OCPP16MeterValuePhase + ); let voltagePhaseLineToNeutralMeasurandValue: number; if (voltagePhaseLineToNeutralSampledValueTemplate) { - const voltagePhaseLineToNeutralSampledValueTemplateValue = voltagePhaseLineToNeutralSampledValueTemplate.value ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value) : this.chargingStation.getVoltageOut(); - const fluctuationPhaseToNeutralPercent = voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; - voltagePhaseLineToNeutralMeasurandValue = Utils.getRandomFloatFluctuatedRounded(voltagePhaseLineToNeutralSampledValueTemplateValue, fluctuationPhaseToNeutralPercent); + const voltagePhaseLineToNeutralSampledValueTemplateValue = + voltagePhaseLineToNeutralSampledValueTemplate.value + ? parseInt(voltagePhaseLineToNeutralSampledValueTemplate.value) + : this.chargingStation.getVoltageOut(); + const fluctuationPhaseToNeutralPercent = + voltagePhaseLineToNeutralSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT; + voltagePhaseLineToNeutralMeasurandValue = Utils.getRandomFloatFluctuatedRounded( + voltagePhaseLineToNeutralSampledValueTemplateValue, + fluctuationPhaseToNeutralPercent + ); } - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate, - voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue, null, phaseLineToNeutralValue as OCPP16MeterValuePhase)); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + voltagePhaseLineToNeutralSampledValueTemplate ?? voltageSampledValueTemplate, + voltagePhaseLineToNeutralMeasurandValue ?? voltageMeasurandValue, + null, + phaseLineToNeutralValue as OCPP16MeterValuePhase + ) + ); if (this.chargingStation.getPhaseLineToLineVoltageMeterValues()) { - const phaseLineToLineValue = `L${phase}-L${(phase + 1) % this.chargingStation.getNumberOfPhases() !== 0 ? (phase + 1) % this.chargingStation.getNumberOfPhases() : this.chargingStation.getNumberOfPhases()}`; - const voltagePhaseLineToLineSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.VOLTAGE, phaseLineToLineValue as OCPP16MeterValuePhase); + const phaseLineToLineValue = `L${phase}-L${ + (phase + 1) % this.chargingStation.getNumberOfPhases() !== 0 + ? (phase + 1) % this.chargingStation.getNumberOfPhases() + : this.chargingStation.getNumberOfPhases() + }`; + const voltagePhaseLineToLineSampledValueTemplate = + this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.VOLTAGE, + phaseLineToLineValue as OCPP16MeterValuePhase + ); let voltagePhaseLineToLineMeasurandValue: number; if (voltagePhaseLineToLineSampledValueTemplate) { - const voltagePhaseLineToLineSampledValueTemplateValue = voltagePhaseLineToLineSampledValueTemplate.value ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value) : Voltage.VOLTAGE_400; - const fluctuationPhaseLineToLinePercent = voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT; - voltagePhaseLineToLineMeasurandValue = Utils.getRandomFloatFluctuatedRounded(voltagePhaseLineToLineSampledValueTemplateValue, fluctuationPhaseLineToLinePercent); + const voltagePhaseLineToLineSampledValueTemplateValue = + voltagePhaseLineToLineSampledValueTemplate.value + ? parseInt(voltagePhaseLineToLineSampledValueTemplate.value) + : Voltage.VOLTAGE_400; + const fluctuationPhaseLineToLinePercent = + voltagePhaseLineToLineSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT; + voltagePhaseLineToLineMeasurandValue = Utils.getRandomFloatFluctuatedRounded( + voltagePhaseLineToLineSampledValueTemplateValue, + fluctuationPhaseLineToLinePercent + ); } - const defaultVoltagePhaseLineToLineMeasurandValue = Utils.getRandomFloatFluctuatedRounded(Voltage.VOLTAGE_400, fluctuationPercent); - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate, - voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue, null, phaseLineToLineValue as OCPP16MeterValuePhase)); + const defaultVoltagePhaseLineToLineMeasurandValue = Utils.getRandomFloatFluctuatedRounded( + Voltage.VOLTAGE_400, + fluctuationPercent + ); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + voltagePhaseLineToLineSampledValueTemplate ?? voltageSampledValueTemplate, + voltagePhaseLineToLineMeasurandValue ?? defaultVoltagePhaseLineToLineMeasurandValue, + null, + phaseLineToLineValue as OCPP16MeterValuePhase + ) + ); } } } // Power.Active.Import measurand - const powerSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT); + const powerSampledValueTemplate = this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT + ); let powerPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}; if (this.chargingStation.getNumberOfPhases() === 3) { powerPerPhaseSampledValueTemplates = { - L1: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L1_N), - L2: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L2_N), - L3: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, OCPP16MeterValuePhase.L3_N), + L1: this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, + OCPP16MeterValuePhase.L1_N + ), + L2: this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, + OCPP16MeterValuePhase.L2_N + ), + L3: this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.POWER_ACTIVE_IMPORT, + OCPP16MeterValuePhase.L3_N + ), }; } if (powerSampledValueTemplate) { - OCPP16ServiceUtils.checkMeasurandPowerDivider(this.chargingStation, powerSampledValueTemplate.measurand); - const errMsg = `${this.chargingStation.logPrefix()} MeterValues measurand ${powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${this.chargingStation.stationTemplateFile}, cannot calculate ${powerSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`; + OCPP16ServiceUtils.checkMeasurandPowerDivider( + this.chargingStation, + powerSampledValueTemplate.measurand + ); + const errMsg = `${this.chargingStation.logPrefix()} MeterValues measurand ${ + powerSampledValueTemplate.measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${ + this.chargingStation.stationTemplateFile + }, cannot calculate ${ + powerSampledValueTemplate.measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + } measurand value`; const powerMeasurandValues = {} as MeasurandValues; const unitDivider = powerSampledValueTemplate?.unit === MeterValueUnit.KILO_WATT ? 1000 : 1; - const maxPower = Math.round(this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider); - const maxPowerPerPhase = Math.round((this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider) / this.chargingStation.getNumberOfPhases()); + const maxPower = Math.round( + this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider + ); + const maxPowerPerPhase = Math.round( + this.chargingStation.stationInfo.maxPower / + this.chargingStation.stationInfo.powerDivider / + this.chargingStation.getNumberOfPhases() + ); switch (this.chargingStation.getCurrentOutType()) { case CurrentType.AC: if (this.chargingStation.getNumberOfPhases() === 3) { - const defaultFluctuatedPowerPerPhase = powerSampledValueTemplate.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(powerSampledValueTemplate.value) / this.chargingStation.getNumberOfPhases(), powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); - const phase1FluctuatedValue = powerPerPhaseSampledValueTemplates?.L1?.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(powerPerPhaseSampledValueTemplates.L1.value), powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); - const phase2FluctuatedValue = powerPerPhaseSampledValueTemplates?.L2?.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(powerPerPhaseSampledValueTemplates.L2.value), powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); - const phase3FluctuatedValue = powerPerPhaseSampledValueTemplates?.L3?.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(powerPerPhaseSampledValueTemplates.L3.value), powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); - powerMeasurandValues.L1 = (phase1FluctuatedValue ?? defaultFluctuatedPowerPerPhase) ?? Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider); - powerMeasurandValues.L2 = (phase2FluctuatedValue ?? defaultFluctuatedPowerPerPhase) ?? Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider); - powerMeasurandValues.L3 = (phase3FluctuatedValue ?? defaultFluctuatedPowerPerPhase) ?? Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider); + const defaultFluctuatedPowerPerPhase = + powerSampledValueTemplate.value && + Utils.getRandomFloatFluctuatedRounded( + parseInt(powerSampledValueTemplate.value) / + this.chargingStation.getNumberOfPhases(), + powerSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ); + const phase1FluctuatedValue = + powerPerPhaseSampledValueTemplates?.L1?.value && + Utils.getRandomFloatFluctuatedRounded( + parseInt(powerPerPhaseSampledValueTemplates.L1.value), + powerPerPhaseSampledValueTemplates.L1.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ); + const phase2FluctuatedValue = + powerPerPhaseSampledValueTemplates?.L2?.value && + Utils.getRandomFloatFluctuatedRounded( + parseInt(powerPerPhaseSampledValueTemplates.L2.value), + powerPerPhaseSampledValueTemplates.L2.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ); + const phase3FluctuatedValue = + powerPerPhaseSampledValueTemplates?.L3?.value && + Utils.getRandomFloatFluctuatedRounded( + parseInt(powerPerPhaseSampledValueTemplates.L3.value), + powerPerPhaseSampledValueTemplates.L3.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ); + powerMeasurandValues.L1 = + phase1FluctuatedValue ?? + defaultFluctuatedPowerPerPhase ?? + Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider); + powerMeasurandValues.L2 = + phase2FluctuatedValue ?? + defaultFluctuatedPowerPerPhase ?? + Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider); + powerMeasurandValues.L3 = + phase3FluctuatedValue ?? + defaultFluctuatedPowerPerPhase ?? + Utils.getRandomFloatRounded(maxPowerPerPhase / unitDivider); } else { powerMeasurandValues.L1 = powerSampledValueTemplate.value - ? Utils.getRandomFloatFluctuatedRounded(parseInt(powerSampledValueTemplate.value), powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) + ? Utils.getRandomFloatFluctuatedRounded( + parseInt(powerSampledValueTemplate.value), + powerSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) : Utils.getRandomFloatRounded(maxPower / unitDivider); powerMeasurandValues.L2 = 0; powerMeasurandValues.L3 = 0; } - powerMeasurandValues.allPhases = Utils.roundTo(powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3, 2); + powerMeasurandValues.allPhases = Utils.roundTo( + powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3, + 2 + ); break; case CurrentType.DC: powerMeasurandValues.allPhases = powerSampledValueTemplate.value - ? Utils.getRandomFloatFluctuatedRounded(parseInt(powerSampledValueTemplate.value), powerSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) + ? Utils.getRandomFloatFluctuatedRounded( + parseInt(powerSampledValueTemplate.value), + powerSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) : Utils.getRandomFloatRounded(maxPower / unitDivider); break; default: logger.error(errMsg); throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES); } - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(powerSampledValueTemplate, powerMeasurandValues.allPhases)); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + powerSampledValueTemplate, + powerMeasurandValues.allPhases + ) + ); const sampledValuesIndex = meterValue.sampledValue.length - 1; const maxPowerRounded = Utils.roundTo(maxPower / unitDivider, 2); - if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPowerRounded || debug) { - logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxPowerRounded}`); + if ( + Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPowerRounded || + debug + ) { + logger.error( + `${this.chargingStation.logPrefix()} MeterValues measurand ${ + meterValue.sampledValue[sampledValuesIndex].measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + meterValue.sampledValue[sampledValuesIndex].value + }/${maxPowerRounded}` + ); } - for (let phase = 1; this.chargingStation.getNumberOfPhases() === 3 && phase <= this.chargingStation.getNumberOfPhases(); phase++) { + for ( + let phase = 1; + this.chargingStation.getNumberOfPhases() === 3 && + phase <= this.chargingStation.getNumberOfPhases(); + phase++ + ) { const phaseValue = `L${phase}-N`; - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(powerPerPhaseSampledValueTemplates[`L${phase}`] ?? powerSampledValueTemplate, powerMeasurandValues[`L${phase}`], null, - phaseValue as OCPP16MeterValuePhase)); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + powerPerPhaseSampledValueTemplates[`L${phase}`] ?? powerSampledValueTemplate, + powerMeasurandValues[`L${phase}`], + null, + phaseValue as OCPP16MeterValuePhase + ) + ); const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1; const maxPowerPerPhaseRounded = Utils.roundTo(maxPowerPerPhase / unitDivider, 2); - if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > maxPowerPerPhaseRounded || debug) { - logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxPowerPerPhaseRounded}`); + if ( + Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > + maxPowerPerPhaseRounded || + debug + ) { + logger.error( + `${this.chargingStation.logPrefix()} MeterValues measurand ${ + meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: phase ${ + meterValue.sampledValue[sampledValuesPerPhaseIndex].phase + }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + meterValue.sampledValue[sampledValuesPerPhaseIndex].value + }/${maxPowerPerPhaseRounded}` + ); } } } // Current.Import measurand - const currentSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT); + const currentSampledValueTemplate = this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.CURRENT_IMPORT + ); let currentPerPhaseSampledValueTemplates: MeasurandPerPhaseSampledValueTemplates = {}; if (this.chargingStation.getNumberOfPhases() === 3) { currentPerPhaseSampledValueTemplates = { - L1: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L1), - L2: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L2), - L3: this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.CURRENT_IMPORT, OCPP16MeterValuePhase.L3), + L1: this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.CURRENT_IMPORT, + OCPP16MeterValuePhase.L1 + ), + L2: this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.CURRENT_IMPORT, + OCPP16MeterValuePhase.L2 + ), + L3: this.chargingStation.getSampledValueTemplate( + connectorId, + OCPP16MeterValueMeasurand.CURRENT_IMPORT, + OCPP16MeterValuePhase.L3 + ), }; } if (currentSampledValueTemplate) { - OCPP16ServiceUtils.checkMeasurandPowerDivider(this.chargingStation, currentSampledValueTemplate.measurand); - const errMsg = `${this.chargingStation.logPrefix()} MeterValues measurand ${currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${this.chargingStation.stationTemplateFile}, cannot calculate ${currentSampledValueTemplate.measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`; + OCPP16ServiceUtils.checkMeasurandPowerDivider( + this.chargingStation, + currentSampledValueTemplate.measurand + ); + const errMsg = `${this.chargingStation.logPrefix()} MeterValues measurand ${ + currentSampledValueTemplate.measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: Unknown ${this.chargingStation.getCurrentOutType()} currentOutType in template file ${ + this.chargingStation.stationTemplateFile + }, cannot calculate ${ + currentSampledValueTemplate.measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + } measurand value`; const currentMeasurandValues: MeasurandValues = {} as MeasurandValues; let maxAmperage: number; switch (this.chargingStation.getCurrentOutType()) { case CurrentType.AC: - maxAmperage = ACElectricUtils.amperagePerPhaseFromPower(this.chargingStation.getNumberOfPhases(), this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider, this.chargingStation.getVoltageOut()); + maxAmperage = ACElectricUtils.amperagePerPhaseFromPower( + this.chargingStation.getNumberOfPhases(), + this.chargingStation.stationInfo.maxPower / + this.chargingStation.stationInfo.powerDivider, + this.chargingStation.getVoltageOut() + ); if (this.chargingStation.getNumberOfPhases() === 3) { - const defaultFluctuatedAmperagePerPhase = currentSampledValueTemplate.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(currentSampledValueTemplate.value), currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); - const phase1FluctuatedValue = currentPerPhaseSampledValueTemplates?.L1?.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(currentPerPhaseSampledValueTemplates.L1.value), currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); - const phase2FluctuatedValue = currentPerPhaseSampledValueTemplates?.L2?.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(currentPerPhaseSampledValueTemplates.L2.value), currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); - const phase3FluctuatedValue = currentPerPhaseSampledValueTemplates?.L3?.value - && Utils.getRandomFloatFluctuatedRounded(parseInt(currentPerPhaseSampledValueTemplates.L3.value), currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT); - currentMeasurandValues.L1 = (phase1FluctuatedValue ?? defaultFluctuatedAmperagePerPhase) ?? Utils.getRandomFloatRounded(maxAmperage); - currentMeasurandValues.L2 = (phase2FluctuatedValue ?? defaultFluctuatedAmperagePerPhase) ?? Utils.getRandomFloatRounded(maxAmperage); - currentMeasurandValues.L3 = (phase3FluctuatedValue ?? defaultFluctuatedAmperagePerPhase) ?? Utils.getRandomFloatRounded(maxAmperage); + const defaultFluctuatedAmperagePerPhase = + currentSampledValueTemplate.value && + Utils.getRandomFloatFluctuatedRounded( + parseInt(currentSampledValueTemplate.value), + currentSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ); + const phase1FluctuatedValue = + currentPerPhaseSampledValueTemplates?.L1?.value && + Utils.getRandomFloatFluctuatedRounded( + parseInt(currentPerPhaseSampledValueTemplates.L1.value), + currentPerPhaseSampledValueTemplates.L1.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ); + const phase2FluctuatedValue = + currentPerPhaseSampledValueTemplates?.L2?.value && + Utils.getRandomFloatFluctuatedRounded( + parseInt(currentPerPhaseSampledValueTemplates.L2.value), + currentPerPhaseSampledValueTemplates.L2.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ); + const phase3FluctuatedValue = + currentPerPhaseSampledValueTemplates?.L3?.value && + Utils.getRandomFloatFluctuatedRounded( + parseInt(currentPerPhaseSampledValueTemplates.L3.value), + currentPerPhaseSampledValueTemplates.L3.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ); + currentMeasurandValues.L1 = + phase1FluctuatedValue ?? + defaultFluctuatedAmperagePerPhase ?? + Utils.getRandomFloatRounded(maxAmperage); + currentMeasurandValues.L2 = + phase2FluctuatedValue ?? + defaultFluctuatedAmperagePerPhase ?? + Utils.getRandomFloatRounded(maxAmperage); + currentMeasurandValues.L3 = + phase3FluctuatedValue ?? + defaultFluctuatedAmperagePerPhase ?? + Utils.getRandomFloatRounded(maxAmperage); } else { currentMeasurandValues.L1 = currentSampledValueTemplate.value - ? Utils.getRandomFloatFluctuatedRounded(parseInt(currentSampledValueTemplate.value), currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) + ? Utils.getRandomFloatFluctuatedRounded( + parseInt(currentSampledValueTemplate.value), + currentSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) : Utils.getRandomFloatRounded(maxAmperage); currentMeasurandValues.L2 = 0; currentMeasurandValues.L3 = 0; } - currentMeasurandValues.allPhases = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / this.chargingStation.getNumberOfPhases(), 2); + currentMeasurandValues.allPhases = Utils.roundTo( + (currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / + this.chargingStation.getNumberOfPhases(), + 2 + ); break; case CurrentType.DC: - maxAmperage = DCElectricUtils.amperage(this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider, this.chargingStation.getVoltageOut()); + maxAmperage = DCElectricUtils.amperage( + this.chargingStation.stationInfo.maxPower / + this.chargingStation.stationInfo.powerDivider, + this.chargingStation.getVoltageOut() + ); currentMeasurandValues.allPhases = currentSampledValueTemplate.value - ? Utils.getRandomFloatFluctuatedRounded(parseInt(currentSampledValueTemplate.value), currentSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) + ? Utils.getRandomFloatFluctuatedRounded( + parseInt(currentSampledValueTemplate.value), + currentSampledValueTemplate.fluctuationPercent ?? + Constants.DEFAULT_FLUCTUATION_PERCENT + ) : Utils.getRandomFloatRounded(maxAmperage); break; default: logger.error(errMsg); throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES); } - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases)); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + currentSampledValueTemplate, + currentMeasurandValues.allPhases + ) + ); const sampledValuesIndex = meterValue.sampledValue.length - 1; - if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage || debug) { - logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxAmperage}`); + if ( + Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage || + debug + ) { + logger.error( + `${this.chargingStation.logPrefix()} MeterValues measurand ${ + meterValue.sampledValue[sampledValuesIndex].measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + meterValue.sampledValue[sampledValuesIndex].value + }/${maxAmperage}` + ); } - for (let phase = 1; this.chargingStation.getNumberOfPhases() === 3 && phase <= this.chargingStation.getNumberOfPhases(); phase++) { + for ( + let phase = 1; + this.chargingStation.getNumberOfPhases() === 3 && + phase <= this.chargingStation.getNumberOfPhases(); + phase++ + ) { const phaseValue = `L${phase}`; - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(currentPerPhaseSampledValueTemplates[phaseValue] ?? currentSampledValueTemplate, - currentMeasurandValues[phaseValue], null, phaseValue as OCPP16MeterValuePhase)); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + currentPerPhaseSampledValueTemplates[phaseValue] ?? currentSampledValueTemplate, + currentMeasurandValues[phaseValue], + null, + phaseValue as OCPP16MeterValuePhase + ) + ); const sampledValuesPerPhaseIndex = meterValue.sampledValue.length - 1; - if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > maxAmperage || debug) { - logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: phase ${meterValue.sampledValue[sampledValuesPerPhaseIndex].phase}, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesPerPhaseIndex].value}/${maxAmperage}`); + if ( + Utils.convertToFloat(meterValue.sampledValue[sampledValuesPerPhaseIndex].value) > + maxAmperage || + debug + ) { + logger.error( + `${this.chargingStation.logPrefix()} MeterValues measurand ${ + meterValue.sampledValue[sampledValuesPerPhaseIndex].measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: phase ${ + meterValue.sampledValue[sampledValuesPerPhaseIndex].phase + }, connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${ + meterValue.sampledValue[sampledValuesPerPhaseIndex].value + }/${maxAmperage}` + ); } } } // Energy.Active.Import.Register measurand (default) const energySampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId); if (energySampledValueTemplate) { - OCPP16ServiceUtils.checkMeasurandPowerDivider(this.chargingStation, energySampledValueTemplate.measurand); - const unitDivider = energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; - const maxEnergyRounded = Utils.roundTo(((this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider) * interval) / (3600 * 1000), 2); + OCPP16ServiceUtils.checkMeasurandPowerDivider( + this.chargingStation, + energySampledValueTemplate.measurand + ); + const unitDivider = + energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; + const maxEnergyRounded = Utils.roundTo( + ((this.chargingStation.stationInfo.maxPower / + this.chargingStation.stationInfo.powerDivider) * + interval) / + (3600 * 1000), + 2 + ); const energyValueRounded = energySampledValueTemplate.value - // Cumulate the fluctuated value around the static one - ? Utils.getRandomFloatFluctuatedRounded(parseInt(energySampledValueTemplate.value), energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) + ? // Cumulate the fluctuated value around the static one + Utils.getRandomFloatFluctuatedRounded( + parseInt(energySampledValueTemplate.value), + energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT + ) : Utils.getRandomFloatRounded(maxEnergyRounded); - // Persist previous value on connector - if (connector && !Utils.isNullOrUndefined(connector.energyActiveImportRegisterValue) && connector.energyActiveImportRegisterValue >= 0 && - !Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) && connector.transactionEnergyActiveImportRegisterValue >= 0) { + // Persist previous value on connector + if ( + connector && + !Utils.isNullOrUndefined(connector.energyActiveImportRegisterValue) && + connector.energyActiveImportRegisterValue >= 0 && + !Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) && + connector.transactionEnergyActiveImportRegisterValue >= 0 + ) { connector.energyActiveImportRegisterValue += energyValueRounded; connector.transactionEnergyActiveImportRegisterValue += energyValueRounded; } else { connector.energyActiveImportRegisterValue = 0; connector.transactionEnergyActiveImportRegisterValue = 0; } - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(energySampledValueTemplate, - Utils.roundTo(this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) / unitDivider, 2))); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + energySampledValueTemplate, + Utils.roundTo( + this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) / + unitDivider, + 2 + ) + ) + ); const sampledValuesIndex = meterValue.sampledValue.length - 1; if (energyValueRounded > maxEnergyRounded || debug) { - logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo(interval / (3600 * 1000), 4)}h`); + logger.error( + `${this.chargingStation.logPrefix()} MeterValues measurand ${ + meterValue.sampledValue[sampledValuesIndex].measurand ?? + OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: connectorId ${connectorId}, transaction ${ + connector.transactionId + }, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo( + interval / (3600 * 1000), + 4 + )}h` + ); } } const payload: MeterValuesRequest = { @@ -326,7 +746,11 @@ export default class OCPP16RequestService extends OCPPRequestService { await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES); } - public async sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: OCPP16MeterValue): Promise { + public async sendTransactionBeginMeterValues( + connectorId: number, + transactionId: number, + beginMeterValue: OCPP16MeterValue + ): Promise { const payload: MeterValuesRequest = { connectorId, transactionId, @@ -335,7 +759,11 @@ export default class OCPP16RequestService extends OCPPRequestService { await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES); } - public async sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: OCPP16MeterValue): Promise { + public async sendTransactionEndMeterValues( + connectorId: number, + transactionId: number, + endMeterValue: OCPP16MeterValue + ): Promise { const payload: MeterValuesRequest = { connectorId, transactionId, @@ -344,10 +772,16 @@ export default class OCPP16RequestService extends OCPPRequestService { await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES); } - public async sendDiagnosticsStatusNotification(diagnosticsStatus: OCPP16DiagnosticsStatus): Promise { + public async sendDiagnosticsStatusNotification( + diagnosticsStatus: OCPP16DiagnosticsStatus + ): Promise { const payload: DiagnosticsStatusNotificationRequest = { - status: diagnosticsStatus + status: diagnosticsStatus, }; - await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION); + await this.sendMessage( + Utils.generateUUID(), + payload, + OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION + ); } } diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 7f1217fa..79064f1c 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -1,8 +1,25 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. -import { AuthorizeRequest, OCPP16AuthorizationStatus, OCPP16AuthorizeResponse, OCPP16StartTransactionResponse, OCPP16StopTransactionResponse, StartTransactionRequest, StopTransactionRequest } from '../../../types/ocpp/1.6/Transaction'; -import { HeartbeatRequest, OCPP16RequestCommand, StatusNotificationRequest } from '../../../types/ocpp/1.6/Requests'; -import { HeartbeatResponse, OCPP16BootNotificationResponse, OCPP16RegistrationStatus, StatusNotificationResponse } from '../../../types/ocpp/1.6/Responses'; +import { + AuthorizeRequest, + OCPP16AuthorizationStatus, + OCPP16AuthorizeResponse, + OCPP16StartTransactionResponse, + OCPP16StopTransactionResponse, + StartTransactionRequest, + StopTransactionRequest, +} from '../../../types/ocpp/1.6/Transaction'; +import { + HeartbeatRequest, + OCPP16RequestCommand, + StatusNotificationRequest, +} from '../../../types/ocpp/1.6/Requests'; +import { + HeartbeatResponse, + OCPP16BootNotificationResponse, + OCPP16RegistrationStatus, + StatusNotificationResponse, +} from '../../../types/ocpp/1.6/Responses'; import { MeterValuesRequest, MeterValuesResponse } from '../../../types/ocpp/1.6/MeterValues'; import type ChargingStation from '../../ChargingStation'; @@ -34,65 +51,134 @@ export default class OCPP16ResponseService extends OCPPResponseService { [OCPP16RequestCommand.START_TRANSACTION, this.handleResponseStartTransaction.bind(this)], [OCPP16RequestCommand.STOP_TRANSACTION, this.handleResponseStopTransaction.bind(this)], [OCPP16RequestCommand.STATUS_NOTIFICATION, this.handleResponseStatusNotification.bind(this)], - [OCPP16RequestCommand.METER_VALUES, this.handleResponseMeterValues.bind(this)] + [OCPP16RequestCommand.METER_VALUES, this.handleResponseMeterValues.bind(this)], ]); } - public async handleResponse(commandName: OCPP16RequestCommand, payload: JsonType | string, requestPayload: JsonType): Promise { - if (this.chargingStation.isRegistered() || commandName === OCPP16RequestCommand.BOOT_NOTIFICATION) { + public async handleResponse( + commandName: OCPP16RequestCommand, + payload: JsonType | string, + requestPayload: JsonType + ): Promise { + if ( + this.chargingStation.isRegistered() || + commandName === OCPP16RequestCommand.BOOT_NOTIFICATION + ) { if (this.responseHandlers.has(commandName)) { try { await this.responseHandlers.get(commandName)(payload, requestPayload); } catch (error) { - logger.error(this.chargingStation.logPrefix() + ' Handle request response error: %j', error); + logger.error( + this.chargingStation.logPrefix() + ' Handle request response error: %j', + error + ); throw error; } } else { // Throw exception - throw new OCPPError(ErrorType.NOT_IMPLEMENTED, `${commandName} is not implemented to handle request response payload ${JSON.stringify(payload, null, 2)}`, commandName); + throw new OCPPError( + ErrorType.NOT_IMPLEMENTED, + `${commandName} is not implemented to handle request response payload ${JSON.stringify( + payload, + null, + 2 + )}`, + commandName + ); } } else { - throw new OCPPError(ErrorType.SECURITY_ERROR, `${commandName} cannot be issued to handle request response payload ${JSON.stringify(payload, null, 2)} while the charging station is not registered on the central server. `, commandName); + throw new OCPPError( + ErrorType.SECURITY_ERROR, + `${commandName} cannot be issued to handle request response payload ${JSON.stringify( + payload, + null, + 2 + )} while the charging station is not registered on the central server. `, + commandName + ); } } private handleResponseBootNotification(payload: OCPP16BootNotificationResponse): void { if (payload.status === OCPP16RegistrationStatus.ACCEPTED) { - this.chargingStation.addConfigurationKey(OCPP16StandardParametersKey.HeartBeatInterval, payload.interval.toString()); - this.chargingStation.addConfigurationKey(OCPP16StandardParametersKey.HeartbeatInterval, payload.interval.toString(), { visible: false }); - this.chargingStation.heartbeatSetInterval ? this.chargingStation.restartHeartbeat() : this.chargingStation.startHeartbeat(); + this.chargingStation.addConfigurationKey( + OCPP16StandardParametersKey.HeartBeatInterval, + payload.interval.toString() + ); + this.chargingStation.addConfigurationKey( + OCPP16StandardParametersKey.HeartbeatInterval, + payload.interval.toString(), + { visible: false } + ); + this.chargingStation.heartbeatSetInterval + ? this.chargingStation.restartHeartbeat() + : this.chargingStation.startHeartbeat(); } if (Object.values(OCPP16RegistrationStatus).includes(payload.status)) { - const logMsg = `${this.chargingStation.logPrefix()} Charging station in '${payload.status}' state on the central server`; - payload.status === OCPP16RegistrationStatus.REJECTED ? logger.warn(logMsg) : logger.info(logMsg); + const logMsg = `${this.chargingStation.logPrefix()} Charging station in '${ + payload.status + }' state on the central server`; + payload.status === OCPP16RegistrationStatus.REJECTED + ? logger.warn(logMsg) + : logger.info(logMsg); } else { - logger.error(this.chargingStation.logPrefix() + ' Charging station boot notification response received: %j with undefined registration status', payload); + logger.error( + this.chargingStation.logPrefix() + + ' Charging station boot notification response received: %j with undefined registration status', + payload + ); } } - private handleResponseHeartbeat(payload: HeartbeatResponse, requestPayload: HeartbeatRequest): void { - logger.debug(this.chargingStation.logPrefix() + ' Heartbeat response received: %j to Heartbeat request: %j', payload, requestPayload); + private handleResponseHeartbeat( + payload: HeartbeatResponse, + requestPayload: HeartbeatRequest + ): void { + logger.debug( + this.chargingStation.logPrefix() + + ' Heartbeat response received: %j to Heartbeat request: %j', + payload, + requestPayload + ); } - private handleResponseAuthorize(payload: OCPP16AuthorizeResponse, requestPayload: AuthorizeRequest): void { + private handleResponseAuthorize( + payload: OCPP16AuthorizeResponse, + requestPayload: AuthorizeRequest + ): void { let authorizeConnectorId: number; for (const connectorId of this.chargingStation.connectors.keys()) { - if (connectorId > 0 && this.chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === requestPayload.idTag) { + if ( + connectorId > 0 && + this.chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag === + requestPayload.idTag + ) { authorizeConnectorId = connectorId; break; } } if (payload.idTagInfo.status === OCPP16AuthorizationStatus.ACCEPTED) { this.chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true; - logger.debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} authorized on connector ${authorizeConnectorId}`); + logger.debug( + `${this.chargingStation.logPrefix()} IdTag ${ + requestPayload.idTag + } authorized on connector ${authorizeConnectorId}` + ); } else { this.chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = false; delete this.chargingStation.getConnectorStatus(authorizeConnectorId).authorizeIdTag; - logger.debug(`${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${payload.idTagInfo.status} on connector ${authorizeConnectorId}`); + logger.debug( + `${this.chargingStation.logPrefix()} IdTag ${requestPayload.idTag} refused with status ${ + payload.idTagInfo.status + } on connector ${authorizeConnectorId}` + ); } } - private async handleResponseStartTransaction(payload: OCPP16StartTransactionResponse, requestPayload: StartTransactionRequest): Promise { + private async handleResponseStartTransaction( + payload: OCPP16StartTransactionResponse, + requestPayload: StartTransactionRequest + ): Promise { const connectorId = requestPayload.connectorId; let transactionConnectorId: number; @@ -103,44 +189,109 @@ export default class OCPP16ResponseService extends OCPPResponseService { } } if (!transactionConnectorId) { - logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction on a non existing connector Id ' + connectorId.toString()); + logger.error( + this.chargingStation.logPrefix() + + ' Trying to start a transaction on a non existing connector Id ' + + connectorId.toString() + ); return; } - if (this.chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted && this.chargingStation.getAuthorizeRemoteTxRequests() - && this.chargingStation.getLocalAuthListEnabled() && this.chargingStation.hasAuthorizedTags() && !this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized) { - logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction with a not local authorized idTag ' + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag + ' on connector Id ' + connectorId.toString()); + if ( + this.chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted && + this.chargingStation.getAuthorizeRemoteTxRequests() && + this.chargingStation.getLocalAuthListEnabled() && + this.chargingStation.hasAuthorizedTags() && + !this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized + ) { + logger.error( + this.chargingStation.logPrefix() + + ' Trying to start a transaction with a not local authorized idTag ' + + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag + + ' on connector Id ' + + connectorId.toString() + ); await this.resetConnectorOnStartTransactionError(connectorId); return; } - if (this.chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted && this.chargingStation.getAuthorizeRemoteTxRequests() - && this.chargingStation.getMayAuthorizeAtRemoteStart() && !this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized - && !this.chargingStation.getConnectorStatus(connectorId).idTagAuthorized) { - logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction with a not authorized idTag ' + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag + ' on connector Id ' + connectorId.toString()); + if ( + this.chargingStation.getConnectorStatus(connectorId).transactionRemoteStarted && + this.chargingStation.getAuthorizeRemoteTxRequests() && + this.chargingStation.getMayAuthorizeAtRemoteStart() && + !this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized && + !this.chargingStation.getConnectorStatus(connectorId).idTagAuthorized + ) { + logger.error( + this.chargingStation.logPrefix() + + ' Trying to start a transaction with a not authorized idTag ' + + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag + + ' on connector Id ' + + connectorId.toString() + ); await this.resetConnectorOnStartTransactionError(connectorId); return; } - if (this.chargingStation.getConnectorStatus(connectorId).idTagAuthorized && this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag !== requestPayload.idTag) { - logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction with an idTag ' + requestPayload.idTag + ' different from the authorize request one ' + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag + ' on connector Id ' + connectorId.toString()); + if ( + this.chargingStation.getConnectorStatus(connectorId).idTagAuthorized && + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag !== requestPayload.idTag + ) { + logger.error( + this.chargingStation.logPrefix() + + ' Trying to start a transaction with an idTag ' + + requestPayload.idTag + + ' different from the authorize request one ' + + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag + + ' on connector Id ' + + connectorId.toString() + ); await this.resetConnectorOnStartTransactionError(connectorId); return; } - if (this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized - && this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag !== requestPayload.idTag) { - logger.error(this.chargingStation.logPrefix() + ' Trying to start a transaction with an idTag ' + requestPayload.idTag + ' different from the local authorized one ' + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag + ' on connector Id ' + connectorId.toString()); + if ( + this.chargingStation.getConnectorStatus(connectorId).idTagLocalAuthorized && + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag !== + requestPayload.idTag + ) { + logger.error( + this.chargingStation.logPrefix() + + ' Trying to start a transaction with an idTag ' + + requestPayload.idTag + + ' different from the local authorized one ' + + this.chargingStation.getConnectorStatus(connectorId).localAuthorizeIdTag + + ' on connector Id ' + + connectorId.toString() + ); await this.resetConnectorOnStartTransactionError(connectorId); return; } if (this.chargingStation.getConnectorStatus(connectorId)?.transactionStarted) { - logger.debug(this.chargingStation.logPrefix() + ' Trying to start a transaction on an already used connector ' + connectorId.toString() + ': %j', this.chargingStation.getConnectorStatus(connectorId)); + logger.debug( + this.chargingStation.logPrefix() + + ' Trying to start a transaction on an already used connector ' + + connectorId.toString() + + ': %j', + this.chargingStation.getConnectorStatus(connectorId) + ); return; } - if (this.chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.AVAILABLE - && this.chargingStation.getConnectorStatus(connectorId)?.status !== OCPP16ChargePointStatus.PREPARING) { - logger.error(`${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${this.chargingStation.getConnectorStatus(connectorId)?.status}`); + if ( + this.chargingStation.getConnectorStatus(connectorId)?.status !== + OCPP16ChargePointStatus.AVAILABLE && + this.chargingStation.getConnectorStatus(connectorId)?.status !== + OCPP16ChargePointStatus.PREPARING + ) { + logger.error( + `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${ + this.chargingStation.getConnectorStatus(connectorId)?.status + }` + ); return; } if (!Number.isInteger(payload.transactionId)) { - logger.warn(`${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${payload.transactionId}, converting to integer`); + logger.warn( + `${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with a non integer transaction Id ${ + payload.transactionId + }, converting to integer` + ); payload.transactionId = Utils.convertToInt(payload.transactionId); } @@ -148,71 +299,178 @@ export default class OCPP16ResponseService extends OCPPResponseService { this.chargingStation.getConnectorStatus(connectorId).transactionStarted = true; this.chargingStation.getConnectorStatus(connectorId).transactionId = payload.transactionId; this.chargingStation.getConnectorStatus(connectorId).transactionIdTag = requestPayload.idTag; - this.chargingStation.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue = 0; - this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue = OCPP16ServiceUtils.buildTransactionBeginMeterValue(this.chargingStation, connectorId, - requestPayload.meterStart); - this.chargingStation.getBeginEndMeterValues() && await this.chargingStation.ocppRequestService.sendTransactionBeginMeterValues(connectorId, payload.transactionId, - this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue); - await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.CHARGING); - this.chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.CHARGING; - logger.info(this.chargingStation.logPrefix() + ' Transaction ' + payload.transactionId.toString() + ' STARTED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + connectorId.toString() + ' for idTag ' + requestPayload.idTag); + this.chargingStation.getConnectorStatus( + connectorId + ).transactionEnergyActiveImportRegisterValue = 0; + this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue = + OCPP16ServiceUtils.buildTransactionBeginMeterValue( + this.chargingStation, + connectorId, + requestPayload.meterStart + ); + this.chargingStation.getBeginEndMeterValues() && + (await this.chargingStation.ocppRequestService.sendTransactionBeginMeterValues( + connectorId, + payload.transactionId, + this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue + )); + await this.chargingStation.ocppRequestService.sendStatusNotification( + connectorId, + OCPP16ChargePointStatus.CHARGING + ); + this.chargingStation.getConnectorStatus(connectorId).status = + OCPP16ChargePointStatus.CHARGING; + logger.info( + this.chargingStation.logPrefix() + + ' Transaction ' + + payload.transactionId.toString() + + ' STARTED on ' + + this.chargingStation.stationInfo.chargingStationId + + '#' + + connectorId.toString() + + ' for idTag ' + + requestPayload.idTag + ); if (this.chargingStation.stationInfo.powerSharedByConnectors) { this.chargingStation.stationInfo.powerDivider++; } - const configuredMeterValueSampleInterval = this.chargingStation.getConfigurationKey(OCPP16StandardParametersKey.MeterValueSampleInterval); - this.chargingStation.startMeterValues(connectorId, configuredMeterValueSampleInterval ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000 : 60000); + const configuredMeterValueSampleInterval = this.chargingStation.getConfigurationKey( + OCPP16StandardParametersKey.MeterValueSampleInterval + ); + this.chargingStation.startMeterValues( + connectorId, + configuredMeterValueSampleInterval + ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000 + : 60000 + ); } else { - logger.warn(this.chargingStation.logPrefix() + ' Starting transaction id ' + payload.transactionId.toString() + ' REJECTED with status ' + payload?.idTagInfo?.status + ', idTag ' + requestPayload.idTag); + logger.warn( + this.chargingStation.logPrefix() + + ' Starting transaction id ' + + payload.transactionId.toString() + + ' REJECTED with status ' + + payload?.idTagInfo?.status + + ', idTag ' + + requestPayload.idTag + ); await this.resetConnectorOnStartTransactionError(connectorId); } } private async resetConnectorOnStartTransactionError(connectorId: number): Promise { this.chargingStation.resetConnectorStatus(connectorId); - if (this.chargingStation.getConnectorStatus(connectorId).status !== OCPP16ChargePointStatus.AVAILABLE) { - await this.chargingStation.ocppRequestService.sendStatusNotification(connectorId, OCPP16ChargePointStatus.AVAILABLE); - this.chargingStation.getConnectorStatus(connectorId).status = OCPP16ChargePointStatus.AVAILABLE; + if ( + this.chargingStation.getConnectorStatus(connectorId).status !== + OCPP16ChargePointStatus.AVAILABLE + ) { + await this.chargingStation.ocppRequestService.sendStatusNotification( + connectorId, + OCPP16ChargePointStatus.AVAILABLE + ); + this.chargingStation.getConnectorStatus(connectorId).status = + OCPP16ChargePointStatus.AVAILABLE; } } - private async handleResponseStopTransaction(payload: OCPP16StopTransactionResponse, requestPayload: StopTransactionRequest): Promise { + private async handleResponseStopTransaction( + payload: OCPP16StopTransactionResponse, + requestPayload: StopTransactionRequest + ): Promise { let transactionConnectorId: number; for (const connectorId of this.chargingStation.connectors.keys()) { - if (connectorId > 0 && this.chargingStation.getConnectorStatus(connectorId)?.transactionId === requestPayload.transactionId) { + if ( + connectorId > 0 && + this.chargingStation.getConnectorStatus(connectorId)?.transactionId === + requestPayload.transactionId + ) { transactionConnectorId = connectorId; break; } } if (!transactionConnectorId) { - logger.error(this.chargingStation.logPrefix() + ' Trying to stop a non existing transaction ' + requestPayload.transactionId.toString()); + logger.error( + this.chargingStation.logPrefix() + + ' Trying to stop a non existing transaction ' + + requestPayload.transactionId.toString() + ); return; } if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { - (this.chargingStation.getBeginEndMeterValues() && !this.chargingStation.getOcppStrictCompliance() && this.chargingStation.getOutOfOrderEndMeterValues()) - && await this.chargingStation.ocppRequestService.sendTransactionEndMeterValues(transactionConnectorId, requestPayload.transactionId, - OCPP16ServiceUtils.buildTransactionEndMeterValue(this.chargingStation, transactionConnectorId, requestPayload.meterStop)); - if (!this.chargingStation.isChargingStationAvailable() || !this.chargingStation.isConnectorAvailable(transactionConnectorId)) { - await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.UNAVAILABLE); - this.chargingStation.getConnectorStatus(transactionConnectorId).status = OCPP16ChargePointStatus.UNAVAILABLE; + this.chargingStation.getBeginEndMeterValues() && + !this.chargingStation.getOcppStrictCompliance() && + this.chargingStation.getOutOfOrderEndMeterValues() && + (await this.chargingStation.ocppRequestService.sendTransactionEndMeterValues( + transactionConnectorId, + requestPayload.transactionId, + OCPP16ServiceUtils.buildTransactionEndMeterValue( + this.chargingStation, + transactionConnectorId, + requestPayload.meterStop + ) + )); + if ( + !this.chargingStation.isChargingStationAvailable() || + !this.chargingStation.isConnectorAvailable(transactionConnectorId) + ) { + await this.chargingStation.ocppRequestService.sendStatusNotification( + transactionConnectorId, + OCPP16ChargePointStatus.UNAVAILABLE + ); + this.chargingStation.getConnectorStatus(transactionConnectorId).status = + OCPP16ChargePointStatus.UNAVAILABLE; } else { - await this.chargingStation.ocppRequestService.sendStatusNotification(transactionConnectorId, OCPP16ChargePointStatus.AVAILABLE); - this.chargingStation.getConnectorStatus(transactionConnectorId).status = OCPP16ChargePointStatus.AVAILABLE; + await this.chargingStation.ocppRequestService.sendStatusNotification( + transactionConnectorId, + OCPP16ChargePointStatus.AVAILABLE + ); + this.chargingStation.getConnectorStatus(transactionConnectorId).status = + OCPP16ChargePointStatus.AVAILABLE; } if (this.chargingStation.stationInfo.powerSharedByConnectors) { this.chargingStation.stationInfo.powerDivider--; } - logger.info(this.chargingStation.logPrefix() + ' Transaction ' + requestPayload.transactionId.toString() + ' STOPPED on ' + this.chargingStation.stationInfo.chargingStationId + '#' + transactionConnectorId.toString()); + logger.info( + this.chargingStation.logPrefix() + + ' Transaction ' + + requestPayload.transactionId.toString() + + ' STOPPED on ' + + this.chargingStation.stationInfo.chargingStationId + + '#' + + transactionConnectorId.toString() + ); this.chargingStation.resetConnectorStatus(transactionConnectorId); } else { - logger.warn(this.chargingStation.logPrefix() + ' Stopping transaction id ' + requestPayload.transactionId.toString() + ' REJECTED with status ' + payload.idTagInfo?.status); + logger.warn( + this.chargingStation.logPrefix() + + ' Stopping transaction id ' + + requestPayload.transactionId.toString() + + ' REJECTED with status ' + + payload.idTagInfo?.status + ); } } - private handleResponseStatusNotification(payload: StatusNotificationRequest, requestPayload: StatusNotificationResponse): void { - logger.debug(this.chargingStation.logPrefix() + ' Status notification response received: %j to StatusNotification request: %j', payload, requestPayload); + private handleResponseStatusNotification( + payload: StatusNotificationRequest, + requestPayload: StatusNotificationResponse + ): void { + logger.debug( + this.chargingStation.logPrefix() + + ' Status notification response received: %j to StatusNotification request: %j', + payload, + requestPayload + ); } - private handleResponseMeterValues(payload: MeterValuesRequest, requestPayload: MeterValuesResponse): void { - logger.debug(this.chargingStation.logPrefix() + ' MeterValues response received: %j to MeterValues request: %j', payload, requestPayload); + private handleResponseMeterValues( + payload: MeterValuesRequest, + requestPayload: MeterValuesResponse + ): void { + logger.debug( + this.chargingStation.logPrefix() + + ' MeterValues response received: %j to MeterValues request: %j', + payload, + requestPayload + ); } } diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index cacbe389..d112270a 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -1,6 +1,14 @@ // Partial Copyright Jerome Benoit. 2021. All Rights Reserved. -import { MeterValueContext, MeterValueLocation, MeterValueUnit, OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16MeterValuePhase, OCPP16SampledValue } from '../../../types/ocpp/1.6/MeterValues'; +import { + MeterValueContext, + MeterValueLocation, + MeterValueUnit, + OCPP16MeterValue, + OCPP16MeterValueMeasurand, + OCPP16MeterValuePhase, + OCPP16SampledValue, +} from '../../../types/ocpp/1.6/MeterValues'; import type ChargingStation from '../../ChargingStation'; import { ErrorType } from '../../../types/ocpp/ErrorType'; @@ -11,34 +19,54 @@ import Utils from '../../../utils/Utils'; import logger from '../../../utils/Logger'; export class OCPP16ServiceUtils { - public static checkMeasurandPowerDivider(chargingStation: ChargingStation, measurandType: OCPP16MeterValueMeasurand): void { + public static checkMeasurandPowerDivider( + chargingStation: ChargingStation, + measurandType: OCPP16MeterValueMeasurand + ): void { if (Utils.isUndefined(chargingStation.stationInfo.powerDivider)) { - const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`; + const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${ + measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: powerDivider is undefined`; logger.error(errMsg); throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES); } else if (chargingStation.stationInfo?.powerDivider <= 0) { - const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`; + const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${ + measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + }: powerDivider have zero or below value ${chargingStation.stationInfo.powerDivider}`; logger.error(errMsg); throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, RequestCommand.METER_VALUES); } } - public static buildSampledValue(sampledValueTemplate: SampledValueTemplate, value: number, context?: MeterValueContext, phase?: OCPP16MeterValuePhase): OCPP16SampledValue { - const sampledValueValue = value ?? (sampledValueTemplate?.value ?? null); - const sampledValueContext = context ?? (sampledValueTemplate?.context ?? null); - const sampledValueLocation = sampledValueTemplate?.location ?? OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate?.measurand ?? null); - const sampledValuePhase = phase ?? (sampledValueTemplate?.phase ?? null); + public static buildSampledValue( + sampledValueTemplate: SampledValueTemplate, + value: number, + context?: MeterValueContext, + phase?: OCPP16MeterValuePhase + ): OCPP16SampledValue { + const sampledValueValue = value ?? sampledValueTemplate?.value ?? null; + const sampledValueContext = context ?? sampledValueTemplate?.context ?? null; + const sampledValueLocation = + sampledValueTemplate?.location ?? + OCPP16ServiceUtils.getMeasurandDefaultLocation(sampledValueTemplate?.measurand ?? null); + const sampledValuePhase = phase ?? sampledValueTemplate?.phase ?? null; return { - ...!Utils.isNullOrUndefined(sampledValueTemplate.unit) && { unit: sampledValueTemplate.unit }, - ...!Utils.isNullOrUndefined(sampledValueContext) && { context: sampledValueContext }, - ...!Utils.isNullOrUndefined(sampledValueTemplate.measurand) && { measurand: sampledValueTemplate.measurand }, - ...!Utils.isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }, - ...!Utils.isNullOrUndefined(sampledValueValue) && { value: sampledValueValue.toString() }, - ...!Utils.isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }, + ...(!Utils.isNullOrUndefined(sampledValueTemplate.unit) && { + unit: sampledValueTemplate.unit, + }), + ...(!Utils.isNullOrUndefined(sampledValueContext) && { context: sampledValueContext }), + ...(!Utils.isNullOrUndefined(sampledValueTemplate.measurand) && { + measurand: sampledValueTemplate.measurand, + }), + ...(!Utils.isNullOrUndefined(sampledValueLocation) && { location: sampledValueLocation }), + ...(!Utils.isNullOrUndefined(sampledValueValue) && { value: sampledValueValue.toString() }), + ...(!Utils.isNullOrUndefined(sampledValuePhase) && { phase: sampledValuePhase }), }; } - public static getMeasurandDefaultUnit(measurandType: OCPP16MeterValueMeasurand): MeterValueUnit | undefined { + public static getMeasurandDefaultUnit( + measurandType: OCPP16MeterValueMeasurand + ): MeterValueUnit | undefined { switch (measurandType) { case OCPP16MeterValueMeasurand.CURRENT_EXPORT: case OCPP16MeterValueMeasurand.CURRENT_IMPORT: @@ -58,14 +86,20 @@ export class OCPP16ServiceUtils { } } - public static getMeasurandDefaultLocation(measurandType: OCPP16MeterValueMeasurand): MeterValueLocation | undefined { + public static getMeasurandDefaultLocation( + measurandType: OCPP16MeterValueMeasurand + ): MeterValueLocation | undefined { switch (measurandType) { case OCPP16MeterValueMeasurand.STATE_OF_CHARGE: return MeterValueLocation.EV; } } - public static buildTransactionBeginMeterValue(chargingStation: ChargingStation, connectorId: number, meterBegin: number): OCPP16MeterValue { + public static buildTransactionBeginMeterValue( + chargingStation: ChargingStation, + connectorId: number, + meterBegin: number + ): OCPP16MeterValue { const meterValue: OCPP16MeterValue = { timestamp: new Date().toISOString(), sampledValue: [], @@ -73,11 +107,21 @@ export class OCPP16ServiceUtils { // Energy.Active.Import.Register measurand (default) const sampledValueTemplate = chargingStation.getSampledValueTemplate(connectorId); const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(sampledValueTemplate, Utils.roundTo(meterBegin / unitDivider, 4), MeterValueContext.TRANSACTION_BEGIN)); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + sampledValueTemplate, + Utils.roundTo(meterBegin / unitDivider, 4), + MeterValueContext.TRANSACTION_BEGIN + ) + ); return meterValue; } - public static buildTransactionEndMeterValue(chargingStation: ChargingStation, connectorId: number, meterEnd: number): OCPP16MeterValue { + public static buildTransactionEndMeterValue( + chargingStation: ChargingStation, + connectorId: number, + meterEnd: number + ): OCPP16MeterValue { const meterValue: OCPP16MeterValue = { timestamp: new Date().toISOString(), sampledValue: [], @@ -85,11 +129,20 @@ export class OCPP16ServiceUtils { // Energy.Active.Import.Register measurand (default) const sampledValueTemplate = chargingStation.getSampledValueTemplate(connectorId); const unitDivider = sampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; - meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(sampledValueTemplate, Utils.roundTo(meterEnd / unitDivider, 4), MeterValueContext.TRANSACTION_END)); + meterValue.sampledValue.push( + OCPP16ServiceUtils.buildSampledValue( + sampledValueTemplate, + Utils.roundTo(meterEnd / unitDivider, 4), + MeterValueContext.TRANSACTION_END + ) + ); return meterValue; } - public static buildTransactionDataMeterValues(transactionBeginMeterValue: OCPP16MeterValue, transactionEndMeterValue: OCPP16MeterValue): OCPP16MeterValue[] { + public static buildTransactionDataMeterValues( + transactionBeginMeterValue: OCPP16MeterValue, + transactionEndMeterValue: OCPP16MeterValue + ): OCPP16MeterValue[] { const meterValues: OCPP16MeterValue[] = []; meterValues.push(transactionBeginMeterValue); meterValues.push(transactionEndMeterValue); diff --git a/src/charging-station/ocpp/OCPPIncomingRequestService.ts b/src/charging-station/ocpp/OCPPIncomingRequestService.ts index 5fadfb95..0d56ebe8 100644 --- a/src/charging-station/ocpp/OCPPIncomingRequestService.ts +++ b/src/charging-station/ocpp/OCPPIncomingRequestService.ts @@ -5,22 +5,36 @@ import { JsonType } from '../../types/JsonType'; import logger from '../../utils/Logger'; export default abstract class OCPPIncomingRequestService { - private static readonly instances: Map = new Map(); + private static readonly instances: Map = new Map< + string, + OCPPIncomingRequestService + >(); protected chargingStation: ChargingStation; protected constructor(chargingStation: ChargingStation) { this.chargingStation = chargingStation; } - public static getInstance(this: new (chargingStation: ChargingStation) => T, chargingStation: ChargingStation): T { + public static getInstance( + this: new (chargingStation: ChargingStation) => T, + chargingStation: ChargingStation + ): T { if (!OCPPIncomingRequestService.instances.has(chargingStation.id)) { OCPPIncomingRequestService.instances.set(chargingStation.id, new this(chargingStation)); } return OCPPIncomingRequestService.instances.get(chargingStation.id) as T; } - protected handleIncomingRequestError(commandName: IncomingRequestCommand, error: Error, params: HandleErrorParams = { throwError: true }): T { - logger.error(this.chargingStation.logPrefix() + ' Incoming request command %s error: %j', commandName, error); + protected handleIncomingRequestError( + commandName: IncomingRequestCommand, + error: Error, + params: HandleErrorParams = { throwError: true } + ): T { + logger.error( + this.chargingStation.logPrefix() + ' Incoming request command %s error: %j', + commandName, + error + ); if (!params?.throwError && params?.errorResponse) { return params?.errorResponse; } @@ -32,5 +46,9 @@ export default abstract class OCPPIncomingRequestService { } } - public abstract handleRequest(messageId: string, commandName: IncomingRequestCommand, commandPayload: JsonType): Promise; + public abstract handleRequest( + messageId: string, + commandName: IncomingRequestCommand, + commandPayload: JsonType + ): Promise; } diff --git a/src/charging-station/ocpp/OCPPRequestService.ts b/src/charging-station/ocpp/OCPPRequestService.ts index 2ecc32ea..04cd508a 100644 --- a/src/charging-station/ocpp/OCPPRequestService.ts +++ b/src/charging-station/ocpp/OCPPRequestService.ts @@ -1,5 +1,16 @@ -import { AuthorizeResponse, StartTransactionResponse, StopTransactionReason, StopTransactionResponse } from '../../types/ocpp/Transaction'; -import { DiagnosticsStatus, IncomingRequestCommand, RequestCommand, ResponseType, SendParams } from '../../types/ocpp/Requests'; +import { + AuthorizeResponse, + StartTransactionResponse, + StopTransactionReason, + StopTransactionResponse, +} from '../../types/ocpp/Transaction'; +import { + DiagnosticsStatus, + IncomingRequestCommand, + RequestCommand, + ResponseType, + SendParams, +} from '../../types/ocpp/Requests'; import { BootNotificationResponse } from '../../types/ocpp/Responses'; import { ChargePointErrorCode } from '../../types/ocpp/ChargePointErrorCode'; @@ -19,146 +30,264 @@ import Utils from '../../utils/Utils'; import logger from '../../utils/Logger'; export default abstract class OCPPRequestService { - private static readonly instances: Map = new Map(); + private static readonly instances: Map = new Map< + string, + OCPPRequestService + >(); protected readonly chargingStation: ChargingStation; private readonly ocppResponseService: OCPPResponseService; - protected constructor(chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) { + protected constructor( + chargingStation: ChargingStation, + ocppResponseService: OCPPResponseService + ) { this.chargingStation = chargingStation; this.ocppResponseService = ocppResponseService; } - public static getInstance(this: new (chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) => T, chargingStation: ChargingStation, ocppResponseService: OCPPResponseService): T { + public static getInstance( + this: new (chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) => T, + chargingStation: ChargingStation, + ocppResponseService: OCPPResponseService + ): T { if (!OCPPRequestService.instances.has(chargingStation.id)) { - OCPPRequestService.instances.set(chargingStation.id, new this(chargingStation, ocppResponseService)); + OCPPRequestService.instances.set( + chargingStation.id, + new this(chargingStation, ocppResponseService) + ); } return OCPPRequestService.instances.get(chargingStation.id) as T; } - public async sendResult(messageId: string, messagePayload: JsonType, commandName: IncomingRequestCommand): Promise { + public async sendResult( + messageId: string, + messagePayload: JsonType, + commandName: IncomingRequestCommand + ): Promise { try { // Send result message - return await this.internalSendMessage(messageId, messagePayload, MessageType.CALL_RESULT_MESSAGE, commandName); + return await this.internalSendMessage( + messageId, + messagePayload, + MessageType.CALL_RESULT_MESSAGE, + commandName + ); } catch (error) { this.handleRequestError(commandName, error as Error); } } - public async sendError(messageId: string, ocppError: OCPPError, commandName: IncomingRequestCommand): Promise { + public async sendError( + messageId: string, + ocppError: OCPPError, + commandName: IncomingRequestCommand + ): Promise { try { // Send error message - return await this.internalSendMessage(messageId, ocppError, MessageType.CALL_ERROR_MESSAGE, commandName); + return await this.internalSendMessage( + messageId, + ocppError, + MessageType.CALL_ERROR_MESSAGE, + commandName + ); } catch (error) { this.handleRequestError(commandName, error as Error); } } - protected async sendMessage(messageId: string, messagePayload: JsonType, commandName: RequestCommand, params: SendParams = { - skipBufferingOnError: false, - triggerMessage: false - }): Promise { + protected async sendMessage( + messageId: string, + messagePayload: JsonType, + commandName: RequestCommand, + params: SendParams = { + skipBufferingOnError: false, + triggerMessage: false, + } + ): Promise { try { - return await this.internalSendMessage(messageId, messagePayload, MessageType.CALL_MESSAGE, commandName, params); + return await this.internalSendMessage( + messageId, + messagePayload, + MessageType.CALL_MESSAGE, + commandName, + params + ); } catch (error) { this.handleRequestError(commandName, error as Error, { throwError: false }); } } - private async internalSendMessage(messageId: string, messagePayload: JsonType | OCPPError, messageType: MessageType, commandName?: RequestCommand | IncomingRequestCommand, - params: SendParams = { - skipBufferingOnError: false, - triggerMessage: false - }): Promise { - if ((this.chargingStation.isInUnknownState() && commandName === RequestCommand.BOOT_NOTIFICATION) - || (!this.chargingStation.getOcppStrictCompliance() && this.chargingStation.isInUnknownState()) - || this.chargingStation.isInAcceptedState() || (this.chargingStation.isInPendingState() && params.triggerMessage)) { + private async internalSendMessage( + messageId: string, + messagePayload: JsonType | OCPPError, + messageType: MessageType, + commandName?: RequestCommand | IncomingRequestCommand, + params: SendParams = { + skipBufferingOnError: false, + triggerMessage: false, + } + ): Promise { + if ( + (this.chargingStation.isInUnknownState() && + commandName === RequestCommand.BOOT_NOTIFICATION) || + (!this.chargingStation.getOcppStrictCompliance() && + this.chargingStation.isInUnknownState()) || + this.chargingStation.isInAcceptedState() || + (this.chargingStation.isInPendingState() && params.triggerMessage) + ) { // eslint-disable-next-line @typescript-eslint/no-this-alias const self = this; // Send a message through wsConnection - return Utils.promiseWithTimeout(new Promise((resolve, reject) => { - const messageToSend = this.buildMessageToSend(messageId, messagePayload, messageType, commandName, responseCallback, rejectCallback); - if (this.chargingStation.getEnableStatistics()) { - this.chargingStation.performanceStatistics.addRequestStatistic(commandName, messageType); - } - // Check if wsConnection opened - if (this.chargingStation.isWebSocketConnectionOpened()) { - // Yes: Send Message - const beginId = PerformanceStatistics.beginMeasure(commandName); - // FIXME: Handle sending error - this.chargingStation.wsConnection.send(messageToSend); - PerformanceStatistics.endMeasure(commandName, beginId); - } else if (!params.skipBufferingOnError) { - // Buffer it - this.chargingStation.bufferMessage(messageToSend); - const ocppError = new OCPPError(ErrorType.GENERIC_ERROR, `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`, commandName, messagePayload?.details as JsonType ?? {}); - if (messageType === MessageType.CALL_MESSAGE) { - // Reject it but keep the request in the cache - return reject(ocppError); + return Utils.promiseWithTimeout( + new Promise((resolve, reject) => { + const messageToSend = this.buildMessageToSend( + messageId, + messagePayload, + messageType, + commandName, + responseCallback, + rejectCallback + ); + if (this.chargingStation.getEnableStatistics()) { + this.chargingStation.performanceStatistics.addRequestStatistic( + commandName, + messageType + ); } - return rejectCallback(ocppError, false); - } else { - // Reject it - return rejectCallback(new OCPPError(ErrorType.GENERIC_ERROR, `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`, commandName, messagePayload?.details as JsonType ?? {}), false); - } - // Response? - if (messageType !== MessageType.CALL_MESSAGE) { - // Yes: send Ok - return resolve(messagePayload); - } - - /** - * Function that will receive the request's response - * - * @param payload - * @param requestPayload - */ - async function responseCallback(payload: JsonType | string, requestPayload: JsonType): Promise { - if (self.chargingStation.getEnableStatistics()) { - self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_RESULT_MESSAGE); + // Check if wsConnection opened + if (this.chargingStation.isWebSocketConnectionOpened()) { + // Yes: Send Message + const beginId = PerformanceStatistics.beginMeasure(commandName); + // FIXME: Handle sending error + this.chargingStation.wsConnection.send(messageToSend); + PerformanceStatistics.endMeasure(commandName, beginId); + } else if (!params.skipBufferingOnError) { + // Buffer it + this.chargingStation.bufferMessage(messageToSend); + const ocppError = new OCPPError( + ErrorType.GENERIC_ERROR, + `WebSocket closed for buffered message id '${messageId}' with content '${messageToSend}'`, + commandName, + (messagePayload?.details as JsonType) ?? {} + ); + if (messageType === MessageType.CALL_MESSAGE) { + // Reject it but keep the request in the cache + return reject(ocppError); + } + return rejectCallback(ocppError, false); + } else { + // Reject it + return rejectCallback( + new OCPPError( + ErrorType.GENERIC_ERROR, + `WebSocket closed for non buffered message id '${messageId}' with content '${messageToSend}'`, + commandName, + (messagePayload?.details as JsonType) ?? {} + ), + false + ); } - // Handle the request's response - try { - await self.ocppResponseService.handleResponse(commandName as RequestCommand, payload, requestPayload); - resolve(payload); - } catch (error) { - reject(error); - throw error; - } finally { - self.chargingStation.requests.delete(messageId); + // Response? + if (messageType !== MessageType.CALL_MESSAGE) { + // Yes: send Ok + return resolve(messagePayload); + } + + /** + * Function that will receive the request's response + * + * @param payload + * @param requestPayload + */ + async function responseCallback( + payload: JsonType | string, + requestPayload: JsonType + ): Promise { + if (self.chargingStation.getEnableStatistics()) { + self.chargingStation.performanceStatistics.addRequestStatistic( + commandName, + MessageType.CALL_RESULT_MESSAGE + ); + } + // Handle the request's response + try { + await self.ocppResponseService.handleResponse( + commandName as RequestCommand, + payload, + requestPayload + ); + resolve(payload); + } catch (error) { + reject(error); + throw error; + } finally { + self.chargingStation.requests.delete(messageId); + } } - } - /** - * Function that will receive the request's error response - * - * @param error - * @param requestStatistic - */ - function rejectCallback(error: OCPPError, requestStatistic = true): void { - if (requestStatistic && self.chargingStation.getEnableStatistics()) { - self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_ERROR_MESSAGE); + /** + * Function that will receive the request's error response + * + * @param error + * @param requestStatistic + */ + function rejectCallback(error: OCPPError, requestStatistic = true): void { + if (requestStatistic && self.chargingStation.getEnableStatistics()) { + self.chargingStation.performanceStatistics.addRequestStatistic( + commandName, + MessageType.CALL_ERROR_MESSAGE + ); + } + logger.error( + `${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`, + error, + commandName, + messagePayload + ); + self.chargingStation.requests.delete(messageId); + reject(error); } - logger.error(`${self.chargingStation.logPrefix()} Error %j occurred when calling command %s with message data %j`, error, commandName, messagePayload); - self.chargingStation.requests.delete(messageId); - reject(error); + }), + Constants.OCPP_WEBSOCKET_TIMEOUT, + new OCPPError( + ErrorType.GENERIC_ERROR, + `Timeout for message id '${messageId}'`, + commandName, + (messagePayload?.details as JsonType) ?? {} + ), + () => { + messageType === MessageType.CALL_MESSAGE && + this.chargingStation.requests.delete(messageId); } - }), Constants.OCPP_WEBSOCKET_TIMEOUT, new OCPPError(ErrorType.GENERIC_ERROR, `Timeout for message id '${messageId}'`, commandName, messagePayload?.details as JsonType ?? {}), () => { - messageType === MessageType.CALL_MESSAGE && this.chargingStation.requests.delete(messageId); - }); + ); } - throw new OCPPError(ErrorType.SECURITY_ERROR, `Cannot send command ${commandName} payload when the charging station is in ${this.chargingStation.getRegistrationStatus()} state on the central server`, commandName); + throw new OCPPError( + ErrorType.SECURITY_ERROR, + `Cannot send command ${commandName} payload when the charging station is in ${this.chargingStation.getRegistrationStatus()} state on the central server`, + commandName + ); } - private buildMessageToSend(messageId: string, messagePayload: JsonType | OCPPError, messageType: MessageType, commandName?: RequestCommand | IncomingRequestCommand, - responseCallback?: (payload: JsonType | string, requestPayload: JsonType) => Promise, - rejectCallback?: (error: OCPPError, requestStatistic?: boolean) => void): string { + private buildMessageToSend( + messageId: string, + messagePayload: JsonType | OCPPError, + messageType: MessageType, + commandName?: RequestCommand | IncomingRequestCommand, + responseCallback?: (payload: JsonType | string, requestPayload: JsonType) => Promise, + rejectCallback?: (error: OCPPError, requestStatistic?: boolean) => void + ): string { let messageToSend: string; // Type of message switch (messageType) { // Request case MessageType.CALL_MESSAGE: // Build request - this.chargingStation.requests.set(messageId, [responseCallback, rejectCallback, commandName, messagePayload]); + this.chargingStation.requests.set(messageId, [ + responseCallback, + rejectCallback, + commandName, + messagePayload, + ]); messageToSend = JSON.stringify([messageType, messageId, commandName, messagePayload]); break; // Response @@ -169,27 +298,78 @@ export default abstract class OCPPRequestService { // Error Message case MessageType.CALL_ERROR_MESSAGE: // Build Error Message - messageToSend = JSON.stringify([messageType, messageId, messagePayload?.code ?? ErrorType.GENERIC_ERROR, messagePayload?.message ?? '', messagePayload?.details ?? { commandName }]); + messageToSend = JSON.stringify([ + messageType, + messageId, + messagePayload?.code ?? ErrorType.GENERIC_ERROR, + messagePayload?.message ?? '', + messagePayload?.details ?? { commandName }, + ]); break; } return messageToSend; } - private handleRequestError(commandName: RequestCommand | IncomingRequestCommand, error: Error, params: HandleErrorParams = { throwError: true }): void { - logger.error(this.chargingStation.logPrefix() + ' Request command %s error: %j', commandName, error); + private handleRequestError( + commandName: RequestCommand | IncomingRequestCommand, + error: Error, + params: HandleErrorParams = { throwError: true } + ): void { + logger.error( + this.chargingStation.logPrefix() + ' Request command %s error: %j', + commandName, + error + ); if (params?.throwError) { throw error; } } public abstract sendHeartbeat(params?: SendParams): Promise; - public abstract sendBootNotification(chargePointModel: string, chargePointVendor: string, chargeBoxSerialNumber?: string, firmwareVersion?: string, chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string, params?: SendParams): Promise; - public abstract sendStatusNotification(connectorId: number, status: ChargePointStatus, errorCode?: ChargePointErrorCode): Promise; + public abstract sendBootNotification( + chargePointModel: string, + chargePointVendor: string, + chargeBoxSerialNumber?: string, + firmwareVersion?: string, + chargePointSerialNumber?: string, + iccid?: string, + imsi?: string, + meterSerialNumber?: string, + meterType?: string, + params?: SendParams + ): Promise; + public abstract sendStatusNotification( + connectorId: number, + status: ChargePointStatus, + errorCode?: ChargePointErrorCode + ): Promise; public abstract sendAuthorize(connectorId: number, idTag?: string): Promise; - public abstract sendStartTransaction(connectorId: number, idTag?: string): Promise; - public abstract sendStopTransaction(transactionId: number, meterStop: number, idTag?: string, reason?: StopTransactionReason): Promise; - public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number): Promise; - public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: MeterValue): Promise; - public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: MeterValue): Promise; - public abstract sendDiagnosticsStatusNotification(diagnosticsStatus: DiagnosticsStatus): Promise; + public abstract sendStartTransaction( + connectorId: number, + idTag?: string + ): Promise; + public abstract sendStopTransaction( + transactionId: number, + meterStop: number, + idTag?: string, + reason?: StopTransactionReason + ): Promise; + public abstract sendMeterValues( + connectorId: number, + transactionId: number, + interval: number + ): Promise; + public abstract sendTransactionBeginMeterValues( + connectorId: number, + transactionId: number, + beginMeterValue: MeterValue + ): Promise; + public abstract sendTransactionEndMeterValues( + connectorId: number, + transactionId: number, + endMeterValue: MeterValue + ): Promise; + public abstract sendDiagnosticsStatusNotification( + diagnosticsStatus: DiagnosticsStatus + ): Promise; } diff --git a/src/charging-station/ocpp/OCPPResponseService.ts b/src/charging-station/ocpp/OCPPResponseService.ts index c0dd3f76..2fa0112c 100644 --- a/src/charging-station/ocpp/OCPPResponseService.ts +++ b/src/charging-station/ocpp/OCPPResponseService.ts @@ -3,19 +3,29 @@ import { JsonType } from '../../types/JsonType'; import { RequestCommand } from '../../types/ocpp/Requests'; export default abstract class OCPPResponseService { - private static readonly instances: Map = new Map(); + private static readonly instances: Map = new Map< + string, + OCPPResponseService + >(); protected readonly chargingStation: ChargingStation; protected constructor(chargingStation: ChargingStation) { this.chargingStation = chargingStation; } - public static getInstance(this: new (chargingStation: ChargingStation) => T, chargingStation: ChargingStation): T { + public static getInstance( + this: new (chargingStation: ChargingStation) => T, + chargingStation: ChargingStation + ): T { if (!OCPPResponseService.instances.has(chargingStation.id)) { OCPPResponseService.instances.set(chargingStation.id, new this(chargingStation)); } return OCPPResponseService.instances.get(chargingStation.id) as T; } - public abstract handleResponse(commandName: RequestCommand, payload: JsonType | string, requestPayload: JsonType): Promise; + public abstract handleResponse( + commandName: RequestCommand, + payload: JsonType | string, + requestPayload: JsonType + ): Promise; } diff --git a/src/charging-station/ui-websocket-services/AbstractUIService.ts b/src/charging-station/ui-websocket-services/AbstractUIService.ts index 2dd6a200..0bc34bd9 100644 --- a/src/charging-station/ui-websocket-services/AbstractUIService.ts +++ b/src/charging-station/ui-websocket-services/AbstractUIService.ts @@ -21,7 +21,7 @@ export default abstract class AbstractUIService { if (this.messageHandlers.has(command)) { try { // Call the method to build the message response - messageResponse = await this.messageHandlers.get(command)(payload) as JsonType; + messageResponse = (await this.messageHandlers.get(command)(payload)) as JsonType; } catch (error) { // Log logger.error(this.uiWebSocketServer.logPrefix() + ' Handle message error: %j', error); @@ -29,16 +29,19 @@ export default abstract class AbstractUIService { } } else { // Throw exception - throw new BaseError(`${command} is not implemented to handle message payload ${JSON.stringify(payload, null, 2)}`); + throw new BaseError( + `${command} is not implemented to handle message payload ${JSON.stringify( + payload, + null, + 2 + )}` + ); } // Send the built message response this.uiWebSocketServer.broadcastToClients(this.buildProtocolMessage(command, messageResponse)); } - protected buildProtocolMessage( - command: ProtocolCommand, - payload: JsonType, - ): string { + protected buildProtocolMessage(command: ProtocolCommand, payload: JsonType): string { return JSON.stringify([command, payload]); } diff --git a/src/charging-station/ui-websocket-services/UIService001.ts b/src/charging-station/ui-websocket-services/UIService001.ts index a9bc401c..18fb66a9 100644 --- a/src/charging-station/ui-websocket-services/UIService001.ts +++ b/src/charging-station/ui-websocket-services/UIService001.ts @@ -7,10 +7,16 @@ import UIWebSocketServer from '../UIWebSocketServer'; export default class UIService001 extends AbstractUIService { constructor(uiWebSocketServer: UIWebSocketServer) { super(uiWebSocketServer); - this.messageHandlers.set(ProtocolCommand.START_TRANSACTION, this.handleStartTransaction.bind(this) as ProtocolRequestHandler); - this.messageHandlers.set(ProtocolCommand.STOP_TRANSACTION, this.handleStopTransaction.bind(this) as ProtocolRequestHandler); + this.messageHandlers.set( + ProtocolCommand.START_TRANSACTION, + this.handleStartTransaction.bind(this) as ProtocolRequestHandler + ); + this.messageHandlers.set( + ProtocolCommand.STOP_TRANSACTION, + this.handleStopTransaction.bind(this) as ProtocolRequestHandler + ); } - private handleStartTransaction(payload: JsonType): void { } - private handleStopTransaction(payload: JsonType): void { } + private handleStartTransaction(payload: JsonType): void {} + private handleStopTransaction(payload: JsonType): void {} } diff --git a/src/charging-station/ui-websocket-services/UIServiceFactory.ts b/src/charging-station/ui-websocket-services/UIServiceFactory.ts index 108a113f..d6a87cdb 100644 --- a/src/charging-station/ui-websocket-services/UIServiceFactory.ts +++ b/src/charging-station/ui-websocket-services/UIServiceFactory.ts @@ -8,7 +8,10 @@ export default class UIServiceFactory { // This is intentional } - public static getUIServiceImplementation(version: ProtocolVersion, uiWebSocketServer: UIWebSocketServer): AbstractUIService | null { + public static getUIServiceImplementation( + version: ProtocolVersion, + uiWebSocketServer: UIWebSocketServer + ): AbstractUIService | null { switch (version) { case ProtocolVersion['0.0.1']: return new UIService001(uiWebSocketServer); diff --git a/src/charging-station/ui-websocket-services/UIServiceUtils.ts b/src/charging-station/ui-websocket-services/UIServiceUtils.ts index 2c37e80a..9427c08e 100644 --- a/src/charging-station/ui-websocket-services/UIServiceUtils.ts +++ b/src/charging-station/ui-websocket-services/UIServiceUtils.ts @@ -5,19 +5,32 @@ import Utils from '../../utils/Utils'; import logger from '../../utils/Logger'; export class UIServiceUtils { - public static handleProtocols = (protocols: Set, request: IncomingMessage): string | false => { + public static handleProtocols = ( + protocols: Set, + request: IncomingMessage + ): string | false => { let protocolIndex: number; let protocol: Protocol; let version: ProtocolVersion; for (const fullProtocol of protocols) { protocolIndex = fullProtocol.indexOf(Protocol.UI); - protocol = fullProtocol.substring(protocolIndex, protocolIndex + Protocol.UI.length) as Protocol; + protocol = fullProtocol.substring( + protocolIndex, + protocolIndex + Protocol.UI.length + ) as Protocol; version = fullProtocol.substring(protocolIndex + Protocol.UI.length) as ProtocolVersion; - if (Object.values(Protocol).includes(protocol) && Object.values(ProtocolVersion).includes(version)) { + if ( + Object.values(Protocol).includes(protocol) && + Object.values(ProtocolVersion).includes(version) + ) { return fullProtocol; } } - logger.error(`${Utils.logPrefix(' UI WebSocket Server:')} Unsupported protocol: ${protocol} or protocol version: ${version}`); + logger.error( + `${Utils.logPrefix( + ' UI WebSocket Server:' + )} Unsupported protocol: ${protocol} or protocol version: ${version}` + ); return false; }; } diff --git a/src/exception/BaseError.ts b/src/exception/BaseError.ts index ad78e261..1471844c 100644 --- a/src/exception/BaseError.ts +++ b/src/exception/BaseError.ts @@ -3,7 +3,7 @@ export default class BaseError extends Error { super(message); this.name = new.target.name; Object.setPrototypeOf(this, new.target.prototype); - Error.captureStackTrace ? (Error.captureStackTrace(this, this.constructor)) : (this.createStack()); + Error.captureStackTrace ? Error.captureStackTrace(this, this.constructor) : this.createStack(); } private createStack(): void { diff --git a/src/exception/OCPPError.ts b/src/exception/OCPPError.ts index 8b108aec..9f2b2262 100644 --- a/src/exception/OCPPError.ts +++ b/src/exception/OCPPError.ts @@ -11,7 +11,12 @@ export default class OCPPError extends BaseError { command?: RequestCommand | IncomingRequestCommand; details?: JsonType; - constructor(code: ErrorType | IncomingRequestCommand, message: string, command?: RequestCommand | IncomingRequestCommand, details?: JsonType) { + constructor( + code: ErrorType | IncomingRequestCommand, + message: string, + command?: RequestCommand | IncomingRequestCommand, + details?: JsonType + ) { super(message); this.code = code ?? ErrorType.GENERIC_ERROR; diff --git a/src/performance/PerformanceStatistics.ts b/src/performance/PerformanceStatistics.ts index b9fa1490..bec0f62e 100644 --- a/src/performance/PerformanceStatistics.ts +++ b/src/performance/PerformanceStatistics.ts @@ -14,7 +14,10 @@ import logger from '../utils/Logger'; import { parentPort } from 'worker_threads'; export default class PerformanceStatistics { - private static readonly instances: Map = new Map(); + private static readonly instances: Map = new Map< + string, + PerformanceStatistics + >(); private readonly objId: string; private readonly objName: string; private performanceObserver: PerformanceObserver; @@ -25,7 +28,13 @@ export default class PerformanceStatistics { 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>() }; + 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>(), + }; } public static getInstance(objId: string, objName: string, uri: URL): PerformanceStatistics { @@ -46,27 +55,48 @@ export default class PerformanceStatistics { performance.clearMarks(markId); } - public addRequestStatistic(command: RequestCommand | IncomingRequestCommand, messageType: MessageType): void { + public addRequestStatistic( + command: RequestCommand | IncomingRequestCommand, + messageType: MessageType + ): void { switch (messageType) { case MessageType.CALL_MESSAGE: - if (this.statistics.statisticsData.has(command) && this.statistics.statisticsData.get(command)?.countRequest) { + if ( + this.statistics.statisticsData.has(command) && + this.statistics.statisticsData.get(command)?.countRequest + ) { this.statistics.statisticsData.get(command).countRequest++; } else { - this.statistics.statisticsData.set(command, Object.assign({ countRequest: 1 }, this.statistics.statisticsData.get(command))); + this.statistics.statisticsData.set( + command, + Object.assign({ countRequest: 1 }, this.statistics.statisticsData.get(command)) + ); } break; case MessageType.CALL_RESULT_MESSAGE: - if (this.statistics.statisticsData.has(command) && this.statistics.statisticsData.get(command)?.countResponse) { + if ( + this.statistics.statisticsData.has(command) && + this.statistics.statisticsData.get(command)?.countResponse + ) { this.statistics.statisticsData.get(command).countResponse++; } else { - this.statistics.statisticsData.set(command, Object.assign({ countResponse: 1 }, this.statistics.statisticsData.get(command))); + this.statistics.statisticsData.set( + command, + Object.assign({ countResponse: 1 }, this.statistics.statisticsData.get(command)) + ); } break; case MessageType.CALL_ERROR_MESSAGE: - if (this.statistics.statisticsData.has(command) && this.statistics.statisticsData.get(command)?.countError) { + if ( + this.statistics.statisticsData.has(command) && + this.statistics.statisticsData.get(command)?.countError + ) { this.statistics.statisticsData.get(command).countError++; } else { - this.statistics.statisticsData.set(command, Object.assign({ countError: 1 }, this.statistics.statisticsData.get(command))); + this.statistics.statisticsData.set( + command, + Object.assign({ countError: 1 }, this.statistics.statisticsData.get(command)) + ); } break; default: @@ -78,7 +108,11 @@ export default class PerformanceStatistics { public start(): void { this.startLogStatisticsInterval(); if (Configuration.getPerformanceStorage().enabled) { - logger.info(`${this.logPrefix()} storage enabled: type ${Configuration.getPerformanceStorage().type}, uri: ${Configuration.getPerformanceStorage().uri}`); + logger.info( + `${this.logPrefix()} storage enabled: type ${ + Configuration.getPerformanceStorage().type + }, uri: ${Configuration.getPerformanceStorage().uri}` + ); } } @@ -99,7 +133,10 @@ export default class PerformanceStatistics { this.performanceObserver = new PerformanceObserver((list) => { const lastPerformanceEntry = list.getEntries()[0]; this.addPerformanceEntryToStatistics(lastPerformanceEntry); - logger.debug(`${this.logPrefix()} '${lastPerformanceEntry.name}' performance entry: %j`, lastPerformanceEntry); + logger.debug( + `${this.logPrefix()} '${lastPerformanceEntry.name}' performance entry: %j`, + lastPerformanceEntry + ); }); this.performanceObserver.observe({ entryTypes: ['measure'] }); } @@ -113,9 +150,18 @@ export default class PerformanceStatistics { this.displayInterval = setInterval(() => { this.logStatistics(); }, Configuration.getLogStatisticsInterval() * 1000); - logger.info(this.logPrefix() + ' logged every ' + Utils.formatDurationSeconds(Configuration.getLogStatisticsInterval())); + logger.info( + this.logPrefix() + + ' logged every ' + + Utils.formatDurationSeconds(Configuration.getLogStatisticsInterval()) + ); } else { - logger.info(this.logPrefix() + ' log interval is set to ' + Configuration.getLogStatisticsInterval().toString() + '. Not logging statistics'); + logger.info( + this.logPrefix() + + ' log interval is set to ' + + Configuration.getLogStatisticsInterval().toString() + + '. Not logging statistics' + ); } } @@ -123,12 +169,12 @@ export default class PerformanceStatistics { 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); const middleIndex = Math.floor(sortedDataSet.length / 2); if (sortedDataSet.length % 2) { return sortedDataSet[middleIndex / 2]; } - return (sortedDataSet[(middleIndex - 1)] + sortedDataSet[middleIndex]) / 2; + return (sortedDataSet[middleIndex - 1] + sortedDataSet[middleIndex]) / 2; } // TODO: use order statistics tree https://en.wikipedia.org/wiki/Order_statistic_tree @@ -139,14 +185,14 @@ export default class PerformanceStatistics { if (Utils.isEmptyArray(dataSet)) { return 0; } - const sortedDataSet = dataSet.slice().sort((a, b) => (a - b)); + const sortedDataSet = dataSet.slice().sort((a, b) => a - b); if (percentile === 0) { return sortedDataSet[0]; } if (percentile === 100) { return sortedDataSet[sortedDataSet.length - 1]; } - const percentileIndex = ((percentile / 100) * sortedDataSet.length) - 1; + const percentileIndex = (percentile / 100) * sortedDataSet.length - 1; if (Number.isInteger(percentileIndex)) { return (sortedDataSet[percentileIndex] + sortedDataSet[percentileIndex + 1]) / 2; } @@ -180,28 +226,61 @@ export default class PerformanceStatistics { } // Update current statistics this.statistics.updatedAt = new Date(); - this.statistics.statisticsData.get(entryName).countTimeMeasurement = this.statistics.statisticsData.get(entryName)?.countTimeMeasurement - ? this.statistics.statisticsData.get(entryName).countTimeMeasurement + 1 - : 1; + this.statistics.statisticsData.get(entryName).countTimeMeasurement = + this.statistics.statisticsData.get(entryName)?.countTimeMeasurement + ? this.statistics.statisticsData.get(entryName).countTimeMeasurement + 1 + : 1; this.statistics.statisticsData.get(entryName).currentTimeMeasurement = entry.duration; - this.statistics.statisticsData.get(entryName).minTimeMeasurement = this.statistics.statisticsData.get(entryName)?.minTimeMeasurement - ? (this.statistics.statisticsData.get(entryName).minTimeMeasurement > entry.duration ? entry.duration : this.statistics.statisticsData.get(entryName).minTimeMeasurement) - : entry.duration; - this.statistics.statisticsData.get(entryName).maxTimeMeasurement = this.statistics.statisticsData.get(entryName)?.maxTimeMeasurement - ? (this.statistics.statisticsData.get(entryName).maxTimeMeasurement < entry.duration ? entry.duration : this.statistics.statisticsData.get(entryName).maxTimeMeasurement) - : entry.duration; - this.statistics.statisticsData.get(entryName).totalTimeMeasurement = this.statistics.statisticsData.get(entryName)?.totalTimeMeasurement - ? this.statistics.statisticsData.get(entryName).totalTimeMeasurement + entry.duration - : entry.duration; - this.statistics.statisticsData.get(entryName).avgTimeMeasurement = this.statistics.statisticsData.get(entryName).totalTimeMeasurement / this.statistics.statisticsData.get(entryName).countTimeMeasurement; + this.statistics.statisticsData.get(entryName).minTimeMeasurement = + this.statistics.statisticsData.get(entryName)?.minTimeMeasurement + ? this.statistics.statisticsData.get(entryName).minTimeMeasurement > entry.duration + ? entry.duration + : this.statistics.statisticsData.get(entryName).minTimeMeasurement + : entry.duration; + this.statistics.statisticsData.get(entryName).maxTimeMeasurement = + this.statistics.statisticsData.get(entryName)?.maxTimeMeasurement + ? this.statistics.statisticsData.get(entryName).maxTimeMeasurement < entry.duration + ? entry.duration + : this.statistics.statisticsData.get(entryName).maxTimeMeasurement + : entry.duration; + this.statistics.statisticsData.get(entryName).totalTimeMeasurement = + this.statistics.statisticsData.get(entryName)?.totalTimeMeasurement + ? this.statistics.statisticsData.get(entryName).totalTimeMeasurement + entry.duration + : entry.duration; + this.statistics.statisticsData.get(entryName).avgTimeMeasurement = + this.statistics.statisticsData.get(entryName).totalTimeMeasurement / + this.statistics.statisticsData.get(entryName).countTimeMeasurement; Array.isArray(this.statistics.statisticsData.get(entryName).timeMeasurementSeries) - ? this.statistics.statisticsData.get(entryName).timeMeasurementSeries.push({ timestamp: entry.startTime, value: entry.duration }) - : this.statistics.statisticsData.get(entryName).timeMeasurementSeries = new CircularArray(DEFAULT_CIRCULAR_ARRAY_SIZE, { timestamp: entry.startTime, value: entry.duration }); - this.statistics.statisticsData.get(entryName).medTimeMeasurement = this.median(this.extractTimeSeriesValues(this.statistics.statisticsData.get(entryName).timeMeasurementSeries)); - this.statistics.statisticsData.get(entryName).ninetyFiveThPercentileTimeMeasurement = this.percentile(this.extractTimeSeriesValues(this.statistics.statisticsData.get(entryName).timeMeasurementSeries), 95); - this.statistics.statisticsData.get(entryName).stdDevTimeMeasurement = this.stdDeviation(this.extractTimeSeriesValues(this.statistics.statisticsData.get(entryName).timeMeasurementSeries)); + ? this.statistics.statisticsData + .get(entryName) + .timeMeasurementSeries.push({ timestamp: entry.startTime, value: entry.duration }) + : (this.statistics.statisticsData.get(entryName).timeMeasurementSeries = + new CircularArray(DEFAULT_CIRCULAR_ARRAY_SIZE, { + timestamp: entry.startTime, + value: entry.duration, + })); + this.statistics.statisticsData.get(entryName).medTimeMeasurement = this.median( + this.extractTimeSeriesValues( + this.statistics.statisticsData.get(entryName).timeMeasurementSeries + ) + ); + this.statistics.statisticsData.get(entryName).ninetyFiveThPercentileTimeMeasurement = + this.percentile( + this.extractTimeSeriesValues( + this.statistics.statisticsData.get(entryName).timeMeasurementSeries + ), + 95 + ); + this.statistics.statisticsData.get(entryName).stdDevTimeMeasurement = this.stdDeviation( + this.extractTimeSeriesValues( + this.statistics.statisticsData.get(entryName).timeMeasurementSeries + ) + ); if (Configuration.getPerformanceStorage().enabled) { - parentPort.postMessage({ id: ChargingStationWorkerMessageEvents.PERFORMANCE_STATISTICS, data: this.statistics }); + parentPort.postMessage({ + id: ChargingStationWorkerMessageEvents.PERFORMANCE_STATISTICS, + data: this.statistics, + }); } } diff --git a/src/performance/storage/JsonFileStorage.ts b/src/performance/storage/JsonFileStorage.ts index 9b016f06..8a8808e9 100644 --- a/src/performance/storage/JsonFileStorage.ts +++ b/src/performance/storage/JsonFileStorage.ts @@ -17,33 +17,45 @@ export class JsonFileStorage extends Storage { public storePerformanceStatistics(performanceStatistics: Statistics): void { this.checkPerformanceRecordsFile(); - lockfile.lock(this.dbName, { stale: 5000, retries: 3 }) + lockfile + .lock(this.dbName, { stale: 5000, retries: 3 }) .then(async (release) => { try { const fileData = fs.readFileSync(this.dbName, 'utf8'); - const performanceRecords: Statistics[] = fileData ? JSON.parse(fileData) as Statistics[] : []; + const performanceRecords: Statistics[] = fileData + ? (JSON.parse(fileData) as Statistics[]) + : []; performanceRecords.push(performanceStatistics); fs.writeFileSync( this.dbName, - JSON.stringify(performanceRecords, + JSON.stringify( + performanceRecords, (key, value) => { if (value instanceof Map) { return { dataType: 'Map', - value: [...value] + value: [...value], }; } return value as Statistics; }, - 2), + 2 + ), 'utf8' ); } catch (error) { - FileUtils.handleFileException(this.logPrefix, Constants.PERFORMANCE_RECORDS_FILETYPE, this.dbName, error as NodeJS.ErrnoException); + FileUtils.handleFileException( + this.logPrefix, + Constants.PERFORMANCE_RECORDS_FILETYPE, + this.dbName, + error as NodeJS.ErrnoException + ); } await release(); }) - .catch(() => { /* This is intentional */ }); + .catch(() => { + /* This is intentional */ + }); } public open(): void { @@ -52,7 +64,12 @@ export class JsonFileStorage extends Storage { this.fd = fs.openSync(this.dbName, 'a+'); } } catch (error) { - FileUtils.handleFileException(this.logPrefix, Constants.PERFORMANCE_RECORDS_FILETYPE, this.dbName, error as NodeJS.ErrnoException); + FileUtils.handleFileException( + this.logPrefix, + Constants.PERFORMANCE_RECORDS_FILETYPE, + this.dbName, + error as NodeJS.ErrnoException + ); } } @@ -63,13 +80,20 @@ export class JsonFileStorage extends Storage { this.fd = null; } } catch (error) { - FileUtils.handleFileException(this.logPrefix, Constants.PERFORMANCE_RECORDS_FILETYPE, this.dbName, error as NodeJS.ErrnoException); + FileUtils.handleFileException( + this.logPrefix, + Constants.PERFORMANCE_RECORDS_FILETYPE, + this.dbName, + error as NodeJS.ErrnoException + ); } } private checkPerformanceRecordsFile(): void { if (!this?.fd) { - throw new Error(`${this.logPrefix} Performance records '${this.dbName}' file descriptor not found`); + throw new Error( + `${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 82bdd234..1953491a 100644 --- a/src/performance/storage/MikroOrmStorage.ts +++ b/src/performance/storage/MikroOrmStorage.ts @@ -54,15 +54,20 @@ export class MikroOrmStorage extends Storage { if (this.storageType === StorageType.SQLITE) { return `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`; } - return this.storageUri.pathname.replace(/(?:^\/)|(?:\/$)/g, '') ?? Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME; + 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(), }; } @@ -75,4 +80,3 @@ export class MikroOrmStorage extends Storage { } } } - diff --git a/src/performance/storage/MongoDBStorage.ts b/src/performance/storage/MongoDBStorage.ts index 6bc43bbb..cefab6f3 100644 --- a/src/performance/storage/MongoDBStorage.ts +++ b/src/performance/storage/MongoDBStorage.ts @@ -14,13 +14,18 @@ export class MongoDBStorage extends Storage { 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; + this.dbName = + this.storageUri.pathname.replace(/(?:^\/)|(?:\/$)/g, '') ?? + Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME; } public async storePerformanceStatistics(performanceStatistics: Statistics): Promise { try { this.checkDBConnection(); - await this.client.db(this.dbName).collection(Constants.PERFORMANCE_RECORDS_TABLE).insertOne(performanceStatistics); + await this.client + .db(this.dbName) + .collection(Constants.PERFORMANCE_RECORDS_TABLE) + .insertOne(performanceStatistics); } catch (error) { this.handleDBError(StorageType.MONGO_DB, error as Error, Constants.PERFORMANCE_RECORDS_TABLE); } @@ -50,10 +55,18 @@ export class MongoDBStorage extends Storage { private checkDBConnection() { if (!this?.client) { - throw new Error(`${this.logPrefix} ${this.getDBNameFromStorageType(StorageType.MONGO_DB)} client initialization failed while trying to issue a request`); + throw new Error( + `${this.logPrefix} ${this.getDBNameFromStorageType( + StorageType.MONGO_DB + )} client initialization failed while trying to issue a request` + ); } if (!this.connected) { - throw new Error(`${this.logPrefix} ${this.getDBNameFromStorageType(StorageType.MONGO_DB)} connection not opened while trying to issue a request`); + throw new Error( + `${this.logPrefix} ${this.getDBNameFromStorageType( + 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 c4a0ce9c..266d9341 100644 --- a/src/performance/storage/Storage.ts +++ b/src/performance/storage/Storage.ts @@ -19,8 +19,18 @@ export abstract class Storage { this.logPrefix = logPrefix; } - protected handleDBError(type: StorageType, error: Error, table?: string, params: HandleErrorParams = { throwError: false }): void { - logger.error(`${this.logPrefix} ${this.getDBNameFromStorageType(type)} error '${error.message}'${(!Utils.isNullOrUndefined(table) || !table) && ` in table or collection '${table}'`}: %j`, error); + protected handleDBError( + type: StorageType, + error: Error, + table?: string, + params: HandleErrorParams = { throwError: false } + ): void { + logger.error( + `${this.logPrefix} ${this.getDBNameFromStorageType(type)} error '${error.message}'${ + (!Utils.isNullOrUndefined(table) || !table) && ` in table or collection '${table}'` + }: %j`, + error + ); if (params?.throwError) { throw error; } @@ -41,5 +51,7 @@ export abstract class Storage { public abstract open(): void | Promise; public abstract close(): void | Promise; - public abstract storePerformanceStatistics(performanceStatistics: Statistics): void | Promise; + public abstract storePerformanceStatistics( + performanceStatistics: Statistics + ): void | Promise; } diff --git a/src/scripts/deleteChargingStations.js b/src/scripts/deleteChargingStations.js index 3ebeb094..ca5875d3 100755 --- a/src/scripts/deleteChargingStations.js +++ b/src/scripts/deleteChargingStations.js @@ -14,14 +14,18 @@ const config = JSON.parse(fs.readFileSync('scriptConfig.json', 'utf8')); // Mongo Connection and Query if (config && config.mongoConnectionString) { - MongoClient.connect(config.mongoConnectionString, async function(err, client) { + 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 } } + const response = await db + .collection(tenantID + '.chargingstations') + .deleteMany({ _id: { $regex: config.idPattern } }); + console.log( + response.deletedCount, + `Charging Stations with id = %${config.idPattern}% deleted. TenantID =`, + tenantID ); - console.log(response.deletedCount, `Charging Stations with id = %${config.idPattern}% deleted. TenantID =`, tenantID); } client.close(); }); diff --git a/src/scripts/scriptConfig-template.json b/src/scripts/scriptConfig-template.json index f30b37ad..fb911ff3 100644 --- a/src/scripts/scriptConfig-template.json +++ b/src/scripts/scriptConfig-template.json @@ -1,8 +1,6 @@ { "publicFlag": true, - "tenantIDs": [ - "" - ], + "tenantIDs": [""], "idPattern": "", "mongoConnectionString": "mongodb://..." } diff --git a/src/scripts/setCSPublicFlag.js b/src/scripts/setCSPublicFlag.js index 5cd7a646..0b334c1a 100755 --- a/src/scripts/setCSPublicFlag.js +++ b/src/scripts/setCSPublicFlag.js @@ -15,15 +15,18 @@ const config = JSON.parse(fs.readFileSync('scriptConfig.json', 'utf8')); // Mongo Connection and Query if (config && config.mongoConnectionString) { - MongoClient.connect(config.mongoConnectionString, async function(err, client) { + 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 } } + const response = await db + .collection(tenantID + '.chargingstations') + .updateMany({ _id: { $regex: config.idPattern } }, { $set: { public: config.publicFlag } }); + console.log( + response.modifiedCount, + `Charging Stations with id = %${config.idPattern}% updated. TenantID =`, + tenantID ); - console.log(response.modifiedCount, `Charging Stations with id = %${config.idPattern}% updated. TenantID =`, tenantID); } client.close(); }); diff --git a/src/start.ts b/src/start.ts index a3c14ffa..ebad51ef 100644 --- a/src/start.ts +++ b/src/start.ts @@ -3,8 +3,8 @@ import Bootstrap from './charging-station/Bootstrap'; import chalk from 'chalk'; -Bootstrap.getInstance().start().catch( - (error) => { +Bootstrap.getInstance() + .start() + .catch((error) => { console.error(chalk.red(error)); - } -); + }); diff --git a/src/types/ChargingStationTemplate.ts b/src/types/ChargingStationTemplate.ts index 309120b6..eabe60d0 100644 --- a/src/types/ChargingStationTemplate.ts +++ b/src/types/ChargingStationTemplate.ts @@ -19,7 +19,7 @@ export enum Voltage { VOLTAGE_110 = 110, VOLTAGE_230 = 230, VOLTAGE_400 = 400, - VOLTAGE_800 = 800 + VOLTAGE_800 = 800, } export interface AutomaticTransactionGenerator { @@ -31,7 +31,7 @@ export interface AutomaticTransactionGenerator { probabilityOfStart: number; stopAfterHours: number; stopOnConnectionFailure: boolean; - requireAuthorize?: boolean + requireAuthorize?: boolean; } export default interface ChargingStationTemplate { diff --git a/src/types/ChargingStationWorker.ts b/src/types/ChargingStationWorker.ts index 155a353a..f5a0206d 100644 --- a/src/types/ChargingStationWorker.ts +++ b/src/types/ChargingStationWorker.ts @@ -15,17 +15,19 @@ export interface ChargingStationWorkerData extends WorkerData { enum InternalChargingStationWorkerMessageEvents { STARTED = 'started', STOPPED = 'stopped', - PERFORMANCE_STATISTICS = 'performanceStatistics' + PERFORMANCE_STATISTICS = 'performanceStatistics', } -export type ChargingStationWorkerMessageEvents = WorkerMessageEvents | InternalChargingStationWorkerMessageEvents; +export type ChargingStationWorkerMessageEvents = + | WorkerMessageEvents + | InternalChargingStationWorkerMessageEvents; export const ChargingStationWorkerMessageEvents = { ...WorkerMessageEvents, - ...InternalChargingStationWorkerMessageEvents + ...InternalChargingStationWorkerMessageEvents, }; - -export interface ChargingStationWorkerMessage extends Omit, 'id'> { +export interface ChargingStationWorkerMessage + extends Omit, 'id'> { id: ChargingStationWorkerMessageEvents; } diff --git a/src/types/Storage.ts b/src/types/Storage.ts index bad6fc8c..1d674edf 100644 --- a/src/types/Storage.ts +++ b/src/types/Storage.ts @@ -7,13 +7,12 @@ export enum StorageType { 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 ea01b4fc..6d247892 100644 --- a/src/types/UIProtocol.ts +++ b/src/types/UIProtocol.ts @@ -17,4 +17,6 @@ export enum ProtocolCommand { export type ProtocolRequest = [ProtocolCommand, JsonType]; -export type ProtocolRequestHandler = (payload: JsonType) => void | Promise | JsonType | Promise; +export type ProtocolRequestHandler = ( + payload: JsonType +) => void | Promise | JsonType | Promise; diff --git a/src/types/WebSocket.ts b/src/types/WebSocket.ts index 60b2f681..7d028afe 100644 --- a/src/types/WebSocket.ts +++ b/src/types/WebSocket.ts @@ -1,21 +1,22 @@ -export const WebSocketCloseEventStatusString: Record = Object.freeze({ - 1000: 'Normal Closure', - 1001: 'Going Away', - 1002: 'Protocol Error', - 1003: 'Unsupported Frame Data', - 1004: 'Reserved', - 1005: 'No Status Received', - 1006: 'Abnormal Closure', - 1007: 'Invalid Frame Payload Data', - 1008: 'Policy Violation', - 1009: 'Message Too Large', - 1010: 'Missing Extension', - 1011: 'Server Internal Error', - 1012: 'Service Restart', - 1013: 'Try Again Later', - 1014: 'Bad Gateway', - 1015: 'TLS Handshake' -}); +export const WebSocketCloseEventStatusString: Record = + Object.freeze({ + 1000: 'Normal Closure', + 1001: 'Going Away', + 1002: 'Protocol Error', + 1003: 'Unsupported Frame Data', + 1004: 'Reserved', + 1005: 'No Status Received', + 1006: 'Abnormal Closure', + 1007: 'Invalid Frame Payload Data', + 1008: 'Policy Violation', + 1009: 'Message Too Large', + 1010: 'Missing Extension', + 1011: 'Server Internal Error', + 1012: 'Service Restart', + 1013: 'Try Again Later', + 1014: 'Bad Gateway', + 1015: 'TLS Handshake', + }); export enum WebSocketCloseEventStatusCode { CLOSE_NORMAL = 1000, @@ -33,9 +34,9 @@ export enum WebSocketCloseEventStatusCode { CLOSE_SERVICE_RESTART = 1012, CLOSE_TRY_AGAIN_LATER = 1013, CLOSE_BAD_GATEWAY = 1014, - CLOSE_TLS_HANDSHAKE = 1015 + CLOSE_TLS_HANDSHAKE = 1015, } export interface WSError extends Error { - code?: string + code?: string; } diff --git a/src/types/Worker.ts b/src/types/Worker.ts index aca1922e..83d62f3f 100644 --- a/src/types/Worker.ts +++ b/src/types/Worker.ts @@ -5,7 +5,7 @@ import { Worker } from 'worker_threads'; export enum WorkerProcessType { WORKER_SET = 'workerSet', DYNAMIC_POOL = 'dynamicPool', - STATIC_POOL = 'staticPool' + STATIC_POOL = 'staticPool', } export interface WorkerOptions { @@ -32,6 +32,5 @@ export interface WorkerMessage { export enum WorkerMessageEvents { START_WORKER_ELEMENT = 'startWorkerElement', - STOP_WORKER_ELEMENT = 'stopWorkerElement' + STOP_WORKER_ELEMENT = 'stopWorkerElement', } - diff --git a/src/types/ocpp/1.6/ChargePointErrorCode.ts b/src/types/ocpp/1.6/ChargePointErrorCode.ts index db06d0c9..4255cd05 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/Configuration.ts b/src/types/ocpp/1.6/Configuration.ts index 474c691f..8e4f58fe 100644 --- a/src/types/ocpp/1.6/Configuration.ts +++ b/src/types/ocpp/1.6/Configuration.ts @@ -4,7 +4,7 @@ export enum OCPP16SupportedFeatureProfiles { Local_Auth_List_Management = 'LocalAuthListManagement', Reservation = 'Reservation', Smart_Charging = 'SmartCharging', - Remote_Trigger = 'RemoteTrigger' + Remote_Trigger = 'RemoteTrigger', } export enum OCPP16StandardParametersKey { @@ -51,9 +51,9 @@ export enum OCPP16StandardParametersKey { ChargingScheduleAllowedChargingRateUnit = 'ChargingScheduleAllowedChargingRateUnit', ChargingScheduleMaxPeriods = 'ChargingScheduleMaxPeriods', ConnectorSwitch3to1PhaseSupported = 'ConnectorSwitch3to1PhaseSupported', - MaxChargingProfilesInstalled = 'MaxChargingProfilesInstalled' + MaxChargingProfilesInstalled = 'MaxChargingProfilesInstalled', } export enum OCPP16VendorDefaultParametersKey { - ConnectionUrl = 'ConnectionUrl' + ConnectionUrl = 'ConnectionUrl', } diff --git a/src/types/ocpp/1.6/DiagnosticsStatus.ts b/src/types/ocpp/1.6/DiagnosticsStatus.ts index c9feccea..c8c2c890 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 97a7d24c..282df11a 100644 --- a/src/types/ocpp/1.6/MeterValues.ts +++ b/src/types/ocpp/1.6/MeterValues.ts @@ -17,7 +17,7 @@ export enum MeterValueUnit { TEMP_CELSIUS = 'Celsius', TEMP_FAHRENHEIT = 'Fahrenheit', TEMP_KELVIN = 'K', - PERCENT = 'Percent' + PERCENT = 'Percent', } export enum MeterValueContext { @@ -28,7 +28,7 @@ export enum MeterValueContext { 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 MeterValueLocation { @@ -61,7 +61,7 @@ export enum MeterValueLocation { CABLE = 'Cable', EV = 'EV', INLET = 'Inlet', - OUTLET = 'Outlet' + OUTLET = 'Outlet', } export enum OCPP16MeterValuePhase { @@ -74,7 +74,7 @@ export enum OCPP16MeterValuePhase { L3_N = 'L3-N', L1_L2 = 'L1-L2', L2_L3 = 'L2-L3', - L3_L1 = 'L3-L1' + L3_L1 = 'L3-L1', } export enum MeterValueFormat { @@ -104,4 +104,3 @@ export interface MeterValuesRequest extends JsonType { } export type MeterValuesResponse = EmptyObject; - diff --git a/src/types/ocpp/1.6/Requests.ts b/src/types/ocpp/1.6/Requests.ts index 97b5d756..4b5ff45c 100644 --- a/src/types/ocpp/1.6/Requests.ts +++ b/src/types/ocpp/1.6/Requests.ts @@ -16,7 +16,7 @@ export enum OCPP16RequestCommand { START_TRANSACTION = 'StartTransaction', STOP_TRANSACTION = 'StopTransaction', METER_VALUES = 'MeterValues', - DIAGNOSTICS_STATUS_NOTIFICATION = 'DiagnosticsStatusNotification' + DIAGNOSTICS_STATUS_NOTIFICATION = 'DiagnosticsStatusNotification', } export enum OCPP16IncomingRequestCommand { @@ -31,7 +31,7 @@ export enum OCPP16IncomingRequestCommand { REMOTE_START_TRANSACTION = 'RemoteStartTransaction', REMOTE_STOP_TRANSACTION = 'RemoteStopTransaction', GET_DIAGNOSTICS = 'GetDiagnostics', - TRIGGER_MESSAGE = 'TriggerMessage' + TRIGGER_MESSAGE = 'TriggerMessage', } export type HeartbeatRequest = EmptyObject; @@ -83,7 +83,7 @@ export interface GetConfigurationRequest extends JsonType { export enum ResetType { HARD = 'Hard', - SOFT = 'Soft' + SOFT = 'Soft', } export interface ResetRequest extends JsonType { @@ -97,7 +97,7 @@ export interface SetChargingProfileRequest extends JsonType { export enum OCPP16AvailabilityType { INOPERATIVE = 'Inoperative', - OPERATIVE = 'Operative' + OPERATIVE = 'Operative', } export interface ChangeAvailabilityRequest extends JsonType { @@ -121,7 +121,7 @@ export interface GetDiagnosticsRequest extends JsonType { } export interface DiagnosticsStatusNotificationRequest extends JsonType { - status: OCPP16DiagnosticsStatus + status: OCPP16DiagnosticsStatus; } export enum MessageTrigger { @@ -130,10 +130,10 @@ export enum MessageTrigger { FirmwareStatusNotification = 'FirmwareStatusNotification', Heartbeat = 'Heartbeat', MeterValues = 'MeterValues', - StatusNotification = 'StatusNotification' + StatusNotification = 'StatusNotification', } export interface OCPP16TriggerMessageRequest extends JsonType { requestedMessage: MessageTrigger; - connectorId?: number + connectorId?: number; } diff --git a/src/types/ocpp/1.6/Responses.ts b/src/types/ocpp/1.6/Responses.ts index 7d3487ba..d6a5d347 100644 --- a/src/types/ocpp/1.6/Responses.ts +++ b/src/types/ocpp/1.6/Responses.ts @@ -9,7 +9,7 @@ export interface HeartbeatResponse extends JsonType { export enum OCPP16UnlockStatus { UNLOCKED = 'Unlocked', UNLOCK_FAILED = 'UnlockFailed', - NOT_SUPPORTED = 'NotSupported' + NOT_SUPPORTED = 'NotSupported', } export interface UnlockConnectorResponse extends JsonType { @@ -20,7 +20,7 @@ export enum OCPP16ConfigurationStatus { ACCEPTED = 'Accepted', REJECTED = 'Rejected', REBOOT_REQUIRED = 'RebootRequired', - NOT_SUPPORTED = 'NotSupported' + NOT_SUPPORTED = 'NotSupported', } export interface ChangeConfigurationResponse extends JsonType { @@ -30,7 +30,7 @@ export interface ChangeConfigurationResponse extends JsonType { export enum OCPP16RegistrationStatus { ACCEPTED = 'Accepted', PENDING = 'Pending', - REJECTED = 'Rejected' + REJECTED = 'Rejected', } export interface OCPP16BootNotificationResponse extends JsonType { @@ -59,7 +59,7 @@ export interface SetChargingProfileResponse extends JsonType { export enum OCPP16AvailabilityStatus { ACCEPTED = 'Accepted', REJECTED = 'Rejected', - SCHEDULED = 'Scheduled' + SCHEDULED = 'Scheduled', } export interface ChangeAvailabilityResponse extends JsonType { @@ -68,7 +68,7 @@ export interface ChangeAvailabilityResponse extends JsonType { export enum OCPP16ClearChargingProfileStatus { ACCEPTED = 'Accepted', - UNKNOWN = 'Unknown' + UNKNOWN = 'Unknown', } export interface ClearChargingProfileResponse extends JsonType { @@ -84,9 +84,9 @@ export type DiagnosticsStatusNotificationResponse = EmptyObject; export enum OCPP16TriggerMessageStatus { ACCEPTED = 'Accepted', REJECTED = 'Rejected', - NOT_IMPLEMENTED = 'NotImplemented' + NOT_IMPLEMENTED = 'NotImplemented', } export interface OCPP16TriggerMessageResponse extends JsonType { - status: OCPP16TriggerMessageStatus + status: OCPP16TriggerMessageStatus; } diff --git a/src/types/ocpp/1.6/Transaction.ts b/src/types/ocpp/1.6/Transaction.ts index bf237777..1b921d4e 100644 --- a/src/types/ocpp/1.6/Transaction.ts +++ b/src/types/ocpp/1.6/Transaction.ts @@ -13,7 +13,7 @@ export enum OCPP16StopTransactionReason { REMOTE = 'Remote', SOFT_RESET = 'SoftReset', UNLOCK_COMMAND = 'UnlockCommand', - DE_AUTHORIZED = 'DeAuthorized' + DE_AUTHORIZED = 'DeAuthorized', } export enum OCPP16AuthorizationStatus { @@ -21,7 +21,7 @@ export enum OCPP16AuthorizationStatus { BLOCKED = 'Blocked', EXPIRED = 'Expired', INVALID = 'Invalid', - CONCURRENT_TX = 'ConcurrentTx' + CONCURRENT_TX = 'ConcurrentTx', } export interface IdTagInfo extends JsonType { diff --git a/src/types/ocpp/ChargePointErrorCode.ts b/src/types/ocpp/ChargePointErrorCode.ts index 016e51d9..75c5c482 100644 --- a/src/types/ocpp/ChargePointErrorCode.ts +++ b/src/types/ocpp/ChargePointErrorCode.ts @@ -3,5 +3,5 @@ import { OCPP16ChargePointErrorCode } from './1.6/ChargePointErrorCode'; export type ChargePointErrorCode = OCPP16ChargePointErrorCode; export const ChargePointErrorCode = { - ...OCPP16ChargePointErrorCode + ...OCPP16ChargePointErrorCode, }; diff --git a/src/types/ocpp/ChargePointStatus.ts b/src/types/ocpp/ChargePointStatus.ts index daa9779b..c28fd6ef 100644 --- a/src/types/ocpp/ChargePointStatus.ts +++ b/src/types/ocpp/ChargePointStatus.ts @@ -3,5 +3,5 @@ import { OCPP16ChargePointStatus } from './1.6/ChargePointStatus'; export type ChargePointStatus = OCPP16ChargePointStatus; export const ChargePointStatus = { - ...OCPP16ChargePointStatus + ...OCPP16ChargePointStatus, }; diff --git a/src/types/ocpp/Configuration.ts b/src/types/ocpp/Configuration.ts index b13a24ba..5747663c 100644 --- a/src/types/ocpp/Configuration.ts +++ b/src/types/ocpp/Configuration.ts @@ -1,23 +1,27 @@ -import { OCPP16StandardParametersKey, OCPP16SupportedFeatureProfiles, OCPP16VendorDefaultParametersKey } from './1.6/Configuration'; +import { + OCPP16StandardParametersKey, + OCPP16SupportedFeatureProfiles, + OCPP16VendorDefaultParametersKey, +} from './1.6/Configuration'; import { JsonType } from '../JsonType'; export type StandardParametersKey = OCPP16StandardParametersKey; export const StandardParametersKey = { - ...OCPP16StandardParametersKey + ...OCPP16StandardParametersKey, }; export type VendorDefaultParametersKey = OCPP16VendorDefaultParametersKey; export const VendorDefaultParametersKey = { - ...OCPP16VendorDefaultParametersKey + ...OCPP16VendorDefaultParametersKey, }; export type SupportedFeatureProfiles = OCPP16SupportedFeatureProfiles; export const SupportedFeatureProfiles = { - ...OCPP16SupportedFeatureProfiles + ...OCPP16SupportedFeatureProfiles, }; export enum ConnectorPhaseRotation { @@ -28,7 +32,7 @@ export enum ConnectorPhaseRotation { SRT = 'SRT', STR = 'STR', TRS = 'TRS', - TSR = 'TSR' + TSR = 'TSR', } export interface OCPPConfigurationKey extends JsonType { diff --git a/src/types/ocpp/MeterValues.ts b/src/types/ocpp/MeterValues.ts index f2e6e9c0..2172ec1f 100644 --- a/src/types/ocpp/MeterValues.ts +++ b/src/types/ocpp/MeterValues.ts @@ -1,15 +1,20 @@ -import { OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16MeterValuePhase, OCPP16SampledValue } from './1.6/MeterValues'; +import { + OCPP16MeterValue, + OCPP16MeterValueMeasurand, + OCPP16MeterValuePhase, + OCPP16SampledValue, +} from './1.6/MeterValues'; export type MeterValueMeasurand = OCPP16MeterValueMeasurand; export const MeterValueMeasurand = { - ...OCPP16MeterValueMeasurand + ...OCPP16MeterValueMeasurand, }; export type MeterValuePhase = OCPP16MeterValuePhase; export const MeterValuePhase = { - ...OCPP16MeterValuePhase + ...OCPP16MeterValuePhase, }; export type SampledValue = OCPP16SampledValue; diff --git a/src/types/ocpp/Requests.ts b/src/types/ocpp/Requests.ts index 1a0d0ffb..c5c813f2 100644 --- a/src/types/ocpp/Requests.ts +++ b/src/types/ocpp/Requests.ts @@ -1,4 +1,9 @@ -import { OCPP16AvailabilityType, OCPP16BootNotificationRequest, OCPP16IncomingRequestCommand, OCPP16RequestCommand } from './1.6/Requests'; +import { + OCPP16AvailabilityType, + OCPP16BootNotificationRequest, + OCPP16IncomingRequestCommand, + OCPP16RequestCommand, +} from './1.6/Requests'; import { JsonType } from '../JsonType'; import { MessageType } from './MessageType'; @@ -6,8 +11,8 @@ import { OCPP16DiagnosticsStatus } from './1.6/DiagnosticsStatus'; import OCPPError from '../../exception/OCPPError'; export interface SendParams { - skipBufferingOnError?: boolean, - triggerMessage?: boolean + skipBufferingOnError?: boolean; + triggerMessage?: boolean; } export type IncomingRequestHandler = (commandPayload: JsonType) => JsonType | Promise; @@ -19,29 +24,34 @@ export type BootNotificationRequest = OCPP16BootNotificationRequest; export type AvailabilityType = OCPP16AvailabilityType; export const AvailabilityType = { - ...OCPP16AvailabilityType + ...OCPP16AvailabilityType, }; export type RequestCommand = OCPP16RequestCommand; export const RequestCommand = { - ...OCPP16RequestCommand + ...OCPP16RequestCommand, }; export type IncomingRequestCommand = OCPP16IncomingRequestCommand; export const IncomingRequestCommand = { - ...OCPP16IncomingRequestCommand + ...OCPP16IncomingRequestCommand, }; export type DiagnosticsStatus = OCPP16DiagnosticsStatus; export const DiagnosticsStatus = { - ...OCPP16DiagnosticsStatus + ...OCPP16DiagnosticsStatus, }; export type Request = [MessageType, string, RequestCommand, JsonType, JsonType]; export type IncomingRequest = [MessageType, string, IncomingRequestCommand, JsonType, JsonType]; -export type CachedRequest = [(payload: JsonType, requestPayload: JsonType) => void, (error: OCPPError, requestStatistic?: boolean) => void, RequestCommand | IncomingRequestCommand, JsonType | OCPPError]; +export type CachedRequest = [ + (payload: JsonType, requestPayload: JsonType) => void, + (error: OCPPError, requestStatistic?: boolean) => void, + RequestCommand | IncomingRequestCommand, + JsonType | OCPPError +]; diff --git a/src/types/ocpp/Responses.ts b/src/types/ocpp/Responses.ts index 21ddd903..50f1d2b1 100644 --- a/src/types/ocpp/Responses.ts +++ b/src/types/ocpp/Responses.ts @@ -1,14 +1,26 @@ -import { OCPP16AvailabilityStatus, OCPP16BootNotificationResponse, OCPP16ChargingProfileStatus, OCPP16ClearChargingProfileStatus, OCPP16ConfigurationStatus, OCPP16RegistrationStatus, OCPP16TriggerMessageStatus, OCPP16UnlockStatus } from './1.6/Responses'; +import { + OCPP16AvailabilityStatus, + OCPP16BootNotificationResponse, + OCPP16ChargingProfileStatus, + OCPP16ClearChargingProfileStatus, + OCPP16ConfigurationStatus, + OCPP16RegistrationStatus, + OCPP16TriggerMessageStatus, + OCPP16UnlockStatus, +} from './1.6/Responses'; import { JsonType } from '../JsonType'; -export type ResponseHandler = (payload: JsonType | string, requestPayload?: JsonType) => void | Promise; +export type ResponseHandler = ( + payload: JsonType | string, + requestPayload?: JsonType +) => void | Promise; export type BootNotificationResponse = OCPP16BootNotificationResponse; export enum DefaultStatus { ACCEPTED = 'Accepted', - REJECTED = 'Rejected' + REJECTED = 'Rejected', } export interface DefaultResponse { @@ -18,41 +30,41 @@ export interface DefaultResponse { export type RegistrationStatus = OCPP16RegistrationStatus; export const RegistrationStatus = { - ...OCPP16RegistrationStatus + ...OCPP16RegistrationStatus, }; export type AvailabilityStatus = OCPP16AvailabilityStatus; export const AvailabilityStatus = { - ...OCPP16AvailabilityStatus + ...OCPP16AvailabilityStatus, }; export type ChargingProfileStatus = OCPP16ChargingProfileStatus; export const ChargingProfileStatus = { - ...OCPP16ChargingProfileStatus + ...OCPP16ChargingProfileStatus, }; export type ClearChargingProfileStatus = OCPP16ClearChargingProfileStatus; export const ClearChargingProfileStatus = { - ...OCPP16ClearChargingProfileStatus + ...OCPP16ClearChargingProfileStatus, }; export type ConfigurationStatus = OCPP16ConfigurationStatus; export const ConfigurationStatus = { - ...OCPP16ConfigurationStatus + ...OCPP16ConfigurationStatus, }; export type UnlockStatus = OCPP16UnlockStatus; export const UnlockStatus = { - ...OCPP16UnlockStatus + ...OCPP16UnlockStatus, }; export type TriggerMessageStatus = OCPP16TriggerMessageStatus; export const TriggerMessageStatus = { - ...OCPP16TriggerMessageStatus + ...OCPP16TriggerMessageStatus, }; diff --git a/src/types/ocpp/Transaction.ts b/src/types/ocpp/Transaction.ts index 83764a21..cee8f3cf 100644 --- a/src/types/ocpp/Transaction.ts +++ b/src/types/ocpp/Transaction.ts @@ -1,4 +1,10 @@ -import { OCPP16AuthorizationStatus, OCPP16AuthorizeResponse, OCPP16StartTransactionResponse, OCPP16StopTransactionReason, OCPP16StopTransactionResponse } from './1.6/Transaction'; +import { + OCPP16AuthorizationStatus, + OCPP16AuthorizeResponse, + OCPP16StartTransactionResponse, + OCPP16StopTransactionReason, + OCPP16StopTransactionResponse, +} from './1.6/Transaction'; export type AuthorizationStatus = OCPP16AuthorizationStatus; diff --git a/src/types/orm/entities/PerformanceData.ts b/src/types/orm/entities/PerformanceData.ts index a37fa374..2622fa72 100644 --- a/src/types/orm/entities/PerformanceData.ts +++ b/src/types/orm/entities/PerformanceData.ts @@ -6,49 +6,34 @@ import { PerformanceRecord } from './PerformanceRecord'; export class PerformanceData { // @PrimaryKey() // pk!: number; - // @Property() // commandName!: string; - // @Property() // countRequest!: number; - // @Property() // countResponse!: number; - // @Property() // countError!: number; - // @Property() // countTimeMeasurement!: number; - // @Property() // timeMeasurementSeries!: number[]; - // @Property() // currentTimeMeasurement!: number; - // @Property() // minTimeMeasurement!: number; - // @Property() // maxTimeMeasurement!: number; - // @Property() // totalTimeMeasurement!: number; - // @Property() // avgTimeMeasurement!: number; - // @Property() // medTimeMeasurement!: number; - // @Property() // ninetyFiveThPercentileTimeMeasurement!: number; - // @Property() // stdDevTimeMeasurement!: number; - // @ManyToOne('PerformanceRecord') // performanceRecord!: PerformanceRecord; } diff --git a/src/types/orm/entities/PerformanceRecord.ts b/src/types/orm/entities/PerformanceRecord.ts index c0d2ab80..81ee84b7 100644 --- a/src/types/orm/entities/PerformanceRecord.ts +++ b/src/types/orm/entities/PerformanceRecord.ts @@ -6,20 +6,14 @@ import { PerformanceData } from './PerformanceData'; export class PerformanceRecord { // @PrimaryKey() // pk!: number; - // @Property() // id!: string; - // @Property() // URI!: string; - // @Property() // createdAt!: Date; - // @Property() // updatedAt?: Date; - // @OneToMany('PerformanceData', 'performanceRecord') // performanceData?= new Collection(this); } - diff --git a/src/utils/CircularArray.ts b/src/utils/CircularArray.ts index feb5e3e2..0eccfa47 100644 --- a/src/utils/CircularArray.ts +++ b/src/utils/CircularArray.ts @@ -32,9 +32,7 @@ export class CircularArray extends Array { } public concat(...items: (T | ConcatArray)[]): CircularArray { - const concatenatedCircularArray = super.concat( - items as T[] - ) as CircularArray; + const concatenatedCircularArray = super.concat(items as T[]) as CircularArray; concatenatedCircularArray.size = this.size; if (concatenatedCircularArray.length > concatenatedCircularArray.size) { concatenatedCircularArray.splice( diff --git a/src/utils/Configuration.ts b/src/utils/Configuration.ts index 57d17c8f..5770d76e 100644 --- a/src/utils/Configuration.ts +++ b/src/utils/Configuration.ts @@ -1,4 +1,9 @@ -import ConfigurationData, { StationTemplateUrl, StorageConfiguration, SupervisionUrlDistribution, UIWebSocketServerConfiguration } from '../types/ConfigurationData'; +import ConfigurationData, { + StationTemplateUrl, + StorageConfiguration, + SupervisionUrlDistribution, + UIWebSocketServerConfiguration, +} from '../types/ConfigurationData'; import Constants from './Constants'; import { EmptyObject } from '../types/EmptyObject'; @@ -12,7 +17,11 @@ import fs from 'fs'; import path from 'path'; export default class Configuration { - private static configurationFilePath = path.join(path.resolve(__dirname, '../'), 'assets', 'config.json'); + private static configurationFilePath = path.join( + path.resolve(__dirname, '../'), + 'assets', + 'config.json' + ); private static configurationFileWatcher: fs.FSWatcher; private static configuration: ConfigurationData | null = null; private static configurationChangeCallback: () => Promise; @@ -22,61 +31,101 @@ export default class Configuration { } static getLogStatisticsInterval(): number { - Configuration.warnDeprecatedConfigurationKey('statisticsDisplayInterval', null, 'Use \'logStatisticsInterval\' instead'); + Configuration.warnDeprecatedConfigurationKey( + 'statisticsDisplayInterval', + null, + "Use 'logStatisticsInterval' instead" + ); // Read conf - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logStatisticsInterval') ? Configuration.getConfig().logStatisticsInterval : 60; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logStatisticsInterval') + ? Configuration.getConfig().logStatisticsInterval + : 60; } static getUIWebSocketServer(): UIWebSocketServerConfiguration { let options: ServerOptions = { host: Constants.DEFAULT_UI_WEBSOCKET_SERVER_HOST, - port: Constants.DEFAULT_UI_WEBSOCKET_SERVER_PORT + port: Constants.DEFAULT_UI_WEBSOCKET_SERVER_PORT, }; let uiWebSocketServerConfiguration: UIWebSocketServerConfiguration = { enabled: true, - options + options, }; if (Configuration.objectHasOwnProperty(Configuration.getConfig(), 'uiWebSocketServer')) { - if (Configuration.objectHasOwnProperty(Configuration.getConfig().uiWebSocketServer, 'options')) { + if ( + Configuration.objectHasOwnProperty(Configuration.getConfig().uiWebSocketServer, 'options') + ) { options = { ...options, - ...Configuration.objectHasOwnProperty(Configuration.getConfig().uiWebSocketServer.options, 'host') && { host: Configuration.getConfig().uiWebSocketServer.options.host }, - ...Configuration.objectHasOwnProperty(Configuration.getConfig().uiWebSocketServer.options, 'port') && { port: Configuration.getConfig().uiWebSocketServer.options.port } + ...(Configuration.objectHasOwnProperty( + Configuration.getConfig().uiWebSocketServer.options, + 'host' + ) && { host: Configuration.getConfig().uiWebSocketServer.options.host }), + ...(Configuration.objectHasOwnProperty( + Configuration.getConfig().uiWebSocketServer.options, + 'port' + ) && { port: Configuration.getConfig().uiWebSocketServer.options.port }), }; } - uiWebSocketServerConfiguration = - { + uiWebSocketServerConfiguration = { ...uiWebSocketServerConfiguration, - ...Configuration.objectHasOwnProperty(Configuration.getConfig().uiWebSocketServer, 'enabled') && { enabled: Configuration.getConfig().uiWebSocketServer.enabled }, - options + ...(Configuration.objectHasOwnProperty( + Configuration.getConfig().uiWebSocketServer, + 'enabled' + ) && { enabled: Configuration.getConfig().uiWebSocketServer.enabled }), + options, }; } return uiWebSocketServerConfiguration; } static getPerformanceStorage(): StorageConfiguration { - Configuration.warnDeprecatedConfigurationKey('URI', 'performanceStorage', 'Use \'uri\' instead'); + Configuration.warnDeprecatedConfigurationKey('URI', 'performanceStorage', "Use 'uri' instead"); let storageConfiguration: StorageConfiguration = { enabled: false, type: StorageType.JSON_FILE, - uri: this.getDefaultPerformanceStorageUri(StorageType.JSON_FILE) + uri: this.getDefaultPerformanceStorageUri(StorageType.JSON_FILE), }; if (Configuration.objectHasOwnProperty(Configuration.getConfig(), 'performanceStorage')) { - storageConfiguration = - { + storageConfiguration = { ...storageConfiguration, - ...Configuration.objectHasOwnProperty(Configuration.getConfig().performanceStorage, 'enabled') && { enabled: Configuration.getConfig().performanceStorage.enabled }, - ...Configuration.objectHasOwnProperty(Configuration.getConfig().performanceStorage, 'type') && { type: Configuration.getConfig().performanceStorage.type }, - ...Configuration.objectHasOwnProperty(Configuration.getConfig().performanceStorage, 'uri') && { uri: this.getDefaultPerformanceStorageUri(Configuration.getConfig()?.performanceStorage?.type ?? StorageType.JSON_FILE) } + ...(Configuration.objectHasOwnProperty( + Configuration.getConfig().performanceStorage, + 'enabled' + ) && { enabled: Configuration.getConfig().performanceStorage.enabled }), + ...(Configuration.objectHasOwnProperty( + Configuration.getConfig().performanceStorage, + 'type' + ) && { type: Configuration.getConfig().performanceStorage.type }), + ...(Configuration.objectHasOwnProperty( + Configuration.getConfig().performanceStorage, + 'uri' + ) && { + uri: this.getDefaultPerformanceStorageUri( + Configuration.getConfig()?.performanceStorage?.type ?? StorageType.JSON_FILE + ), + }), }; } return storageConfiguration; } static getAutoReconnectMaxRetries(): number { - Configuration.warnDeprecatedConfigurationKey('autoReconnectTimeout', null, 'Use \'ConnectionTimeOut\' OCPP parameter in charging station template instead'); - Configuration.warnDeprecatedConfigurationKey('connectionTimeout', null, 'Use \'ConnectionTimeOut\' OCPP parameter in charging station template instead'); - Configuration.warnDeprecatedConfigurationKey('autoReconnectMaxRetries', null, 'Use it in charging station template instead'); + Configuration.warnDeprecatedConfigurationKey( + 'autoReconnectTimeout', + null, + "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead" + ); + Configuration.warnDeprecatedConfigurationKey( + 'connectionTimeout', + null, + "Use 'ConnectionTimeOut' OCPP parameter in charging station template instead" + ); + Configuration.warnDeprecatedConfigurationKey( + 'autoReconnectMaxRetries', + null, + 'Use it in charging station template instead' + ); // Read conf if (Configuration.objectHasOwnProperty(Configuration.getConfig(), 'autoReconnectMaxRetries')) { return Configuration.getConfig().autoReconnectMaxRetries; @@ -84,11 +133,22 @@ export default class Configuration { } static getStationTemplateUrls(): StationTemplateUrl[] { - Configuration.warnDeprecatedConfigurationKey('stationTemplateURLs', null, 'Use \'stationTemplateUrls\' instead'); - !Configuration.isUndefined(Configuration.getConfig()['stationTemplateURLs']) && (Configuration.getConfig().stationTemplateUrls = Configuration.getConfig()['stationTemplateURLs'] as StationTemplateUrl[]); + Configuration.warnDeprecatedConfigurationKey( + 'stationTemplateURLs', + null, + "Use 'stationTemplateUrls' instead" + ); + !Configuration.isUndefined(Configuration.getConfig()['stationTemplateURLs']) && + (Configuration.getConfig().stationTemplateUrls = Configuration.getConfig()[ + 'stationTemplateURLs' + ] as StationTemplateUrl[]); Configuration.getConfig().stationTemplateUrls.forEach((stationUrl: StationTemplateUrl) => { if (!Configuration.isUndefined(stationUrl['numberOfStation'])) { - console.error(chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key 'numberOfStation' usage for template file '${stationUrl.file}' in 'stationTemplateUrls'. Use 'numberOfStations' instead}`); + console.error( + chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key 'numberOfStation' usage for template file '${ + stationUrl.file + }' in 'stationTemplateUrls'. Use 'numberOfStations' instead}` + ); } }); // Read conf @@ -96,25 +156,43 @@ export default class Configuration { } static getWorkerProcess(): WorkerProcessType { - Configuration.warnDeprecatedConfigurationKey('useWorkerPool;', null, 'Use \'workerProcess\' to define the type of worker process to use instead'); - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'workerProcess') ? Configuration.getConfig().workerProcess : WorkerProcessType.WORKER_SET; + Configuration.warnDeprecatedConfigurationKey( + 'useWorkerPool;', + null, + "Use 'workerProcess' to define the type of worker process to use instead" + ); + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'workerProcess') + ? Configuration.getConfig().workerProcess + : WorkerProcessType.WORKER_SET; } static getWorkerStartDelay(): number { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'workerStartDelay') ? Configuration.getConfig().workerStartDelay : Constants.WORKER_START_DELAY; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'workerStartDelay') + ? Configuration.getConfig().workerStartDelay + : Constants.WORKER_START_DELAY; } static getElementStartDelay(): number { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'elementStartDelay') ? Configuration.getConfig().elementStartDelay : Constants.ELEMENT_START_DELAY; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'elementStartDelay') + ? Configuration.getConfig().elementStartDelay + : Constants.ELEMENT_START_DELAY; } static getWorkerPoolMinSize(): number { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'workerPoolMinSize') ? Configuration.getConfig().workerPoolMinSize : Constants.DEFAULT_WORKER_POOL_MIN_SIZE; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'workerPoolMinSize') + ? Configuration.getConfig().workerPoolMinSize + : Constants.DEFAULT_WORKER_POOL_MIN_SIZE; } static getWorkerPoolMaxSize(): number { - Configuration.warnDeprecatedConfigurationKey('workerPoolSize;', null, 'Use \'workerPoolMaxSize\' instead'); - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'workerPoolMaxSize') ? Configuration.getConfig().workerPoolMaxSize : Constants.DEFAULT_WORKER_POOL_MAX_SIZE; + Configuration.warnDeprecatedConfigurationKey( + 'workerPoolSize;', + null, + "Use 'workerPoolMaxSize' instead" + ); + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'workerPoolMaxSize') + ? Configuration.getConfig().workerPoolMaxSize + : Constants.DEFAULT_WORKER_POOL_MAX_SIZE; } static getWorkerPoolStrategy(): WorkerChoiceStrategy { @@ -122,62 +200,117 @@ export default class Configuration { } static getChargingStationsPerWorker(): number { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'chargingStationsPerWorker') ? Configuration.getConfig().chargingStationsPerWorker : Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER; + return Configuration.objectHasOwnProperty( + Configuration.getConfig(), + 'chargingStationsPerWorker' + ) + ? Configuration.getConfig().chargingStationsPerWorker + : Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER; } static getLogConsole(): boolean { - Configuration.warnDeprecatedConfigurationKey('consoleLog', null, 'Use \'logConsole\' instead'); - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logConsole') ? Configuration.getConfig().logConsole : false; + Configuration.warnDeprecatedConfigurationKey('consoleLog', null, "Use 'logConsole' instead"); + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logConsole') + ? Configuration.getConfig().logConsole + : false; } static getLogFormat(): string { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logFormat') ? Configuration.getConfig().logFormat : 'simple'; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logFormat') + ? Configuration.getConfig().logFormat + : 'simple'; } static getLogRotate(): boolean { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logRotate') ? Configuration.getConfig().logRotate : true; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logRotate') + ? Configuration.getConfig().logRotate + : true; } static getLogMaxFiles(): number { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logMaxFiles') ? Configuration.getConfig().logMaxFiles : 7; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logMaxFiles') + ? Configuration.getConfig().logMaxFiles + : 7; } static getLogLevel(): string { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logLevel') ? Configuration.getConfig().logLevel.toLowerCase() : 'info'; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logLevel') + ? Configuration.getConfig().logLevel.toLowerCase() + : 'info'; } static getLogFile(): string { - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logFile') ? Configuration.getConfig().logFile : 'combined.log'; + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logFile') + ? Configuration.getConfig().logFile + : 'combined.log'; } static getLogErrorFile(): string { - Configuration.warnDeprecatedConfigurationKey('errorFile', null, 'Use \'logErrorFile\' instead'); - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logErrorFile') ? Configuration.getConfig().logErrorFile : 'error.log'; + Configuration.warnDeprecatedConfigurationKey('errorFile', null, "Use 'logErrorFile' instead"); + return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'logErrorFile') + ? Configuration.getConfig().logErrorFile + : 'error.log'; } static getSupervisionUrls(): string | string[] { - Configuration.warnDeprecatedConfigurationKey('supervisionURLs', null, 'Use \'supervisionUrls\' instead'); - !Configuration.isUndefined(Configuration.getConfig()['supervisionURLs']) && (Configuration.getConfig().supervisionUrls = Configuration.getConfig()['supervisionURLs'] as string[]); + Configuration.warnDeprecatedConfigurationKey( + 'supervisionURLs', + null, + "Use 'supervisionUrls' instead" + ); + !Configuration.isUndefined(Configuration.getConfig()['supervisionURLs']) && + (Configuration.getConfig().supervisionUrls = Configuration.getConfig()[ + 'supervisionURLs' + ] as string[]); // Read conf return Configuration.getConfig().supervisionUrls; } static getSupervisionUrlDistribution(): SupervisionUrlDistribution { - Configuration.warnDeprecatedConfigurationKey('distributeStationToTenantEqually', null, 'Use \'supervisionUrlDistribution\' instead'); - Configuration.warnDeprecatedConfigurationKey('distributeStationsToTenantsEqually', null, 'Use \'supervisionUrlDistribution\' instead'); - return Configuration.objectHasOwnProperty(Configuration.getConfig(), 'supervisionUrlDistribution') ? Configuration.getConfig().supervisionUrlDistribution : SupervisionUrlDistribution.ROUND_ROBIN; + Configuration.warnDeprecatedConfigurationKey( + 'distributeStationToTenantEqually', + null, + "Use 'supervisionUrlDistribution' instead" + ); + Configuration.warnDeprecatedConfigurationKey( + 'distributeStationsToTenantsEqually', + null, + "Use 'supervisionUrlDistribution' instead" + ); + return Configuration.objectHasOwnProperty( + Configuration.getConfig(), + 'supervisionUrlDistribution' + ) + ? Configuration.getConfig().supervisionUrlDistribution + : SupervisionUrlDistribution.ROUND_ROBIN; } private static logPrefix(): string { return new Date().toLocaleString() + ' Simulator configuration |'; } - private static warnDeprecatedConfigurationKey(key: string, sectionName?: string, logMsgToAppend = '') { + private static warnDeprecatedConfigurationKey( + key: string, + sectionName?: string, + logMsgToAppend = '' + ) { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - if (sectionName && !Configuration.isUndefined(Configuration.getConfig()[sectionName]) && !Configuration.isUndefined(Configuration.getConfig()[sectionName][key])) { - console.error(chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage in section '${sectionName}'${logMsgToAppend && '. ' + logMsgToAppend}}`); + if ( + sectionName && + !Configuration.isUndefined(Configuration.getConfig()[sectionName]) && + !Configuration.isUndefined(Configuration.getConfig()[sectionName][key]) + ) { + console.error( + chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage in section '${sectionName}'${ + logMsgToAppend && '. ' + logMsgToAppend + }}` + ); } else if (!Configuration.isUndefined(Configuration.getConfig()[key])) { - console.error(chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage${logMsgToAppend && '. ' + logMsgToAppend}}`); + console.error( + chalk`{green ${Configuration.logPrefix()}} {red Deprecated configuration key '${key}' usage${ + logMsgToAppend && '. ' + logMsgToAppend + }}` + ); } } @@ -185,9 +318,16 @@ export default class Configuration { private static getConfig(): ConfigurationData { if (!Configuration.configuration) { try { - Configuration.configuration = JSON.parse(fs.readFileSync(Configuration.configurationFilePath, 'utf8')) as ConfigurationData; + Configuration.configuration = JSON.parse( + fs.readFileSync(Configuration.configurationFilePath, 'utf8') + ) as ConfigurationData; } catch (error) { - Configuration.handleFileException(Configuration.logPrefix(), 'Configuration', Configuration.configurationFilePath, error); + Configuration.handleFileException( + Configuration.logPrefix(), + 'Configuration', + Configuration.configurationFilePath, + error + ); } if (!Configuration.configurationFileWatcher) { Configuration.configurationFileWatcher = Configuration.getConfigurationFileWatcher(); @@ -210,7 +350,12 @@ export default class Configuration { } }); } catch (error) { - Configuration.handleFileException(Configuration.logPrefix(), 'Configuration', Configuration.configurationFilePath, error as Error); + Configuration.handleFileException( + Configuration.logPrefix(), + 'Configuration', + Configuration.configurationFilePath, + error as Error + ); } } @@ -218,7 +363,10 @@ export default class Configuration { const SQLiteFileName = `${Constants.DEFAULT_PERFORMANCE_RECORDS_DB_NAME}.db`; switch (storageType) { case StorageType.JSON_FILE: - return `file://${path.join(path.resolve(__dirname, '../../'), Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME)}`; + return `file://${path.join( + path.resolve(__dirname, '../../'), + Constants.DEFAULT_PERFORMANCE_RECORDS_FILENAME + )}`; case StorageType.SQLITE: return `file://${path.join(path.resolve(__dirname, '../../'), SQLiteFileName)}`; default: @@ -234,17 +382,34 @@ export default class Configuration { return typeof obj === 'undefined'; } - private static handleFileException(logPrefix: string, fileType: string, filePath: string, error: NodeJS.ErrnoException, - params: HandleErrorParams = { throwError: true }): void { + private static handleFileException( + logPrefix: string, + fileType: string, + filePath: string, + error: NodeJS.ErrnoException, + params: HandleErrorParams = { throwError: true } + ): void { const prefix = logPrefix.length !== 0 ? logPrefix + ' ' : ''; if (error.code === 'ENOENT') { - console.error(chalk.green(prefix) + chalk.red(fileType + ' file ' + filePath + ' not found: '), error); + console.error( + chalk.green(prefix) + chalk.red(fileType + ' file ' + filePath + ' not found: '), + error + ); } else if (error.code === 'EEXIST') { - console.error(chalk.green(prefix) + chalk.red(fileType + ' file ' + filePath + ' already exists: '), error); + console.error( + chalk.green(prefix) + chalk.red(fileType + ' file ' + filePath + ' already exists: '), + error + ); } else if (error.code === 'EACCES') { - console.error(chalk.green(prefix) + chalk.red(fileType + ' file ' + filePath + ' access denied: '), error); + console.error( + chalk.green(prefix) + chalk.red(fileType + ' file ' + filePath + ' access denied: '), + error + ); } else { - console.error(chalk.green(prefix) + chalk.red(fileType + ' file ' + filePath + ' error: '), error); + console.error( + chalk.green(prefix) + chalk.red(fileType + ' file ' + filePath + ' error: '), + error + ); } if (params?.throwError) { throw error; diff --git a/src/utils/Constants.ts b/src/utils/Constants.ts index 59a41997..968de2d0 100644 --- a/src/utils/Constants.ts +++ b/src/utils/Constants.ts @@ -1,4 +1,12 @@ -import { AvailabilityStatus, ChargingProfileStatus, ClearChargingProfileStatus, ConfigurationStatus, DefaultStatus, TriggerMessageStatus, UnlockStatus } from '../types/ocpp/Responses'; +import { + AvailabilityStatus, + ChargingProfileStatus, + ClearChargingProfileStatus, + ConfigurationStatus, + DefaultStatus, + TriggerMessageStatus, + UnlockStatus, +} from '../types/ocpp/Responses'; import { MeterValueMeasurand } from '../types/ocpp/MeterValues'; @@ -6,24 +14,58 @@ export default class Constants { static readonly OCPP_RESPONSE_EMPTY = Object.freeze({}); static readonly OCPP_RESPONSE_ACCEPTED = Object.freeze({ status: DefaultStatus.ACCEPTED }); static readonly OCPP_RESPONSE_REJECTED = Object.freeze({ status: DefaultStatus.REJECTED }); - static readonly OCPP_CONFIGURATION_RESPONSE_ACCEPTED = Object.freeze({ status: ConfigurationStatus.ACCEPTED }); - static readonly OCPP_CONFIGURATION_RESPONSE_REJECTED = Object.freeze({ status: ConfigurationStatus.REJECTED }); - static readonly OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED = Object.freeze({ status: ConfigurationStatus.REBOOT_REQUIRED }); - static readonly OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED = Object.freeze({ status: ConfigurationStatus.NOT_SUPPORTED }); - static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED = Object.freeze({ status: ChargingProfileStatus.ACCEPTED }); - static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED = Object.freeze({ status: ChargingProfileStatus.REJECTED }); - static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED = Object.freeze({ status: ChargingProfileStatus.NOT_SUPPORTED }); - static readonly OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED = Object.freeze({ status: ClearChargingProfileStatus.ACCEPTED }); - static readonly OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN = Object.freeze({ status: ClearChargingProfileStatus.UNKNOWN }); + static readonly OCPP_CONFIGURATION_RESPONSE_ACCEPTED = Object.freeze({ + status: ConfigurationStatus.ACCEPTED, + }); + static readonly OCPP_CONFIGURATION_RESPONSE_REJECTED = Object.freeze({ + status: ConfigurationStatus.REJECTED, + }); + static readonly OCPP_CONFIGURATION_RESPONSE_REBOOT_REQUIRED = Object.freeze({ + status: ConfigurationStatus.REBOOT_REQUIRED, + }); + static readonly OCPP_CONFIGURATION_RESPONSE_NOT_SUPPORTED = Object.freeze({ + status: ConfigurationStatus.NOT_SUPPORTED, + }); + static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_ACCEPTED = Object.freeze({ + status: ChargingProfileStatus.ACCEPTED, + }); + static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_REJECTED = Object.freeze({ + status: ChargingProfileStatus.REJECTED, + }); + static readonly OCPP_SET_CHARGING_PROFILE_RESPONSE_NOT_SUPPORTED = Object.freeze({ + status: ChargingProfileStatus.NOT_SUPPORTED, + }); + static readonly OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_ACCEPTED = Object.freeze({ + status: ClearChargingProfileStatus.ACCEPTED, + }); + static readonly OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN = Object.freeze({ + status: ClearChargingProfileStatus.UNKNOWN, + }); static readonly OCPP_RESPONSE_UNLOCKED = Object.freeze({ status: UnlockStatus.UNLOCKED }); - static readonly OCPP_RESPONSE_UNLOCK_FAILED = Object.freeze({ status: UnlockStatus.UNLOCK_FAILED }); - static readonly OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED = Object.freeze({ status: UnlockStatus.NOT_SUPPORTED }); - static readonly OCPP_AVAILABILITY_RESPONSE_ACCEPTED = Object.freeze({ status: AvailabilityStatus.ACCEPTED }); - static readonly OCPP_AVAILABILITY_RESPONSE_REJECTED = Object.freeze({ status: AvailabilityStatus.REJECTED }); - static readonly OCPP_AVAILABILITY_RESPONSE_SCHEDULED = Object.freeze({ status: AvailabilityStatus.SCHEDULED }); - static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED = Object.freeze({ status: TriggerMessageStatus.ACCEPTED }); - static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED = Object.freeze({ status: TriggerMessageStatus.REJECTED }); - static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED = Object.freeze({ status: TriggerMessageStatus.NOT_IMPLEMENTED }); + static readonly OCPP_RESPONSE_UNLOCK_FAILED = Object.freeze({ + status: UnlockStatus.UNLOCK_FAILED, + }); + static readonly OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED = Object.freeze({ + status: UnlockStatus.NOT_SUPPORTED, + }); + static readonly OCPP_AVAILABILITY_RESPONSE_ACCEPTED = Object.freeze({ + status: AvailabilityStatus.ACCEPTED, + }); + static readonly OCPP_AVAILABILITY_RESPONSE_REJECTED = Object.freeze({ + status: AvailabilityStatus.REJECTED, + }); + static readonly OCPP_AVAILABILITY_RESPONSE_SCHEDULED = Object.freeze({ + status: AvailabilityStatus.SCHEDULED, + }); + static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED = Object.freeze({ + status: TriggerMessageStatus.ACCEPTED, + }); + static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED = Object.freeze({ + status: TriggerMessageStatus.REJECTED, + }); + static readonly OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED = Object.freeze({ + status: TriggerMessageStatus.NOT_IMPLEMENTED, + }); static readonly OCPP_DEFAULT_BOOT_NOTIFICATION_INTERVAL = 60000; // Ms static readonly OCPP_WEBSOCKET_TIMEOUT = 60000; // Ms @@ -51,7 +93,7 @@ export default class Constants { MeterValueMeasurand.VOLTAGE, MeterValueMeasurand.POWER_ACTIVE_IMPORT, MeterValueMeasurand.CURRENT_IMPORT, - MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER + MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER, ]); static readonly DEFAULT_FLUCTUATION_PERCENT = 5; diff --git a/src/utils/FileUtils.ts b/src/utils/FileUtils.ts index 014ebbde..0e8efc61 100644 --- a/src/utils/FileUtils.ts +++ b/src/utils/FileUtils.ts @@ -4,30 +4,47 @@ import chalk from 'chalk'; import logger from './Logger'; export default class FileUtils { - static handleFileException(logPrefix: string, fileType: string, filePath: string, error: NodeJS.ErrnoException, - params: HandleErrorParams = { throwError: true, consoleOut: false }): void { + static handleFileException( + logPrefix: string, + fileType: string, + filePath: string, + error: NodeJS.ErrnoException, + params: HandleErrorParams = { throwError: true, consoleOut: false } + ): void { const prefix = logPrefix.length !== 0 ? logPrefix + ' ' : ''; if (error.code === 'ENOENT') { if (params?.consoleOut) { - console.warn(chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' not found: '), error); + console.warn( + chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' not found: '), + error + ); } else { logger.warn(prefix + fileType + ' file ' + filePath + ' not found: %j', error); } } else if (error.code === 'EEXIST') { if (params?.consoleOut) { - console.warn(chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' already exists: '), error); + console.warn( + chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' already exists: '), + error + ); } else { logger.warn(prefix + fileType + ' file ' + filePath + ' already exists: %j', error); } } else if (error.code === 'EACCES') { if (params?.consoleOut) { - console.warn(chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' access denied: '), error); + console.warn( + chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' access denied: '), + error + ); } else { logger.warn(prefix + fileType + ' file ' + filePath + ' access denied: %j', error); } } else { if (params?.consoleOut) { - console.warn(chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' error: '), error); + console.warn( + chalk.green(prefix) + chalk.yellow(fileType + ' file ' + filePath + ' error: '), + error + ); } else { logger.warn(prefix + fileType + ' file ' + filePath + ' error: %j', error); } diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index ccbc63ca..a72bcbe6 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -9,8 +9,23 @@ let transports: transport[]; if (Configuration.getLogRotate()) { const logMaxFiles = Configuration.getLogMaxFiles(); transports = [ - new DailyRotateFile({ filename: Utils.insertAt(Configuration.getLogErrorFile(), '-%DATE%', Configuration.getLogErrorFile().indexOf('.log')), level: 'error', maxFiles: logMaxFiles }), - new DailyRotateFile({ filename: Utils.insertAt(Configuration.getLogFile(), '-%DATE%', Configuration.getLogFile().indexOf('.log')), maxFiles: logMaxFiles }), + new DailyRotateFile({ + filename: Utils.insertAt( + Configuration.getLogErrorFile(), + '-%DATE%', + Configuration.getLogErrorFile().indexOf('.log') + ), + level: 'error', + maxFiles: logMaxFiles, + }), + new DailyRotateFile({ + filename: Utils.insertAt( + Configuration.getLogFile(), + '-%DATE%', + Configuration.getLogFile().indexOf('.log') + ), + maxFiles: logMaxFiles, + }), ]; } else { transports = [ @@ -30,9 +45,11 @@ const logger: Logger = createLogger({ // `${info.level}: ${info.message} JSON.stringify({ ...rest }) ` // if (Configuration.getLogConsole()) { - logger.add(new Console({ - format: format.combine(format.splat(), format[Configuration.getLogFormat()]()), - })); + logger.add( + new Console({ + format: format.combine(format.splat(), format[Configuration.getLogFormat()]()), + }) + ); } export default logger; diff --git a/src/utils/Utils.ts b/src/utils/Utils.ts index fb8aebfe..4fb2ce7b 100644 --- a/src/utils/Utils.ts +++ b/src/utils/Utils.ts @@ -20,8 +20,8 @@ export default class Utils { public static formatDurationMilliSeconds(duration: number): string { duration = Utils.convertToInt(duration); const hours = Math.floor(duration / (3600 * 1000)); - const minutes = Math.floor((duration / 1000 - (hours * 3600)) / 60); - const seconds = duration / 1000 - (hours * 3600) - (minutes * 60); + const minutes = Math.floor((duration / 1000 - hours * 3600) / 60); + const seconds = duration / 1000 - hours * 3600 - minutes * 60; let hoursStr = hours.toString(); let minutesStr = minutes.toString(); let secondsStr = seconds.toString(); @@ -93,7 +93,7 @@ export default class Utils { result = value; } else { // Convert - result = (value === 'true'); + result = value === 'true'; } } return result; @@ -104,7 +104,7 @@ export default class Utils { throw new RangeError('Invalid interval'); } const randomPositiveFloat = crypto.randomBytes(4).readUInt32LE() / 0xffffffff; - const sign = (negative && randomPositiveFloat < 0.5) ? -1 : 1; + const sign = negative && randomPositiveFloat < 0.5 ? -1 : 1; return sign * (randomPositiveFloat * (max - min) + min); } @@ -140,12 +140,20 @@ export default class Utils { return Utils.roundTo(Utils.getRandomFloat(max), scale); } - public static getRandomFloatFluctuatedRounded(staticValue: number, fluctuationPercent: number, scale = 2): number { + public static getRandomFloatFluctuatedRounded( + staticValue: number, + fluctuationPercent: number, + scale = 2 + ): number { if (fluctuationPercent === 0) { return Utils.roundTo(staticValue, scale); } const fluctuationRatio = fluctuationPercent / 100; - return Utils.getRandomFloatRounded(staticValue * (1 + fluctuationRatio), staticValue * (1 - fluctuationRatio), scale); + return Utils.getRandomFloatRounded( + staticValue * (1 + fluctuationRatio), + staticValue * (1 - fluctuationRatio), + scale + ); } public static cloneObject(object: T): T { @@ -189,7 +197,8 @@ export default class Utils { return !Object.keys(obj).length; } - public static insertAt = (str: string, subStr: string, pos: number): string => `${str.slice(0, pos)}${subStr}${str.slice(pos)}`; + public static insertAt = (str: string, subStr: string, pos: number): string => + `${str.slice(0, pos)}${subStr}${str.slice(pos)}`; /** * @param [retryNumber=0] @@ -228,7 +237,9 @@ export default class Utils { } public static workerPoolInUse(): boolean { - return [WorkerProcessType.DYNAMIC_POOL, WorkerProcessType.STATIC_POOL].includes(Configuration.getWorkerProcess()); + return [WorkerProcessType.DYNAMIC_POOL, WorkerProcessType.STATIC_POOL].includes( + Configuration.getWorkerProcess() + ); } public static workerDynamicPoolInUse(): boolean { @@ -236,10 +247,12 @@ export default class Utils { } public static async promiseWithTimeout( - promise: Promise, - timeoutMs: number, - timeoutError: Error, - timeoutCallback: () => void = () => { /* This is intentional */ } + promise: Promise, + timeoutMs: number, + timeoutError: Error, + timeoutCallback: () => void = () => { + /* This is intentional */ + } ): Promise { // Create a timeout promise that rejects in timeout milliseconds const timeoutPromise = new Promise((_, reject) => { diff --git a/src/worker/WorkerAbstract.ts b/src/worker/WorkerAbstract.ts index 12cc7c73..c3e0ed88 100644 --- a/src/worker/WorkerAbstract.ts +++ b/src/worker/WorkerAbstract.ts @@ -14,15 +14,20 @@ export default abstract class WorkerAbstract { * @param workerScript * @param workerOptions */ - constructor(workerScript: string, workerOptions: WorkerOptions = { - workerStartDelay: Constants.WORKER_START_DELAY, - elementStartDelay: Constants.ELEMENT_START_DELAY, - poolMinSize: Constants.DEFAULT_WORKER_POOL_MIN_SIZE, - poolMaxSize: Constants.DEFAULT_WORKER_POOL_MAX_SIZE, - elementsPerWorker: Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER, - poolOptions: {}, - messageHandler: () => { /* This is intentional */ } - }) { + constructor( + workerScript: string, + workerOptions: WorkerOptions = { + workerStartDelay: Constants.WORKER_START_DELAY, + elementStartDelay: Constants.ELEMENT_START_DELAY, + poolMinSize: Constants.DEFAULT_WORKER_POOL_MIN_SIZE, + poolMaxSize: Constants.DEFAULT_WORKER_POOL_MAX_SIZE, + elementsPerWorker: Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER, + poolOptions: {}, + messageHandler: () => { + /* This is intentional */ + }, + } + ) { this.workerScript = workerScript; this.workerOptions = workerOptions; } diff --git a/src/worker/WorkerDynamicPool.ts b/src/worker/WorkerDynamicPool.ts index a263335c..dfa34423 100644 --- a/src/worker/WorkerDynamicPool.ts +++ b/src/worker/WorkerDynamicPool.ts @@ -16,8 +16,14 @@ export default class WorkerDynamicPool extends WorkerAbstract { */ constructor(workerScript: string, workerOptions?: WorkerOptions) { super(workerScript, workerOptions); - this.workerOptions.poolOptions.exitHandler = this.workerOptions?.poolOptions?.exitHandler ?? WorkerUtils.defaultExitHandler; - this.pool = new DynamicThreadPool(this.workerOptions.poolMinSize, this.workerOptions.poolMaxSize, this.workerScript, this.workerOptions.poolOptions); + this.workerOptions.poolOptions.exitHandler = + this.workerOptions?.poolOptions?.exitHandler ?? WorkerUtils.defaultExitHandler; + this.pool = new DynamicThreadPool( + this.workerOptions.poolMinSize, + this.workerOptions.poolMaxSize, + this.workerScript, + this.workerOptions.poolOptions + ); } get size(): number { @@ -55,6 +61,7 @@ export default class WorkerDynamicPool extends WorkerAbstract { public async addElement(elementData: WorkerData): Promise { await this.pool.execute(elementData); // Start element sequentially to optimize memory at startup - this.workerOptions.elementStartDelay > 0 && await Utils.sleep(this.workerOptions.elementStartDelay); + this.workerOptions.elementStartDelay > 0 && + (await Utils.sleep(this.workerOptions.elementStartDelay)); } } diff --git a/src/worker/WorkerFactory.ts b/src/worker/WorkerFactory.ts index debd3f6a..1c4b81c8 100644 --- a/src/worker/WorkerFactory.ts +++ b/src/worker/WorkerFactory.ts @@ -13,28 +13,39 @@ export default class WorkerFactory { // This is intentional } - public static getWorkerImplementation(workerScript: string, workerProcessType: WorkerProcessType, workerOptions?: WorkerOptions): WorkerAbstract | null { + public static getWorkerImplementation( + workerScript: string, + workerProcessType: WorkerProcessType, + workerOptions?: WorkerOptions + ): WorkerAbstract | null { if (!isMainThread) { throw new Error('Trying to get a worker implementation outside the main thread'); } - workerOptions = workerOptions ?? {} as WorkerOptions; - workerOptions.workerStartDelay = workerOptions?.workerStartDelay ?? Constants.WORKER_START_DELAY; - workerOptions.elementStartDelay = workerOptions?.elementStartDelay ?? Constants.ELEMENT_START_DELAY; - workerOptions.poolOptions = workerOptions?.poolOptions ?? {} as PoolOptions; - workerOptions?.messageHandler && (workerOptions.poolOptions.messageHandler = workerOptions.messageHandler); + workerOptions = workerOptions ?? ({} as WorkerOptions); + workerOptions.workerStartDelay = + workerOptions?.workerStartDelay ?? Constants.WORKER_START_DELAY; + workerOptions.elementStartDelay = + workerOptions?.elementStartDelay ?? Constants.ELEMENT_START_DELAY; + workerOptions.poolOptions = workerOptions?.poolOptions ?? ({} as PoolOptions); + workerOptions?.messageHandler && + (workerOptions.poolOptions.messageHandler = workerOptions.messageHandler); let workerImplementation: WorkerAbstract = null; switch (workerProcessType) { case WorkerProcessType.WORKER_SET: - workerOptions.elementsPerWorker = workerOptions?.elementsPerWorker ?? Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER; + workerOptions.elementsPerWorker = + workerOptions?.elementsPerWorker ?? Constants.DEFAULT_CHARGING_STATIONS_PER_WORKER; workerImplementation = new WorkerSet(workerScript, workerOptions); break; case WorkerProcessType.STATIC_POOL: - workerOptions.poolMaxSize = workerOptions?.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; + workerOptions.poolMaxSize = + workerOptions?.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; workerImplementation = new WorkerStaticPool(workerScript, workerOptions); break; case WorkerProcessType.DYNAMIC_POOL: - workerOptions.poolMinSize = workerOptions?.poolMinSize ?? Constants.DEFAULT_WORKER_POOL_MIN_SIZE; - workerOptions.poolMaxSize = workerOptions?.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; + workerOptions.poolMinSize = + workerOptions?.poolMinSize ?? Constants.DEFAULT_WORKER_POOL_MIN_SIZE; + workerOptions.poolMaxSize = + workerOptions?.poolMaxSize ?? Constants.DEFAULT_WORKER_POOL_MAX_SIZE; workerImplementation = new WorkerDynamicPool(workerScript, workerOptions); break; default: diff --git a/src/worker/WorkerSet.ts b/src/worker/WorkerSet.ts index 5bc39918..6e890902 100644 --- a/src/worker/WorkerSet.ts +++ b/src/worker/WorkerSet.ts @@ -20,7 +20,11 @@ export default class WorkerSet extends WorkerAbstract { constructor(workerScript: string, workerOptions?: WorkerOptions) { super(workerScript, workerOptions); this.workerSet = new Set(); - this.messageHandler = workerOptions?.messageHandler ?? (() => { /* This is intentional */ }); + this.messageHandler = + workerOptions?.messageHandler ?? + (() => { + /* This is intentional */ + }); } get size(): number { @@ -39,14 +43,16 @@ export default class WorkerSet extends WorkerAbstract { */ public async addElement(elementData: WorkerData): Promise { if (!this.workerSet) { - throw new Error('Cannot add a WorkerSet element: workers\' set does not exist'); + throw new Error("Cannot add a WorkerSet element: workers' set does not exist"); } - if (this.getLastWorkerSetElement().numberOfWorkerElements >= this.workerOptions.elementsPerWorker) { + if ( + this.getLastWorkerSetElement().numberOfWorkerElements >= this.workerOptions.elementsPerWorker + ) { await this.startWorker(); } this.getLastWorker().postMessage({ id: WorkerMessageEvents.START_WORKER_ELEMENT, - data: elementData + data: elementData, }); this.getLastWorkerSetElement().numberOfWorkerElements++; // Start element sequentially to optimize memory at startup @@ -85,21 +91,28 @@ export default class WorkerSet extends WorkerAbstract { worker.on('message', (msg) => { (async () => { await this.messageHandler(msg); - })().catch(() => { /* This is intentional */ }); + })().catch(() => { + /* This is intentional */ + }); + }); + worker.on('error', () => { + /* This is intentional */ }); - worker.on('error', () => { /* This is intentional */ }); worker.on('exit', (code) => { WorkerUtils.defaultExitHandler(code); this.workerSet.delete(this.getWorkerSetElementByWorker(worker)); }); this.workerSet.add({ worker, numberOfWorkerElements: 0 }); // Start worker sequentially to optimize memory at startup - this.workerOptions.workerStartDelay > 0 && await Utils.sleep(this.workerOptions.workerStartDelay); + this.workerOptions.workerStartDelay > 0 && + (await Utils.sleep(this.workerOptions.workerStartDelay)); } private getLastWorkerSetElement(): WorkerSetElement { let workerSetElement: WorkerSetElement; - for (workerSetElement of this.workerSet) { /* This is intentional */ } + for (workerSetElement of this.workerSet) { + /* This is intentional */ + } return workerSetElement; } diff --git a/src/worker/WorkerStaticPool.ts b/src/worker/WorkerStaticPool.ts index 818fd21a..3eaffff4 100644 --- a/src/worker/WorkerStaticPool.ts +++ b/src/worker/WorkerStaticPool.ts @@ -16,8 +16,13 @@ export default class WorkerStaticPool extends WorkerAbstract { */ constructor(workerScript: string, workerOptions?: WorkerOptions) { super(workerScript, workerOptions); - this.workerOptions.poolOptions.exitHandler = this.workerOptions?.poolOptions?.exitHandler ?? WorkerUtils.defaultExitHandler; - this.pool = new FixedThreadPool(this.workerOptions.poolMaxSize, this.workerScript, this.workerOptions.poolOptions); + this.workerOptions.poolOptions.exitHandler = + this.workerOptions?.poolOptions?.exitHandler ?? WorkerUtils.defaultExitHandler; + this.pool = new FixedThreadPool( + this.workerOptions.poolMaxSize, + this.workerScript, + this.workerOptions.poolOptions + ); } get size(): number { @@ -55,6 +60,7 @@ export default class WorkerStaticPool extends WorkerAbstract { public async addElement(elementData: WorkerData): Promise { await this.pool.execute(elementData); // Start element sequentially to optimize memory at startup - this.workerOptions.elementStartDelay > 0 && await Utils.sleep(this.workerOptions.elementStartDelay); + this.workerOptions.elementStartDelay > 0 && + (await Utils.sleep(this.workerOptions.elementStartDelay)); } } diff --git a/test/robohydra/config.json b/test/robohydra/config.json index 71e532fd..abd684f8 100644 --- a/test/robohydra/config.json +++ b/test/robohydra/config.json @@ -1,9 +1,4 @@ { - "pluginLoadPaths": [ - "test/robohydra/plugins" - ], - "plugins": [ - "logger", - "wsServer" - ] + "pluginLoadPaths": ["test/robohydra/plugins"], + "plugins": ["logger", "wsServer"] } diff --git a/test/robohydra/plugins/wsServer/index.js b/test/robohydra/plugins/wsServer/index.js index d4ab6b8d..485ee19e 100644 --- a/test/robohydra/plugins/wsServer/index.js +++ b/test/robohydra/plugins/wsServer/index.js @@ -2,7 +2,7 @@ const RoboHydraHead = require('robohydra').heads.RoboHydraHead; const RoboHydraWebSocketHead = require('robohydra').heads.RoboHydraWebSocketHead; const RoboHydraWebSocketHeadProxy = require('robohydra').heads.RoboHydraWebSocketHeadProxy; -exports.getBodyParts = function(conf) { +exports.getBodyParts = function (conf) { let wsSocket; return { heads: [ @@ -10,7 +10,7 @@ exports.getBodyParts = function(conf) { name: 'message', path: '/message', method: 'POST', - handler: function(req, res) { + handler: function (req, res) { const msg = JSON.stringify(req.body); if (wsSocket) { wsSocket.send(msg); @@ -18,42 +18,42 @@ exports.getBodyParts = function(conf) { } else { res.send('Cannot send message, no opened websocket found'); } - } + }, }), new RoboHydraHead({ name: 'close', path: '/close', method: 'GET', - handler: function(req, res) { + handler: function (req, res) { if (wsSocket) { wsSocket.close(); res.send('Websocket closed'); } else { res.send('Cannot close websocket, no opened websocket found'); } - } + }, }), new RoboHydraWebSocketHeadProxy({ name: 'proxy', mountPath: '/proxy', proxyTo: 'ws://server.example.com', - preProcessor: function(data) { + preProcessor: function (data) { console.log('From the client: ' + data); }, - postProcessor: function(data) { + postProcessor: function (data) { console.log('From the server: ' + data); - } + }, }), new RoboHydraWebSocketHead({ name: 'WS Server', path: '/.*', - handler: function(req, socket) { + handler: function (req, socket) { wsSocket = socket; - } - }) - ] + }, + }), + ], }; }; diff --git a/test/utils/CircularArrayTest.ts b/test/utils/CircularArrayTest.ts index db1e529e..d17df344 100644 --- a/test/utils/CircularArrayTest.ts +++ b/test/utils/CircularArrayTest.ts @@ -26,10 +26,8 @@ describe('Circular array test suite', () => { expect(circularArray.length).toBe(4); }); - it('Verify that circular array size can\'t be negative at instance creation', () => { - expect(() => new CircularArray(-1)).toThrowError( - new RangeError('Invalid circular array size') - ); + it("Verify that circular array size can't be negative at instance creation", () => { + expect(() => new CircularArray(-1)).toThrowError(new RangeError('Invalid circular array size')); }); it('Verify that circular array empty works as intended', () => { diff --git a/tsconfig.json b/tsconfig.json index ecd539c5..a0c7e162 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,9 +6,7 @@ // "incremental": true, /* Enable incremental compilation */ "target": "es2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ "module": "es2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ - "lib": [ - "es2020" - ], /* Specify library files to be included in the compilation. */ + "lib": ["es2020"], /* Specify library files to be included in the compilation. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ -- 2.34.1