"stationTemplateURLs": [
{
"file": "./src/assets/station-templates/siemens.station-template.json",
- "numberOfStation": 0
+ "numberOfStations": 0
},
{
"file": "./src/assets/station-templates/keba.station-template.json",
- "numberOfStation": 0
+ "numberOfStations": 0
},
{
"file": "./src/assets/station-templates/abb.station-template.json",
- "numberOfStation": 0
+ "numberOfStations": 0
},
{
"file": "./src/assets/station-templates/evlink.station-template.json",
- "numberOfStation": 0
+ "numberOfStations": 0
},
{
"file": "./src/assets/station-templates/schneider.station-template.json",
- "numberOfStation": 0
+ "numberOfStations": 0
},
{
"file": "./src/assets/station-templates/virtual-simple-atg.station-template.json",
- "numberOfStation": 10
+ "numberOfStations": 10
}
],
"consoleLog": false,
"supervisionURLs": [
"ws://localhost:8010/OCPP16/5be7fb271014d90008992f06"
],
- "statisticsDisplayInterval": 60,
"autoReconnectTimeout": 10,
"autoReconnectMaxRetries": 10,
+ "statisticsDisplayInterval": 60,
"distributeStationToTenantEqually": true,
"useWorkerPool": false,
"workerPoolSize": 16,
"stationTemplateURLs": [
{
"file": "./src/assets/station-templates/siemens.station-template.json",
- "numberOfStation": 1
+ "numberOfStations": 1
},
{
"file": "./src/assets/station-templates/keba.station-template.json",
- "numberOfStation": 2
+ "numberOfStations": 2
},
{
"file": "./src/assets/station-templates/abb.station-template.json",
- "numberOfStation": 2
+ "numberOfStations": 2
},
{
"file": "./src/assets/station-templates/evlink.station-template.json",
- "numberOfStation": 4
+ "numberOfStations": 4
},
{
"file": "./src/assets/station-templates/schneider.station-template.json",
- "numberOfStation": 1
+ "numberOfStations": 1
}
],
"logFile": "combined.log",
+import ChargingStationConfiguration, { ConfigurationKey } from '../types/ChargingStationConfiguration';
import Connectors, { Connector } from '../types/Connectors';
import MeterValue, { MeterValueLocation, MeterValueMeasurand, MeterValuePhase, MeterValueUnit } from '../types/MeterValue';
import { PerformanceObserver, performance } from 'perf_hooks';
private _index: number;
private _stationTemplateFile: string;
private _stationInfo;
- private _bootNotificationMessage;
+ private _bootNotificationMessage: {
+ chargePointModel: string,
+ chargePointVendor: string,
+ chargeBoxSerialNumber?: string,
+ firmwareVersion?: string,
+ };
+
private _connectors: Connectors;
- private _configuration;
+ private _configuration: ChargingStationConfiguration;
private _connectorsConfigurationHash: string;
private _supervisionUrl: string;
private _wsConnectionUrl: string;
private _autoReconnectRetryCount: number;
private _autoReconnectMaxRetries: number;
private _autoReconnectTimeout: number;
- private _requests;
+ private _requests: { [id: string]: [(payload?, requestPayload?) => void, (error?: OCPPError) => void, object] };
private _messageQueue: any[];
private _automaticTransactionGeneration: AutomaticTransactionGenerator;
private _authorizedTags: string[];
return Utils.logPrefix(` ${this._stationInfo.name}:`);
}
- _getConfiguration() {
- return this._stationInfo.Configuration ? this._stationInfo.Configuration : {};
+ _getConfiguration(): ChargingStationConfiguration {
+ return this._stationInfo.Configuration ? this._stationInfo.Configuration : {} as ChargingStationConfiguration;
}
_getAuthorizationFile(): string {
// Get a random url
indexUrl = Math.floor(Math.random() * supervisionUrls.length);
}
- return supervisionUrls[indexUrl];
+ return supervisionUrls[indexUrl] as string;
}
- return supervisionUrls;
+ return supervisionUrls as string;
}
_getAuthorizeRemoteTxRequests(): boolean {
logger.error(`${this._logPrefix()} Socket: connection retry with timeout ${this._autoReconnectTimeout}ms`);
this._autoReconnectRetryCount++;
setTimeout(() => {
- logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount);
+ logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount.toString());
this.start();
}, this._autoReconnectTimeout);
} else if (this._autoReconnectTimeout !== 0 || this._autoReconnectMaxRetries !== -1) {
...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue.toString() },
});
for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
- const voltageValue = Utils.convertToInt(sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value);
+ const voltageValue = Utils.convertToFloat(sampledValues.sampledValue[sampledValues.sampledValue.length - 1].value);
let phaseValue: string;
if (voltageValue >= 0 && voltageValue <= 250) {
phaseValue = `L${phase}-N`;
});
}
// Power.Active.Import measurand
- } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_EXPORT && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.POWER_ACTIVE_EXPORT)) {
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.POWER_ACTIVE_IMPORT)) {
// FIXME: factor out powerDivider checks
if (Utils.isUndefined(self._stationInfo.powerDivider)) {
const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`;
}
}
- async sendError(messageId, err: Error | OCPPError, commandName): Promise<unknown> {
+ async sendError(messageId: string, err: Error | OCPPError, commandName: string): Promise<unknown> {
// Check exception type: only OCPP error are accepted
const error = err instanceof OCPPError ? err : new OCPPError(Constants.OCPP_ERROR_INTERNAL_ERROR, err.message, err.stack && err.stack);
// Send error
return this.sendMessage(messageId, error, Constants.OCPP_JSON_CALL_ERROR_MESSAGE, commandName);
}
- async sendMessage(messageId, commandParams, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName: string): Promise<unknown> {
+ async sendMessage(messageId: string, commandParams, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName: string): Promise<unknown> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
// Send a message through wsConnection
logger.debug(`${self._logPrefix()} Error %j occurred when calling command %s with parameters %j`, error, commandName, commandParams);
// Build Exception
// eslint-disable-next-line no-empty-function
- self._requests[messageId] = [() => { }, () => { }, '']; // Properly format the request
+ self._requests[messageId] = [() => { }, () => { }, {}]; // Properly format the request
// Send error
reject(error);
}
}
const configuredMeterValueSampleInterval = this._getConfigurationKey('MeterValueSampleInterval');
this._startMeterValues(requestPayload.connectorId,
- configuredMeterValueSampleInterval ? configuredMeterValueSampleInterval.value * 1000 : 60000);
+ configuredMeterValueSampleInterval ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000 : 60000);
} else {
logger.error(this._logPrefix() + ' Starting transaction id ' + payload.transactionId + ' REJECTED with status ' + payload.idTagInfo.status + ', idTag ' + requestPayload.idTag);
this._resetTransactionOnConnector(requestPayload.connectorId);
logger.debug(this._logPrefix() + ' Heartbeat response received: %j to Heartbeat request: %j', payload, requestPayload);
}
- async handleRequest(messageId, commandName, commandPayload): Promise<void> {
+ async handleRequest(messageId: string, commandName: string, commandPayload): Promise<void> {
let response;
// Call
if (typeof this['handleRequest' + commandName] === 'function') {
return Constants.OCPP_RESPONSE_ACCEPTED;
}
- _getConfigurationKey(key: string) {
+ _getConfigurationKey(key: string): ConfigurationKey {
return this._configuration.configurationKey.find((configElement) => configElement.key === key);
}
}
}
- handleRequestGetConfiguration(commandPayload) {
- const configurationKey = [];
- const unknownKey = [];
+ handleRequestGetConfiguration(commandPayload): { configurationKey: ConfigurationKey[]; unknownKey: string[] } {
+ const configurationKey: ConfigurationKey[] = [];
+ const unknownKey: string[] = [];
if (Utils.isEmptyArray(commandPayload.key)) {
for (const configuration of this._configuration.configurationKey) {
if (Utils.isUndefined(configuration.visible)) {
import Configuration from './utils/Configuration';
+import { StationTemplateURL } from './types/ConfigurationData';
import Utils from './utils/Utils';
import Wrk from './charging-station/Worker';
import logger from './utils/Logger';
let numStationsTotal = 0;
// Start each ChargingStation object in a worker thread
if (Configuration.getStationTemplateURLs()) {
- Configuration.getStationTemplateURLs().forEach((stationURL) => {
+ Configuration.getStationTemplateURLs().forEach((stationURL: StationTemplateURL) => {
try {
- const nbStation = stationURL.numberOfStation ? stationURL.numberOfStation : 0;
- numStationsTotal += nbStation;
- for (let index = 1; index <= nbStation; index++) {
+ const nbStations = stationURL.numberOfStations ? stationURL.numberOfStations : 0;
+ numStationsTotal += nbStations;
+ for (let index = 1; index <= nbStations; index++) {
const worker = new Wrk('./dist/charging-station/StationWorker.js', {
index,
templateFile: stationURL.file,
--- /dev/null
+export interface ConfigurationKey {
+ key: string;
+ readonly?: boolean;
+ value: string;
+ visible?: boolean;
+ reboot?: boolean;
+}
+
+export default interface ChargingStationConfiguration {
+ configurationKey: ConfigurationKey[];
+}
--- /dev/null
+export default interface CommandStatisticsData {
+ countRequest: number;
+ countResponse: number;
+ countError: number;
+ countTime: number;
+ minTime: number;
+ maxTime: number;
+ totalTime: number;
+ avgTime: number;
+}
--- /dev/null
+export interface StationTemplateURL {
+ file: string;
+ numberOfStations: number;
+}
+
+export default interface ConfigurationData {
+ supervisionURLs?: string[];
+ stationTemplateURLs: StationTemplateURL[];
+ statisticsDisplayInterval?: number;
+ autoReconnectTimeout?: number;
+ autoReconnectMaxRetries?: number;
+ distributeStationToTenantEqually?: boolean;
+ useWorkerPool?: boolean;
+ workerPoolSize?: number;
+ logFormat?: string;
+ logLevel?: string;
+ logFile?: string;
+ errorFile?: string;
+ consoleLog?: boolean;
+}
+import ConfigurationData, { StationTemplateURL } from '../types/ConfigurationData';
+
import Utils from './Utils';
import fs from 'fs';
export default class Configuration {
- static configuration;
+ static configuration: ConfigurationData;
// Read the config file
- static getConfig() {
+ static getConfig(): ConfigurationData {
if (!Configuration.configuration) {
- Configuration.configuration = JSON.parse(fs.readFileSync('./src/assets/config.json', 'utf8'));
+ Configuration.configuration = JSON.parse(fs.readFileSync('./src/assets/config.json', 'utf8')) as ConfigurationData;
}
return Configuration.configuration;
}
return Utils.objectHasOwnProperty(Configuration.getConfig(), 'autoReconnectMaxRetries') ? Configuration.getConfig().autoReconnectMaxRetries : -1;
}
- static getStationTemplateURLs(): any[] {
+ static getStationTemplateURLs(): StationTemplateURL[] {
// Read conf
return Configuration.getConfig().stationTemplateURLs;
}
return Utils.objectHasOwnProperty(Configuration.getConfig(), 'errorFile') ? Configuration.getConfig().errorFile : 'error.log';
}
- static getSupervisionURLs(): string {
+ static getSupervisionURLs(): string[] {
// Read conf
return Configuration.getConfig().supervisionURLs;
}
+import CommandStatisticsData from '../types/CommandStatisticsData';
import Configuration from './Configuration';
import Constants from './Constants';
+import { PerformanceEntry } from 'perf_hooks';
import Utils from './Utils';
import logger from './Logger';
export default class Statistics {
private static instance: Statistics;
- private _statistics;
private _objName: string;
+ private _commandsStatistics: {
+ [command: string]: CommandStatisticsData
+ };
private constructor() {
- this._statistics = {};
+ this._commandsStatistics = {};
}
set objName(objName: string) {
addMessage(command: string, messageType: number): void {
switch (messageType) {
case Constants.OCPP_JSON_CALL_MESSAGE:
- if (this._statistics[command] && this._statistics[command].countRequest) {
- this._statistics[command].countRequest++;
+ if (this._commandsStatistics[command] && this._commandsStatistics[command].countRequest) {
+ this._commandsStatistics[command].countRequest++;
} else {
- this._statistics[command] = {};
- this._statistics[command].countRequest = 1;
+ this._commandsStatistics[command] = {} as CommandStatisticsData;
+ this._commandsStatistics[command].countRequest = 1;
}
break;
case Constants.OCPP_JSON_CALL_RESULT_MESSAGE:
- if (this._statistics[command]) {
- if (this._statistics[command].countResponse) {
- this._statistics[command].countResponse++;
+ if (this._commandsStatistics[command]) {
+ if (this._commandsStatistics[command].countResponse) {
+ this._commandsStatistics[command].countResponse++;
} else {
- this._statistics[command].countResponse = 1;
+ this._commandsStatistics[command].countResponse = 1;
}
} else {
- this._statistics[command] = {};
- this._statistics[command].countResponse = 1;
+ this._commandsStatistics[command] = {} as CommandStatisticsData;
+ this._commandsStatistics[command].countResponse = 1;
}
break;
case Constants.OCPP_JSON_CALL_ERROR_MESSAGE:
- if (this._statistics[command]) {
- if (this._statistics[command].countError) {
- this._statistics[command].countError++;
+ if (this._commandsStatistics[command]) {
+ if (this._commandsStatistics[command].countError) {
+ this._commandsStatistics[command].countError++;
} else {
- this._statistics[command].countError = 1;
+ this._commandsStatistics[command].countError = 1;
}
} else {
- this._statistics[command] = {};
- this._statistics[command].countError = 1;
+ this._commandsStatistics[command] = {} as CommandStatisticsData;
+ this._commandsStatistics[command].countError = 1;
}
break;
default:
stopTransaction: 'StopTransaction',
};
if (MAPCOMMAND[command]) {
- command = MAPCOMMAND[command];
+ command = MAPCOMMAND[command] as string;
}
// Initialize command statistics
- if (!this._statistics[command]) {
- this._statistics[command] = {};
+ if (!this._commandsStatistics[command]) {
+ this._commandsStatistics[command] = {} as CommandStatisticsData;
}
// Update current statistics timers
- this._statistics[command].countTime = this._statistics[command].countTime ? this._statistics[command].countTime + 1 : 1;
- this._statistics[command].minTime = this._statistics[command].minTime ? (this._statistics[command].minTime > duration ? duration : this._statistics[command].minTime) : duration;
- this._statistics[command].maxTime = this._statistics[command].maxTime ? (this._statistics[command].maxTime < duration ? duration : this._statistics[command].maxTime) : duration;
- this._statistics[command].totalTime = this._statistics[command].totalTime ? this._statistics[command].totalTime + duration : duration;
- this._statistics[command].avgTime = this._statistics[command].totalTime / this._statistics[command].countTime;
+ this._commandsStatistics[command].countTime = this._commandsStatistics[command].countTime ? this._commandsStatistics[command].countTime + 1 : 1;
+ this._commandsStatistics[command].minTime = this._commandsStatistics[command].minTime ? (this._commandsStatistics[command].minTime > duration ? duration : this._commandsStatistics[command].minTime) : duration;
+ this._commandsStatistics[command].maxTime = this._commandsStatistics[command].maxTime ? (this._commandsStatistics[command].maxTime < duration ? duration : this._commandsStatistics[command].maxTime) : duration;
+ this._commandsStatistics[command].totalTime = this._commandsStatistics[command].totalTime ? this._commandsStatistics[command].totalTime + duration : duration;
+ this._commandsStatistics[command].avgTime = this._commandsStatistics[command].totalTime / this._commandsStatistics[command].countTime;
}
- logPerformance(entry, className: string): void {
+ logPerformance(entry: PerformanceEntry, className: string): void {
this.addPerformanceTimer(entry.name, entry.duration);
logger.info(`${this._logPrefix()} class->${className}, method->${entry.name}, duration->${entry.duration}`);
}
_display(): void {
- logger.info(this._logPrefix() + ' %j', this._statistics);
+ logger.info(this._logPrefix() + ' %j', this._commandsStatistics);
}
_displayInterval(): void {
setInterval(() => {
this._display();
}, Configuration.getStatisticsDisplayInterval() * 1000);
- logger.info(this._logPrefix() + ' displayed every ' + Configuration.getStatisticsDisplayInterval() + 's');
+ logger.info(this._logPrefix() + ' displayed every ' + Configuration.getStatisticsDisplayInterval().toString() + 's');
}
}