RaoResultDeserializer.java

/*
 * Copyright (c) 2021, 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.openrao.data.raoresult.io.json.deserializers;

import com.powsybl.openrao.commons.OpenRaoException;
import com.powsybl.openrao.data.crac.api.Crac;
import com.powsybl.openrao.data.raoresult.api.RaoResult;
import com.powsybl.openrao.data.raoresult.impl.RaoResultImpl;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

import static com.powsybl.openrao.data.raoresult.io.json.RaoResultJsonConstants.*;
import static com.powsybl.openrao.data.raoresult.io.json.deserializers.Utils.*;

/**
 * @author Baptiste Seguinot {@literal <baptiste.seguinot at rte-france.com>}
 */
public class RaoResultDeserializer extends JsonDeserializer<RaoResult> {

    private static final Logger LOGGER = LoggerFactory.getLogger(RaoResultDeserializer.class);

    private Crac crac;

    private final boolean checkHeaderOnly;

    public RaoResultDeserializer(boolean checkHeaderOnly) {
        this.checkHeaderOnly = checkHeaderOnly;
    }

    public RaoResultDeserializer(Crac crac) {
        this.crac = crac;
        this.checkHeaderOnly = false;
    }

    @Override
    public RaoResult deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {

        RaoResultImpl raoResult = new RaoResultImpl(crac);

        String jsonFileVersion = isValid(jsonParser, raoResult);
        if (checkHeaderOnly) {
            return null;
        }

        while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
            switch (jsonParser.getCurrentName()) {
                case INFO:
                    //no need to import this
                    jsonParser.nextToken();
                    break;

                case COMPUTATION_STATUS:
                    raoResult.setComputationStatus(deserializeStatus(jsonParser.nextTextValue()));
                    break;

                case OPTIMIZATION_STEPS_EXECUTED:
                    checkDeprecatedField(jsonParser, RAO_RESULT_TYPE, jsonFileVersion, "1.6");
                    raoResult.setExecutionDetails(jsonParser.nextTextValue());
                    break;

                case EXECUTION_DETAILS:
                    raoResult.setExecutionDetails(jsonParser.nextTextValue());
                    break;

                case COMPUTATION_STATUS_MAP:
                    jsonParser.nextToken();
                    ComputationStatusMapDeserializer.deserialize(jsonParser, raoResult, crac);
                    break;

                case COST_RESULTS:
                    jsonParser.nextToken();
                    CostResultMapDeserializer.deserialize(jsonParser, raoResult, jsonFileVersion, crac);
                    break;

                case FLOWCNEC_RESULTS:
                    jsonParser.nextToken();
                    FlowCnecResultArrayDeserializer.deserialize(jsonParser, raoResult, crac, jsonFileVersion);
                    break;

                case ANGLECNEC_RESULTS:
                    jsonParser.nextToken();
                    AngleCnecResultArrayDeserializer.deserialize(jsonParser, raoResult, crac, jsonFileVersion);
                    break;

                case VOLTAGECNEC_RESULTS:
                    jsonParser.nextToken();
                    VoltageCnecResultArrayDeserializer.deserialize(jsonParser, raoResult, crac, jsonFileVersion);
                    break;

                case NETWORKACTION_RESULTS:
                    jsonParser.nextToken();
                    NetworkActionResultArrayDeserializer.deserialize(jsonParser, raoResult, crac);
                    break;

                case DeprecatedRaoResultJsonConstants.HVDCRANGEACTION_RESULTS:
                    checkDeprecatedField(jsonParser, RAO_RESULT_TYPE, jsonFileVersion, "1.1");
                    importRangeAction(jsonParser, raoResult, jsonFileVersion);
                    break;

                case STANDARDRANGEACTION_RESULTS, PSTRANGEACTION_RESULTS:
                    checkDeprecatedField(jsonParser, RAO_RESULT_TYPE, jsonFileVersion, "1.2");
                    importRangeAction(jsonParser, raoResult, jsonFileVersion);
                    break;

                case RANGEACTION_RESULTS:
                    importRangeAction(jsonParser, raoResult, jsonFileVersion);
                    break;

                default:
                    throw new OpenRaoException(String.format("Cannot deserialize RaoResult: unexpected field (%s)", jsonParser.getCurrentName()));
            }
        }
        return raoResult;
    }

    public static String isValid(JsonParser jsonParser, RaoResultImpl raoResult) throws IOException {
        String firstFieldName = jsonParser.nextFieldName();
        String jsonFileVersion;

        if (firstFieldName.equals(COMPUTATION_STATUS)) {
            /*
             it is assumed that the document version is 1.0
             at this time, there were not the headers with TYPE, VERSION and INFO of the document
             */
            jsonFileVersion = "1.0";
            raoResult.setComputationStatus(deserializeStatus(jsonParser.nextTextValue()));
        } else {
            if (!jsonParser.nextTextValue().equals(RAO_RESULT_TYPE)) {
                throw new OpenRaoException(String.format("type of document must be %s", RAO_RESULT_TYPE));
            }
            if (!jsonParser.nextFieldName().equals(VERSION)) {
                throw new OpenRaoException(String.format("%s must contain a version in its second field", RAO_RESULT_TYPE));
            }
            jsonFileVersion = jsonParser.nextTextValue();
        }

        checkVersion(jsonFileVersion);
        return jsonFileVersion;
    }

    private void importRangeAction(JsonParser jsonParser, RaoResultImpl raoResult, String jsonFileVersion) throws IOException {
        jsonParser.nextToken();
        RangeActionResultArrayDeserializer.deserialize(jsonParser, raoResult, crac, jsonFileVersion);
    }

    private static void checkVersion(String raoResultVersion) {

        if (getPrimaryVersionNumber(RAO_RESULT_IO_VERSION) > getPrimaryVersionNumber(raoResultVersion)) {
            throw new OpenRaoException(String.format("RAO-result importer %s is no longer compatible with json RAO-result version %s", RAO_RESULT_IO_VERSION, raoResultVersion));
        }
        if (getPrimaryVersionNumber(RAO_RESULT_IO_VERSION) < getPrimaryVersionNumber(raoResultVersion)) {
            throw new OpenRaoException(String.format("RAO-result importer %s cannot handle json RAO-result version %s, consider upgrading open-rao version", RAO_RESULT_IO_VERSION, raoResultVersion));
        }
        if (getSubVersionNumber(RAO_RESULT_IO_VERSION) < getSubVersionNumber(raoResultVersion)) {
            LOGGER.warn("RAO-result importer {} might not be compatible with json RAO-result version {}, consider upgrading open-rao version", RAO_RESULT_IO_VERSION, raoResultVersion);
        }

        // otherwise, all is good !
    }
}