AbstractCgmesModel.java
/**
* Copyright (c) 2017-2018, 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.cgmes.model;
import com.powsybl.commons.datasource.ReadOnlyDataSource;
import com.powsybl.commons.report.ReportNode;
import com.powsybl.triplestore.api.PropertyBag;
import com.powsybl.triplestore.api.PropertyBags;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
* @author Luma Zamarre��o {@literal <zamarrenolm at aia.es>}
*/
public abstract class AbstractCgmesModel implements CgmesModel {
protected AbstractCgmesModel() {
// FIXME(Luma) we must remove properties from here. They are not used!
this.properties = new Properties();
}
@Override
public Properties getProperties() {
return this.properties;
}
@Override
public Collection<CgmesTerminal> computedTerminals() {
if (cachedTerminals == null) {
cachedTerminals = computeTerminals();
}
return cachedTerminals.values();
}
@Override
public CgmesTerminal terminal(String terminalId) {
if (cachedTerminals == null) {
cachedTerminals = computeTerminals();
}
return cachedTerminals.get(terminalId);
}
@Override
public CgmesDcTerminal dcTerminal(String dcTerminalId) {
if (cachedDcTerminals == null) {
cachedDcTerminals = computeDcTerminals();
}
return cachedDcTerminals.get(dcTerminalId);
}
@Override
public String substation(CgmesTerminal t, boolean nodeBreaker) {
CgmesContainer c = container(t, nodeBreaker);
if (c == null) {
return null;
}
return c.substation();
}
@Override
public String voltageLevel(CgmesTerminal t, boolean nodeBreaker) {
CgmesContainer c = container(t, nodeBreaker);
if (c == null) {
return null;
}
return c.voltageLevel();
}
@Override
public CgmesContainer container(String containerId) {
if (cachedContainers == null) {
cachedContainers = computeContainers();
}
if (cachedContainers.get(containerId) == null) {
throw new CgmesModelException("Unexpected CgmesContainer for containerId: " + containerId);
}
return cachedContainers.get(containerId);
}
@Override
public double nominalVoltage(String baseVoltageId) {
if (cachedBaseVoltages == null) {
cachedBaseVoltages = new HashMap<>();
baseVoltages()
.forEach(bv -> cachedBaseVoltages.put(bv.getId("BaseVoltage"), bv.asDouble("nominalVoltage")));
}
return cachedBaseVoltages.getOrDefault(baseVoltageId, Double.NaN);
}
@Override
public Optional<String> node(CgmesTerminal t, boolean nodeBreaker) {
cacheNodes();
String nodeId = nodeBreaker && t.connectivityNode() != null ? t.connectivityNode() : t.topologicalNode();
return nodeId != null ? Optional.of(nodeId) : Optional.empty();
}
@Override
public Optional<CgmesContainer> nodeContainer(String nodeId) {
cacheNodes();
String containerId = null;
if (nodeId != null) {
PropertyBag node = cachedNodesById.get(nodeId);
if (node != null) {
containerId = node.getId(CgmesNames.CONNECTIVITY_NODE_CONTAINER);
} else {
if (LOG.isWarnEnabled()) {
LOG.warn("Missing node {}", nodeId);
}
}
}
return containerId == null ? Optional.empty() : Optional.of(container(containerId));
}
private CgmesContainer container(CgmesTerminal t, boolean nodeBreaker) {
cacheNodes();
String containerId = null;
String nodeId = nodeBreaker && t.connectivityNode() != null ? t.connectivityNode() : t.topologicalNode();
if (nodeId != null) {
PropertyBag node = cachedNodesById.get(nodeId);
if (node != null) {
containerId = node.getId(CgmesNames.CONNECTIVITY_NODE_CONTAINER);
} else {
if (LOG.isWarnEnabled()) {
LOG.warn("Missing node {} from terminal {}", nodeId, t.id());
}
}
}
return (containerId == null) ? null : container(containerId);
}
protected void cacheNodes() {
if (!cachedNodes) {
cachedConnectivityNodes = connectivityNodes();
cachedTopologicalNodes = topologicalNodes();
cachedNodesById = new HashMap<>();
cachedConnectivityNodes.forEach(cn -> cachedNodesById.put(cn.getId("ConnectivityNode"), cn));
cachedTopologicalNodes.forEach(tn -> cachedNodesById.put(tn.getId("TopologicalNode"), tn));
cachedNodes = true;
}
}
private Map<String, CgmesTerminal> computeTerminals() {
Map<String, CgmesTerminal> ts = new HashMap<>();
terminals().forEach(t -> {
CgmesTerminal td = new CgmesTerminal(t);
if (ts.containsKey(td.id())) {
return;
}
ts.put(td.id(), td);
});
return ts;
}
private Map<String, CgmesDcTerminal> computeDcTerminals() {
Map<String, CgmesDcTerminal> ts = new HashMap<>();
dcTerminals().forEach(t -> {
CgmesDcTerminal td = new CgmesDcTerminal(t);
if (ts.containsKey(td.id())) {
return;
}
ts.put(td.id(), td);
});
return ts;
}
// TODO(Luma): better caches create an object "Cache" that is final ...
// (avoid filling all places with if cached == null...)
private Map<String, CgmesContainer> computeContainers() {
Map<String, CgmesContainer> cs = new HashMap<>();
connectivityNodeContainers().forEach(c -> {
String id = c.getId(CgmesNames.CONNECTIVITY_NODE_CONTAINER);
String voltageLevel = c.getId("VoltageLevel");
String substation = c.getId(SUBSTATION);
String type = c.getId("connectivityNodeContainerType");
String line = type != null && type.contains("Line") ? id : null;
String name = c.get("name");
cs.put(id, new CgmesContainer(voltageLevel, substation, line, name));
});
return cs;
}
// read/write
@Override
public void setBasename(String baseName) {
this.baseName = Objects.requireNonNull(baseName);
}
@Override
public String getBasename() {
return baseName;
}
@Override
public void read(ReadOnlyDataSource mainDataSource, ReadOnlyDataSource alternativeDataSourceForBoundary, ReportNode reportNode) {
setBasename(CgmesModel.baseName(mainDataSource));
read(mainDataSource, reportNode);
if (!hasBoundary() && alternativeDataSourceForBoundary != null) {
read(alternativeDataSourceForBoundary, reportNode);
}
}
@Override
public void read(ReadOnlyDataSource ds, ReportNode reportNode) {
Objects.requireNonNull(reportNode);
invalidateCaches();
CgmesOnDataSource cds = new CgmesOnDataSource(ds);
for (String name : cds.names()) {
LOG.info("Reading [{}]", name);
CgmesModelReports.readFile(reportNode, name);
try (InputStream is = cds.dataSource().newInputStream(name)) {
read(is, baseName, name, reportNode);
} catch (IOException e) {
String msg = String.format("Reading [%s]", name);
LOG.warn(msg);
throw new CgmesModelException(msg, e);
}
}
}
protected void invalidateCaches() {
cachedTerminals = null;
cachedContainers = null;
cachedBaseVoltages = null;
cachedNodes = false;
cachedConnectivityNodes = null;
cachedTopologicalNodes = null;
cachedNodesById = null;
cachedDcTerminals = null;
}
private final Properties properties;
private String baseName;
// Caches
private Map<String, CgmesTerminal> cachedTerminals;
private Map<String, CgmesContainer> cachedContainers;
private Map<String, Double> cachedBaseVoltages;
protected boolean cachedNodes = false;
protected PropertyBags cachedConnectivityNodes;
protected PropertyBags cachedTopologicalNodes;
private Map<String, PropertyBag> cachedNodesById;
// equipmentId, sequenceNumber, terminalId
private Map<String, CgmesDcTerminal> cachedDcTerminals;
private static final Logger LOG = LoggerFactory.getLogger(AbstractCgmesModel.class);
private static final String SUBSTATION = "Substation";
}