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 type { OCPP16StatusNotificationRequest
} from
'../../types/ocpp/1.6/Requests';
7 import type { OCPP20StatusNotificationRequest
} from
'../../types/ocpp/2.0/Requests';
8 import { ChargePointErrorCode
} from
'../../types/ocpp/ChargePointErrorCode';
9 import { StandardParametersKey
} from
'../../types/ocpp/Configuration';
10 import type { ConnectorStatusEnum
} from
'../../types/ocpp/ConnectorStatusEnum';
11 import { ErrorType
} from
'../../types/ocpp/ErrorType';
12 import { MessageType
} from
'../../types/ocpp/MessageType';
13 import { MeterValueMeasurand
, type MeterValuePhase
} from
'../../types/ocpp/MeterValues';
14 import { OCPPVersion
} from
'../../types/ocpp/OCPPVersion';
16 IncomingRequestCommand
,
19 type StatusNotificationRequest
,
20 } from
'../../types/ocpp/Requests';
21 import Constants from
'../../utils/Constants';
22 import logger from
'../../utils/Logger';
23 import Utils from
'../../utils/Utils';
24 import type ChargingStation from
'../ChargingStation';
25 import { ChargingStationConfigurationUtils
} from
'../ChargingStationConfigurationUtils';
27 export class OCPPServiceUtils
{
28 protected constructor() {
29 // This is intentional
32 public static ajvErrorsToErrorType(errors
: ErrorObject
[]): ErrorType
{
33 for (const error
of errors
as DefinedError
[]) {
34 switch (error
.keyword
) {
36 return ErrorType
.TYPE_CONSTRAINT_VIOLATION
;
39 return ErrorType
.OCCURRENCE_CONSTRAINT_VIOLATION
;
42 return ErrorType
.PROPERTY_CONSTRAINT_VIOLATION
;
45 return ErrorType
.FORMAT_VIOLATION
;
48 public static getMessageTypeString(messageType
: MessageType
): string {
49 switch (messageType
) {
50 case MessageType
.CALL_MESSAGE
:
52 case MessageType
.CALL_RESULT_MESSAGE
:
54 case MessageType
.CALL_ERROR_MESSAGE
:
61 public static isRequestCommandSupported(
62 chargingStation
: ChargingStation
,
63 command
: RequestCommand
65 const isRequestCommand
= Object.values
<RequestCommand
>(RequestCommand
).includes(command
);
67 isRequestCommand
=== true &&
68 !chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
72 isRequestCommand
=== true &&
73 chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
75 return chargingStation
.stationInfo
?.commandsSupport
?.outgoingCommands
[command
] ?? false;
77 logger
.error(`${chargingStation.logPrefix()} Unknown outgoing OCPP command '${command}'`);
81 public static isIncomingRequestCommandSupported(
82 chargingStation
: ChargingStation
,
83 command
: IncomingRequestCommand
85 const isIncomingRequestCommand
=
86 Object.values
<IncomingRequestCommand
>(IncomingRequestCommand
).includes(command
);
88 isIncomingRequestCommand
=== true &&
89 !chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
93 isIncomingRequestCommand
=== true &&
94 chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
96 return chargingStation
.stationInfo
?.commandsSupport
?.incomingCommands
[command
] ?? false;
98 logger
.error(`${chargingStation.logPrefix()} Unknown incoming OCPP command '${command}'`);
102 public static isMessageTriggerSupported(
103 chargingStation
: ChargingStation
,
104 messageTrigger
: MessageTrigger
106 const isMessageTrigger
= Object.values(MessageTrigger
).includes(messageTrigger
);
107 if (isMessageTrigger
=== true && !chargingStation
.stationInfo
?.messageTriggerSupport
) {
109 } else if (isMessageTrigger
=== true && chargingStation
.stationInfo
?.messageTriggerSupport
) {
110 return chargingStation
.stationInfo
?.messageTriggerSupport
[messageTrigger
] ?? false;
113 `${chargingStation.logPrefix()} Unknown incoming OCPP message trigger '${messageTrigger}'`
118 public static isConnectorIdValid(
119 chargingStation
: ChargingStation
,
120 ocppCommand
: IncomingRequestCommand
,
123 if (connectorId
< 0) {
125 `${chargingStation.logPrefix()} ${ocppCommand} incoming request received with invalid connector Id ${connectorId}`
132 public static convertDateToISOString
<T
extends JsonType
>(obj
: T
): void {
133 for (const key
in obj
) {
134 if (obj
[key
] instanceof Date) {
135 (obj
as JsonObject
)[key
] = (obj
[key
] as Date).toISOString();
136 } else if (obj
[key
] !== null && typeof obj
[key
] === 'object') {
137 this.convertDateToISOString
<T
>(obj
[key
] as T
);
142 public static buildStatusNotificationRequest(
143 chargingStation
: ChargingStation
,
145 status: ConnectorStatusEnum
146 ): StatusNotificationRequest
{
147 switch (chargingStation
.stationInfo
.ocppVersion
?? OCPPVersion
.VERSION_16
) {
148 case OCPPVersion
.VERSION_16
:
152 errorCode
: ChargePointErrorCode
.NO_ERROR
,
153 } as OCPP16StatusNotificationRequest
;
154 case OCPPVersion
.VERSION_20
:
155 case OCPPVersion
.VERSION_201
:
157 timestamp
: new Date(),
158 connectorStatus
: status,
161 } as OCPP20StatusNotificationRequest
;
163 throw new BaseError('Cannot build status notification payload: OCPP version not supported');
167 protected static getSampledValueTemplate(
168 chargingStation
: ChargingStation
,
170 measurand
: MeterValueMeasurand
= MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
,
171 phase
?: MeterValuePhase
172 ): SampledValueTemplate
| undefined {
173 const onPhaseStr
= phase
? `on phase ${phase} ` : '';
174 if (Constants
.SUPPORTED_MEASURANDS
.includes(measurand
) === false) {
176 `${chargingStation.logPrefix()} Trying to get unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
181 measurand
!== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
&&
182 ChargingStationConfigurationUtils
.getConfigurationKey(
184 StandardParametersKey
.MeterValuesSampledData
185 )?.value
?.includes(measurand
) === false
188 `${chargingStation.logPrefix()} Trying to get MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId} not found in '${
189 StandardParametersKey.MeterValuesSampledData
194 const sampledValueTemplates
: SampledValueTemplate
[] =
195 chargingStation
.getConnectorStatus(connectorId
)?.MeterValues
;
198 Utils
.isEmptyArray(sampledValueTemplates
) === false && index
< sampledValueTemplates
.length
;
202 Constants
.SUPPORTED_MEASURANDS
.includes(
203 sampledValueTemplates
[index
]?.measurand
??
204 MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
208 `${chargingStation.logPrefix()} Unsupported MeterValues measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
212 sampledValueTemplates
[index
]?.phase
=== phase
&&
213 sampledValueTemplates
[index
]?.measurand
=== measurand
&&
214 ChargingStationConfigurationUtils
.getConfigurationKey(
216 StandardParametersKey
.MeterValuesSampledData
217 )?.value
?.includes(measurand
) === true
219 return sampledValueTemplates
[index
];
222 !sampledValueTemplates
[index
].phase
&&
223 sampledValueTemplates
[index
]?.measurand
=== measurand
&&
224 ChargingStationConfigurationUtils
.getConfigurationKey(
226 StandardParametersKey
.MeterValuesSampledData
227 )?.value
?.includes(measurand
) === true
229 return sampledValueTemplates
[index
];
231 measurand
=== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
&&
232 (!sampledValueTemplates
[index
].measurand
||
233 sampledValueTemplates
[index
].measurand
=== measurand
)
235 return sampledValueTemplates
[index
];
238 if (measurand
=== MeterValueMeasurand
.ENERGY_ACTIVE_IMPORT_REGISTER
) {
239 const errorMsg
= `Missing MeterValues for default measurand '${measurand}' in template on connectorId ${connectorId}`;
240 logger
.error(`${chargingStation.logPrefix()} ${errorMsg}`);
241 throw new BaseError(errorMsg
);
244 `${chargingStation.logPrefix()} No MeterValues for measurand '${measurand}' ${onPhaseStr}in template on connectorId ${connectorId}`
248 protected static getLimitFromSampledValueTemplateCustomValue(
251 options
: { limitationEnabled
?: boolean; unitMultiplier
?: number } = {
252 limitationEnabled
: true,
256 options
.limitationEnabled
= options
?.limitationEnabled
?? true;
257 options
.unitMultiplier
= options
?.unitMultiplier
?? 1;
258 const parsedInt
= parseInt(value
);
259 const numberValue
= isNaN(parsedInt
) ? Infinity : parsedInt
;
260 return options
?.limitationEnabled
261 ? Math.min(numberValue
* options
.unitMultiplier
, limit
)
262 : numberValue
* options
.unitMultiplier
;