import { AuthorizationStatus, StartTransactionRequest, StartTransactionResponse, StopTransactionReason, StopTransactionRequest, StopTransactionResponse } from '../types/ocpp/1.6/Transaction';
+import { BootNotificationRequest, ChangeConfigurationRequest, GetConfigurationRequest, HeartbeatRequest, IncomingRequestCommand, RemoteStartTransactionRequest, RemoteStopTransactionRequest, RequestCommand, ResetRequest, SetChargingProfileRequest, StatusNotificationRequest, UnlockConnectorRequest } from '../types/ocpp/1.6/Requests';
import { BootNotificationResponse, ChangeConfigurationResponse, DefaultResponse, GetConfigurationResponse, HeartbeatResponse, RegistrationStatus, SetChargingProfileResponse, StatusNotificationResponse, UnlockConnectorResponse } from '../types/ocpp/1.6/RequestResponses';
import { ChargingProfile, ChargingProfilePurposeType } from '../types/ocpp/1.6/ChargingProfile';
import ChargingStationConfiguration, { ConfigurationKey } from '../types/ChargingStationConfiguration';
import Connectors, { Connector } from '../types/Connectors';
import { MeterValue, MeterValueLocation, MeterValueMeasurand, MeterValuePhase, MeterValueUnit, MeterValuesRequest, MeterValuesResponse, SampledValue } from '../types/ocpp/1.6/MeterValues';
import { PerformanceObserver, performance } from 'perf_hooks';
-import Requests, { BootNotificationRequest, ChangeConfigurationRequest, GetConfigurationRequest, HeartbeatRequest, IncomingRequest, IncomingRequestCommand, RemoteStartTransactionRequest, RemoteStopTransactionRequest, Request, RequestCommand, ResetRequest, SetChargingProfileRequest, StatusNotificationRequest, UnlockConnectorRequest } from '../types/ocpp/1.6/Requests';
+import Requests, { IncomingRequest, Request } from '../types/ocpp/Requests';
import WebSocket, { MessageEvent } from 'ws';
import AutomaticTransactionGenerator from './AutomaticTransactionGenerator';
import { ErrorType } from '../types/ocpp/ErrorType';
import MeasurandValues from '../types/MeasurandValues';
import { MessageType } from '../types/ocpp/MessageType';
+import { OCPPConfigurationKey } from '../types/ocpp/Configuration';
import OCPPError from './OcppError';
+import { StandardParametersKey } from '../types/ocpp/1.6/Configuration';
import Statistics from '../utils/Statistics';
import Utils from '../utils/Utils';
import { WebSocketCloseEventStatusCode } from '../types/WebSocket';
private _messageQueue: string[];
private _automaticTransactionGeneration: AutomaticTransactionGenerator;
private _authorizedTags: string[];
- private _heartbeatInterval: number;
private _heartbeatSetInterval: NodeJS.Timeout;
private _webSocketPingSetInterval: NodeJS.Timeout;
private _statistics: Statistics;
}
}
// OCPP parameters
- this._addConfigurationKey('NumberOfConnectors', this._getNumberOfConnectors().toString(), true);
- if (!this._getConfigurationKey('MeterValuesSampledData')) {
- this._addConfigurationKey('MeterValuesSampledData', MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER);
+ this._addConfigurationKey(StandardParametersKey.NumberOfConnectors, this._getNumberOfConnectors().toString(), true);
+ if (!this._getConfigurationKey(StandardParametersKey.MeterValuesSampledData)) {
+ this._addConfigurationKey(StandardParametersKey.MeterValuesSampledData, MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER);
}
this._stationInfo.powerDivider = this._getPowerDivider();
if (this.getEnableStatistics()) {
return !Utils.isUndefined(this._stationInfo.reconnectExponentialDelay) ? this._stationInfo.reconnectExponentialDelay : false;
}
+ _getHeartbeatInterval(): number {
+ const HeartbeatInterval = this._getConfigurationKey(StandardParametersKey.HeartbeatInterval);
+ if (HeartbeatInterval) {
+ return Utils.convertToInt(HeartbeatInterval.value) * 1000;
+ }
+ const HeartBeatInterval = this._getConfigurationKey(StandardParametersKey.HeartBeatInterval);
+ if (HeartBeatInterval) {
+ return Utils.convertToInt(HeartBeatInterval.value) * 1000;
+ }
+ return 0;
+ }
+
_getAuthorizeRemoteTxRequests(): boolean {
- const authorizeRemoteTxRequests = this._getConfigurationKey('AuthorizeRemoteTxRequests');
+ const authorizeRemoteTxRequests = this._getConfigurationKey(StandardParametersKey.AuthorizeRemoteTxRequests);
return authorizeRemoteTxRequests ? Utils.convertToBoolean(authorizeRemoteTxRequests.value) : false;
}
_getLocalAuthListEnabled(): boolean {
- const localAuthListEnabled = this._getConfigurationKey('LocalAuthListEnabled');
+ const localAuthListEnabled = this._getConfigurationKey(StandardParametersKey.LocalAuthListEnabled);
return localAuthListEnabled ? Utils.convertToBoolean(localAuthListEnabled.value) : false;
}
}
_startWebSocketPing(): void {
- const webSocketPingInterval: number = this._getConfigurationKey('WebSocketPingInterval') ? Utils.convertToInt(this._getConfigurationKey('WebSocketPingInterval').value) : 0;
+ 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._isWebSocketOpen()) {
}
_startHeartbeat(): void {
- if (this._heartbeatInterval && this._heartbeatInterval > 0 && !this._heartbeatSetInterval) {
+ if (this._getHeartbeatInterval() && this._getHeartbeatInterval() > 0 && !this._heartbeatSetInterval) {
this._heartbeatSetInterval = setInterval(async () => {
await this.sendHeartbeat();
- }, this._heartbeatInterval);
- logger.info(this._logPrefix() + ' Heartbeat started every ' + Utils.milliSecondsToHHMMSS(this._heartbeatInterval));
+ }, this._getHeartbeatInterval());
+ logger.info(this._logPrefix() + ' Heartbeat started every ' + Utils.milliSecondsToHHMMSS(this._getHeartbeatInterval()));
} else if (this._heartbeatSetInterval) {
- logger.info(this._logPrefix() + ' Heartbeat every ' + Utils.milliSecondsToHHMMSS(this._heartbeatInterval) + ' already started');
+ logger.info(this._logPrefix() + ' Heartbeat every ' + Utils.milliSecondsToHHMMSS(this._getHeartbeatInterval()) + ' already started');
} else {
- logger.error(`${this._logPrefix()} Heartbeat interval set to ${this._heartbeatInterval ? Utils.milliSecondsToHHMMSS(this._heartbeatInterval) : this._heartbeatInterval}, not starting the heartbeat`);
+ logger.error(`${this._logPrefix()} Heartbeat interval set to ${this._getHeartbeatInterval() ? Utils.milliSecondsToHHMMSS(this._getHeartbeatInterval()) : this._getHeartbeatInterval()}, not starting the heartbeat`);
}
}
async onMessage(messageEvent: MessageEvent): Promise<void> {
let [messageType, messageId, commandName, commandPayload, errorDetails]: IncomingRequest = [0, '', '' as IncomingRequestCommand, '', ''];
+ let responseCallback: (payload?, requestPayload?) => void;
+ let rejectCallback: (error: OCPPError) => void;
+ let requestPayload: Record<string, unknown>;
+ let errMsg: string;
try {
// Parse the message
[messageType, messageId, commandName, commandPayload, errorDetails] = JSON.parse(messageEvent.toString()) as IncomingRequest;
// Incoming Message
case MessageType.CALL_MESSAGE:
if (this.getEnableStatistics()) {
- this._statistics.addMessage(commandName , messageType);
+ this._statistics.addMessage(commandName, messageType);
}
// Process the call
- await this.handleRequest(messageId, commandName , commandPayload);
+ await this.handleRequest(messageId, commandName, commandPayload);
break;
// Outcome Message
case MessageType.CALL_RESULT_MESSAGE:
// Respond
- // eslint-disable-next-line no-case-declarations
- let responseCallback; let requestPayload;
if (Utils.isIterable(this._requests[messageId])) {
[responseCallback, , requestPayload] = this._requests[messageId];
} else {
// Error
throw new Error(`Error request for unknown message id ${messageId}`);
}
- // eslint-disable-next-line no-case-declarations
- let rejectCallback;
if (Utils.isIterable(this._requests[messageId])) {
[, rejectCallback] = this._requests[messageId];
} else {
break;
// Error
default:
- // eslint-disable-next-line no-case-declarations
- const errMsg = `${this._logPrefix()} Wrong message type ${messageType}`;
+ errMsg = `${this._logPrefix()} Wrong message type ${messageType}`;
logger.error(errMsg);
throw new Error(errMsg);
}
for (let index = 0; index < meterValuesTemplate.length; index++) {
const connector = self.getConnector(connectorId);
// SoC measurand
- if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.STATE_OF_CHARGE && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.STATE_OF_CHARGE)) {
+ if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.STATE_OF_CHARGE && self._getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(MeterValueMeasurand.STATE_OF_CHARGE)) {
meterValue.sampledValue.push({
...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.PERCENT },
...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
logger.error(`${self._logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ? meterValue.sampledValue[sampledValuesIndex].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/100`);
}
// Voltage measurand
- } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.VOLTAGE && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.VOLTAGE)) {
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.VOLTAGE && self._getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(MeterValueMeasurand.VOLTAGE)) {
const voltageMeasurandValue = Utils.getRandomFloatRounded(self._getVoltageOut() + self._getVoltageOut() * 0.1, self._getVoltageOut() - self._getVoltageOut() * 0.1);
meterValue.sampledValue.push({
...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.VOLT },
});
}
// Power.Active.Import measurand
- } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.POWER_ACTIVE_IMPORT)) {
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.POWER_ACTIVE_IMPORT && self._getConfigurationKey(StandardParametersKey.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`;
});
}
// Current.Import measurand
- } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.CURRENT_IMPORT && self._getConfigurationKey('MeterValuesSampledData').value.includes(MeterValueMeasurand.CURRENT_IMPORT)) {
+ } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.CURRENT_IMPORT && self._getConfigurationKey(StandardParametersKey.MeterValuesSampledData).value.includes(MeterValueMeasurand.CURRENT_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`;
return this.sendMessage(messageId, error, MessageType.CALL_ERROR_MESSAGE, commandName);
}
- async sendMessage(messageId: string, commandParams, messageType = MessageType.CALL_RESULT_MESSAGE, commandName: RequestCommand | IncomingRequestCommand): Promise<any> {
+ async sendMessage(messageId: string, commandParams, messageType: MessageType = MessageType.CALL_RESULT_MESSAGE, commandName: RequestCommand | IncomingRequestCommand): Promise<any> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
// Send a message through wsConnection
return new Promise((resolve: (value?: any | PromiseLike<any>) => void, reject: (reason?: any) => void) => {
- let messageToSend;
+ let messageToSend: string;
// Type of message
switch (messageType) {
// Request
// Handle dups in buffer
for (const message of this._messageQueue) {
// Same message
- if (JSON.stringify(messageToSend) === JSON.stringify(message)) {
+ if (messageToSend === message) {
dups = true;
break;
}
handleResponseBootNotification(payload: BootNotificationResponse, requestPayload: BootNotificationRequest): void {
if (payload.status === RegistrationStatus.ACCEPTED) {
- this._heartbeatInterval = payload.interval * 1000;
this._heartbeatSetInterval ? this._restartHeartbeat() : this._startHeartbeat();
- this._addConfigurationKey('HeartBeatInterval', payload.interval.toString());
- this._addConfigurationKey('HeartbeatInterval', payload.interval.toString(), false, false);
+ this._addConfigurationKey(StandardParametersKey.HeartBeatInterval, payload.interval.toString());
+ this._addConfigurationKey(StandardParametersKey.HeartbeatInterval, payload.interval.toString(), false, false);
this._hasStopped && (this._hasStopped = false);
} else if (payload.status === RegistrationStatus.PENDING) {
logger.info(this._logPrefix() + ' Charging station in pending state on the central server');
if (this._stationInfo.powerSharedByConnectors) {
this._stationInfo.powerDivider++;
}
- const configuredMeterValueSampleInterval = this._getConfigurationKey('MeterValueSampleInterval');
+ const configuredMeterValueSampleInterval = this._getConfigurationKey(StandardParametersKey.MeterValueSampleInterval);
this._startMeterValues(connectorId,
configuredMeterValueSampleInterval ? Utils.convertToInt(configuredMeterValueSampleInterval.value) * 1000 : 60000);
} else {
return Constants.OCPP_RESPONSE_UNLOCKED;
}
- _getConfigurationKey(key: string, caseInsensitive = false): ConfigurationKey {
- return this._configuration.configurationKey.find((configElement) => {
+ _getConfigurationKey(key: string | StandardParametersKey, caseInsensitive = false): ConfigurationKey {
+ const configurationKey: ConfigurationKey = this._configuration.configurationKey.find((configElement) => {
if (caseInsensitive) {
return configElement.key.toLowerCase() === key.toLowerCase();
}
return configElement.key === key;
});
+ if (configurationKey && Utils.isUndefined(configurationKey.readonly)) {
+ configurationKey.readonly = false;
+ }
+ return configurationKey;
}
- _addConfigurationKey(key: string, value: string, readonly = false, visible = true, reboot = false): void {
+ _addConfigurationKey(key: string | StandardParametersKey, value: string, readonly = false, visible = true, reboot = false): void {
const keyFound = this._getConfigurationKey(key);
if (!keyFound) {
this._configuration.configurationKey.push({
visible,
reboot,
});
+ } else {
+ logger.error(`${this._logPrefix()} Trying to add an already existing configuration key: %j`, keyFound);
}
}
- _setConfigurationKeyValue(key: string, value: string): void {
+ _setConfigurationKeyValue(key: string | StandardParametersKey, value: string): void {
const keyFound = this._getConfigurationKey(key);
if (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 });
}
}
handleRequestGetConfiguration(commandPayload: GetConfigurationRequest): GetConfigurationResponse {
- const configurationKey: ConfigurationKey[] = [];
+ const configurationKey: OCPPConfigurationKey[] = [];
const unknownKey: string[] = [];
if (Utils.isEmptyArray(commandPayload.key)) {
for (const configuration of this._configuration.configurationKey) {
valueChanged = true;
}
let triggerHeartbeatRestart = false;
- if (keyToChange.key === 'HeartBeatInterval' && valueChanged) {
- this._setConfigurationKeyValue('HeartbeatInterval', commandPayload.value);
+ if (keyToChange.key === StandardParametersKey.HeartBeatInterval && valueChanged) {
+ this._setConfigurationKeyValue(StandardParametersKey.HeartbeatInterval, commandPayload.value);
triggerHeartbeatRestart = true;
}
- if (keyToChange.key === 'HeartbeatInterval' && valueChanged) {
- this._setConfigurationKeyValue('HeartBeatInterval', commandPayload.value);
+ if (keyToChange.key === StandardParametersKey.HeartbeatInterval && valueChanged) {
+ this._setConfigurationKeyValue(StandardParametersKey.HeartBeatInterval, commandPayload.value);
triggerHeartbeatRestart = true;
}
if (triggerHeartbeatRestart) {
- this._heartbeatInterval = Utils.convertToInt(commandPayload.value) * 1000;
this._restartHeartbeat();
}
- if (keyToChange.key === 'WebSocketPingInterval' && valueChanged) {
+ if (keyToChange.key === StandardParametersKey.WebSocketPingInterval && valueChanged) {
this._restartWebSocketPing();
}
if (keyToChange.reboot) {