From 290d006c2d91dce4e9d187189c3e4fae870647f7 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Fri, 16 Jul 2021 12:05:29 +0200 Subject: [PATCH] Improve and fix error handling at sending OCPP commands MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- package-lock.json | 74 +++++++++---------- package.json | 6 +- src/charging-station/ChargingStation.ts | 4 +- .../ocpp/1.6/OCCP16IncomingRequestService.ts | 10 +-- .../ocpp/1.6/OCPP16RequestService.ts | 56 +++++++++----- .../ocpp/1.6/OCPP16ResponseService.ts | 4 + .../ocpp/1.6/OCPP16ServiceUtils.ts | 12 +-- src/worker/WorkerSet.ts | 2 +- 8 files changed, 94 insertions(+), 74 deletions(-) diff --git a/package-lock.json b/package-lock.json index d4275beb..3044e93f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1925,13 +1925,13 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.2.tgz", - "integrity": "sha512-PGqpLLzHSxq956rzNGasO3GsAPf2lY9lDUBXhS++SKonglUmJypaUtcKzRtUte8CV7nruwnDxtLUKpVxs0wQBw==", + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.3.tgz", + "integrity": "sha512-jW8sEFu1ZeaV8xzwsfi6Vgtty2jf7/lJmQmDkDruBjYAbx5DA8JtbcMnP0rNPUG+oH5GoQBTSp+9613BzuIpYg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.28.2", - "@typescript-eslint/scope-manager": "4.28.2", + "@typescript-eslint/experimental-utils": "4.28.3", + "@typescript-eslint/scope-manager": "4.28.3", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.1.0", @@ -1981,15 +1981,15 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.2.tgz", - "integrity": "sha512-MwHPsL6qo98RC55IoWWP8/opTykjTp4JzfPu1VfO2Z0MshNP0UZ1GEV5rYSSnZSUI8VD7iHvtIPVGW5Nfh7klQ==", + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.3.tgz", + "integrity": "sha512-zZYl9TnrxwEPi3FbyeX0ZnE8Hp7j3OCR+ELoUfbwGHGxWnHg9+OqSmkw2MoCVpZksPCZYpQzC559Ee9pJNHTQw==", "dev": true, "requires": { "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.28.2", - "@typescript-eslint/types": "4.28.2", - "@typescript-eslint/typescript-estree": "4.28.2", + "@typescript-eslint/scope-manager": "4.28.3", + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/typescript-estree": "4.28.3", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -2006,14 +2006,14 @@ } }, "@typescript-eslint/parser": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.2.tgz", - "integrity": "sha512-Q0gSCN51eikAgFGY+gnd5p9bhhCUAl0ERMiDKrTzpSoMYRubdB8MJrTTR/BBii8z+iFwz8oihxd0RAdP4l8w8w==", + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.3.tgz", + "integrity": "sha512-ZyWEn34bJexn/JNYvLQab0Mo5e+qqQNhknxmc8azgNd4XqspVYR5oHq9O11fLwdZMRcj4by15ghSlIEq+H5ltQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.28.2", - "@typescript-eslint/types": "4.28.2", - "@typescript-eslint/typescript-estree": "4.28.2", + "@typescript-eslint/scope-manager": "4.28.3", + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/typescript-estree": "4.28.3", "debug": "^4.3.1" }, "dependencies": { @@ -2035,29 +2035,29 @@ } }, "@typescript-eslint/scope-manager": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.2.tgz", - "integrity": "sha512-MqbypNjIkJFEFuOwPWNDjq0nqXAKZvDNNs9yNseoGBB1wYfz1G0WHC2AVOy4XD7di3KCcW3+nhZyN6zruqmp2A==", + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.3.tgz", + "integrity": "sha512-/8lMisZ5NGIzGtJB+QizQ5eX4Xd8uxedFfMBXOKuJGP0oaBBVEMbJVddQKDXyyB0bPlmt8i6bHV89KbwOelJiQ==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.2", - "@typescript-eslint/visitor-keys": "4.28.2" + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/visitor-keys": "4.28.3" } }, "@typescript-eslint/types": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.2.tgz", - "integrity": "sha512-Gr15fuQVd93uD9zzxbApz3wf7ua3yk4ZujABZlZhaxxKY8ojo448u7XTm/+ETpy0V0dlMtj6t4VdDvdc0JmUhA==", + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.3.tgz", + "integrity": "sha512-kQFaEsQBQVtA9VGVyciyTbIg7S3WoKHNuOp/UF5RG40900KtGqfoiETWD/v0lzRXc+euVE9NXmfer9dLkUJrkA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.2.tgz", - "integrity": "sha512-86lLstLvK6QjNZjMoYUBMMsULFw0hPHJlk1fzhAVoNjDBuPVxiwvGuPQq3fsBMCxuDJwmX87tM/AXoadhHRljg==", + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.3.tgz", + "integrity": "sha512-YAb1JED41kJsqCQt1NcnX5ZdTA93vKFCMP4lQYG6CFxd0VzDJcKttRlMrlG+1qiWAw8+zowmHU1H0OzjWJzR2w==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.2", - "@typescript-eslint/visitor-keys": "4.28.2", + "@typescript-eslint/types": "4.28.3", + "@typescript-eslint/visitor-keys": "4.28.3", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", @@ -2127,12 +2127,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.2.tgz", - "integrity": "sha512-aT2B4PLyyRDUVUafXzpZFoc0C9t0za4BJAKP5sgWIhG+jHECQZUEjuQSCIwZdiJJ4w4cgu5r3Kh20SOdtEBl0w==", + "version": "4.28.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.3.tgz", + "integrity": "sha512-ri1OzcLnk1HH4gORmr1dllxDzzrN6goUIz/P4MHFV0YZJDCADPR3RvYNp0PW2SetKTThar6wlbFTL00hV2Q+fg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.2", + "@typescript-eslint/types": "4.28.3", "eslint-visitor-keys": "^2.0.0" } }, @@ -13450,9 +13450,9 @@ } }, "rollup": { - "version": "2.53.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.1.tgz", - "integrity": "sha512-yiTCvcYXZEulNWNlEONOQVlhXA/hgxjelFSjNcrwAAIfYx/xqjSHwqg/cCaWOyFRKr+IQBaXwt723m8tCaIUiw==", + "version": "2.53.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.53.2.tgz", + "integrity": "sha512-1CtEYuS5CRCzFZ7SNW5528SlDlk4VDXIRGwbm/2POQxA/G4+7/crIqJwkmnj8Q/74hGx4oVlNvh4E1CJQ5hZ6w==", "dev": true, "requires": { "fsevents": "~2.3.2" diff --git a/package.json b/package.json index 9faf8ff9..e9281bb4 100644 --- a/package.json +++ b/package.json @@ -81,8 +81,8 @@ "@types/tar": "^4.0.5", "@types/uuid": "^8.3.1", "@types/ws": "^7.4.6", - "@typescript-eslint/eslint-plugin": "^4.28.2", - "@typescript-eslint/parser": "^4.28.2", + "@typescript-eslint/eslint-plugin": "^4.28.3", + "@typescript-eslint/parser": "^4.28.3", "auto-changelog": "^2.3.0", "clinic": "^9.0.0", "cross-env": "^7.0.3", @@ -98,7 +98,7 @@ "nyc": "^15.1.0", "release-it": "^14.10.0", "robohydra": "^0.6.9", - "rollup": "^2.53.1", + "rollup": "^2.53.2", "rollup-plugin-analyzer": "^4.0.0", "rollup-plugin-copy": "^3.4.0", "rollup-plugin-delete": "^2.0.0", diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index c1b51ce2..bd25116b 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -140,7 +140,7 @@ export default class ChargingStation { break; default: logger.error(errMsg); - throw Error(errMsg); + throw new Error(errMsg); } return !Utils.isUndefined(this.stationInfo.voltageOut) ? this.stationInfo.voltageOut : defaultVoltageOut; } @@ -228,7 +228,7 @@ export default class ChargingStation { } const sampledValueTemplates: SampledValueTemplate[] = this.getConnector(connectorId).MeterValues; for (let index = 0; !Utils.isEmptyArray(sampledValueTemplates) && index < sampledValueTemplates.length; index++) { - if (!Constants.SUPPORTED_MEASURANDS.includes(sampledValueTemplates[index]?.measurand)) { + if (!Constants.SUPPORTED_MEASURANDS.includes(sampledValueTemplates[index]?.measurand ?? MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER)) { logger.warn(`${this.logPrefix()} Unsupported MeterValues measurand ${measurand} ${phase ? `on phase ${phase} ` : ''}in template on connectorId ${connectorId}`); continue; } else if (phase && sampledValueTemplates[index]?.phase === phase && sampledValueTemplates[index]?.measurand === measurand diff --git a/src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts index 4a0e82d6..58814495 100644 --- a/src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCCP16IncomingRequestService.ts @@ -4,12 +4,12 @@ import { ChangeAvailabilityRequest, ChangeConfigurationRequest, ClearChargingPro import { ChangeAvailabilityResponse, ChangeConfigurationResponse, ClearChargingProfileResponse, GetConfigurationResponse, GetDiagnosticsResponse, SetChargingProfileResponse, UnlockConnectorResponse } from '../../../types/ocpp/1.6/Responses'; import { ChargingProfilePurposeType, OCPP16ChargingProfile } from '../../../types/ocpp/1.6/ChargingProfile'; import { Client, FTPResponse } from 'basic-ftp'; +import { IncomingRequestCommand, RequestCommand } from '../../../types/ocpp/Requests'; import { OCPP16AuthorizationStatus, OCPP16StopTransactionReason } from '../../../types/ocpp/1.6/Transaction'; import Constants from '../../../utils/Constants'; import { DefaultResponse } from '../../../types/ocpp/Responses'; import { ErrorType } from '../../../types/ocpp/ErrorType'; -import { IncomingRequestCommand } from '../../../types/ocpp/Requests'; import { MessageType } from '../../../types/ocpp/MessageType'; import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus'; import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus'; @@ -132,10 +132,10 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer private handleRequestChangeConfiguration(commandPayload: ChangeConfigurationRequest): ChangeConfigurationResponse { // JSON request fields type sanity check if (!Utils.isString(commandPayload.key)) { - logger.error(`${this.chargingStation.logPrefix()} ChangeConfiguration request key field is not a string:`, commandPayload); + logger.error(`${this.chargingStation.logPrefix()} ${RequestCommand.CHANGE_CONFIGURATION} request key field is not a string:`, commandPayload); } if (!Utils.isString(commandPayload.value)) { - logger.error(`${this.chargingStation.logPrefix()} ChangeConfiguration request value field is not a string:`, commandPayload); + logger.error(`${this.chargingStation.logPrefix()} ${RequestCommand.CHANGE_CONFIGURATION} request value field is not a string:`, commandPayload); } const keyToChange = this.chargingStation.getConfigurationKey(commandPayload.key, true); if (!keyToChange) { @@ -380,9 +380,9 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer } return { fileName: diagnosticsArchive }; } - throw Error(`Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`); + throw new Error(`Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`); } - throw Error(`Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`); + throw new Error(`Diagnostics transfer failed with error code ${accessResponse.code.toString()}${uploadResponse?.code && '|' + uploadResponse?.code.toString()}`); } catch (error) { await this.chargingStation.ocppRequestService.sendDiagnosticsStatusNotification(OCPP16DiagnosticsStatus.UploadFailed); this.handleIncomingRequestError(IncomingRequestCommand.GET_DIAGNOSTICS, error); diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index 7302f94c..b19e3192 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -216,7 +216,7 @@ export default class OCPP16RequestService extends OCPPRequestService { break; default: logger.error(errMsg); - throw Error(errMsg); + throw new Error(errMsg); } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(powerSampledValueTemplate, powerMeasurandValues.allPhases)); const sampledValuesIndex = meterValue.sampledValue.length - 1; @@ -282,7 +282,7 @@ export default class OCPP16RequestService extends OCPPRequestService { break; default: logger.error(errMsg); - throw Error(errMsg); + throw new Error(errMsg); } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases)); const sampledValuesIndex = meterValue.sampledValue.length - 1; @@ -338,32 +338,48 @@ export default class OCPP16RequestService extends OCPPRequestService { } public async sendTransactionBeginMeterValues(connectorId: number, transactionId: number, beginMeterValue: OCPP16MeterValue): Promise { - const payload: MeterValuesRequest = { - connectorId, - transactionId, - meterValue: beginMeterValue, - }; - await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); + try { + const payload: MeterValuesRequest = { + connectorId, + transactionId, + meterValue: beginMeterValue, + }; + await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); + } catch (error) { + this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); + } } public async sendTransactionEndMeterValues(connectorId: number, transactionId: number, endMeterValue: OCPP16MeterValue): Promise { - const payload: MeterValuesRequest = { - connectorId, - transactionId, - meterValue: endMeterValue, - }; - await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); + try { + const payload: MeterValuesRequest = { + connectorId, + transactionId, + meterValue: endMeterValue, + }; + await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); + } catch (error) { + this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); + } } public async sendDiagnosticsStatusNotification(diagnosticsStatus: OCPP16DiagnosticsStatus): Promise { - const payload: DiagnosticsStatusNotificationRequest = { - status: diagnosticsStatus - }; - await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION); + try { + const payload: DiagnosticsStatusNotificationRequest = { + status: diagnosticsStatus + }; + await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION); + } catch (error) { + this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); + } } public async sendError(messageId: string, error: OCPPError, commandName: OCPP16RequestCommand | OCPP16IncomingRequestCommand): Promise { - // Send error - return this.sendMessage(messageId, error, MessageType.CALL_ERROR_MESSAGE, commandName); + try { + // Send error + return this.sendMessage(messageId, error, MessageType.CALL_ERROR_MESSAGE, commandName); + } catch (err) { + this.handleRequestError(commandName as OCPP16RequestCommand, err); + } } } diff --git a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts index 0bfef77e..d12acffc 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ResponseService.ts @@ -54,6 +54,10 @@ export default class OCPP16ResponseService extends OCPPResponseService { logger.debug(this.chargingStation.logPrefix() + ' Trying to start a transaction on an already used connector ' + connectorId.toString() + ': %j', this.chargingStation.getConnector(connectorId)); return; } + if (this.chargingStation.getConnector(connectorId)?.status !== OCPP16ChargePointStatus.AVAILABLE) { + logger.error(`${this.chargingStation.logPrefix()} Trying to start a transaction on connector ${connectorId.toString()} with status ${this.chargingStation.getConnector(connectorId)?.status}`); + return; + } if (payload.idTagInfo?.status === OCPP16AuthorizationStatus.ACCEPTED) { this.chargingStation.getConnector(connectorId).transactionStarted = true; diff --git a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts index 34a65de8..ead2558a 100644 --- a/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts +++ b/src/charging-station/ocpp/1.6/OCPP16ServiceUtils.ts @@ -10,19 +10,19 @@ export class OCPP16ServiceUtils { if (Utils.isUndefined(chargingStation.stationInfo.powerDivider)) { const errMsg = `${chargingStation.logPrefix()} MeterValues measurand ${measurandType ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: powerDivider is undefined`; logger.error(errMsg); - throw Error(errMsg); + throw new Error(errMsg); } 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}`; logger.error(errMsg); - throw Error(errMsg); + throw new Error(errMsg); } } 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); + 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 }, diff --git a/src/worker/WorkerSet.ts b/src/worker/WorkerSet.ts index 21f9f482..2ccd2796 100644 --- a/src/worker/WorkerSet.ts +++ b/src/worker/WorkerSet.ts @@ -33,7 +33,7 @@ export default class WorkerSet extends WorkerAbstract { */ public async addElement(elementData: T): Promise { if (!this.workerSet) { - throw 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.maxElementsPerWorker) { this.startWorker(); -- 2.34.1