Add trigger message type feature flag in charging station template
authorJérôme Benoit <jerome.benoit@sap.com>
Sat, 5 Nov 2022 21:37:33 +0000 (22:37 +0100)
committerJérôme Benoit <jerome.benoit@sap.com>
Sat, 5 Nov 2022 21:37:33 +0000 (22:37 +0100)
Close #257

Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
README.md
src/assets/station-templates/chargex.station-template.json
src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts
src/charging-station/ocpp/OCPPServiceUtils.ts
src/types/ChargingStationTemplate.ts
src/types/ocpp/1.6/Requests.ts
src/types/ocpp/Requests.ts
ui/web/src/types/ChargingStationType.ts

index 7bdb708667baf9543ded62b4276d3ce07fa3b71a..562a1ba493d13b1c4d4e5068756dd29c26efe79b 100644 (file)
--- a/README.md
+++ b/README.md
@@ -170,7 +170,8 @@ But the modifications to test have to be done to the files in the build target d
 | mainVoltageMeterValues             | true/false | true                                                              | boolean                                                                                                                            | include charging stations main voltage MeterValues on three phased charging stations                                                                                                                  |
 | phaseLineToLineVoltageMeterValues  | true/false | true                                                              | boolean                                                                                                                            | include charging stations line to line voltage MeterValues on three phased charging stations                                                                                                          |
 | customValueLimitationMeterValues   | true/false | true                                                              | boolean                                                                                                                            | enable limitation on custom fluctuated value in MeterValues                                                                                                                                           |
-| commandsSupport                    |            | {<br />"incomingCommands": {},<br />"outgoingCommands": {}<br />} | {<br /> incomingCommands: Record<IncomingRequestCommand, boolean>;<br />outgoingCommands?: Record<RequestCommand, boolean>;<br />} | Configuration section for OCPP commands support. Empty section or subsections means all implemented commands are supported                                                                            |
+| commandsSupport                    |            | {<br />"incomingCommands": {},<br />"outgoingCommands": {}<br />} | {<br /> incomingCommands: Record<IncomingRequestCommand, boolean>;<br />outgoingCommands?: Record<RequestCommand, boolean>;<br />} | Configuration section for OCPP commands support. Empty section or subsections means all implemented OCPP commands are supported                                                                       |
+| messageTriggerSupport              |            | {}                                                                | Record<MessageTrigger, boolean>                                                                                                    | Configuration section for OCPP commands trigger support. Empty section means all implemented OCPP trigger commands are supported                                                                      |
 | Configuration                      |            |                                                                   | ChargingStationConfiguration                                                                                                       | charging stations OCPP parameters configuration section                                                                                                                                               |
 | AutomaticTransactionGenerator      |            |                                                                   | AutomaticTransactionGenerator                                                                                                      | charging stations ATG configuration section                                                                                                                                                           |
 | Connectors                         |            |                                                                   | Connectors                                                                                                                         | charging stations connectors configuration section                                                                                                                                                    |
index 2630278b0b4bd9ca8a3aefa775b2c7ac71dbdb01..39cc58046effaecf49a44226264dfeb22d4334ad 100644 (file)
       "TriggerMessage": false
     }
   },
+  "messageTriggerSupport": {
+    "BootNotification": true,
+    "Heartbeat": true,
+    "StatusNotification": false
+  },
   "Configuration": {
     "configurationKey": [
       {
index aecc08f786ccfe0d119260cfd2bb498688008230..0af87023faf37e425e6f975e80d9e7eab29e5c6f 100644 (file)
@@ -28,12 +28,12 @@ import {
   DiagnosticsStatusNotificationRequest,
   GetConfigurationRequest,
   GetDiagnosticsRequest,
-  MessageTrigger,
   OCPP16AvailabilityType,
   OCPP16BootNotificationRequest,
   OCPP16ClearCacheRequest,
   OCPP16HeartbeatRequest,
   OCPP16IncomingRequestCommand,
+  OCPP16MessageTrigger,
   OCPP16RequestCommand,
   OCPP16StatusNotificationRequest,
   OCPP16TriggerMessageRequest,
@@ -413,9 +413,15 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
     commandPayload: UnlockConnectorRequest
   ): Promise<UnlockConnectorResponse> {
     const connectorId = commandPayload.connectorId;
+    if (chargingStation.connectors.has(connectorId) === false) {
+      logger.error(
+        `${chargingStation.logPrefix()} Trying to unlock a non existing connector Id ${connectorId.toString()}`
+      );
+      return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
+    }
     if (connectorId === 0) {
       logger.error(
-        chargingStation.logPrefix() + ' Trying to unlock connector ' + connectorId.toString()
+        chargingStation.logPrefix() + ' Trying to unlock connector Id ' + connectorId.toString()
       );
       return Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
     }
@@ -686,7 +692,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
     commandPayload: ChangeAvailabilityRequest
   ): Promise<ChangeAvailabilityResponse> {
     const connectorId: number = commandPayload.connectorId;
-    if (!chargingStation.getConnectorStatus(connectorId)) {
+    if (chargingStation.connectors.has(connectorId) === false) {
       logger.error(
         `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
       );
@@ -1112,22 +1118,26 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
         chargingStation,
         OCPP16SupportedFeatureProfiles.RemoteTrigger,
         OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
+      ) ||
+      !OCPP16ServiceUtils.isMessageTriggerSupported(
+        chargingStation,
+        commandPayload.requestedMessage
       )
     ) {
       return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_NOT_IMPLEMENTED;
     }
-    // TODO: factor out the check on connector id
-    if (commandPayload?.connectorId < 0) {
-      logger.warn(
-        `${chargingStation.logPrefix()} ${
-          OCPP16IncomingRequestCommand.TRIGGER_MESSAGE
-        } incoming request received with invalid connectorId ${commandPayload.connectorId}`
-      );
+    if (
+      OCPP16ServiceUtils.isConnectorIdValid(
+        chargingStation,
+        OCPP16IncomingRequestCommand.TRIGGER_MESSAGE,
+        commandPayload.connectorId
+      )
+    ) {
       return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_REJECTED;
     }
     try {
       switch (commandPayload.requestedMessage) {
-        case MessageTrigger.BootNotification:
+        case OCPP16MessageTrigger.BootNotification:
           setTimeout(() => {
             chargingStation.ocppRequestService
               .requestHandler<OCPP16BootNotificationRequest, OCPP16BootNotificationResponse>(
@@ -1144,7 +1154,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
               });
           }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
           return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
-        case MessageTrigger.Heartbeat:
+        case OCPP16MessageTrigger.Heartbeat:
           setTimeout(() => {
             chargingStation.ocppRequestService
               .requestHandler<OCPP16HeartbeatRequest, OCPP16HeartbeatResponse>(
@@ -1160,7 +1170,7 @@ export default class OCPP16IncomingRequestService extends OCPPIncomingRequestSer
               });
           }, Constants.OCPP_TRIGGER_MESSAGE_DELAY);
           return Constants.OCPP_TRIGGER_MESSAGE_RESPONSE_ACCEPTED;
-        case MessageTrigger.StatusNotification:
+        case OCPP16MessageTrigger.StatusNotification:
           setTimeout(() => {
             if (commandPayload?.connectorId) {
               chargingStation.ocppRequestService
index ad78d347b965ddef05d06777388461cce61e6a2c..2fae9934fb9e29dcc5b08e66e289289f44f5ad35 100644 (file)
@@ -5,7 +5,7 @@ import type { SampledValueTemplate } from '../../types/MeasurandPerPhaseSampledV
 import { StandardParametersKey } from '../../types/ocpp/Configuration';
 import { ErrorType } from '../../types/ocpp/ErrorType';
 import { MeterValueMeasurand, type MeterValuePhase } from '../../types/ocpp/MeterValues';
-import { IncomingRequestCommand, RequestCommand } from '../../types/ocpp/Requests';
+import { IncomingRequestCommand, MessageTrigger, RequestCommand } from '../../types/ocpp/Requests';
 import Constants from '../../utils/Constants';
 import logger from '../../utils/Logger';
 import Utils from '../../utils/Utils';
@@ -73,6 +73,36 @@ export class OCPPServiceUtils {
     return false;
   }
 
+  public static isMessageTriggerSupported(
+    chargingStation: ChargingStation,
+    messageTrigger: MessageTrigger
+  ): boolean {
+    const isMessageTrigger = Object.values(MessageTrigger).includes(messageTrigger);
+    if (isMessageTrigger === true && !chargingStation.stationInfo?.messageTriggerSupport) {
+      return true;
+    } else if (isMessageTrigger === true && chargingStation.stationInfo?.messageTriggerSupport) {
+      return chargingStation.stationInfo?.messageTriggerSupport[messageTrigger] ?? false;
+    }
+    logger.error(
+      `${chargingStation.logPrefix()} Unknown incoming OCPP message trigger '${messageTrigger}'`
+    );
+    return false;
+  }
+
+  public static isConnectorIdValid(
+    chargingStation: ChargingStation,
+    ocppCommand: IncomingRequestCommand,
+    connectorId: number
+  ): boolean {
+    if (connectorId < 0) {
+      logger.error(
+        `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connectorId ${connectorId}`
+      );
+      return false;
+    }
+    return true;
+  }
+
   protected static getSampledValueTemplate(
     chargingStation: ChargingStation,
     connectorId: number,
index 0d063d51a05d0ce4f8d3d23facd9ca8c217de3b4..3945e8925f94ebbafd9ce5435b10b12466649e45 100644 (file)
@@ -7,7 +7,7 @@ import type { ChargingStationOcppConfiguration } from './ChargingStationOcppConf
 import type { ConnectorStatus } from './ConnectorStatus';
 import type { OCPPProtocol } from './ocpp/OCPPProtocol';
 import type { OCPPVersion } from './ocpp/OCPPVersion';
-import type { IncomingRequestCommand, RequestCommand } from './ocpp/Requests';
+import type { IncomingRequestCommand, MessageTrigger, RequestCommand } from './ocpp/Requests';
 
 export enum CurrentType {
   AC = 'AC',
@@ -93,6 +93,7 @@ export type ChargingStationTemplate = {
   phaseLineToLineVoltageMeterValues?: boolean;
   customValueLimitationMeterValues?: boolean;
   commandsSupport?: CommandsSupport;
+  messageTriggerSupport?: Record<MessageTrigger, boolean>;
   Configuration?: ChargingStationOcppConfiguration;
   AutomaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration;
   Connectors: Record<string, ConnectorStatus>;
index 263947c2d46f47d8dc4e0de9077ae75fa963f5ee..b2961a916804cafeb62d545d95c277ce7ccd2512 100644 (file)
@@ -124,7 +124,7 @@ export interface DiagnosticsStatusNotificationRequest extends JsonObject {
   status: OCPP16DiagnosticsStatus;
 }
 
-export enum MessageTrigger {
+export enum OCPP16MessageTrigger {
   BootNotification = 'BootNotification',
   DiagnosticsStatusNotification = 'DiagnosticsStatusNotification',
   FirmwareStatusNotification = 'FirmwareStatusNotification',
@@ -134,6 +134,6 @@ export enum MessageTrigger {
 }
 
 export interface OCPP16TriggerMessageRequest extends JsonObject {
-  requestedMessage: MessageTrigger;
+  requestedMessage: OCPP16MessageTrigger;
   connectorId?: number;
 }
index 052bfc6bc044e41bf8e6e8a7f1d8e78f24983db1..9934f1671292158bb5d4f3d47d03f95f3e7c361a 100644 (file)
@@ -8,6 +8,7 @@ import {
   OCPP16BootNotificationRequest,
   OCPP16HeartbeatRequest,
   OCPP16IncomingRequestCommand,
+  OCPP16MessageTrigger,
   OCPP16RequestCommand,
   OCPP16StatusNotificationRequest,
 } from './1.6/Requests';
@@ -41,6 +42,12 @@ export type CachedRequest = [
   JsonType
 ];
 
+export type MessageTrigger = OCPP16MessageTrigger;
+
+export const MessageTrigger = {
+  ...OCPP16MessageTrigger,
+};
+
 export type BootNotificationRequest = OCPP16BootNotificationRequest;
 
 export type HeartbeatRequest = OCPP16HeartbeatRequest;
index a3f9931c2387cdcee226dcdddfb3d5d6c27dad4a..929f40e3c886881f1e51d2d7665d9799285daf50 100644 (file)
@@ -67,6 +67,7 @@ export type ChargingStationInfo = {
   phaseLineToLineVoltageMeterValues?: boolean;
   customValueLimitationMeterValues?: boolean;
   commandsSupport?: CommandsSupport;
+  messageTriggerSupport?: Record<MessageTrigger, boolean>;
 };
 
 export enum OCPP16IncomingRequestCommand {
@@ -121,6 +122,21 @@ export interface OCPP16BootNotificationResponse extends JsonObject {
   interval: number;
 }
 
+export enum OCPP16MessageTrigger {
+  BootNotification = 'BootNotification',
+  DiagnosticsStatusNotification = 'DiagnosticsStatusNotification',
+  FirmwareStatusNotification = 'FirmwareStatusNotification',
+  Heartbeat = 'Heartbeat',
+  MeterValues = 'MeterValues',
+  StatusNotification = 'StatusNotification',
+}
+
+export type MessageTrigger = OCPP16MessageTrigger;
+
+export const MessageTrigger = {
+  ...OCPP16MessageTrigger,
+};
+
 type CommandsSupport = {
   incomingCommands: Record<IncomingRequestCommand, boolean>;
   outgoingCommands?: Record<RequestCommand, boolean>;