"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"
],
"@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",
"@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",
"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
// "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",
"Symbol",
"symbol"
],
- "linebreak-style": [
- "error",
- "unix"
- ],
+ "linebreak-style": ["error", "unix"],
"max-len": [
"warn",
{
"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",
"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",
"exceptAfterSingleLine": true
}
],
- "quotes": [
- "error",
- "single"
- ]
+ "quotes": ["error", "single"]
},
"overrides": [
{
- "files": [
- "*.js"
- ],
+ "files": ["*.js"],
"extends": "plugin:node/recommended",
"rules": {
"node/shebang": "off",
- 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
- 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
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
# 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'
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
"parallel": true,
"diff": true,
"recursive": true,
- "extension": [
- "ts"
- ],
+ "extension": ["ts"],
"package": "./package.json",
"reporter": "mochawesome",
"reporter-options": "reportDir=outputs/mochawesome-report,json=false",
"ui": "bdd",
"full-trace": true,
"exit": true,
- "require": [
- "ts-node/register",
- "mochawesome/register"
- ]
+ "require": ["ts-node/register", "mochawesome/register"]
}
{
"extends": "@istanbuljs/nyc-config-typescript",
"all": true,
- "include": [
- "src/**/*.ts"
- ]
+ "include": ["src/**/*.ts"]
}
"cwd": "${workspaceFolder}",
"preLaunchTask": "Build",
"runtimeExecutable": "npm",
- "runtimeArgs": [
- "run-script",
- "start:debug"
- ],
- "skipFiles": [
- "<node_internals>/**"
- ],
+ "runtimeArgs": ["run-script", "start:debug"],
+ "skipFiles": ["<node_internals>/**"],
"stopOnEntry": true
},
{
"name": "Debug Simulator Development Build via npm",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "npm",
- "runtimeArgs": [
- "run-script",
- "start:dev:debug"
- ],
- "skipFiles": [
- "<node_internals>/**"
- ],
+ "runtimeArgs": ["run-script", "start:dev:debug"],
+ "skipFiles": ["<node_internals>/**"],
"stopOnEntry": true
}
]
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
### 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
### Windows
-* [Chocolatey](https://chocolatey.org/):
+- [Chocolatey](https://chocolatey.org/):
```powershell
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).
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
"requireAuthorize": true
}
```
+
#### Connectors section
```json
## 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
#### Firmware Management Profile
-- *none*
+- _none_
#### Local Auth List Management Profile
#### Reservation Profile
-- *none*
+- _none_
#### Smart Charging Profile
#### Remote Trigger Profile
-- *none*
+- _none_
## License
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:
{
- "supervisionUrls": [
- "ws://server:8010/OCPP16/5c866e81a2d9593de43efdb4"
- ],
+ "supervisionUrls": ["ws://server:8010/OCPP16/5c866e81a2d9593de43efdb4"],
"distributeStationsToTenantsEqually": true,
"workerProcess": "workerSet",
"workerPoolMinSize": 4,
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
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`
+ )}`,
};
"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",
"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",
"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",
"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",
}
}
},
+ "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",
"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",
"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",
"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",
"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",
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(),
+ ],
};
-[
- "UNDEFINED",
- "BB2TBR09"
-]
+["UNDEFINED", "BB2TBR09"]
{
- "supervisionUrls": [
- "ws://localhost:8010/OCPP16/5be7fb271014d90008992f06"
- ],
+ "supervisionUrls": ["ws://localhost:8010/OCPP16/5be7fb271014d90008992f06"],
"supervisionUrlDistribution": "sequential",
"performanceStorage": {
"enabled": true,
// 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';
import logger from '../utils/Logger';
export default class AutomaticTransactionGenerator {
- private static readonly instances: Map<string, AutomaticTransactionGenerator> = new Map<string, AutomaticTransactionGenerator>();
+ private static readonly instances: Map<string, AutomaticTransactionGenerator> = new Map<
+ string,
+ AutomaticTransactionGenerator
+ >();
public started: boolean;
private readonly chargingStation: ChargingStation;
private readonly connectorsStatus: Map<number, Status>;
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);
}
}
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()) {
private async internalStartConnector(connectorId: number): Promise<void> {
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);
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<StartTransactionResponse | AuthorizeResponse> {
+ private async startTransaction(
+ connectorId: number
+ ): Promise<StartTransactionResponse | AuthorizeResponse> {
const measureId = 'StartTransaction with ATG';
const beginId = PerformanceStatistics.beginMeasure(measureId);
let startResponse: StartTransactionResponse;
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;
}
}
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;
}
return startResponse;
}
- private async stopTransaction(connectorId: number, reason: StopTransactionReason = StopTransactionReason.NONE): Promise<StopTransactionResponse> {
+ private async stopTransaction(
+ connectorId: number,
+ reason: StopTransactionReason = StopTransactionReason.NONE
+ ): Promise<StopTransactionResponse> {
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;
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:');
}
// 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';
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());
}
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) {
}
private initWorkerImplementation(): void {
- this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(this.workerScript, Configuration.getWorkerProcess(),
+ this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(
+ this.workerScript,
+ Configuration.getWorkerProcess(),
{
workerStartDelay: Configuration.getWorkerStartDelay(),
elementStartDelay: Configuration.getElementStartDelay(),
poolMinSize: Configuration.getWorkerPoolMinSize(),
elementsPerWorker: Configuration.getChargingStationsPerWorker(),
poolOptions: {
- workerChoiceStrategy: Configuration.getWorkerPoolStrategy()
+ workerChoiceStrategy: Configuration.getWorkerPoolStrategy(),
},
messageHandler: async (msg: ChargingStationWorkerMessage) => {
if (msg.id === ChargingStationWorkerMessageEvents.STARTED) {
} 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<void> {
+ private async startChargingStation(
+ index: number,
+ stationTemplateUrl: StationTemplateUrl
+ ): Promise<void> {
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++;
// 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';
}
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 {
}
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 {
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;
}
}
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:
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 {
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;
}
}
}
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;
}
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];
}
}
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 {
}
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<void> => {
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`
+ );
}
}
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<void> => {
- await this.ocppRequestService.sendMeterValues(connectorId, this.getConnectorStatus(connectorId).transactionId, interval);
- }, interval);
+ this.getConnectorStatus(connectorId).transactionSetInterval = setInterval(
+ async (): Promise<void> => {
+ 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`
+ );
}
}
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<void> {
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;
}
}
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();
});
}
- 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;
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
+ );
}
}
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);
}
// 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 {
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;
}
}
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);
}
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<ConnectorStatus>(this.stationInfo.Connectors[lastConnector]));
+ if (
+ lastConnectorId === 0 &&
+ this.getUseConnectorId0() &&
+ this.stationInfo.Connectors[lastConnector]
+ ) {
+ this.connectors.set(
+ lastConnectorId,
+ Utils.cloneObject<ConnectorStatus>(this.stationInfo.Connectors[lastConnector])
+ );
this.getConnectorStatus(lastConnectorId).availability = AvailabilityType.OPERATIVE;
if (Utils.isUndefined(this.getConnectorStatus(lastConnectorId)?.chargingProfiles)) {
this.getConnectorStatus(lastConnectorId).chargingProfiles = [];
}
}
// 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<ConnectorStatus>(this.stationInfo.Connectors[randConnectorId]));
+ const randConnectorId = this.stationInfo.randomConnectors
+ ? Utils.getRandomInteger(Utils.convertToInt(lastConnector), 1)
+ : index;
+ this.connectors.set(
+ index,
+ Utils.cloneObject<ConnectorStatus>(this.stationInfo.Connectors[randConnectorId])
+ );
this.getConnectorStatus(index).availability = AvailabilityType.OPERATIVE;
if (Utils.isUndefined(this.getConnectorStatus(index)?.chargingProfiles)) {
this.getConnectorStatus(index).chargingProfiles = [];
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<OCPP16IncomingRequestService>(this);
- this.ocppRequestService = OCPP16RequestService.getInstance<OCPP16RequestService>(this, OCPP16ResponseService.getInstance<OCPP16ResponseService>(this));
+ this.ocppIncomingRequestService =
+ OCPP16IncomingRequestService.getInstance<OCPP16IncomingRequestService>(this);
+ this.ocppRequestService = OCPP16RequestService.getInstance<OCPP16RequestService>(
+ this,
+ OCPP16ResponseService.getInstance<OCPP16ResponseService>(this)
+ );
break;
default:
this.handleUnsupportedVersion(this.getOcppVersion());
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 = [];
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<void> {
- 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();
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;
// 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<void> {
- 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;
// 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) {
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:
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;
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:
}
} 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));
}
}
}
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[] {
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 {
// 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;
}
} 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<void> {
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();
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;
}
}
}
}
- private async stopMessageSequence(reason: StopTransactionReason = StopTransactionReason.NONE): Promise<void> {
+ private async stopMessageSequence(
+ reason: StopTransactionReason = StopTransactionReason.NONE
+ ): Promise<void> {
// 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`
+ );
}
}
}
}
- 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];
}
private getConfiguredSupervisionUrl(): URL {
- const supervisionUrls = Utils.cloneObject<string | string[]>(this.stationInfo.supervisionUrls ?? Configuration.getSupervisionUrls());
+ const supervisionUrls = Utils.cloneObject<string | string[]>(
+ this.stationInfo.supervisionUrls ?? Configuration.getSupervisionUrls()
+ );
if (!Utils.isEmptyArray(supervisionUrls)) {
let urlIndex = 0;
switch (Configuration.getSupervisionUrlDistribution()) {
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;
}
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;
}
}
}
- 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) {
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) {
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) {
}
});
} 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'
+ );
}
}
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();
}
// 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<void> {
// 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()})`
+ );
}
}
this.getConnectorStatus(connectorId).transactionEnergyActiveImportRegisterValue = 0;
}
}
-
// 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';
// Conditionally export ThreadWorker instance for pool usage
export let threadWorker: ThreadWorker;
if (Utils.workerPoolInUse()) {
- threadWorker = new ThreadWorker<ChargingStationWorkerData>(startChargingStation, { maxInactiveTime: Constants.WORKER_POOL_MAX_INACTIVE_TIME, async: false });
+ threadWorker = new ThreadWorker<ChargingStationWorkerData>(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,
+ });
}
}
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) => {
} 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);
// 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';
[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<void> {
+ public async handleRequest(
+ messageId: string,
+ commandName: OCPP16IncomingRequestCommand,
+ commandPayload: JsonType
+ ): Promise<void> {
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
}
} 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);
private handleRequestReset(commandPayload: ResetRequest): DefaultResponse {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
setImmediate(async (): Promise<void> => {
- 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;
}
return Constants.OCPP_RESPONSE_ACCEPTED;
}
- private async handleRequestUnlockConnector(commandPayload: UnlockConnectorRequest): Promise<UnlockConnectorResponse> {
+ private async handleRequestUnlockConnector(
+ commandPayload: UnlockConnectorRequest
+ ): Promise<UnlockConnectorResponse> {
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)) {
};
}
- 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) {
} 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) {
}
}
- 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) {
return Constants.OCPP_CLEAR_CHARGING_PROFILE_RESPONSE_UNKNOWN;
}
- private async handleRequestChangeAvailability(commandPayload: ChangeAvailabilityRequest): Promise<ChangeAvailabilityResponse> {
+ private async handleRequestChangeAvailability(
+ commandPayload: ChangeAvailabilityRequest
+ ): Promise<ChangeAvailabilityResponse> {
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()) {
}
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<DefaultResponse> {
+ private async handleRequestRemoteStartTransaction(
+ commandPayload: RemoteStartTransactionRequest
+ ): Promise<DefaultResponse> {
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<DefaultResponse> {
- 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<DefaultResponse> {
+ 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<DefaultResponse> {
+ private async handleRequestRemoteStopTransaction(
+ commandPayload: RemoteStopTransactionRequest
+ ): Promise<DefaultResponse> {
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<GetDiagnosticsResponse> {
- logger.debug(this.chargingStation.logPrefix() + ' ' + OCPP16IncomingRequestCommand.GET_DIAGNOSTICS + ' request received: %j', commandPayload);
+ private async handleRequestGetDiagnostics(
+ commandPayload: GetDiagnosticsRequest
+ ): Promise<GetDiagnosticsResponse> {
+ 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 }
+ );
}
}
}
// 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';
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<OCPP16BootNotificationResponse> {
+ public async sendBootNotification(
+ chargePointModel: string,
+ chargePointVendor: string,
+ chargeBoxSerialNumber?: string,
+ firmwareVersion?: string,
+ chargePointSerialNumber?: string,
+ iccid?: string,
+ imsi?: string,
+ meterSerialNumber?: string,
+ meterType?: string,
+ params?: SendParams
+ ): Promise<OCPP16BootNotificationResponse> {
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<void> {
+ public async sendStatusNotification(
+ connectorId: number,
+ status: OCPP16ChargePointStatus,
+ errorCode: OCPP16ChargePointErrorCode = OCPP16ChargePointErrorCode.NO_ERROR
+ ): Promise<void> {
const payload: StatusNotificationRequest = {
connectorId,
errorCode,
await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.STATUS_NOTIFICATION);
}
- public async sendAuthorize(connectorId: number, idTag?: string): Promise<OCPP16AuthorizeResponse> {
+ public async sendAuthorize(
+ connectorId: number,
+ idTag?: string
+ ): Promise<OCPP16AuthorizeResponse> {
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<OCPP16StartTransactionResponse> {
+ public async sendStartTransaction(
+ connectorId: number,
+ idTag?: string
+ ): Promise<OCPP16StartTransactionResponse> {
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<OCPP16StopTransactionResponse> {
+ public async sendStopTransaction(
+ transactionId: number,
+ meterStop: number,
+ idTag?: string,
+ reason: OCPP16StopTransactionReason = OCPP16StopTransactionReason.NONE
+ ): Promise<OCPP16StopTransactionResponse> {
let connectorId: number;
for (const id of this.chargingStation.connectors.keys()) {
if (id > 0 && this.chargingStation.getConnectorStatus(id)?.transactionId === transactionId) {
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<void> {
+ public async sendMeterValues(
+ connectorId: number,
+ transactionId: number,
+ interval: number,
+ debug = false
+ ): Promise<void> {
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 = {
await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES);
}
- public async sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: OCPP16MeterValue): Promise<void> {
+ public async sendTransactionBeginMeterValues(
+ connectorId: number,
+ transactionId: number,
+ beginMeterValue: OCPP16MeterValue
+ ): Promise<void> {
const payload: MeterValuesRequest = {
connectorId,
transactionId,
await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES);
}
- public async sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: OCPP16MeterValue): Promise<void> {
+ public async sendTransactionEndMeterValues(
+ connectorId: number,
+ transactionId: number,
+ endMeterValue: OCPP16MeterValue
+ ): Promise<void> {
const payload: MeterValuesRequest = {
connectorId,
transactionId,
await this.sendMessage(Utils.generateUUID(), payload, OCPP16RequestCommand.METER_VALUES);
}
- public async sendDiagnosticsStatusNotification(diagnosticsStatus: OCPP16DiagnosticsStatus): Promise<void> {
+ public async sendDiagnosticsStatusNotification(
+ diagnosticsStatus: OCPP16DiagnosticsStatus
+ ): Promise<void> {
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
+ );
}
}
// 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';
[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<void> {
- if (this.chargingStation.isRegistered() || commandName === OCPP16RequestCommand.BOOT_NOTIFICATION) {
+ public async handleResponse(
+ commandName: OCPP16RequestCommand,
+ payload: JsonType | string,
+ requestPayload: JsonType
+ ): Promise<void> {
+ 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<void> {
+ private async handleResponseStartTransaction(
+ payload: OCPP16StartTransactionResponse,
+ requestPayload: StartTransactionRequest
+ ): Promise<void> {
const connectorId = requestPayload.connectorId;
let transactionConnectorId: number;
}
}
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);
}
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<void> {
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<void> {
+ private async handleResponseStopTransaction(
+ payload: OCPP16StopTransactionResponse,
+ requestPayload: StopTransactionRequest
+ ): Promise<void> {
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
+ );
}
}
// 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';
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:
}
}
- 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: [],
// 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: [],
// 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);
import logger from '../../utils/Logger';
export default abstract class OCPPIncomingRequestService {
- private static readonly instances: Map<string, OCPPIncomingRequestService> = new Map<string, OCPPIncomingRequestService>();
+ private static readonly instances: Map<string, OCPPIncomingRequestService> = new Map<
+ string,
+ OCPPIncomingRequestService
+ >();
protected chargingStation: ChargingStation;
protected constructor(chargingStation: ChargingStation) {
this.chargingStation = chargingStation;
}
- public static getInstance<T extends OCPPIncomingRequestService>(this: new (chargingStation: ChargingStation) => T, chargingStation: ChargingStation): T {
+ public static getInstance<T extends OCPPIncomingRequestService>(
+ 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<T>(commandName: IncomingRequestCommand, error: Error, params: HandleErrorParams<T> = { throwError: true }): T {
- logger.error(this.chargingStation.logPrefix() + ' Incoming request command %s error: %j', commandName, error);
+ protected handleIncomingRequestError<T>(
+ commandName: IncomingRequestCommand,
+ error: Error,
+ params: HandleErrorParams<T> = { throwError: true }
+ ): T {
+ logger.error(
+ this.chargingStation.logPrefix() + ' Incoming request command %s error: %j',
+ commandName,
+ error
+ );
if (!params?.throwError && params?.errorResponse) {
return params?.errorResponse;
}
}
}
- public abstract handleRequest(messageId: string, commandName: IncomingRequestCommand, commandPayload: JsonType): Promise<void>;
+ public abstract handleRequest(
+ messageId: string,
+ commandName: IncomingRequestCommand,
+ commandPayload: JsonType
+ ): Promise<void>;
}
-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';
import logger from '../../utils/Logger';
export default abstract class OCPPRequestService {
- private static readonly instances: Map<string, OCPPRequestService> = new Map<string, OCPPRequestService>();
+ private static readonly instances: Map<string, OCPPRequestService> = 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<T extends OCPPRequestService>(this: new (chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) => T, chargingStation: ChargingStation, ocppResponseService: OCPPResponseService): T {
+ public static getInstance<T extends OCPPRequestService>(
+ 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<ResponseType> {
+ public async sendResult(
+ messageId: string,
+ messagePayload: JsonType,
+ commandName: IncomingRequestCommand
+ ): Promise<ResponseType> {
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<ResponseType> {
+ public async sendError(
+ messageId: string,
+ ocppError: OCPPError,
+ commandName: IncomingRequestCommand
+ ): Promise<ResponseType> {
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<ResponseType> {
+ protected async sendMessage(
+ messageId: string,
+ messagePayload: JsonType,
+ commandName: RequestCommand,
+ params: SendParams = {
+ skipBufferingOnError: false,
+ triggerMessage: false,
+ }
+ ): Promise<ResponseType> {
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<ResponseType> {
- 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<ResponseType> {
+ 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<void> {
- 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<void> {
+ 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<void>,
- 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<void>,
+ 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
// 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<EmptyObject> = { throwError: true }): void {
- logger.error(this.chargingStation.logPrefix() + ' Request command %s error: %j', commandName, error);
+ private handleRequestError(
+ commandName: RequestCommand | IncomingRequestCommand,
+ error: Error,
+ params: HandleErrorParams<EmptyObject> = { 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<void>;
- public abstract sendBootNotification(chargePointModel: string, chargePointVendor: string, chargeBoxSerialNumber?: string, firmwareVersion?: string, chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string, params?: SendParams): Promise<BootNotificationResponse>;
- public abstract sendStatusNotification(connectorId: number, status: ChargePointStatus, errorCode?: ChargePointErrorCode): Promise<void>;
+ public abstract sendBootNotification(
+ chargePointModel: string,
+ chargePointVendor: string,
+ chargeBoxSerialNumber?: string,
+ firmwareVersion?: string,
+ chargePointSerialNumber?: string,
+ iccid?: string,
+ imsi?: string,
+ meterSerialNumber?: string,
+ meterType?: string,
+ params?: SendParams
+ ): Promise<BootNotificationResponse>;
+ public abstract sendStatusNotification(
+ connectorId: number,
+ status: ChargePointStatus,
+ errorCode?: ChargePointErrorCode
+ ): Promise<void>;
public abstract sendAuthorize(connectorId: number, idTag?: string): Promise<AuthorizeResponse>;
- public abstract sendStartTransaction(connectorId: number, idTag?: string): Promise<StartTransactionResponse>;
- public abstract sendStopTransaction(transactionId: number, meterStop: number, idTag?: string, reason?: StopTransactionReason): Promise<StopTransactionResponse>;
- public abstract sendMeterValues(connectorId: number, transactionId: number, interval: number): Promise<void>;
- public abstract sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: MeterValue): Promise<void>;
- public abstract sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: MeterValue): Promise<void>;
- public abstract sendDiagnosticsStatusNotification(diagnosticsStatus: DiagnosticsStatus): Promise<void>;
+ public abstract sendStartTransaction(
+ connectorId: number,
+ idTag?: string
+ ): Promise<StartTransactionResponse>;
+ public abstract sendStopTransaction(
+ transactionId: number,
+ meterStop: number,
+ idTag?: string,
+ reason?: StopTransactionReason
+ ): Promise<StopTransactionResponse>;
+ public abstract sendMeterValues(
+ connectorId: number,
+ transactionId: number,
+ interval: number
+ ): Promise<void>;
+ public abstract sendTransactionBeginMeterValues(
+ connectorId: number,
+ transactionId: number,
+ beginMeterValue: MeterValue
+ ): Promise<void>;
+ public abstract sendTransactionEndMeterValues(
+ connectorId: number,
+ transactionId: number,
+ endMeterValue: MeterValue
+ ): Promise<void>;
+ public abstract sendDiagnosticsStatusNotification(
+ diagnosticsStatus: DiagnosticsStatus
+ ): Promise<void>;
}
import { RequestCommand } from '../../types/ocpp/Requests';
export default abstract class OCPPResponseService {
- private static readonly instances: Map<string, OCPPResponseService> = new Map<string, OCPPResponseService>();
+ private static readonly instances: Map<string, OCPPResponseService> = new Map<
+ string,
+ OCPPResponseService
+ >();
protected readonly chargingStation: ChargingStation;
protected constructor(chargingStation: ChargingStation) {
this.chargingStation = chargingStation;
}
- public static getInstance<T extends OCPPResponseService>(this: new (chargingStation: ChargingStation) => T, chargingStation: ChargingStation): T {
+ public static getInstance<T extends OCPPResponseService>(
+ 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<void>;
+ public abstract handleResponse(
+ commandName: RequestCommand,
+ payload: JsonType | string,
+ requestPayload: JsonType
+ ): Promise<void>;
}
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);
}
} 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]);
}
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 {}
}
// 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);
import logger from '../../utils/Logger';
export class UIServiceUtils {
- public static handleProtocols = (protocols: Set<string>, request: IncomingMessage): string | false => {
+ public static handleProtocols = (
+ protocols: Set<string>,
+ 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;
};
}
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 {
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;
import { parentPort } from 'worker_threads';
export default class PerformanceStatistics {
- private static readonly instances: Map<string, PerformanceStatistics> = new Map<string, PerformanceStatistics>();
+ private static readonly instances: Map<string, PerformanceStatistics> = new Map<
+ string,
+ PerformanceStatistics
+ >();
private readonly objId: string;
private readonly objName: string;
private performanceObserver: PerformanceObserver;
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<string, Partial<StatisticsData>>() };
+ 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<string, Partial<StatisticsData>>(),
+ };
}
public static getInstance(objId: string, objName: string, uri: URL): 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:
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}`
+ );
}
}
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'] });
}
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'
+ );
}
}
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
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;
}
}
// 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<TimeSeries>(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<TimeSeries>(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,
+ });
}
}
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 {
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
+ );
}
}
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`
+ );
}
}
}
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<IDatabaseDriver<Connection>> | Options<IDatabaseDriver<Connection>> {
+ private getOptions():
+ | Configuration<IDatabaseDriver<Connection>>
+ | Options<IDatabaseDriver<Connection>> {
return {
metadataProvider: TsMorphMetadataProvider,
entities: [PerformanceRecord, PerformanceData],
type: this.storageType as MikroORMDBType,
- clientUrl: this.getClientUrl()
+ clientUrl: this.getClientUrl(),
};
}
}
}
}
-
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<void> {
try {
this.checkDBConnection();
- await this.client.db(this.dbName).collection<Statistics>(Constants.PERFORMANCE_RECORDS_TABLE).insertOne(performanceStatistics);
+ await this.client
+ .db(this.dbName)
+ .collection<Statistics>(Constants.PERFORMANCE_RECORDS_TABLE)
+ .insertOne(performanceStatistics);
} catch (error) {
this.handleDBError(StorageType.MONGO_DB, error as Error, Constants.PERFORMANCE_RECORDS_TABLE);
}
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`
+ );
}
}
}
this.logPrefix = logPrefix;
}
- protected handleDBError(type: StorageType, error: Error, table?: string, params: HandleErrorParams<EmptyObject> = { 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<EmptyObject> = { 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;
}
public abstract open(): void | Promise<void>;
public abstract close(): void | Promise<void>;
- public abstract storePerformanceStatistics(performanceStatistics: Statistics): void | Promise<void>;
+ public abstract storePerformanceStatistics(
+ performanceStatistics: Statistics
+ ): void | Promise<void>;
}
// 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();
});
{
"publicFlag": true,
- "tenantIDs": [
- ""
- ],
+ "tenantIDs": [""],
"idPattern": "",
"mongoConnectionString": "mongodb://..."
}
// 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();
});
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));
- }
-);
+ });
VOLTAGE_110 = 110,
VOLTAGE_230 = 230,
VOLTAGE_400 = 400,
- VOLTAGE_800 = 800
+ VOLTAGE_800 = 800,
}
export interface AutomaticTransactionGenerator {
probabilityOfStart: number;
stopAfterHours: number;
stopOnConnectionFailure: boolean;
- requireAuthorize?: boolean
+ requireAuthorize?: boolean;
}
export default interface ChargingStationTemplate {
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<WorkerMessage<ChargingStationWorkerData>, 'id'> {
+export interface ChargingStationWorkerMessage
+ extends Omit<WorkerMessage<ChargingStationWorkerData>, 'id'> {
id: ChargingStationWorkerMessageEvents;
}
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',
}
-
export type ProtocolRequest = [ProtocolCommand, JsonType];
-export type ProtocolRequestHandler = (payload: JsonType) => void | Promise<void> | JsonType | Promise<JsonType>;
+export type ProtocolRequestHandler = (
+ payload: JsonType
+) => void | Promise<void> | JsonType | Promise<JsonType>;
-export const WebSocketCloseEventStatusString: Record<WebSocketCloseEventStatusCode, string> = 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<WebSocketCloseEventStatusCode, string> =
+ 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,
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;
}
export enum WorkerProcessType {
WORKER_SET = 'workerSet',
DYNAMIC_POOL = 'dynamicPool',
- STATIC_POOL = 'staticPool'
+ STATIC_POOL = 'staticPool',
}
export interface WorkerOptions {
export enum WorkerMessageEvents {
START_WORKER_ELEMENT = 'startWorkerElement',
- STOP_WORKER_ELEMENT = 'stopWorkerElement'
+ STOP_WORKER_ELEMENT = 'stopWorkerElement',
}
-
READER_FAILURE = 'ReaderFailure',
RESET_FAILURE = 'ResetFailure',
UNDER_VOLTAGE = 'UnderVoltage',
- WEAK_SIGNAL = 'WeakSignal'
+ WEAK_SIGNAL = 'WeakSignal',
}
Local_Auth_List_Management = 'LocalAuthListManagement',
Reservation = 'Reservation',
Smart_Charging = 'SmartCharging',
- Remote_Trigger = 'RemoteTrigger'
+ Remote_Trigger = 'RemoteTrigger',
}
export enum OCPP16StandardParametersKey {
ChargingScheduleAllowedChargingRateUnit = 'ChargingScheduleAllowedChargingRateUnit',
ChargingScheduleMaxPeriods = 'ChargingScheduleMaxPeriods',
ConnectorSwitch3to1PhaseSupported = 'ConnectorSwitch3to1PhaseSupported',
- MaxChargingProfilesInstalled = 'MaxChargingProfilesInstalled'
+ MaxChargingProfilesInstalled = 'MaxChargingProfilesInstalled',
}
export enum OCPP16VendorDefaultParametersKey {
- ConnectionUrl = 'ConnectionUrl'
+ ConnectionUrl = 'ConnectionUrl',
}
Idle = 'Idle',
Uploaded = 'Uploaded',
UploadFailed = 'UploadFailed',
- Uploading = 'Uploading'
+ Uploading = 'Uploading',
}
TEMP_CELSIUS = 'Celsius',
TEMP_FAHRENHEIT = 'Fahrenheit',
TEMP_KELVIN = 'K',
- PERCENT = 'Percent'
+ PERCENT = 'Percent',
}
export enum MeterValueContext {
SAMPLE_PERIODIC = 'Sample.Periodic',
TRANSACTION_BEGIN = 'Transaction.Begin',
TRANSACTION_END = 'Transaction.End',
- TRIGGER = 'Trigger'
+ TRIGGER = 'Trigger',
}
export enum OCPP16MeterValueMeasurand {
FAN_RPM = 'RPM',
STATE_OF_CHARGE = 'SoC',
TEMPERATURE = 'Temperature',
- VOLTAGE = 'Voltage'
+ VOLTAGE = 'Voltage',
}
export enum MeterValueLocation {
CABLE = 'Cable',
EV = 'EV',
INLET = 'Inlet',
- OUTLET = 'Outlet'
+ OUTLET = 'Outlet',
}
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 {
}
export type MeterValuesResponse = EmptyObject;
-
START_TRANSACTION = 'StartTransaction',
STOP_TRANSACTION = 'StopTransaction',
METER_VALUES = 'MeterValues',
- DIAGNOSTICS_STATUS_NOTIFICATION = 'DiagnosticsStatusNotification'
+ DIAGNOSTICS_STATUS_NOTIFICATION = 'DiagnosticsStatusNotification',
}
export enum OCPP16IncomingRequestCommand {
REMOTE_START_TRANSACTION = 'RemoteStartTransaction',
REMOTE_STOP_TRANSACTION = 'RemoteStopTransaction',
GET_DIAGNOSTICS = 'GetDiagnostics',
- TRIGGER_MESSAGE = 'TriggerMessage'
+ TRIGGER_MESSAGE = 'TriggerMessage',
}
export type HeartbeatRequest = EmptyObject;
export enum ResetType {
HARD = 'Hard',
- SOFT = 'Soft'
+ SOFT = 'Soft',
}
export interface ResetRequest extends JsonType {
export enum OCPP16AvailabilityType {
INOPERATIVE = 'Inoperative',
- OPERATIVE = 'Operative'
+ OPERATIVE = 'Operative',
}
export interface ChangeAvailabilityRequest extends JsonType {
}
export interface DiagnosticsStatusNotificationRequest extends JsonType {
- status: OCPP16DiagnosticsStatus
+ status: OCPP16DiagnosticsStatus;
}
export enum MessageTrigger {
FirmwareStatusNotification = 'FirmwareStatusNotification',
Heartbeat = 'Heartbeat',
MeterValues = 'MeterValues',
- StatusNotification = 'StatusNotification'
+ StatusNotification = 'StatusNotification',
}
export interface OCPP16TriggerMessageRequest extends JsonType {
requestedMessage: MessageTrigger;
- connectorId?: number
+ connectorId?: number;
}
export enum OCPP16UnlockStatus {
UNLOCKED = 'Unlocked',
UNLOCK_FAILED = 'UnlockFailed',
- NOT_SUPPORTED = 'NotSupported'
+ NOT_SUPPORTED = 'NotSupported',
}
export interface UnlockConnectorResponse extends JsonType {
ACCEPTED = 'Accepted',
REJECTED = 'Rejected',
REBOOT_REQUIRED = 'RebootRequired',
- NOT_SUPPORTED = 'NotSupported'
+ NOT_SUPPORTED = 'NotSupported',
}
export interface ChangeConfigurationResponse extends JsonType {
export enum OCPP16RegistrationStatus {
ACCEPTED = 'Accepted',
PENDING = 'Pending',
- REJECTED = 'Rejected'
+ REJECTED = 'Rejected',
}
export interface OCPP16BootNotificationResponse extends JsonType {
export enum OCPP16AvailabilityStatus {
ACCEPTED = 'Accepted',
REJECTED = 'Rejected',
- SCHEDULED = 'Scheduled'
+ SCHEDULED = 'Scheduled',
}
export interface ChangeAvailabilityResponse extends JsonType {
export enum OCPP16ClearChargingProfileStatus {
ACCEPTED = 'Accepted',
- UNKNOWN = 'Unknown'
+ UNKNOWN = 'Unknown',
}
export interface ClearChargingProfileResponse extends JsonType {
export enum OCPP16TriggerMessageStatus {
ACCEPTED = 'Accepted',
REJECTED = 'Rejected',
- NOT_IMPLEMENTED = 'NotImplemented'
+ NOT_IMPLEMENTED = 'NotImplemented',
}
export interface OCPP16TriggerMessageResponse extends JsonType {
- status: OCPP16TriggerMessageStatus
+ status: OCPP16TriggerMessageStatus;
}
REMOTE = 'Remote',
SOFT_RESET = 'SoftReset',
UNLOCK_COMMAND = 'UnlockCommand',
- DE_AUTHORIZED = 'DeAuthorized'
+ DE_AUTHORIZED = 'DeAuthorized',
}
export enum OCPP16AuthorizationStatus {
BLOCKED = 'Blocked',
EXPIRED = 'Expired',
INVALID = 'Invalid',
- CONCURRENT_TX = 'ConcurrentTx'
+ CONCURRENT_TX = 'ConcurrentTx',
}
export interface IdTagInfo extends JsonType {
export type ChargePointErrorCode = OCPP16ChargePointErrorCode;
export const ChargePointErrorCode = {
- ...OCPP16ChargePointErrorCode
+ ...OCPP16ChargePointErrorCode,
};
export type ChargePointStatus = OCPP16ChargePointStatus;
export const ChargePointStatus = {
- ...OCPP16ChargePointStatus
+ ...OCPP16ChargePointStatus,
};
-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 {
SRT = 'SRT',
STR = 'STR',
TRS = 'TRS',
- TSR = 'TSR'
+ TSR = 'TSR',
}
export interface OCPPConfigurationKey extends JsonType {
-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;
-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';
import OCPPError from '../../exception/OCPPError';
export interface SendParams {
- skipBufferingOnError?: boolean,
- triggerMessage?: boolean
+ skipBufferingOnError?: boolean;
+ triggerMessage?: boolean;
}
export type IncomingRequestHandler = (commandPayload: JsonType) => JsonType | Promise<JsonType>;
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
+];
-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<void>;
+export type ResponseHandler = (
+ payload: JsonType | string,
+ requestPayload?: JsonType
+) => void | Promise<void>;
export type BootNotificationResponse = OCPP16BootNotificationResponse;
export enum DefaultStatus {
ACCEPTED = 'Accepted',
- REJECTED = 'Rejected'
+ REJECTED = 'Rejected',
}
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,
};
-import { OCPP16AuthorizationStatus, OCPP16AuthorizeResponse, OCPP16StartTransactionResponse, OCPP16StopTransactionReason, OCPP16StopTransactionResponse } from './1.6/Transaction';
+import {
+ OCPP16AuthorizationStatus,
+ OCPP16AuthorizeResponse,
+ OCPP16StartTransactionResponse,
+ OCPP16StopTransactionReason,
+ OCPP16StopTransactionResponse,
+} from './1.6/Transaction';
export type AuthorizationStatus = OCPP16AuthorizationStatus;
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;
}
export class PerformanceRecord {
// @PrimaryKey()
// pk!: number;
-
// @Property()
// id!: string;
-
// @Property()
// URI!: string;
-
// @Property()
// createdAt!: Date;
-
// @Property()
// updatedAt?: Date;
-
// @OneToMany('PerformanceData', 'performanceRecord')
// performanceData?= new Collection<PerformanceData>(this);
}
-
}
public concat(...items: (T | ConcatArray<T>)[]): CircularArray<T> {
- const concatenatedCircularArray = super.concat(
- items as T[]
- ) as CircularArray<T>;
+ const concatenatedCircularArray = super.concat(items as T[]) as CircularArray<T>;
concatenatedCircularArray.size = this.size;
if (concatenatedCircularArray.length > concatenatedCircularArray.size) {
concatenatedCircularArray.splice(
-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';
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<void>;
}
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;
}
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
}
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 {
}
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
+ }}`
+ );
}
}
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();
}
});
} catch (error) {
- Configuration.handleFileException(Configuration.logPrefix(), 'Configuration', Configuration.configurationFilePath, error as Error);
+ Configuration.handleFileException(
+ Configuration.logPrefix(),
+ 'Configuration',
+ Configuration.configurationFilePath,
+ error as Error
+ );
}
}
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:
return typeof obj === 'undefined';
}
- private static handleFileException(logPrefix: string, fileType: string, filePath: string, error: NodeJS.ErrnoException,
- params: HandleErrorParams<EmptyObject> = { throwError: true }): void {
+ private static handleFileException(
+ logPrefix: string,
+ fileType: string,
+ filePath: string,
+ error: NodeJS.ErrnoException,
+ params: HandleErrorParams<EmptyObject> = { 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;
-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';
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
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;
import logger from './Logger';
export default class FileUtils {
- static handleFileException(logPrefix: string, fileType: string, filePath: string, error: NodeJS.ErrnoException,
- params: HandleErrorParams<EmptyObject> = { throwError: true, consoleOut: false }): void {
+ static handleFileException(
+ logPrefix: string,
+ fileType: string,
+ filePath: string,
+ error: NodeJS.ErrnoException,
+ params: HandleErrorParams<EmptyObject> = { 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);
}
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 = [
// `${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;
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();
result = value;
} else {
// Convert
- result = (value === 'true');
+ result = value === 'true';
}
}
return result;
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);
}
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<T>(object: T): T {
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]
}
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 {
}
public static async promiseWithTimeout<T>(
- promise: Promise<T>,
- timeoutMs: number,
- timeoutError: Error,
- timeoutCallback: () => void = () => { /* This is intentional */ }
+ promise: Promise<T>,
+ timeoutMs: number,
+ timeoutError: Error,
+ timeoutCallback: () => void = () => {
+ /* This is intentional */
+ }
): Promise<T> {
// Create a timeout promise that rejects in timeout milliseconds
const timeoutPromise = new Promise<never>((_, reject) => {
* @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;
}
*/
constructor(workerScript: string, workerOptions?: WorkerOptions) {
super(workerScript, workerOptions);
- this.workerOptions.poolOptions.exitHandler = this.workerOptions?.poolOptions?.exitHandler ?? WorkerUtils.defaultExitHandler;
- this.pool = new DynamicThreadPool<WorkerData>(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<WorkerData>(
+ this.workerOptions.poolMinSize,
+ this.workerOptions.poolMaxSize,
+ this.workerScript,
+ this.workerOptions.poolOptions
+ );
}
get size(): number {
public async addElement(elementData: WorkerData): Promise<void> {
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));
}
}
// This is intentional
}
- public static getWorkerImplementation<T extends WorkerData>(workerScript: string, workerProcessType: WorkerProcessType, workerOptions?: WorkerOptions): WorkerAbstract<T> | null {
+ public static getWorkerImplementation<T extends WorkerData>(
+ workerScript: string,
+ workerProcessType: WorkerProcessType,
+ workerOptions?: WorkerOptions
+ ): WorkerAbstract<T> | 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<Worker>;
- 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<Worker>);
+ workerOptions?.messageHandler &&
+ (workerOptions.poolOptions.messageHandler = workerOptions.messageHandler);
let workerImplementation: WorkerAbstract<T> = 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:
constructor(workerScript: string, workerOptions?: WorkerOptions) {
super(workerScript, workerOptions);
this.workerSet = new Set<WorkerSetElement>();
- this.messageHandler = workerOptions?.messageHandler ?? (() => { /* This is intentional */ });
+ this.messageHandler =
+ workerOptions?.messageHandler ??
+ (() => {
+ /* This is intentional */
+ });
}
get size(): number {
*/
public async addElement(elementData: WorkerData): Promise<void> {
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
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;
}
*/
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 {
public async addElement(elementData: WorkerData): Promise<void> {
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));
}
}
{
- "pluginLoadPaths": [
- "test/robohydra/plugins"
- ],
- "plugins": [
- "logger",
- "wsServer"
- ]
+ "pluginLoadPaths": ["test/robohydra/plugins"],
+ "plugins": ["logger", "wsServer"]
}
const RoboHydraWebSocketHead = require('robohydra').heads.RoboHydraWebSocketHead;
const RoboHydraWebSocketHeadProxy = require('robohydra').heads.RoboHydraWebSocketHeadProxy;
-exports.getBodyParts = function(conf) {
+exports.getBodyParts = function (conf) {
let wsSocket;
return {
heads: [
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);
} 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;
- }
- })
- ]
+ },
+ }),
+ ],
};
};
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', () => {
// "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'. */