1 import type { DefinedError
, ErrorObject
} from
'ajv';
3 import BaseError from
'../../exception/BaseError';
4 import type { SampledValueTemplate
} from
'../../types/MeasurandPerPhaseSampledValueTemplates';
5 import { StandardParametersKey
} from
'../../types/ocpp/Configuration';
6 import { ErrorType
} from
'../../types/ocpp/ErrorType';
7 import { MeterValueMeasurand
, type MeterValuePhase
} from
'../../types/ocpp/MeterValues';
8 import { IncomingRequestCommand
, MessageTrigger
, RequestCommand
} from
'../../types/ocpp/Requests';
9 import Constants from
'../../utils/Constants';
10 import logger from
'../../utils/Logger';
11 import Utils from
'../../utils/Utils';
12 import type ChargingStation from
'../ChargingStation';
13 import { ChargingStationConfigurationUtils
} from
'../ChargingStationConfigurationUtils';
15 export class OCPPServiceUtils
{
16 protected constructor() {
17 // This is intentional
20 public static ajvErrorsToErrorType(errors
: ErrorObject
[]): ErrorType
{
21 for (const error
of errors
as DefinedError
[]) {
22 switch (error
.keyword
) {
24 return ErrorType
.TYPE_CONSTRAINT_VIOLATION
;
27 return ErrorType
.OCCURRENCE_CONSTRAINT_VIOLATION
;
30 return ErrorType
.PROPERTY_CONSTRAINT_VIOLATION
;
33 return ErrorType
.FORMAT_VIOLATION
;
36 public static isRequestCommandSupported(
37 chargingStation
: ChargingStation
,
38 command
: RequestCommand
40 const isRequestCommand
= Object.values
<RequestCommand
>(RequestCommand
).includes(command
);
42 isRequestCommand
=== true &&
43 !chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
47 isRequestCommand
=== true &&
48 chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
50 return chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
[command
] ?? false;
52 logger
.error(`${chargingStation.logPrefix()} Unknown outgoing OCPP command '${command}'`);
56 public static isIncomingRequestCommandSupported(
57 chargingStation
: ChargingStation
,
58 command
: IncomingRequestCommand
60 const isIncomingRequestCommand
=
61 Object.values
<IncomingRequestCommand
>(IncomingRequestCommand
).includes(command
);
63 isIncomingRequestCommand
=== true &&
64 !chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
68 isIncomingRequestCommand
=== true &&
69 chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
71 return chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
[command
] ?? false;
73 logger
.error(`${chargingStation.logPrefix()} Unknown incoming OCPP command '${command}'`);
77 public static isMessageTriggerSupported(
78 chargingStation
: ChargingStation
,
79 messageTrigger
: MessageTrigger
81 const isMessageTrigger
= Object.values(MessageTrigger
).includes(messageTrigger
);
82 if (isMessageTrigger
=== true && !chargingStation
.stationInfo
?.messageTriggerSupport
) {
84 } else if (isMessageTrigger
=== true && chargingStation
.stationInfo
?.messageTriggerSupport
) {
85 return chargingStation
.stationInfo
?.messageTriggerSupport
[messageTrigger
] ?? false;
88 `${chargingStation.logPrefix()} Unknown incoming OCPP message trigger '${messageTrigger}'`
93 public static isConnectorIdValid(
94 chargingStation
: ChargingStation
,
95 ocppCommand
: IncomingRequestCommand
,
98 if (connectorId
< 0) {
100 `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector Id ${connectorId}`
107 protected static getSampledValueTemplate(
108 chargingStation
: ChargingStation
,
110 measurand
: MeterValueMeasurand
= MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
,
111 phase
?: MeterValuePhase
112 ): SampledValueTemplate
| undefined {
113 const onPhaseStr
= phase
? `on phase ${phase} ` : '';
114 if (Constants
.SUPPORTED_MEASURANDS
.includes(measurand
) === false) {
116 `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
121 measurand
!== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
&&
122 ChargingStationConfigurationUtils
.getConfigurationKey(
124 StandardParametersKey
.MeterValuesSampledData
125 )?.value
.includes(measurand
) === false
128 `${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId} not found in '${
129 StandardParametersKey.MeterValuesSampledData
134 const sampledValueTemplates
: SampledValueTemplate
[] =
135 chargingStation
.getConnectorStatus(connectorId
).MeterValues
;
138 Utils
.isEmptyArray(sampledValueTemplates
) === false && index
< sampledValueTemplates
.length
;
142 Constants
.SUPPORTED_MEASURANDS
.includes(
143 sampledValueTemplates
[index
]?.measurand
??
144 MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
148 `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
152 sampledValueTemplates
[index
]?.phase
=== phase
&&
153 sampledValueTemplates
[index
]?.measurand
=== measurand
&&
154 ChargingStationConfigurationUtils
.getConfigurationKey(
156 StandardParametersKey
.MeterValuesSampledData
157 )?.value
.includes(measurand
) === true
159 return sampledValueTemplates
[index
];
162 !sampledValueTemplates
[index
].phase
&&
163 sampledValueTemplates
[index
]?.measurand
=== measurand
&&
164 ChargingStationConfigurationUtils
.getConfigurationKey(
166 StandardParametersKey
.MeterValuesSampledData
167 )?.value
.includes(measurand
) === true
169 return sampledValueTemplates
[index
];
171 measurand
=== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
&&
172 (!sampledValueTemplates
[index
].measurand
||
173 sampledValueTemplates
[index
].measurand
=== measurand
)
175 return sampledValueTemplates
[index
];
178 if (measurand
=== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
179 const errorMsg
= `Missing MeterValues for default measurand '${measurand}' in template on connectorId ${connectorId}`;
180 logger
.error(`${chargingStation.logPrefix()} ${errorMsg}`);
181 throw new BaseError(errorMsg
);
184 `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
188 protected static getLimitFromSampledValueTemplateCustomValue(
191 options
: { limitationEnabled
?: boolean; unitMultiplier
?: number } = {
192 limitationEnabled
: true,
196 options
.limitationEnabled
= options
?.limitationEnabled
?? true;
197 options
.unitMultiplier
= options
?.unitMultiplier
?? 1;
198 const parsedInt
= parseInt(value
);
199 const numberValue
= isNaN(parsedInt
) ? Infinity : parsedInt
;
200 return options
?.limitationEnabled
201 ? Math.min(numberValue
* options
.unitMultiplier
, limit
)
202 : numberValue
* options
.unitMultiplier
;