diff --git a/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesControlAreasSerDe.java b/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesControlAreasSerDe.java index 229c2c88756..8c1cd814a0f 100644 --- a/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesControlAreasSerDe.java +++ b/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesControlAreasSerDe.java @@ -77,10 +77,7 @@ public void write(CgmesControlAreas extension, SerializerContext context) { writer.writeStringAttribute("id", networkContext.getAnonymizer().anonymizeString(boundary.getDanglingLine().getId())); // TODO use TieLine Id and DanglingLine Id for reference instead of TieLine Id and Side - TwoSides side = getSide(boundary); - if (side != null) { - writer.writeEnumAttribute("side", side); - } + writer.writeEnumAttribute("side", getSide(boundary)); writer.writeEndNode(); } } diff --git a/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesTapChangersSerDe.java b/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesTapChangersSerDe.java index 2970f5c7080..30776711c68 100644 --- a/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesTapChangersSerDe.java +++ b/cgmes/cgmes-extensions/src/main/java/com/powsybl/cgmes/extensions/CgmesTapChangersSerDe.java @@ -10,10 +10,10 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.commons.extensions.AbstractExtensionSerDe; import com.powsybl.commons.extensions.ExtensionSerDe; -import com.powsybl.commons.io.TreeDataReader; -import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.commons.io.DeserializerContext; import com.powsybl.commons.io.SerializerContext; +import com.powsybl.commons.io.TreeDataReader; +import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.iidm.network.Connectable; import com.powsybl.iidm.serde.NetworkDeserializerContext; import com.powsybl.iidm.serde.NetworkSerializerContext; @@ -50,11 +50,9 @@ public void write(CgmesTapChangers extension, SerializerContext context) { writer.writeStringAttribute("id", tapChanger.getId()); writer.writeStringAttribute("combinedTapChangerId", tapChanger.getCombinedTapChangerId()); writer.writeStringAttribute("type", tapChanger.getType()); - if (tapChanger.isHidden()) { - writer.writeBooleanAttribute("hidden", true); - writer.writeIntAttribute("step", tapChanger.getStep() - .orElseThrow(() -> new PowsyblException("Step should be defined"))); - } + writer.writeBooleanAttribute("hidden", tapChanger.isHidden(), false); + writer.writeOptionalIntAttribute("step", + !tapChanger.isHidden() ? null : tapChanger.getStep().orElseThrow(() -> new PowsyblException("Step should be defined"))); writer.writeStringAttribute("controlId", tapChanger.getControlId()); writer.writeEndNode(); } @@ -75,10 +73,7 @@ public CgmesTapChangers read(C extendable, DeserializerContext context) { .setType(reader.readStringAttribute("type")) .setHiddenStatus(reader.readBooleanAttribute("hidden", false)) .setControlId(reader.readStringAttribute("controlId")); - Integer step = reader.readIntAttribute("step"); - if (step != null) { - adder.setStep(step); - } + reader.readOptionalIntAttribute("step").ifPresent(adder::setStep); context.getReader().readEndNode(); adder.add(); } else { diff --git a/commons-test/src/main/java/com/powsybl/commons/test/ComparisonUtils.java b/commons-test/src/main/java/com/powsybl/commons/test/ComparisonUtils.java index 6256f310c86..3dee28bf206 100644 --- a/commons-test/src/main/java/com/powsybl/commons/test/ComparisonUtils.java +++ b/commons-test/src/main/java/com/powsybl/commons/test/ComparisonUtils.java @@ -16,8 +16,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.*; /** * @author Stanislao Fidanza {@literal } @@ -46,6 +45,14 @@ public static void compareTxt(InputStream expected, InputStream actual) { } } + public static void compareBytes(InputStream expected, InputStream actual) { + try { + assertArrayEquals(expected.readAllBytes(), actual.readAllBytes()); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + public static void compareTxt(InputStream expected, InputStream actual, List excludedLines) { BufferedReader expectedReader = new BufferedReader(new InputStreamReader(expected)); List expectedLines = expectedReader.lines().toList(); diff --git a/commons/src/main/java/com/powsybl/commons/binary/BinReader.java b/commons/src/main/java/com/powsybl/commons/binary/BinReader.java new file mode 100644 index 00000000000..b45f63eb025 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/binary/BinReader.java @@ -0,0 +1,278 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.binary; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.io.TreeDataHeader; +import com.powsybl.commons.io.TreeDataReader; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.function.Supplier; + +import static com.powsybl.commons.binary.BinUtil.END_NODE; +import static com.powsybl.commons.binary.BinUtil.NULL_ENUM; + +/** + * @author Florian Dupuy {@literal } + */ +public class BinReader implements TreeDataReader { + + private final DataInputStream dis; + private final Map dictionary = new HashMap<>(); + private final byte[] binaryMagicNumber; + + public BinReader(InputStream is, byte[] binaryMagicNumber) { + this.binaryMagicNumber = binaryMagicNumber; + this.dis = new DataInputStream(new BufferedInputStream(Objects.requireNonNull(is))); + } + + @Override + public TreeDataHeader readHeader() { + try { + readMagicNumber(); + TreeDataHeader header = new TreeDataHeader(readString(), readExtensionVersions()); + readDictionary(); + return header; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void readMagicNumber() throws IOException { + byte[] read = dis.readNBytes(binaryMagicNumber.length); + if (!Arrays.equals(read, binaryMagicNumber)) { + throw new PowsyblException("Unexpected bytes at file start"); + } + } + + public Map readExtensionVersions() throws IOException { + int nbVersions = dis.readShort(); + Map versions = new HashMap<>(); + for (int i = 0; i < nbVersions; i++) { + versions.put(readString(), readString()); + } + return versions; + } + + private void readDictionary() throws IOException { + int nbEntries = dis.readShort(); + for (int i = 0; i < nbEntries; i++) { + dictionary.put(i + 1, readString()); + } + } + + private String readString() { + try { + int stringNbBytes = dis.readShort(); + if (stringNbBytes == 0) { + return null; + } + byte[] stringBytes = dis.readNBytes(stringNbBytes); + if (stringBytes.length != stringNbBytes) { + throw new PowsyblException("Cannot read the full string, bytes missing: " + (stringNbBytes - stringBytes.length)); + } + return new String(stringBytes, StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private double readDouble() { + try { + return dis.readDouble(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private float readFloat() { + try { + return dis.readFloat(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private int readInt() { + try { + return dis.readInt(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private boolean readBoolean() { + try { + return dis.readBoolean(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private > T readEnum(Class clazz) { + try { + short ordinal = dis.readShort(); + return ordinal != NULL_ENUM ? clazz.getEnumConstants()[ordinal] : null; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private List readArray(Supplier valueReader) { + try { + int nbValues = dis.readShort(); + List values = new ArrayList<>(nbValues); + for (int i = 0; i < nbValues; i++) { + values.add(valueReader.get()); + } + return values; + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public double readDoubleAttribute(String name) { + return readDouble(); + } + + @Override + public double readDoubleAttribute(String name, double defaultValue) { + return readDouble(); + } + + @Override + public OptionalDouble readOptionalDoubleAttribute(String name) { + if (!readBoolean()) { + return OptionalDouble.empty(); + } + return OptionalDouble.of(readDouble()); + } + + @Override + public float readFloatAttribute(String name) { + return readFloat(); + } + + @Override + public float readFloatAttribute(String name, float defaultValue) { + return readFloat(); + } + + @Override + public String readStringAttribute(String name) { + return readString(); + } + + @Override + public int readIntAttribute(String name) { + return readInt(); + } + + @Override + public OptionalInt readOptionalIntAttribute(String name) { + if (!readBoolean()) { + return OptionalInt.empty(); + } + return OptionalInt.of(readInt()); + } + + @Override + public int readIntAttribute(String name, int defaultValue) { + return readInt(); + } + + @Override + public boolean readBooleanAttribute(String name) { + return readBoolean(); + } + + @Override + public boolean readBooleanAttribute(String name, boolean defaultValue) { + return readBoolean(); + } + + @Override + public Optional readOptionalBooleanAttribute(String name) { + if (!readBoolean()) { + return Optional.empty(); + } + return Optional.of(readBoolean()); + } + + @Override + public > T readEnumAttribute(String name, Class clazz) { + return readEnum(clazz); + } + + @Override + public > T readEnumAttribute(String name, Class clazz, T defaultValue) { + return readEnum(clazz); + } + + @Override + public String readContent() { + String content = readString(); + readEndNode(); + return content; + } + + @Override + public List readIntArrayAttribute(String name) { + return readArray(this::readInt); + } + + @Override + public List readStringArrayAttribute(String name) { + return readArray(this::readString); + } + + @Override + public void skipChildNodes() { + throw new PowsyblException("Binary format does not support skipping child nodes"); + } + + @Override + public void readChildNodes(ChildNodeReader childNodeReader) { + try { + int nodeNameIndex; + while ((nodeNameIndex = dis.readShort()) != END_NODE) { + String nodeName = dictionary.get(nodeNameIndex); + if (nodeName == null) { + throw new PowsyblException("Cannot read child node: unknown element name index " + nodeNameIndex); + } + childNodeReader.onStartNode(nodeName); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public void readEndNode() { + try { + int nextIndex = dis.readShort(); + if (nextIndex != END_NODE) { + throw new PowsyblException("Binary parsing: expected end node but got " + nextIndex); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public void close() { + try { + dis.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/commons/src/main/java/com/powsybl/commons/binary/BinUtil.java b/commons/src/main/java/com/powsybl/commons/binary/BinUtil.java new file mode 100644 index 00000000000..a3acbc4bece --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/binary/BinUtil.java @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.binary; + +/** + * @author Florian Dupuy {@literal } + */ +public final class BinUtil { + + private BinUtil() { + } + + static final int END_NODE = 0; + static final int NULL_ENUM = -1; + +} diff --git a/commons/src/main/java/com/powsybl/commons/binary/BinWriter.java b/commons/src/main/java/com/powsybl/commons/binary/BinWriter.java new file mode 100644 index 00000000000..14bb1860f21 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/binary/BinWriter.java @@ -0,0 +1,266 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.binary; + +import com.powsybl.commons.io.TreeDataWriter; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; + +import static com.powsybl.commons.binary.BinUtil.END_NODE; +import static com.powsybl.commons.binary.BinUtil.NULL_ENUM; + +/** + * @author Florian Dupuy {@literal } + */ +public class BinWriter implements TreeDataWriter { + + private final String rootVersion; + private final DataOutputStream dos; + private final DataOutputStream tmpDos; + private final ByteArrayOutputStream buffer; + private final Map nodeNamesIndex = new LinkedHashMap<>(); + private Map extensionVersions; + private final byte[] binaryMagicNumber; + + public BinWriter(OutputStream outputStream, byte[] binaryMagicNumber, String rootVersion) { + this.binaryMagicNumber = Objects.requireNonNull(binaryMagicNumber); + this.rootVersion = Objects.requireNonNull(rootVersion); + this.dos = new DataOutputStream(new BufferedOutputStream(Objects.requireNonNull(outputStream))); + this.buffer = new ByteArrayOutputStream(); + this.tmpDos = new DataOutputStream(buffer); + } + + private static void writeIndex(int index, DataOutputStream dataOutputStream) { + try { + dataOutputStream.writeShort(index); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static void writeString(String value, DataOutputStream dataOutputStream) { + try { + if (value == null) { + writeIndex(0, dataOutputStream); + } else { + byte[] bytes = value.getBytes(StandardCharsets.UTF_8); + writeIndex(bytes.length, dataOutputStream); + dataOutputStream.write(bytes); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void writeDouble(double value) { + try { + tmpDos.writeDouble(value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void writeInt(int value) { + try { + tmpDos.writeInt(value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void writeArray(Collection values, Consumer valueWriter) { + try { + tmpDos.writeShort(values.size()); + for (T value : values) { + valueWriter.accept(value); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void writeBoolean(boolean value) { + try { + tmpDos.writeBoolean(value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public void writeStartNodes() { + // nothing to do + } + + @Override + public void writeEndNodes() { + // nothing to do + } + + @Override + public void writeStartNode(String namespace, String name) { + if (nodeNamesIndex.isEmpty()) { + nodeNamesIndex.put(name, 1); // root element is not a child of another node, hence index is not expected + } else { + int index = nodeNamesIndex.computeIfAbsent(name, n -> 1 + nodeNamesIndex.size()); + writeIndex(index, tmpDos); + } + } + + @Override + public void writeEndNode() { + writeIndex(END_NODE, tmpDos); + } + + @Override + public void writeNamespace(String prefix, String namespace) { + // nothing to do + } + + @Override + public void writeNodeContent(String value) { + writeString(value, tmpDos); + } + + @Override + public void writeStringAttribute(String name, String value) { + writeString(value, tmpDos); + } + + @Override + public void writeFloatAttribute(String name, float value) { + try { + tmpDos.writeFloat(value); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public void writeDoubleAttribute(String name, double value) { + writeDouble(value); + } + + @Override + public void writeDoubleAttribute(String name, double value, double absentValue) { + writeDouble(value); + } + + @Override + public void writeOptionalDoubleAttribute(String name, Double value) { + try { + tmpDos.writeBoolean(value != null); + if (value != null) { + writeDouble(value); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public void writeIntAttribute(String name, int value) { + writeInt(value); + } + + @Override + public void writeIntAttribute(String name, int value, int absentValue) { + writeInt(value); + } + + @Override + public void writeOptionalIntAttribute(String name, Integer value) { + try { + tmpDos.writeBoolean(value != null); + if (value != null) { + writeInt(value); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public void writeIntArrayAttribute(String name, Collection values) { + writeArray(values, this::writeInt); + } + + @Override + public void writeStringArrayAttribute(String name, Collection values) { + writeArray(values, s -> writeString(s, tmpDos)); + } + + @Override + public > void writeEnumAttribute(String name, E value) { + writeIndex(value != null ? value.ordinal() : NULL_ENUM, tmpDos); + } + + @Override + public void writeBooleanAttribute(String name, boolean value) { + writeBoolean(value); + } + + @Override + public void writeBooleanAttribute(String name, boolean value, boolean absentValue) { + writeBoolean(value); + } + + @Override + public void writeOptionalBooleanAttribute(String name, Boolean value) { + try { + tmpDos.writeBoolean(value != null); + if (value != null) { + writeBoolean(value); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public void close() { + try { + tmpDos.flush(); + writeHeader(); + dos.write(buffer.toByteArray()); + dos.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private void writeHeader() throws IOException { + // magic number ("Binary IIDM" in ASCII) + dos.write(binaryMagicNumber); + + // iidm version + writeString(rootVersion, dos); + + // extensions versions + writeIndex(extensionVersions.size(), dos); + extensionVersions.forEach((extensionName, extensionVersion) -> { + writeString(extensionName, dos); + writeString(extensionVersion, dos); + }); + + // dictionary + writeIndex(nodeNamesIndex.size(), dos); + nodeNamesIndex.forEach((name, index) -> writeString(name, dos)); + } + + @Override + public void setVersions(Map extensionVersions) { + this.extensionVersions = Objects.requireNonNull(extensionVersions); + } +} diff --git a/commons/src/main/java/com/powsybl/commons/io/AbstractTreeDataReader.java b/commons/src/main/java/com/powsybl/commons/io/AbstractTreeDataReader.java index 9669e647a41..cafc604580c 100644 --- a/commons/src/main/java/com/powsybl/commons/io/AbstractTreeDataReader.java +++ b/commons/src/main/java/com/powsybl/commons/io/AbstractTreeDataReader.java @@ -7,11 +7,22 @@ */ package com.powsybl.commons.io; +import java.util.Map; + /** * @author Florian Dupuy {@literal } */ public abstract class AbstractTreeDataReader implements TreeDataReader { + @Override + public TreeDataHeader readHeader() { + return new TreeDataHeader(readRootVersion(), readExtensionVersions()); + } + + protected abstract String readRootVersion(); + + protected abstract Map readExtensionVersions(); + @Override public double readDoubleAttribute(String name) { return readDoubleAttribute(name, Double.NaN); diff --git a/commons/src/main/java/com/powsybl/commons/io/AbstractTreeDataWriter.java b/commons/src/main/java/com/powsybl/commons/io/AbstractTreeDataWriter.java new file mode 100644 index 00000000000..c963f7ea86c --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/io/AbstractTreeDataWriter.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.io; + +/** + * @author Florian Dupuy {@literal } + */ +public abstract class AbstractTreeDataWriter implements TreeDataWriter { + + @Override + public void writeOptionalBooleanAttribute(String name, Boolean value) { + if (value != null) { + writeBooleanAttribute(name, value); + } + } + + @Override + public void writeOptionalDoubleAttribute(String name, Double value) { + if (value != null) { + writeDoubleAttribute(name, value); + } + } + + @Override + public void writeOptionalIntAttribute(String name, Integer value) { + if (value != null) { + writeIntAttribute(name, value); + } + } +} diff --git a/commons/src/main/java/com/powsybl/commons/io/TreeDataFormat.java b/commons/src/main/java/com/powsybl/commons/io/TreeDataFormat.java index c37cab6dc78..9fe6218a3f8 100644 --- a/commons/src/main/java/com/powsybl/commons/io/TreeDataFormat.java +++ b/commons/src/main/java/com/powsybl/commons/io/TreeDataFormat.java @@ -12,5 +12,6 @@ */ public enum TreeDataFormat { XML, - JSON + JSON, + BIN } diff --git a/commons/src/main/java/com/powsybl/commons/io/TreeDataHeader.java b/commons/src/main/java/com/powsybl/commons/io/TreeDataHeader.java new file mode 100644 index 00000000000..3b6e25b3659 --- /dev/null +++ b/commons/src/main/java/com/powsybl/commons/io/TreeDataHeader.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.commons.io; + +import java.util.Map; + +/** + * @author Florian Dupuy {@literal } + */ +public record TreeDataHeader(String rootVersion, Map extensionVersions) { + +} diff --git a/commons/src/main/java/com/powsybl/commons/io/TreeDataReader.java b/commons/src/main/java/com/powsybl/commons/io/TreeDataReader.java index 16a9cfd9aec..59e0a58afdf 100644 --- a/commons/src/main/java/com/powsybl/commons/io/TreeDataReader.java +++ b/commons/src/main/java/com/powsybl/commons/io/TreeDataReader.java @@ -7,7 +7,9 @@ package com.powsybl.commons.io; import java.util.List; -import java.util.Map; +import java.util.Optional; +import java.util.OptionalDouble; +import java.util.OptionalInt; /** * @author Geoffroy Jamgotchian {@literal } @@ -24,28 +26,32 @@ interface ChildNodeReader { void onStartNode(String nodeName); } - String readRootVersion(); - - Map readVersions(); + TreeDataHeader readHeader(); double readDoubleAttribute(String name); double readDoubleAttribute(String name, double defaultValue); + OptionalDouble readOptionalDoubleAttribute(String name); + float readFloatAttribute(String name); float readFloatAttribute(String name, float defaultValue); String readStringAttribute(String name); - Integer readIntAttribute(String name); + int readIntAttribute(String name); + + OptionalInt readOptionalIntAttribute(String name); int readIntAttribute(String name, int defaultValue); - Boolean readBooleanAttribute(String name); + boolean readBooleanAttribute(String name); boolean readBooleanAttribute(String name, boolean defaultValue); + Optional readOptionalBooleanAttribute(String name); + > T readEnumAttribute(String name, Class clazz); > T readEnumAttribute(String name, Class clazz, T defaultValue); diff --git a/commons/src/main/java/com/powsybl/commons/io/TreeDataWriter.java b/commons/src/main/java/com/powsybl/commons/io/TreeDataWriter.java index 1f4fcf00182..2bce938bd67 100644 --- a/commons/src/main/java/com/powsybl/commons/io/TreeDataWriter.java +++ b/commons/src/main/java/com/powsybl/commons/io/TreeDataWriter.java @@ -34,10 +34,14 @@ public interface TreeDataWriter extends AutoCloseable { void writeDoubleAttribute(String name, double value, double absentValue); + void writeOptionalDoubleAttribute(String name, Double value); + void writeIntAttribute(String name, int value); void writeIntAttribute(String name, int value, int absentValue); + void writeOptionalIntAttribute(String name, Integer value); + void writeIntArrayAttribute(String name, Collection values); void writeStringArrayAttribute(String name, Collection values); @@ -48,6 +52,8 @@ public interface TreeDataWriter extends AutoCloseable { void writeBooleanAttribute(String name, boolean value, boolean absentValue); + void writeOptionalBooleanAttribute(String name, Boolean value); + @Override void close(); diff --git a/commons/src/main/java/com/powsybl/commons/json/JsonReader.java b/commons/src/main/java/com/powsybl/commons/json/JsonReader.java index 8635822fb42..4268fd519a8 100644 --- a/commons/src/main/java/com/powsybl/commons/json/JsonReader.java +++ b/commons/src/main/java/com/powsybl/commons/json/JsonReader.java @@ -50,7 +50,7 @@ public String readRootVersion() { } @Override - public Map readVersions() { + public Map readExtensionVersions() { if (!(EXTENSION_VERSIONS_NAME.equals(getFieldName()))) { return Collections.emptyMap(); } @@ -83,6 +83,11 @@ public double readDoubleAttribute(String name, double defaultValue) { return Objects.requireNonNull(name).equals(getFieldName()) ? getDoubleValue() : defaultValue; } + @Override + public OptionalDouble readOptionalDoubleAttribute(String name) { + return Objects.requireNonNull(name).equals(getFieldName()) ? OptionalDouble.of(getDoubleValue()) : OptionalDouble.empty(); + } + @Override public float readFloatAttribute(String name, float defaultValue) { return Objects.requireNonNull(name).equals(getFieldName()) ? getFloatValue() : defaultValue; @@ -112,8 +117,17 @@ private String readStringAttribute(String name, boolean throwException) { } @Override - public Integer readIntAttribute(String name) { - return Objects.requireNonNull(name).equals(getFieldName()) ? getIntValue() : null; + public int readIntAttribute(String name) { + String fieldName = getFieldName(); + if (!Objects.requireNonNull(name).equals(fieldName)) { + throw new PowsyblException("JSON parsing: expected '" + name + "' but got '" + fieldName + "'"); + } + return getIntValue(); + } + + @Override + public OptionalInt readOptionalIntAttribute(String name) { + return Objects.requireNonNull(name).equals(getFieldName()) ? OptionalInt.of(getIntValue()) : OptionalInt.empty(); } @Override @@ -122,8 +136,12 @@ public int readIntAttribute(String name, int defaultValue) { } @Override - public Boolean readBooleanAttribute(String name) { - return Objects.requireNonNull(name).equals(getFieldName()) ? getBooleanValue() : null; + public boolean readBooleanAttribute(String name) { + String fieldName = getFieldName(); + if (!Objects.requireNonNull(name).equals(fieldName)) { + throw new PowsyblException("JSON parsing: expected '" + name + "' but got '" + fieldName + "'"); + } + return getBooleanValue(); } @Override @@ -131,6 +149,11 @@ public boolean readBooleanAttribute(String name, boolean defaultValue) { return Objects.requireNonNull(name).equals(getFieldName()) ? getBooleanValue() : defaultValue; } + @Override + public Optional readOptionalBooleanAttribute(String name) { + return Objects.requireNonNull(name).equals(getFieldName()) ? Optional.of(getBooleanValue()) : Optional.empty(); + } + private double getDoubleValue() { try { currentJsonTokenConsumed = true; diff --git a/commons/src/main/java/com/powsybl/commons/json/JsonWriter.java b/commons/src/main/java/com/powsybl/commons/json/JsonWriter.java index d449310b969..9d3386fa6b6 100644 --- a/commons/src/main/java/com/powsybl/commons/json/JsonWriter.java +++ b/commons/src/main/java/com/powsybl/commons/json/JsonWriter.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.powsybl.commons.PowsyblException; -import com.powsybl.commons.io.TreeDataWriter; +import com.powsybl.commons.io.AbstractTreeDataWriter; import com.powsybl.commons.json.JsonUtil.Context; import com.powsybl.commons.json.JsonUtil.ContextType; @@ -20,7 +20,7 @@ /** * @author Geoffroy Jamgotchian {@literal } */ -public class JsonWriter implements TreeDataWriter { +public class JsonWriter extends AbstractTreeDataWriter { public static final String VERSION = "version"; private static final String EXTENSION_VERSIONS = "extensionVersions"; @@ -192,12 +192,14 @@ public void writeIntAttribute(String name, int value, int absentValue) { @Override public void writeIntArrayAttribute(String name, Collection values) { try { - jsonGenerator.writeFieldName(name); - jsonGenerator.writeStartArray(); - for (int value : values) { - jsonGenerator.writeNumber(value); + if (!values.isEmpty()) { + jsonGenerator.writeFieldName(name); + jsonGenerator.writeStartArray(); + for (int value : values) { + jsonGenerator.writeNumber(value); + } + jsonGenerator.writeEndArray(); } - jsonGenerator.writeEndArray(); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -206,12 +208,14 @@ public void writeIntArrayAttribute(String name, Collection values) { @Override public void writeStringArrayAttribute(String name, Collection values) { try { - jsonGenerator.writeFieldName(name); - jsonGenerator.writeStartArray(); - for (String value : values) { - jsonGenerator.writeString(value); + if (!values.isEmpty()) { + jsonGenerator.writeFieldName(name); + jsonGenerator.writeStartArray(); + for (String value : values) { + jsonGenerator.writeString(value); + } + jsonGenerator.writeEndArray(); } - jsonGenerator.writeEndArray(); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/commons/src/main/java/com/powsybl/commons/xml/XmlReader.java b/commons/src/main/java/com/powsybl/commons/xml/XmlReader.java index 7e2ee3e489f..d8df00967d9 100644 --- a/commons/src/main/java/com/powsybl/commons/xml/XmlReader.java +++ b/commons/src/main/java/com/powsybl/commons/xml/XmlReader.java @@ -8,6 +8,7 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; +import com.powsybl.commons.PowsyblException; import com.powsybl.commons.exceptions.UncheckedXmlStreamException; import com.powsybl.commons.extensions.ExtensionSerDe; import com.powsybl.commons.io.AbstractTreeDataReader; @@ -46,7 +47,7 @@ public String readRootVersion() { } @Override - public Map readVersions() { + public Map readExtensionVersions() { Map versions = new HashMap<>(); for (ExtensionSerDe e : extensionProviders) { String namespaceUri = reader.getNamespaceURI(e.getNamespacePrefix()); @@ -62,6 +63,11 @@ public double readDoubleAttribute(String name, double defaultValue) { return XmlUtil.readDoubleAttribute(reader, name, defaultValue); } + @Override + public OptionalDouble readOptionalDoubleAttribute(String name) { + return XmlUtil.readOptionalDoubleAttribute(reader, name); + } + @Override public float readFloatAttribute(String name, float defaultValue) { return XmlUtil.readFloatAttribute(reader, name, defaultValue); @@ -73,8 +79,17 @@ public String readStringAttribute(String name) { } @Override - public Integer readIntAttribute(String name) { - return XmlUtil.readIntegerAttribute(reader, name); + public int readIntAttribute(String name) { + Integer value = XmlUtil.readIntegerAttribute(reader, name); + if (value == null) { + throw new PowsyblException("XML parsing: cannot find required attribute '" + name + "'"); + } + return value; + } + + @Override + public OptionalInt readOptionalIntAttribute(String name) { + return XmlUtil.readOptionalIntegerAttribute(reader, name); } @Override @@ -83,8 +98,12 @@ public int readIntAttribute(String name, int defaultValue) { } @Override - public Boolean readBooleanAttribute(String name) { - return XmlUtil.readBooleanAttribute(reader, name); + public boolean readBooleanAttribute(String name) { + Boolean value = XmlUtil.readBooleanAttribute(reader, name); + if (value == null) { + throw new PowsyblException("XML parsing: cannot find required attribute '" + name + "'"); + } + return value; } @Override @@ -92,6 +111,11 @@ public boolean readBooleanAttribute(String name, boolean defaultValue) { return XmlUtil.readBooleanAttribute(reader, name, defaultValue); } + @Override + public Optional readOptionalBooleanAttribute(String name) { + return Optional.ofNullable(XmlUtil.readBooleanAttribute(reader, name)); + } + @Override public String readContent() { try { diff --git a/commons/src/main/java/com/powsybl/commons/xml/XmlUtil.java b/commons/src/main/java/com/powsybl/commons/xml/XmlUtil.java index f4c046b9d4b..dfb260c4273 100644 --- a/commons/src/main/java/com/powsybl/commons/xml/XmlUtil.java +++ b/commons/src/main/java/com/powsybl/commons/xml/XmlUtil.java @@ -19,6 +19,8 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Objects; +import java.util.OptionalDouble; +import java.util.OptionalInt; import java.util.function.Supplier; /** @@ -112,6 +114,11 @@ public static Integer readIntegerAttribute(XMLStreamReader reader, String name) return attributeValue != null ? Integer.valueOf(attributeValue) : null; } + public static OptionalInt readOptionalIntegerAttribute(XMLStreamReader reader, String name) { + String attributeValue = reader.getAttributeValue(null, name); + return attributeValue != null ? OptionalInt.of(Integer.parseInt(attributeValue)) : OptionalInt.empty(); + } + public static int readIntAttribute(XMLStreamReader reader, String name, int defaultValue) { String attributeValue = reader.getAttributeValue(null, name); return attributeValue != null ? Integer.parseInt(attributeValue) : defaultValue; @@ -132,6 +139,11 @@ public static double readDoubleAttribute(XMLStreamReader reader, String name, do return attributeValue != null ? Double.parseDouble(attributeValue) : defaultValue; } + public static OptionalDouble readOptionalDoubleAttribute(XMLStreamReader reader, String name) { + String attributeValue = reader.getAttributeValue(null, name); + return attributeValue != null ? OptionalDouble.of(Double.parseDouble(attributeValue)) : OptionalDouble.empty(); + } + public static float readFloatAttribute(XMLStreamReader reader, String name, float defaultValue) { String attributeValue = reader.getAttributeValue(null, name); return attributeValue != null ? Float.parseFloat(attributeValue) : defaultValue; diff --git a/commons/src/main/java/com/powsybl/commons/xml/XmlWriter.java b/commons/src/main/java/com/powsybl/commons/xml/XmlWriter.java index 5c3b8b5cbd0..b7690b3e4bf 100644 --- a/commons/src/main/java/com/powsybl/commons/xml/XmlWriter.java +++ b/commons/src/main/java/com/powsybl/commons/xml/XmlWriter.java @@ -8,7 +8,7 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.commons.exceptions.UncheckedXmlStreamException; -import com.powsybl.commons.io.TreeDataWriter; +import com.powsybl.commons.io.AbstractTreeDataWriter; import org.apache.commons.lang3.StringUtils; import javax.xml.stream.XMLStreamException; @@ -21,7 +21,7 @@ /** * @author Geoffroy Jamgotchian {@literal } */ -public class XmlWriter implements TreeDataWriter { +public class XmlWriter extends AbstractTreeDataWriter { private final XMLStreamWriter writer; @@ -171,14 +171,18 @@ public void writeIntAttribute(String name, int value, int absentValue) { @Override public void writeIntArrayAttribute(String name, Collection values) { - writeStringAttribute(name, values.stream() - .map(i -> Integer.toString(i)) - .collect(Collectors.joining(","))); + if (!values.isEmpty()) { + writeStringAttribute(name, values.stream() + .map(i -> Integer.toString(i)) + .collect(Collectors.joining(","))); + } } @Override public void writeStringArrayAttribute(String name, Collection values) { - writeStringAttribute(name, String.join(",", values)); + if (!values.isEmpty()) { + writeStringAttribute(name, String.join(",", values)); + } } @Override diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractComplexIdentifiableSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractComplexIdentifiableSerDe.java index eb4991c2337..01dd154249c 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractComplexIdentifiableSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractComplexIdentifiableSerDe.java @@ -23,7 +23,7 @@ */ abstract class AbstractComplexIdentifiableSerDe, A extends IdentifiableAdder, P extends Identifiable> extends AbstractIdentifiableSerDe { - protected abstract void readRootElementAttributes(A adder, List> toApply, NetworkDeserializerContext context); + protected abstract void readRootElementAttributes(A adder, P parent, List> toApply, NetworkDeserializerContext context); protected void readSubElement(String elementName, String id, List> toApply, NetworkDeserializerContext context) { switch (elementName) { @@ -43,7 +43,7 @@ public final void read(P parent, NetworkDeserializerContext context) { List> toApply = new ArrayList<>(); A adder = createAdder(parent); String id = readIdentifierAttributes(adder, context); - readRootElementAttributes(adder, toApply, context); + readRootElementAttributes(adder, parent, toApply, context); readSubElements(id, adder, toApply, context); if (postponeElementCreation()) { context.getEndTasks().add(() -> createElement(adder, toApply)); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractIdentifiableSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractIdentifiableSerDe.java index 85043270f0b..66aaedd4193 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractIdentifiableSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractIdentifiableSerDe.java @@ -32,7 +32,7 @@ public final void write(T identifiable, P parent, NetworkSerializerContext conte } context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), getRootElementName()); context.getWriter().writeStringAttribute("id", context.getAnonymizer().anonymizeString(identifiable.getId())); - identifiable.getOptionalName().ifPresent(name -> context.getWriter().writeStringAttribute("name", context.getAnonymizer().anonymizeString(name))); + context.getWriter().writeStringAttribute("name", identifiable.getOptionalName().map(context.getAnonymizer()::anonymizeString).orElse(null)); IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_2, context, () -> context.getWriter().writeBooleanAttribute("fictitious", identifiable.isFictitious(), false)); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTransformerSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTransformerSerDe.java index 074adbea8be..8f927931d58 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTransformerSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AbstractTransformerSerDe.java @@ -9,8 +9,10 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.PhaseTapChanger.RegulationMode; import com.powsybl.iidm.serde.util.IidmSerDeUtil; +import java.util.OptionalInt; import java.util.function.DoubleConsumer; /** @@ -63,17 +65,17 @@ private static double readTargetDeadband(NetworkDeserializerContext context, boo private static void writeTapChanger(TapChanger tc, NetworkSerializerContext context) { context.getWriter().writeIntAttribute(ATTR_LOW_TAP_POSITION, tc.getLowTapPosition()); - if (tc.findTapPosition().isPresent()) { - context.getWriter().writeIntAttribute(ATTR_TAP_POSITION, tc.getTapPosition()); - } + var tp = tc.findTapPosition(); + context.getWriter().writeOptionalIntAttribute(ATTR_TAP_POSITION, tp.isPresent() ? tp.getAsInt() : null); writeTargetDeadband(tc.getTargetDeadband(), context); } protected static void writeRatioTapChanger(String name, RatioTapChanger rtc, NetworkSerializerContext context) { context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), name); - if (rtc.hasLoadTapChangingCapabilities() || rtc.isRegulating()) { - context.getWriter().writeBooleanAttribute(ATTR_REGULATING, rtc.isRegulating()); - } + + Boolean optionalRegulatingValue = rtc.hasLoadTapChangingCapabilities() || rtc.isRegulating() ? rtc.isRegulating() : null; + context.getWriter().writeOptionalBooleanAttribute(ATTR_REGULATING, optionalRegulatingValue); + writeTapChanger(rtc, context); context.getWriter().writeBooleanAttribute("loadTapChangingCapabilities", rtc.hasLoadTapChangingCapabilities()); IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_11, context, () -> context.getWriter().writeDoubleAttribute("targetV", rtc.getRegulationValue())); @@ -81,9 +83,7 @@ protected static void writeRatioTapChanger(String name, RatioTapChanger rtc, Net context.getWriter().writeEnumAttribute(ATTR_REGULATION_MODE, rtc.getRegulationMode()); context.getWriter().writeDoubleAttribute(ATTR_REGULATION_VALUE, rtc.getRegulationValue()); }); - if (rtc.getRegulationTerminal() != null) { - TerminalRefSerDe.writeTerminalRef(rtc.getRegulationTerminal(), context, ELEM_TERMINAL_REF); - } + TerminalRefSerDe.writeTerminalRef(rtc.getRegulationTerminal(), context, ELEM_TERMINAL_REF); context.getWriter().writeStartNodes(); for (int p = rtc.getLowTapPosition(); p <= rtc.getHighTapPosition(); p++) { @@ -148,19 +148,15 @@ protected static void readRatioTapChanger(int leg, ThreeWindingsTransformer.Leg protected static void writePhaseTapChanger(String name, PhaseTapChanger ptc, NetworkSerializerContext context) { context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), name); - if (ptc.getRegulationMode() != null && ptc.getRegulationMode() != PhaseTapChanger.RegulationMode.FIXED_TAP - || ptc.isRegulating()) { - context.getWriter().writeBooleanAttribute(ATTR_REGULATING, ptc.isRegulating()); - } + + RegulationMode regMode = ptc.getRegulationMode(); + Boolean optionalRegulatingValue = (regMode == null || regMode == RegulationMode.FIXED_TAP) && !ptc.isRegulating() ? null : ptc.isRegulating(); + context.getWriter().writeOptionalBooleanAttribute(ATTR_REGULATING, optionalRegulatingValue); + writeTapChanger(ptc, context); - context.getWriter().writeEnumAttribute(ATTR_REGULATION_MODE, ptc.getRegulationMode()); - if (ptc.getRegulationMode() != null && ptc.getRegulationMode() != PhaseTapChanger.RegulationMode.FIXED_TAP - || !Double.isNaN(ptc.getRegulationValue())) { - context.getWriter().writeDoubleAttribute(ATTR_REGULATION_VALUE, ptc.getRegulationValue()); - } - if (ptc.getRegulationTerminal() != null) { - TerminalRefSerDe.writeTerminalRef(ptc.getRegulationTerminal(), context, ELEM_TERMINAL_REF); - } + context.getWriter().writeEnumAttribute(ATTR_REGULATION_MODE, regMode); + context.getWriter().writeDoubleAttribute(ATTR_REGULATION_VALUE, ptc.getRegulationValue()); + TerminalRefSerDe.writeTerminalRef(ptc.getRegulationTerminal(), context, ELEM_TERMINAL_REF); context.getWriter().writeStartNodes(); for (int p = ptc.getLowTapPosition(); p <= ptc.getHighTapPosition(); p++) { @@ -214,16 +210,14 @@ private static void readTapChangerTerminalRef(TapChangerAdder adder, Te } private static void readTapChangerAttributes(TapChangerAdder adder, NetworkDeserializerContext context) { - boolean regulating = context.getReader().readBooleanAttribute(ATTR_REGULATING, false); + boolean regulating = context.getReader().readOptionalBooleanAttribute(ATTR_REGULATING).orElse(false); int lowTapPosition = context.getReader().readIntAttribute(ATTR_LOW_TAP_POSITION); - Integer tapPosition = context.getReader().readIntAttribute(ATTR_TAP_POSITION); + OptionalInt tapPosition = context.getReader().readOptionalIntAttribute(ATTR_TAP_POSITION); double targetDeadband = readTargetDeadband(context, regulating); adder.setLowTapPosition(lowTapPosition) .setTargetDeadband(targetDeadband) .setRegulating(regulating); - if (tapPosition != null) { - adder.setTapPosition(tapPosition); - } + tapPosition.ifPresent(adder::setTapPosition); } protected static void readPhaseTapChanger(TwoWindingsTransformer twt, NetworkDeserializerContext context) { @@ -250,8 +244,10 @@ private static void readSteps(NetworkDeserializerContext context, TapChangerStep * @param consumer the method will used apparent power value read */ protected static void readRatedS(String name, NetworkDeserializerContext context, DoubleConsumer consumer) { - double ratedS = context.getReader().readDoubleAttribute(name); - consumer.accept(ratedS); + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_2, context, () -> { + double ratedS = context.getReader().readDoubleAttribute(name); + consumer.accept(ratedS); + }); } /** diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AliasesSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AliasesSerDe.java index ee61fe19b5f..ce14e6dce5f 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AliasesSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/AliasesSerDe.java @@ -26,7 +26,7 @@ public static void write(Identifiable identifiable, String rootElementName, N for (String alias : identifiable.getAliases()) { context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), ROOT_ELEMENT_NAME); IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_4, context, - () -> identifiable.getAliasType(alias).ifPresent(type -> context.getWriter().writeStringAttribute("type", type))); + () -> context.getWriter().writeStringAttribute("type", identifiable.getAliasType(alias).orElse(null))); context.getWriter().writeNodeContent(context.getAnonymizer().anonymizeString(alias)); context.getWriter().writeEndNode(); } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BatterySerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BatterySerDe.java index 57932ec3c5b..29cec927bdf 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BatterySerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BatterySerDe.java @@ -60,7 +60,7 @@ protected Battery readRootElementAttributes(BatteryAdder adder, VoltageLevel vol IidmSerDeUtil.getAttributeName("q0", "targetQ", context.getVersion(), IidmVersion.V_1_8)); double minP = context.getReader().readDoubleAttribute("minP"); double maxP = context.getReader().readDoubleAttribute("maxP"); - readNodeOrBus(adder, context); + readNodeOrBus(adder, context, voltageLevel.getTopologyKind()); Battery b = adder.setTargetP(targetP) .setTargetQ(targetQ) .setMinP(minP) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryExporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryExporter.java new file mode 100644 index 00000000000..eeb4e4dbdc3 --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryExporter.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.config.PlatformConfig; +import com.powsybl.commons.io.TreeDataFormat; +import com.powsybl.iidm.network.Exporter; + +import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; + +/** + * Binary export of an IIDM model. + * + * @author Florian Dupuy {@literal } + */ +@AutoService(Exporter.class) +public class BinaryExporter extends AbstractTreeDataExporter { + + public BinaryExporter() { + this(PlatformConfig.defaultConfig()); + } + + public BinaryExporter(PlatformConfig platformConfig) { + super(platformConfig); + } + + @Override + public String getFormat() { + return "BIIDM"; + } + + @Override + public String getComment() { + return "IIDM binary v" + CURRENT_IIDM_VERSION.toString(".") + " exporter"; + } + + @Override + protected TreeDataFormat getTreeDataFormat() { + return TreeDataFormat.BIN; + } + + @Override + protected String getExtension() { + return "biidm"; + } +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java new file mode 100644 index 00000000000..27460f369d1 --- /dev/null +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/BinaryImporter.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + */ +package com.powsybl.iidm.serde; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.datasource.ReadOnlyDataSource; +import com.powsybl.commons.io.TreeDataFormat; +import com.powsybl.iidm.network.Importer; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Properties; + +import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; + +/** + * Binary import of an IIDM model. + * + * @author Florian Dupuy {@literal } + */ +@AutoService(Importer.class) +public class BinaryImporter extends AbstractTreeDataImporter { + + private static final String[] EXTENSIONS = {"biidm", "bin", "iidm.bin"}; + + @Override + protected String[] getExtensions() { + return EXTENSIONS; + } + + @Override + public String getFormat() { + return "BIIDM"; + } + + @Override + public String getComment() { + return "IIDM binary v " + CURRENT_IIDM_VERSION.toString(".") + " importer"; + } + + @Override + protected boolean exists(ReadOnlyDataSource dataSource, String ext) throws IOException { + if (ext != null) { + try (InputStream dis = dataSource.newInputStream(null, ext)) { + return Arrays.equals(dis.readNBytes(NetworkSerDe.BIIDM_MAGIC_NUMBER.length), NetworkSerDe.BIIDM_MAGIC_NUMBER); + } + } + return false; + } + + @Override + protected ImportOptions createImportOptions(Properties parameters) { + return super.createImportOptions(parameters) + .setFormat(TreeDataFormat.BIN); + } +} diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ConnectableSerDeUtil.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ConnectableSerDeUtil.java index 0ec057ce888..2c1be8668db 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ConnectableSerDeUtil.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ConnectableSerDeUtil.java @@ -13,6 +13,11 @@ import com.powsybl.iidm.network.ThreeWindingsTransformerAdder.LegAdder; import com.powsybl.iidm.serde.util.IidmSerDeUtil; +import java.util.Optional; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.IntConsumer; + /** * @author Geoffroy Jamgotchian {@literal } */ @@ -82,79 +87,64 @@ private static void writeNode(Integer index, Terminal t, NetworkSerializerContex } private static void writeBus(Integer index, Bus bus, Bus connectableBus, NetworkSerializerContext context) { - if (bus != null) { - context.getWriter().writeStringAttribute(BUS + indexToString(index), context.getAnonymizer().anonymizeString(bus.getId())); - } - if (connectableBus != null) { - context.getWriter().writeStringAttribute(CONNECTABLE_BUS + indexToString(index), context.getAnonymizer().anonymizeString(connectableBus.getId())); - } + context.getWriter().writeStringAttribute(BUS + indexToString(index), + Optional.ofNullable(bus).map(b -> context.getAnonymizer().anonymizeString(b.getId())).orElse(null)); + context.getWriter().writeStringAttribute(CONNECTABLE_BUS + indexToString(index), + Optional.ofNullable(connectableBus).map(b -> context.getAnonymizer().anonymizeString(b.getId())).orElse(null)); } - public static void readNodeOrBus(InjectionAdder adder, NetworkDeserializerContext context) { - readNodeOrBus(adder, "", context); + public static void readNodeOrBus(InjectionAdder adder, NetworkDeserializerContext context, TopologyKind topologyKind) { + readNodeOrBus(adder, "", context, topologyKind); } - public static void readNodeOrBus(InjectionAdder adder, String suffix, NetworkDeserializerContext context) { - String bus = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(BUS + suffix)); - String connectableBus = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(CONNECTABLE_BUS + suffix)); - Integer node = context.getReader().readIntAttribute(NODE + suffix); - if (bus != null) { - adder.setBus(bus); - } - if (connectableBus != null) { - adder.setConnectableBus(connectableBus); - } - if (node != null) { - adder.setNode(node); - } + public static void readNodeOrBus(InjectionAdder adder, String suffix, NetworkDeserializerContext context, TopologyKind topologyKind) { + readNodeOrBus(suffix, topologyKind, adder::setNode, adder::setBus, adder::setConnectableBus, context); } - public static void readNodeOrBus(BranchAdder adder, NetworkDeserializerContext context) { - String voltageLevelId1 = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("voltageLevelId1")); - String bus1 = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("bus1")); - String connectableBus1 = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("connectableBus1")); - Integer node1 = context.getReader().readIntAttribute("node1"); - String voltageLevelId2 = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("voltageLevelId2")); - String bus2 = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("bus2")); - String connectableBus2 = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("connectableBus2")); - Integer node2 = context.getReader().readIntAttribute("node2"); - if (bus1 != null) { - adder.setBus1(bus1); - } - if (connectableBus1 != null) { - adder.setConnectableBus1(connectableBus1); - } - if (node1 != null) { - adder.setNode1(node1); - } - adder.setVoltageLevel1(voltageLevelId1); - if (bus2 != null) { - adder.setBus2(bus2); - } - if (connectableBus2 != null) { - adder.setConnectableBus2(connectableBus2); - } - if (node2 != null) { - adder.setNode2(node2); - } - adder.setVoltageLevel2(voltageLevelId2); + private static void readNode(IntConsumer nodeAdder, String suffix, NetworkDeserializerContext context) { + nodeAdder.accept(context.getReader().readIntAttribute(NODE + suffix)); } - public static void readNodeOrBus(int index, LegAdder adder, NetworkDeserializerContext context) { - String voltageLevelId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("voltageLevelId" + index)); - String bus = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(BUS + index)); - String connectableBus = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(CONNECTABLE_BUS + index)); - Integer node = context.getReader().readIntAttribute(NODE + index); - if (bus != null) { - adder.setBus(bus); - } - if (connectableBus != null) { - adder.setConnectableBus(connectableBus); + private static void readBus(Consumer busAdder, String suffix, NetworkDeserializerContext context) { + busAdder.accept(context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(BUS + suffix))); + } + + private static void readConnectableBus(Consumer busAdder, String suffix, NetworkDeserializerContext context) { + busAdder.accept(context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(CONNECTABLE_BUS + suffix))); + } + + public static void readVoltageLevelAndNodeOrBus(BranchAdder adder, Network network, NetworkDeserializerContext context) { + readVoltageLevelAndNodeOrBus("1", adder::setVoltageLevel1, adder::setNode1, adder::setBus1, adder::setConnectableBus1, network, context); + readVoltageLevelAndNodeOrBus("2", adder::setVoltageLevel2, adder::setNode2, adder::setBus2, adder::setConnectableBus2, network, context); + } + + private static void readVoltageLevelAndNodeOrBus(String suffix, Consumer voltageLevelSetter, IntConsumer nodeSetter, Consumer busSetter, Consumer connectableBusSetter, Network network, NetworkDeserializerContext context) { + String voltageLevelId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("voltageLevelId" + suffix)); + voltageLevelSetter.accept(voltageLevelId); + readNodeOrBus(suffix, getTopologKind(voltageLevelId, network), nodeSetter, busSetter, connectableBusSetter, context); + } + + private static void readNodeOrBus(String suffix, TopologyKind topologyKind, IntConsumer nodeSetter, Consumer busSetter, Consumer connectableBusSetter, NetworkDeserializerContext context) { + switch (topologyKind) { + case NODE_BREAKER -> readNode(nodeSetter, suffix, context); + case BUS_BREAKER -> { + readBus(busSetter, suffix, context); + readConnectableBus(connectableBusSetter, suffix, context); + } + default -> throw new IllegalStateException(); } - if (node != null) { - adder.setNode(node); + } + + private static TopologyKind getTopologKind(String vlId, Network network) { + VoltageLevel vl = network.getVoltageLevel(vlId); + if (vl == null) { + throw new PowsyblException("Voltage level '" + vlId + "' not found"); } - adder.setVoltageLevel(voltageLevelId); + return vl.getTopologyKind(); + } + + public static void readNodeOrBus(int index, LegAdder adder, Network network, NetworkDeserializerContext context) { + readVoltageLevelAndNodeOrBus(String.valueOf(index), adder::setVoltageLevel, adder::setNode, adder::setBus, adder::setConnectableBus, network, context); } public static void writePQ(Integer index, Terminal t, TreeDataWriter writer) { @@ -162,6 +152,13 @@ public static void writePQ(Integer index, Terminal t, TreeDataWriter writer) { writer.writeDoubleAttribute("q" + indexToString(index), t.getQ()); } + public static void writeOptionalPQ(Integer index, Terminal t, TreeDataWriter writer, BooleanSupplier write) { + Double nullableP = write.getAsBoolean() ? t.getP() : null; + Double nullableQ = write.getAsBoolean() ? t.getQ() : null; + writer.writeOptionalDoubleAttribute("p" + indexToString(index), nullableP); + writer.writeOptionalDoubleAttribute("q" + indexToString(index), nullableQ); + } + public static void readPQ(Integer index, Terminal t, TreeDataReader reader) { double p = reader.readDoubleAttribute("p" + indexToString(index)); double q = reader.readDoubleAttribute("q" + indexToString(index)); @@ -169,6 +166,13 @@ public static void readPQ(Integer index, Terminal t, TreeDataReader reader) { .setQ(q); } + public static void readOptionalPQ(Integer index, Terminal t, TreeDataReader reader) { + reader.readOptionalDoubleAttribute("p" + indexToString(index)) + .ifPresent(t::setP); + reader.readOptionalDoubleAttribute("q" + indexToString(index)) + .ifPresent(t::setQ); + } + public static void readActivePowerLimits(ActivePowerLimitsAdder activePowerLimitsAdder, TreeDataReader reader, IidmVersion iidmVersion, ImportOptions options) { readLoadingLimits(ACTIVE_POWER_LIMITS, activePowerLimitsAdder, reader, iidmVersion, options); } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/DanglingLineSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/DanglingLineSerDe.java index 70fa4f68083..ee109f8b7b9 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/DanglingLineSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/DanglingLineSerDe.java @@ -7,9 +7,12 @@ package com.powsybl.iidm.serde; import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.DanglingLine.Generation; import com.powsybl.iidm.serde.util.IidmSerDeUtil; import java.util.Optional; +import java.util.OptionalDouble; +import java.util.function.Function; import java.util.function.Supplier; import static com.powsybl.iidm.serde.ConnectableSerDeUtil.*; @@ -41,7 +44,7 @@ protected void writeRootElementAttributes(DanglingLine dl, VoltageLevel vl, Netw } static void writeRootElementAttributesInternal(DanglingLine dl, Supplier terminalGetter, NetworkSerializerContext context) { - DanglingLine.Generation generation = dl.getGeneration(); + Generation generation = dl.getGeneration(); double[] p0 = new double[1]; double[] q0 = new double[1]; p0[0] = dl.getP0(); @@ -63,42 +66,35 @@ static void writeRootElementAttributesInternal(DanglingLine dl, Supplier { - context.getWriter().writeBooleanAttribute("generationVoltageRegulationOn", generation.isVoltageRegulationOn()); - context.getWriter().writeDoubleAttribute(GENERATION_MIN_P, generation.getMinP()); - context.getWriter().writeDoubleAttribute(GENERATION_MAX_P, generation.getMaxP()); - context.getWriter().writeDoubleAttribute(GENERATION_TARGET_P, generation.getTargetP()); - context.getWriter().writeDoubleAttribute(GENERATION_TARGET_V, generation.getTargetV()); - context.getWriter().writeDoubleAttribute(GENERATION_TARGET_Q, generation.getTargetQ()); - }); - } + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_3, context, () -> { + context.getWriter().writeOptionalBooleanAttribute("generationVoltageRegulationOn", getOptionalValue(generation, Generation::isVoltageRegulationOn)); + context.getWriter().writeOptionalDoubleAttribute(GENERATION_MIN_P, getOptionalValue(generation, Generation::getMinP)); + context.getWriter().writeOptionalDoubleAttribute(GENERATION_MAX_P, getOptionalValue(generation, Generation::getMaxP)); + context.getWriter().writeOptionalDoubleAttribute(GENERATION_TARGET_P, getOptionalValue(generation, Generation::getTargetP)); + context.getWriter().writeOptionalDoubleAttribute(GENERATION_TARGET_V, getOptionalValue(generation, Generation::getTargetV)); + context.getWriter().writeOptionalDoubleAttribute(GENERATION_TARGET_Q, getOptionalValue(generation, Generation::getTargetQ)); + }); Terminal t = terminalGetter.get(); writeNodeOrBus(null, t, context); - if (dl.getPairingKey() != null) { - IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_10, context, - () -> context.getWriter().writeStringAttribute("ucteXnodeCode", dl.getPairingKey()) - ); - IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_11, context, - () -> context.getWriter().writeStringAttribute("pairingKey", dl.getPairingKey()) - ); - } + IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_10, context, + () -> context.getWriter().writeStringAttribute("ucteXnodeCode", dl.getPairingKey()) + ); + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_11, context, + () -> context.getWriter().writeStringAttribute("pairingKey", dl.getPairingKey()) + ); writePQ(null, t, context.getWriter()); } + private static T getOptionalValue(Generation generation, Function valueGetter) { + return Optional.ofNullable(generation).map(valueGetter).orElse(null); + } + @Override protected DanglingLineAdder createAdder(VoltageLevel parent) { return parent.newDanglingLine(); } - static boolean hasValidGeneration(DanglingLine dl, NetworkSerializerContext context) { - if (dl.getGeneration() != null) { - return context.getVersion().compareTo(IidmVersion.V_1_3) > 0; - } - return false; - } - @Override protected void writeSubElements(DanglingLine dl, VoltageLevel vl, NetworkSerializerContext context) { if (dl.getGeneration() != null) { @@ -123,7 +119,7 @@ protected void writeSubElements(DanglingLine dl, VoltageLevel vl, NetworkSeriali @Override protected DanglingLine readRootElementAttributes(DanglingLineAdder adder, VoltageLevel voltageLevel, NetworkDeserializerContext context) { - readRootElementAttributesInternal(adder, context); + readRootElementAttributesInternal(adder, voltageLevel, context); IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_10, context, () -> { String pairingKey = context.getReader().readStringAttribute("ucteXnodeCode"); adder.setPairingKey(pairingKey); @@ -137,7 +133,7 @@ protected DanglingLine readRootElementAttributes(DanglingLineAdder adder, Voltag return dl; } - public static void readRootElementAttributesInternal(DanglingLineAdder adder, NetworkDeserializerContext context) { + public static void readRootElementAttributesInternal(DanglingLineAdder adder, VoltageLevel voltageLevel, NetworkDeserializerContext context) { double p0 = context.getReader().readDoubleAttribute("p0"); double q0 = context.getReader().readDoubleAttribute("q0"); double r = context.getReader().readDoubleAttribute("r"); @@ -145,24 +141,24 @@ public static void readRootElementAttributesInternal(DanglingLineAdder adder, Ne double g = context.getReader().readDoubleAttribute("g"); double b = context.getReader().readDoubleAttribute("b"); IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_3, context, () -> { - Boolean voltageRegulationOn = context.getReader().readBooleanAttribute("generationVoltageRegulationOn"); - if (voltageRegulationOn != null) { - double minP = context.getReader().readDoubleAttribute(GENERATION_MIN_P); - double maxP = context.getReader().readDoubleAttribute(GENERATION_MAX_P); - double targetP = context.getReader().readDoubleAttribute(GENERATION_TARGET_P); - double targetV = context.getReader().readDoubleAttribute(GENERATION_TARGET_V); - double targetQ = context.getReader().readDoubleAttribute(GENERATION_TARGET_Q); - adder.newGeneration() - .setMinP(minP) - .setMaxP(maxP) - .setVoltageRegulationOn(voltageRegulationOn) - .setTargetP(targetP) - .setTargetV(targetV) - .setTargetQ(targetQ) - .add(); + Optional voltageRegulationOn = context.getReader().readOptionalBooleanAttribute("generationVoltageRegulationOn"); + OptionalDouble minP = context.getReader().readOptionalDoubleAttribute(GENERATION_MIN_P); + OptionalDouble maxP = context.getReader().readOptionalDoubleAttribute(GENERATION_MAX_P); + OptionalDouble targetP = context.getReader().readOptionalDoubleAttribute(GENERATION_TARGET_P); + OptionalDouble targetV = context.getReader().readOptionalDoubleAttribute(GENERATION_TARGET_V); + OptionalDouble targetQ = context.getReader().readOptionalDoubleAttribute(GENERATION_TARGET_Q); + if (voltageRegulationOn.isPresent()) { + DanglingLineAdder.GenerationAdder generationAdder = adder.newGeneration() + .setVoltageRegulationOn(voltageRegulationOn.get()); + minP.ifPresent(generationAdder::setMinP); + maxP.ifPresent(generationAdder::setMaxP); + targetP.ifPresent(generationAdder::setTargetP); + targetV.ifPresent(generationAdder::setTargetV); + targetQ.ifPresent(generationAdder::setTargetQ); + generationAdder.add(); } }); - readNodeOrBus(adder, context); + readNodeOrBus(adder, context, voltageLevel.getTopologyKind()); adder.setP0(p0) .setQ0(q0) .setR(r) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java index a82af956c44..ee1880e2986 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GeneratorSerDe.java @@ -6,10 +6,7 @@ */ package com.powsybl.iidm.serde; -import com.powsybl.iidm.network.EnergySource; -import com.powsybl.iidm.network.Generator; -import com.powsybl.iidm.network.GeneratorAdder; -import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.iidm.network.*; import static com.powsybl.iidm.serde.ConnectableSerDeUtil.*; @@ -61,11 +58,11 @@ protected Generator readRootElementAttributes(GeneratorAdder adder, VoltageLevel double minP = context.getReader().readDoubleAttribute("minP"); double maxP = context.getReader().readDoubleAttribute("maxP"); double ratedS = context.getReader().readDoubleAttribute("ratedS"); - Boolean voltageRegulatorOn = context.getReader().readBooleanAttribute("voltageRegulatorOn"); + boolean voltageRegulatorOn = context.getReader().readBooleanAttribute("voltageRegulatorOn"); double targetP = context.getReader().readDoubleAttribute("targetP"); double targetV = context.getReader().readDoubleAttribute("targetV"); double targetQ = context.getReader().readDoubleAttribute("targetQ"); - readNodeOrBus(adder, context); + readNodeOrBus(adder, context, voltageLevel.getTopologyKind()); adder.setEnergySource(energySource) .setMinP(minP) .setMaxP(maxP) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GroundSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GroundSerDe.java index 7eb26230d54..b0d38f633e0 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GroundSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/GroundSerDe.java @@ -41,7 +41,7 @@ protected GroundAdder createAdder(VoltageLevel voltageLevel) { @Override protected Ground readRootElementAttributes(GroundAdder adder, VoltageLevel parent, NetworkDeserializerContext context) { - readNodeOrBus(adder, context); + readNodeOrBus(adder, context, parent.getTopologyKind()); return adder.add(); } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LccConverterStationSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LccConverterStationSerDe.java index 34cb62e80ec..e49d8565912 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LccConverterStationSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LccConverterStationSerDe.java @@ -45,7 +45,7 @@ protected LccConverterStationAdder createAdder(VoltageLevel vl) { protected LccConverterStation readRootElementAttributes(LccConverterStationAdder adder, VoltageLevel voltageLevel, NetworkDeserializerContext context) { float lossFactor = context.getReader().readFloatAttribute("lossFactor"); float powerFactor = context.getReader().readFloatAttribute("powerFactor"); - readNodeOrBus(adder, context); + readNodeOrBus(adder, context, voltageLevel.getTopologyKind()); LccConverterStation cs = adder .setLossFactor(lossFactor) .setPowerFactor(powerFactor) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LineSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LineSerDe.java index c6b6bc8d94f..f2358814814 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LineSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LineSerDe.java @@ -39,10 +39,8 @@ protected void writeRootElementAttributes(Line l, Network n, NetworkSerializerCo context.getWriter().writeDoubleAttribute("b2", l.getB2()); writeNodeOrBus(1, l.getTerminal1(), context); writeNodeOrBus(2, l.getTerminal2(), context); - if (context.getOptions().isWithBranchSV()) { - writePQ(1, l.getTerminal1(), context.getWriter()); - writePQ(2, l.getTerminal2(), context.getWriter()); - } + writeOptionalPQ(1, l.getTerminal1(), context.getWriter(), context.getOptions()::isWithBranchSV); + writeOptionalPQ(2, l.getTerminal2(), context.getWriter(), context.getOptions()::isWithBranchSV); } @Override @@ -94,10 +92,10 @@ protected Line readRootElementAttributes(LineAdder adder, Network network, Netwo .setB1(b1) .setG2(g2) .setB2(b2); - readNodeOrBus(adder, context); + ConnectableSerDeUtil.readVoltageLevelAndNodeOrBus(adder, network, context); Line l = adder.add(); - readPQ(1, l.getTerminal1(), context.getReader()); - readPQ(2, l.getTerminal2(), context.getReader()); + readOptionalPQ(1, l.getTerminal1(), context.getReader()); + readOptionalPQ(2, l.getTerminal2(), context.getReader()); return l; } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LoadSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LoadSerDe.java index 77e94e8b34f..490663bd109 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LoadSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/LoadSerDe.java @@ -82,11 +82,11 @@ protected LoadAdder createAdder(VoltageLevel vl) { } @Override - protected void readRootElementAttributes(LoadAdder adder, List> toApply, NetworkDeserializerContext context) { + protected void readRootElementAttributes(LoadAdder adder, VoltageLevel parent, List> toApply, NetworkDeserializerContext context) { LoadType loadType = context.getReader().readEnumAttribute("loadType", LoadType.class, LoadType.UNDEFINED); double p0 = context.getReader().readDoubleAttribute("p0"); double q0 = context.getReader().readDoubleAttribute("q0"); - readNodeOrBus(adder, context); + readNodeOrBus(adder, context, parent.getTopologyKind()); adder.setLoadType(loadType) .setP0(p0) .setQ0(q0); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java index 1fe0935a93f..632324b8e80 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/NetworkSerDe.java @@ -10,6 +10,8 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.binary.BinReader; +import com.powsybl.commons.binary.BinWriter; import com.powsybl.commons.datasource.DataSource; import com.powsybl.commons.datasource.ReadOnlyDataSource; import com.powsybl.commons.exceptions.UncheckedSaxException; @@ -18,6 +20,7 @@ import com.powsybl.commons.extensions.ExtensionProviders; import com.powsybl.commons.extensions.ExtensionSerDe; import com.powsybl.commons.io.TreeDataFormat; +import com.powsybl.commons.io.TreeDataHeader; import com.powsybl.commons.io.TreeDataReader; import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.commons.json.JsonReader; @@ -77,6 +80,9 @@ public final class NetworkSerDe { private static final String ID = "id"; private static final String MINIMUM_VALIDATION_LEVEL = "minimumValidationLevel"; + /** Magic number for binary iidm files ("Binary IIDM" in ASCII) */ + static final byte[] BIIDM_MAGIC_NUMBER = {0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x49, 0x49, 0x44, 0x4d}; + private static final Supplier> EXTENSIONS_SUPPLIER = Suppliers.memoize(() -> ExtensionProviders.createProvider(ExtensionSerDe.class, EXTENSION_CATEGORY_NAME)); @@ -263,6 +269,11 @@ private static JsonWriter createJsonWriter(OutputStream os, ExportOptions option } } + private static TreeDataWriter createBinWriter(OutputStream os, ExportOptions options) { + LOGGER.warn("BETA feature, the resulting binary file is not guaranteed to still be readable in the next releases"); + return new BinWriter(os, BIIDM_MAGIC_NUMBER, options.getVersion().toString(".")); + } + private static void writeRootElement(Network n, NetworkSerializerContext context) { IidmSerDeUtil.assertMinimumVersionIfNotDefault(n.getValidationLevel() != ValidationLevel.STEADY_STATE_HYPOTHESIS, NETWORK_ROOT_ELEMENT_NAME, MINIMUM_VALIDATION_LEVEL, IidmSerDeUtil.ErrorMessage.NOT_SUPPORTED, IidmVersion.V_1_7, context.getVersion()); @@ -409,6 +420,7 @@ private static TreeDataWriter createTreeDataWriter(Network n, ExportOptions opti return switch (options.getFormat()) { case XML -> createXmlWriter(n, os, options); case JSON -> createJsonWriter(os, options); + case BIN -> createBinWriter(os, options); }; } @@ -508,6 +520,7 @@ private static TreeDataReader createTreeDataReader(InputStream is, ImportOptions return switch (config.getFormat()) { case XML -> createXmlReader(is, config); case JSON -> createJsonReader(is, config); + case BIN -> new BinReader(is, BIIDM_MAGIC_NUMBER); }; } @@ -684,10 +697,9 @@ public static Network read(TreeDataReader reader, ImportOptions config, Anonymiz Objects.requireNonNull(networkFactory); Objects.requireNonNull(reporter); - IidmVersion iidmVersion = IidmVersion.of(reader.readRootVersion(), "."); - Map extensionVersions = reader.readVersions(); - - NetworkDeserializerContext context = new NetworkDeserializerContext(anonymizer, reader, config, iidmVersion, extensionVersions); + TreeDataHeader header = reader.readHeader(); + IidmVersion iidmVersion = IidmVersion.of(header.rootVersion(), "."); + NetworkDeserializerContext context = new NetworkDeserializerContext(anonymizer, reader, config, iidmVersion, header.extensionVersions()); Network network = initNetwork(networkFactory, context, reader, null); network.getReporterContext().pushReporter(reporter); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDe.java index d5824bf83a9..c0daff70056 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/OverloadManagementSystemSerDe.java @@ -37,7 +37,7 @@ protected String getRootElementName() { protected void writeRootElementAttributes(OverloadManagementSystem oms, Substation substation, NetworkSerializerContext context) { context.getWriter().writeBooleanAttribute("enabled", oms.isEnabled()); context.getWriter().writeStringAttribute("monitoredElementId", context.getAnonymizer().anonymizeString(oms.getMonitoredElementId())); - context.getWriter().writeStringAttribute("side", oms.getMonitoredSide().name()); + context.getWriter().writeEnumAttribute("side", oms.getMonitoredSide()); } @Override @@ -53,7 +53,7 @@ private void writeTripping(OverloadManagementSystem.Tripping tripping, NetworkSe writeTrippingCommonAttributes(tripping, context); context.getWriter().writeStringAttribute("branchId", context.getAnonymizer().anonymizeString(branchTripping.getBranchToOperateId())); - context.getWriter().writeStringAttribute("side", branchTripping.getSideToOperate().name()); + context.getWriter().writeEnumAttribute("side", branchTripping.getSideToOperate()); context.getWriter().writeEndNode(); } case SWITCH_TRIPPING -> { @@ -71,7 +71,7 @@ private void writeTripping(OverloadManagementSystem.Tripping tripping, NetworkSe writeTrippingCommonAttributes(tripping, context); context.getWriter().writeStringAttribute("threeWindingsTransformerId", context.getAnonymizer().anonymizeString(twtTripping.getThreeWindingsTransformerToOperateId())); - context.getWriter().writeStringAttribute("side", twtTripping.getSideToOperate().name()); + context.getWriter().writeEnumAttribute("side", twtTripping.getSideToOperate()); context.getWriter().writeEndNode(); } default -> throw new PowsyblException("Unexpected tripping type: " + tripping.getType()); @@ -95,12 +95,12 @@ protected OverloadManagementSystemAdder createAdder(Substation s) { @Override protected void readRootElementAttributes(OverloadManagementSystemAdder adder, + Substation parent, List> toApply, NetworkDeserializerContext context) { boolean enabled = context.getReader().readBooleanAttribute("enabled", true); String monitoredElementId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("monitoredElementId")); - String side = context.getReader().readStringAttribute("side"); - ThreeSides monitoredSide = side == null ? ThreeSides.ONE : ThreeSides.valueOf(side); + ThreeSides monitoredSide = context.getReader().readEnumAttribute("side", ThreeSides.class, ThreeSides.ONE); if (adder != null) { adder.setEnabled(enabled) .setMonitoredElementId(monitoredElementId) @@ -130,8 +130,7 @@ protected void readSubElements(String id, OverloadManagementSystemAdder adder, private static void readBranchTripping(OverloadManagementSystemAdder adder, NetworkDeserializerContext context, String key, String name, double currentLimit, boolean openAction) { String branchId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("branchId")); - String side = context.getReader().readStringAttribute("side"); - TwoSides sideToOperate = side == null ? TwoSides.ONE : TwoSides.valueOf(side); + TwoSides sideToOperate = context.getReader().readEnumAttribute("side", TwoSides.class, TwoSides.ONE); context.getReader().readEndNode(); if (adder != null) { adder.newBranchTripping() @@ -164,8 +163,7 @@ private static void readThreeWindingsTransformerTripping(OverloadManagementSyste String key, String name, double currentLimit, boolean openAction) { String twtId = context.getAnonymizer().deanonymizeString( context.getReader().readStringAttribute("threeWindingsTransformerId")); - String side = context.getReader().readStringAttribute("side"); - ThreeSides sideToOperate = side == null ? ThreeSides.ONE : ThreeSides.valueOf(side); + ThreeSides sideToOperate = context.getReader().readEnumAttribute("side", ThreeSides.class, ThreeSides.ONE); context.getReader().readEndNode(); if (adder != null) { adder.newThreeWindingsTransformerTripping() @@ -188,10 +186,10 @@ protected boolean postponeElementCreation() { return true; } - public final void skip(NetworkDeserializerContext context) { + public final void skip(Substation s, NetworkDeserializerContext context) { List> toApply = new ArrayList<>(); String id = readIdentifierAttributes(null, context); - readRootElementAttributes(null, toApply, context); + readRootElementAttributes(null, s, toApply, context); readSubElements(id, null, toApply, context); } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ShuntSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ShuntSerDe.java index 474171173a1..e386f4a78a8 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ShuntSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ShuntSerDe.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import java.util.List; +import java.util.OptionalInt; import java.util.function.Consumer; import static com.powsybl.iidm.serde.ConnectableSerDeUtil.readNodeOrBus; @@ -64,9 +65,11 @@ protected void writeRootElementAttributes(ShuntCompensator sc, VoltageLevel vl, int currentSectionCount = model instanceof ShuntCompensatorLinearModel ? sc.getSectionCount() : 1; context.getWriter().writeIntAttribute("currentSectionCount", currentSectionCount); }); - if (sc.findSectionCount().isPresent()) { - IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_3, context, () -> context.getWriter().writeIntAttribute("sectionCount", sc.getSectionCount())); - } + + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_3, context, () -> { + OptionalInt sectionCount = sc.findSectionCount(); + context.getWriter().writeOptionalIntAttribute("sectionCount", sectionCount.isPresent() ? sectionCount.getAsInt() : null); + }); IidmSerDeUtil.writeBooleanAttributeFromMinimumVersion(ROOT_ELEMENT_NAME, "voltageRegulatorOn", sc.isVoltageRegulatorOn(), false, IidmSerDeUtil.ErrorMessage.NOT_DEFAULT_NOT_SUPPORTED, IidmVersion.V_1_2, context); IidmSerDeUtil.writeDoubleAttributeFromMinimumVersion(ROOT_ELEMENT_NAME, "targetV", sc.getTargetV(), IidmSerDeUtil.ErrorMessage.NOT_DEFAULT_NOT_SUPPORTED, IidmVersion.V_1_2, context); @@ -130,7 +133,7 @@ private static void writeModel(ShuntCompensator sc, NetworkSerializerContext con } @Override - protected void readRootElementAttributes(ShuntCompensatorAdder adder, List> toApply, NetworkDeserializerContext context) { + protected void readRootElementAttributes(ShuntCompensatorAdder adder, VoltageLevel parent, List> toApply, NetworkDeserializerContext context) { IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_1, context, () -> adder.setVoltageRegulatorOn(false)); IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_2, context, () -> { double bPerSection = context.getReader().readDoubleAttribute(B_PER_SECTION); @@ -143,10 +146,8 @@ protected void readRootElementAttributes(ShuntCompensatorAdder adder, List { - Integer sectionCount = context.getReader().readIntAttribute("sectionCount"); - if (sectionCount != null) { - adder.setSectionCount(sectionCount); - } + OptionalInt sectionCount = context.getReader().readOptionalIntAttribute("sectionCount"); + sectionCount.ifPresent(adder::setSectionCount); }); IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_2, context, () -> { boolean voltageRegulatorOn = context.getReader().readBooleanAttribute("voltageRegulatorOn"); @@ -156,10 +157,16 @@ protected void readRootElementAttributes(ShuntCompensatorAdder adder, List sc.getTerminal().setP(p).setQ(q)); + readNodeOrBus(adder, context, parent.getTopologyKind()); + IidmSerDeUtil.runUntilMaximumVersion(IidmVersion.V_1_8, context, () -> { + double q = context.getReader().readDoubleAttribute("q"); + toApply.add(sc -> sc.getTerminal().setQ(q)); + }); + IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_9, context, () -> { + double p = context.getReader().readDoubleAttribute("p"); + double q = context.getReader().readDoubleAttribute("q"); + toApply.add(sc -> sc.getTerminal().setP(p).setQ(q)); + }); } @Override @@ -168,7 +175,7 @@ protected void readSubElements(String id, ShuntCompensatorAdder adder, List { String regId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("id")); - String regSide = context.getReader().readStringAttribute("side"); + ThreeSides regSide = context.getReader().readEnumAttribute("side", ThreeSides.class); context.getReader().readEndNode(); toApply.add(sc -> context.getEndTasks().add(() -> sc.setRegulatingTerminal(TerminalRefSerDe.resolve(regId, regSide, sc.getNetwork())))); } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/StaticVarCompensatorSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/StaticVarCompensatorSerDe.java index 1448035c761..d17d3c9379a 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/StaticVarCompensatorSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/StaticVarCompensatorSerDe.java @@ -43,9 +43,7 @@ protected void writeRootElementAttributes(StaticVarCompensator svc, VoltageLevel context.getWriter().writeDoubleAttribute(voltageSetpointName[0], svc.getVoltageSetpoint()); context.getWriter().writeDoubleAttribute(reactivePowerSetpointName[0], svc.getReactivePowerSetpoint()); - if (svc.getRegulationMode() != null) { - context.getWriter().writeStringAttribute("regulationMode", svc.getRegulationMode().name()); - } + context.getWriter().writeEnumAttribute("regulationMode", svc.getRegulationMode()); writeNodeOrBus(null, svc.getTerminal(), context); writePQ(null, svc.getTerminal(), context.getWriter()); } @@ -82,7 +80,7 @@ protected StaticVarCompensator readRootElementAttributes(StaticVarCompensatorAdd .setVoltageSetpoint(voltageSetpoint) .setReactivePowerSetpoint(reactivePowerSetpoint) .setRegulationMode(regulationMode); - readNodeOrBus(adder, context); + readNodeOrBus(adder, context, voltageLevel.getTopologyKind()); StaticVarCompensator svc = adder.add(); readPQ(null, svc.getTerminal(), context.getReader()); return svc; diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/SubstationSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/SubstationSerDe.java index e70034b8999..bf1ecf7c21b 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/SubstationSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/SubstationSerDe.java @@ -36,14 +36,9 @@ protected String getRootElementName() { @Override protected void writeRootElementAttributes(Substation s, Network n, NetworkSerializerContext context) { - Optional country = s.getCountry(); - country.ifPresent(value -> context.getWriter().writeStringAttribute(COUNTRY, context.getAnonymizer().anonymizeCountry(value).toString())); - if (s.getTso() != null) { - context.getWriter().writeStringAttribute("tso", context.getAnonymizer().anonymizeString(s.getTso())); - } - if (!s.getGeographicalTags().isEmpty()) { - context.getWriter().writeStringArrayAttribute("geographicalTags", s.getGeographicalTags().stream().map(tag -> context.getAnonymizer().anonymizeString(tag)).toList()); - } + context.getWriter().writeStringAttribute(COUNTRY, s.getCountry().map(c -> context.getAnonymizer().anonymizeCountry(c).toString()).orElse(null)); + context.getWriter().writeStringAttribute("tso", Optional.ofNullable(s.getTso()).map(tso -> context.getAnonymizer().anonymizeString(tso)).orElse(null)); + context.getWriter().writeStringArrayAttribute("geographicalTags", s.getGeographicalTags().stream().map(tag -> context.getAnonymizer().anonymizeString(tag)).toList()); } @Override @@ -179,7 +174,7 @@ private static void checkSupportedAndReadOverloadManagementSystems(Substation s, if (context.getOptions().isWithAutomationSystems()) { OverloadManagementSystemSerDe.INSTANCE.read(s, context); } else { - OverloadManagementSystemSerDe.INSTANCE.skip(context); + OverloadManagementSystemSerDe.INSTANCE.skip(s, context); } } } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TerminalRefSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TerminalRefSerDe.java index ade98f6a3e2..4e376671782 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TerminalRefSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TerminalRefSerDe.java @@ -11,6 +11,7 @@ import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.iidm.network.*; +import java.util.Optional; import java.util.function.Consumer; /** @@ -30,17 +31,35 @@ public static void writeTerminalRef(Terminal t, NetworkSerializerContext context } public static void writeTerminalRef(Terminal t, NetworkSerializerContext context, String namespace, String elementName, TreeDataWriter writer) { - writer.writeStartNode(namespace, elementName); - writeTerminalRefAttribute(t, context, writer); - writer.writeEndNode(); + if (t != null) { + writer.writeStartNode(namespace, elementName); + writeTerminalRefAttribute(t, context, writer); + writer.writeEndNode(); + } } public static void writeTerminalRefAttribute(Terminal t, NetworkSerializerContext context) { writeTerminalRefAttribute(t, context, context.getWriter()); } - public static void writeTerminalRefAttribute(Terminal t, NetworkSerializerContext context, TreeDataWriter writer) { - Connectable c = t.getConnectable(); + public static void writeTerminalRefAttribute(Terminal terminal, NetworkSerializerContext context, TreeDataWriter writer) { + + String connectableId = Optional.ofNullable(terminal) + .map(t -> { + checkTerminal(t, context); + return context.getAnonymizer().anonymizeString(t.getConnectable().getId()); + }) + .orElse(null); + ThreeSides tSide = Optional.ofNullable(terminal) + .flatMap(Terminal::getConnectableSide) + .orElse(null); + + writer.writeStringAttribute("id", connectableId); + writer.writeEnumAttribute("side", tSide); + } + + private static void checkTerminal(Terminal t, NetworkSerializerContext context) { + Connectable c = t.getConnectable(); if (!context.getFilter().test(c)) { throw new PowsyblException("Oups, terminal ref point to a filtered equipment " + c.getId()); } @@ -50,21 +69,18 @@ public static void writeTerminalRefAttribute(Terminal t, NetworkSerializerContex throw new PowsyblException(String.format("Terminal ref should not point to a busbar section (here %s). Try to export in node-breaker or delete this terminal ref.", t.getConnectable().getId())); } - writer.writeStringAttribute("id", context.getAnonymizer().anonymizeString(c.getId())); - - Terminal.getConnectableSide(t).ifPresent(side -> writer.writeStringAttribute("side", side.name())); } public static Terminal readTerminal(NetworkDeserializerContext context, Network n) { String id = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(ID)); - String side = context.getReader().readStringAttribute(SIDE); + ThreeSides side = context.getReader().readEnumAttribute(SIDE, ThreeSides.class); context.getReader().readEndNode(); return TerminalRefSerDe.resolve(id, side, n); } public static void readTerminalRef(NetworkDeserializerContext context, Network network, Consumer endTaskTerminalConsumer) { String id = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(ID)); - String side = context.getReader().readStringAttribute(SIDE); + ThreeSides side = context.getReader().readEnumAttribute(SIDE, ThreeSides.class); context.getReader().readEndNode(); context.getEndTasks().add(() -> { Terminal t = resolve(id, side, network); @@ -72,17 +88,12 @@ public static void readTerminalRef(NetworkDeserializerContext context, Network n }); } - public static Terminal resolve(String id, String sideText, Network network) { - ThreeSides side = sideText == null ? ThreeSides.ONE : ThreeSides.valueOf(sideText); - return TerminalRefSerDe.resolve(id, side, network); - } - public static Terminal resolve(String id, ThreeSides side, Network network) { Identifiable identifiable = network.getIdentifiable(id); if (identifiable == null) { throw new PowsyblException("Terminal reference identifiable not found: '" + id + "'"); } - return Terminal.getTerminal(identifiable, side); + return Terminal.getTerminal(identifiable, side != null ? side : ThreeSides.ONE); } private TerminalRefSerDe() { diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ThreeWindingsTransformerSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ThreeWindingsTransformerSerDe.java index d63bd8850b1..bffc3a81d07 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ThreeWindingsTransformerSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/ThreeWindingsTransformerSerDe.java @@ -65,11 +65,9 @@ protected void writeRootElementAttributes(ThreeWindingsTransformer twt, Substati writeNodeOrBus(1, twt.getLeg1().getTerminal(), context); writeNodeOrBus(2, twt.getLeg2().getTerminal(), context); writeNodeOrBus(3, twt.getLeg3().getTerminal(), context); - if (context.getOptions().isWithBranchSV()) { - writePQ(1, twt.getLeg1().getTerminal(), context.getWriter()); - writePQ(2, twt.getLeg2().getTerminal(), context.getWriter()); - writePQ(3, twt.getLeg3().getTerminal(), context.getWriter()); - } + writeOptionalPQ(1, twt.getLeg1().getTerminal(), context.getWriter(), context.getOptions()::isWithBranchSV); + writeOptionalPQ(2, twt.getLeg2().getTerminal(), context.getWriter(), context.getOptions()::isWithBranchSV); + writeOptionalPQ(3, twt.getLeg3().getTerminal(), context.getWriter(), context.getOptions()::isWithBranchSV); } @Override @@ -162,16 +160,16 @@ protected ThreeWindingsTransformer readRootElementAttributes(ThreeWindingsTransf adder.setRatedU0(ratedU0); }); - readNodeOrBus(1, legAdder1, context); - readNodeOrBus(2, legAdder2, context); - readNodeOrBus(3, legAdder3, context); + readNodeOrBus(1, legAdder1, s.getNetwork(), context); + readNodeOrBus(2, legAdder2, s.getNetwork(), context); + readNodeOrBus(3, legAdder3, s.getNetwork(), context); legAdder1.add(); legAdder2.add(); legAdder3.add(); ThreeWindingsTransformer twt = adder.add(); - readPQ(1, twt.getLeg1().getTerminal(), context.getReader()); - readPQ(2, twt.getLeg2().getTerminal(), context.getReader()); - readPQ(3, twt.getLeg3().getTerminal(), context.getReader()); + readOptionalPQ(1, twt.getLeg1().getTerminal(), context.getReader()); + readOptionalPQ(2, twt.getLeg2().getTerminal(), context.getReader()); + readOptionalPQ(3, twt.getLeg3().getTerminal(), context.getReader()); return twt; } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TieLineSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TieLineSerDe.java index 4ad5aedfc9b..350a2842938 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TieLineSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TieLineSerDe.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import java.util.Optional; +import java.util.OptionalDouble; import static com.powsybl.iidm.serde.ConnectableSerDeUtil.*; @@ -35,7 +36,7 @@ protected String getRootElementName() { private static void writeDanglingLine(DanglingLine danglingLine, NetworkSerializerContext context, int side) { Boundary boundary = danglingLine.getBoundary(); context.getWriter().writeStringAttribute("id_" + side, context.getAnonymizer().anonymizeString(danglingLine.getId())); - danglingLine.getOptionalName().ifPresent(name -> context.getWriter().writeStringAttribute("name_" + side, context.getAnonymizer().anonymizeString(name))); + context.getWriter().writeStringAttribute("name_" + side, danglingLine.getOptionalName().map(n -> context.getAnonymizer().anonymizeString(n)).orElse(null)); context.getWriter().writeDoubleAttribute("r_" + side, danglingLine.getR()); context.getWriter().writeDoubleAttribute("x_" + side, danglingLine.getX()); // TODO change serialization @@ -63,10 +64,8 @@ protected void writeRootElementAttributes(TieLine tl, Network n, NetworkSerializ } writeNodeOrBus(1, tl.getDanglingLine1().getTerminal(), context); writeNodeOrBus(2, tl.getDanglingLine2().getTerminal(), context); - if (context.getOptions().isWithBranchSV()) { - writePQ(1, tl.getDanglingLine1().getTerminal(), context.getWriter()); - writePQ(2, tl.getDanglingLine2().getTerminal(), context.getWriter()); - } + writeOptionalPQ(1, tl.getDanglingLine1().getTerminal(), context.getWriter(), context.getOptions()::isWithBranchSV); + writeOptionalPQ(2, tl.getDanglingLine2().getTerminal(), context.getWriter(), context.getOptions()::isWithBranchSV); writeDanglingLine(tl.getDanglingLine1(), context, 1); writeDanglingLine(tl.getDanglingLine2(), context, 2); }); @@ -160,14 +159,16 @@ protected TieLine readRootElementAttributes(TieLineAdder adder, Network network, String pairingKey = context.getReader().readStringAttribute("ucteXnodeCode"); DanglingLineAdder adderDl1 = readVlAndNodeOrBus(context, network, 1); DanglingLineAdder adderDl2 = readVlAndNodeOrBus(context, network, 2); - double p1 = context.getReader().readDoubleAttribute("p1"); - double q1 = context.getReader().readDoubleAttribute("q1"); - double p2 = context.getReader().readDoubleAttribute("p2"); - double q2 = context.getReader().readDoubleAttribute("q2"); + OptionalDouble p1 = context.getReader().readOptionalDoubleAttribute("p1"); + OptionalDouble q1 = context.getReader().readOptionalDoubleAttribute("q1"); + OptionalDouble p2 = context.getReader().readOptionalDoubleAttribute("p2"); + OptionalDouble q2 = context.getReader().readOptionalDoubleAttribute("q2"); DanglingLine dl1 = readDanglingLine(adderDl1, pairingKey, context, 1); DanglingLine dl2 = readDanglingLine(adderDl2, pairingKey, context, 2); - dl1.getTerminal().setP(p1).setQ(q1); - dl2.getTerminal().setP(p2).setQ(q2); + p1.ifPresent(dl1.getTerminal()::setP); + q1.ifPresent(dl1.getTerminal()::setQ); + p2.ifPresent(dl2.getTerminal()::setP); + q2.ifPresent(dl2.getTerminal()::setQ); adder.setDanglingLine1(dl1.getId()).setDanglingLine2(dl2.getId()); }); IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_10, context, () -> { @@ -180,8 +181,9 @@ protected TieLine readRootElementAttributes(TieLineAdder adder, Network network, private static DanglingLineAdder readVlAndNodeOrBus(NetworkDeserializerContext context, Network network, int side) { String voltageLevelId = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute("voltageLevelId" + side)); - DanglingLineAdder adderDl1 = network.getVoltageLevel(voltageLevelId).newDanglingLine(); - readNodeOrBus(adderDl1, String.valueOf(side), context); + VoltageLevel voltageLevel = network.getVoltageLevel(voltageLevelId); + DanglingLineAdder adderDl1 = voltageLevel.newDanglingLine(); + readNodeOrBus(adderDl1, String.valueOf(side), context, voltageLevel.getTopologyKind()); return adderDl1; } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TwoWindingsTransformerSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TwoWindingsTransformerSerDe.java index 02f0b3e233b..949f2fa041a 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TwoWindingsTransformerSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/TwoWindingsTransformerSerDe.java @@ -40,10 +40,8 @@ protected void writeRootElementAttributes(TwoWindingsTransformer twt, Substation writeRatedS("ratedS", twt.getRatedS(), context); writeNodeOrBus(1, twt.getTerminal1(), context); writeNodeOrBus(2, twt.getTerminal2(), context); - if (context.getOptions().isWithBranchSV()) { - writePQ(1, twt.getTerminal1(), context.getWriter()); - writePQ(2, twt.getTerminal2(), context.getWriter()); - } + writeOptionalPQ(1, twt.getTerminal1(), context.getWriter(), context.getOptions()::isWithBranchSV); + writeOptionalPQ(2, twt.getTerminal2(), context.getWriter(), context.getOptions()::isWithBranchSV); } @Override @@ -108,10 +106,10 @@ protected TwoWindingsTransformer readRootElementAttributes(TwoWindingsTransforme .setRatedU1(ratedU1) .setRatedU2(ratedU2); readRatedS("ratedS", context, adder::setRatedS); - readNodeOrBus(adder, context); + ConnectableSerDeUtil.readVoltageLevelAndNodeOrBus(adder, s.getNetwork(), context); TwoWindingsTransformer twt = adder.add(); - readPQ(1, twt.getTerminal1(), context.getReader()); - readPQ(2, twt.getTerminal2(), context.getReader()); + readOptionalPQ(1, twt.getTerminal1(), context.getReader()); + readOptionalPQ(2, twt.getTerminal2(), context.getReader()); return twt; } diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageAngleLimitSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageAngleLimitSerDe.java index 3f3e5f7105e..8f4751fe76d 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageAngleLimitSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageAngleLimitSerDe.java @@ -15,6 +15,8 @@ import com.powsybl.iidm.network.VoltageAngleLimitAdder; import com.powsybl.iidm.serde.util.IidmSerDeUtil; +import java.util.OptionalDouble; + /** * * @author Luma ZamarreƱo {@literal } @@ -34,8 +36,12 @@ public static void write(VoltageAngleLimit voltageAngleLimit, NetworkSerializerC IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_11, context, () -> { context.getWriter().writeStartNode(context.getVersion().getNamespaceURI(context.isValid()), ROOT_ELEMENT_NAME); context.getWriter().writeStringAttribute(ID, context.getAnonymizer().anonymizeString(voltageAngleLimit.getId())); - voltageAngleLimit.getLowLimit().ifPresent(low -> context.getWriter().writeDoubleAttribute(LOW_LIMIT, low)); - voltageAngleLimit.getHighLimit().ifPresent(high -> context.getWriter().writeDoubleAttribute(HIGH_LIMIT, high)); + + OptionalDouble lowLimit = voltageAngleLimit.getLowLimit(); + OptionalDouble highLimit = voltageAngleLimit.getHighLimit(); + context.getWriter().writeOptionalDoubleAttribute(LOW_LIMIT, lowLimit.isPresent() ? lowLimit.getAsDouble() : null); + context.getWriter().writeOptionalDoubleAttribute(HIGH_LIMIT, highLimit.isPresent() ? highLimit.getAsDouble() : null); + TerminalRefSerDe.writeTerminalRef(voltageAngleLimit.getTerminalFrom(), context, FROM); TerminalRefSerDe.writeTerminalRef(voltageAngleLimit.getTerminalTo(), context, TO); context.getWriter().writeEndNode(); @@ -46,17 +52,13 @@ public static void read(Network network, NetworkDeserializerContext context) { IidmSerDeUtil.runFromMinimumVersion(IidmVersion.V_1_11, context, () -> { String id = context.getAnonymizer().deanonymizeString(context.getReader().readStringAttribute(ID)); - double lowLimit = context.getReader().readDoubleAttribute(LOW_LIMIT); - double highLimit = context.getReader().readDoubleAttribute(HIGH_LIMIT); + OptionalDouble lowLimit = context.getReader().readOptionalDoubleAttribute(LOW_LIMIT); + OptionalDouble highLimit = context.getReader().readOptionalDoubleAttribute(HIGH_LIMIT); VoltageAngleLimitAdder adder = network.newVoltageAngleLimit(); adder.setId(id); - if (!Double.isNaN(lowLimit)) { - adder.setLowLimit(lowLimit); - } - if (!Double.isNaN(highLimit)) { - adder.setHighLimit(highLimit); - } + lowLimit.ifPresent(adder::setLowLimit); + highLimit.ifPresent(adder::setHighLimit); context.getReader().readChildNodes(elementName -> { Terminal terminal = TerminalRefSerDe.readTerminal(context, network); switch (elementName) { diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelSerDe.java index bb09f0b23ff..a755993a3c9 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VoltageLevelSerDe.java @@ -51,7 +51,7 @@ protected void writeRootElementAttributes(VoltageLevel vl, Container { - context.getReader().readStringAttribute(NODE_COUNT); + context.getReader().readIntAttribute(NODE_COUNT); LOGGER.trace("attribute " + NODE_BREAKER_TOPOLOGY_ELEMENT_NAME + ".nodeCount is ignored."); }); context.getReader().readChildNodes(elementName -> { diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VscConverterStationSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VscConverterStationSerDe.java index beabbc8b29c..6ed6a10a658 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VscConverterStationSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/VscConverterStationSerDe.java @@ -62,7 +62,7 @@ protected VscConverterStation readRootElementAttributes(VscConverterStationAdder float lossFactor = context.getReader().readFloatAttribute("lossFactor"); double voltageSetpoint = context.getReader().readDoubleAttribute("voltageSetpoint"); double reactivePowerSetpoint = context.getReader().readDoubleAttribute("reactivePowerSetpoint"); - readNodeOrBus(adder, context); + readNodeOrBus(adder, context, voltageLevel.getTopologyKind()); adder .setLossFactor(lossFactor) .setVoltageSetpoint(voltageSetpoint) diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java index 5b78fdd3216..de0670ab858 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ActivePowerControlSerDe.java @@ -75,7 +75,7 @@ public List getXsdAsStreamList() { @Override public ActivePowerControl read(T identifiable, DeserializerContext context) { boolean participate = context.getReader().readBooleanAttribute("participate"); - float droop = context.getReader().readFloatAttribute("droop"); + double droop = context.getReader().readDoubleAttribute("droop"); double participationFactor = Double.NaN; NetworkDeserializerContext networkContext = (NetworkDeserializerContext) context; String extVersionStr = networkContext.getExtensionVersion(this).orElseThrow(IllegalStateException::new); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/BranchObservabilitySerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/BranchObservabilitySerDe.java index db667605176..32313d57722 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/BranchObservabilitySerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/BranchObservabilitySerDe.java @@ -54,14 +54,13 @@ public void write(BranchObservability branchObservability, SerializerContext } private void writeOptionalQuality(SerializerContext context, ObservabilityQuality quality, String type, TwoSides side) { - if (quality == null) { - return; + if (quality != null) { + context.getWriter().writeStartNode(getNamespaceUri(), type); + context.getWriter().writeEnumAttribute(SIDE, side); + context.getWriter().writeDoubleAttribute(STANDARD_DEVIATION, quality.getStandardDeviation()); + context.getWriter().writeOptionalBooleanAttribute(REDUNDANT, quality.isRedundant().orElse(null)); + context.getWriter().writeEndNode(); } - context.getWriter().writeStartNode(getNamespaceUri(), type); - context.getWriter().writeEnumAttribute(SIDE, side); - context.getWriter().writeDoubleAttribute(STANDARD_DEVIATION, quality.getStandardDeviation()); - quality.isRedundant().ifPresent(redundant -> context.getWriter().writeBooleanAttribute(REDUNDANT, redundant)); - context.getWriter().writeEndNode(); } @Override @@ -74,7 +73,7 @@ public BranchObservability read(T identifiable, DeserializerContext context) context.getReader().readChildNodes(elementName -> { var side = context.getReader().readEnumAttribute(SIDE, TwoSides.class); var standardDeviation = context.getReader().readDoubleAttribute(STANDARD_DEVIATION); - var redundant = context.getReader().readBooleanAttribute(REDUNDANT); + var redundant = context.getReader().readOptionalBooleanAttribute(REDUNDANT).orElse(null); context.getReader().readEndNode(); switch (elementName) { case QUALITY_P -> readQualityP(standardDeviation, redundant, side, adder); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ConnectablePositionSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ConnectablePositionSerDe.java index d97fdb4ae2a..6044961a19e 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ConnectablePositionSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/ConnectablePositionSerDe.java @@ -22,7 +22,6 @@ import java.io.InputStream; import java.util.List; -import java.util.Optional; /** * @author Geoffroy Jamgotchian {@literal } @@ -67,14 +66,13 @@ private void writePosition(String connectableId, ConnectablePosition.Feeder feed context.getWriter().writeStringAttribute("name", feeder.getName().orElse(connectableId)); break; case V_1_1: - feeder.getName().ifPresent(name -> context.getWriter().writeStringAttribute("name", name)); + context.getWriter().writeStringAttribute("name", feeder.getName().orElse(null)); break; default: throw new PowsyblException("Unsupported version (" + extVersionStr + ") for " + ConnectablePosition.NAME); } - Optional oOrder = feeder.getOrder(); - oOrder.ifPresent(integer -> context.getWriter().writeIntAttribute("order", integer)); - context.getWriter().writeStringAttribute("direction", feeder.getDirection().name()); + context.getWriter().writeOptionalIntAttribute("order", feeder.getOrder().orElse(null)); + context.getWriter().writeEnumAttribute("direction", feeder.getDirection()); context.getWriter().writeEndNode(); } @@ -98,8 +96,8 @@ public void write(ConnectablePosition connectablePosition, SerializerContext private void readPosition(DeserializerContext context, ConnectablePositionAdder.FeederAdder adder) { String name = context.getReader().readStringAttribute("name"); - Optional.ofNullable(context.getReader().readIntAttribute("order")). - ifPresent(adder::withOrder); + context.getReader().readOptionalIntAttribute("order") + .ifPresent(adder::withOrder); ConnectablePosition.Direction direction = context.getReader().readEnumAttribute("direction", ConnectablePosition.Direction.class); context.getReader().readEndNode(); if (name != null) { diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/DiscreteMeasurementsSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/DiscreteMeasurementsSerDe.java index 39c56afe8c7..3d486eecc6f 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/DiscreteMeasurementsSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/DiscreteMeasurementsSerDe.java @@ -10,10 +10,10 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.commons.extensions.AbstractExtensionSerDe; import com.powsybl.commons.extensions.ExtensionSerDe; -import com.powsybl.commons.io.TreeDataReader; -import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.commons.io.DeserializerContext; import com.powsybl.commons.io.SerializerContext; +import com.powsybl.commons.io.TreeDataReader; +import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.iidm.network.Identifiable; import com.powsybl.iidm.network.extensions.DiscreteMeasurement; import com.powsybl.iidm.network.extensions.DiscreteMeasurementAdder; @@ -21,7 +21,6 @@ import com.powsybl.iidm.network.extensions.DiscreteMeasurementsAdder; import java.util.Map; -import java.util.Optional; /** * @author Miora Ralambotiana {@literal } @@ -57,17 +56,9 @@ public void write(DiscreteMeasurements extension, SerializerContext context) writer.writeEnumAttribute("tapChanger", discreteMeasurement.getTapChanger()); writer.writeEnumAttribute("valueType", discreteMeasurement.getValueType()); switch (discreteMeasurement.getValueType()) { - case BOOLEAN: - writer.writeBooleanAttribute(VALUE, discreteMeasurement.getValueAsBoolean()); - break; - case INT: - writer.writeIntAttribute(VALUE, discreteMeasurement.getValueAsInt()); - break; - case STRING: - writer.writeStringAttribute(VALUE, discreteMeasurement.getValueAsString()); - break; - default: - throw new PowsyblException("Unsupported serialization for value type: " + discreteMeasurement.getValueType()); + case BOOLEAN -> writer.writeBooleanAttribute(VALUE, discreteMeasurement.getValueAsBoolean()); + case INT -> writer.writeIntAttribute(VALUE, discreteMeasurement.getValueAsInt()); + case STRING -> writer.writeStringAttribute(VALUE, discreteMeasurement.getValueAsString()); } writer.writeBooleanAttribute("valid", discreteMeasurement.isValid()); @@ -110,10 +101,9 @@ private static > void readDiscreteMeasurement(Discrete .setType(type) .setTapChanger(tapChanger); switch (valueType) { - case BOOLEAN -> Optional.ofNullable(reader.readBooleanAttribute(VALUE)).ifPresent(adder::setValue); - case INT -> Optional.ofNullable(reader.readIntAttribute(VALUE)).ifPresent(adder::setValue); - case STRING -> Optional.ofNullable(reader.readStringAttribute(VALUE)).ifPresent(adder::setValue); - default -> throw new PowsyblException("Unsupported value type: " + valueType); + case BOOLEAN -> adder.setValue(reader.readBooleanAttribute(VALUE)); + case INT -> adder.setValue(reader.readIntAttribute(VALUE)); + case STRING -> adder.setValue(reader.readStringAttribute(VALUE)); } adder.setValid(reader.readBooleanAttribute("valid", true)); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/InjectionObservabilitySerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/InjectionObservabilitySerDe.java index 3c62950adf8..f5db6b2df76 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/InjectionObservabilitySerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/InjectionObservabilitySerDe.java @@ -10,9 +10,9 @@ import com.powsybl.commons.PowsyblException; import com.powsybl.commons.extensions.AbstractExtensionSerDe; import com.powsybl.commons.extensions.ExtensionSerDe; -import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.commons.io.DeserializerContext; import com.powsybl.commons.io.SerializerContext; +import com.powsybl.commons.io.TreeDataWriter; import com.powsybl.iidm.network.Injection; import com.powsybl.iidm.network.extensions.InjectionObservability; import com.powsybl.iidm.network.extensions.InjectionObservabilityAdder; @@ -48,7 +48,7 @@ private void writeOptionalQuality(String elementName, ObservabilityQuality qu if (quality != null) { writer.writeStartNode(getNamespaceUri(), elementName); writer.writeDoubleAttribute(STANDARD_DEVIATION, quality.getStandardDeviation()); - quality.isRedundant().ifPresent(redundant -> writer.writeBooleanAttribute(REDUNDANT, redundant)); + writer.writeOptionalBooleanAttribute(REDUNDANT, quality.isRedundant().orElse(null)); writer.writeEndNode(); } } @@ -62,7 +62,7 @@ public InjectionObservability read(T identifiable, DeserializerContext contex context.getReader().readChildNodes(elementName -> { var standardDeviation = context.getReader().readDoubleAttribute(STANDARD_DEVIATION); - var redundant = context.getReader().readBooleanAttribute(REDUNDANT, false); + boolean redundant = context.getReader().readOptionalBooleanAttribute(REDUNDANT).orElse(false); context.getReader().readEndNode(); switch (elementName) { case QUALITY_P -> adder.withStandardDeviationP(standardDeviation).withRedundantP(redundant); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/LoadAsymmetricalSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/LoadAsymmetricalSerDe.java index ce194037da4..cdfdbc5e803 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/LoadAsymmetricalSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/LoadAsymmetricalSerDe.java @@ -30,7 +30,7 @@ public LoadAsymmetricalSerDe() { @Override public void write(LoadAsymmetrical loadAsym, SerializerContext context) { - context.getWriter().writeStringAttribute("connectionType", loadAsym.getConnectionType().name()); + context.getWriter().writeEnumAttribute("connectionType", loadAsym.getConnectionType()); context.getWriter().writeDoubleAttribute("deltaPa", loadAsym.getDeltaPa(), 0); context.getWriter().writeDoubleAttribute("deltaQa", loadAsym.getDeltaQa(), 0); context.getWriter().writeDoubleAttribute("deltaPb", loadAsym.getDeltaPb(), 0); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/MeasurementsSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/MeasurementsSerDe.java index 4fcac7e101d..458ccdff223 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/MeasurementsSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/MeasurementsSerDe.java @@ -48,9 +48,7 @@ public void write(Measurements extension, SerializerContext context) { writer.writeStartNodes(); for (Measurement measurement : extension.getMeasurements()) { writer.writeStartNode(getNamespaceUri(), MEASUREMENT_ROOT_ELEMENT); - if (measurement.getId() != null) { - writer.writeStringAttribute("id", measurement.getId()); - } + writer.writeStringAttribute("id", measurement.getId()); writer.writeEnumAttribute("type", measurement.getType()); writer.writeEnumAttribute("side", measurement.getSide()); writer.writeDoubleAttribute(VALUE, measurement.getValue()); diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/SlackTerminalSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/SlackTerminalSerDe.java index 5089569ed2a..e439127f04d 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/SlackTerminalSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/SlackTerminalSerDe.java @@ -73,10 +73,7 @@ public List getXsdAsStreamList() { @Override public void write(SlackTerminal slackTerminal, SerializerContext context) { - Terminal terminal = slackTerminal.getTerminal(); - if (terminal != null) { - TerminalRefSerDe.writeTerminalRefAttribute(slackTerminal.getTerminal(), (NetworkSerializerContext) context); - } + TerminalRefSerDe.writeTerminalRefAttribute(slackTerminal.getTerminal(), (NetworkSerializerContext) context); } @Override diff --git a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/TwoWindingsTransformerFortescueSerDe.java b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/TwoWindingsTransformerFortescueSerDe.java index 13d2c0f9a38..24e2822195c 100644 --- a/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/TwoWindingsTransformerFortescueSerDe.java +++ b/iidm/iidm-serde/src/main/java/com/powsybl/iidm/serde/extensions/TwoWindingsTransformerFortescueSerDe.java @@ -33,8 +33,8 @@ public void write(TwoWindingsTransformerFortescue twtFortescue, SerializerContex context.getWriter().writeDoubleAttribute("rz", twtFortescue.getRz(), Double.NaN); context.getWriter().writeDoubleAttribute("xz", twtFortescue.getXz(), Double.NaN); context.getWriter().writeBooleanAttribute("freeFluxes", twtFortescue.isFreeFluxes()); - context.getWriter().writeStringAttribute("connectionType1", twtFortescue.getConnectionType1().name()); - context.getWriter().writeStringAttribute("connectionType2", twtFortescue.getConnectionType2().name()); + context.getWriter().writeEnumAttribute("connectionType1", twtFortescue.getConnectionType1()); + context.getWriter().writeEnumAttribute("connectionType2", twtFortescue.getConnectionType2()); context.getWriter().writeDoubleAttribute("groundingR1", twtFortescue.getGroundingR1(), 0); context.getWriter().writeDoubleAttribute("groundingX1", twtFortescue.getGroundingX1(), 0); context.getWriter().writeDoubleAttribute("groundingR2", twtFortescue.getGroundingR2(), 0); diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java index 169a85e4316..2a18d6b6a83 100644 --- a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/AbstractIidmSerDeTest.java @@ -260,8 +260,19 @@ private static Network jsonWriteAndRead(Network networkInput, ExportOptions opti TreeDataFormat previousFormat = options.getFormat(); options.setFormat(TreeDataFormat.JSON); Anonymizer anonymizer = NetworkSerDe.write(networkInput, options, path); + + Network network1; + try (InputStream is = Files.newInputStream(path)) { + network1 = NetworkSerDe.read(is, new ImportOptions().setFormat(TreeDataFormat.JSON), anonymizer); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + options.setFormat(TreeDataFormat.BIN); + anonymizer = NetworkSerDe.write(network1, options, path); + try (InputStream is = Files.newInputStream(path)) { - Network networkOutput = NetworkSerDe.read(is, new ImportOptions().setFormat(TreeDataFormat.JSON), anonymizer); + Network networkOutput = NetworkSerDe.read(is, new ImportOptions().setFormat(TreeDataFormat.BIN), anonymizer); options.setFormat(previousFormat); return networkOutput; } catch (IOException e) { diff --git a/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/EurostagBinaryTest.java b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/EurostagBinaryTest.java new file mode 100644 index 00000000000..2d1a6a9738b --- /dev/null +++ b/iidm/iidm-serde/src/test/java/com/powsybl/iidm/serde/EurostagBinaryTest.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.iidm.serde; + +import com.powsybl.commons.io.TreeDataFormat; +import com.powsybl.commons.test.ComparisonUtils; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.extensions.ActivePowerControlAdder; +import com.powsybl.iidm.network.extensions.ConnectablePosition; +import com.powsybl.iidm.network.extensions.ConnectablePositionAdder; +import com.powsybl.iidm.network.test.EurostagTutorialExample1Factory; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.nio.file.Files; + +import static com.powsybl.iidm.serde.IidmSerDeConstants.CURRENT_IIDM_VERSION; + +/** + * @author Geoffroy Jamgotchian {@literal } + */ +class EurostagBinaryTest extends AbstractIidmSerDeTest { + + @Test + void roundTripTest() throws IOException { + String fileName = "eurostag-tutorial1-lf.bin"; + roundTripTest(EurostagTutorialExample1Factory.createWithLFResults(), + (n, jsonFile) -> NetworkSerDe.write(n, new ExportOptions().setFormat(TreeDataFormat.BIN), jsonFile), + n -> { + try (InputStream is = Files.newInputStream(n)) { + return Network.read(fileName, is); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + }, + ComparisonUtils::compareBytes, + getVersionedNetworkPath(fileName, CURRENT_IIDM_VERSION)); + + //backward compatibility + roundTripVersionedJsonFromMinToCurrentVersionTest(fileName, IidmVersion.V_1_12); + } + + @Test + void roundTripTestWithExtension() throws IOException { + ExportOptions exportOptions = new ExportOptions().setFormat(TreeDataFormat.BIN); + ImportOptions importOptions = new ImportOptions().setFormat(TreeDataFormat.BIN); + Network network = EurostagTutorialExample1Factory.createWithLFResults(); + network.getGeneratorStream().findFirst().ifPresent(g -> g.newExtension(ActivePowerControlAdder.class).withDroop(2).withParticipate(true).add()); + network.getLoadStream().forEach(l -> l.newExtension(ConnectablePositionAdder.class).newFeeder().withDirection(ConnectablePosition.Direction.BOTTOM).add().add()); + roundTripTest(network, + (n, binFile) -> NetworkSerDe.write(n, exportOptions, binFile), + binFile -> NetworkSerDe.read(binFile, importOptions), + ComparisonUtils::compareBytes, + getVersionedNetworkPath("eurostag-tutorial1-lf-extensions.bin", CURRENT_IIDM_VERSION)); + + //backward compatibility + roundTripVersionedJsonFromMinToCurrentVersionTest("eurostag-tutorial1-lf-extensions.bin", IidmVersion.V_1_12); + } +} diff --git a/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.bin b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.bin new file mode 100644 index 00000000000..c110968ce65 Binary files /dev/null and b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf-extensions.bin differ diff --git a/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf.bin b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf.bin new file mode 100644 index 00000000000..796dc0566ae Binary files /dev/null and b/iidm/iidm-serde/src/test/resources/V1_12/eurostag-tutorial1-lf.bin differ