From 6e939d9e416de33fa1c64fc7f4eb6be70954f69c Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Tue, 10 Jan 2023 00:36:59 +0100 Subject: [PATCH] Add StatusNotification command to OCPP 2.0.1 stack MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Jérôme Benoit --- src/charging-station/ChargingStation.ts | 39 +++++++++++-------- .../ocpp/2.0/OCPP20RequestService.ts | 20 ++++++++++ .../ocpp/2.0/OCPP20ResponseService.ts | 14 +++++++ src/charging-station/ocpp/OCPPServiceUtils.ts | 35 ++++++++++++++++- src/types/ConnectorStatus.ts | 6 +-- src/types/ocpp/2.0/Requests.ts | 16 ++++++++ src/types/ocpp/2.0/Responses.ts | 3 ++ src/types/ocpp/ChargePointStatus.ts | 6 --- src/types/ocpp/ChargingProfile.ts | 4 +- src/types/ocpp/ConnectorStatusEnum.ts | 8 ++++ src/types/ocpp/Requests.ts | 5 ++- src/types/ocpp/Responses.ts | 10 ++++- 12 files changed, 134 insertions(+), 32 deletions(-) delete mode 100644 src/types/ocpp/ChargePointStatus.ts create mode 100644 src/types/ocpp/ConnectorStatusEnum.ts diff --git a/src/charging-station/ChargingStation.ts b/src/charging-station/ChargingStation.ts index 84aa81ca..997a8ebb 100644 --- a/src/charging-station/ChargingStation.ts +++ b/src/charging-station/ChargingStation.ts @@ -26,7 +26,6 @@ import type { ConnectorStatus } from '../types/ConnectorStatus'; import { FileType } from '../types/FileType'; import type { JsonType } from '../types/JsonType'; import { ChargePointErrorCode } from '../types/ocpp/ChargePointErrorCode'; -import { ChargePointStatus } from '../types/ocpp/ChargePointStatus'; import { ChargingProfile, ChargingRateUnitType } from '../types/ocpp/ChargingProfile'; import { ConnectorPhaseRotation, @@ -34,6 +33,7 @@ import { SupportedFeatureProfiles, VendorDefaultParametersKey, } from '../types/ocpp/Configuration'; +import { ConnectorStatusEnum } from '../types/ocpp/ConnectorStatusEnum'; import { ErrorType } from '../types/ocpp/ErrorType'; import { MessageType } from '../types/ocpp/MessageType'; import { MeterValue, MeterValueMeasurand } from '../types/ocpp/MeterValues'; @@ -90,6 +90,7 @@ import OCPP20RequestService from './ocpp/2.0/OCPP20RequestService'; import OCPP20ResponseService from './ocpp/2.0/OCPP20ResponseService'; import type OCPPIncomingRequestService from './ocpp/OCPPIncomingRequestService'; import type OCPPRequestService from './ocpp/OCPPRequestService'; +import { OCPPServiceUtils } from './ocpp/OCPPServiceUtils'; import SharedLRUCache from './SharedLRUCache'; export default class ChargingStation { @@ -1815,7 +1816,7 @@ export default class ChargingStation { this.startHeartbeat(); // Initialize connectors status for (const connectorId of this.connectors.keys()) { - let chargePointStatus: ChargePointStatus; + let connectorStatus: ConnectorStatusEnum; if (connectorId === 0) { continue; } else if ( @@ -1823,29 +1824,29 @@ export default class ChargingStation { (this.isChargingStationAvailable() === false || this.isConnectorAvailable(connectorId) === false) ) { - chargePointStatus = ChargePointStatus.UNAVAILABLE; + connectorStatus = ConnectorStatusEnum.UNAVAILABLE; } else if ( !this.getConnectorStatus(connectorId)?.status && this.getConnectorStatus(connectorId)?.bootStatus ) { // Set boot status in template at startup - chargePointStatus = this.getConnectorStatus(connectorId).bootStatus; + connectorStatus = this.getConnectorStatus(connectorId).bootStatus; } else if (this.getConnectorStatus(connectorId)?.status) { // Set previous status at startup - chargePointStatus = this.getConnectorStatus(connectorId).status; + connectorStatus = this.getConnectorStatus(connectorId).status; } else { // Set default status - chargePointStatus = ChargePointStatus.AVAILABLE; + connectorStatus = ConnectorStatusEnum.AVAILABLE; } await this.ocppRequestService.requestHandler< StatusNotificationRequest, StatusNotificationResponse - >(this, RequestCommand.STATUS_NOTIFICATION, { - connectorId, - status: chargePointStatus, - errorCode: ChargePointErrorCode.NO_ERROR, - }); - this.getConnectorStatus(connectorId).status = chargePointStatus; + >( + this, + RequestCommand.STATUS_NOTIFICATION, + OCPPServiceUtils.buildStatusNotificationRequest(this, connectorId, connectorStatus) + ); + this.getConnectorStatus(connectorId).status = connectorStatus; } if (this.stationInfo?.firmwareStatus === FirmwareStatus.Installing) { await this.ocppRequestService.requestHandler< @@ -1882,11 +1883,15 @@ export default class ChargingStation { await this.ocppRequestService.requestHandler< StatusNotificationRequest, StatusNotificationResponse - >(this, RequestCommand.STATUS_NOTIFICATION, { - connectorId, - status: ChargePointStatus.UNAVAILABLE, - errorCode: ChargePointErrorCode.NO_ERROR, - }); + >( + this, + RequestCommand.STATUS_NOTIFICATION, + OCPPServiceUtils.buildStatusNotificationRequest( + this, + connectorId, + ConnectorStatusEnum.UNAVAILABLE + ) + ); this.getConnectorStatus(connectorId).status = null; } } diff --git a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts index 3e54ca50..6a7fe370 100644 --- a/src/charging-station/ocpp/2.0/OCPP20RequestService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20RequestService.ts @@ -12,6 +12,7 @@ import { type OCPP20BootNotificationRequest, type OCPP20HeartbeatRequest, OCPP20RequestCommand, + type OCPP20StatusNotificationRequest, } from '../../../types/ocpp/2.0/Requests'; import { ErrorType } from '../../../types/ocpp/ErrorType'; import { OCPPVersion } from '../../../types/ocpp/OCPPVersion'; @@ -57,6 +58,18 @@ export default class OCPP20RequestService extends OCPPRequestService { ) ) as JSONSchemaType, ], + [ + OCPP20RequestCommand.STATUS_NOTIFICATION, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/2.0/StatusNotificationRequest.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], ]); this.buildRequestPayload.bind(this); } @@ -125,6 +138,13 @@ export default class OCPP20RequestService extends OCPPRequestService { } as unknown as Request; case OCPP20RequestCommand.HEARTBEAT: return {} as unknown as Request; + case OCPP20RequestCommand.STATUS_NOTIFICATION: + return { + timestamp: commandParams?.timestamp, + connectorStatus: commandParams?.connectorStatus, + evseId: commandParams?.evseId, + connectorId: commandParams?.connectorId, + } 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( diff --git a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts index 15b355a5..add126e4 100644 --- a/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts +++ b/src/charging-station/ocpp/2.0/OCPP20ResponseService.ts @@ -16,6 +16,7 @@ import type { OCPP20BootNotificationResponse, OCPP20ClearCacheResponse, OCPP20HeartbeatResponse, + OCPP20StatusNotificationResponse, } from '../../../types/ocpp/2.0/Responses'; import { ErrorType } from '../../../types/ocpp/ErrorType'; import { OCPPVersion } from '../../../types/ocpp/OCPPVersion'; @@ -44,6 +45,7 @@ export default class OCPP20ResponseService extends OCPPResponseService { this.responseHandlers = new Map([ [OCPP20RequestCommand.BOOT_NOTIFICATION, this.handleResponseBootNotification.bind(this)], [OCPP20RequestCommand.HEARTBEAT, this.emptyResponseHandler.bind(this)], + [OCPP20RequestCommand.STATUS_NOTIFICATION, this.emptyResponseHandler.bind(this)], ]); this.jsonSchemas = new Map>([ [ @@ -70,6 +72,18 @@ export default class OCPP20ResponseService extends OCPPResponseService { ) ) as JSONSchemaType, ], + [ + OCPP20RequestCommand.STATUS_NOTIFICATION, + JSON.parse( + fs.readFileSync( + path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '../../../assets/json-schemas/ocpp/2.0/StatusNotificationResponse.json' + ), + 'utf8' + ) + ) as JSONSchemaType, + ], ]); this.jsonIncomingRequestResponseSchemas = new Map([ [ diff --git a/src/charging-station/ocpp/OCPPServiceUtils.ts b/src/charging-station/ocpp/OCPPServiceUtils.ts index 3c415251..06c22367 100644 --- a/src/charging-station/ocpp/OCPPServiceUtils.ts +++ b/src/charging-station/ocpp/OCPPServiceUtils.ts @@ -3,10 +3,20 @@ import type { DefinedError, ErrorObject } from 'ajv'; import BaseError from '../../exception/BaseError'; import type { JsonObject, JsonType } from '../../types/JsonType'; import type { SampledValueTemplate } from '../../types/MeasurandPerPhaseSampledValueTemplates'; +import type { OCPP16StatusNotificationRequest } from '../../types/ocpp/1.6/Requests'; +import type { OCPP20StatusNotificationRequest } from '../../types/ocpp/2.0/Requests'; +import { ChargePointErrorCode } from '../../types/ocpp/ChargePointErrorCode'; import { StandardParametersKey } from '../../types/ocpp/Configuration'; +import type { ConnectorStatusEnum } from '../../types/ocpp/ConnectorStatusEnum'; import { ErrorType } from '../../types/ocpp/ErrorType'; import { MeterValueMeasurand, type MeterValuePhase } from '../../types/ocpp/MeterValues'; -import { IncomingRequestCommand, MessageTrigger, RequestCommand } from '../../types/ocpp/Requests'; +import { OCPPVersion } from '../../types/ocpp/OCPPVersion'; +import { + IncomingRequestCommand, + MessageTrigger, + RequestCommand, + type StatusNotificationRequest, +} from '../../types/ocpp/Requests'; import Constants from '../../utils/Constants'; import logger from '../../utils/Logger'; import Utils from '../../utils/Utils'; @@ -115,6 +125,29 @@ export class OCPPServiceUtils { } } + public static buildStatusNotificationRequest( + chargingStation: ChargingStation, + connectorId: number, + status: ConnectorStatusEnum + ): StatusNotificationRequest { + switch (chargingStation.stationInfo.ocppVersion) { + case OCPPVersion.VERSION_16: + return { + connectorId, + status, + errorCode: ChargePointErrorCode.NO_ERROR, + } as OCPP16StatusNotificationRequest; + case OCPPVersion.VERSION_20: + case OCPPVersion.VERSION_201: + return { + timestamp: new Date(), + connectorStatus: status, + connectorId, + evseId: connectorId, + } as OCPP20StatusNotificationRequest; + } + } + protected static getSampledValueTemplate( chargingStation: ChargingStation, connectorId: number, diff --git a/src/types/ConnectorStatus.ts b/src/types/ConnectorStatus.ts index 209cd42e..d7698107 100644 --- a/src/types/ConnectorStatus.ts +++ b/src/types/ConnectorStatus.ts @@ -1,13 +1,13 @@ import type { SampledValueTemplate } from './MeasurandPerPhaseSampledValueTemplates'; -import type { ChargePointStatus } from './ocpp/ChargePointStatus'; import type { ChargingProfile } from './ocpp/ChargingProfile'; +import type { ConnectorStatusEnum } from './ocpp/ConnectorStatusEnum'; import type { MeterValue } from './ocpp/MeterValues'; import type { AvailabilityType } from './ocpp/Requests'; export type ConnectorStatus = { availability: AvailabilityType; - bootStatus?: ChargePointStatus; - status?: ChargePointStatus; + bootStatus?: ConnectorStatusEnum; + status?: ConnectorStatusEnum; MeterValues: SampledValueTemplate[]; authorizeIdTag?: string; idTagAuthorized?: boolean; diff --git a/src/types/ocpp/2.0/Requests.ts b/src/types/ocpp/2.0/Requests.ts index 72ad392e..1b30a976 100644 --- a/src/types/ocpp/2.0/Requests.ts +++ b/src/types/ocpp/2.0/Requests.ts @@ -4,6 +4,7 @@ import type { JsonObject } from '../../JsonType'; export enum OCPP20RequestCommand { BOOT_NOTIFICATION = 'BootNotification', HEARTBEAT = 'Heartbeat', + STATUS_NOTIFICATION = 'StatusNotification', } export enum OCPP20IncomingRequestCommand { @@ -45,3 +46,18 @@ export type OCPP20BootNotificationRequest = { export type OCPP20HeartbeatRequest = EmptyObject; export type OCPP20ClearCacheRequest = EmptyObject; + +export enum OCPP20ConnectorStatusEnumType { + AVAILABLE = 'Available', + OCCUPIED = 'Occupied', + RESERVED = 'Reserved', + UNAVAILABLE = 'Unavailable', + FAULTED = 'Faulted', +} + +export type OCPP20StatusNotificationRequest = { + timestamp: Date; + connectorStatus: OCPP20ConnectorStatusEnumType; + evseId: number; + connectorId: number; +}; diff --git a/src/types/ocpp/2.0/Responses.ts b/src/types/ocpp/2.0/Responses.ts index b83f0c84..53bbb173 100644 --- a/src/types/ocpp/2.0/Responses.ts +++ b/src/types/ocpp/2.0/Responses.ts @@ -1,3 +1,4 @@ +import type { EmptyObject } from '../../EmptyObject'; import type { JsonObject } from '../../JsonType'; import type { DefaultStatus, RegistrationStatusEnumType } from '../Responses'; @@ -21,3 +22,5 @@ export type OCPP20ClearCacheResponse = { status: DefaultStatus; statusInfo?: StatusInfoType; } & JsonObject; + +export type OCPP20StatusNotificationResponse = EmptyObject; diff --git a/src/types/ocpp/ChargePointStatus.ts b/src/types/ocpp/ChargePointStatus.ts deleted file mode 100644 index a8ae442f..00000000 --- a/src/types/ocpp/ChargePointStatus.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { OCPP16ChargePointStatus } from './1.6/ChargePointStatus'; - -export const ChargePointStatus = { - ...OCPP16ChargePointStatus, -} as const; -export type ChargePointStatus = OCPP16ChargePointStatus; diff --git a/src/types/ocpp/ChargingProfile.ts b/src/types/ocpp/ChargingProfile.ts index a8b73a24..83710272 100644 --- a/src/types/ocpp/ChargingProfile.ts +++ b/src/types/ocpp/ChargingProfile.ts @@ -1,7 +1,7 @@ import { - OCPP16ChargingProfile, + type OCPP16ChargingProfile, OCPP16ChargingRateUnitType, - OCPP16ChargingSchedulePeriod, + type OCPP16ChargingSchedulePeriod, } from './1.6/ChargingProfile'; export type ChargingProfile = OCPP16ChargingProfile; diff --git a/src/types/ocpp/ConnectorStatusEnum.ts b/src/types/ocpp/ConnectorStatusEnum.ts new file mode 100644 index 00000000..929d59cd --- /dev/null +++ b/src/types/ocpp/ConnectorStatusEnum.ts @@ -0,0 +1,8 @@ +import { OCPP16ChargePointStatus } from './1.6/ChargePointStatus'; +import { OCPP20ConnectorStatusEnumType } from './2.0/Requests'; + +export const ConnectorStatusEnum = { + ...OCPP16ChargePointStatus, + ...OCPP20ConnectorStatusEnumType, +} as const; +export type ConnectorStatusEnum = OCPP16ChargePointStatus | OCPP20ConnectorStatusEnumType; diff --git a/src/types/ocpp/Requests.ts b/src/types/ocpp/Requests.ts index 993ba086..35933c83 100644 --- a/src/types/ocpp/Requests.ts +++ b/src/types/ocpp/Requests.ts @@ -20,6 +20,7 @@ import { type OCPP20BootNotificationRequest, OCPP20IncomingRequestCommand, OCPP20RequestCommand, + type OCPP20StatusNotificationRequest, } from './2.0/Requests'; import type { MessageType } from './MessageType'; @@ -65,7 +66,9 @@ export type BootNotificationRequest = OCPP16BootNotificationRequest | OCPP20Boot export type HeartbeatRequest = OCPP16HeartbeatRequest; -export type StatusNotificationRequest = OCPP16StatusNotificationRequest; +export type StatusNotificationRequest = + | OCPP16StatusNotificationRequest + | OCPP20StatusNotificationRequest; export type MeterValuesRequest = OCPP16MeterValuesRequest; diff --git a/src/types/ocpp/Responses.ts b/src/types/ocpp/Responses.ts index 90c5f1ea..32367462 100644 --- a/src/types/ocpp/Responses.ts +++ b/src/types/ocpp/Responses.ts @@ -16,7 +16,11 @@ import { OCPP16TriggerMessageStatus, OCPP16UnlockStatus, } from './1.6/Responses'; -import type { OCPP20BootNotificationResponse, OCPP20ClearCacheResponse } from './2.0/Responses'; +import type { + OCPP20BootNotificationResponse, + OCPP20ClearCacheResponse, + OCPP20StatusNotificationResponse, +} from './2.0/Responses'; import type { ErrorType } from './ErrorType'; import type { MessageType } from './MessageType'; @@ -38,7 +42,9 @@ export type HeartbeatResponse = OCPP16HeartbeatResponse; export type ClearCacheResponse = DefaultResponse | OCPP20ClearCacheResponse; -export type StatusNotificationResponse = OCPP16StatusNotificationResponse; +export type StatusNotificationResponse = + | OCPP16StatusNotificationResponse + | OCPP20StatusNotificationResponse; export type MeterValuesResponse = OCPP16MeterValuesResponse; -- 2.34.1