Reference: https://github.com/SAP/e-mobility-charging-stations-simulator/issues/349
Signed-off-by: Jérôme Benoit <jerome.benoit@sap.com>
"cacheable",
"commitlint",
"CSMS",
+ "evse",
+ "evses",
"iccid",
"imsi",
"lcov",
type ErrorCallback,
type ErrorResponse,
ErrorType,
+ type EvseStatus,
FileType,
FirmwareStatus,
type FirmwareStatusNotificationRequest,
public ocppConfiguration!: ChargingStationOcppConfiguration | undefined;
public wsConnection!: WebSocket | null;
public readonly connectors: Map<number, ConnectorStatus>;
+ public readonly evses: Map<number, EvseStatus>;
public readonly requests: Map<string, CachedRequest>;
public performanceStatistics!: PerformanceStatistics | undefined;
public heartbeatSetInterval!: NodeJS.Timeout;
private configurationFile!: string;
private configurationFileHash!: string;
private connectorsConfigurationHash!: string;
+ private evsesConfigurationHash!: string;
private ocppIncomingRequestService!: OCPPIncomingRequestService;
private readonly messageBuffer: Set<string>;
private configuredSupervisionUrl!: URL;
this.index = index;
this.templateFile = templateFile;
this.connectors = new Map<number, ConnectorStatus>();
+ this.evses = new Map<number, EvseStatus>();
this.requests = new Map<string, CachedRequest>();
this.messageBuffer = new Set<string>();
this.sharedLRUCache = SharedLRUCache.getInstance();
public startMeterValues(connectorId: number, interval: number): void {
if (connectorId === 0) {
logger.error(
- `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId.toString()}`
+ `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId.toString()}`
);
return;
}
if (!this.getConnectorStatus(connectorId)) {
logger.error(
- `${this.logPrefix()} Trying to start MeterValues on non existing connector Id ${connectorId.toString()}`
+ `${this.logPrefix()} Trying to start MeterValues on non existing connector id ${connectorId.toString()}`
);
return;
}
if (this.getConnectorStatus(connectorId)?.transactionStarted === false) {
logger.error(
- `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction started`
+ `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction started`
);
return;
} else if (
Utils.isNullOrUndefined(this.getConnectorStatus(connectorId)?.transactionId)
) {
logger.error(
- `${this.logPrefix()} Trying to start MeterValues on connector Id ${connectorId} with no transaction id`
+ `${this.logPrefix()} Trying to start MeterValues on connector id ${connectorId} with no transaction id`
);
return;
}
}
// Build connectors if needed (FIXME: should be factored out)
this.initializeConnectors(stationInfo, configuredMaxConnectors, templateMaxConnectors);
+ this.initializeEvses(stationInfo);
stationInfo.maximumAmperage = this.getMaximumAmperage(stationInfo);
ChargingStationUtils.createStationInfoHash(stationInfo);
return stationInfo;
logger.warn(
`${this.logPrefix()} Charging station information from template ${
this.templateFile
- } with no connector Id 0 configuration`
+ } with no connector id 0 configuration`
);
}
if (stationInfo?.Connectors) {
if (this.connectors?.size === 0 || connectorsConfigChanged) {
connectorsConfigChanged && this.connectors.clear();
this.connectorsConfigurationHash = connectorsConfigHash;
- // Add connector Id 0
+ // Add connector id 0
let lastConnector = '0';
for (lastConnector in stationInfo?.Connectors) {
const connectorStatus = stationInfo?.Connectors[lastConnector];
}
}
+ private initializeEvses(stationInfo: ChargingStationInfo): void {
+ if (!stationInfo?.Evses && this.evses.size === 0) {
+ const logMsg = `No already defined evses and charging station information from template ${this.templateFile} with no evses configuration defined`;
+ logger.warn(`${this.logPrefix()} ${logMsg}`);
+ return;
+ }
+ if (!stationInfo?.Evses[0]) {
+ logger.warn(
+ `${this.logPrefix()} Charging station information from template ${
+ this.templateFile
+ } with no evse id 0 configuration`
+ );
+ }
+ if (stationInfo?.Evses) {
+ const evsesConfigHash = crypto
+ .createHash(Constants.DEFAULT_HASH_ALGORITHM)
+ .update(`${JSON.stringify(stationInfo?.Evses)}`)
+ .digest('hex');
+ const evsesConfigChanged =
+ this.evses?.size !== 0 && this.evsesConfigurationHash !== evsesConfigHash;
+ if (this.evses?.size === 0 || evsesConfigChanged) {
+ evsesConfigChanged && this.evses.clear();
+ this.evsesConfigurationHash = evsesConfigHash;
+ for (const evse in stationInfo?.Evses) {
+ const evseId = Utils.convertToInt(evse);
+ this.evses.set(evseId, Utils.cloneObject<EvseStatus>(stationInfo?.Evses[evse]));
+ this.evses.get(evseId).availability = AvailabilityType.OPERATIVE;
+ }
+ }
+ } else {
+ if (this.connectors.size === 0) {
+ const logMsg = `No already defined connectors and charging station information from template ${this.templateFile} with no evses configuration defined`;
+ logger.error(`${this.logPrefix()} ${logMsg}`);
+ throw new BaseError(logMsg);
+ }
+ logger.info(
+ `${this.logPrefix()} Charging station information from template ${
+ this.templateFile
+ } with no evses configuration defined, mapping one connector to one evse`
+ );
+ for (const [connectorId, connectorStatus] of this.connectors) {
+ this.evses.set(connectorId, {
+ connectorIds: [connectorId],
+ availability: connectorStatus.availability,
+ });
+ }
+ }
+ }
+
private checkStationInfoConnectorStatus(
connectorId: number,
connectorStatus: ConnectorStatus
const connectorId = commandPayload.connectorId;
if (chargingStation.connectors.has(connectorId) === false) {
logger.error(
- `${chargingStation.logPrefix()} Trying to unlock a non existing connector Id ${connectorId.toString()}`
+ `${chargingStation.logPrefix()} Trying to unlock a non existing connector id ${connectorId.toString()}`
);
return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
}
if (connectorId === 0) {
logger.error(
- `${chargingStation.logPrefix()} Trying to unlock connector Id ${connectorId.toString()}`
+ `${chargingStation.logPrefix()} Trying to unlock connector id ${connectorId.toString()}`
);
return OCPP16Constants.OCPP_RESPONSE_UNLOCK_NOT_SUPPORTED;
}
}
if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
logger.error(
- `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector Id ${
+ `${chargingStation.logPrefix()} Trying to set charging profile(s) to a non existing connector id ${
commandPayload.connectorId
}`
);
}
if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
logger.error(
- `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector Id ${
+ `${chargingStation.logPrefix()} Trying to get composite schedule to a non existing connector id ${
commandPayload.connectorId
}`
);
}
if (chargingStation.connectors.has(commandPayload.connectorId) === false) {
logger.error(
- `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector Id ${
+ `${chargingStation.logPrefix()} Trying to clear a charging profile(s) to a non existing connector id ${
commandPayload.connectorId
}`
);
const connectorId: number = commandPayload.connectorId;
if (chargingStation.connectors.has(connectorId) === false) {
logger.error(
- `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector Id ${connectorId.toString()}`
+ `${chargingStation.logPrefix()} Trying to change the availability of a non existing connector id ${connectorId.toString()}`
);
return OCPP16Constants.OCPP_AVAILABILITY_RESPONSE_REJECTED;
}
);
}
logger.warn(
- `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector Id ${connectorId.toString()}, idTag '${idTag}', availability '${
+ `${chargingStation.logPrefix()} Remote starting transaction REJECTED on connector id ${connectorId.toString()}, idTag '${idTag}', availability '${
chargingStation.getConnectorStatus(connectorId)?.availability
}', status '${chargingStation.getConnectorStatus(connectorId)?.status}'`
);
authorizeConnectorIdDefined &&
(chargingStation.getConnectorStatus(authorizeConnectorId).idTagAuthorized = true);
logger.debug(
- `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' accepted${
+ `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' accepted${
authorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''
}`
);
delete chargingStation.getConnectorStatus(authorizeConnectorId)?.authorizeIdTag;
}
logger.debug(
- `${chargingStation.logPrefix()} IdTag '${requestPayload.idTag}' rejected with status '${
+ `${chargingStation.logPrefix()} idTag '${requestPayload.idTag}' rejected with status '${
payload.idTagInfo.status
}'${authorizeConnectorIdDefined ? ` on connector ${authorizeConnectorId}` : ''}`
);
}
if (Utils.isNullOrUndefined(transactionConnectorId)) {
logger.error(
- `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector Id ${connectorId.toString()}`
+ `${chargingStation.logPrefix()} Trying to start a transaction on a non existing connector id ${connectorId.toString()}`
);
return;
}
logger.error(
`${chargingStation.logPrefix()} Trying to start a transaction with a not local authorized idTag ${
chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
- } on connector Id ${connectorId.toString()}`
+ } on connector id ${connectorId.toString()}`
);
await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
logger.error(
`${chargingStation.logPrefix()} Trying to start a transaction with a not authorized idTag ${
chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
- } on connector Id ${connectorId.toString()}`
+ } on connector id ${connectorId.toString()}`
);
await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
requestPayload.idTag
} different from the authorize request one ${
chargingStation.getConnectorStatus(connectorId)?.authorizeIdTag
- } on connector Id ${connectorId.toString()}`
+ } on connector id ${connectorId.toString()}`
);
await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
requestPayload.idTag
} different from the local authorized one ${
chargingStation.getConnectorStatus(connectorId)?.localAuthorizeIdTag
- } on connector Id ${connectorId.toString()}`
+ } on connector id ${connectorId.toString()}`
);
await this.resetConnectorOnStartTransactionError(chargingStation, connectorId);
return;
): 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;
}
AutomaticTransactionGeneratorConfiguration,
ChargingStationOcppConfiguration,
ConnectorStatus,
+ EvseStatus,
FirmwareStatus,
IncomingRequestCommand,
MessageTrigger,
messageTriggerSupport?: Record<MessageTrigger, boolean>;
Configuration?: ChargingStationOcppConfiguration;
AutomaticTransactionGenerator?: AutomaticTransactionGeneratorConfiguration;
+ Evses?: Record<string, EvseStatus>;
Connectors: Record<string, ConnectorStatus>;
};
type ErrorCallback,
type ErrorResponse,
ErrorType,
+ type EvseStatus,
FileType,
FirmwareStatus,
type FirmwareStatusNotificationRequest,
export * from './ChargingStationTemplate';
export * from './ChargingStationWorker';
export * from './ConfigurationData';
+export type { EvseStatus } from './EvseStatus';
export type { ConnectorStatus } from './ConnectorStatus';
export type { EmptyObject } from './EmptyObject';
export type { HandleErrorParams } from './Error';
Watchdog = 'Watchdog',
}
+export enum OperationalStatusEnumType {
+ Operative = 'Operative',
+ Inoperative = 'Inoperative',
+}
+
export enum OCPP20ConnectorStatusEnumType {
Available = 'Available',
Occupied = 'Occupied',
OCPP20IncomingRequestCommand,
OCPP20RequestCommand,
type OCPP20StatusNotificationRequest,
+ OperationalStatusEnumType,
} from '../internal';
export const RequestCommand = {
export const AvailabilityType = {
...OCPP16AvailabilityType,
+ ...OperationalStatusEnumType,
} as const;
-export type AvailabilityType = OCPP16AvailabilityType;
+export type AvailabilityType = OCPP16AvailabilityType | OperationalStatusEnumType;
export const DiagnosticsStatus = {
...OCPP16DiagnosticsStatus,