Add Hearbeat command to OCPP 2.0.1
authorJérôme Benoit <jerome.benoit@sap.com>
Mon, 9 Jan 2023 21:46:56 +0000 (22:46 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Mon, 9 Jan 2023 21:46:56 +0000 (22:46 +0100)
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
src/charging-station/ocpp/2.0/OCPP20IncomingRequestService.ts
src/charging-station/ocpp/2.0/OCPP20RequestService.ts
src/charging-station/ocpp/2.0/OCPP20ResponseService.ts
src/types/ocpp/2.0/Requests.ts
src/types/ocpp/2.0/Responses.ts

index ed4f1fe1cf53a51e5ae2ed985dcf700c93967bc1..08fa5d2ebb4381755d8b12fe7221c1d650bd7ee4 100644 (file)
@@ -9,7 +9,7 @@ import type { JSONSchemaType } from 'ajv';
 import OCPPError from '../../../exception/OCPPError';
 import type { JsonObject, JsonType } from '../../../types/JsonType';
 import {
-  OCPP20ClearCacheRequest,
+  type OCPP20ClearCacheRequest,
   OCPP20IncomingRequestCommand,
 } from '../../../types/ocpp/2.0/Requests';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
@@ -60,9 +60,9 @@ export default class OCPP20IncomingRequestService extends OCPPIncomingRequestSer
     let response: JsonType;
     if (
       chargingStation.getOcppStrictCompliance() === true &&
-      chargingStation.isInPendingState() === true /* &&
-       (commandName === OCPP20IncomingRequestCommand.REMOTE_START_TRANSACTION ||
-        commandName === OCPP20IncomingRequestCommand.REMOTE_STOP_TRANSACTION ) */
+      chargingStation.isInPendingState() === true &&
+      (commandName === OCPP20IncomingRequestCommand.REQUEST_START_TRANSACTION ||
+        commandName === OCPP20IncomingRequestCommand.REQUEST_STOP_TRANSACTION)
     ) {
       throw new OCPPError(
         ErrorType.SECURITY_ERROR,
index bc056bef13541e3b00f3cde9af16371ab6140765..3e54ca50326c4bde179674662b50bfbe5ed6d6bb 100644 (file)
@@ -10,6 +10,7 @@ import OCPPError from '../../../exception/OCPPError';
 import type { JsonObject, JsonType } from '../../../types/JsonType';
 import {
   type OCPP20BootNotificationRequest,
+  type OCPP20HeartbeatRequest,
   OCPP20RequestCommand,
 } from '../../../types/ocpp/2.0/Requests';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
@@ -44,6 +45,18 @@ export default class OCPP20RequestService extends OCPPRequestService {
           )
         ) as JSONSchemaType<OCPP20BootNotificationRequest>,
       ],
+      [
+        OCPP20RequestCommand.HEARTBEAT,
+        JSON.parse(
+          fs.readFileSync(
+            path.resolve(
+              path.dirname(fileURLToPath(import.meta.url)),
+              '../../../assets/json-schemas/ocpp/2.0/HeartbeatRequest.json'
+            ),
+            'utf8'
+          )
+        ) as JSONSchemaType<OCPP20HeartbeatRequest>,
+      ],
     ]);
     this.buildRequestPayload.bind(this);
   }
@@ -110,6 +123,8 @@ export default class OCPP20RequestService extends OCPPRequestService {
             }),
           },
         } as unknown as Request;
+      case OCPP20RequestCommand.HEARTBEAT:
+        return {} as unknown as Request;
       default:
         // OCPPError usage here is debatable: it's an error in the OCPP stack but not targeted to sendError().
         throw new OCPPError(
index ef83ad4376c1e1d4fdecb99d04cb208b8b940242..15b355a5b0862e1de4e851e84d36070c376663d4 100644 (file)
@@ -15,6 +15,7 @@ import {
 import type {
   OCPP20BootNotificationResponse,
   OCPP20ClearCacheResponse,
+  OCPP20HeartbeatResponse,
 } from '../../../types/ocpp/2.0/Responses';
 import { ErrorType } from '../../../types/ocpp/ErrorType';
 import { OCPPVersion } from '../../../types/ocpp/OCPPVersion';
@@ -42,6 +43,7 @@ export default class OCPP20ResponseService extends OCPPResponseService {
     super(OCPPVersion.VERSION_20);
     this.responseHandlers = new Map<OCPP20RequestCommand, ResponseHandler>([
       [OCPP20RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)],
+      [OCPP20RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)],
     ]);
     this.jsonSchemas = new Map<OCPP20RequestCommand, JSONSchemaType<JsonObject>>([
       [
@@ -56,6 +58,18 @@ export default class OCPP20ResponseService extends OCPPResponseService {
           )
         ) as JSONSchemaType<OCPP20BootNotificationResponse>,
       ],
+      [
+        OCPP20RequestCommand.HEARTBEAT,
+        JSON.parse(
+          fs.readFileSync(
+            path.resolve(
+              path.dirname(fileURLToPath(import.meta.url)),
+              '../../../assets/json-schemas/ocpp/2.0/HeartbeatResponse.json'
+            ),
+            'utf8'
+          )
+        ) as JSONSchemaType<OCPP20HeartbeatResponse>,
+      ],
     ]);
     this.jsonIncomingRequestResponseSchemas = new Map([
       [
index 00826ab2204f34f93cdd6ca91e7cce9343104257..72ad392eace7838e6e00295ee55189e4d6319345 100644 (file)
@@ -8,6 +8,8 @@ export enum OCPP20RequestCommand {
 
 export enum OCPP20IncomingRequestCommand {
   CLEAR_CACHE = 'ClearCache',
+  REQUEST_START_TRANSACTION = 'RequestStartTransaction',
+  REQUEST_STOP_TRANSACTION = 'RequestStopTransaction',
 }
 
 export enum BootReasonEnumType {
@@ -40,4 +42,6 @@ export type OCPP20BootNotificationRequest = {
   chargingStation: ChargingStationType;
 } & JsonObject;
 
+export type OCPP20HeartbeatRequest = EmptyObject;
+
 export type OCPP20ClearCacheRequest = EmptyObject;
index 8fdfe89bb69e62e996a68d013b49f45f988a5ad0..b83f0c84369eefbcdced173c26941294691cdd1f 100644 (file)
@@ -13,6 +13,10 @@ export type OCPP20BootNotificationResponse = {
   statusInfo?: StatusInfoType;
 } & JsonObject;
 
+export type OCPP20HeartbeatResponse = {
+  currentTime: Date;
+} & JsonObject;
+
 export type OCPP20ClearCacheResponse = {
   status: DefaultStatus;
   statusInfo?: StatusInfoType;