SecondaryVoltageControlSerDe.java

/**
 * Copyright (c) 2023, 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.extensions;

import com.google.auto.service.AutoService;
import com.powsybl.commons.PowsyblException;
import com.powsybl.commons.extensions.AbstractExtensionSerDe;
import com.powsybl.commons.extensions.ExtensionSerDe;
import com.powsybl.commons.io.DeserializerContext;
import com.powsybl.commons.io.SerializerContext;
import com.powsybl.commons.io.TreeDataWriter;
import com.powsybl.iidm.network.Network;
import com.powsybl.iidm.network.extensions.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Geoffroy Jamgotchian {@literal <geoffroy.jamgotchian at rte-france.com>}
 */
@AutoService(ExtensionSerDe.class)
public class SecondaryVoltageControlSerDe extends AbstractExtensionSerDe<Network, SecondaryVoltageControl> {

    private static final String CONTROL_ZONE_ROOT_ELEMENT = "controlZone";
    private static final String CONTROL_ZONE_ARRAY_ELEMENT = "controlZones";
    private static final String PILOT_POINT_ELEMENT = "pilotPoint";
    private static final String BUSBAR_SECTION_OR_BUS_ID_ROOT_ELEMENT = "busbarSectionOrBusId";
    private static final String BUSBAR_SECTION_OR_BUS_ID_ARRAY_ELEMENT = "busbarSectionOrBusIds";
    private static final String CONTROL_UNIT_ROOT_ELEMENT = "controlUnit";
    private static final String CONTROL_UNIT_ARRAY_ELEMENT = "controlUnits";

    public SecondaryVoltageControlSerDe() {
        super(SecondaryVoltageControl.NAME, "network", SecondaryVoltageControl.class,
                "secondaryVoltageControl_V1_0.xsd", "http://www.powsybl.org/schema/iidm/ext/secondary_voltage_control/1_0", "svc");
    }

    @Override
    public Map<String, String> getArrayNameToSingleNameMap() {
        return Map.of(CONTROL_ZONE_ARRAY_ELEMENT, CONTROL_ZONE_ROOT_ELEMENT,
                CONTROL_UNIT_ARRAY_ELEMENT, CONTROL_UNIT_ROOT_ELEMENT,
                BUSBAR_SECTION_OR_BUS_ID_ARRAY_ELEMENT, BUSBAR_SECTION_OR_BUS_ID_ROOT_ELEMENT);
    }

    @Override
    public void write(SecondaryVoltageControl control, SerializerContext context) {
        TreeDataWriter writer = context.getWriter();
        writer.writeStartNodes();
        for (ControlZone controlZone : control.getControlZones()) {
            writer.writeStartNode(getNamespaceUri(), CONTROL_ZONE_ROOT_ELEMENT);
            writer.writeStringAttribute("name", controlZone.getName());
            writePilotPoint(controlZone, writer);
            writeControlUnits(controlZone.getControlUnits(), writer);
            writer.writeEndNode();
        }
        writer.writeEndNodes();
    }

    private void writePilotPoint(ControlZone controlZone, TreeDataWriter writer) {
        writer.writeStartNode(getNamespaceUri(), PILOT_POINT_ELEMENT);
        writer.writeDoubleAttribute("targetV", controlZone.getPilotPoint().getTargetV());
        writer.writeStartNodes();
        for (String busbarSectionOrBusId : controlZone.getPilotPoint().getBusbarSectionsOrBusesIds()) {
            writer.writeStartNode(getNamespaceUri(), BUSBAR_SECTION_OR_BUS_ID_ROOT_ELEMENT);
            writer.writeNodeContent(busbarSectionOrBusId);
            writer.writeEndNode();
        }
        writer.writeEndNodes();
        writer.writeEndNode();
    }

    private void writeControlUnits(List<ControlUnit> controlUnits, TreeDataWriter writer) {
        writer.writeStartNodes();
        for (ControlUnit controlUnit : controlUnits) {
            writer.writeStartNode(getNamespaceUri(), CONTROL_UNIT_ROOT_ELEMENT);
            writer.writeBooleanAttribute("participate", controlUnit.isParticipate());
            writer.writeNodeContent(controlUnit.getId());
            writer.writeEndNode();
        }
        writer.writeEndNodes();
    }

    @Override
    public SecondaryVoltageControl read(Network network, DeserializerContext context) {
        SecondaryVoltageControlAdder adder = network.newExtension(SecondaryVoltageControlAdder.class);
        context.getReader().readChildNodes(elementName -> {
            if (!elementName.equals(CONTROL_ZONE_ROOT_ELEMENT)) {
                throw new PowsyblException(getExceptionMessageUnknownElement(elementName, "secondaryVoltageControl"));
            }
            readControlZone(context, adder);
        });
        return adder.add();
    }

    private static void readControlZone(DeserializerContext context, SecondaryVoltageControlAdder adder) {
        String name = context.getReader().readStringAttribute("name");
        ControlZoneAdder controlZoneAdder = adder.newControlZone()
                .withName(name);
        context.getReader().readChildNodes(elementName -> {
            switch (elementName) {
                case PILOT_POINT_ELEMENT -> readPilotPoint(context, controlZoneAdder);
                case CONTROL_UNIT_ROOT_ELEMENT -> readControlUnit(context, controlZoneAdder);
                default -> throw new PowsyblException(getExceptionMessageUnknownElement(elementName, "'controlZone'"));
            }
        });
        controlZoneAdder.add();
    }

    private static void readPilotPoint(DeserializerContext context, ControlZoneAdder controlZoneAdder) {
        double targetV = context.getReader().readDoubleAttribute("targetV");
        List<String> busbarSectionsOrBusesIds = new ArrayList<>();
        context.getReader().readChildNodes(elementName -> {
            if (!elementName.equals(BUSBAR_SECTION_OR_BUS_ID_ROOT_ELEMENT)) {
                throw new PowsyblException(getExceptionMessageUnknownElement(elementName, PILOT_POINT_ELEMENT));
            }
            busbarSectionsOrBusesIds.add(context.getReader().readContent());
        });
        controlZoneAdder.newPilotPoint()
                .withBusbarSectionsOrBusesIds(busbarSectionsOrBusesIds)
                .withTargetV(targetV)
                .add();
    }

    private static void readControlUnit(DeserializerContext context, ControlZoneAdder controlZoneAdder) {
        boolean participate = context.getReader().readBooleanAttribute("participate");
        String id = context.getReader().readContent();
        controlZoneAdder.newControlUnit()
                .withId(id)
                .withParticipate(participate)
                .add();
    }

    private static String getExceptionMessageUnknownElement(String elementName, String where) {
        return "Unknown element name '" + elementName + "' in '" + where + "'";
    }
}