X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Fcharging-station%2Focpp%2F1.6%2FOCPP16RequestService.ts;h=0385f45a5eee50714ad4db8e93c4a85c8c9020c8;hb=9f2e313013116428f5bce2be59e2f5c07502c026;hp=8bdc37cf8bf8415d2d3bc31a69b575d573e450a9;hpb=b02ab2157918aede5b539c6307a6010d97b6ee83;p=e-mobility-charging-stations-simulator.git diff --git a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts index 8bdc37cf..0385f45a 100644 --- a/src/charging-station/ocpp/1.6/OCPP16RequestService.ts +++ b/src/charging-station/ocpp/1.6/OCPP16RequestService.ts @@ -1,10 +1,15 @@ +// Partial Copyright Jerome Benoit. 2021. All Rights Reserved. + import { ACElectricUtils, DCElectricUtils } from '../../../utils/ElectricUtils'; import { AuthorizeRequest, OCPP16AuthorizeResponse, OCPP16StartTransactionResponse, OCPP16StopTransactionReason, OCPP16StopTransactionResponse, StartTransactionRequest, StopTransactionRequest } from '../../../types/ocpp/1.6/Transaction'; import { CurrentType, Voltage } from '../../../types/ChargingStationTemplate'; import { DiagnosticsStatusNotificationRequest, HeartbeatRequest, OCPP16BootNotificationRequest, OCPP16IncomingRequestCommand, OCPP16RequestCommand, StatusNotificationRequest } from '../../../types/ocpp/1.6/Requests'; import { MeterValueUnit, MeterValuesRequest, OCPP16MeterValue, OCPP16MeterValueMeasurand, OCPP16MeterValuePhase } from '../../../types/ocpp/1.6/MeterValues'; +import ChargingStation from '../../ChargingStation'; import Constants from '../../../utils/Constants'; +import { ErrorType } from '../../../types/ocpp/ErrorType'; +import { JsonType } from '../../../types/JsonType'; import MeasurandPerPhaseSampledValueTemplates from '../../../types/MeasurandPerPhaseSampledValueTemplates'; import MeasurandValues from '../../../types/MeasurandValues'; import { MessageType } from '../../../types/ocpp/MessageType'; @@ -13,23 +18,33 @@ import { OCPP16ChargePointErrorCode } from '../../../types/ocpp/1.6/ChargePointE import { OCPP16ChargePointStatus } from '../../../types/ocpp/1.6/ChargePointStatus'; import { OCPP16DiagnosticsStatus } from '../../../types/ocpp/1.6/DiagnosticsStatus'; import { OCPP16ServiceUtils } from './OCPP16ServiceUtils'; -import OCPPError from '../../OCPPError'; +import OCPPError from '../../../exception/OCPPError'; import OCPPRequestService from '../OCPPRequestService'; +import OCPPResponseService from '../OCPPResponseService'; +import { SendParams } from '../../../types/ocpp/Requests'; import Utils from '../../../utils/Utils'; import logger from '../../../utils/Logger'; export default class OCPP16RequestService extends OCPPRequestService { - public async sendHeartbeat(): Promise { + public constructor(chargingStation: ChargingStation, ocppResponseService: OCPPResponseService) { + if (new.target?.name === 'OCPP16RequestService') { + throw new TypeError('Cannot construct OCPP16RequestService instances directly'); + } + super(chargingStation, ocppResponseService); + } + + public async sendHeartbeat(params?: SendParams): Promise { try { const payload: HeartbeatRequest = {}; - await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.HEARTBEAT); + await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.HEARTBEAT, params); } catch (error) { - this.handleRequestError(OCPP16RequestCommand.HEARTBEAT, error); + this.handleRequestError(OCPP16RequestCommand.HEARTBEAT, error as Error); } } public async sendBootNotification(chargePointModel: string, chargePointVendor: string, chargeBoxSerialNumber?: string, firmwareVersion?: string, - chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string): Promise { + chargePointSerialNumber?: string, iccid?: string, imsi?: string, meterSerialNumber?: string, meterType?: string, + params?: SendParams): Promise { try { const payload: OCPP16BootNotificationRequest = { chargePointModel, @@ -42,9 +57,10 @@ export default class OCPP16RequestService extends OCPPRequestService { ...!Utils.isUndefined(meterSerialNumber) && { meterSerialNumber }, ...!Utils.isUndefined(meterType) && { meterType } }; - return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.BOOT_NOTIFICATION) as OCPP16BootNotificationResponse; + return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, + OCPP16RequestCommand.BOOT_NOTIFICATION, { ...params, skipBufferingOnError: true }) as OCPP16BootNotificationResponse; } catch (error) { - this.handleRequestError(OCPP16RequestCommand.BOOT_NOTIFICATION, error); + this.handleRequestError(OCPP16RequestCommand.BOOT_NOTIFICATION, error as Error); } } @@ -58,7 +74,7 @@ export default class OCPP16RequestService extends OCPPRequestService { }; await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.STATUS_NOTIFICATION); } catch (error) { - this.handleRequestError(OCPP16RequestCommand.STATUS_NOTIFICATION, error); + this.handleRequestError(OCPP16RequestCommand.STATUS_NOTIFICATION, error as Error); } } @@ -67,10 +83,10 @@ export default class OCPP16RequestService extends OCPPRequestService { const payload: AuthorizeRequest = { ...!Utils.isUndefined(idTag) ? { idTag } : { idTag: Constants.TRANSACTION_DEFAULT_IDTAG }, }; - this.chargingStation.getConnector(connectorId).authorizeIdTag = idTag; + this.chargingStation.getConnectorStatus(connectorId).authorizeIdTag = idTag; return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.AUTHORIZE) as OCPP16AuthorizeResponse; } catch (error) { - this.handleRequestError(OCPP16RequestCommand.AUTHORIZE, error); + this.handleRequestError(OCPP16RequestCommand.AUTHORIZE, error as Error); } } @@ -84,7 +100,7 @@ export default class OCPP16RequestService extends OCPPRequestService { }; return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.START_TRANSACTION) as OCPP16StartTransactionResponse; } catch (error) { - this.handleRequestError(OCPP16RequestCommand.START_TRANSACTION, error); + this.handleRequestError(OCPP16RequestCommand.START_TRANSACTION, error as Error); } } @@ -92,15 +108,15 @@ export default class OCPP16RequestService extends OCPPRequestService { reason: OCPP16StopTransactionReason = OCPP16StopTransactionReason.NONE): Promise { try { let connectorId: number; - for (const connector in this.chargingStation.connectors) { - if (Utils.convertToInt(connector) > 0 && this.chargingStation.getConnector(Utils.convertToInt(connector))?.transactionId === transactionId) { - connectorId = Utils.convertToInt(connector); + for (const id of this.chargingStation.connectors.keys()) { + if (id > 0 && this.chargingStation.getConnectorStatus(id)?.transactionId === transactionId) { + connectorId = id; break; } } const transactionEndMeterValue = OCPP16ServiceUtils.buildTransactionEndMeterValue(this.chargingStation, connectorId, meterStop); // FIXME: should be a callback, each OCPP commands implementation must do only one job - (this.chargingStation.getBeginEndMeterValues() && !this.chargingStation.getOutOfOrderEndMeterValues()) + (this.chargingStation.getBeginEndMeterValues() && this.chargingStation.getOcppStrictCompliance() && !this.chargingStation.getOutOfOrderEndMeterValues()) && await this.sendTransactionEndMeterValues(connectorId, transactionId, transactionEndMeterValue); const payload: StopTransactionRequest = { transactionId, @@ -108,28 +124,27 @@ export default class OCPP16RequestService extends OCPPRequestService { meterStop, timestamp: new Date().toISOString(), ...reason && { reason }, - ...this.chargingStation.getTransactionDataMeterValues() && { transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(this.chargingStation.getConnector(connectorId).transactionBeginMeterValue, transactionEndMeterValue) }, + ...this.chargingStation.getTransactionDataMeterValues() && { transactionData: OCPP16ServiceUtils.buildTransactionDataMeterValues(this.chargingStation.getConnectorStatus(connectorId).transactionBeginMeterValue, transactionEndMeterValue) }, }; return await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.STOP_TRANSACTION) as OCPP16StartTransactionResponse; } catch (error) { - this.handleRequestError(OCPP16RequestCommand.STOP_TRANSACTION, error); + this.handleRequestError(OCPP16RequestCommand.STOP_TRANSACTION, error as Error); } } - // eslint-disable-next-line consistent-this public async sendMeterValues(connectorId: number, transactionId: number, interval: number, debug = false): Promise { try { const meterValue: OCPP16MeterValue = { timestamp: new Date().toISOString(), sampledValue: [], }; - const connector = this.chargingStation.getConnector(connectorId); + const connector = this.chargingStation.getConnectorStatus(connectorId); // SoC measurand const socSampledValueTemplate = this.chargingStation.getSampledValueTemplate(connectorId, OCPP16MeterValueMeasurand.STATE_OF_CHARGE); if (socSampledValueTemplate) { const socSampledValueTemplateValue = socSampledValueTemplate.value ? Utils.getRandomFloatFluctuatedRounded(parseInt(socSampledValueTemplate.value), socSampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) - : Utils.getRandomInt(100); + : Utils.getRandomInteger(100); meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(socSampledValueTemplate, socSampledValueTemplateValue)); const sampledValuesIndex = meterValue.sampledValue.length - 1; if (Utils.convertToInt(meterValue.sampledValue[sampledValuesIndex].value) > 100 || debug) { @@ -219,7 +234,7 @@ export default class OCPP16RequestService extends OCPPRequestService { break; default: logger.error(errMsg); - throw new Error(errMsg); + throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES); } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(powerSampledValueTemplate, powerMeasurandValues.allPhases)); const sampledValuesIndex = meterValue.sampledValue.length - 1; @@ -285,7 +300,7 @@ export default class OCPP16RequestService extends OCPPRequestService { break; default: logger.error(errMsg); - throw new Error(errMsg); + throw new OCPPError(ErrorType.INTERNAL_ERROR, errMsg, OCPP16RequestCommand.METER_VALUES); } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(currentSampledValueTemplate, currentMeasurandValues.allPhases)); const sampledValuesIndex = meterValue.sampledValue.length - 1; @@ -307,26 +322,25 @@ export default class OCPP16RequestService extends OCPPRequestService { if (energySampledValueTemplate) { OCPP16ServiceUtils.checkMeasurandPowerDivider(this.chargingStation, energySampledValueTemplate.measurand); const unitDivider = energySampledValueTemplate?.unit === MeterValueUnit.KILO_WATT_HOUR ? 1000 : 1; - const energyMeasurandValue = energySampledValueTemplate.value + const maxEnergyRounded = Utils.roundTo(((this.chargingStation.stationInfo.maxPower / this.chargingStation.stationInfo.powerDivider) * interval) / (3600 * 1000), 2); + const energyValueRounded = energySampledValueTemplate.value // Cumulate the fluctuated value around the static one ? Utils.getRandomFloatFluctuatedRounded(parseInt(energySampledValueTemplate.value), energySampledValueTemplate.fluctuationPercent ?? Constants.DEFAULT_FLUCTUATION_PERCENT) - : Utils.getRandomInt(this.chargingStation.stationInfo.maxPower / (this.chargingStation.stationInfo.powerDivider * 3600000) * interval); + : Utils.getRandomFloatRounded(maxEnergyRounded); // Persist previous value on connector if (connector && !Utils.isNullOrUndefined(connector.energyActiveImportRegisterValue) && connector.energyActiveImportRegisterValue >= 0 && - !Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) && connector.transactionEnergyActiveImportRegisterValue >= 0) { - connector.energyActiveImportRegisterValue += energyMeasurandValue; - connector.transactionEnergyActiveImportRegisterValue += energyMeasurandValue; + !Utils.isNullOrUndefined(connector.transactionEnergyActiveImportRegisterValue) && connector.transactionEnergyActiveImportRegisterValue >= 0) { + connector.energyActiveImportRegisterValue += energyValueRounded; + connector.transactionEnergyActiveImportRegisterValue += energyValueRounded; } else { connector.energyActiveImportRegisterValue = 0; connector.transactionEnergyActiveImportRegisterValue = 0; } meterValue.sampledValue.push(OCPP16ServiceUtils.buildSampledValue(energySampledValueTemplate, - Utils.roundTo(this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) / unitDivider, 4))); + Utils.roundTo(this.chargingStation.getEnergyActiveImportRegisterByTransactionId(transactionId) / unitDivider, 2))); const sampledValuesIndex = meterValue.sampledValue.length - 1; - const maxEnergy = Math.round(this.chargingStation.stationInfo.maxPower * 3600 / (this.chargingStation.stationInfo.powerDivider * interval)); - const maxEnergyRounded = Utils.roundTo(maxEnergy / unitDivider, 4); - if (Utils.convertToFloat(meterValue.sampledValue[sampledValuesIndex].value) > maxEnergyRounded || debug) { - logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${meterValue.sampledValue[sampledValuesIndex].value}/${maxEnergyRounded}`); + if (energyValueRounded > maxEnergyRounded || debug) { + logger.error(`${this.chargingStation.logPrefix()} MeterValues measurand ${meterValue.sampledValue[sampledValuesIndex].measurand ?? OCPP16MeterValueMeasurand.ENERGY_ACTIVE_IMPORT_REGISTER}: connectorId ${connectorId}, transaction ${connector.transactionId}, value: ${energyValueRounded}/${maxEnergyRounded}, duration: ${Utils.roundTo(interval / (3600 * 1000), 4)}h`); } } const payload: MeterValuesRequest = { @@ -336,7 +350,7 @@ export default class OCPP16RequestService extends OCPPRequestService { }; await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); } catch (error) { - this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); + this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error as Error); } } @@ -349,7 +363,7 @@ export default class OCPP16RequestService extends OCPPRequestService { }; await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); } catch (error) { - this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); + this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error as Error); } } @@ -362,7 +376,7 @@ export default class OCPP16RequestService extends OCPPRequestService { }; await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.METER_VALUES); } catch (error) { - this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); + this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error as Error); } } @@ -373,16 +387,25 @@ export default class OCPP16RequestService extends OCPPRequestService { }; await this.sendMessage(Utils.generateUUID(), payload, MessageType.CALL_MESSAGE, OCPP16RequestCommand.DIAGNOSTICS_STATUS_NOTIFICATION); } catch (error) { - this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error); + this.handleRequestError(OCPP16RequestCommand.METER_VALUES, error as Error); } } - public async sendError(messageId: string, error: OCPPError, commandName: OCPP16RequestCommand | OCPP16IncomingRequestCommand): Promise { + public async sendResult(messageId: string, resultMessageData: JsonType, commandName: OCPP16RequestCommand | OCPP16IncomingRequestCommand): Promise { try { // Send error - return this.sendMessage(messageId, error, MessageType.CALL_ERROR_MESSAGE, commandName); - } catch (err) { - this.handleRequestError(commandName as OCPP16RequestCommand, err); + return await this.sendMessage(messageId, resultMessageData, MessageType.CALL_RESULT_MESSAGE, commandName) as JsonType; + } catch (error) { + this.handleRequestError(commandName as OCPP16RequestCommand, error as Error); + } + } + + public async sendError(messageId: string, ocppError: OCPPError, commandName: OCPP16RequestCommand | OCPP16IncomingRequestCommand): Promise { + try { + // Send error + return await this.sendMessage(messageId, ocppError, MessageType.CALL_ERROR_MESSAGE, commandName) as JsonType; + } catch (error) { + this.handleRequestError(commandName as OCPP16RequestCommand, error as Error); } } }