Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Change spotless to use googleJavaFormat('1.28.0') [#1409](https://github.com/ie3-institute/PowerSystemDataModel/issues/1409)
- Change `TimeSeries` to no longer extend `UniqueEntity` [#1441](https://github.com/ie3-institute/PowerSystemDataModel/issues/1441)
- Refactor CSV handling into shared file-based infrastructure. [#1450](https://github.com/ie3-institute/PowerSystemDataModel/issues/1445)
- Change Evcs.locationType from single element to list [#1460](https://github.com/ie3-institute/PowerSystemDataModel/issues/1460)

## [8.1.0] - 2025-07-25

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import edu.ie3.datamodel.models.input.system.type.chargingpoint.ChargingPointTypeUtils;
import edu.ie3.datamodel.models.input.system.type.evcslocation.EvcsLocationType;
import edu.ie3.datamodel.models.input.system.type.evcslocation.EvcsLocationTypeUtils;
import java.util.List;
import java.util.UUID;

/**
Expand All @@ -33,7 +34,7 @@ public class EvcsInputFactory
private static final String TYPE = "type";
private static final String CHARGING_POINTS = "chargingPoints";
private static final String COS_PHI_RATED = "cosPhiRated";
private static final String LOCATION_TYPE = "locationType";
private static final String LOCATION_TYPES = "locationTypes";
private static final String V2G_SUPPORT = "v2gSupport";

public EvcsInputFactory() {
Expand All @@ -42,7 +43,7 @@ public EvcsInputFactory() {

@Override
protected String[] getAdditionalFields() {
return new String[] {TYPE, CHARGING_POINTS, COS_PHI_RATED, LOCATION_TYPE, V2G_SUPPORT};
return new String[] {TYPE, CHARGING_POINTS, COS_PHI_RATED, LOCATION_TYPES, V2G_SUPPORT};
}

@Override
Expand All @@ -68,15 +69,24 @@ protected EvcsInput buildModel(
final int chargingPoints = data.getInt(CHARGING_POINTS);
final double cosPhi = data.getDouble(COS_PHI_RATED);

final EvcsLocationType locationType;
final List<EvcsLocationType> locationTypes;
try {
locationType = EvcsLocationTypeUtils.parse(data.getField(LOCATION_TYPE));
} catch (ParsingException e) {
String locationTypesField = data.getField(LOCATION_TYPES);
if (locationTypesField.contains(",")) {
locationTypes = EvcsLocationTypeUtils.parse(locationTypesField);
} else {
locationTypes = List.of(EvcsLocationTypeUtils.parseSingle(locationTypesField));
}
} catch (ParsingException | RuntimeException e) {
Throwable cause =
e instanceof RuntimeException && e.getCause() instanceof ParsingException
? e.getCause()
: e;
throw new FactoryException(
String.format(
"Exception while trying to parse field \"%s\" with supposed int value \"%s\"",
LOCATION_TYPE, data.getField(LOCATION_TYPE)),
e);
"Exception while trying to parse field \"%s\" with supposed value \"%s\"",
LOCATION_TYPES, data.getField(LOCATION_TYPES)),
cause);
}

final boolean v2gSupport = data.getBoolean(V2G_SUPPORT);
Expand All @@ -92,7 +102,7 @@ protected EvcsInput buildModel(
type,
chargingPoints,
cosPhi,
locationType,
locationTypes,
v2gSupport);
}
}
10 changes: 9 additions & 1 deletion src/main/java/edu/ie3/datamodel/io/processor/Processor.java
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,16 @@ protected String processMethodResult(
"DayOfWeek",
"Season",
"ChargingPointType",
"EvcsLocationType" ->
"EvcsLocationTypes" ->
resultStringBuilder.append(methodReturnObject.toString());
case "List", "ArrayList" -> {
if (methodReturnObject instanceof Collection<?> collection) {
resultStringBuilder.append(
collection.stream().map(Object::toString).collect(Collectors.joining(",", "[", "]")));
} else {
resultStringBuilder.append(methodReturnObject.toString());
}
}
case "Quantity", "ComparableQuantity" ->
resultStringBuilder.append(handleQuantity((Quantity<?>) methodReturnObject, fieldName));
case "Optional" ->
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/edu/ie3/datamodel/io/sink/CsvFileSink.java
Original file line number Diff line number Diff line change
Expand Up @@ -330,8 +330,8 @@ private String[] csvHeaderElements(String[] strings) {
}

/**
* Transforms a provided map of string to string to valid csv formatted strings (according to csv
* specification RFC 4180)
* Transforms a provided map of string to string into valid csv formatted strings (according to
* csv specification RFC 4180)
*
* @param entityFieldData a string to string map that should be processed
* @return a new map with valid csv formatted keys and values strings
Expand Down
57 changes: 32 additions & 25 deletions src/main/java/edu/ie3/datamodel/models/input/system/EvcsInput.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import edu.ie3.datamodel.models.input.system.characteristic.ReactivePowerCharacteristic;
import edu.ie3.datamodel.models.input.system.type.chargingpoint.ChargingPointType;
import edu.ie3.datamodel.models.input.system.type.evcslocation.EvcsLocationType;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import javax.measure.quantity.Power;
Expand All @@ -28,8 +29,8 @@ public class EvcsInput extends SystemParticipantInput {
/** Rated power factor */
private final double cosPhiRated;

/** Evcs location type */
private final EvcsLocationType locationType;
/** Evcs location types (minimum one required) */
private final List<EvcsLocationType> locationTypes;

/** Whether charging station supports vehicle to grid */
private final boolean v2gSupport;
Expand All @@ -45,7 +46,7 @@ public class EvcsInput extends SystemParticipantInput {
* @param type type of the charging points available to this charging station
* @param chargingPoints number of charging points available at this charging station
* @param cosPhiRated rated cos phi
* @param locationType the location type
* @param locationTypes the location types (minimum one required)
* @param v2gSupport whether charging station supports vehicle to grid
*/
public EvcsInput(
Expand All @@ -59,13 +60,16 @@ public EvcsInput(
ChargingPointType type,
int chargingPoints,
double cosPhiRated,
EvcsLocationType locationType,
List<EvcsLocationType> locationTypes,
boolean v2gSupport) {
super(uuid, id, operator, operationTime, node, qCharacteristics, em);
if (locationTypes == null || locationTypes.isEmpty()) {
throw new IllegalArgumentException("At least one location type must be provided");
}
this.type = type;
this.chargingPoints = chargingPoints;
this.cosPhiRated = cosPhiRated;
this.locationType = locationType;
this.locationTypes = List.copyOf(locationTypes);
this.v2gSupport = v2gSupport;
}

Expand All @@ -79,7 +83,7 @@ public EvcsInput(
* @param em The {@link EmInput} controlling this system participant. Null, if not applicable.
* @param type type of the charging points available to this charging station
* @param cosPhiRated rated cos phi
* @param locationType the location type
* @param locationTypes the location types (minimum one required)
* @param v2gSupport whether charging station supports vehicle to grid
*/
public EvcsInput(
Expand All @@ -92,7 +96,7 @@ public EvcsInput(
EmInput em,
ChargingPointType type,
double cosPhiRated,
EvcsLocationType locationType,
List<EvcsLocationType> locationTypes,
boolean v2gSupport) {
this(
uuid,
Expand All @@ -105,7 +109,7 @@ public EvcsInput(
type,
1,
cosPhiRated,
locationType,
locationTypes,
v2gSupport);
}

Expand All @@ -118,7 +122,7 @@ public EvcsInput(
* @param type type of the charging points available to this charging station
* @param chargingPoints number of charging points available at this charging station
* @param cosPhiRated rated cos phi
* @param locationType the location type
* @param locationTypes the location types (minimum one required)
* @param v2gSupport whether charging station supports vehicle to grid
*/
public EvcsInput(
Expand All @@ -130,13 +134,16 @@ public EvcsInput(
ChargingPointType type,
int chargingPoints,
double cosPhiRated,
EvcsLocationType locationType,
List<EvcsLocationType> locationTypes,
boolean v2gSupport) {
super(uuid, id, node, qCharacteristics, em);
if (locationTypes == null || locationTypes.isEmpty()) {
throw new IllegalArgumentException("At least one location type must be provided");
}
this.type = type;
this.chargingPoints = chargingPoints;
this.cosPhiRated = cosPhiRated;
this.locationType = locationType;
this.locationTypes = List.copyOf(locationTypes);
this.v2gSupport = v2gSupport;
}

Expand All @@ -148,7 +155,7 @@ public EvcsInput(
* @param em The {@link EmInput} controlling this system participant. Null, if not applicable.
* @param type type of the charging points available to this charging station
* @param cosPhiRated rated cos phi
* @param locationType the location type
* @param locationTypes the location types (minimum one required)
* @param v2gSupport whether charging station supports vehicle to grid
*/
public EvcsInput(
Expand All @@ -159,9 +166,9 @@ public EvcsInput(
EmInput em,
ChargingPointType type,
double cosPhiRated,
EvcsLocationType locationType,
List<EvcsLocationType> locationTypes,
boolean v2gSupport) {
this(uuid, id, node, qCharacteristics, em, type, 1, cosPhiRated, locationType, v2gSupport);
this(uuid, id, node, qCharacteristics, em, type, 1, cosPhiRated, locationTypes, v2gSupport);
}

public ChargingPointType getType() {
Expand All @@ -176,8 +183,8 @@ public double getCosPhiRated() {
return cosPhiRated;
}

public EvcsLocationType getLocationType() {
return locationType;
public List<EvcsLocationType> getLocationTypes() {
return locationTypes;
}

public boolean getV2gSupport() {
Expand All @@ -202,13 +209,13 @@ public boolean equals(Object o) {
return chargingPoints == evcsInput.chargingPoints
&& Double.compare(evcsInput.cosPhiRated, cosPhiRated) == 0
&& type.equals(evcsInput.type)
&& locationType.equals(evcsInput.locationType)
&& locationTypes.equals(evcsInput.locationTypes)
&& v2gSupport == evcsInput.v2gSupport;
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), type, chargingPoints, cosPhiRated, locationType);
return Objects.hash(super.hashCode(), type, chargingPoints, cosPhiRated, locationTypes);
}

@Override
Expand All @@ -234,8 +241,8 @@ public String toString() {
+ chargingPoints
+ ", cosPhiRated="
+ cosPhiRated
+ ", locationType="
+ locationType
+ ", locationTypes="
+ locationTypes
+ ", v2gSupport="
+ getV2gSupport()
+ '}';
Expand All @@ -254,15 +261,15 @@ public static class EvcsInputCopyBuilder
private ChargingPointType type;
private int chargingPoints;
private double cosPhiRated;
private EvcsLocationType locationType;
private List<EvcsLocationType> locationTypes;
private boolean v2gSupport;

public EvcsInputCopyBuilder(EvcsInput entity) {
super(entity);
this.type = entity.type;
this.chargingPoints = entity.chargingPoints;
this.cosPhiRated = entity.cosPhiRated;
this.locationType = entity.locationType;
this.locationTypes = entity.locationTypes;
this.v2gSupport = entity.v2gSupport;
}

Expand All @@ -281,8 +288,8 @@ public EvcsInputCopyBuilder cosPhiRated(double cosPhiRated) {
return thisInstance();
}

public EvcsInputCopyBuilder locationType(EvcsLocationType locationType) {
this.locationType = locationType;
public EvcsInputCopyBuilder locationTypes(List<EvcsLocationType> locationTypes) {
this.locationTypes = locationTypes;
return thisInstance();
}

Expand Down Expand Up @@ -310,7 +317,7 @@ public EvcsInput build() {
type,
chargingPoints,
cosPhiRated,
locationType,
locationTypes,
v2gSupport);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
package edu.ie3.datamodel.models.input.system.type.evcslocation;

import edu.ie3.datamodel.exceptions.ParsingException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
* Utility class providing tools to retrieve {@link EvcsLocationType}s from string representation
Expand All @@ -28,15 +31,51 @@
}

/**
* Parsing a location type string into one {@link EvcsLocationType}. Matching the string is
* case-insensitive and all - and _ are removed. Throws exception, if type does not exist.
* Parsing a location type string into one {@link EvcsLocationType} or a list of
* EvcsLocationTypes. Matching the string is case-insensitive and all - and _ are removed.
*
* @param parsableString string to parse
* @return List<EvcsLocationType>
* @throws ParsingException if string does not represent a location type
*/
public static List<EvcsLocationType> parse(String parsableString) throws ParsingException {
if (parsableString == null || parsableString.trim().isEmpty()) {
throw new ParsingException("Location types string cannot be null or empty");
}

// Remove brackets if present
parsableString = parsableString.replace("[", "").replace("]", "");

// Check if it contains comma for multiple values
if (parsableString.contains(",")) {
return Arrays.stream(parsableString.split(","))
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(
s -> {
try {
return parseSingle(s);
} catch (ParsingException e) {
throw new RuntimeException(e);

Check warning on line 59 in src/main/java/edu/ie3/datamodel/models/input/system/type/evcslocation/EvcsLocationTypeUtils.java

View check run for this annotation

SonarQubeGithubPRChecks / SonarQube Code Analysis

src/main/java/edu/ie3/datamodel/models/input/system/type/evcslocation/EvcsLocationTypeUtils.java#L59

Define and throw a dedicated exception instead of using a generic one.
}
})
.collect(Collectors.toList());

Check warning on line 62 in src/main/java/edu/ie3/datamodel/models/input/system/type/evcslocation/EvcsLocationTypeUtils.java

View check run for this annotation

SonarQubeGithubPRChecks / SonarQube Code Analysis

src/main/java/edu/ie3/datamodel/models/input/system/type/evcslocation/EvcsLocationTypeUtils.java#L62

Replace this usage of 'Stream.collect(Collectors.toList())' with 'Stream.toList()' and ensure that the list is unmodified.
} else {
// Single value - wrap in List
return List.of(parseSingle(parsableString.trim()));
}
}

/**
* Parsing a single location type string into one {@link EvcsLocationType}. Matching the string is
* case-insensitive and all - and _ are removed.
*
* @param parsableString string to parse
* @return corresponding EvcsLocationType
* @throws ParsingException if string does not represent a location type
*/
public static EvcsLocationType parse(String parsableString) throws ParsingException {
final String key = toKey(parsableString);
public static EvcsLocationType parseSingle(String parsableString) throws ParsingException {
final String key = toKey(parsableString.replace("[", "").replace("]", ""));
if (nameToType.containsKey(key)) return nameToType.get(key);
else throw new ParsingException("EvcsLocationType '" + key + "' does not exist.");
}
Expand Down
Loading