Improve OCPP request's response error handling
authorJérôme Benoit <jerome.benoit@sap.com>
Thu, 7 Oct 2021 22:22:59 +0000 (00:22 +0200)
committerJérôme Benoit <jerome.benoit@sap.com>
Thu, 7 Oct 2021 22:22:59 +0000 (00:22 +0200)
Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
src/charging-station/Bootstrap.ts
src/charging-station/ocpp/1.6/OCPP16ResponseService.ts
src/charging-station/ocpp/OCPPRequestService.ts

index 809c623ab51062db24812fefac5f780be76e63ab..4f49a3d9ab06565fb21e9c89ad67e1456ac880e3 100644 (file)
@@ -15,9 +15,9 @@ import { version } from '../../package.json';
 
 export default class Bootstrap {
   private static instance: Bootstrap | null = null;
-  private static workerImplementation: WorkerAbstract | null = null;
-  private static storage: Storage;
-  private static numberOfChargingStations: number;
+  private workerImplementation: WorkerAbstract | null = null;
+  private readonly storage: Storage;
+  private numberOfChargingStations: number;
   private readonly version: string = version;
   private started: boolean;
   private readonly workerScript: string;
@@ -26,7 +26,7 @@ export default class Bootstrap {
     this.started = false;
     this.workerScript = path.join(path.resolve(__dirname, '../'), 'charging-station', 'ChargingStationWorker.js');
     this.initWorkerImplementation();
-    Bootstrap.storage = StorageFactory.getStorage(Configuration.getPerformanceStorage().type, Configuration.getPerformanceStorage().URI, this.logPrefix());
+    this.storage = StorageFactory.getStorage(Configuration.getPerformanceStorage().type, Configuration.getPerformanceStorage().URI, this.logPrefix());
     Configuration.setConfigurationChangeCallback(async () => Bootstrap.getInstance().restart());
   }
 
@@ -40,9 +40,9 @@ export default class Bootstrap {
   public async start(): Promise<void> {
     if (isMainThread && !this.started) {
       try {
-        Bootstrap.numberOfChargingStations = 0;
-        await Bootstrap.storage.open();
-        await Bootstrap.workerImplementation.start();
+        this.numberOfChargingStations = 0;
+        await this.storage.open();
+        await this.workerImplementation.start();
         // Start ChargingStation object in worker thread
         if (Configuration.getStationTemplateURLs()) {
           for (const stationURL of Configuration.getStationTemplateURLs()) {
@@ -53,8 +53,8 @@ export default class Bootstrap {
                   index,
                   templateFile: path.join(path.resolve(__dirname, '../'), 'assets', 'station-templates', path.basename(stationURL.file))
                 };
-                await Bootstrap.workerImplementation.addElement(workerData);
-                Bootstrap.numberOfChargingStations++;
+                await this.workerImplementation.addElement(workerData);
+                this.numberOfChargingStations++;
               }
             } catch (error) {
               console.error(chalk.red('Charging station start with template file ' + stationURL.file + ' error '), error);
@@ -63,10 +63,10 @@ export default class Bootstrap {
         } else {
           console.warn(chalk.yellow('No stationTemplateURLs defined in configuration, exiting'));
         }
-        if (Bootstrap.numberOfChargingStations === 0) {
+        if (this.numberOfChargingStations === 0) {
           console.warn(chalk.yellow('No charging station template enabled in configuration, exiting'));
         } else {
-          console.log(chalk.green(`Charging stations simulator ${this.version} started with ${Bootstrap.numberOfChargingStations.toString()} charging station(s) and ${Utils.workerDynamicPoolInUse() ? `${Configuration.getWorkerPoolMinSize().toString()}/` : ''}${Bootstrap.workerImplementation.size}${Utils.workerPoolInUse() ? `/${Configuration.getWorkerPoolMaxSize().toString()}` : ''} worker(s) concurrently running in '${Configuration.getWorkerProcess()}' mode${Bootstrap.workerImplementation.maxElementsPerWorker ? ` (${Bootstrap.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) {
@@ -79,8 +79,8 @@ export default class Bootstrap {
 
   public async stop(): Promise<void> {
     if (isMainThread && this.started) {
-      await Bootstrap.workerImplementation.stop();
-      await Bootstrap.storage.close();
+      await this.workerImplementation.stop();
+      await this.storage.close();
     } else {
       console.error(chalk.red('Trying to stop the charging stations simulator while not started'));
     }
@@ -94,7 +94,7 @@ export default class Bootstrap {
   }
 
   private initWorkerImplementation(): void {
-    Bootstrap.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(this.workerScript, Configuration.getWorkerProcess(),
+    this.workerImplementation = WorkerFactory.getWorkerImplementation<ChargingStationWorkerData>(this.workerScript, Configuration.getWorkerProcess(),
       {
         startDelay: Configuration.getWorkerStartDelay(),
         poolMaxSize: Configuration.getWorkerPoolMaxSize(),
@@ -105,7 +105,7 @@ export default class Bootstrap {
         },
         messageHandler: async (msg: ChargingStationWorkerMessage) => {
           if (msg.id === ChargingStationWorkerMessageEvents.PERFORMANCE_STATISTICS) {
-            await Bootstrap.storage.storePerformanceStatistics(msg.data);
+            await this.storage.storePerformanceStatistics(msg.data);
           }
         }
       });
index 2c2b80488954520555decfa8fa7eaedd156f0387..3e63c84ac90e6c128f614a20225adaaf148b30d0 100644 (file)
@@ -6,9 +6,11 @@ import { HeartbeatResponse, OCPP16BootNotificationResponse, OCPP16RegistrationSt
 import { MeterValuesRequest, MeterValuesResponse } from '../../../types/ocpp/1.6/MeterValues';
 
 import ChargingStation from '../../ChargingStation';
+import { ErrorType } from '../../../types/ocpp/ErrorType';
 import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus';
 import { OCPP16ServiceUtils } from './OCPP16ServiceUtils';
 import { OCPP16StandardParametersKey } from '../../../types/ocpp/1.6/Configuration';
+import OCPPError from '../OCPPError';
 import OCPPResponseService from '../OCPPResponseService';
 import { ResponseHandler } from '../../../types/ocpp/Responses';
 import Utils from '../../../utils/Utils';
@@ -36,9 +38,11 @@ export default class OCPP16ResponseService extends OCPPResponseService {
         await this.responseHandlers.get(commandName)(payload, requestPayload);
       } catch (error) {
         logger.error(this.chargingStation.logPrefix() + ' Handle request response error: %j', error);
+        throw error;
       }
     } else {
-      logger.error(`${this.chargingStation.logPrefix()} %s is not implemented to handle request response payload %j`, commandName, payload);
+      // Throw exception
+      throw new OCPPError(ErrorType.NOT_IMPLEMENTED, `${commandName} is not implemented to handle request response payload ${JSON.stringify(payload, null, 2)}`, commandName);
     }
   }
 
index 5d767f0cb0c6726e432d7fc7faf28a733a0b6293..2076a2964a061b3c4767208d973e872cf0558d97 100644 (file)
@@ -70,10 +70,16 @@ export default abstract class OCPPRequestService {
         if (self.chargingStation.getEnableStatistics()) {
           self.chargingStation.performanceStatistics.addRequestStatistic(commandName, MessageType.CALL_RESULT_MESSAGE);
         }
-        // Send the response
-        await self.ocppResponseService.handleResponse(commandName as RequestCommand, payload, requestPayload);
-        self.chargingStation.requests.delete(messageId);
-        resolve(payload);
+        // 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);
+        }
       }
 
       /**