SubstationData.java

/**
 * 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.psse.model.pf.io;

import com.powsybl.psse.model.PsseException;
import com.powsybl.psse.model.io.*;
import com.powsybl.psse.model.pf.PsseSubstation;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static com.powsybl.psse.model.pf.PsseSubstation.*;
import static com.powsybl.psse.model.pf.io.PowerFlowRecordGroup.*;

/**
 *
 * @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
 * @author Jos�� Antonio Marqu��s {@literal <marquesja at aia.es>}
 */
class SubstationData extends AbstractRecordGroup<PsseSubstation> {

    SubstationData() {
        super(SUBSTATION);
        withIO(FileFormat.LEGACY_TEXT, new IOLegacyText(this));
        withIO(FileFormat.JSON, new IOJson(this));
    }

    @Override
    protected Class<PsseSubstation> psseTypeClass() {
        return PsseSubstation.class;
    }

    private static class IOLegacyText extends RecordGroupIOLegacyText<PsseSubstation> {

        IOLegacyText(AbstractRecordGroup<PsseSubstation> recordGroup) {
            super(recordGroup);
        }

        @Override
        public List<PsseSubstation> read(LegacyTextReader reader, Context context) throws IOException {

            SubstationRecordData recordData = new SubstationRecordData();
            List<PsseSubstation> substationList = new ArrayList<>();

            if (!reader.isQRecordFound()) {
                String line = reader.readUntilFindingARecordLineNotEmpty();
                while (!reader.endOfBlock(line)) {
                    PsseSubstationRecord substationRecord = recordData.readFromStrings(Collections.singletonList(line), context).get(0);

                    List<PsseSubstationNode> nodeList = new SubstationNodeData().read(reader, context);
                    List<PsseSubstationSwitchingDevice> switchingDeviceList = new SubstationSwitchingDeviceData().read(reader, context);
                    List<PsseSubstationEquipmentTerminal> equipmentTerminalList = readEquipmentTerminalData(reader, context);

                    PsseSubstation substation = new PsseSubstation(substationRecord, nodeList, switchingDeviceList, equipmentTerminalList);
                    substationList.add(substation);

                    line = reader.readUntilFindingARecordLineNotEmpty();
                }
            }

            return substationList;
        }

        private List<PsseSubstationEquipmentTerminal> readEquipmentTerminalData(LegacyTextReader reader, Context context) throws IOException {
            SubstationEquipmentTerminalDataCommonStart commonStartData = new SubstationEquipmentTerminalDataCommonStart();
            List<String> equipmentTerminalOneBusRecords = new ArrayList<>();
            List<String> equipmentTerminalTwoBusesRecords = new ArrayList<>();
            List<String> equipmentTerminalThreeBusesRecords = new ArrayList<>();

            String line = reader.readUntilFindingARecordLineNotEmpty();
            while (!reader.endOfBlock(line)) {
                PsseSubstationEquipmentTerminalCommonStart commonStart = commonStartData.readFromStrings(Collections.singletonList(line), context).get(0);
                if (isOneBus(commonStart.getType())) {
                    equipmentTerminalOneBusRecords.add(line);
                } else if (isTwoBuses(commonStart.getType())) {
                    equipmentTerminalTwoBusesRecords.add(line);
                } else if (isThreeBuses(commonStart.getType())) {
                    equipmentTerminalThreeBusesRecords.add(line);
                } else {
                    throw new PsseException("Unexpected equipment terminal type: " + commonStart.getType());
                }
                line = reader.readUntilFindingARecordLineNotEmpty();
            }

            List<PsseSubstationEquipmentTerminal> equipmentTerminalList = new SubstationEquipmentTerminalDataOneBus().readFromStrings(equipmentTerminalOneBusRecords, context);
            equipmentTerminalList.addAll(new SubstationEquipmentTerminalDataTwoBuses().readFromStrings(equipmentTerminalTwoBusesRecords, context));
            equipmentTerminalList.addAll(new SubstationEquipmentTerminalDataThreeBuses().readFromStrings(equipmentTerminalThreeBusesRecords, context));

            return equipmentTerminalList;
        }

        @Override
        public void write(List<PsseSubstation> substationList, Context context, OutputStream outputStream) {

            writeBegin(outputStream);

            substationList.forEach(substation -> {

                SubstationRecordData recordData = new SubstationRecordData();
                write(recordData.buildRecords(Collections.singletonList(substation.getRecord()), context.getFieldNames(SUBSTATION), recordData.quotedFields(), context), outputStream);

                SubstationNodeData nodeData = new SubstationNodeData();
                write(nodeData.buildRecords(substation.getNodes(), context.getFieldNames(INTERNAL_SUBSTATION_NODE), nodeData.quotedFields(), context), outputStream);
                writeEndComment(" END OF SUBSTATION NODE DATA, BEGIN SUBSTATION SWITCHING DEVICE DATA", outputStream);

                SubstationSwitchingDeviceData switchingDeviceData = new SubstationSwitchingDeviceData();
                write(switchingDeviceData.buildRecords(substation.getSwitchingDevices(), context.getFieldNames(INTERNAL_SUBSTATION_SWITCHING_DEVICE), switchingDeviceData.quotedFields(), context), outputStream);
                writeEndComment(" END OF SUBSTATION SWITCHING DEVICE DATA, BEGIN SUBSTATION EQUIPMENT TERMINAL DATA", outputStream);

                write(writeEquipmentTerminalData(substation.getEquipmentTerminals(), context), outputStream);
                writeEndComment(" END OF SUBSTATION EQUIPMENT TERMINAL DATA", outputStream);
            });

            writeEnd(outputStream);
        }

        private List<String> writeEquipmentTerminalData(List<PsseSubstationEquipmentTerminal> equipmentTerminalList, Context context) {

            List<PsseSubstationEquipmentTerminal> eqOneBus = equipmentTerminalList.stream().filter(eq -> isOneBus(eq.getType())).toList();
            List<PsseSubstationEquipmentTerminal> eqTwoBuses = equipmentTerminalList.stream().filter(eq -> isTwoBuses(eq.getType())).toList();
            List<PsseSubstationEquipmentTerminal> eqThreeBuses = equipmentTerminalList.stream().filter(eq -> isThreeBuses(eq.getType())).toList();

            SubstationEquipmentTerminalDataOneBus oneBusData = new SubstationEquipmentTerminalDataOneBus();
            List<String> strings = oneBusData.buildRecords(eqOneBus, context.getFieldNames(INTERNAL_SUBSTATION_EQUIPMENT_TERMINAL_ONE_BUS), oneBusData.quotedFields(), context);

            SubstationEquipmentTerminalDataTwoBuses twoBusesData = new SubstationEquipmentTerminalDataTwoBuses();
            strings.addAll(twoBusesData.buildRecords(eqTwoBuses, context.getFieldNames(INTERNAL_SUBSTATION_EQUIPMENT_TERMINAL_TWO_BUSES), twoBusesData.quotedFields(), context));

            SubstationEquipmentTerminalDataThreeBuses threeBusesData = new SubstationEquipmentTerminalDataThreeBuses();
            strings.addAll(threeBusesData.buildRecords(eqThreeBuses, context.getFieldNames(INTERNAL_SUBSTATION_EQUIPMENT_TERMINAL_THREE_BUSES), threeBusesData.quotedFields(), context));

            return strings;
        }

        private static class SubstationNodeData extends AbstractRecordGroup<PsseSubstationNode> {
            SubstationNodeData() {
                super(INTERNAL_SUBSTATION_NODE, "ni", "name", "i", "status", "vm", "va");
                withQuotedFields(QUOTED_FIELDS);
            }

            @Override
            public Class<PsseSubstationNode> psseTypeClass() {
                return PsseSubstationNode.class;
            }
        }

        private static class SubstationSwitchingDeviceData extends AbstractRecordGroup<PsseSubstationSwitchingDevice> {
            SubstationSwitchingDeviceData() {
                super(INTERNAL_SUBSTATION_SWITCHING_DEVICE, "ni", "nj", "ckt", "name", "type", "status", "nstat", "x", "rate1", "rate2", "rate3");
                withQuotedFields(QUOTED_FIELDS_SWITCHING_DEVICES);
            }

            @Override
            public Class<PsseSubstationSwitchingDevice> psseTypeClass() {
                return PsseSubstationSwitchingDevice.class;
            }
        }

        private static class SubstationEquipmentTerminalDataCommonStart extends AbstractRecordGroup<PsseSubstationEquipmentTerminalCommonStart> {
            SubstationEquipmentTerminalDataCommonStart() {
                super(INTERNAL_SUBSTATION_EQUIPMENT_TERMINAL_COMMON_START, "i", "ni", "type");
                withQuotedFields(QUOTED_FIELDS);
            }

            @Override
            public Class<PsseSubstationEquipmentTerminalCommonStart> psseTypeClass() {
                return PsseSubstationEquipmentTerminalCommonStart.class;
            }
        }

        private static class SubstationEquipmentTerminalDataOneBus extends AbstractRecordGroup<PsseSubstationEquipmentTerminal> {
            SubstationEquipmentTerminalDataOneBus() {
                super(INTERNAL_SUBSTATION_EQUIPMENT_TERMINAL_ONE_BUS, "i", "ni", "type", "id");
                withQuotedFields(QUOTED_FIELDS);
            }

            @Override
            public Class<PsseSubstationEquipmentTerminal> psseTypeClass() {
                return PsseSubstationEquipmentTerminal.class;
            }
        }

        private static class SubstationEquipmentTerminalDataTwoBuses extends AbstractRecordGroup<PsseSubstationEquipmentTerminal> {
            SubstationEquipmentTerminalDataTwoBuses() {
                super(INTERNAL_SUBSTATION_EQUIPMENT_TERMINAL_TWO_BUSES, "i", "ni", "type", "j", "id");
                withQuotedFields(QUOTED_FIELDS);
            }

            @Override
            public Class<PsseSubstationEquipmentTerminal> psseTypeClass() {
                return PsseSubstationEquipmentTerminal.class;
            }
        }

        private static class SubstationEquipmentTerminalDataThreeBuses extends AbstractRecordGroup<PsseSubstationEquipmentTerminal> {
            SubstationEquipmentTerminalDataThreeBuses() {
                super(INTERNAL_SUBSTATION_EQUIPMENT_TERMINAL_THREE_BUSES, "i", "ni", "type", "j", "k", "id");
                withQuotedFields(QUOTED_FIELDS);
            }

            @Override
            public Class<PsseSubstationEquipmentTerminal> psseTypeClass() {
                return PsseSubstationEquipmentTerminal.class;
            }
        }
    }

    private static class IOJson extends RecordGroupIOJson<PsseSubstation> {
        IOJson(AbstractRecordGroup<PsseSubstation> recordGroup) {
            super(recordGroup);
        }

        @Override
        public List<PsseSubstation> read(LegacyTextReader reader, Context context) throws IOException {
            if (reader != null) {
                throw new PsseException("Unexpected reader. Should be null");
            }
            List<PsseSubstationRecord> recordList = new SubstationRecordData().read(null, context);
            List<PsseSubstationNodex> nodexList = new SubstationNodexData().read(null, context);
            List<PsseSubstationSwitchingDevicex> switchingDevicexList = new SubstationSwitchingDevicexData().read(null, context);
            List<PsseSubstationEquipmentTerminalx> equipmentTerminalxList = new SubstationEquipmentTerminalxData().read(null, context);
            return convertToSubstationList(recordList, nodexList, switchingDevicexList, equipmentTerminalxList);
        }

        @Override
        public void write(List<PsseSubstation> substationList, Context context, OutputStream outputStream) {
            if (outputStream != null) {
                throw new PsseException("Unexpected outputStream. Should be null");
            }
            List<PsseSubstationRecord> recordList = substationList.stream().map(PsseSubstation::getRecord).toList();
            new SubstationRecordData().write(recordList, context, null);

            List<PsseSubstationNodex> nodexList = new ArrayList<>();
            substationList.forEach(substation -> substation.getNodes().forEach(node -> nodexList.add(
                    new PsseSubstationNodex(substation.getRecord().getIs(), node))));
            new SubstationNodexData().write(nodexList, context, null);

            List<PsseSubstationSwitchingDevicex> switchingDevicexList = new ArrayList<>();
            substationList.forEach(substation -> substation.getSwitchingDevices().forEach(switchingDevice -> switchingDevicexList.add(
                    new PsseSubstationSwitchingDevicex(substation.getRecord().getIs(), switchingDevice))));
            new SubstationSwitchingDevicexData().write(switchingDevicexList, context, null);

            List<PsseSubstationEquipmentTerminalx> equipmentTerminalxList = new ArrayList<>();
            substationList.forEach(substation -> substation.getEquipmentTerminals().forEach(equipmentTerminal -> equipmentTerminalxList.add(
                    new PsseSubstationEquipmentTerminalx(substation.getRecord().getIs(), equipmentTerminal))));
            new SubstationEquipmentTerminalxData().write(equipmentTerminalxList, context, null);
        }

        private static List<PsseSubstation> convertToSubstationList(List<PsseSubstationRecord> recordList,
                                                                    List<PsseSubstationNodex> nodexList, List<PsseSubstationSwitchingDevicex> switchingDevicexList,
                                                                    List<PsseSubstationEquipmentTerminalx> equipmentTerminalxList) {

            List<PsseSubstation> substationList = new ArrayList<>();
            for (PsseSubstationRecord substationRecord : recordList) {
                List<PsseSubstationNode> nodeList = nodexList.stream().filter(n -> n.getIsub() == substationRecord.getIs()).map(PsseSubstationNodex::getNode).toList();
                List<PsseSubstationSwitchingDevice> switchingDeviceList = switchingDevicexList.stream().filter(sd -> sd.getIsub() == substationRecord.getIs()).map(PsseSubstationSwitchingDevicex::getSwitchingDevice).toList();
                List<PsseSubstationEquipmentTerminal> equipmentTerminalList = equipmentTerminalxList.stream().filter(eq -> eq.getIsub() == substationRecord.getIs()).map(PsseSubstationEquipmentTerminalx::getEquipmentTerminal).toList();

                substationList.add(new PsseSubstation(substationRecord, nodeList, switchingDeviceList, equipmentTerminalList));
            }
            return substationList;
        }

        private static class SubstationNodexData extends AbstractRecordGroup<PsseSubstationNodex> {
            SubstationNodexData() {
                super(INTERNAL_SUBSTATION_NODE);
                withQuotedFields(QUOTED_FIELDS);
            }

            @Override
            public Class<PsseSubstationNodex> psseTypeClass() {
                return PsseSubstationNodex.class;
            }
        }

        private static class SubstationSwitchingDevicexData extends AbstractRecordGroup<PsseSubstationSwitchingDevicex> {
            SubstationSwitchingDevicexData() {
                super(INTERNAL_SUBSTATION_SWITCHING_DEVICE);
                withQuotedFields(QUOTED_FIELDS_SWITCHING_DEVICES);
            }

            @Override
            public Class<PsseSubstationSwitchingDevicex> psseTypeClass() {
                return PsseSubstationSwitchingDevicex.class;
            }
        }

        private static class SubstationEquipmentTerminalxData extends AbstractRecordGroup<PsseSubstationEquipmentTerminalx> {
            SubstationEquipmentTerminalxData() {
                super(INTERNAL_SUBSTATION_EQUIPMENT_TERMINAL);
                withQuotedFields(QUOTED_FIELDS);
            }

            @Override
            public Class<PsseSubstationEquipmentTerminalx> psseTypeClass() {
                return PsseSubstationEquipmentTerminalx.class;
            }
        }
    }

    private static class SubstationRecordData extends AbstractRecordGroup<PsseSubstationRecord> {
        SubstationRecordData() {
            super(SUBSTATION, "is", "name", "lati", "long", "srg");
            withQuotedFields(QUOTED_FIELDS);
        }

        @Override
        public Class<PsseSubstationRecord> psseTypeClass() {
            return PsseSubstationRecord.class;
        }
    }

    private static final String[] QUOTED_FIELDS = {"name", "type", "id", "ckt", "eqid"};
    private static final String[] QUOTED_FIELDS_SWITCHING_DEVICES = {"name", "ckt"};
}