- logger.error(this._logPrefix() + ' Socket: reconnecting try #' + this._autoReconnectRetryCount.toString());
- this._openWSConnection({ handshakeTimeout: reconnectDelay - 100 });
- } else if (this._autoReconnectMaxRetries !== -1) {
- logger.error(`${this._logPrefix()} Socket: max retries reached (${this._autoReconnectRetryCount}) or retry disabled (${this._autoReconnectMaxRetries})`);
- }
- }
-
- async onOpen(): Promise<void> {
- logger.info(`${this._logPrefix()} Is connected to server through ${this._wsConnectionUrl}`);
- if (!this._hasSocketRestarted || this._hasStopped) {
- // Send BootNotification
- this._bootNotificationResponse = await this.sendBootNotification();
- }
- if (this._bootNotificationResponse.status === RegistrationStatus.ACCEPTED) {
- await this._startMessageSequence();
- } else {
- do {
- await Utils.sleep(this._bootNotificationResponse.interval * 1000);
- // Resend BootNotification
- this._bootNotificationResponse = await this.sendBootNotification();
- } while (this._bootNotificationResponse.status !== RegistrationStatus.ACCEPTED);
- }
- if (this._hasSocketRestarted && this._bootNotificationResponse.status === RegistrationStatus.ACCEPTED) {
- if (!Utils.isEmptyArray(this._messageQueue)) {
- this._messageQueue.forEach((message, index) => {
- if (this._wsConnection?.readyState === WebSocket.OPEN) {
- this._messageQueue.splice(index, 1);
- this._wsConnection.send(message);
- }
- });
- }
- }
- this._autoReconnectRetryCount = 0;
- this._hasSocketRestarted = false;
- }
-
- async onError(errorEvent): Promise<void> {
- switch (errorEvent.code) {
- case 'ECONNREFUSED':
- this._hasSocketRestarted = true;
- await this._reconnect(errorEvent);
- break;
- default:
- logger.error(this._logPrefix() + ' Socket error: %j', errorEvent);
- break;
- }
- }
-
- async onClose(closeEvent): Promise<void> {
- switch (closeEvent) {
- case 1000: // Normal close
- case 1005:
- logger.info(this._logPrefix() + ' Socket normally closed: %j', closeEvent);
- this._autoReconnectRetryCount = 0;
- break;
- default: // Abnormal close
- this._hasSocketRestarted = true;
- await this._reconnect(closeEvent);
- break;
- }
- }
-
- onPing(): void {
- logger.debug(this._logPrefix() + ' Has received a WS ping (rfc6455) from the server');
- }
-
- onPong(): void {
- logger.debug(this._logPrefix() + ' Has received a WS pong (rfc6455) from the server');
- }
-
- async onMessage(messageEvent: MessageEvent): Promise<void> {
- let [messageType, messageId, commandName, commandPayload, errorDetails] = [0, '', Constants.ENTITY_CHARGING_STATION, '', ''];
- try {
- // Parse the message
- [messageType, messageId, commandName, commandPayload, errorDetails] = JSON.parse(messageEvent.toString());
-
- // Check the Type of message
- switch (messageType) {
- // Incoming Message
- case Constants.OCPP_JSON_CALL_MESSAGE:
- if (this.getEnableStatistics()) {
- this._statistics.addMessage(commandName, messageType);
- }
- // Process the call
- await this.handleRequest(messageId, commandName, commandPayload);
- break;
- // Outcome Message
- case Constants.OCPP_JSON_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 {
- throw new Error(`Response request for message id ${messageId} is not iterable`);
- }
- if (!responseCallback) {
- // Error
- throw new Error(`Response request for unknown message id ${messageId}`);
- }
- delete this._requests[messageId];
- responseCallback(commandName, requestPayload);
- break;
- // Error Message
- case Constants.OCPP_JSON_CALL_ERROR_MESSAGE:
- if (!this._requests[messageId]) {
- // 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 {
- throw new Error(`Error request for message id ${messageId} is not iterable`);
- }
- delete this._requests[messageId];
- rejectCallback(new OCPPError(commandName, commandPayload, errorDetails));
- break;
- // Error
- default:
- // eslint-disable-next-line no-case-declarations
- const errMsg = `${this._logPrefix()} Wrong message type ${messageType}`;
- logger.error(errMsg);
- throw new Error(errMsg);
- }
- } catch (error) {
- // Log
- logger.error('%s Incoming message %j processing error %s on request content type %s', this._logPrefix(), messageEvent, error, this._requests[messageId]);
- // Send error
- messageType !== Constants.OCPP_JSON_CALL_ERROR_MESSAGE && await this.sendError(messageId, error, commandName);
- }
- }
-
- async sendHeartbeat(): Promise<void> {
- try {
- const payload: HeartbeatRequest = {};
- await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'Heartbeat');
- } catch (error) {
- logger.error(this._logPrefix() + ' Send Heartbeat error: %j', error);
- throw error;
- }
- }
-
- async sendBootNotification(): Promise<BootNotificationResponse> {
- try {
- return await this.sendMessage(Utils.generateUUID(), this._bootNotificationRequest, Constants.OCPP_JSON_CALL_MESSAGE, 'BootNotification') as BootNotificationResponse;
- } catch (error) {
- logger.error(this._logPrefix() + ' Send BootNotification error: %j', error);
- throw error;
- }
- }
-
- async sendStatusNotification(connectorId: number, status: ChargePointStatus, errorCode: ChargePointErrorCode = ChargePointErrorCode.NO_ERROR): Promise<void> {
- this.getConnector(connectorId).status = status;
- try {
- const payload: StatusNotificationRequest = {
- connectorId,
- errorCode,
- status,
- };
- await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StatusNotification');
- } catch (error) {
- logger.error(this._logPrefix() + ' Send StatusNotification error: %j', error);
- throw error;
- }
- }
-
- async sendStartTransaction(connectorId: number, idTag?: string): Promise<StartTransactionResponse> {
- try {
- const payload: StartTransactionRequest = {
- connectorId,
- ...!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.TRANSACTION_DEFAULT_IDTAG },
- meterStart: 0,
- timestamp: new Date().toISOString(),
- };
- return await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StartTransaction') as StartTransactionResponse;
- } catch (error) {
- logger.error(this._logPrefix() + ' Send StartTransaction error: %j', error);
- throw error;
- }
- }
-
- async sendStopTransaction(transactionId: number, reason: StopTransactionReason = StopTransactionReason.NONE): Promise<StopTransactionResponse> {
- const idTag = this._getTransactionIdTag(transactionId);
- try {
- const payload: StopTransactionRequest = {
- transactionId,
- ...!Utils.isUndefined(idTag) && { idTag: idTag },
- meterStop: this._getTransactionMeterStop(transactionId),
- timestamp: new Date().toISOString(),
- ...reason && { reason },
- };
- return await this.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'StopTransaction') as StartTransactionResponse;
- } catch (error) {
- logger.error(this._logPrefix() + ' Send StopTransaction error: %j', error);
- throw error;
- }
- }
-
- // eslint-disable-next-line consistent-this
- async sendMeterValues(connectorId: number, interval: number, self: ChargingStation, debug = false): Promise<void> {
- try {
- const meterValue: MeterValue = {
- timestamp: new Date().toISOString(),
- sampledValue: [],
- };
- const meterValuesTemplate: SampledValue[] = self.getConnector(connectorId).MeterValues;
- 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)) {
- meterValue.sampledValue.push({
- ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.PERCENT },
- ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
- measurand: meterValuesTemplate[index].measurand,
- ...!Utils.isUndefined(meterValuesTemplate[index].location) ? { location: meterValuesTemplate[index].location } : { location: MeterValueLocation.EV },
- ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: Utils.getRandomInt(100).toString() },
- });
- const sampledValuesIndex = meterValue.sampledValue.length - 1;
- if (Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > 100 || debug) {
- 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)) {
- 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 },
- ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
- measurand: meterValuesTemplate[index].measurand,
- ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
- ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue.toString() },
- });
- for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
- let phaseValue: string;
- if (self._getVoltageOut() >= 0 && self._getVoltageOut() <= 250) {
- phaseValue = `L${phase}-N`;
- } else if (self._getVoltageOut() > 250) {
- phaseValue = `L${phase}-L${(phase + 1) % self._getNumberOfPhases() !== 0 ? (phase + 1) % self._getNumberOfPhases() : self._getNumberOfPhases()}`;
- }
- meterValue.sampledValue.push({
- ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.VOLT },
- ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
- measurand: meterValuesTemplate[index].measurand,
- ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
- ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: voltageMeasurandValue.toString() },
- phase: phaseValue as MeterValuePhase,
- });
- }
- // 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)) {
- // 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`;
- logger.error(errMsg);
- throw Error(errMsg);
- } else if (self._stationInfo.powerDivider && self._stationInfo.powerDivider <= 0) {
- const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${self._stationInfo.powerDivider}`;
- logger.error(errMsg);
- throw Error(errMsg);
- }
- const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self._getPowerOutType()} powerOutType in template file ${self._stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
- const powerMeasurandValues = {} as MeasurandValues;
- const maxPower = Math.round(self._stationInfo.maxPower / self._stationInfo.powerDivider);
- const maxPowerPerPhase = Math.round((self._stationInfo.maxPower / self._stationInfo.powerDivider) / self._getNumberOfPhases());
- switch (self._getPowerOutType()) {
- case PowerOutType.AC:
- if (Utils.isUndefined(meterValuesTemplate[index].value)) {
- powerMeasurandValues.L1 = Utils.getRandomFloatRounded(maxPowerPerPhase);
- powerMeasurandValues.L2 = 0;
- powerMeasurandValues.L3 = 0;
- if (self._getNumberOfPhases() === 3) {
- powerMeasurandValues.L2 = Utils.getRandomFloatRounded(maxPowerPerPhase);
- powerMeasurandValues.L3 = Utils.getRandomFloatRounded(maxPowerPerPhase);
- }
- powerMeasurandValues.allPhases = Utils.roundTo(powerMeasurandValues.L1 + powerMeasurandValues.L2 + powerMeasurandValues.L3, 2);
- }
- break;
- case PowerOutType.DC:
- if (Utils.isUndefined(meterValuesTemplate[index].value)) {
- powerMeasurandValues.allPhases = Utils.getRandomFloatRounded(maxPower);
- }
- break;
- default:
- logger.error(errMsg);
- throw Error(errMsg);
- }
- meterValue.sampledValue.push({
- ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT },
- ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
- measurand: meterValuesTemplate[index].measurand,
- ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
- ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: powerMeasurandValues.allPhases.toString() },
- });
- const sampledValuesIndex = meterValue.sampledValue.length - 1;
- if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxPower || debug) {
- 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}/${maxPower}`);
- }
- for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
- const phaseValue = `L${phase}-N`;
- meterValue.sampledValue.push({
- ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT },
- ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
- ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && { measurand: meterValuesTemplate[index].measurand },
- ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
- ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: powerMeasurandValues[`L${phase}`] as string },
- phase: phaseValue as MeterValuePhase,
- });
- }
- // Current.Import measurand
- } else if (meterValuesTemplate[index].measurand && meterValuesTemplate[index].measurand === MeterValueMeasurand.CURRENT_IMPORT && self._getConfigurationKey('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`;
- logger.error(errMsg);
- throw Error(errMsg);
- } else if (self._stationInfo.powerDivider && self._stationInfo.powerDivider <= 0) {
- const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${self._stationInfo.powerDivider}`;
- logger.error(errMsg);
- throw Error(errMsg);
- }
- const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: Unknown ${self._getPowerOutType()} powerOutType in template file ${self._stationTemplateFile}, cannot calculate ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} measurand value`;
- const currentMeasurandValues: MeasurandValues = {} as MeasurandValues;
- let maxAmperage: number;
- switch (self._getPowerOutType()) {
- case PowerOutType.AC:
- maxAmperage = ElectricUtils.ampPerPhaseFromPower(self._getNumberOfPhases(), self._stationInfo.maxPower / self._stationInfo.powerDivider, self._getVoltageOut());
- if (Utils.isUndefined(meterValuesTemplate[index].value)) {
- currentMeasurandValues.L1 = Utils.getRandomFloatRounded(maxAmperage);
- currentMeasurandValues.L2 = 0;
- currentMeasurandValues.L3 = 0;
- if (self._getNumberOfPhases() === 3) {
- currentMeasurandValues.L2 = Utils.getRandomFloatRounded(maxAmperage);
- currentMeasurandValues.L3 = Utils.getRandomFloatRounded(maxAmperage);
- }
- currentMeasurandValues.allPhases = Utils.roundTo((currentMeasurandValues.L1 + currentMeasurandValues.L2 + currentMeasurandValues.L3) / self._getNumberOfPhases(), 2);
- }
- break;
- case PowerOutType.DC:
- maxAmperage = ElectricUtils.ampTotalFromPower(self._stationInfo.maxPower / self._stationInfo.powerDivider, self._getVoltageOut());
- if (Utils.isUndefined(meterValuesTemplate[index].value)) {
- currentMeasurandValues.allPhases = Utils.getRandomFloatRounded(maxAmperage);
- }
- break;
- default:
- logger.error(errMsg);
- throw Error(errMsg);
- }
- meterValue.sampledValue.push({
- ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.AMP },
- ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
- measurand: meterValuesTemplate[index].measurand,
- ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
- ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: currentMeasurandValues.allPhases.toString() },
- });
- const sampledValuesIndex = meterValue.sampledValue.length - 1;
- if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxAmperage || debug) {
- 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}/${maxAmperage}`);
- }
- for (let phase = 1; self._getNumberOfPhases() === 3 && phase <= self._getNumberOfPhases(); phase++) {
- const phaseValue = `L${phase}`;
- meterValue.sampledValue.push({
- ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.AMP },
- ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
- ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && { measurand: meterValuesTemplate[index].measurand },
- ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
- ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } : { value: currentMeasurandValues[phaseValue] as string },
- phase: phaseValue as MeterValuePhase,
- });
- }
- // Energy.Active.Import.Register measurand (default)
- } else if (!meterValuesTemplate[index].measurand || meterValuesTemplate[index].measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) {
- // 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`;
- logger.error(errMsg);
- throw Error(errMsg);
- } else if (self._stationInfo.powerDivider && self._stationInfo.powerDivider <= 0) {
- const errMsg = `${self._logPrefix()} MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider have zero or below value ${self._stationInfo.powerDivider}`;
- logger.error(errMsg);
- throw Error(errMsg);
- }
- if (Utils.isUndefined(meterValuesTemplate[index].value)) {
- const measurandValue = Utils.getRandomInt(self._stationInfo.maxPower / (self._stationInfo.powerDivider * 3600000) * interval);
- // Persist previous value in connector
- if (connector && !Utils.isNullOrUndefined(connector.lastEnergyActiveImportRegisterValue) && connector.lastEnergyActiveImportRegisterValue >= 0) {
- connector.lastEnergyActiveImportRegisterValue += measurandValue;
- } else {
- connector.lastEnergyActiveImportRegisterValue = 0;
- }
- }
- meterValue.sampledValue.push({
- ...!Utils.isUndefined(meterValuesTemplate[index].unit) ? { unit: meterValuesTemplate[index].unit } : { unit: MeterValueUnit.WATT_HOUR },
- ...!Utils.isUndefined(meterValuesTemplate[index].context) && { context: meterValuesTemplate[index].context },
- ...!Utils.isUndefined(meterValuesTemplate[index].measurand) && { measurand: meterValuesTemplate[index].measurand },
- ...!Utils.isUndefined(meterValuesTemplate[index].location) && { location: meterValuesTemplate[index].location },
- ...!Utils.isUndefined(meterValuesTemplate[index].value) ? { value: meterValuesTemplate[index].value } :
- { value: connector.lastEnergyActiveImportRegisterValue.toString() },
- });
- const sampledValuesIndex = meterValue.sampledValue.length - 1;
- const maxConsumption = Math.round(self._stationInfo.maxPower * 3600 / (self._stationInfo.powerDivider * interval));
- if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxConsumption || debug) {
- 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}/${maxConsumption}`);
- }
- // Unsupported measurand
- } else {
- logger.info(`${self._logPrefix()} Unsupported MeterValues measurand ${meterValuesTemplate[index].measurand ? meterValuesTemplate[index].measurand : MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER} on connectorId ${connectorId}`);
- }
- }
- const payload: MeterValuesRequest = {
- connectorId,
- transactionId: self.getConnector(connectorId).transactionId,
- meterValue: meterValue,
- };
- await self.sendMessage(Utils.generateUUID(), payload, Constants.OCPP_JSON_CALL_MESSAGE, 'MeterValues');
- } catch (error) {
- logger.error(self._logPrefix() + ' Send MeterValues error: %j', error);
- throw error;
- }
- }
-
- 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: string, commandParams, messageType = Constants.OCPP_JSON_CALL_RESULT_MESSAGE, commandName: string): 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;
- // Type of message
- switch (messageType) {
- // Request
- case Constants.OCPP_JSON_CALL_MESSAGE:
- // Build request
- this._requests[messageId] = [responseCallback, rejectCallback, commandParams];
- messageToSend = JSON.stringify([messageType, messageId, commandName, commandParams]);
- break;
- // Response
- case Constants.OCPP_JSON_CALL_RESULT_MESSAGE:
- // Build response
- messageToSend = JSON.stringify([messageType, messageId, commandParams]);
- break;
- // Error Message
- case Constants.OCPP_JSON_CALL_ERROR_MESSAGE:
- // Build Error Message
- messageToSend = JSON.stringify([messageType, messageId, commandParams.code ? commandParams.code : Constants.OCPP_ERROR_GENERIC_ERROR, commandParams.message ? commandParams.message : '', commandParams.details ? commandParams.details : {}]);
- break;
- }
- // Check if wsConnection is ready
- if (this._wsConnection?.readyState === WebSocket.OPEN) {
- if (this.getEnableStatistics()) {
- this._statistics.addMessage(commandName, messageType);
- }
- // Yes: Send Message
- this._wsConnection.send(messageToSend);
- } else {
- let dups = false;
- // Handle dups in buffer
- for (const message of this._messageQueue) {
- // Same message
- if (JSON.stringify(messageToSend) === JSON.stringify(message)) {
- dups = true;
- break;
- }
- }
- if (!dups) {
- // Buffer message
- this._messageQueue.push(messageToSend);
- }
- // Reject it
- return rejectCallback(new OCPPError(commandParams.code ? commandParams.code : Constants.OCPP_ERROR_GENERIC_ERROR, commandParams.message ? commandParams.message : `WebSocket closed for message id '${messageId}' with content '${messageToSend}', message buffered`, commandParams.details ? commandParams.details : {}));
- }
- // Response?
- if (messageType === Constants.OCPP_JSON_CALL_RESULT_MESSAGE) {
- // Yes: send Ok
- resolve();
- } else if (messageType === Constants.OCPP_JSON_CALL_ERROR_MESSAGE) {
- // Send timeout
- setTimeout(() => rejectCallback(new OCPPError(commandParams.code ? commandParams.code : Constants.OCPP_ERROR_GENERIC_ERROR, commandParams.message ? commandParams.message : `Timeout for message id '${messageId}' with content '${messageToSend}'`, commandParams.details ? commandParams.details : {})), Constants.OCPP_SOCKET_TIMEOUT);
- }
-
- // Function that will receive the request's response
- async function responseCallback(payload, requestPayload): Promise<void> {
- if (self.getEnableStatistics()) {
- self._statistics.addMessage(commandName, messageType);
- }
- // Send the response
- await self.handleResponse(commandName, payload, requestPayload);
- resolve(payload);
- }
-
- // Function that will receive the request's rejection
- function rejectCallback(error: OCPPError): void {
- if (self.getEnableStatistics()) {
- self._statistics.addMessage(commandName, messageType);
- }
- 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
- // Send error
- reject(error);
- }
- });
- }
-
- async handleResponse(commandName: string, payload, requestPayload): Promise<void> {
- const responseCallbackFn = 'handleResponse' + commandName;
- if (typeof this[responseCallbackFn] === 'function') {
- await this[responseCallbackFn](payload, requestPayload);
- } else {
- logger.error(this._logPrefix() + ' Trying to call an undefined response callback function: ' + responseCallbackFn);
- }
- }
-
- 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._hasStopped && (this._hasStopped = false);
- } else if (payload.status === RegistrationStatus.PENDING) {
- logger.info(this._logPrefix() + ' Charging station in pending state on the central server');
- } else {
- logger.info(this._logPrefix() + ' Charging station rejected by the central server');