X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2FOCPPServiceUtils.ts;h=9e8db34edf795e5d3f882a6cbf8557d21c08efa7;hb=4c3c0d59f56be4d58e906e938c00390b41e0ca7f;hp=451870862ad1c5630a68fa34da323594e1e27fa7;hpb=130783a74f495abcb198b7f01abe19dab4f7fb47;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ocpp/OCPPServiceUtils.ts b/src/charging-station/ocpp/OCPPServiceUtils.ts index 45187086..9e8db34e 100644 --- a/src/charging-station/ocpp/OCPPServiceUtils.ts +++ b/src/charging-station/ocpp/OCPPServiceUtils.ts @@ -1,28 +1,33 @@ -import type { DefinedError, ErrorObject } from 'ajv'; +import fs from 'node:fs'; -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 { MessageType } from '../../types/ocpp/MessageType'; -import { MeterValueMeasurand, type MeterValuePhase } from '../../types/ocpp/MeterValues'; -import { OCPPVersion } from '../../types/ocpp/OCPPVersion'; +import type { DefinedError, ErrorObject, JSONSchemaType } from 'ajv'; + +import { OCPP16Constants } from './1.6/OCPP16Constants'; +import { OCPP20Constants } from './2.0/OCPP20Constants'; +import { type ChargingStation, ChargingStationConfigurationUtils } from '../../charging-station'; +import { BaseError } from '../../exception'; import { + ChargePointErrorCode, + type ConnectorStatusEnum, + ErrorType, + FileType, IncomingRequestCommand, + type JsonObject, + type JsonType, MessageTrigger, + MessageType, + MeterValueMeasurand, + type MeterValuePhase, + type OCPP16StatusNotificationRequest, + type OCPP20StatusNotificationRequest, + OCPPVersion, RequestCommand, + type SampledValueTemplate, + StandardParametersKey, type StatusNotificationRequest, -} from '../../types/ocpp/Requests'; -import Constants from '../../utils/Constants'; -import logger from '../../utils/Logger'; -import Utils from '../../utils/Utils'; -import type ChargingStation from '../ChargingStation'; -import { ChargingStationConfigurationUtils } from '../ChargingStationConfigurationUtils'; + type StatusNotificationResponse, +} from '../../types'; +import { Constants, FileUtils, Utils, logger } from '../../utils'; export class OCPPServiceUtils { protected constructor() { @@ -122,7 +127,7 @@ export class OCPPServiceUtils { ): boolean { if (connectorId < 0) { logger.error( - `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector Id ${connectorId}` + `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector id ${connectorId}` ); return false; } @@ -134,7 +139,7 @@ export class OCPPServiceUtils { if (obj[key] instanceof Date) { (obj as JsonObject)[key] = (obj[key] as Date).toISOString(); } else if (obj[key] !== null && typeof obj[key] === 'object') { - this.convertDateToISOString(obj[key] as T); + OCPPServiceUtils.convertDateToISOString(obj[key] as T); } } } @@ -142,7 +147,8 @@ export class OCPPServiceUtils { public static buildStatusNotificationRequest( chargingStation: ChargingStation, connectorId: number, - status: ConnectorStatusEnum + status: ConnectorStatusEnum, + evseId?: number ): StatusNotificationRequest { switch (chargingStation.stationInfo.ocppVersion ?? OCPPVersion.VERSION_16) { case OCPPVersion.VERSION_16: @@ -157,15 +163,111 @@ export class OCPPServiceUtils { timestamp: new Date(), connectorStatus: status, connectorId, - evseId: connectorId, + evseId, } as OCPP20StatusNotificationRequest; default: throw new BaseError('Cannot build status notification payload: OCPP version not supported'); } } - protected static logPrefix(ocppVersion: OCPPVersion): string { - return Utils.logPrefix(` OCPP ${ocppVersion} |`); + public static startHeartbeatInterval(chargingStation: ChargingStation, interval: number): void { + if (!chargingStation.heartbeatSetInterval) { + chargingStation.startHeartbeat(); + } else if (chargingStation.getHeartbeatInterval() !== interval) { + chargingStation.restartHeartbeat(); + } + } + + public static async sendAndSetConnectorStatus( + chargingStation: ChargingStation, + connectorId: number, + status: ConnectorStatusEnum, + evseId?: number + ) { + OCPPServiceUtils.checkConnectorStatusTransition(chargingStation, connectorId, status); + await chargingStation.ocppRequestService.requestHandler< + StatusNotificationRequest, + StatusNotificationResponse + >( + chargingStation, + RequestCommand.STATUS_NOTIFICATION, + OCPPServiceUtils.buildStatusNotificationRequest(chargingStation, connectorId, status, evseId) + ); + chargingStation.getConnectorStatus(connectorId).status = status; + } + + protected static checkConnectorStatusTransition( + chargingStation: ChargingStation, + connectorId: number, + status: ConnectorStatusEnum + ): boolean { + const fromStatus = chargingStation.getConnectorStatus(connectorId).status; + let transitionAllowed = false; + switch (chargingStation.stationInfo.ocppVersion) { + case OCPPVersion.VERSION_16: + if ( + (connectorId === 0 && + OCPP16Constants.ChargePointStatusChargingStationTransitions.findIndex( + (transition) => transition.from === fromStatus && transition.to === status + ) !== -1) || + (connectorId > 0 && + OCPP16Constants.ChargePointStatusConnectorTransitions.findIndex( + (transition) => transition.from === fromStatus && transition.to === status + ) !== -1) + ) { + transitionAllowed = true; + } + break; + case OCPPVersion.VERSION_20: + case OCPPVersion.VERSION_201: + if ( + (connectorId === 0 && + OCPP20Constants.ChargingStationStatusTransitions.findIndex( + (transition) => transition.from === fromStatus && transition.to === status + ) !== -1) || + (connectorId > 0 && + OCPP20Constants.ConnectorStatusTransitions.findIndex( + (transition) => transition.from === fromStatus && transition.to === status + ) !== -1) + ) { + transitionAllowed = true; + } + break; + default: + throw new BaseError( + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + `Cannot check connector status transition: OCPP version ${chargingStation.stationInfo.ocppVersion} not supported` + ); + } + if (transitionAllowed === false) { + logger.warn( + `${chargingStation.logPrefix()} OCPP ${ + chargingStation.stationInfo.ocppVersion + } connector id ${connectorId} status transition from '${ + chargingStation.getConnectorStatus(connectorId).status + }' to '${status}' is not allowed` + ); + } + return transitionAllowed; + } + + protected static parseJsonSchemaFile( + filePath: string, + ocppVersion: OCPPVersion, + moduleName?: string, + methodName?: string + ): JSONSchemaType { + try { + return JSON.parse(fs.readFileSync(filePath, 'utf8')) as JSONSchemaType; + } catch (error) { + FileUtils.handleFileException( + filePath, + FileType.JsonSchema, + error as NodeJS.ErrnoException, + OCPPServiceUtils.logPrefix(ocppVersion, moduleName, methodName), + { throwError: false } + ); + } } protected static getSampledValueTemplate( @@ -177,7 +279,7 @@ export class OCPPServiceUtils { const onPhaseStr = phase ? `on phase ${phase} ` : ''; if (Constants.SUPPORTED_MEASURANDS.includes(measurand) === false) { logger.warn( - `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}` + `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}` ); return; } @@ -189,7 +291,7 @@ export class OCPPServiceUtils { )?.value?.includes(measurand) === false ) { logger.debug( - `${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId} not found in '${ + `${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId} not found in '${ StandardParametersKey.MeterValuesSampledData }' OCPP parameter` ); @@ -199,7 +301,7 @@ export class OCPPServiceUtils { chargingStation.getConnectorStatus(connectorId)?.MeterValues; for ( let index = 0; - Utils.isEmptyArray(sampledValueTemplates) === false && index < sampledValueTemplates.length; + Utils.isNotEmptyArray(sampledValueTemplates) === true && index < sampledValueTemplates.length; index++ ) { if ( @@ -209,7 +311,7 @@ export class OCPPServiceUtils { ) === false ) { logger.warn( - `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}` + `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}` ); } else if ( phase && @@ -240,12 +342,12 @@ export class OCPPServiceUtils { } } if (measurand === MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER) { - const errorMsg = `Missing MeterValues for default measurand '${measurand}' in template on connectorId ${connectorId}`; + const errorMsg = `Missing MeterValues for default measurand '${measurand}' in template on connector id ${connectorId}`; logger.error(`${chargingStation.logPrefix()} ${errorMsg}`); throw new BaseError(errorMsg); } logger.debug( - `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}` + `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connector id ${connectorId}` ); } @@ -265,4 +367,16 @@ export class OCPPServiceUtils { ? Math.min(numberValue * options.unitMultiplier, limit) : numberValue * options.unitMultiplier; } + + private static logPrefix = ( + ocppVersion: OCPPVersion, + moduleName?: string, + methodName?: string + ): string => { + const logMsg = + Utils.isNotEmptyString(moduleName) && Utils.isNotEmptyString(methodName) + ? ` OCPP ${ocppVersion} | ${moduleName}.${methodName}:` + : ` OCPP ${ocppVersion} |`; + return Utils.logPrefix(logMsg); + }; }