Skip to content

Commit

Permalink
Merge pull request #314 from powsybl/dynamic_simulation_logs
Browse files Browse the repository at this point in the history
Dynamic simulation logs
  • Loading branch information
flo-dup authored Dec 6, 2023
2 parents b258e6c + 4ca79f5 commit cb2ab35
Show file tree
Hide file tree
Showing 26 changed files with 3,791 additions and 137 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
uses: actions/checkout@v1
with:
repository: powsybl/powsybl-core
ref: refs/heads/main
ref: refs/tags/v6.1.0

- name: Checkout sources
uses: actions/checkout@v1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* 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.dynawo.commons;

import com.powsybl.commons.PowsyblException;
import com.univocity.parsers.common.ParsingContext;
import com.univocity.parsers.common.ResultIterator;
import com.univocity.parsers.csv.CsvParser;
import com.univocity.parsers.csv.CsvParserSettings;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;

/**
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public abstract class AbstractCsvParser<T> {

protected static final char DEFAULT_SEPARATOR = '|';

private final char separator;

protected AbstractCsvParser(char separator) {
this.separator = separator;
}

public List<T> parse(Path file) {
if (!Files.exists(file)) {
return Collections.emptyList();
}

try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
return parse(reader);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private List<T> parse(BufferedReader reader) {
Objects.requireNonNull(reader);
CsvParserSettings settings = new CsvParserSettings();
settings.getFormat().setDelimiter(separator);
settings.getFormat().setQuoteEscape('"');
settings.getFormat().setLineSeparator(System.lineSeparator());
settings.setMaxColumns(getNbColumns());
CsvParser csvParser = new CsvParser(settings);
ResultIterator<String[], ParsingContext> iterator = csvParser.iterate(reader).iterator();
return read(iterator);
}

protected List<T> read(ResultIterator<String[], ParsingContext> iterator) {
List<T> logs = new ArrayList<>();
int iLine = 0;
while (iterator.hasNext()) {
iLine++;
String[] tokens = iterator.next();
if (!hasCorrectNbColumns(tokens.length)) {
throw new PowsyblException("Columns of line " + iLine + " are inconsistent");
}
createEntry(tokens).ifPresent(logs::add);
}
return logs;
}

protected abstract Optional<T> createEntry(String[] tokens);

protected abstract boolean hasCorrectNbColumns(int tokensSize);

protected abstract int getNbColumns();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* 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.dynawo.commons;

import com.powsybl.commons.reporter.Report;
import com.powsybl.commons.reporter.Reporter;
import com.powsybl.commons.reporter.TypedValue;
import com.powsybl.dynawo.commons.dynawologs.LogEntry;
import com.powsybl.dynawo.commons.timeline.TimelineEntry;

/**
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public final class CommonReports {

private static final String TIME_MS = "timeMsTypedValue";
private static final String ID = "idTypedValue";

private CommonReports() {
}

public static Reporter createDynawoLogReporter(Reporter reporter) {
return reporter.createSubReporter("dynawoLog",
"Dynawo Log");
}

public static void reportTimelineEvent(Reporter reporter, TimelineEntry timelineEntry) {
reporter.report(Report.builder()
.withKey("DynawoTimelineEvent")
.withDefaultMessage("[t=${time}] ${message} on equipment '${identifiableId}'")
.withTypedValue("time", timelineEntry.time(), TIME_MS)
.withTypedValue("identifiableId", timelineEntry.modelName(), ID)
.withValue("message", timelineEntry.message())
.withSeverity(TypedValue.TRACE_SEVERITY)
.build());
}

public static void reportLogEvent(Reporter reporter, LogEntry logEntry) {
reporter.report(Report.builder()
.withKey("DynawoTimelineEvent")
.withDefaultMessage("${message}")
.withValue("message", logEntry.message())
.withSeverity(logEntry.severity())
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ private DynawoConstants() {

public static final String DYNAWO_CMD_NAME = "dynawo";

public static final String DYNAWO_TIMELINE_FOLDER = "timeLine";

public static final DynawoVersion VERSION_MIN = new DynawoVersion(1, 5, 0);

public static final List<String> IIDM_EXTENSIONS = List.of(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* 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.dynawo.commons.dynawologs;

import com.powsybl.dynawo.commons.AbstractCsvParser;

import java.util.Optional;

/**
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public final class CsvLogParser extends AbstractCsvParser<LogEntry> {

private static final int NB_COLUMNS = 4;

public CsvLogParser() {
this(DEFAULT_SEPARATOR);
}

public CsvLogParser(char separator) {
super(separator);
}

@Override
protected Optional<LogEntry> createEntry(String[] tokens) {
return tokens.length == NB_COLUMNS ? LogUtils.createLog(tokens[1], tokens[2] + " " + tokens[3])
: LogUtils.createLog(tokens[1], tokens[2]);
}

@Override
protected boolean hasCorrectNbColumns(int tokensSize) {
return tokensSize == NB_COLUMNS - 1 || tokensSize == NB_COLUMNS;
}

@Override
protected int getNbColumns() {
return NB_COLUMNS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* 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.dynawo.commons.dynawologs;

import com.powsybl.commons.reporter.TypedValue;

/**
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public record LogEntry(TypedValue severity, String message) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* 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.dynawo.commons.dynawologs;

import com.powsybl.commons.reporter.TypedValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;

/**
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public final class LogUtils {

private static final Logger LOGGER = LoggerFactory.getLogger(LogUtils.class);
private static final String EMPTY_START = "======";
private static final String EMPTY_BREAKER = "------";

private LogUtils() {
}

public static Optional<LogEntry> createLog(String severity, String message) {
if (severity == null) {
LOGGER.warn("Inconsistent log entry (modelName: '{}', message: '{}')", severity, message);
} else {
if (emptyMessage(message)) {
LOGGER.debug("Empty message, the entry will be skipped : {}", message);
} else {
return convertDynawoLog(severity).map(severityTypedValue -> new LogEntry(severityTypedValue, message));
}
}
return Optional.empty();
}

private static Optional<TypedValue> convertDynawoLog(String severity) {
return switch (severity) {
case "DEBUG" -> Optional.of(TypedValue.DEBUG_SEVERITY);
case "INFO" -> Optional.of(TypedValue.INFO_SEVERITY);
case "WARN" -> Optional.of(TypedValue.WARN_SEVERITY);
case "ERROR" -> Optional.of(TypedValue.ERROR_SEVERITY);
default -> {
LOGGER.warn("Inconsistent severity entry '{}'", severity);
yield Optional.empty();
}
};
}

private static boolean emptyMessage(String message) {
return message == null || message.startsWith(EMPTY_BREAKER) || message.startsWith(EMPTY_START);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,80 +7,37 @@
*/
package com.powsybl.dynawo.commons.timeline;

import com.powsybl.commons.PowsyblException;
import com.univocity.parsers.common.ParsingContext;
import com.univocity.parsers.common.ResultIterator;
import com.univocity.parsers.csv.CsvParser;
import com.univocity.parsers.csv.CsvParserSettings;
import com.powsybl.dynawo.commons.AbstractCsvParser;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
* @author Laurent Issertial {@literal <laurent.issertial at rte-france.com>}
*/
public final class CsvTimeLineParser implements TimeLineParser {
public final class CsvTimeLineParser extends AbstractCsvParser<TimelineEntry> implements TimeLineParser {

private static final int NB_COLUMNS = 3;
private final char separator;

public CsvTimeLineParser() {
this('|');
this(DEFAULT_SEPARATOR);
}

public CsvTimeLineParser(char separator) {
this.separator = separator;
super(separator);
}

public List<TimelineEntry> parse(Path file) {
return parse(file, separator);
@Override
protected Optional<TimelineEntry> createEntry(String[] tokens) {
return TimeLineUtil.createEvent(tokens[0], tokens[1], tokens[2]);
}

public static List<TimelineEntry> parse(Path file, char separator) {
if (!Files.exists(file)) {
return Collections.emptyList();
}

try (BufferedReader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
return parse(reader, separator);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

static List<TimelineEntry> parse(BufferedReader reader, char separator) {
Objects.requireNonNull(reader);
CsvParserSettings settings = new CsvParserSettings();
settings.getFormat().setDelimiter(separator);
settings.getFormat().setQuoteEscape('"');
settings.getFormat().setLineSeparator(System.lineSeparator());
settings.setMaxColumns(NB_COLUMNS);
CsvParser csvParser = new CsvParser(settings);
ResultIterator<String[], ParsingContext> iterator = csvParser.iterate(reader).iterator();
return read(iterator);
@Override
protected boolean hasCorrectNbColumns(int tokensSize) {
return NB_COLUMNS == tokensSize;
}

static List<TimelineEntry> read(ResultIterator<String[], ParsingContext> iterator) {
List<TimelineEntry> timeline = new ArrayList<>();
int iLine = 0;
while (iterator.hasNext()) {
iLine++;
String[] tokens = iterator.next();
if (tokens.length != NB_COLUMNS) {
throw new PowsyblException("Columns of line " + iLine + " are inconsistent");
}
TimeLineUtil.createEvent(tokens[0], tokens[1], tokens[2])
.ifPresent(timeline::add);
}
return timeline;
@Override
protected int getNbColumns() {
return NB_COLUMNS;
}

}
Loading

0 comments on commit cb2ab35

Please sign in to comment.