From c60ed4b8646f72758a27b62003e51568d3966d29 Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Sat, 5 Nov 2022 22:37:33 +0100 Subject: [PATCH] Add trigger message type feature flag in charging station template MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Close #257 Signed-off-by: Jérôme Benoit --- README.md | 3 +- .../chargex.station-template.json | 5 +++ .../ocpp/1.6/OCPP16IncomingRequestService.ts | 36 ++++++++++++------- src/charging-station/ocpp/OCPPServiceUtils.ts | 32 ++++++++++++++++- src/types/ChargingStationTemplate.ts | 3 +- src/types/ocpp/1.6/Requests.ts | 4 +-- src/types/ocpp/Requests.ts | 7 ++++ ui/web/src/types/ChargingStationType.ts | 16 +++++++++ 8 files changed, 88 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 7bdb7086..562a1ba4 100644 --- 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 | | {
"incomingCommands": {},
"outgoingCommands": {}
} | {
incomingCommands: Record;
outgoingCommands?: Record;
} | Configuration section for OCPP commands support. Empty section or subsections means all implemented commands are supported | +| commandsSupport | | {
"incomingCommands": {},
"outgoingCommands": {}
} | {
incomingCommands: Record;
outgoingCommands?: Record;
} | Configuration section for OCPP commands support. Empty section or subsections means all implemented OCPP commands are supported | +| messageTriggerSupport | | {} | Record | 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 | diff --git a/src/assets/station-templates/chargex.station-template.json b/src/assets/station-templates/chargex.station-template.json index 2630278b..39cc5804 100644 --- a/src/assets/station-templates/chargex.station-template.json +++ b/src/assets/station-templates/chargex.station-template.json @@ -26,6 +26,11 @@ "TriggerMessage": false } }, + "messageTriggerSupport": { + "BootNotification": true, + "Heartbeat": true, + "StatusNotification": false + }, "Configuration": { "configurationKey": [ { diff --git a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts index aecc08f7..0af87023 100644 --- a/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16IncomingRequestService.ts @@ -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 { 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 { 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( @@ -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( @@ -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 diff --git a/src/charging-station/ocpp/OCPPServiceUtils.ts b/src/charging-station/ocpp/OCPPServiceUtils.ts index ad78d347..2fae9934 100644 --- a/src/charging-station/ocpp/OCPPServiceUtils.ts +++ b/src/charging-station/ocpp/OCPPServiceUtils.ts @@ -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, diff --git a/src/types/ChargingStationTemplate.ts b/src/types/ChargingStationTemplate.ts index 0d063d51..3945e892 100644 --- a/src/types/ChargingStationTemplate.ts +++ b/src/types/ChargingStationTemplate.ts @@ -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; Configuration?: ChargingStationOcppConfiguration; AutomaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration; Connectors: Record; diff --git a/src/types/ocpp/1.6/Requests.ts b/src/types/ocpp/1.6/Requests.ts index 263947c2..b2961a91 100644 --- a/src/types/ocpp/1.6/Requests.ts +++ b/src/types/ocpp/1.6/Requests.ts @@ -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; } diff --git a/src/types/ocpp/Requests.ts b/src/types/ocpp/Requests.ts index 052bfc6b..9934f167 100644 --- a/src/types/ocpp/Requests.ts +++ b/src/types/ocpp/Requests.ts @@ -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; diff --git a/ui/web/src/types/ChargingStationType.ts b/ui/web/src/types/ChargingStationType.ts index a3f9931c..929f40e3 100644 --- a/ui/web/src/types/ChargingStationType.ts +++ b/ui/web/src/types/ChargingStationType.ts @@ -67,6 +67,7 @@ export type ChargingStationInfo = { phaseLineToLineVoltageMeterValues?: boolean; customValueLimitationMeterValues?: boolean; commandsSupport?: CommandsSupport; + messageTriggerSupport?: Record; }; 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; outgoingCommands?: Record; -- 2.34.1