1 import type { DefinedError
, ErrorObject
} from
'ajv';
3 import BaseError from
'../../exception/BaseError';
4 import type { JsonObject
, JsonType
} from
'../../types/JsonType';
5 import type { SampledValueTemplate
} from
'../../types/MeasurandPerPhaseSampledValueTemplates';
6 import { StandardParametersKey
} from
'../../types/ocpp/Configuration';
7 import { ErrorType
} from
'../../types/ocpp/ErrorType';
8 import { MeterValueMeasurand
, type MeterValuePhase
} from
'../../types/ocpp/MeterValues';
9 import { IncomingRequestCommand
, MessageTrigger
, RequestCommand
} from
'../../types/ocpp/Requests';
10 import Constants from
'../../utils/Constants';
11 import logger from
'../../utils/Logger';
12 import Utils from
'../../utils/Utils';
13 import type ChargingStation from
'../ChargingStation';
14 import { ChargingStationConfigurationUtils
} from
'../ChargingStationConfigurationUtils';
16 export class OCPPServiceUtils
{
17 protected constructor() {
18 // This is intentional
21 public static ajvErrorsToErrorType(errors
: ErrorObject
[]): ErrorType
{
22 for (const error
of errors
as DefinedError
[]) {
23 switch (error
.keyword
) {
25 return ErrorType
.TYPE_CONSTRAINT_VIOLATION
;
28 return ErrorType
.OCCURRENCE_CONSTRAINT_VIOLATION
;
31 return ErrorType
.PROPERTY_CONSTRAINT_VIOLATION
;
34 return ErrorType
.FORMAT_VIOLATION
;
37 public static isRequestCommandSupported(
38 chargingStation
: ChargingStation
,
39 command
: RequestCommand
41 const isRequestCommand
= Object.values
<RequestCommand
>(RequestCommand
).includes(command
);
43 isRequestCommand
=== true &&
44 !chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
48 isRequestCommand
=== true &&
49 chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
51 return chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
[command
] ?? false;
53 logger
.error(`${chargingStation.logPrefix()} Unknown outgoing OCPP command '${command}'`);
57 public static isIncomingRequestCommandSupported(
58 chargingStation
: ChargingStation
,
59 command
: IncomingRequestCommand
61 const isIncomingRequestCommand
=
62 Object.values
<IncomingRequestCommand
>(IncomingRequestCommand
).includes(command
);
64 isIncomingRequestCommand
=== true &&
65 !chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
69 isIncomingRequestCommand
=== true &&
70 chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
72 return chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
[command
] ?? false;
74 logger
.error(`${chargingStation.logPrefix()} Unknown incoming OCPP command '${command}'`);
78 public static isMessageTriggerSupported(
79 chargingStation
: ChargingStation
,
80 messageTrigger
: MessageTrigger
82 const isMessageTrigger
= Object.values(MessageTrigger
).includes(messageTrigger
);
83 if (isMessageTrigger
=== true && !chargingStation
.stationInfo
?.messageTriggerSupport
) {
85 } else if (isMessageTrigger
=== true && chargingStation
.stationInfo
?.messageTriggerSupport
) {
86 return chargingStation
.stationInfo
?.messageTriggerSupport
[messageTrigger
] ?? false;
89 `${chargingStation.logPrefix()} Unknown incoming OCPP message trigger '${messageTrigger}'`
94 public static isConnectorIdValid(
95 chargingStation
: ChargingStation
,
96 ocppCommand
: IncomingRequestCommand
,
99 if (connectorId
< 0) {
101 `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector Id ${connectorId}`
108 public static convertDateToISOString
<T
extends JsonType
>(obj
: T
): void {
109 for (const key
in obj
) {
110 if (obj
[key
] instanceof Date) {
111 (obj
as JsonObject
)[key
] = (obj
[key
] as Date).toISOString();
112 } else if (obj
[key
] !== null && typeof obj
[key
] === 'object') {
113 this.convertDateToISOString
<T
>(obj
[key
] as T
);
118 protected static getSampledValueTemplate(
119 chargingStation
: ChargingStation
,
121 measurand
: MeterValueMeasurand
= MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
,
122 phase
?: MeterValuePhase
123 ): SampledValueTemplate
| undefined {
124 const onPhaseStr
= phase
? `on phase ${phase} ` : '';
125 if (Constants
.SUPPORTED_MEASURANDS
.includes(measurand
) === false) {
127 `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
132 measurand
!== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
&&
133 ChargingStationConfigurationUtils
.getConfigurationKey(
135 StandardParametersKey
.MeterValuesSampledData
136 )?.value
.includes(measurand
) === false
139 `${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId} not found in '${
140 StandardParametersKey.MeterValuesSampledData
145 const sampledValueTemplates
: SampledValueTemplate
[] =
146 chargingStation
.getConnectorStatus(connectorId
).MeterValues
;
149 Utils
.isEmptyArray(sampledValueTemplates
) === false && index
< sampledValueTemplates
.length
;
153 Constants
.SUPPORTED_MEASURANDS
.includes(
154 sampledValueTemplates
[index
]?.measurand
??
155 MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
159 `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
163 sampledValueTemplates
[index
]?.phase
=== phase
&&
164 sampledValueTemplates
[index
]?.measurand
=== measurand
&&
165 ChargingStationConfigurationUtils
.getConfigurationKey(
167 StandardParametersKey
.MeterValuesSampledData
168 )?.value
.includes(measurand
) === true
170 return sampledValueTemplates
[index
];
173 !sampledValueTemplates
[index
].phase
&&
174 sampledValueTemplates
[index
]?.measurand
=== measurand
&&
175 ChargingStationConfigurationUtils
.getConfigurationKey(
177 StandardParametersKey
.MeterValuesSampledData
178 )?.value
.includes(measurand
) === true
180 return sampledValueTemplates
[index
];
182 measurand
=== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
&&
183 (!sampledValueTemplates
[index
].measurand
||
184 sampledValueTemplates
[index
].measurand
=== measurand
)
186 return sampledValueTemplates
[index
];
189 if (measurand
=== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
190 const errorMsg
= `Missing MeterValues for default measurand '${measurand}' in template on connectorId ${connectorId}`;
191 logger
.error(`${chargingStation.logPrefix()} ${errorMsg}`);
192 throw new BaseError(errorMsg
);
195 `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
199 protected static getLimitFromSampledValueTemplateCustomValue(
202 options
: { limitationEnabled
?: boolean; unitMultiplier
?: number } = {
203 limitationEnabled
: true,
207 options
.limitationEnabled
= options
?.limitationEnabled
?? true;
208 options
.unitMultiplier
= options
?.unitMultiplier
?? 1;
209 const parsedInt
= parseInt(value
);
210 const numberValue
= isNaN(parsedInt
) ? Infinity : parsedInt
;
211 return options
?.limitationEnabled
212 ? Math.min(numberValue
* options
.unitMultiplier
, limit
)
213 : numberValue
* options
.unitMultiplier
;