apply new code formatter and save action
This commit is contained in:
@@ -1,17 +1,18 @@
|
||||
package org.lucares.pdb.plot.api;
|
||||
|
||||
/**
|
||||
* Note: The order in this enum defines the order in which the aggregates are drawn.
|
||||
* Note: The order in this enum defines the order in which the aggregates are
|
||||
* drawn.
|
||||
*/
|
||||
public enum Aggregate {
|
||||
PARALLEL,
|
||||
|
||||
SCATTER,
|
||||
|
||||
/**
|
||||
* Empirical cumulative distribution functions
|
||||
*
|
||||
* @see https://serialmentor.com/dataviz/ecdf-qq.html
|
||||
*/
|
||||
CUM_DISTRIBUTION,
|
||||
PARALLEL,
|
||||
|
||||
SCATTER,
|
||||
|
||||
/**
|
||||
* Empirical cumulative distribution functions
|
||||
*
|
||||
* @see https://serialmentor.com/dataviz/ecdf-qq.html
|
||||
*/
|
||||
CUM_DISTRIBUTION,
|
||||
}
|
||||
|
||||
@@ -13,52 +13,53 @@ import org.lucares.recommind.logs.LineStyle;
|
||||
|
||||
public abstract class AggregateHandler implements Appender {
|
||||
|
||||
private GnuplotAxis xAxis = GnuplotAxis.X1;
|
||||
|
||||
private GnuplotAxis yAxis = GnuplotAxis.Y1;
|
||||
|
||||
public GnuplotAxis getxAxis() {
|
||||
return xAxis;
|
||||
}
|
||||
private GnuplotAxis xAxis = GnuplotAxis.X1;
|
||||
|
||||
public void updateAxis(GnuplotAxis axis) {
|
||||
switch (axis) {
|
||||
case X1:
|
||||
case X2:
|
||||
this.xAxis = axis;
|
||||
break;
|
||||
case Y1:
|
||||
case Y2:
|
||||
this.yAxis = axis;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + axis);
|
||||
private GnuplotAxis yAxis = GnuplotAxis.Y1;
|
||||
|
||||
public GnuplotAxis getxAxis() {
|
||||
return xAxis;
|
||||
}
|
||||
}
|
||||
|
||||
public GnuplotAxis getyAxis() {
|
||||
return yAxis;
|
||||
}
|
||||
public void updateAxis(final GnuplotAxis axis) {
|
||||
switch (axis) {
|
||||
case X1:
|
||||
case X2:
|
||||
this.xAxis = axis;
|
||||
break;
|
||||
case Y1:
|
||||
case Y2:
|
||||
this.yAxis = axis;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + axis);
|
||||
}
|
||||
}
|
||||
|
||||
protected String gnuplotXYAxis() {
|
||||
return xAxis.getAxisNameForPlots()+yAxis.getAxisNameForPlots();
|
||||
}
|
||||
|
||||
abstract Type getAxisType(GnuplotAxis axis);
|
||||
public GnuplotAxis getyAxis() {
|
||||
return yAxis;
|
||||
}
|
||||
|
||||
abstract Aggregate getAggregateType();
|
||||
protected String gnuplotXYAxis() {
|
||||
return xAxis.getAxisNameForPlots() + yAxis.getAxisNameForPlots();
|
||||
}
|
||||
|
||||
abstract AxisSettings createXAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries);
|
||||
abstract Type getAxisType(GnuplotAxis axis);
|
||||
|
||||
abstract AxisSettings createYAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries);
|
||||
abstract Aggregate getAggregateType();
|
||||
|
||||
abstract void addPlot(StringBuilder result, AggregatedData aggregatedData, LineStyle lineStyle, Optional<String> title);
|
||||
abstract AxisSettings createXAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries);
|
||||
|
||||
abstract CustomAggregator createCustomAggregator(Path tmpDir, PlotSettings plotSettings, long fromEpochMilli,
|
||||
long toEpochMilli);
|
||||
abstract AxisSettings createYAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries);
|
||||
|
||||
protected String gnuplotTitle(Optional<String> title) {
|
||||
|
||||
return title.isPresent() ? "title '" + title.get() + "'" : "notitle";
|
||||
}
|
||||
abstract void addPlot(StringBuilder result, AggregatedData aggregatedData, LineStyle lineStyle,
|
||||
Optional<String> title);
|
||||
|
||||
abstract CustomAggregator createCustomAggregator(Path tmpDir, PlotSettings plotSettings, long fromEpochMilli,
|
||||
long toEpochMilli);
|
||||
|
||||
protected String gnuplotTitle(final Optional<String> title) {
|
||||
|
||||
return title.isPresent() ? "title '" + title.get() + "'" : "notitle";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,91 +17,97 @@ import org.lucares.utils.CollectionUtils;
|
||||
import org.lucares.utils.Preconditions;
|
||||
|
||||
public class AggregateHandlerCollection {
|
||||
private static final Comparator<AggregateHandler> PLOTTING_ORDER = Comparator.comparing(AggregateHandler::getAggregateType);
|
||||
|
||||
private final List<AggregateHandler> aggregateHandlers = new ArrayList<>();
|
||||
private static final Comparator<AggregateHandler> PLOTTING_ORDER = Comparator
|
||||
.comparing(AggregateHandler::getAggregateType);
|
||||
|
||||
public void add(AggregateHandler aggregateHandler) {
|
||||
aggregateHandlers.add(aggregateHandler);
|
||||
}
|
||||
|
||||
public void updateAxisForHandlers() {
|
||||
updateAxisForHandlers(GnuplotAxis.X1);
|
||||
updateAxisForHandlers(GnuplotAxis.Y1);
|
||||
}
|
||||
private final List<AggregateHandler> aggregateHandlers = new ArrayList<>();
|
||||
|
||||
private void updateAxisForHandlers(GnuplotAxis axis) {
|
||||
final EnumSet<Type> result = EnumSet.noneOf(Type.class);
|
||||
for (AggregateHandler handler : aggregateHandlers) {
|
||||
final Type type = handler.getAxisType(axis);
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result.add(type);
|
||||
}else {
|
||||
final boolean containsType = result.contains(type);
|
||||
if (containsType) {
|
||||
// already has an axis of this type
|
||||
// TODO merge axis definitions and use the greater values for: range, ticsIncrement
|
||||
} else{
|
||||
Preconditions.checkSmaller(result.size(), 2, "at most two different axis are supported");
|
||||
final GnuplotAxis mirrorAxis = axis.mirrorAxis();
|
||||
handler.updateAxis(mirrorAxis);
|
||||
result.add(type);
|
||||
public void add(final AggregateHandler aggregateHandler) {
|
||||
aggregateHandlers.add(aggregateHandler);
|
||||
}
|
||||
|
||||
public void updateAxisForHandlers() {
|
||||
updateAxisForHandlers(GnuplotAxis.X1);
|
||||
updateAxisForHandlers(GnuplotAxis.Y1);
|
||||
}
|
||||
|
||||
private void updateAxisForHandlers(final GnuplotAxis axis) {
|
||||
final EnumSet<Type> result = EnumSet.noneOf(Type.class);
|
||||
for (final AggregateHandler handler : aggregateHandlers) {
|
||||
final Type type = handler.getAxisType(axis);
|
||||
|
||||
if (result.isEmpty()) {
|
||||
result.add(type);
|
||||
} else {
|
||||
final boolean containsType = result.contains(type);
|
||||
if (containsType) {
|
||||
// already has an axis of this type
|
||||
// TODO merge axis definitions and use the greater values for: range,
|
||||
// ticsIncrement
|
||||
} else {
|
||||
Preconditions.checkSmaller(result.size(), 2, "at most two different axis are supported");
|
||||
final GnuplotAxis mirrorAxis = axis.mirrorAxis();
|
||||
handler.updateAxis(mirrorAxis);
|
||||
result.add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<AxisSettings> getXAxisDefinitions(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final List<AxisSettings> result = new ArrayList<>();
|
||||
for (AggregateHandler handler : aggregateHandlers) {
|
||||
AxisSettings axis = handler.createXAxisSettings(settings, dataSeries);
|
||||
result.add(axis);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<AxisSettings> getYAxisDefinitions(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
List<AxisSettings> result = new ArrayList<>();
|
||||
for (AggregateHandler handler : aggregateHandlers) {
|
||||
final AxisSettings axis = handler.createYAxisSettings(settings, dataSeries);
|
||||
result.add(axis);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public AggregatorCollection createCustomAggregator(Path tmpDir, PlotSettings plotSettings, long fromEpochMilli,
|
||||
long toEpochMilli) {
|
||||
|
||||
final List<CustomAggregator> aggregators = new ArrayList<>();
|
||||
|
||||
for (AggregateHandler handler : aggregateHandlers) {
|
||||
final CustomAggregator aggregator = handler.createCustomAggregator(tmpDir, plotSettings, fromEpochMilli, toEpochMilli);
|
||||
if (aggregator != null) {
|
||||
aggregators.add(aggregator);
|
||||
}
|
||||
}
|
||||
|
||||
return new AggregatorCollection(aggregators);
|
||||
}
|
||||
|
||||
public void addPlots(StringBuilder result, Collection<DataSeries> dataSeries) {
|
||||
|
||||
boolean first = true;
|
||||
final List<AggregateHandler> handlersInPlottingOrder = CollectionUtils.copySort(aggregateHandlers, PLOTTING_ORDER);
|
||||
for (AggregateHandler handler : handlersInPlottingOrder) {
|
||||
|
||||
for (DataSeries dataSerie : dataSeries) {
|
||||
final Optional<String> title = first ? Optional.of(dataSerie.getTitle()) : Optional.empty();
|
||||
|
||||
Optional<AggregatedData> aggregatedData = dataSerie.getAggregatedData().get(handler.getAggregateType());
|
||||
if(aggregatedData.isPresent()) {
|
||||
handler.addPlot(result, aggregatedData.get(), dataSerie.getStyle(), title);
|
||||
public List<AxisSettings> getXAxisDefinitions(final GnuplotSettings settings,
|
||||
final Collection<DataSeries> dataSeries) {
|
||||
final List<AxisSettings> result = new ArrayList<>();
|
||||
for (final AggregateHandler handler : aggregateHandlers) {
|
||||
final AxisSettings axis = handler.createXAxisSettings(settings, dataSeries);
|
||||
result.add(axis);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<AxisSettings> getYAxisDefinitions(final GnuplotSettings settings,
|
||||
final Collection<DataSeries> dataSeries) {
|
||||
final List<AxisSettings> result = new ArrayList<>();
|
||||
for (final AggregateHandler handler : aggregateHandlers) {
|
||||
final AxisSettings axis = handler.createYAxisSettings(settings, dataSeries);
|
||||
result.add(axis);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public AggregatorCollection createCustomAggregator(final Path tmpDir, final PlotSettings plotSettings,
|
||||
final long fromEpochMilli, final long toEpochMilli) {
|
||||
|
||||
final List<CustomAggregator> aggregators = new ArrayList<>();
|
||||
|
||||
for (final AggregateHandler handler : aggregateHandlers) {
|
||||
final CustomAggregator aggregator = handler.createCustomAggregator(tmpDir, plotSettings, fromEpochMilli,
|
||||
toEpochMilli);
|
||||
if (aggregator != null) {
|
||||
aggregators.add(aggregator);
|
||||
}
|
||||
}
|
||||
|
||||
return new AggregatorCollection(aggregators);
|
||||
}
|
||||
|
||||
public void addPlots(final StringBuilder result, final Collection<DataSeries> dataSeries) {
|
||||
|
||||
boolean first = true;
|
||||
final List<AggregateHandler> handlersInPlottingOrder = CollectionUtils.copySort(aggregateHandlers,
|
||||
PLOTTING_ORDER);
|
||||
for (final AggregateHandler handler : handlersInPlottingOrder) {
|
||||
|
||||
for (final DataSeries dataSerie : dataSeries) {
|
||||
final Optional<String> title = first ? Optional.of(dataSerie.getTitle()) : Optional.empty();
|
||||
|
||||
final Optional<AggregatedData> aggregatedData = dataSerie.getAggregatedData()
|
||||
.get(handler.getAggregateType());
|
||||
if (aggregatedData.isPresent()) {
|
||||
handler.addPlot(result, aggregatedData.get(), dataSerie.getStyle(), title);
|
||||
}
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,19 @@ package org.lucares.pdb.plot.api;
|
||||
import java.io.File;
|
||||
|
||||
public class AggregatedData {
|
||||
private final String label;
|
||||
private final File dataFile;
|
||||
private final String label;
|
||||
private final File dataFile;
|
||||
|
||||
public AggregatedData(final String label, final File dataFile) {
|
||||
this.label = label;
|
||||
this.dataFile = dataFile;
|
||||
}
|
||||
public AggregatedData(final String label, final File dataFile) {
|
||||
this.label = label;
|
||||
this.dataFile = dataFile;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public File getDataFile() {
|
||||
return dataFile;
|
||||
}
|
||||
public File getDataFile() {
|
||||
return dataFile;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,19 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
public class AggregatedDataCollection implements Iterable<AggregatedData>{
|
||||
private final LinkedHashMap<Aggregate, AggregatedData> aggregatedDatas = new LinkedHashMap<>();
|
||||
public class AggregatedDataCollection implements Iterable<AggregatedData> {
|
||||
private final LinkedHashMap<Aggregate, AggregatedData> aggregatedDatas = new LinkedHashMap<>();
|
||||
|
||||
public void put(Aggregate aggregate, AggregatedData aggregatedData) {
|
||||
aggregatedDatas.put(aggregate, aggregatedData);
|
||||
}
|
||||
public void put(final Aggregate aggregate, final AggregatedData aggregatedData) {
|
||||
aggregatedDatas.put(aggregate, aggregatedData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AggregatedData> iterator() {
|
||||
return aggregatedDatas.values().iterator();
|
||||
}
|
||||
@Override
|
||||
public Iterator<AggregatedData> iterator() {
|
||||
return aggregatedDatas.values().iterator();
|
||||
}
|
||||
|
||||
public Optional<AggregatedData> get(Aggregate aggregateType) {
|
||||
return Optional.ofNullable(aggregatedDatas.get(aggregateType));
|
||||
}
|
||||
public Optional<AggregatedData> get(final Aggregate aggregateType) {
|
||||
return Optional.ofNullable(aggregatedDatas.get(aggregateType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
package org.lucares.pdb.plot.api;
|
||||
|
||||
public class AggregatedDataEntry {
|
||||
private final double epochSeconds;
|
||||
private final long value;
|
||||
public AggregatedDataEntry(double epochSeconds, long value) {
|
||||
super();
|
||||
this.epochSeconds = epochSeconds;
|
||||
this.value = value;
|
||||
}
|
||||
public double getEpochSeconds() {
|
||||
return epochSeconds;
|
||||
}
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
private final double epochSeconds;
|
||||
private final long value;
|
||||
|
||||
public AggregatedDataEntry(final double epochSeconds, final long value) {
|
||||
super();
|
||||
this.epochSeconds = epochSeconds;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public double getEpochSeconds() {
|
||||
return epochSeconds;
|
||||
}
|
||||
|
||||
public long getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,26 +4,26 @@ import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class AggregatorCollection {
|
||||
private final List<CustomAggregator> aggregators;
|
||||
private final List<CustomAggregator> aggregators;
|
||||
|
||||
public AggregatorCollection(List<CustomAggregator> aggregators) {
|
||||
this.aggregators = aggregators;
|
||||
}
|
||||
|
||||
public void addValue(boolean valueIsInYRange, long epochMilli, long value) {
|
||||
for (CustomAggregator aggregator : aggregators) {
|
||||
aggregator.addValue(valueIsInYRange, epochMilli, value);
|
||||
public AggregatorCollection(final List<CustomAggregator> aggregators) {
|
||||
this.aggregators = aggregators;
|
||||
}
|
||||
}
|
||||
|
||||
public AggregatedDataCollection getAggregatedData() throws IOException {
|
||||
|
||||
AggregatedDataCollection result = new AggregatedDataCollection();
|
||||
|
||||
for (CustomAggregator aggregator : aggregators) {
|
||||
result.put(aggregator.getType(), aggregator.getAggregatedData());
|
||||
public void addValue(final boolean valueIsInYRange, final long epochMilli, final long value) {
|
||||
for (final CustomAggregator aggregator : aggregators) {
|
||||
aggregator.addValue(valueIsInYRange, epochMilli, value);
|
||||
}
|
||||
}
|
||||
|
||||
public AggregatedDataCollection getAggregatedData() throws IOException {
|
||||
|
||||
final AggregatedDataCollection result = new AggregatedDataCollection();
|
||||
|
||||
for (final CustomAggregator aggregator : aggregators) {
|
||||
result.put(aggregator.getType(), aggregator.getAggregatedData());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,15 @@ package org.lucares.pdb.plot.api;
|
||||
import java.util.Locale;
|
||||
|
||||
public interface Appender {
|
||||
default void appendln(final StringBuilder builder, final String string) {
|
||||
builder.append(string + "\n");
|
||||
}
|
||||
default void appendln(final StringBuilder builder, final String string) {
|
||||
builder.append(string + "\n");
|
||||
}
|
||||
|
||||
default void appendfln(final StringBuilder builder, final String format, final Object... args) {
|
||||
builder.append(String.format(Locale.US,format + "\n", args));
|
||||
}
|
||||
default void appendfln(final StringBuilder builder, final String format, final Object... args) {
|
||||
builder.append(String.format(Locale.US, format + "\n", args));
|
||||
}
|
||||
|
||||
default void appendf(final StringBuilder builder, final String format, final Object... args) {
|
||||
builder.append(String.format(Locale.US,format, args));
|
||||
}
|
||||
default void appendf(final StringBuilder builder, final String format, final Object... args) {
|
||||
builder.append(String.format(Locale.US, format, args));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package org.lucares.pdb.plot.api;
|
||||
|
||||
public enum AxisScale {
|
||||
LINEAR, LOG10
|
||||
LINEAR, LOG10
|
||||
}
|
||||
|
||||
@@ -15,114 +15,114 @@ import org.lucares.collections.LongLongHashMap;
|
||||
|
||||
public class CumulativeDistributionCustomAggregator implements CustomAggregator {
|
||||
|
||||
private final static int POINTS = 500;
|
||||
private final static int POINTS = 500;
|
||||
|
||||
private static final class ToPercentiles implements LongLongConsumer {
|
||||
private static final class ToPercentiles implements LongLongConsumer {
|
||||
|
||||
private long cumulativeCount = 0;
|
||||
private long cumulativeCount = 0;
|
||||
|
||||
private long maxValue = 0;
|
||||
private long maxValue = 0;
|
||||
|
||||
private final LinkedHashMap<Double, Long> percentiles = new LinkedHashMap<>(POINTS);
|
||||
private final LinkedHashMap<Double, Long> percentiles = new LinkedHashMap<>(POINTS);
|
||||
|
||||
private final double stepSize;
|
||||
private final double stepSize;
|
||||
|
||||
private double lastPercentile;
|
||||
private double nextPercentile;
|
||||
private double lastPercentile;
|
||||
private double nextPercentile;
|
||||
|
||||
private final long totalValues;
|
||||
private final long totalValues;
|
||||
|
||||
public ToPercentiles(final long totalValues) {
|
||||
this.totalValues = totalValues;
|
||||
stepSize = 100.0 / POINTS;
|
||||
nextPercentile = stepSize;
|
||||
}
|
||||
public ToPercentiles(final long totalValues) {
|
||||
this.totalValues = totalValues;
|
||||
stepSize = 100.0 / POINTS;
|
||||
nextPercentile = stepSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(final long duration, final long count) {
|
||||
maxValue = duration;
|
||||
@Override
|
||||
public void accept(final long duration, final long count) {
|
||||
maxValue = duration;
|
||||
|
||||
cumulativeCount += count;
|
||||
final double newPercentile = cumulativeCount * 100.0 / totalValues;
|
||||
cumulativeCount += count;
|
||||
final double newPercentile = cumulativeCount * 100.0 / totalValues;
|
||||
|
||||
if (newPercentile >= nextPercentile) {
|
||||
double currentPercentile = lastPercentile + stepSize;
|
||||
while (currentPercentile <= newPercentile) {
|
||||
percentiles.put(currentPercentile, duration);
|
||||
currentPercentile += stepSize;
|
||||
}
|
||||
nextPercentile = currentPercentile;
|
||||
lastPercentile = currentPercentile - stepSize;
|
||||
}
|
||||
}
|
||||
if (newPercentile >= nextPercentile) {
|
||||
double currentPercentile = lastPercentile + stepSize;
|
||||
while (currentPercentile <= newPercentile) {
|
||||
percentiles.put(currentPercentile, duration);
|
||||
currentPercentile += stepSize;
|
||||
}
|
||||
nextPercentile = currentPercentile;
|
||||
lastPercentile = currentPercentile - stepSize;
|
||||
}
|
||||
}
|
||||
|
||||
public long getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
public long getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public LinkedHashMap<Double, Long> getPercentiles() {
|
||||
return percentiles;
|
||||
}
|
||||
public LinkedHashMap<Double, Long> getPercentiles() {
|
||||
return percentiles;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// the rather large initial capacity should prevent too many grow&re-hash phases
|
||||
private final LongLongHashMap map = new LongLongHashMap(5_000, 0.75);
|
||||
// the rather large initial capacity should prevent too many grow&re-hash phases
|
||||
private final LongLongHashMap map = new LongLongHashMap(5_000, 0.75);
|
||||
|
||||
private long totalValues = 0;
|
||||
private long totalValues = 0;
|
||||
|
||||
private final Path tmpDir;
|
||||
private final Path tmpDir;
|
||||
|
||||
public CumulativeDistributionCustomAggregator(final Path tmpDir) {
|
||||
this.tmpDir = tmpDir;
|
||||
}
|
||||
public CumulativeDistributionCustomAggregator(final Path tmpDir) {
|
||||
this.tmpDir = tmpDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addValue(boolean valueIsInYRange, final long epochMilli, final long value) {
|
||||
map.compute(value, 0, l -> l + 1);
|
||||
totalValues++;
|
||||
}
|
||||
@Override
|
||||
public void addValue(boolean valueIsInYRange, final long epochMilli, final long value) {
|
||||
map.compute(value, 0, l -> l + 1);
|
||||
totalValues++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregatedData getAggregatedData() throws IOException {
|
||||
final char separator = ',';
|
||||
final char newline = '\n';
|
||||
@Override
|
||||
public AggregatedData getAggregatedData() throws IOException {
|
||||
final char separator = ',';
|
||||
final char newline = '\n';
|
||||
|
||||
final ToPercentiles toPercentiles = new ToPercentiles(totalValues);
|
||||
map.forEachOrdered(toPercentiles);
|
||||
final ToPercentiles toPercentiles = new ToPercentiles(totalValues);
|
||||
map.forEachOrdered(toPercentiles);
|
||||
|
||||
final File dataFile = File.createTempFile("data", ".dat", tmpDir.toFile());
|
||||
try (final Writer output = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(dataFile), StandardCharsets.US_ASCII));) {
|
||||
final File dataFile = File.createTempFile("data", ".dat", tmpDir.toFile());
|
||||
try (final Writer output = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(dataFile), StandardCharsets.US_ASCII));) {
|
||||
|
||||
final StringBuilder data = new StringBuilder();
|
||||
if (map.size() > 0) {
|
||||
// compute the percentiles
|
||||
toPercentiles.getPercentiles().forEach((percentile, value) -> {
|
||||
final StringBuilder data = new StringBuilder();
|
||||
if (map.size() > 0) {
|
||||
// compute the percentiles
|
||||
toPercentiles.getPercentiles().forEach((percentile, value) -> {
|
||||
|
||||
data.append(percentile);
|
||||
data.append(separator);
|
||||
data.append(value);
|
||||
data.append(newline);
|
||||
});
|
||||
data.append(percentile);
|
||||
data.append(separator);
|
||||
data.append(value);
|
||||
data.append(newline);
|
||||
});
|
||||
|
||||
final long maxValue = toPercentiles.getMaxValue();
|
||||
data.append(100);
|
||||
data.append(separator);
|
||||
data.append(maxValue);
|
||||
data.append(newline);
|
||||
}
|
||||
output.write(data.toString());
|
||||
final long maxValue = toPercentiles.getMaxValue();
|
||||
data.append(100);
|
||||
data.append(separator);
|
||||
data.append(maxValue);
|
||||
data.append(newline);
|
||||
}
|
||||
output.write(data.toString());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
final String title = String.format("cumulative distribution");
|
||||
return new AggregatedData(title, dataFile);
|
||||
}
|
||||
final String title = String.format("cumulative distribution");
|
||||
return new AggregatedData(title, dataFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getType() {
|
||||
return Aggregate.CUM_DISTRIBUTION;
|
||||
}
|
||||
@Override
|
||||
public Aggregate getType() {
|
||||
return Aggregate.CUM_DISTRIBUTION;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,74 +14,74 @@ import org.lucares.recommind.logs.AxisSettings.Type;
|
||||
|
||||
public class CumulativeDistributionHandler extends AggregateHandler {
|
||||
|
||||
@Override
|
||||
public CustomAggregator createCustomAggregator(final Path tmpDir, PlotSettings plotSettings,
|
||||
final long fromEpochMilli, final long toEpochMilli) {
|
||||
return new CumulativeDistributionCustomAggregator(tmpDir);
|
||||
}
|
||||
|
||||
public CumulativeDistributionHandler() {
|
||||
}
|
||||
|
||||
@Override
|
||||
Type getAxisType(GnuplotAxis axis) {
|
||||
switch (axis) {
|
||||
case X1:
|
||||
case X2:
|
||||
return Type.Percent;
|
||||
case Y1:
|
||||
case Y2:
|
||||
return Type.Duration;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + axis);
|
||||
@Override
|
||||
public CustomAggregator createCustomAggregator(final Path tmpDir, PlotSettings plotSettings,
|
||||
final long fromEpochMilli, final long toEpochMilli) {
|
||||
return new CumulativeDistributionCustomAggregator(tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisSettings createYAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
AxisSettings result = AxisTime.createYAxis(settings, dataSeries);
|
||||
result.setAxis(getyAxis());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisSettings createXAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
AxisSettings result = new AxisSettings();
|
||||
result.setLabel("Cumulative Distribution");
|
||||
result.setType(Type.Percent);
|
||||
result.setAxis(getxAxis());
|
||||
result.setFormat("%.0f%%");
|
||||
result.setTicIncrement(computeTicIncrement(settings));
|
||||
result.setFrom("0");
|
||||
result.setTo("100");
|
||||
return result;
|
||||
}
|
||||
|
||||
private int computeTicIncrement(GnuplotSettings settings) {
|
||||
int widthByFontSize = settings.getWidth() / GnuplotSettings.TICKS_FONT_SIZE;
|
||||
if (widthByFontSize < 50) {
|
||||
return 20;
|
||||
} else if (widthByFontSize < 75) {
|
||||
return 10;
|
||||
} else {
|
||||
return 5;
|
||||
public CumulativeDistributionHandler() {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlot(StringBuilder result, AggregatedData aggregatedData, LineStyle lineStyle,
|
||||
Optional<String> title) {
|
||||
appendfln(result, "'%s' using 1:2 %s with lines axes %s lw 2 %s, \\", //
|
||||
aggregatedData.getDataFile().getAbsolutePath(), //
|
||||
gnuplotTitle(title), //
|
||||
gnuplotXYAxis(), //
|
||||
lineStyle.darker()//
|
||||
);
|
||||
}
|
||||
@Override
|
||||
Type getAxisType(GnuplotAxis axis) {
|
||||
switch (axis) {
|
||||
case X1:
|
||||
case X2:
|
||||
return Type.Percent;
|
||||
case Y1:
|
||||
case Y2:
|
||||
return Type.Duration;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + axis);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getAggregateType() {
|
||||
return Aggregate.CUM_DISTRIBUTION;
|
||||
}
|
||||
@Override
|
||||
public AxisSettings createYAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
AxisSettings result = AxisTime.createYAxis(settings, dataSeries);
|
||||
result.setAxis(getyAxis());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisSettings createXAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
AxisSettings result = new AxisSettings();
|
||||
result.setLabel("Cumulative Distribution");
|
||||
result.setType(Type.Percent);
|
||||
result.setAxis(getxAxis());
|
||||
result.setFormat("%.0f%%");
|
||||
result.setTicIncrement(computeTicIncrement(settings));
|
||||
result.setFrom("0");
|
||||
result.setTo("100");
|
||||
return result;
|
||||
}
|
||||
|
||||
private int computeTicIncrement(GnuplotSettings settings) {
|
||||
int widthByFontSize = settings.getWidth() / GnuplotSettings.TICKS_FONT_SIZE;
|
||||
if (widthByFontSize < 50) {
|
||||
return 20;
|
||||
} else if (widthByFontSize < 75) {
|
||||
return 10;
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlot(StringBuilder result, AggregatedData aggregatedData, LineStyle lineStyle,
|
||||
Optional<String> title) {
|
||||
appendfln(result, "'%s' using 1:2 %s with lines axes %s lw 2 %s, \\", //
|
||||
aggregatedData.getDataFile().getAbsolutePath(), //
|
||||
gnuplotTitle(title), //
|
||||
gnuplotXYAxis(), //
|
||||
lineStyle.darker()//
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getAggregateType() {
|
||||
return Aggregate.CUM_DISTRIBUTION;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ import java.io.IOException;
|
||||
|
||||
public interface CustomAggregator {
|
||||
|
||||
void addValue(boolean valueIsInYRange, long epochMilli, long value);
|
||||
void addValue(boolean valueIsInYRange, long epochMilli, long value);
|
||||
|
||||
AggregatedData getAggregatedData() throws IOException;
|
||||
AggregatedData getAggregatedData() throws IOException;
|
||||
|
||||
Aggregate getType();
|
||||
Aggregate getType();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package org.lucares.pdb.plot.api;
|
||||
|
||||
public enum Limit {
|
||||
NO_LIMIT, MOST_VALUES, FEWEST_VALUES, MAX_VALUE, MIN_VALUE
|
||||
NO_LIMIT, MOST_VALUES, FEWEST_VALUES, MAX_VALUE, MIN_VALUE
|
||||
}
|
||||
|
||||
@@ -15,61 +15,61 @@ import org.lucares.recommind.logs.DataSeries;
|
||||
|
||||
public class ParallelRequestsAggregate extends AggregateHandler {
|
||||
|
||||
@Override
|
||||
Type getAxisType(GnuplotAxis axis) {
|
||||
switch (axis) {
|
||||
case X1:
|
||||
case X2:
|
||||
return Type.Time;
|
||||
case Y1:
|
||||
case Y2:
|
||||
return Type.Number;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + axis);
|
||||
@Override
|
||||
Type getAxisType(GnuplotAxis axis) {
|
||||
switch (axis) {
|
||||
case X1:
|
||||
case X2:
|
||||
return Type.Time;
|
||||
case Y1:
|
||||
case Y2:
|
||||
return Type.Number;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + axis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisSettings createYAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final AxisSettings result = new AxisSettings();
|
||||
result.setLabel("Parallel Requests");
|
||||
result.setType(Type.Number);
|
||||
result.setAxis(getyAxis());
|
||||
result.setTicsEnabled(true);
|
||||
result.setFrom("0");
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisSettings createXAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final AxisSettings result = AxisTime.createXAxis(settings);
|
||||
result.setAxis(getxAxis());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlot(StringBuilder result, AggregatedData aggregatedData, LineStyle lineStyle,
|
||||
Optional<String> title) {
|
||||
appendfln(result, "'%s' using 1:2 %s with filledcurve axes %s lw 1 %s, \\", //
|
||||
aggregatedData.getDataFile().getAbsolutePath(), //
|
||||
gnuplotTitle(title), //
|
||||
gnuplotXYAxis(), //
|
||||
lineStyle.brighter().asGnuplotLineStyle()//
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAggregator createCustomAggregator(final Path tmpDir, PlotSettings plotSettings,
|
||||
final long fromEpochMilli, final long toEpochMilli) {
|
||||
if ((toEpochMilli - fromEpochMilli) <= TimeUnit.HOURS.toMillis(50)) {
|
||||
return new ParallelRequestsAggregator(tmpDir, fromEpochMilli, toEpochMilli);
|
||||
} else {
|
||||
return null;
|
||||
@Override
|
||||
public AxisSettings createYAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final AxisSettings result = new AxisSettings();
|
||||
result.setLabel("Parallel Requests");
|
||||
result.setType(Type.Number);
|
||||
result.setAxis(getyAxis());
|
||||
result.setTicsEnabled(true);
|
||||
result.setFrom("0");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getAggregateType() {
|
||||
return Aggregate.PARALLEL;
|
||||
}
|
||||
@Override
|
||||
public AxisSettings createXAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final AxisSettings result = AxisTime.createXAxis(settings);
|
||||
result.setAxis(getxAxis());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlot(StringBuilder result, AggregatedData aggregatedData, LineStyle lineStyle,
|
||||
Optional<String> title) {
|
||||
appendfln(result, "'%s' using 1:2 %s with filledcurve axes %s lw 1 %s, \\", //
|
||||
aggregatedData.getDataFile().getAbsolutePath(), //
|
||||
gnuplotTitle(title), //
|
||||
gnuplotXYAxis(), //
|
||||
lineStyle.brighter().asGnuplotLineStyle()//
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomAggregator createCustomAggregator(final Path tmpDir, PlotSettings plotSettings,
|
||||
final long fromEpochMilli, final long toEpochMilli) {
|
||||
if ((toEpochMilli - fromEpochMilli) <= TimeUnit.HOURS.toMillis(50)) {
|
||||
return new ParallelRequestsAggregator(tmpDir, fromEpochMilli, toEpochMilli);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getAggregateType() {
|
||||
return Aggregate.PARALLEL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,88 +15,88 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ParallelRequestsAggregator implements CustomAggregator {
|
||||
|
||||
private static final char NEWLINE = '\n';
|
||||
private static final char NEWLINE = '\n';
|
||||
|
||||
private static final char SEPARATOR = ',';
|
||||
private static final char SEPARATOR = ',';
|
||||
|
||||
private static final Logger METRICS_LOGGER = LoggerFactory
|
||||
.getLogger("org.lucares.metrics.aggregator.parallelRequests");
|
||||
private static final Logger METRICS_LOGGER = LoggerFactory
|
||||
.getLogger("org.lucares.metrics.aggregator.parallelRequests");
|
||||
|
||||
private final Path tmpDir;
|
||||
private final Path tmpDir;
|
||||
|
||||
private final short[] increments;
|
||||
private final short[] increments;
|
||||
|
||||
private final long fromEpochMilli;
|
||||
private final long fromEpochMilli;
|
||||
|
||||
private final long toEpochMilli;
|
||||
private final long toEpochMilli;
|
||||
|
||||
public ParallelRequestsAggregator(final Path tmpDir, final long fromEpochMilli, final long toEpochMilli) {
|
||||
this.tmpDir = tmpDir;
|
||||
this.fromEpochMilli = fromEpochMilli;
|
||||
this.toEpochMilli = toEpochMilli;
|
||||
public ParallelRequestsAggregator(final Path tmpDir, final long fromEpochMilli, final long toEpochMilli) {
|
||||
this.tmpDir = tmpDir;
|
||||
this.fromEpochMilli = fromEpochMilli;
|
||||
this.toEpochMilli = toEpochMilli;
|
||||
|
||||
final int milliseconds = (int) (toEpochMilli - fromEpochMilli);
|
||||
increments = new short[milliseconds+1];
|
||||
}
|
||||
final int milliseconds = (int) (toEpochMilli - fromEpochMilli);
|
||||
increments = new short[milliseconds + 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addValue(boolean valueIsInYRange,final long epochMilli, final long value) {
|
||||
@Override
|
||||
public void addValue(boolean valueIsInYRange, final long epochMilli, final long value) {
|
||||
|
||||
final int endPos = (int) (epochMilli - fromEpochMilli);
|
||||
increments[endPos]--;
|
||||
final int endPos = (int) (epochMilli - fromEpochMilli);
|
||||
increments[endPos]--;
|
||||
|
||||
final int startPos = Math.max(0, (int) (endPos - value));
|
||||
increments[startPos]++;
|
||||
final int startPos = Math.max(0, (int) (endPos - value));
|
||||
increments[startPos]++;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregatedData getAggregatedData() throws IOException {
|
||||
@Override
|
||||
public AggregatedData getAggregatedData() throws IOException {
|
||||
|
||||
final long start = System.nanoTime();
|
||||
final long start = System.nanoTime();
|
||||
|
||||
final File dataFile = File.createTempFile("data", ".dat", tmpDir.toFile());
|
||||
try (final Writer output = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(dataFile), StandardCharsets.US_ASCII));) {
|
||||
final File dataFile = File.createTempFile("data", ".dat", tmpDir.toFile());
|
||||
try (final Writer output = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(dataFile), StandardCharsets.US_ASCII));) {
|
||||
|
||||
final StringBuilder data = new StringBuilder();
|
||||
final StringBuilder data = new StringBuilder();
|
||||
|
||||
// first and last value should be 0, or gnuplot will draw a diagonal line
|
||||
appendTimeAndValue(data, fromEpochMilli, 0);
|
||||
// first and last value should be 0, or gnuplot will draw a diagonal line
|
||||
appendTimeAndValue(data, fromEpochMilli, 0);
|
||||
|
||||
int value = 0;
|
||||
for (int i = 0; i < increments.length - 1; i++) {
|
||||
final int increment = increments[i];
|
||||
final int nextIncrement = increments[i + 1];
|
||||
if (increment != 0 || nextIncrement != 0) {
|
||||
value += increment;
|
||||
appendTimeAndValue(data, fromEpochMilli + i, value);
|
||||
}
|
||||
}
|
||||
int value = 0;
|
||||
for (int i = 0; i < increments.length - 1; i++) {
|
||||
final int increment = increments[i];
|
||||
final int nextIncrement = increments[i + 1];
|
||||
if (increment != 0 || nextIncrement != 0) {
|
||||
value += increment;
|
||||
appendTimeAndValue(data, fromEpochMilli + i, value);
|
||||
}
|
||||
}
|
||||
|
||||
// first and last value should be 0, or gnuplot will draw a diagonal line
|
||||
appendTimeAndValue(data, toEpochMilli, 0);
|
||||
// first and last value should be 0, or gnuplot will draw a diagonal line
|
||||
appendTimeAndValue(data, toEpochMilli, 0);
|
||||
|
||||
output.write(data.toString());
|
||||
output.write(data.toString());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
final String title = String.format("parallelRequests");
|
||||
METRICS_LOGGER.debug("wrote parallelRequests csv in: {}ms file={}", (System.nanoTime() - start) / 1_000_000.0,
|
||||
dataFile);
|
||||
return new AggregatedData(title, dataFile);
|
||||
}
|
||||
final String title = String.format("parallelRequests");
|
||||
METRICS_LOGGER.debug("wrote parallelRequests csv in: {}ms file={}", (System.nanoTime() - start) / 1_000_000.0,
|
||||
dataFile);
|
||||
return new AggregatedData(title, dataFile);
|
||||
}
|
||||
|
||||
private void appendTimeAndValue(final StringBuilder builder, final long timeEpochMilli, final int value) {
|
||||
builder.append(String.format(Locale.US, "%.3f", timeEpochMilli / 1000.0));
|
||||
builder.append(SEPARATOR);
|
||||
builder.append(value);
|
||||
builder.append(NEWLINE);
|
||||
}
|
||||
private void appendTimeAndValue(final StringBuilder builder, final long timeEpochMilli, final int value) {
|
||||
builder.append(String.format(Locale.US, "%.3f", timeEpochMilli / 1000.0));
|
||||
builder.append(SEPARATOR);
|
||||
builder.append(value);
|
||||
builder.append(NEWLINE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getType() {
|
||||
return Aggregate.PARALLEL;
|
||||
}
|
||||
@Override
|
||||
public Aggregate getType() {
|
||||
return Aggregate.PARALLEL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,186 +12,186 @@ import org.lucares.utils.Preconditions;
|
||||
|
||||
public class PlotSettings {
|
||||
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private String query;
|
||||
private String query;
|
||||
|
||||
private int height;
|
||||
private int height;
|
||||
|
||||
private int width;
|
||||
private int width;
|
||||
|
||||
private int thumbnailMaxWidth = 0;
|
||||
private int thumbnailMaxWidth = 0;
|
||||
|
||||
private int thumbnailMaxHeight = 0;
|
||||
private int thumbnailMaxHeight = 0;
|
||||
|
||||
private List<String> groupBy;
|
||||
private List<String> groupBy;
|
||||
|
||||
private Limit limitBy;
|
||||
private Limit limitBy;
|
||||
|
||||
private int limit;
|
||||
private int limit;
|
||||
|
||||
private String dateRangeAsString;
|
||||
private String dateRangeAsString;
|
||||
|
||||
private AxisScale yAxisScale;
|
||||
private AxisScale yAxisScale;
|
||||
|
||||
private AggregateHandlerCollection aggregates;
|
||||
private AggregateHandlerCollection aggregates;
|
||||
|
||||
private int yRangeMin;
|
||||
private int yRangeMax;
|
||||
private TimeRangeUnitInternal yRangeUnit = TimeRangeUnitInternal.AUTOMATIC;
|
||||
private int yRangeMin;
|
||||
private int yRangeMax;
|
||||
private TimeRangeUnitInternal yRangeUnit = TimeRangeUnitInternal.AUTOMATIC;
|
||||
|
||||
private boolean keyOutside;
|
||||
private boolean keyOutside;
|
||||
|
||||
private boolean generateThumbnail;
|
||||
private boolean generateThumbnail;
|
||||
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
public String getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
public void setQuery(final String query) {
|
||||
this.query = query;
|
||||
}
|
||||
public void setQuery(final String query) {
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(final int height) {
|
||||
this.height = height;
|
||||
}
|
||||
public void setHeight(final int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(final int width) {
|
||||
this.width = width;
|
||||
}
|
||||
public void setWidth(final int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public int getThumbnailMaxWidth() {
|
||||
return thumbnailMaxWidth;
|
||||
}
|
||||
public int getThumbnailMaxWidth() {
|
||||
return thumbnailMaxWidth;
|
||||
}
|
||||
|
||||
public void setThumbnailMaxWidth(final int thumbnailMaxWidth) {
|
||||
this.thumbnailMaxWidth = thumbnailMaxWidth;
|
||||
}
|
||||
public void setThumbnailMaxWidth(final int thumbnailMaxWidth) {
|
||||
this.thumbnailMaxWidth = thumbnailMaxWidth;
|
||||
}
|
||||
|
||||
public int getThumbnailMaxHeight() {
|
||||
return thumbnailMaxHeight;
|
||||
}
|
||||
public int getThumbnailMaxHeight() {
|
||||
return thumbnailMaxHeight;
|
||||
}
|
||||
|
||||
public void setThumbnailMaxHeight(final int thumbnailMaxHeight) {
|
||||
this.thumbnailMaxHeight = thumbnailMaxHeight;
|
||||
}
|
||||
public void setThumbnailMaxHeight(final int thumbnailMaxHeight) {
|
||||
this.thumbnailMaxHeight = thumbnailMaxHeight;
|
||||
}
|
||||
|
||||
public List<String> getGroupBy() {
|
||||
return groupBy;
|
||||
}
|
||||
public List<String> getGroupBy() {
|
||||
return groupBy;
|
||||
}
|
||||
|
||||
public void setGroupBy(final List<String> groupBy) {
|
||||
this.groupBy = groupBy;
|
||||
}
|
||||
public void setGroupBy(final List<String> groupBy) {
|
||||
this.groupBy = groupBy;
|
||||
}
|
||||
|
||||
public Limit getLimitBy() {
|
||||
return limitBy;
|
||||
}
|
||||
public Limit getLimitBy() {
|
||||
return limitBy;
|
||||
}
|
||||
|
||||
public void setLimitBy(final Limit limitBy) {
|
||||
this.limitBy = limitBy;
|
||||
}
|
||||
public void setLimitBy(final Limit limitBy) {
|
||||
this.limitBy = limitBy;
|
||||
}
|
||||
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
public int getLimit() {
|
||||
return limit;
|
||||
}
|
||||
|
||||
public void setLimit(final int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
public void setLimit(final int limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
|
||||
public String getDateRange() {
|
||||
return dateRangeAsString;
|
||||
}
|
||||
public String getDateRange() {
|
||||
return dateRangeAsString;
|
||||
}
|
||||
|
||||
public void setDateRange(final String dateRangeAsString) {
|
||||
this.dateRangeAsString = dateRangeAsString;
|
||||
}
|
||||
public void setDateRange(final String dateRangeAsString) {
|
||||
this.dateRangeAsString = dateRangeAsString;
|
||||
}
|
||||
|
||||
public DateTimeRange dateRange() {
|
||||
public DateTimeRange dateRange() {
|
||||
|
||||
final String[] startEnd = dateRangeAsString.split(Pattern.quote(" - "));
|
||||
Preconditions.checkEqual(startEnd.length, 2, "invalid date range: ''{0}''", dateRangeAsString);
|
||||
final String[] startEnd = dateRangeAsString.split(Pattern.quote(" - "));
|
||||
Preconditions.checkEqual(startEnd.length, 2, "invalid date range: ''{0}''", dateRangeAsString);
|
||||
|
||||
final OffsetDateTime startDate = LocalDateTime.parse(startEnd[0], DATE_FORMAT).atOffset(ZoneOffset.UTC);
|
||||
final OffsetDateTime endDate = LocalDateTime.parse(startEnd[1], DATE_FORMAT).atOffset(ZoneOffset.UTC);
|
||||
final OffsetDateTime startDate = LocalDateTime.parse(startEnd[0], DATE_FORMAT).atOffset(ZoneOffset.UTC);
|
||||
final OffsetDateTime endDate = LocalDateTime.parse(startEnd[1], DATE_FORMAT).atOffset(ZoneOffset.UTC);
|
||||
|
||||
return new DateTimeRange(startDate, endDate);
|
||||
return new DateTimeRange(startDate, endDate);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void setYAxisScale(final AxisScale axisScale) {
|
||||
this.yAxisScale = axisScale;
|
||||
}
|
||||
public void setYAxisScale(final AxisScale axisScale) {
|
||||
this.yAxisScale = axisScale;
|
||||
}
|
||||
|
||||
public AxisScale getYAxisScale() {
|
||||
return yAxisScale;
|
||||
}
|
||||
public AxisScale getYAxisScale() {
|
||||
return yAxisScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlotSettings [query=" + query + ", height=" + height + ", width=" + width + ", thumbnailMaxWidth="
|
||||
+ thumbnailMaxWidth + ", thumbnailMaxHeight=" + thumbnailMaxHeight + ", groupBy=" + groupBy
|
||||
+ ", limitBy=" + limitBy + ", limit=" + limit + ", dateRangeAsString=" + dateRangeAsString
|
||||
+ ", yAxisScale=" + yAxisScale + ", aggregates=" + aggregates + ", yRangeMin=" + yRangeMin
|
||||
+ ", yRangeMax=" + yRangeMax + ", yRangeUnit=" + yRangeUnit + ", keyOutside=" + keyOutside
|
||||
+ ", generateThumbnail=" + generateThumbnail + "]";
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlotSettings [query=" + query + ", height=" + height + ", width=" + width + ", thumbnailMaxWidth="
|
||||
+ thumbnailMaxWidth + ", thumbnailMaxHeight=" + thumbnailMaxHeight + ", groupBy=" + groupBy
|
||||
+ ", limitBy=" + limitBy + ", limit=" + limit + ", dateRangeAsString=" + dateRangeAsString
|
||||
+ ", yAxisScale=" + yAxisScale + ", aggregates=" + aggregates + ", yRangeMin=" + yRangeMin
|
||||
+ ", yRangeMax=" + yRangeMax + ", yRangeUnit=" + yRangeUnit + ", keyOutside=" + keyOutside
|
||||
+ ", generateThumbnail=" + generateThumbnail + "]";
|
||||
}
|
||||
|
||||
public void setAggregates(final AggregateHandlerCollection aggregates) {
|
||||
this.aggregates = aggregates;
|
||||
}
|
||||
public void setAggregates(final AggregateHandlerCollection aggregates) {
|
||||
this.aggregates = aggregates;
|
||||
}
|
||||
|
||||
public AggregateHandlerCollection getAggregates() {
|
||||
return aggregates;
|
||||
}
|
||||
public AggregateHandlerCollection getAggregates() {
|
||||
return aggregates;
|
||||
}
|
||||
|
||||
public void setKeyOutside(final boolean keyOutside) {
|
||||
this.keyOutside = keyOutside;
|
||||
}
|
||||
public void setKeyOutside(final boolean keyOutside) {
|
||||
this.keyOutside = keyOutside;
|
||||
}
|
||||
|
||||
public boolean isKeyOutside() {
|
||||
return keyOutside;
|
||||
}
|
||||
public boolean isKeyOutside() {
|
||||
return keyOutside;
|
||||
}
|
||||
|
||||
public void setGenerateThumbnail(final boolean generateThumbnail) {
|
||||
this.generateThumbnail = generateThumbnail;
|
||||
}
|
||||
public void setGenerateThumbnail(final boolean generateThumbnail) {
|
||||
this.generateThumbnail = generateThumbnail;
|
||||
}
|
||||
|
||||
public boolean isGenerateThumbnail() {
|
||||
return generateThumbnail;
|
||||
}
|
||||
public boolean isGenerateThumbnail() {
|
||||
return generateThumbnail;
|
||||
}
|
||||
|
||||
public int getYRangeMin() {
|
||||
return yRangeMin;
|
||||
}
|
||||
public int getYRangeMin() {
|
||||
return yRangeMin;
|
||||
}
|
||||
|
||||
public void setYRangeMin(final int yRangeMin) {
|
||||
this.yRangeMin = yRangeMin;
|
||||
}
|
||||
public void setYRangeMin(final int yRangeMin) {
|
||||
this.yRangeMin = yRangeMin;
|
||||
}
|
||||
|
||||
public int getYRangeMax() {
|
||||
return yRangeMax;
|
||||
}
|
||||
public int getYRangeMax() {
|
||||
return yRangeMax;
|
||||
}
|
||||
|
||||
public void setYRangeMax(final int yRangeMax) {
|
||||
this.yRangeMax = yRangeMax;
|
||||
}
|
||||
public void setYRangeMax(final int yRangeMax) {
|
||||
this.yRangeMax = yRangeMax;
|
||||
}
|
||||
|
||||
public TimeRangeUnitInternal getYRangeUnit() {
|
||||
return yRangeUnit;
|
||||
}
|
||||
public TimeRangeUnitInternal getYRangeUnit() {
|
||||
return yRangeUnit;
|
||||
}
|
||||
|
||||
public void setYRangeUnit(final TimeRangeUnitInternal yRangeUnit) {
|
||||
this.yRangeUnit = yRangeUnit;
|
||||
}
|
||||
public void setYRangeUnit(final TimeRangeUnitInternal yRangeUnit) {
|
||||
this.yRangeUnit = yRangeUnit;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,56 +15,56 @@ import org.lucares.recommind.logs.AxisSettings.Type;
|
||||
|
||||
public class ScatterAggregateHandler extends AggregateHandler {
|
||||
|
||||
@Override
|
||||
Type getAxisType(GnuplotAxis axis) {
|
||||
switch (axis) {
|
||||
case X1:
|
||||
case X2:
|
||||
return Type.Time;
|
||||
case Y1:
|
||||
case Y2:
|
||||
return Type.Duration;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + axis);
|
||||
@Override
|
||||
Type getAxisType(GnuplotAxis axis) {
|
||||
switch (axis) {
|
||||
case X1:
|
||||
case X2:
|
||||
return Type.Time;
|
||||
case Y1:
|
||||
case Y2:
|
||||
return Type.Duration;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + axis);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisSettings createYAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final AxisSettings result = AxisTime.createYAxis(settings, dataSeries);
|
||||
result.setAxis(getyAxis());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AxisSettings createXAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final AxisSettings result = AxisTime.createXAxis(settings);
|
||||
result.setAxis(getxAxis());
|
||||
return result;
|
||||
}
|
||||
@Override
|
||||
public AxisSettings createYAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final AxisSettings result = AxisTime.createYAxis(settings, dataSeries);
|
||||
result.setAxis(getyAxis());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPlot(StringBuilder result, AggregatedData aggregatedData, LineStyle lineStyle,
|
||||
Optional<String> title) {
|
||||
@Override
|
||||
public AxisSettings createXAxisSettings(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
final AxisSettings result = AxisTime.createXAxis(settings);
|
||||
result.setAxis(getxAxis());
|
||||
return result;
|
||||
}
|
||||
|
||||
appendfln(result, "'%s' using 1:2 %s with %s axes %s %s, \\", //
|
||||
aggregatedData.getDataFile(), //
|
||||
gnuplotTitle(title), //
|
||||
GnuplotLineType.Points, //
|
||||
gnuplotXYAxis(),//
|
||||
lineStyle//
|
||||
);
|
||||
}
|
||||
@Override
|
||||
public void addPlot(StringBuilder result, AggregatedData aggregatedData, LineStyle lineStyle,
|
||||
Optional<String> title) {
|
||||
|
||||
@Override
|
||||
public CustomAggregator createCustomAggregator(Path tmpDir, PlotSettings plotSettings, long fromEpochMilli,
|
||||
long toEpochMilli) {
|
||||
appendfln(result, "'%s' using 1:2 %s with %s axes %s %s, \\", //
|
||||
aggregatedData.getDataFile(), //
|
||||
gnuplotTitle(title), //
|
||||
GnuplotLineType.Points, //
|
||||
gnuplotXYAxis(), //
|
||||
lineStyle//
|
||||
);
|
||||
}
|
||||
|
||||
return new ScatterAggregator(tmpDir, plotSettings, fromEpochMilli, toEpochMilli);
|
||||
}
|
||||
@Override
|
||||
public CustomAggregator createCustomAggregator(Path tmpDir, PlotSettings plotSettings, long fromEpochMilli,
|
||||
long toEpochMilli) {
|
||||
|
||||
@Override
|
||||
public Aggregate getAggregateType() {
|
||||
return Aggregate.SCATTER;
|
||||
}
|
||||
return new ScatterAggregator(tmpDir, plotSettings, fromEpochMilli, toEpochMilli);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getAggregateType() {
|
||||
return Aggregate.SCATTER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,83 +18,82 @@ import org.lucares.recommind.logs.LongUtils;
|
||||
|
||||
public class ScatterAggregator implements CustomAggregator {
|
||||
|
||||
private final Sparse2DLongArray matrix2d = new Sparse2DLongArray();
|
||||
private final Sparse2DLongArray matrix2d = new Sparse2DLongArray();
|
||||
|
||||
private final boolean useMillis;
|
||||
private final long plotAreaWidthInPx;
|
||||
private final long plotAreaHeightInPx;
|
||||
private final long epochMillisPerPixel;
|
||||
private final boolean useMillis;
|
||||
private final long plotAreaWidthInPx;
|
||||
private final long plotAreaHeightInPx;
|
||||
private final long epochMillisPerPixel;
|
||||
|
||||
private final long minValue;
|
||||
private final long maxValue;
|
||||
private final long durationMillisPerPixel;
|
||||
private final long minValue;
|
||||
private final long maxValue;
|
||||
private final long durationMillisPerPixel;
|
||||
|
||||
private Path tmpDir;
|
||||
private Path tmpDir;
|
||||
|
||||
public ScatterAggregator(Path tmpDir, PlotSettings plotSettings, long fromEpochMilli, long toEpochMilli) {
|
||||
public ScatterAggregator(Path tmpDir, PlotSettings plotSettings, long fromEpochMilli, long toEpochMilli) {
|
||||
|
||||
this.tmpDir = tmpDir;
|
||||
useMillis = (toEpochMilli - fromEpochMilli) < TimeUnit.MINUTES.toMillis(5);
|
||||
plotAreaWidthInPx = plotSettings.getWidth() - GnuplotSettings.GNUPLOT_LEFT_RIGHT_MARGIN;
|
||||
plotAreaHeightInPx = plotSettings.getHeight() - GnuplotSettings.GNUPLOT_TOP_BOTTOM_MARGIN;
|
||||
epochMillisPerPixel = Math.max(1, (toEpochMilli - fromEpochMilli) / plotAreaWidthInPx);
|
||||
this.tmpDir = tmpDir;
|
||||
useMillis = (toEpochMilli - fromEpochMilli) < TimeUnit.MINUTES.toMillis(5);
|
||||
plotAreaWidthInPx = plotSettings.getWidth() - GnuplotSettings.GNUPLOT_LEFT_RIGHT_MARGIN;
|
||||
plotAreaHeightInPx = plotSettings.getHeight() - GnuplotSettings.GNUPLOT_TOP_BOTTOM_MARGIN;
|
||||
epochMillisPerPixel = Math.max(1, (toEpochMilli - fromEpochMilli) / plotAreaWidthInPx);
|
||||
|
||||
minValue = plotSettings.getYRangeUnit() == TimeRangeUnitInternal.AUTOMATIC ? 0
|
||||
: plotSettings.getYRangeUnit().toMilliSeconds(plotSettings.getYRangeMin());
|
||||
maxValue = plotSettings.getYRangeUnit() == TimeRangeUnitInternal.AUTOMATIC ? Long.MAX_VALUE
|
||||
: plotSettings.getYRangeUnit().toMilliSeconds(plotSettings.getYRangeMax());
|
||||
durationMillisPerPixel = plotSettings.getYAxisScale() == AxisScale.LINEAR
|
||||
? Math.max(1, (maxValue - minValue) / plotAreaHeightInPx)
|
||||
: 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addValue(boolean valueIsInYRange, long epochMilli, long value) {
|
||||
final long roundedEpochMilli = epochMilli - epochMilli % epochMillisPerPixel;
|
||||
final long roundedValue = value - value % durationMillisPerPixel;
|
||||
matrix2d.put(roundedEpochMilli, roundedValue, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregatedData getAggregatedData() throws IOException {
|
||||
|
||||
final File dataFile = File.createTempFile("data", ".dat", tmpDir.toFile());
|
||||
final int separator = ',';
|
||||
final int newline = '\n';
|
||||
|
||||
long[] actualValuesWritten = new long[1];
|
||||
final StringBuilder formattedDateBuilder = new StringBuilder();
|
||||
try (
|
||||
final LambdaFriendlyWriter output = new LambdaFriendlyWriter(
|
||||
new BufferedWriter(new OutputStreamWriter(new FileOutputStream(dataFile), StandardCharsets.ISO_8859_1)));
|
||||
final Formatter formatter = new Formatter(formattedDateBuilder);) {
|
||||
|
||||
matrix2d.forEach((epochMilli, value, __) -> {
|
||||
|
||||
final String stringValue = LongUtils.longToString(value);
|
||||
final String formattedDate;
|
||||
|
||||
if (useMillis) {
|
||||
formattedDateBuilder.delete(0, formattedDateBuilder.length());
|
||||
formatter.format(Locale.US, "%.3f", epochMilli / 1000.0);
|
||||
formattedDate = formattedDateBuilder.toString();
|
||||
} else {
|
||||
formattedDate = String.valueOf(epochMilli / 1000);
|
||||
}
|
||||
|
||||
output.write(formattedDate);
|
||||
output.write(separator);
|
||||
output.write(stringValue);
|
||||
output.write(newline);
|
||||
actualValuesWritten[0]++;
|
||||
});
|
||||
minValue = plotSettings.getYRangeUnit() == TimeRangeUnitInternal.AUTOMATIC ? 0
|
||||
: plotSettings.getYRangeUnit().toMilliSeconds(plotSettings.getYRangeMin());
|
||||
maxValue = plotSettings.getYRangeUnit() == TimeRangeUnitInternal.AUTOMATIC ? Long.MAX_VALUE
|
||||
: plotSettings.getYRangeUnit().toMilliSeconds(plotSettings.getYRangeMax());
|
||||
durationMillisPerPixel = plotSettings.getYAxisScale() == AxisScale.LINEAR
|
||||
? Math.max(1, (maxValue - minValue) / plotAreaHeightInPx)
|
||||
: 1;
|
||||
}
|
||||
|
||||
return new AggregatedData("scatter", dataFile);
|
||||
}
|
||||
@Override
|
||||
public void addValue(boolean valueIsInYRange, long epochMilli, long value) {
|
||||
final long roundedEpochMilli = epochMilli - epochMilli % epochMillisPerPixel;
|
||||
final long roundedValue = value - value % durationMillisPerPixel;
|
||||
matrix2d.put(roundedEpochMilli, roundedValue, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getType() {
|
||||
return Aggregate.SCATTER;
|
||||
}
|
||||
@Override
|
||||
public AggregatedData getAggregatedData() throws IOException {
|
||||
|
||||
final File dataFile = File.createTempFile("data", ".dat", tmpDir.toFile());
|
||||
final int separator = ',';
|
||||
final int newline = '\n';
|
||||
|
||||
long[] actualValuesWritten = new long[1];
|
||||
final StringBuilder formattedDateBuilder = new StringBuilder();
|
||||
try (final LambdaFriendlyWriter output = new LambdaFriendlyWriter(new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(dataFile), StandardCharsets.ISO_8859_1)));
|
||||
final Formatter formatter = new Formatter(formattedDateBuilder);) {
|
||||
|
||||
matrix2d.forEach((epochMilli, value, __) -> {
|
||||
|
||||
final String stringValue = LongUtils.longToString(value);
|
||||
final String formattedDate;
|
||||
|
||||
if (useMillis) {
|
||||
formattedDateBuilder.delete(0, formattedDateBuilder.length());
|
||||
formatter.format(Locale.US, "%.3f", epochMilli / 1000.0);
|
||||
formattedDate = formattedDateBuilder.toString();
|
||||
} else {
|
||||
formattedDate = String.valueOf(epochMilli / 1000);
|
||||
}
|
||||
|
||||
output.write(formattedDate);
|
||||
output.write(separator);
|
||||
output.write(stringValue);
|
||||
output.write(newline);
|
||||
actualValuesWritten[0]++;
|
||||
});
|
||||
}
|
||||
|
||||
return new AggregatedData("scatter", dataFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Aggregate getType() {
|
||||
return Aggregate.SCATTER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
package org.lucares.pdb.plot.api;
|
||||
|
||||
public enum TimeRangeUnitInternal {
|
||||
AUTOMATIC, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS;
|
||||
AUTOMATIC, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS;
|
||||
|
||||
public int toMilliSeconds(final int value) {
|
||||
public int toMilliSeconds(final int value) {
|
||||
|
||||
switch (this) {
|
||||
case MILLISECONDS:
|
||||
return value;
|
||||
case SECONDS:
|
||||
return value * 1000;
|
||||
case MINUTES:
|
||||
return value * 60 * 1000;
|
||||
case HOURS:
|
||||
return value * 60 * 60 * 1000;
|
||||
case DAYS:
|
||||
return value * 24 * 60 * 60 * 1000;
|
||||
case AUTOMATIC:
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
switch (this) {
|
||||
case MILLISECONDS:
|
||||
return value;
|
||||
case SECONDS:
|
||||
return value * 1000;
|
||||
case MINUTES:
|
||||
return value * 60 * 1000;
|
||||
case HOURS:
|
||||
return value * 60 * 60 * 1000;
|
||||
case DAYS:
|
||||
return value * 24 * 60 * 60 * 1000;
|
||||
case AUTOMATIC:
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,175 +9,173 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
public class AxisSettings {
|
||||
|
||||
public enum Type {
|
||||
Number, Time, Duration, Percent
|
||||
}
|
||||
|
||||
private String format = "";
|
||||
|
||||
private String label = "";
|
||||
|
||||
private int rotateLabel = 0;
|
||||
|
||||
private String from;
|
||||
private String to;
|
||||
|
||||
private Type type = Type.Number;
|
||||
|
||||
private GnuplotAxis axis = GnuplotAxis.X1;
|
||||
|
||||
private double ticIncrement;
|
||||
|
||||
private boolean ticsEnabled;
|
||||
|
||||
private boolean logscale;
|
||||
|
||||
private List<String> ticsLabels;
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public int getRotateXAxisLabel() {
|
||||
return rotateLabel;
|
||||
}
|
||||
|
||||
public void setRotateLabel(int rotateLabel) {
|
||||
this.rotateLabel = rotateLabel;
|
||||
}
|
||||
|
||||
public String getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public void setFrom(String from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
public String getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public void setTo(String to) {
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public GnuplotAxis getAxis() {
|
||||
return axis;
|
||||
}
|
||||
|
||||
public void setAxis(GnuplotAxis axis) {
|
||||
this.axis = axis;
|
||||
}
|
||||
|
||||
public void setTicIncrement(double ticIncrement) {
|
||||
this.ticIncrement = ticIncrement;
|
||||
}
|
||||
|
||||
public double getTicIncrement() {
|
||||
return ticIncrement;
|
||||
}
|
||||
|
||||
public void setTicsEnabled(boolean ticsEnabled) {
|
||||
this.ticsEnabled = ticsEnabled;
|
||||
}
|
||||
|
||||
public boolean isTicsEnabled() {
|
||||
return ticsEnabled;
|
||||
}
|
||||
|
||||
public void setLogscale(boolean logscale) {
|
||||
this.logscale = logscale;
|
||||
}
|
||||
|
||||
public boolean isLogscale() {
|
||||
return logscale;
|
||||
}
|
||||
|
||||
public void setTics(List<String> ticsLabels) {
|
||||
this.ticsLabels = ticsLabels;
|
||||
}
|
||||
|
||||
public List<String> getTics() {
|
||||
return ticsLabels;
|
||||
}
|
||||
|
||||
|
||||
public String toGnuplotDefinition(boolean renderLabels) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (type == Type.Time) {
|
||||
appendfln(result, "set %sdata time", axis);
|
||||
public enum Type {
|
||||
Number, Time, Duration, Percent
|
||||
}
|
||||
|
||||
if (renderLabels) {
|
||||
|
||||
if (ticIncrement != 0) {
|
||||
appendfln(result, "set %stics %f nomirror", axis, ticIncrement);
|
||||
}
|
||||
else if (ticsLabels != null && ticsLabels.size() > 0) {
|
||||
appendfln(result,"set %stics(%s) nomirror", axis, String.join(", ", ticsLabels));
|
||||
}else if(ticsEnabled) {
|
||||
appendfln(result, "set %stics nomirror", axis);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(format)) {
|
||||
appendfln(result, "set format %s \"%s\"", axis, format);
|
||||
}
|
||||
|
||||
if (rotateLabel != 0) {
|
||||
appendfln(result, "set %stics nomirror rotate by %d", axis, rotateLabel);
|
||||
}
|
||||
if (StringUtils.isNotBlank(label)) {
|
||||
appendfln(result, "set %slabel \"%s\"", axis, label);
|
||||
}
|
||||
}else {
|
||||
|
||||
appendfln(result, "set format %s \"\"", axis);
|
||||
appendfln(result, "set %slabel \"\"", axis);
|
||||
private String format = "";
|
||||
|
||||
private String label = "";
|
||||
|
||||
private int rotateLabel = 0;
|
||||
|
||||
private String from;
|
||||
private String to;
|
||||
|
||||
private Type type = Type.Number;
|
||||
|
||||
private GnuplotAxis axis = GnuplotAxis.X1;
|
||||
|
||||
private double ticIncrement;
|
||||
|
||||
private boolean ticsEnabled;
|
||||
|
||||
private boolean logscale;
|
||||
|
||||
private List<String> ticsLabels;
|
||||
|
||||
public String getFormat() {
|
||||
return format;
|
||||
}
|
||||
|
||||
if (!StringUtils.isAllBlank(from, to)) {
|
||||
final String f = StringUtils.isEmpty(from) ? "" : "\""+from+"\"";
|
||||
final String t = StringUtils.isEmpty(to) ? "" : "\""+to+"\"";
|
||||
appendfln(result, "set %srange [%s:%s]", axis, f, t);
|
||||
public void setFormat(String format) {
|
||||
this.format = format;
|
||||
}
|
||||
if (logscale) {
|
||||
appendfln(result, "set logscale %s", axis);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private void appendfln(final StringBuilder builder, final String format, final Object... args) {
|
||||
builder.append(String.format(format + "\n", args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
return mapper.writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
return e.getMessage();
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public int getRotateXAxisLabel() {
|
||||
return rotateLabel;
|
||||
}
|
||||
|
||||
public void setRotateLabel(int rotateLabel) {
|
||||
this.rotateLabel = rotateLabel;
|
||||
}
|
||||
|
||||
public String getFrom() {
|
||||
return from;
|
||||
}
|
||||
|
||||
public void setFrom(String from) {
|
||||
this.from = from;
|
||||
}
|
||||
|
||||
public String getTo() {
|
||||
return to;
|
||||
}
|
||||
|
||||
public void setTo(String to) {
|
||||
this.to = to;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public GnuplotAxis getAxis() {
|
||||
return axis;
|
||||
}
|
||||
|
||||
public void setAxis(GnuplotAxis axis) {
|
||||
this.axis = axis;
|
||||
}
|
||||
|
||||
public void setTicIncrement(double ticIncrement) {
|
||||
this.ticIncrement = ticIncrement;
|
||||
}
|
||||
|
||||
public double getTicIncrement() {
|
||||
return ticIncrement;
|
||||
}
|
||||
|
||||
public void setTicsEnabled(boolean ticsEnabled) {
|
||||
this.ticsEnabled = ticsEnabled;
|
||||
}
|
||||
|
||||
public boolean isTicsEnabled() {
|
||||
return ticsEnabled;
|
||||
}
|
||||
|
||||
public void setLogscale(boolean logscale) {
|
||||
this.logscale = logscale;
|
||||
}
|
||||
|
||||
public boolean isLogscale() {
|
||||
return logscale;
|
||||
}
|
||||
|
||||
public void setTics(List<String> ticsLabels) {
|
||||
this.ticsLabels = ticsLabels;
|
||||
}
|
||||
|
||||
public List<String> getTics() {
|
||||
return ticsLabels;
|
||||
}
|
||||
|
||||
public String toGnuplotDefinition(boolean renderLabels) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (type == Type.Time) {
|
||||
appendfln(result, "set %sdata time", axis);
|
||||
}
|
||||
|
||||
if (renderLabels) {
|
||||
|
||||
if (ticIncrement != 0) {
|
||||
appendfln(result, "set %stics %f nomirror", axis, ticIncrement);
|
||||
} else if (ticsLabels != null && ticsLabels.size() > 0) {
|
||||
appendfln(result, "set %stics(%s) nomirror", axis, String.join(", ", ticsLabels));
|
||||
} else if (ticsEnabled) {
|
||||
appendfln(result, "set %stics nomirror", axis);
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(format)) {
|
||||
appendfln(result, "set format %s \"%s\"", axis, format);
|
||||
}
|
||||
|
||||
if (rotateLabel != 0) {
|
||||
appendfln(result, "set %stics nomirror rotate by %d", axis, rotateLabel);
|
||||
}
|
||||
if (StringUtils.isNotBlank(label)) {
|
||||
appendfln(result, "set %slabel \"%s\"", axis, label);
|
||||
}
|
||||
} else {
|
||||
|
||||
appendfln(result, "set format %s \"\"", axis);
|
||||
appendfln(result, "set %slabel \"\"", axis);
|
||||
}
|
||||
|
||||
if (!StringUtils.isAllBlank(from, to)) {
|
||||
final String f = StringUtils.isEmpty(from) ? "" : "\"" + from + "\"";
|
||||
final String t = StringUtils.isEmpty(to) ? "" : "\"" + to + "\"";
|
||||
appendfln(result, "set %srange [%s:%s]", axis, f, t);
|
||||
}
|
||||
if (logscale) {
|
||||
appendfln(result, "set logscale %s", axis);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private void appendfln(final StringBuilder builder, final String format, final Object... args) {
|
||||
builder.append(String.format(format + "\n", args));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
try {
|
||||
return mapper.writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
return e.getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,116 +11,89 @@ import org.lucares.pdb.plot.api.AxisScale;
|
||||
import org.lucares.recommind.logs.AxisSettings.Type;
|
||||
|
||||
public class AxisTime {
|
||||
public static AxisSettings createXAxis(GnuplotSettings settings) {
|
||||
AxisSettings result = new AxisSettings();
|
||||
|
||||
final OffsetDateTime minDate = settings.getDateTimeRange().getStart();
|
||||
final OffsetDateTime maxDate = settings.getDateTimeRange().getEnd();
|
||||
final String formatX;
|
||||
if (minDate.until(maxDate, ChronoUnit.WEEKS) > 1) {
|
||||
formatX = "%Y-%m-%d";
|
||||
} else if (minDate.until(maxDate, ChronoUnit.SECONDS) > 30) {
|
||||
formatX = "%Y-%m-%d\\n%H:%M:%S";
|
||||
} else {
|
||||
formatX = "%Y-%m-%d\\n%H:%M:%.3S";
|
||||
}
|
||||
final String formattedMinDate = String.valueOf(minDate.toEpochSecond());
|
||||
final String formattedMaxDate = String.valueOf(maxDate.toEpochSecond());
|
||||
public static AxisSettings createXAxis(GnuplotSettings settings) {
|
||||
AxisSettings result = new AxisSettings();
|
||||
|
||||
result.setLabel("Time");
|
||||
result.setType(Type.Time);
|
||||
result.setTicsEnabled(true);
|
||||
result.setFormat(formatX);
|
||||
result.setFrom(formattedMinDate);
|
||||
result.setTo(formattedMaxDate);
|
||||
result.setTicIncrement(computeTimeTicIncrement(settings.getWidth(), settings.getDateTimeRange()));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static AxisSettings createYAxis(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
AxisSettings result = new AxisSettings();
|
||||
result.setLabel("Duration");
|
||||
result.setType(Type.Duration);
|
||||
result.setAxis(GnuplotAxis.Y1);
|
||||
result.setTicsEnabled(true);
|
||||
final OffsetDateTime minDate = settings.getDateTimeRange().getStart();
|
||||
final OffsetDateTime maxDate = settings.getDateTimeRange().getEnd();
|
||||
final String formatX;
|
||||
if (minDate.until(maxDate, ChronoUnit.WEEKS) > 1) {
|
||||
formatX = "%Y-%m-%d";
|
||||
} else if (minDate.until(maxDate, ChronoUnit.SECONDS) > 30) {
|
||||
formatX = "%Y-%m-%d\\n%H:%M:%S";
|
||||
} else {
|
||||
formatX = "%Y-%m-%d\\n%H:%M:%.3S";
|
||||
}
|
||||
final String formattedMinDate = String.valueOf(minDate.toEpochSecond());
|
||||
final String formattedMaxDate = String.valueOf(maxDate.toEpochSecond());
|
||||
|
||||
final int graphOffset = settings.getYAxisScale() == AxisScale.LINEAR ? 0 : 1;
|
||||
if (settings.hasYRange()) {
|
||||
final int min = Math.max(settings.getYRangeMin(), graphOffset);
|
||||
final int max = settings.getYRangeMax();
|
||||
result.setFrom(String.valueOf(min));
|
||||
result.setTo(String.valueOf(max));
|
||||
} else {
|
||||
result.setFrom(String.valueOf(graphOffset));
|
||||
result.setLabel("Time");
|
||||
result.setType(Type.Time);
|
||||
result.setTicsEnabled(true);
|
||||
result.setFormat(formatX);
|
||||
result.setFrom(formattedMinDate);
|
||||
result.setTo(formattedMaxDate);
|
||||
result.setTicIncrement(computeTimeTicIncrement(settings.getWidth(), settings.getDateTimeRange()));
|
||||
return result;
|
||||
}
|
||||
|
||||
result.setLogscale(settings.getYAxisScale() == AxisScale.LOG10);
|
||||
|
||||
result.setTics(YAxisTicks.computeYTicks(settings, dataSeries));
|
||||
public static AxisSettings createYAxis(GnuplotSettings settings, Collection<DataSeries> dataSeries) {
|
||||
AxisSettings result = new AxisSettings();
|
||||
result.setLabel("Duration");
|
||||
result.setType(Type.Duration);
|
||||
result.setAxis(GnuplotAxis.Y1);
|
||||
result.setTicsEnabled(true);
|
||||
|
||||
return result;
|
||||
}
|
||||
final int graphOffset = settings.getYAxisScale() == AxisScale.LINEAR ? 0 : 1;
|
||||
if (settings.hasYRange()) {
|
||||
final int min = Math.max(settings.getYRangeMin(), graphOffset);
|
||||
final int max = settings.getYRangeMax();
|
||||
result.setFrom(String.valueOf(min));
|
||||
result.setTo(String.valueOf(max));
|
||||
} else {
|
||||
result.setFrom(String.valueOf(graphOffset));
|
||||
}
|
||||
|
||||
public static double computeTimeTicIncrement(int width, DateTimeRange dateTimeRange) {
|
||||
final long startEpochMilli = dateTimeRange.getStartEpochMilli();
|
||||
final long endEpochMilli = dateTimeRange.getEndEpochMilli();
|
||||
final long rangeInMs = endEpochMilli - startEpochMilli + 1;
|
||||
|
||||
int widthInPx = width - GnuplotSettings.GNUPLOT_LEFT_RIGHT_MARGIN;
|
||||
|
||||
final long maxLabels = Math.max(1, widthInPx / (GnuplotSettings.TICKS_FONT_SIZE * 8));
|
||||
result.setLogscale(settings.getYAxisScale() == AxisScale.LOG10);
|
||||
|
||||
final long tickIncrement = roundToTickIncrement(rangeInMs / maxLabels);
|
||||
return tickIncrement/1000.0;
|
||||
}
|
||||
|
||||
private static long roundToTickIncrement(long milliseconds) {
|
||||
LongList increments = LongList.of(
|
||||
100,
|
||||
200,
|
||||
500,
|
||||
TimeUnit.SECONDS.toMillis(1),
|
||||
TimeUnit.SECONDS.toMillis(2),
|
||||
TimeUnit.SECONDS.toMillis(5),
|
||||
TimeUnit.SECONDS.toMillis(10),
|
||||
TimeUnit.SECONDS.toMillis(15),
|
||||
TimeUnit.SECONDS.toMillis(30),
|
||||
TimeUnit.MINUTES.toMillis(1),
|
||||
TimeUnit.MINUTES.toMillis(2),
|
||||
TimeUnit.MINUTES.toMillis(5),
|
||||
TimeUnit.MINUTES.toMillis(10),
|
||||
TimeUnit.MINUTES.toMillis(15),
|
||||
TimeUnit.MINUTES.toMillis(30),
|
||||
TimeUnit.HOURS.toMillis(1),
|
||||
TimeUnit.HOURS.toMillis(2),
|
||||
TimeUnit.HOURS.toMillis(3),
|
||||
TimeUnit.HOURS.toMillis(6),
|
||||
TimeUnit.HOURS.toMillis(12),
|
||||
TimeUnit.HOURS.toMillis(18),
|
||||
TimeUnit.DAYS.toMillis(1),
|
||||
TimeUnit.DAYS.toMillis(2),
|
||||
TimeUnit.DAYS.toMillis(3),
|
||||
TimeUnit.DAYS.toMillis(4),
|
||||
TimeUnit.DAYS.toMillis(5),
|
||||
TimeUnit.DAYS.toMillis(6),
|
||||
TimeUnit.DAYS.toMillis(7),
|
||||
TimeUnit.DAYS.toMillis(14),
|
||||
TimeUnit.DAYS.toMillis(30),
|
||||
TimeUnit.DAYS.toMillis(90),
|
||||
TimeUnit.DAYS.toMillis(180),
|
||||
TimeUnit.DAYS.toMillis(365),
|
||||
TimeUnit.DAYS.toMillis(365*2),
|
||||
TimeUnit.DAYS.toMillis(365*5),
|
||||
TimeUnit.DAYS.toMillis(365*10),
|
||||
TimeUnit.DAYS.toMillis(365*20)
|
||||
);
|
||||
|
||||
for ( int i = 0; i < increments.size(); i++) {
|
||||
if (increments.get(i) > milliseconds) {
|
||||
return increments.get(i);
|
||||
}
|
||||
result.setTics(YAxisTicks.computeYTicks(settings, dataSeries));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static double computeTimeTicIncrement(int width, DateTimeRange dateTimeRange) {
|
||||
final long startEpochMilli = dateTimeRange.getStartEpochMilli();
|
||||
final long endEpochMilli = dateTimeRange.getEndEpochMilli();
|
||||
final long rangeInMs = endEpochMilli - startEpochMilli + 1;
|
||||
|
||||
int widthInPx = width - GnuplotSettings.GNUPLOT_LEFT_RIGHT_MARGIN;
|
||||
|
||||
final long maxLabels = Math.max(1, widthInPx / (GnuplotSettings.TICKS_FONT_SIZE * 8));
|
||||
|
||||
final long tickIncrement = roundToTickIncrement(rangeInMs / maxLabels);
|
||||
return tickIncrement / 1000.0;
|
||||
}
|
||||
|
||||
private static long roundToTickIncrement(long milliseconds) {
|
||||
LongList increments = LongList.of(100, 200, 500, TimeUnit.SECONDS.toMillis(1), TimeUnit.SECONDS.toMillis(2),
|
||||
TimeUnit.SECONDS.toMillis(5), TimeUnit.SECONDS.toMillis(10), TimeUnit.SECONDS.toMillis(15),
|
||||
TimeUnit.SECONDS.toMillis(30), TimeUnit.MINUTES.toMillis(1), TimeUnit.MINUTES.toMillis(2),
|
||||
TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(10), TimeUnit.MINUTES.toMillis(15),
|
||||
TimeUnit.MINUTES.toMillis(30), TimeUnit.HOURS.toMillis(1), TimeUnit.HOURS.toMillis(2),
|
||||
TimeUnit.HOURS.toMillis(3), TimeUnit.HOURS.toMillis(6), TimeUnit.HOURS.toMillis(12),
|
||||
TimeUnit.HOURS.toMillis(18), TimeUnit.DAYS.toMillis(1), TimeUnit.DAYS.toMillis(2),
|
||||
TimeUnit.DAYS.toMillis(3), TimeUnit.DAYS.toMillis(4), TimeUnit.DAYS.toMillis(5),
|
||||
TimeUnit.DAYS.toMillis(6), TimeUnit.DAYS.toMillis(7), TimeUnit.DAYS.toMillis(14),
|
||||
TimeUnit.DAYS.toMillis(30), TimeUnit.DAYS.toMillis(90), TimeUnit.DAYS.toMillis(180),
|
||||
TimeUnit.DAYS.toMillis(365), TimeUnit.DAYS.toMillis(365 * 2), TimeUnit.DAYS.toMillis(365 * 5),
|
||||
TimeUnit.DAYS.toMillis(365 * 10), TimeUnit.DAYS.toMillis(365 * 20));
|
||||
|
||||
for (int i = 0; i < increments.size(); i++) {
|
||||
if (increments.get(i) > milliseconds) {
|
||||
return increments.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
return TimeUnit.DAYS.toMillis(365 * 10);
|
||||
}
|
||||
|
||||
return TimeUnit.DAYS.toMillis(365*10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class Config {
|
||||
public static final Path DATA_DIR = Paths.get("/home/andi/ws/performanceDb/db");
|
||||
public static final Path DATA_DIR = Paths.get("/home/andi/ws/performanceDb/db");
|
||||
}
|
||||
|
||||
@@ -3,52 +3,51 @@ package org.lucares.recommind.logs;
|
||||
import org.lucares.pdb.plot.api.AggregatedDataCollection;
|
||||
|
||||
class CsvSummary {
|
||||
private final int values;
|
||||
private final long maxValue;
|
||||
private final AggregatedDataCollection aggregatedData;
|
||||
private final double statsAverage;
|
||||
private final int plottedValues;
|
||||
private final int values;
|
||||
private final long maxValue;
|
||||
private final AggregatedDataCollection aggregatedData;
|
||||
private final double statsAverage;
|
||||
private final int plottedValues;
|
||||
|
||||
public CsvSummary(final int values, final int plottedValues, final long maxValue,
|
||||
final double statsAverage, final AggregatedDataCollection aggregatedData) {
|
||||
super();
|
||||
this.values = values;
|
||||
this.plottedValues = plottedValues;
|
||||
this.maxValue = maxValue;
|
||||
this.statsAverage = statsAverage;
|
||||
this.aggregatedData = aggregatedData;
|
||||
}
|
||||
public CsvSummary(final int values, final int plottedValues, final long maxValue, final double statsAverage,
|
||||
final AggregatedDataCollection aggregatedData) {
|
||||
super();
|
||||
this.values = values;
|
||||
this.plottedValues = plottedValues;
|
||||
this.maxValue = maxValue;
|
||||
this.statsAverage = statsAverage;
|
||||
this.aggregatedData = aggregatedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total number of values in the selected date range.
|
||||
*
|
||||
* @see CsvSummary#getPlottedValues()
|
||||
* @return total number of values
|
||||
*/
|
||||
public int getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Total number of values in the selected date range.
|
||||
*
|
||||
* @see CsvSummary#getPlottedValues()
|
||||
* @return total number of values
|
||||
*/
|
||||
public int getValues() {
|
||||
return values;
|
||||
}
|
||||
/**
|
||||
* Number of plotted values in the selected date range <em>and</em> y-range.
|
||||
*
|
||||
* @see CsvSummary#getValues()
|
||||
* @return number of plotted values
|
||||
*/
|
||||
public int getPlottedValues() {
|
||||
return plottedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of plotted values in the selected date range <em>and</em> y-range.
|
||||
*
|
||||
* @see CsvSummary#getValues()
|
||||
* @return number of plotted values
|
||||
*/
|
||||
public int getPlottedValues() {
|
||||
return plottedValues;
|
||||
}
|
||||
public long getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
|
||||
public long getMaxValue() {
|
||||
return maxValue;
|
||||
}
|
||||
public double getStatsAverage() {
|
||||
return statsAverage;
|
||||
}
|
||||
|
||||
public double getStatsAverage() {
|
||||
return statsAverage;
|
||||
}
|
||||
|
||||
public AggregatedDataCollection getAggregatedData() {
|
||||
return aggregatedData;
|
||||
}
|
||||
public AggregatedDataCollection getAggregatedData() {
|
||||
return aggregatedData;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,20 +2,20 @@ package org.lucares.recommind.logs;
|
||||
|
||||
public enum DashTypes {
|
||||
|
||||
DASH_TYPE_2("2"), DASH_TYPE_3("3"), DASH_TYPE_4("4"), DASH_TYPE_5("5"), DASH_TYPE_6("6"), DASH_TYPE_DOT(
|
||||
"\".\""), DASH_TYPE_DASH("\"-\""), DASH_TYPE_DOT_DASH("\"._\""), DASH_TYPE_DOT_DOT_DASH("\"..- \"");
|
||||
DASH_TYPE_2("2"), DASH_TYPE_3("3"), DASH_TYPE_4("4"), DASH_TYPE_5("5"), DASH_TYPE_6("6"), DASH_TYPE_DOT("\".\""),
|
||||
DASH_TYPE_DASH("\"-\""), DASH_TYPE_DOT_DASH("\"._\""), DASH_TYPE_DOT_DOT_DASH("\"..- \"");
|
||||
|
||||
private final String gnuplotDashType;
|
||||
private final String gnuplotDashType;
|
||||
|
||||
private DashTypes(final String gnuplotDashType) {
|
||||
this.gnuplotDashType = gnuplotDashType;
|
||||
}
|
||||
private DashTypes(final String gnuplotDashType) {
|
||||
this.gnuplotDashType = gnuplotDashType;
|
||||
}
|
||||
|
||||
public String toGnuplotDashType() {
|
||||
return gnuplotDashType;
|
||||
}
|
||||
public String toGnuplotDashType() {
|
||||
return gnuplotDashType;
|
||||
}
|
||||
|
||||
static DashTypes get(final int i) {
|
||||
return values()[i % values().length];
|
||||
}
|
||||
static DashTypes get(final int i) {
|
||||
return values()[i % values().length];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,110 +10,110 @@ import org.lucares.pdb.plot.api.AggregatedDataCollection;
|
||||
import org.lucares.pdb.plot.api.Limit;
|
||||
|
||||
public interface DataSeries {
|
||||
public static final Comparator<? super DataSeries> BY_NUMBER_OF_VALUES = (a, b) -> {
|
||||
return a.getValues() - b.getValues();
|
||||
};
|
||||
public static final Comparator<? super DataSeries> BY_NUMBER_OF_VALUES = (a, b) -> {
|
||||
return a.getValues() - b.getValues();
|
||||
};
|
||||
|
||||
public static final Comparator<? super DataSeries> BY_MAX_VALUE = (a, b) -> {
|
||||
final long result = a.getMaxValue() - b.getMaxValue();
|
||||
return result < 0 ? -1 : (result > 0 ? 1 : 0);
|
||||
};
|
||||
public static final Comparator<? super DataSeries> BY_MAX_VALUE = (a, b) -> {
|
||||
final long result = a.getMaxValue() - b.getMaxValue();
|
||||
return result < 0 ? -1 : (result > 0 ? 1 : 0);
|
||||
};
|
||||
|
||||
public static final Comparator<? super DataSeries> BY_NAME = (a, b) -> {
|
||||
return a.getTitle().compareToIgnoreCase(b.getTitle());
|
||||
};
|
||||
public static final Comparator<? super DataSeries> BY_NAME = (a, b) -> {
|
||||
return a.getTitle().compareToIgnoreCase(b.getTitle());
|
||||
};
|
||||
|
||||
public String getIdAsString();
|
||||
public String getIdAsString();
|
||||
|
||||
public int getId();
|
||||
public int getId();
|
||||
|
||||
public String getTitle();
|
||||
public String getTitle();
|
||||
|
||||
public int getValues();
|
||||
public int getValues();
|
||||
|
||||
public int getPlottedValues();
|
||||
public int getPlottedValues();
|
||||
|
||||
public long getMaxValue();
|
||||
public long getMaxValue();
|
||||
|
||||
public double getAverage();
|
||||
public double getAverage();
|
||||
|
||||
public void setStyle(LineStyle style);
|
||||
public void setStyle(LineStyle style);
|
||||
|
||||
public LineStyle getStyle();
|
||||
public LineStyle getStyle();
|
||||
|
||||
public AggregatedDataCollection getAggregatedData();
|
||||
public AggregatedDataCollection getAggregatedData();
|
||||
|
||||
public static Map<String, Integer> toMap(final List<DataSeries> dataSeries) {
|
||||
final Map<String, Integer> result = new LinkedHashMap<>();
|
||||
public static Map<String, Integer> toMap(final List<DataSeries> dataSeries) {
|
||||
final Map<String, Integer> result = new LinkedHashMap<>();
|
||||
|
||||
for (final DataSeries dataSerie : dataSeries) {
|
||||
for (final DataSeries dataSerie : dataSeries) {
|
||||
|
||||
result.put(dataSerie.getTitle(), dataSerie.getValues());
|
||||
result.put(dataSerie.getTitle(), dataSerie.getValues());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static Comparator<? super DataSeries> getDataSeriesComparator(final Limit limitBy) {
|
||||
static Comparator<? super DataSeries> getDataSeriesComparator(final Limit limitBy) {
|
||||
|
||||
switch (limitBy) {
|
||||
case MOST_VALUES:
|
||||
return DataSeries.BY_NUMBER_OF_VALUES.reversed();
|
||||
case FEWEST_VALUES:
|
||||
return DataSeries.BY_NUMBER_OF_VALUES;
|
||||
case MAX_VALUE:
|
||||
return DataSeries.BY_MAX_VALUE.reversed();
|
||||
case MIN_VALUE:
|
||||
return DataSeries.BY_MAX_VALUE;
|
||||
case NO_LIMIT:
|
||||
return DataSeries.BY_NAME;
|
||||
}
|
||||
throw new IllegalStateException("unhandled enum: " + limitBy);
|
||||
}
|
||||
switch (limitBy) {
|
||||
case MOST_VALUES:
|
||||
return DataSeries.BY_NUMBER_OF_VALUES.reversed();
|
||||
case FEWEST_VALUES:
|
||||
return DataSeries.BY_NUMBER_OF_VALUES;
|
||||
case MAX_VALUE:
|
||||
return DataSeries.BY_MAX_VALUE.reversed();
|
||||
case MIN_VALUE:
|
||||
return DataSeries.BY_MAX_VALUE;
|
||||
case NO_LIMIT:
|
||||
return DataSeries.BY_NAME;
|
||||
}
|
||||
throw new IllegalStateException("unhandled enum: " + limitBy);
|
||||
}
|
||||
|
||||
static void sortAndLimit(final List<DataSeries> dataSeries, final Limit limitBy, final int limit) {
|
||||
static void sortAndLimit(final List<DataSeries> dataSeries, final Limit limitBy, final int limit) {
|
||||
|
||||
dataSeries.sort(DataSeries.getDataSeriesComparator(limitBy));
|
||||
dataSeries.sort(DataSeries.getDataSeriesComparator(limitBy));
|
||||
|
||||
switch (limitBy) {
|
||||
case FEWEST_VALUES:
|
||||
case MOST_VALUES:
|
||||
case MAX_VALUE:
|
||||
case MIN_VALUE:
|
||||
while (dataSeries.size() > limit) {
|
||||
dataSeries.remove(limit);
|
||||
}
|
||||
break;
|
||||
case NO_LIMIT:
|
||||
}
|
||||
}
|
||||
switch (limitBy) {
|
||||
case FEWEST_VALUES:
|
||||
case MOST_VALUES:
|
||||
case MAX_VALUE:
|
||||
case MIN_VALUE:
|
||||
while (dataSeries.size() > limit) {
|
||||
dataSeries.remove(limit);
|
||||
}
|
||||
break;
|
||||
case NO_LIMIT:
|
||||
}
|
||||
}
|
||||
|
||||
static void setColors(final List<DataSeries> dataSeries) {
|
||||
static void setColors(final List<DataSeries> dataSeries) {
|
||||
|
||||
int i = 0;
|
||||
int i = 0;
|
||||
|
||||
for (final DataSeries dataSerie : dataSeries) {
|
||||
for (final DataSeries dataSerie : dataSeries) {
|
||||
|
||||
final int numColors = GnuplotColorPalettes.DEFAULT.size();
|
||||
final int numColors = GnuplotColorPalettes.DEFAULT.size();
|
||||
|
||||
final GnuplotColor color = GnuplotColorPalettes.DEFAULT.get(i % numColors);
|
||||
final GnuplotColor color = GnuplotColorPalettes.DEFAULT.get(i % numColors);
|
||||
|
||||
final DashTypes dashType = DashTypes.get(i / numColors);
|
||||
final LineStyle lineStyle = new LineStyle(color, dashType);
|
||||
dataSerie.setStyle(lineStyle);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
final DashTypes dashType = DashTypes.get(i / numColors);
|
||||
final LineStyle lineStyle = new LineStyle(color, dashType);
|
||||
dataSerie.setStyle(lineStyle);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
public static long maxValue(final Collection<DataSeries> dataSeries) {
|
||||
long result = 0;
|
||||
public static long maxValue(final Collection<DataSeries> dataSeries) {
|
||||
long result = 0;
|
||||
|
||||
for (final DataSeries series : dataSeries) {
|
||||
result = Math.max(result, series.getMaxValue());
|
||||
}
|
||||
for (final DataSeries series : dataSeries) {
|
||||
result = Math.max(result, series.getMaxValue());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,69 +4,67 @@ import org.lucares.pdb.plot.api.AggregatedDataCollection;
|
||||
|
||||
public class FileBackedDataSeries implements DataSeries {
|
||||
|
||||
private final String title;
|
||||
private final String title;
|
||||
|
||||
private final CsvSummary csvSummary;
|
||||
private final CsvSummary csvSummary;
|
||||
|
||||
private final int id;
|
||||
private final int id;
|
||||
|
||||
private LineStyle style;
|
||||
private LineStyle style;
|
||||
|
||||
public FileBackedDataSeries(final int id, final String title, final CsvSummary csvSummary
|
||||
) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.csvSummary = csvSummary;
|
||||
}
|
||||
public FileBackedDataSeries(final int id, final String title, final CsvSummary csvSummary) {
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.csvSummary = csvSummary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdAsString() {
|
||||
return "id" + id;
|
||||
}
|
||||
@Override
|
||||
public String getIdAsString() {
|
||||
return "id" + id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
@Override
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(final LineStyle style) {
|
||||
this.style = style;
|
||||
}
|
||||
@Override
|
||||
public void setStyle(final LineStyle style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineStyle getStyle() {
|
||||
return style;
|
||||
}
|
||||
@Override
|
||||
public LineStyle getStyle() {
|
||||
return style;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
@Override
|
||||
public int getValues() {
|
||||
return csvSummary.getValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValues() {
|
||||
return csvSummary.getValues();
|
||||
}
|
||||
@Override
|
||||
public int getPlottedValues() {
|
||||
return csvSummary.getPlottedValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlottedValues() {
|
||||
return csvSummary.getPlottedValues();
|
||||
}
|
||||
@Override
|
||||
public long getMaxValue() {
|
||||
return csvSummary.getMaxValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaxValue() {
|
||||
return csvSummary.getMaxValue();
|
||||
}
|
||||
@Override
|
||||
public double getAverage() {
|
||||
return csvSummary.getStatsAverage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getAverage() {
|
||||
return csvSummary.getStatsAverage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AggregatedDataCollection getAggregatedData() {
|
||||
return csvSummary.getAggregatedData();
|
||||
}
|
||||
@Override
|
||||
public AggregatedDataCollection getAggregatedData() {
|
||||
return csvSummary.getAggregatedData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,95 +18,95 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Gnuplot {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Gnuplot.class);
|
||||
private static final Logger METRICS_LOGGER = LoggerFactory.getLogger("org.lucares.metrics.gnuplot");
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Gnuplot.class);
|
||||
private static final Logger METRICS_LOGGER = LoggerFactory.getLogger("org.lucares.metrics.gnuplot");
|
||||
|
||||
private static final String ENV_GNUPLOT_HOME = "GNUPLOT_HOME";
|
||||
private static final String PROPERTY_GNUPLOT_HOME = "gnuplot.home";
|
||||
private final Path tmpDirectory;
|
||||
private static final String ENV_GNUPLOT_HOME = "GNUPLOT_HOME";
|
||||
private static final String PROPERTY_GNUPLOT_HOME = "gnuplot.home";
|
||||
private final Path tmpDirectory;
|
||||
|
||||
// This would be bad style if this code was executed in a web-container, because
|
||||
// it would cause a memory leak.
|
||||
// But this code is only (and will only) be executed as standalone application.
|
||||
private static final ExecutorService POOL = Executors.newCachedThreadPool();
|
||||
// This would be bad style if this code was executed in a web-container, because
|
||||
// it would cause a memory leak.
|
||||
// But this code is only (and will only) be executed as standalone application.
|
||||
private static final ExecutorService POOL = Executors.newCachedThreadPool();
|
||||
|
||||
public Gnuplot(final Path tmpDirectory) {
|
||||
this.tmpDirectory = tmpDirectory;
|
||||
}
|
||||
public Gnuplot(final Path tmpDirectory) {
|
||||
this.tmpDirectory = tmpDirectory;
|
||||
}
|
||||
|
||||
public void plot(final GnuplotSettings settings, final Collection<DataSeries> dataSeries)
|
||||
throws IOException, InterruptedException {
|
||||
public void plot(final GnuplotSettings settings, final Collection<DataSeries> dataSeries)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
final GnuplotFileGenerator generator = new GnuplotFileGenerator();
|
||||
final GnuplotFileGenerator generator = new GnuplotFileGenerator();
|
||||
|
||||
final String gnuplotFileContent = generator.generate(settings, dataSeries);
|
||||
LOGGER.debug(gnuplotFileContent);
|
||||
final String gnuplotFileContent = generator.generate(settings, dataSeries);
|
||||
LOGGER.debug(gnuplotFileContent);
|
||||
|
||||
final File gnuplotFile = File.createTempFile("gnuplot", ".dem", tmpDirectory.toFile());
|
||||
Files.writeString(gnuplotFile.toPath(), gnuplotFileContent, StandardCharsets.UTF_8);
|
||||
final File gnuplotFile = File.createTempFile("gnuplot", ".dem", tmpDirectory.toFile());
|
||||
Files.writeString(gnuplotFile.toPath(), gnuplotFileContent, StandardCharsets.UTF_8);
|
||||
|
||||
final long start = System.nanoTime();
|
||||
final long start = System.nanoTime();
|
||||
|
||||
try {
|
||||
final ProcessBuilder processBuilder = new ProcessBuilder(gnuplotBinary(), gnuplotFile.getAbsolutePath())//
|
||||
.redirectOutput(Redirect.PIPE)//
|
||||
.redirectError(Redirect.PIPE);
|
||||
try {
|
||||
final ProcessBuilder processBuilder = new ProcessBuilder(gnuplotBinary(), gnuplotFile.getAbsolutePath())//
|
||||
.redirectOutput(Redirect.PIPE)//
|
||||
.redirectError(Redirect.PIPE);
|
||||
|
||||
final Process process = processBuilder.start();
|
||||
logOutput("stderr", process.getErrorStream());
|
||||
logOutput("stdout", process.getInputStream());
|
||||
process.waitFor();
|
||||
} catch (final IOException e) {
|
||||
if (e.getMessage().contains("No such file or directory")) {
|
||||
throw new IOException("Did not find gnuplot. Add it to the 'PATH' or create an environment variable '"
|
||||
+ ENV_GNUPLOT_HOME + "' or add the java property '" + PROPERTY_GNUPLOT_HOME + "': "
|
||||
+ e.getMessage(), e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
final Process process = processBuilder.start();
|
||||
logOutput("stderr", process.getErrorStream());
|
||||
logOutput("stdout", process.getInputStream());
|
||||
process.waitFor();
|
||||
} catch (final IOException e) {
|
||||
if (e.getMessage().contains("No such file or directory")) {
|
||||
throw new IOException("Did not find gnuplot. Add it to the 'PATH' or create an environment variable '"
|
||||
+ ENV_GNUPLOT_HOME + "' or add the java property '" + PROPERTY_GNUPLOT_HOME + "': "
|
||||
+ e.getMessage(), e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
METRICS_LOGGER.debug("gnuplot: {}ms", (System.nanoTime() - start) / 1_000_000.0);
|
||||
}
|
||||
METRICS_LOGGER.debug("gnuplot: {}ms", (System.nanoTime() - start) / 1_000_000.0);
|
||||
}
|
||||
|
||||
private void logOutput(final String humanReadableType, final InputStream stream) throws IOException {
|
||||
private void logOutput(final String humanReadableType, final InputStream stream) throws IOException {
|
||||
|
||||
POOL.submit(() -> {
|
||||
try {
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
LOGGER.info("gnuplot {}: {}", humanReadableType, line);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
LOGGER.warn("Exception while reading " + humanReadableType + " of gnuplot command", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
POOL.submit(() -> {
|
||||
try {
|
||||
final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
LOGGER.info("gnuplot {}: {}", humanReadableType, line);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
LOGGER.warn("Exception while reading " + humanReadableType + " of gnuplot command", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String gnuplotBinary() {
|
||||
private String gnuplotBinary() {
|
||||
|
||||
if (System.getProperty(PROPERTY_GNUPLOT_HOME) != null) {
|
||||
if (System.getProperty(PROPERTY_GNUPLOT_HOME) != null) {
|
||||
|
||||
if (isWindows()) {
|
||||
return System.getProperty(PROPERTY_GNUPLOT_HOME) + "\\bin\\gnuplot.exe";
|
||||
} else {
|
||||
return System.getProperty(PROPERTY_GNUPLOT_HOME) + "/bin/gnuplot";
|
||||
}
|
||||
}
|
||||
if (System.getenv(ENV_GNUPLOT_HOME) != null) {
|
||||
if (isWindows()) {
|
||||
return System.getProperty(PROPERTY_GNUPLOT_HOME) + "\\bin\\gnuplot.exe";
|
||||
} else {
|
||||
return System.getProperty(PROPERTY_GNUPLOT_HOME) + "/bin/gnuplot";
|
||||
}
|
||||
}
|
||||
if (System.getenv(ENV_GNUPLOT_HOME) != null) {
|
||||
|
||||
if (isWindows()) {
|
||||
return System.getenv(ENV_GNUPLOT_HOME) + "\\bin\\gnuplot.exe";
|
||||
} else {
|
||||
return System.getenv(ENV_GNUPLOT_HOME) + "/bin/gnuplot";
|
||||
}
|
||||
}
|
||||
if (isWindows()) {
|
||||
return System.getenv(ENV_GNUPLOT_HOME) + "\\bin\\gnuplot.exe";
|
||||
} else {
|
||||
return System.getenv(ENV_GNUPLOT_HOME) + "/bin/gnuplot";
|
||||
}
|
||||
}
|
||||
|
||||
return "gnuplot";
|
||||
}
|
||||
return "gnuplot";
|
||||
}
|
||||
|
||||
private boolean isWindows() {
|
||||
return System.getProperty("os.name").toLowerCase().startsWith("windows");
|
||||
}
|
||||
private boolean isWindows() {
|
||||
return System.getProperty("os.name").toLowerCase().startsWith("windows");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
package org.lucares.recommind.logs;
|
||||
|
||||
public enum GnuplotAxis {
|
||||
X1("x", "x1"),
|
||||
X1("x", "x1"),
|
||||
|
||||
X2("x2", "x2"),
|
||||
X2("x2", "x2"),
|
||||
|
||||
Y1("y", "y1"),
|
||||
Y1("y", "y1"),
|
||||
|
||||
Y2("y2", "y2");
|
||||
Y2("y2", "y2");
|
||||
|
||||
private String axis;
|
||||
private String axisNameForPlots;
|
||||
private String axis;
|
||||
private String axisNameForPlots;
|
||||
|
||||
private GnuplotAxis(String axis, String axisNameForPlots) {
|
||||
this.axis = axis;
|
||||
this.axisNameForPlots = axisNameForPlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return axis;
|
||||
}
|
||||
|
||||
public String getAxisNameForPlots() {
|
||||
return axisNameForPlots;
|
||||
}
|
||||
|
||||
public GnuplotAxis mirrorAxis() {
|
||||
switch (this) {
|
||||
case X1:
|
||||
return X2;
|
||||
case X2:
|
||||
return X1;
|
||||
case Y1:
|
||||
return Y2;
|
||||
case Y2:
|
||||
return Y1;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + this);
|
||||
private GnuplotAxis(String axis, String axisNameForPlots) {
|
||||
this.axis = axis;
|
||||
this.axisNameForPlots = axisNameForPlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return axis;
|
||||
}
|
||||
|
||||
public String getAxisNameForPlots() {
|
||||
return axisNameForPlots;
|
||||
}
|
||||
|
||||
public GnuplotAxis mirrorAxis() {
|
||||
switch (this) {
|
||||
case X1:
|
||||
return X2;
|
||||
case X2:
|
||||
return X1;
|
||||
case Y1:
|
||||
return Y2;
|
||||
case Y2:
|
||||
return Y1;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected value: " + this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,54 +3,53 @@ package org.lucares.recommind.logs;
|
||||
import java.awt.Color;
|
||||
|
||||
public class GnuplotColor {
|
||||
private final String color; // hex: 00efcc
|
||||
private final String color; // hex: 00efcc
|
||||
|
||||
private GnuplotColor(String color) {
|
||||
this.color = color;
|
||||
}
|
||||
private GnuplotColor(String color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public static GnuplotColor byHex(String aHex) {
|
||||
return new GnuplotColor(aHex);
|
||||
}
|
||||
public static GnuplotColor byHex(String aHex) {
|
||||
return new GnuplotColor(aHex);
|
||||
}
|
||||
|
||||
public static GnuplotColor byAwtColor(Color color) {
|
||||
public static GnuplotColor byAwtColor(Color color) {
|
||||
|
||||
final String hex = String.format("%02x%02x%02x",//
|
||||
color.getRed(),//
|
||||
color.getGreen(),//
|
||||
color.getBlue()//
|
||||
);
|
||||
final String hex = String.format("%02x%02x%02x", //
|
||||
color.getRed(), //
|
||||
color.getGreen(), //
|
||||
color.getBlue()//
|
||||
);
|
||||
|
||||
return new GnuplotColor(hex);
|
||||
}
|
||||
return new GnuplotColor(hex);
|
||||
}
|
||||
|
||||
public String getColor() {
|
||||
return "rgb \"#" + color + "\"";
|
||||
}
|
||||
public String getColor() {
|
||||
return "rgb \"#" + color + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getColor();
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return getColor();
|
||||
}
|
||||
|
||||
Color toAwtColor() {
|
||||
int red = Integer.parseInt(color.substring(0, 2), 16);
|
||||
int green = Integer.parseInt(color.substring(2, 4), 16);
|
||||
int blue = Integer.parseInt(color.substring(4, 6), 16);
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
Color toAwtColor() {
|
||||
int red = Integer.parseInt(color.substring(0, 2), 16);
|
||||
int green = Integer.parseInt(color.substring(2, 4), 16);
|
||||
int blue = Integer.parseInt(color.substring(4, 6), 16);
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
public GnuplotColor brighter() {
|
||||
public GnuplotColor brighter() {
|
||||
|
||||
final Color brighterColor = toAwtColor().brighter();
|
||||
|
||||
|
||||
return byAwtColor(brighterColor);
|
||||
}
|
||||
final Color brighterColor = toAwtColor().brighter();
|
||||
|
||||
public GnuplotColor darker() {
|
||||
return byAwtColor(brighterColor);
|
||||
}
|
||||
|
||||
final Color darkerColor = toAwtColor().darker();
|
||||
return byAwtColor(darkerColor);
|
||||
}
|
||||
public GnuplotColor darker() {
|
||||
|
||||
final Color darkerColor = toAwtColor().darker();
|
||||
return byAwtColor(darkerColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,52 +5,50 @@ import java.util.List;
|
||||
|
||||
public interface GnuplotColorPalettes {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* <div style="background-color: #9400D3; display:inline; padding:
|
||||
* 8px;">#9400D3</div>
|
||||
* <div style="background-color: #009e73; display:inline; padding:
|
||||
* 8px;">#009e73</div>
|
||||
* <div style="background-color: #56b4e9; display:inline; padding:
|
||||
* 8px;">#56b4e9</div>
|
||||
* <div style="background-color: #e69f00; display:inline; padding:
|
||||
* 8px;">#e69f00</div>
|
||||
* <div style="background-color: #f0e442; display:inline; padding:
|
||||
* 8px;">#f0e442</div>
|
||||
* <div style="background-color: #0072b2; display:inline; padding:
|
||||
* 8px;">#0072b2</div>
|
||||
* <div style="background-color: #e51e10; display:inline; padding:
|
||||
* 8px;">#e51e10</div>
|
||||
* <div style="background-color: #FF69B4; display:inline; padding:
|
||||
* 8px;">#FF69B4</div>
|
||||
*/
|
||||
/**
|
||||
*
|
||||
* <div style="background-color: #9400D3; display:inline; padding:
|
||||
* 8px;">#9400D3</div>
|
||||
* <div style="background-color: #009e73; display:inline; padding:
|
||||
* 8px;">#009e73</div>
|
||||
* <div style="background-color: #56b4e9; display:inline; padding:
|
||||
* 8px;">#56b4e9</div>
|
||||
* <div style="background-color: #e69f00; display:inline; padding:
|
||||
* 8px;">#e69f00</div>
|
||||
* <div style="background-color: #f0e442; display:inline; padding:
|
||||
* 8px;">#f0e442</div>
|
||||
* <div style="background-color: #0072b2; display:inline; padding:
|
||||
* 8px;">#0072b2</div>
|
||||
* <div style="background-color: #e51e10; display:inline; padding:
|
||||
* 8px;">#e51e10</div>
|
||||
* <div style="background-color: #FF69B4; display:inline; padding:
|
||||
* 8px;">#FF69B4</div>
|
||||
*/
|
||||
|
||||
List<GnuplotColor> GNUPLOT = Arrays.asList(//
|
||||
GnuplotColor.byHex("9400D3"), // purple
|
||||
GnuplotColor.byHex("009e73"), // green
|
||||
GnuplotColor.byHex("56b4e9"), // light blue
|
||||
GnuplotColor.byHex("e69f00"), // orange
|
||||
GnuplotColor.byHex("f0e442"), // yellow
|
||||
GnuplotColor.byHex("0072b2"), // blue
|
||||
GnuplotColor.byHex("e51e10"), // red
|
||||
GnuplotColor.byHex("FF69B4")// magenta
|
||||
);
|
||||
|
||||
List<GnuplotColor> GNUPLOT_REORDERED = Arrays.asList(//
|
||||
GnuplotColor.byHex("0072b2"), // blue
|
||||
GnuplotColor.byHex("e69f00"), // orange
|
||||
GnuplotColor.byHex("9400D3"), //purple
|
||||
GnuplotColor.byHex("009e73"), //green
|
||||
List<GnuplotColor> GNUPLOT = Arrays.asList(//
|
||||
GnuplotColor.byHex("9400D3"), // purple
|
||||
GnuplotColor.byHex("009e73"), // green
|
||||
GnuplotColor.byHex("56b4e9"), // light blue
|
||||
GnuplotColor.byHex("e69f00"), // orange
|
||||
GnuplotColor.byHex("f0e442"), // yellow
|
||||
GnuplotColor.byHex("0072b2"), // blue
|
||||
GnuplotColor.byHex("e51e10"), // red
|
||||
GnuplotColor.byHex("FF69B4")// magenta
|
||||
);
|
||||
|
||||
List<GnuplotColor> GNUPLOT_REORDERED = Arrays.asList(//
|
||||
GnuplotColor.byHex("0072b2"), // blue
|
||||
GnuplotColor.byHex("e69f00"), // orange
|
||||
GnuplotColor.byHex("9400D3"), // purple
|
||||
GnuplotColor.byHex("009e73"), // green
|
||||
GnuplotColor.byHex("f0e442"), // yellow
|
||||
GnuplotColor.byHex("e51e10"), // red
|
||||
GnuplotColor.byHex("56b4e9"), // lightblue
|
||||
GnuplotColor.byHex("FF69B4")// magenta
|
||||
);
|
||||
|
||||
/**
|
||||
* <div style="background-color: #1f77b4; display:inline; padding:
|
||||
|
||||
/**
|
||||
* <div style="background-color: #1f77b4; display:inline; padding:
|
||||
* 8px;">#1f77b4</div>
|
||||
* <div style="background-color: #ff7f0e; display:inline; padding:
|
||||
* 8px;">#ff7f0e</div>
|
||||
@@ -63,23 +61,21 @@ public interface GnuplotColorPalettes {
|
||||
* <div style="background-color: #b3df72; display:inline; padding:
|
||||
* 8px;">#b3df72</div>
|
||||
* <div style="background-color: #feffbe; display:inline; padding:
|
||||
* 8px;">#feffbe</div>
|
||||
* --
|
||||
* 8px;">#feffbe</div> --
|
||||
* <div style="background-color: #4660ff; display:inline; padding:
|
||||
* 8px;">#4660ff</div>
|
||||
*
|
||||
*
|
||||
*/
|
||||
List<GnuplotColor> MATPLOTLIB = Arrays.asList(//
|
||||
GnuplotColor.byHex("1f77b4"), // blue
|
||||
GnuplotColor.byHex("ff7f0e"), // orange
|
||||
GnuplotColor.byHex("d62728"), // red
|
||||
GnuplotColor.byHex("2ca02c"), // green
|
||||
GnuplotColor.byHex("fdbb6c"), // light orange
|
||||
GnuplotColor.byHex("b3df72"), // light green
|
||||
GnuplotColor.byHex("feffbe")// light yellow
|
||||
);
|
||||
|
||||
|
||||
List<GnuplotColor> DEFAULT = GNUPLOT_REORDERED;
|
||||
*
|
||||
*
|
||||
*/
|
||||
List<GnuplotColor> MATPLOTLIB = Arrays.asList(//
|
||||
GnuplotColor.byHex("1f77b4"), // blue
|
||||
GnuplotColor.byHex("ff7f0e"), // orange
|
||||
GnuplotColor.byHex("d62728"), // red
|
||||
GnuplotColor.byHex("2ca02c"), // green
|
||||
GnuplotColor.byHex("fdbb6c"), // light orange
|
||||
GnuplotColor.byHex("b3df72"), // light green
|
||||
GnuplotColor.byHex("feffbe")// light yellow
|
||||
);
|
||||
|
||||
List<GnuplotColor> DEFAULT = GNUPLOT_REORDERED;
|
||||
}
|
||||
|
||||
@@ -9,72 +9,72 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class GnuplotFileGenerator implements Appender {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GnuplotFileGenerator.class);
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(GnuplotFileGenerator.class);
|
||||
|
||||
private static final int KEY_FONT_SIZE = 10;
|
||||
private static final int KEY_FONT_SIZE = 10;
|
||||
|
||||
public String generate(final GnuplotSettings settings, final Collection<DataSeries> dataSeries) {
|
||||
public String generate(final GnuplotSettings settings, final Collection<DataSeries> dataSeries) {
|
||||
|
||||
final StringBuilder result = new StringBuilder();
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
appendfln(result, "set terminal %s noenhanced size %d,%d", settings.getTerminal(), settings.getWidth(),
|
||||
settings.getHeight());
|
||||
appendfln(result, "set terminal %s noenhanced size %d,%d", settings.getTerminal(), settings.getWidth(),
|
||||
settings.getHeight());
|
||||
|
||||
appendfln(result, "set datafile separator \"%s\"", settings.getDatafileSeparator());
|
||||
appendfln(result, "set timefmt '%s'", settings.getTimefmt());
|
||||
appendfln(result, "set datafile separator \"%s\"", settings.getDatafileSeparator());
|
||||
appendfln(result, "set timefmt '%s'", settings.getTimefmt());
|
||||
|
||||
final List<AxisSettings> xAxisDefinitions = settings.getAggregates().getXAxisDefinitions(settings, dataSeries);
|
||||
for (AxisSettings axisSettings : xAxisDefinitions) {
|
||||
appendln(result, axisSettings.toGnuplotDefinition(settings.isRenderLabels()));
|
||||
final List<AxisSettings> xAxisDefinitions = settings.getAggregates().getXAxisDefinitions(settings, dataSeries);
|
||||
for (AxisSettings axisSettings : xAxisDefinitions) {
|
||||
appendln(result, axisSettings.toGnuplotDefinition(settings.isRenderLabels()));
|
||||
}
|
||||
|
||||
final List<AxisSettings> yAxisDefinitions = settings.getAggregates().getYAxisDefinitions(settings, dataSeries);
|
||||
if (dataSeries.isEmpty()) {
|
||||
// If there is no data, then Gnuplot won't generate an image.
|
||||
// Workaround is to explicitly specify the y-axis range.
|
||||
// We choose a range for which no ticks are defined. This creates an empty
|
||||
// y-axis.
|
||||
yAxisDefinitions.forEach(s -> s.setFrom("0"));
|
||||
yAxisDefinitions.forEach(s -> s.setFrom("-1"));
|
||||
}
|
||||
for (AxisSettings axisSettings : yAxisDefinitions) {
|
||||
appendln(result, axisSettings.toGnuplotDefinition(settings.isRenderLabels()));
|
||||
}
|
||||
|
||||
appendfln(result, "set grid");
|
||||
appendfln(result, "set output \"%s\"", settings.getOutput().toAbsolutePath().toString().replace("\\", "/"));
|
||||
|
||||
appendfln(result, "set key font \",%d\"", KEY_FONT_SIZE);
|
||||
appendfln(result, "set tics font \",%d\"", GnuplotSettings.TICKS_FONT_SIZE);
|
||||
|
||||
if (!settings.isRenderLabels()) {
|
||||
|
||||
appendln(result, "set nokey");
|
||||
} else {
|
||||
if (settings.isKeyOutside()) {
|
||||
appendfln(result, "set key outside");
|
||||
} else {
|
||||
|
||||
// make sure left and right margins are always the same
|
||||
// this is need to be able to zoom in by selecting a region
|
||||
// (horizontal: 1 unit = 10px; vertical: 1 unit = 19px)
|
||||
appendln(result, "set lmargin 11"); // margin 11 -> 110px
|
||||
appendln(result, "set rmargin 11"); // margin 11 -> 110px
|
||||
appendln(result, "set tmargin 3"); // margin 3 -> 57px - marker (1)
|
||||
appendln(result, "set bmargin 4"); // margin 4 -> 76
|
||||
}
|
||||
}
|
||||
|
||||
appendf(result, "plot ");
|
||||
|
||||
settings.getAggregates().addPlots(result, dataSeries);
|
||||
|
||||
// Add a plot outside of the visible range. Without this gnuplot would not
|
||||
// render images when there are not data points on it.
|
||||
appendf(result, "-1 with lines notitle");
|
||||
|
||||
LOGGER.info("{}", result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
final List<AxisSettings> yAxisDefinitions = settings.getAggregates().getYAxisDefinitions(settings, dataSeries);
|
||||
if (dataSeries.isEmpty()) {
|
||||
// If there is no data, then Gnuplot won't generate an image.
|
||||
// Workaround is to explicitly specify the y-axis range.
|
||||
// We choose a range for which no ticks are defined. This creates an empty
|
||||
// y-axis.
|
||||
yAxisDefinitions.forEach(s -> s.setFrom("0"));
|
||||
yAxisDefinitions.forEach(s -> s.setFrom("-1"));
|
||||
}
|
||||
for (AxisSettings axisSettings : yAxisDefinitions) {
|
||||
appendln(result, axisSettings.toGnuplotDefinition(settings.isRenderLabels()));
|
||||
}
|
||||
|
||||
appendfln(result, "set grid");
|
||||
appendfln(result, "set output \"%s\"", settings.getOutput().toAbsolutePath().toString().replace("\\", "/"));
|
||||
|
||||
appendfln(result, "set key font \",%d\"", KEY_FONT_SIZE);
|
||||
appendfln(result, "set tics font \",%d\"", GnuplotSettings.TICKS_FONT_SIZE);
|
||||
|
||||
if (!settings.isRenderLabels()) {
|
||||
|
||||
appendln(result, "set nokey");
|
||||
} else {
|
||||
if (settings.isKeyOutside()) {
|
||||
appendfln(result, "set key outside");
|
||||
} else {
|
||||
|
||||
// make sure left and right margins are always the same
|
||||
// this is need to be able to zoom in by selecting a region
|
||||
// (horizontal: 1 unit = 10px; vertical: 1 unit = 19px)
|
||||
appendln(result, "set lmargin 11"); // margin 11 -> 110px
|
||||
appendln(result, "set rmargin 11"); // margin 11 -> 110px
|
||||
appendln(result, "set tmargin 3"); // margin 3 -> 57px - marker (1)
|
||||
appendln(result, "set bmargin 4"); // margin 4 -> 76
|
||||
}
|
||||
}
|
||||
|
||||
appendf(result, "plot ");
|
||||
|
||||
settings.getAggregates().addPlots(result, dataSeries);
|
||||
|
||||
// Add a plot outside of the visible range. Without this gnuplot would not
|
||||
// render images when there are not data points on it.
|
||||
appendf(result, "-1 with lines notitle");
|
||||
|
||||
LOGGER.info("{}", result);
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
package org.lucares.recommind.logs;
|
||||
|
||||
public enum GnuplotLineType {
|
||||
LINE("line"),
|
||||
|
||||
Points("points");
|
||||
|
||||
private String gnuplotLineType;
|
||||
LINE("line"),
|
||||
|
||||
GnuplotLineType(String gnuplotLineType) {
|
||||
this.gnuplotLineType = gnuplotLineType;
|
||||
}
|
||||
Points("points");
|
||||
|
||||
private String gnuplotLineType;
|
||||
|
||||
GnuplotLineType(String gnuplotLineType) {
|
||||
this.gnuplotLineType = gnuplotLineType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return gnuplotLineType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return gnuplotLineType;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -8,148 +8,148 @@ import org.lucares.pdb.plot.api.AxisScale;
|
||||
|
||||
public class GnuplotSettings {
|
||||
|
||||
public final static int GNUPLOT_LEFT_MARGIN = 110; // The left margin configured for gnuplot
|
||||
public final static int GNUPLOT_RIGHT_MARGIN = 110; // The right margin configured for gnuplot
|
||||
public final static int GNUPLOT_TOP_MARGIN = 57; // The top margin configured for gnuplot
|
||||
public final static int GNUPLOT_BOTTOM_MARGIN = 76; // The bottom margin configured for gnuplot
|
||||
public final static int GNUPLOT_TOP_BOTTOM_MARGIN = GNUPLOT_TOP_MARGIN + GNUPLOT_BOTTOM_MARGIN;
|
||||
public final static int GNUPLOT_LEFT_RIGHT_MARGIN = GNUPLOT_LEFT_MARGIN+GNUPLOT_RIGHT_MARGIN;
|
||||
public static final int TICKS_FONT_SIZE = 12;
|
||||
public final static int GNUPLOT_LEFT_MARGIN = 110; // The left margin configured for gnuplot
|
||||
public final static int GNUPLOT_RIGHT_MARGIN = 110; // The right margin configured for gnuplot
|
||||
public final static int GNUPLOT_TOP_MARGIN = 57; // The top margin configured for gnuplot
|
||||
public final static int GNUPLOT_BOTTOM_MARGIN = 76; // The bottom margin configured for gnuplot
|
||||
public final static int GNUPLOT_TOP_BOTTOM_MARGIN = GNUPLOT_TOP_MARGIN + GNUPLOT_BOTTOM_MARGIN;
|
||||
public final static int GNUPLOT_LEFT_RIGHT_MARGIN = GNUPLOT_LEFT_MARGIN + GNUPLOT_RIGHT_MARGIN;
|
||||
public static final int TICKS_FONT_SIZE = 12;
|
||||
|
||||
private String terminal = "png";
|
||||
private int height = 1200;
|
||||
private int width = 1600;
|
||||
private String timefmt = "%s"; // time as unix epoch, but as double
|
||||
private String terminal = "png";
|
||||
private int height = 1200;
|
||||
private int width = 1600;
|
||||
private String timefmt = "%s"; // time as unix epoch, but as double
|
||||
|
||||
// set datafile separator
|
||||
private String datafileSeparator = ",";
|
||||
// set datafile separator
|
||||
private String datafileSeparator = ",";
|
||||
|
||||
// set output "datausage.png"
|
||||
private final Path output;
|
||||
// set output "datausage.png"
|
||||
private final Path output;
|
||||
|
||||
private AxisScale yAxisScale;
|
||||
private AggregateHandlerCollection aggregates;
|
||||
private boolean keyOutside = false;
|
||||
private AxisScale yAxisScale;
|
||||
private AggregateHandlerCollection aggregates;
|
||||
private boolean keyOutside = false;
|
||||
|
||||
private AxisSettings xAxisSettings = new AxisSettings();
|
||||
private boolean renderLabels = true;
|
||||
private int yRangeMin = -1;
|
||||
private int yRangeMax = -1;
|
||||
private DateTimeRange dateTimeRange;
|
||||
private AxisSettings xAxisSettings = new AxisSettings();
|
||||
private boolean renderLabels = true;
|
||||
private int yRangeMin = -1;
|
||||
private int yRangeMax = -1;
|
||||
private DateTimeRange dateTimeRange;
|
||||
|
||||
public GnuplotSettings(final Path output) {
|
||||
this.output = output;
|
||||
}
|
||||
public GnuplotSettings(final Path output) {
|
||||
this.output = output;
|
||||
}
|
||||
|
||||
public AxisSettings getxAxisSettings() {
|
||||
return xAxisSettings;
|
||||
}
|
||||
public AxisSettings getxAxisSettings() {
|
||||
return xAxisSettings;
|
||||
}
|
||||
|
||||
public void setxAxisSettings(final AxisSettings xAxisSettings) {
|
||||
this.xAxisSettings = xAxisSettings;
|
||||
}
|
||||
public void setxAxisSettings(final AxisSettings xAxisSettings) {
|
||||
this.xAxisSettings = xAxisSettings;
|
||||
}
|
||||
|
||||
public String getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
public String getTerminal() {
|
||||
return terminal;
|
||||
}
|
||||
|
||||
public void setTerminal(final String terminal) {
|
||||
this.terminal = terminal;
|
||||
}
|
||||
public void setTerminal(final String terminal) {
|
||||
this.terminal = terminal;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(final int height) {
|
||||
this.height = height;
|
||||
}
|
||||
public void setHeight(final int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(final int width) {
|
||||
this.width = width;
|
||||
}
|
||||
public void setWidth(final int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public String getTimefmt() {
|
||||
return timefmt;
|
||||
}
|
||||
public String getTimefmt() {
|
||||
return timefmt;
|
||||
}
|
||||
|
||||
public void setTimefmt(final String timefmt) {
|
||||
this.timefmt = timefmt;
|
||||
}
|
||||
public void setTimefmt(final String timefmt) {
|
||||
this.timefmt = timefmt;
|
||||
}
|
||||
|
||||
public String getDatafileSeparator() {
|
||||
return datafileSeparator;
|
||||
}
|
||||
public String getDatafileSeparator() {
|
||||
return datafileSeparator;
|
||||
}
|
||||
|
||||
public void setDatafileSeparator(final String datafileSeparator) {
|
||||
this.datafileSeparator = datafileSeparator;
|
||||
}
|
||||
public void setDatafileSeparator(final String datafileSeparator) {
|
||||
this.datafileSeparator = datafileSeparator;
|
||||
}
|
||||
|
||||
public Path getOutput() {
|
||||
return output;
|
||||
}
|
||||
public Path getOutput() {
|
||||
return output;
|
||||
}
|
||||
|
||||
public void setYAxisScale(final AxisScale yAxisScale) {
|
||||
this.yAxisScale = yAxisScale;
|
||||
}
|
||||
public void setYAxisScale(final AxisScale yAxisScale) {
|
||||
this.yAxisScale = yAxisScale;
|
||||
}
|
||||
|
||||
public AxisScale getYAxisScale() {
|
||||
return yAxisScale;
|
||||
}
|
||||
public AxisScale getYAxisScale() {
|
||||
return yAxisScale;
|
||||
}
|
||||
|
||||
public void setAggregates(final AggregateHandlerCollection aggregates) {
|
||||
this.aggregates = aggregates;
|
||||
}
|
||||
public void setAggregates(final AggregateHandlerCollection aggregates) {
|
||||
this.aggregates = aggregates;
|
||||
}
|
||||
|
||||
public AggregateHandlerCollection getAggregates() {
|
||||
return aggregates;
|
||||
}
|
||||
public AggregateHandlerCollection getAggregates() {
|
||||
return aggregates;
|
||||
}
|
||||
|
||||
public void setKeyOutside(final boolean keyOutside) {
|
||||
this.keyOutside = keyOutside;
|
||||
}
|
||||
public void setKeyOutside(final boolean keyOutside) {
|
||||
this.keyOutside = keyOutside;
|
||||
}
|
||||
|
||||
public boolean isKeyOutside() {
|
||||
return keyOutside;
|
||||
}
|
||||
public boolean isKeyOutside() {
|
||||
return keyOutside;
|
||||
}
|
||||
|
||||
public void renderLabels(final boolean renderLabels) {
|
||||
this.renderLabels = renderLabels;
|
||||
}
|
||||
public void renderLabels(final boolean renderLabels) {
|
||||
this.renderLabels = renderLabels;
|
||||
}
|
||||
|
||||
public boolean isRenderLabels() {
|
||||
return renderLabels;
|
||||
}
|
||||
public boolean isRenderLabels() {
|
||||
return renderLabels;
|
||||
}
|
||||
|
||||
public boolean hasYRange() {
|
||||
return yRangeMin >= 0 && yRangeMax >= 0 && yRangeMin < yRangeMax;
|
||||
}
|
||||
public boolean hasYRange() {
|
||||
return yRangeMin >= 0 && yRangeMax >= 0 && yRangeMin < yRangeMax;
|
||||
}
|
||||
|
||||
public void setYRange(final int yRangeMin, final int yRangeMax) {
|
||||
this.yRangeMin = yRangeMin;
|
||||
this.yRangeMax = yRangeMax;
|
||||
}
|
||||
public void setYRange(final int yRangeMin, final int yRangeMax) {
|
||||
this.yRangeMin = yRangeMin;
|
||||
this.yRangeMax = yRangeMax;
|
||||
}
|
||||
|
||||
public int getYRangeMin() {
|
||||
return yRangeMin;
|
||||
}
|
||||
public int getYRangeMin() {
|
||||
return yRangeMin;
|
||||
}
|
||||
|
||||
public int getYRangeMax() {
|
||||
return yRangeMax;
|
||||
}
|
||||
public int getYRangeMax() {
|
||||
return yRangeMax;
|
||||
}
|
||||
|
||||
public void setDateTimeRange(DateTimeRange dateTimeRange) {
|
||||
this.dateTimeRange = dateTimeRange;
|
||||
}
|
||||
|
||||
public DateTimeRange getDateTimeRange() {
|
||||
return dateTimeRange;
|
||||
}
|
||||
public void setDateTimeRange(DateTimeRange dateTimeRange) {
|
||||
this.dateTimeRange = dateTimeRange;
|
||||
}
|
||||
|
||||
// plot 'sample.txt' using 1:2 title 'Bytes' with linespoints 2
|
||||
public DateTimeRange getDateTimeRange() {
|
||||
return dateTimeRange;
|
||||
}
|
||||
|
||||
// plot 'sample.txt' using 1:2 title 'Bytes' with linespoints 2
|
||||
|
||||
}
|
||||
|
||||
@@ -2,13 +2,13 @@ package org.lucares.recommind.logs;
|
||||
|
||||
public class InternalPlottingException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public InternalPlottingException() {
|
||||
super();
|
||||
}
|
||||
public InternalPlottingException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public InternalPlottingException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
public InternalPlottingException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,75 +5,75 @@ import java.io.Writer;
|
||||
|
||||
import org.lucares.pdb.api.RuntimeIOException;
|
||||
|
||||
public class LambdaFriendlyWriter extends Writer{
|
||||
public class LambdaFriendlyWriter extends Writer {
|
||||
|
||||
private final Writer writer;
|
||||
|
||||
public LambdaFriendlyWriter(Writer writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
private final Writer writer;
|
||||
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) {
|
||||
try {
|
||||
writer.write(cbuf, off, len);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int c) {
|
||||
try {
|
||||
writer.write(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String str) {
|
||||
try {
|
||||
writer.write(str);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer append(CharSequence csq) {
|
||||
try {
|
||||
return writer.append(csq);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer append(char c) {
|
||||
try {
|
||||
return writer.append(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
public LambdaFriendlyWriter(Writer writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) {
|
||||
try {
|
||||
writer.write(cbuf, off, len);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void write(int c) {
|
||||
try {
|
||||
writer.write(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String str) {
|
||||
try {
|
||||
writer.write(str);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer append(CharSequence csq) {
|
||||
try {
|
||||
return writer.append(csq);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer append(char c) {
|
||||
try {
|
||||
return writer.append(c);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() {
|
||||
try {
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
writer.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,31 +2,31 @@ package org.lucares.recommind.logs;
|
||||
|
||||
public class LineStyle {
|
||||
|
||||
private final GnuplotColor color;
|
||||
private final DashTypes dashType;
|
||||
private final GnuplotColor color;
|
||||
private final DashTypes dashType;
|
||||
|
||||
public LineStyle(final GnuplotColor color, final DashTypes dashType) {
|
||||
this.color = color;
|
||||
this.dashType = dashType;
|
||||
}
|
||||
public LineStyle(final GnuplotColor color, final DashTypes dashType) {
|
||||
this.color = color;
|
||||
this.dashType = dashType;
|
||||
}
|
||||
|
||||
public String asGnuplotLineStyle() {
|
||||
return String.format("lt %s dt %s ", //
|
||||
color.getColor(), //
|
||||
dashType.toGnuplotDashType()//
|
||||
);
|
||||
}
|
||||
public String asGnuplotLineStyle() {
|
||||
return String.format("lt %s dt %s ", //
|
||||
color.getColor(), //
|
||||
dashType.toGnuplotDashType()//
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return asGnuplotLineStyle();
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return asGnuplotLineStyle();
|
||||
}
|
||||
|
||||
public LineStyle brighter() {
|
||||
return new LineStyle(color.brighter(), dashType);
|
||||
}
|
||||
public LineStyle brighter() {
|
||||
return new LineStyle(color.brighter(), dashType);
|
||||
}
|
||||
|
||||
public LineStyle darker() {
|
||||
return new LineStyle(color.darker(), dashType);
|
||||
}
|
||||
public LineStyle darker() {
|
||||
return new LineStyle(color.darker(), dashType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,23 @@ package org.lucares.recommind.logs;
|
||||
|
||||
public class LongUtils {
|
||||
|
||||
private static final int INT_TO_STRING_CACHE_SIZE = 10_000;
|
||||
private static final String[] INT_TO_STRING;
|
||||
static {
|
||||
private static final int INT_TO_STRING_CACHE_SIZE = 10_000;
|
||||
private static final String[] INT_TO_STRING;
|
||||
static {
|
||||
|
||||
INT_TO_STRING = new String[INT_TO_STRING_CACHE_SIZE];
|
||||
INT_TO_STRING = new String[INT_TO_STRING_CACHE_SIZE];
|
||||
|
||||
for (int i = 0; i < INT_TO_STRING_CACHE_SIZE; i++) {
|
||||
INT_TO_STRING[i] = String.valueOf(i);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < INT_TO_STRING_CACHE_SIZE; i++) {
|
||||
INT_TO_STRING[i] = String.valueOf(i);
|
||||
}
|
||||
}
|
||||
|
||||
public static String longToString(final long value) {
|
||||
// using pre-generated strings reduces memory allocation by up to 25%
|
||||
public static String longToString(final long value) {
|
||||
// using pre-generated strings reduces memory allocation by up to 25%
|
||||
|
||||
if (value < INT_TO_STRING_CACHE_SIZE) {
|
||||
return INT_TO_STRING[(int) value];
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
if (value < INT_TO_STRING_CACHE_SIZE) {
|
||||
return INT_TO_STRING[(int) value];
|
||||
}
|
||||
return String.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package org.lucares.recommind.logs;
|
||||
|
||||
public class NoDataPointsException extends InternalPlottingException {
|
||||
|
||||
private static final long serialVersionUID = 1054594230615520105L;
|
||||
private static final long serialVersionUID = 1054594230615520105L;
|
||||
|
||||
public NoDataPointsException() {
|
||||
super();
|
||||
}
|
||||
public NoDataPointsException() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,34 +4,34 @@ import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class PlotResult {
|
||||
private final Path imagePath;
|
||||
private final List<DataSeries> dataSeries;
|
||||
private final Path thumbnail;
|
||||
private final Path imagePath;
|
||||
private final List<DataSeries> dataSeries;
|
||||
private final Path thumbnail;
|
||||
|
||||
public PlotResult(final Path imagePath, final List<DataSeries> dataSeries, final Path thumbnail) {
|
||||
super();
|
||||
this.imagePath = imagePath;
|
||||
this.dataSeries = dataSeries;
|
||||
this.thumbnail = thumbnail;
|
||||
}
|
||||
public PlotResult(final Path imagePath, final List<DataSeries> dataSeries, final Path thumbnail) {
|
||||
super();
|
||||
this.imagePath = imagePath;
|
||||
this.dataSeries = dataSeries;
|
||||
this.thumbnail = thumbnail;
|
||||
}
|
||||
|
||||
public Path getImageName() {
|
||||
return imagePath.getFileName();
|
||||
}
|
||||
public Path getImageName() {
|
||||
return imagePath.getFileName();
|
||||
}
|
||||
|
||||
public Path getImagePath() {
|
||||
return imagePath;
|
||||
}
|
||||
public Path getImagePath() {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
public Path getThumbnailName() {
|
||||
return thumbnail.getFileName();
|
||||
}
|
||||
public Path getThumbnailName() {
|
||||
return thumbnail.getFileName();
|
||||
}
|
||||
|
||||
public Path getThumbnailPath() {
|
||||
return thumbnail;
|
||||
}
|
||||
public Path getThumbnailPath() {
|
||||
return thumbnail;
|
||||
}
|
||||
|
||||
public List<DataSeries> getDataSeries() {
|
||||
return dataSeries;
|
||||
}
|
||||
public List<DataSeries> getDataSeries() {
|
||||
return dataSeries;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,234 +32,229 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Plotter {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Plotter.class);
|
||||
private static final Logger METRICS_LOGGER = LoggerFactory.getLogger("org.lucares.metrics.plotter.scatter");
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Plotter.class);
|
||||
private static final Logger METRICS_LOGGER = LoggerFactory.getLogger("org.lucares.metrics.plotter.scatter");
|
||||
|
||||
static final String DEFAULT_GROUP = "<none>";
|
||||
static final String DEFAULT_GROUP = "<none>";
|
||||
|
||||
private final PerformanceDb db;
|
||||
private final Path tmpBaseDir;
|
||||
private final Path outputDir;
|
||||
private final PerformanceDb db;
|
||||
private final Path tmpBaseDir;
|
||||
private final Path outputDir;
|
||||
|
||||
public Plotter(final PerformanceDb db, final Path tmpBaseDir, final Path outputDir) {
|
||||
this.db = db;
|
||||
this.tmpBaseDir = tmpBaseDir;
|
||||
this.outputDir = outputDir;
|
||||
public Plotter(final PerformanceDb db, final Path tmpBaseDir, final Path outputDir) {
|
||||
this.db = db;
|
||||
this.tmpBaseDir = tmpBaseDir;
|
||||
this.outputDir = outputDir;
|
||||
|
||||
if (!Files.isDirectory(tmpBaseDir, LinkOption.NOFOLLOW_LINKS)) {
|
||||
throw new IllegalArgumentException(tmpBaseDir + " is not a directory");
|
||||
if (!Files.isDirectory(tmpBaseDir, LinkOption.NOFOLLOW_LINKS)) {
|
||||
throw new IllegalArgumentException(tmpBaseDir + " is not a directory");
|
||||
}
|
||||
if (!Files.isDirectory(outputDir)) {
|
||||
throw new IllegalArgumentException(outputDir + " is not a directory");
|
||||
}
|
||||
}
|
||||
if (!Files.isDirectory(outputDir)) {
|
||||
throw new IllegalArgumentException(outputDir + " is not a directory");
|
||||
|
||||
public Path getOutputDir() {
|
||||
return outputDir;
|
||||
}
|
||||
}
|
||||
|
||||
public Path getOutputDir() {
|
||||
return outputDir;
|
||||
}
|
||||
public PlotResult plot(final PlotSettings plotSettings) throws InternalPlottingException {
|
||||
|
||||
public PlotResult plot(final PlotSettings plotSettings) throws InternalPlottingException {
|
||||
LOGGER.trace("start plot: {}", plotSettings);
|
||||
|
||||
LOGGER.trace("start plot: {}", plotSettings);
|
||||
|
||||
final String tmpSubDir = uniqueDirectoryName();
|
||||
final Path tmpDir = tmpBaseDir.resolve(tmpSubDir);
|
||||
try {
|
||||
Files.createDirectories(tmpDir);
|
||||
final List<DataSeries> dataSeries = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
final String query = plotSettings.getQuery();
|
||||
final List<String> groupBy = plotSettings.getGroupBy();
|
||||
final int height = plotSettings.getHeight();
|
||||
final int width = plotSettings.getWidth();
|
||||
final DateTimeRange dateRange = plotSettings.dateRange();
|
||||
final OffsetDateTime dateFrom = dateRange.getStart();
|
||||
final OffsetDateTime dateTo = dateRange.getEnd();
|
||||
|
||||
final Result result = db.get(new Query(query, dateRange), groupBy);
|
||||
|
||||
final long start = System.nanoTime();
|
||||
final AtomicInteger idCounter = new AtomicInteger(0);
|
||||
result.getGroups().stream().parallel().forEach(groupResult -> {
|
||||
final String tmpSubDir = uniqueDirectoryName();
|
||||
final Path tmpDir = tmpBaseDir.resolve(tmpSubDir);
|
||||
try {
|
||||
final CsvSummary csvSummary = toCsvDeduplicated(groupResult, tmpDir, dateFrom, dateTo, plotSettings);
|
||||
Files.createDirectories(tmpDir);
|
||||
final List<DataSeries> dataSeries = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
final int id = idCounter.incrementAndGet();
|
||||
final String title = title(groupResult.getGroupedBy(), csvSummary);
|
||||
final DataSeries dataSerie = new FileBackedDataSeries(id, title, csvSummary);
|
||||
if (dataSerie.getValues() > 0) {
|
||||
dataSeries.add(dataSerie);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
final String query = plotSettings.getQuery();
|
||||
final List<String> groupBy = plotSettings.getGroupBy();
|
||||
final int height = plotSettings.getHeight();
|
||||
final int width = plotSettings.getWidth();
|
||||
final DateTimeRange dateRange = plotSettings.dateRange();
|
||||
final OffsetDateTime dateFrom = dateRange.getStart();
|
||||
final OffsetDateTime dateTo = dateRange.getEnd();
|
||||
|
||||
final Result result = db.get(new Query(query, dateRange), groupBy);
|
||||
|
||||
final long start = System.nanoTime();
|
||||
final AtomicInteger idCounter = new AtomicInteger(0);
|
||||
result.getGroups().stream().parallel().forEach(groupResult -> {
|
||||
try {
|
||||
final CsvSummary csvSummary = toCsvDeduplicated(groupResult, tmpDir, dateFrom, dateTo,
|
||||
plotSettings);
|
||||
|
||||
final int id = idCounter.incrementAndGet();
|
||||
final String title = title(groupResult.getGroupedBy(), csvSummary);
|
||||
final DataSeries dataSerie = new FileBackedDataSeries(id, title, csvSummary);
|
||||
if (dataSerie.getValues() > 0) {
|
||||
dataSeries.add(dataSerie);
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
});
|
||||
METRICS_LOGGER.debug("csv generation took: " + (System.nanoTime() - start) / 1_000_000.0 + "ms");
|
||||
|
||||
final Limit limitBy = plotSettings.getLimitBy();
|
||||
final int limit = plotSettings.getLimit();
|
||||
DataSeries.sortAndLimit(dataSeries, limitBy, limit);
|
||||
DataSeries.setColors(dataSeries);
|
||||
|
||||
final Path outputFile = Files.createTempFile(outputDir, "", ".png");
|
||||
{
|
||||
final Gnuplot gnuplot = new Gnuplot(tmpBaseDir);
|
||||
final GnuplotSettings gnuplotSettings = new GnuplotSettings(outputFile);
|
||||
gnuplotSettings.setHeight(height);
|
||||
gnuplotSettings.setWidth(width);
|
||||
gnuplotSettings.setDateTimeRange(plotSettings.dateRange());
|
||||
|
||||
gnuplotSettings.setYAxisScale(plotSettings.getYAxisScale());
|
||||
gnuplotSettings.setAggregates(plotSettings.getAggregates());
|
||||
defineYRange(gnuplotSettings, plotSettings.getYRangeMin(), plotSettings.getYRangeMax(),
|
||||
plotSettings.getYRangeUnit());
|
||||
gnuplotSettings.setKeyOutside(plotSettings.isKeyOutside());
|
||||
gnuplot.plot(gnuplotSettings, dataSeries);
|
||||
}
|
||||
|
||||
final Path thumbnail;
|
||||
if (plotSettings.isGenerateThumbnail()) {
|
||||
thumbnail = Files.createTempFile(outputDir, "", ".png");
|
||||
final Gnuplot gnuplot = new Gnuplot(tmpBaseDir);
|
||||
final GnuplotSettings gnuplotSettings = new GnuplotSettings(thumbnail);
|
||||
gnuplotSettings.setHeight(plotSettings.getThumbnailMaxHeight());
|
||||
gnuplotSettings.setWidth(plotSettings.getThumbnailMaxWidth());
|
||||
gnuplotSettings.setDateTimeRange(plotSettings.dateRange());
|
||||
|
||||
gnuplotSettings.setYAxisScale(plotSettings.getYAxisScale());
|
||||
gnuplotSettings.setAggregates(plotSettings.getAggregates());
|
||||
defineYRange(gnuplotSettings, plotSettings.getYRangeMin(), plotSettings.getYRangeMax(),
|
||||
plotSettings.getYRangeUnit());
|
||||
gnuplotSettings.setKeyOutside(false);
|
||||
gnuplotSettings.renderLabels(false);
|
||||
gnuplot.plot(gnuplotSettings, dataSeries);
|
||||
} else {
|
||||
thumbnail = null;
|
||||
}
|
||||
|
||||
return new PlotResult(outputFile, dataSeries, thumbnail);
|
||||
} catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException("Plotting was interrupted.");
|
||||
} catch (final IOException e) {
|
||||
throw new InternalPlottingException("Plotting failed: " + e.getMessage(), e);
|
||||
} finally {
|
||||
FileUtils.delete(tmpDir);
|
||||
LOGGER.trace("done plot");
|
||||
}
|
||||
});
|
||||
METRICS_LOGGER.debug("csv generation took: " + (System.nanoTime() - start) / 1_000_000.0 + "ms");
|
||||
|
||||
final Limit limitBy = plotSettings.getLimitBy();
|
||||
final int limit = plotSettings.getLimit();
|
||||
DataSeries.sortAndLimit(dataSeries, limitBy, limit);
|
||||
DataSeries.setColors(dataSeries);
|
||||
|
||||
final Path outputFile = Files.createTempFile(outputDir, "", ".png");
|
||||
{
|
||||
final Gnuplot gnuplot = new Gnuplot(tmpBaseDir);
|
||||
final GnuplotSettings gnuplotSettings = new GnuplotSettings(outputFile);
|
||||
gnuplotSettings.setHeight(height);
|
||||
gnuplotSettings.setWidth(width);
|
||||
gnuplotSettings.setDateTimeRange(plotSettings.dateRange());
|
||||
|
||||
gnuplotSettings.setYAxisScale(plotSettings.getYAxisScale());
|
||||
gnuplotSettings.setAggregates(plotSettings.getAggregates());
|
||||
defineYRange(gnuplotSettings, plotSettings.getYRangeMin(), plotSettings.getYRangeMax(),
|
||||
plotSettings.getYRangeUnit());
|
||||
gnuplotSettings.setKeyOutside(plotSettings.isKeyOutside());
|
||||
gnuplot.plot(gnuplotSettings, dataSeries);
|
||||
}
|
||||
|
||||
final Path thumbnail;
|
||||
if (plotSettings.isGenerateThumbnail()) {
|
||||
thumbnail = Files.createTempFile(outputDir, "", ".png");
|
||||
final Gnuplot gnuplot = new Gnuplot(tmpBaseDir);
|
||||
final GnuplotSettings gnuplotSettings = new GnuplotSettings(thumbnail);
|
||||
gnuplotSettings.setHeight(plotSettings.getThumbnailMaxHeight());
|
||||
gnuplotSettings.setWidth(plotSettings.getThumbnailMaxWidth());
|
||||
gnuplotSettings.setDateTimeRange(plotSettings.dateRange());
|
||||
|
||||
gnuplotSettings.setYAxisScale(plotSettings.getYAxisScale());
|
||||
gnuplotSettings.setAggregates(plotSettings.getAggregates());
|
||||
defineYRange(gnuplotSettings, plotSettings.getYRangeMin(), plotSettings.getYRangeMax(),
|
||||
plotSettings.getYRangeUnit());
|
||||
gnuplotSettings.setKeyOutside(false);
|
||||
gnuplotSettings.renderLabels(false);
|
||||
gnuplot.plot(gnuplotSettings, dataSeries);
|
||||
} else {
|
||||
thumbnail = null;
|
||||
}
|
||||
|
||||
return new PlotResult(outputFile, dataSeries, thumbnail);
|
||||
} catch (final InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new IllegalStateException("Plotting was interrupted.");
|
||||
} catch (final IOException e) {
|
||||
throw new InternalPlottingException("Plotting failed: " + e.getMessage(), e);
|
||||
} finally {
|
||||
FileUtils.delete(tmpDir);
|
||||
LOGGER.trace("done plot");
|
||||
}
|
||||
}
|
||||
|
||||
private void defineYRange(final GnuplotSettings gnuplotSettings, final int yRangeMin, final int yRangeMax,
|
||||
final TimeRangeUnitInternal yRangeUnit) {
|
||||
private void defineYRange(final GnuplotSettings gnuplotSettings, final int yRangeMin, final int yRangeMax,
|
||||
final TimeRangeUnitInternal yRangeUnit) {
|
||||
|
||||
if (yRangeUnit != TimeRangeUnitInternal.AUTOMATIC) {
|
||||
final int min = yRangeUnit.toMilliSeconds(yRangeMin);
|
||||
final int max = yRangeUnit.toMilliSeconds(yRangeMax);
|
||||
gnuplotSettings.setYRange(min, max);
|
||||
if (yRangeUnit != TimeRangeUnitInternal.AUTOMATIC) {
|
||||
final int min = yRangeUnit.toMilliSeconds(yRangeMin);
|
||||
final int max = yRangeUnit.toMilliSeconds(yRangeMax);
|
||||
gnuplotSettings.setYRange(min, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static CsvSummary toCsvDeduplicated(final GroupResult groupResult, final Path tmpDir,
|
||||
final OffsetDateTime dateFrom, final OffsetDateTime dateTo, final PlotSettings plotSettings)
|
||||
throws IOException {
|
||||
|
||||
private static CsvSummary toCsvDeduplicated(final GroupResult groupResult, final Path tmpDir,
|
||||
final OffsetDateTime dateFrom, final OffsetDateTime dateTo, final PlotSettings plotSettings) throws IOException {
|
||||
final long start = System.nanoTime();
|
||||
final Stream<LongList> timeValueStream = groupResult.asStream();
|
||||
final long fromEpochMilli = dateFrom.toInstant().toEpochMilli();
|
||||
final long toEpochMilli = dateTo.toInstant().toEpochMilli();
|
||||
final boolean useMillis = (toEpochMilli - fromEpochMilli) < TimeUnit.MINUTES.toMillis(5);
|
||||
|
||||
final long start = System.nanoTime();
|
||||
final Stream<LongList> timeValueStream = groupResult.asStream();
|
||||
final long fromEpochMilli = dateFrom.toInstant().toEpochMilli();
|
||||
final long toEpochMilli = dateTo.toInstant().toEpochMilli();
|
||||
final boolean useMillis = (toEpochMilli - fromEpochMilli) < TimeUnit.MINUTES.toMillis(5);
|
||||
final long minValue = plotSettings.getYRangeUnit() == TimeRangeUnitInternal.AUTOMATIC ? 0
|
||||
: plotSettings.getYRangeUnit().toMilliSeconds(plotSettings.getYRangeMin());
|
||||
final long maxValue = plotSettings.getYRangeUnit() == TimeRangeUnitInternal.AUTOMATIC ? Long.MAX_VALUE
|
||||
: plotSettings.getYRangeUnit().toMilliSeconds(plotSettings.getYRangeMax());
|
||||
|
||||
final long minValue = plotSettings.getYRangeUnit() == TimeRangeUnitInternal.AUTOMATIC ? 0
|
||||
: plotSettings.getYRangeUnit().toMilliSeconds(plotSettings.getYRangeMin());
|
||||
final long maxValue = plotSettings.getYRangeUnit() == TimeRangeUnitInternal.AUTOMATIC ? Long.MAX_VALUE
|
||||
: plotSettings.getYRangeUnit().toMilliSeconds(plotSettings.getYRangeMax());
|
||||
final AggregatorCollection aggregator = plotSettings.getAggregates().createCustomAggregator(tmpDir,
|
||||
plotSettings, fromEpochMilli, toEpochMilli);
|
||||
|
||||
final AggregatorCollection aggregator = plotSettings.getAggregates().createCustomAggregator(tmpDir, plotSettings, fromEpochMilli,
|
||||
toEpochMilli);
|
||||
int count = 0; // number of values in the x-axis range (used to compute stats)
|
||||
int plottedValues = 0;
|
||||
long statsMaxValue = 0;
|
||||
double statsCurrentAverage = 0.0;
|
||||
long ignoredValues = 0;
|
||||
|
||||
int count = 0; // number of values in the x-axis range (used to compute stats)
|
||||
int plottedValues = 0;
|
||||
long statsMaxValue = 0;
|
||||
double statsCurrentAverage = 0.0;
|
||||
long ignoredValues = 0;
|
||||
final Iterator<LongList> it = timeValueStream.iterator();
|
||||
while (it.hasNext()) {
|
||||
final LongList entry = it.next();
|
||||
|
||||
final Iterator<LongList> it = timeValueStream.iterator();
|
||||
while (it.hasNext()) {
|
||||
final LongList entry = it.next();
|
||||
for (int i = 0; i < entry.size(); i += 2) {
|
||||
|
||||
for (int i = 0; i < entry.size(); i += 2) {
|
||||
final long epochMilli = entry.get(i);
|
||||
if (fromEpochMilli > epochMilli || epochMilli > toEpochMilli) {
|
||||
ignoredValues++;
|
||||
continue;
|
||||
}
|
||||
|
||||
final long epochMilli = entry.get(i);
|
||||
if (fromEpochMilli > epochMilli || epochMilli > toEpochMilli) {
|
||||
ignoredValues++;
|
||||
continue;
|
||||
final long value = entry.get(i + 1);
|
||||
|
||||
// compute stats
|
||||
count++;
|
||||
statsMaxValue = Math.max(statsMaxValue, value);
|
||||
|
||||
// compute average (important to do this after 'count' has been incremented)
|
||||
statsCurrentAverage = statsCurrentAverage + (value - statsCurrentAverage) / count;
|
||||
|
||||
// check if value is in the selected y-range
|
||||
boolean valueIsInYRange = value < minValue || value > maxValue;
|
||||
if (valueIsInYRange) {
|
||||
ignoredValues++;
|
||||
} else {
|
||||
plottedValues++;
|
||||
}
|
||||
|
||||
aggregator.addValue(valueIsInYRange, epochMilli, value);
|
||||
}
|
||||
}
|
||||
|
||||
final long value = entry.get(i + 1);
|
||||
METRICS_LOGGER.debug("wrote {} values to csv in: {}ms (ignored {} values) use millis: {}, grouping={}",
|
||||
plottedValues, (System.nanoTime() - start) / 1_000_000.0, ignoredValues, Boolean.toString(useMillis),
|
||||
groupResult.getGroupedBy().asString());
|
||||
return new CsvSummary(count, plottedValues, statsMaxValue, statsCurrentAverage, aggregator.getAggregatedData());
|
||||
|
||||
// compute stats
|
||||
count++;
|
||||
statsMaxValue = Math.max(statsMaxValue, value);
|
||||
}
|
||||
|
||||
// compute average (important to do this after 'count' has been incremented)
|
||||
statsCurrentAverage = statsCurrentAverage + (value - statsCurrentAverage) / count;
|
||||
static String uniqueDirectoryName() {
|
||||
return OffsetDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm_ss")) + "_"
|
||||
+ UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
// check if value is in the selected y-range
|
||||
boolean valueIsInYRange = value < minValue || value > maxValue;
|
||||
if (valueIsInYRange) {
|
||||
ignoredValues++;
|
||||
}else {
|
||||
plottedValues++;
|
||||
static String title(final Tags tags, final CsvSummary csvSummary) {
|
||||
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
final int values = csvSummary.getValues();
|
||||
final int plottedValues = csvSummary.getPlottedValues();
|
||||
|
||||
if (tags.isEmpty()) {
|
||||
result.append(DEFAULT_GROUP);
|
||||
} else {
|
||||
tags.forEach((k, v) -> {
|
||||
if (result.length() > 0) {
|
||||
result.append(" / ");
|
||||
}
|
||||
result.append(v);
|
||||
});
|
||||
}
|
||||
|
||||
aggregator.addValue(valueIsInYRange, epochMilli, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
METRICS_LOGGER.debug(
|
||||
"wrote {} values to csv in: {}ms (ignored {} values) use millis: {}, grouping={}",
|
||||
plottedValues,
|
||||
(System.nanoTime() - start) / 1_000_000.0, ignoredValues, Boolean.toString(useMillis),
|
||||
groupResult.getGroupedBy().asString());
|
||||
return new CsvSummary( count, plottedValues, statsMaxValue, statsCurrentAverage,
|
||||
aggregator.getAggregatedData());
|
||||
|
||||
}
|
||||
|
||||
static String uniqueDirectoryName() {
|
||||
return OffsetDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HH_mm_ss")) + "_"
|
||||
+ UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
static String title(final Tags tags, final CsvSummary csvSummary) {
|
||||
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
final int values = csvSummary.getValues();
|
||||
final int plottedValues = csvSummary.getPlottedValues();
|
||||
|
||||
if (tags.isEmpty()) {
|
||||
result.append(DEFAULT_GROUP);
|
||||
} else {
|
||||
tags.forEach((k, v) -> {
|
||||
if (result.length() > 0) {
|
||||
result.append(" / ");
|
||||
result.append(" (");
|
||||
if (plottedValues != values) {
|
||||
result.append(String.format("%,d / %,d", plottedValues, values));
|
||||
} else {
|
||||
result.append(String.format("%,d", values));
|
||||
}
|
||||
result.append(v);
|
||||
});
|
||||
}
|
||||
result.append(")");
|
||||
|
||||
result.append(" (");
|
||||
if (plottedValues != values) {
|
||||
result.append(String.format("%,d / %,d", plottedValues, values));
|
||||
} else {
|
||||
result.append(String.format("%,d", values));
|
||||
return result.toString();
|
||||
}
|
||||
result.append(")");
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,147 +13,144 @@ import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
class YAxisTicks {
|
||||
|
||||
|
||||
public static List<String> computeYTicks(final GnuplotSettings settings, final Collection<DataSeries> dataSeries) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
final long yRangeMax;
|
||||
final long yRangeMin;
|
||||
if (settings.hasYRange()) {
|
||||
yRangeMax = settings.getYRangeMax();
|
||||
yRangeMin = settings.getYRangeMin();
|
||||
} else {
|
||||
yRangeMax = DataSeries.maxValue(dataSeries);
|
||||
yRangeMin = 0;
|
||||
}
|
||||
final int height = settings.getHeight();
|
||||
public static List<String> computeYTicks(final GnuplotSettings settings, final Collection<DataSeries> dataSeries) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
|
||||
switch (settings.getYAxisScale()) {
|
||||
case LINEAR:
|
||||
result = computeLinearYTicks(height, yRangeMin, yRangeMax);
|
||||
break;
|
||||
case LOG10:
|
||||
result = computeLog10YTicks(height, yRangeMin, yRangeMax);
|
||||
break;
|
||||
default:
|
||||
// use the default
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<String> computeLog10YTicks(final int height, final long yRangeMin, final long yRangeMax) {
|
||||
|
||||
final List<String> ticsLabels = Arrays.asList(//
|
||||
"\"1ms\" 1", //
|
||||
"\"2ms\" 2", //
|
||||
"\"5ms\" 5", //
|
||||
"\"10ms\" 10", //
|
||||
"\"20ms\" 20", //
|
||||
"\"50ms\" 50", //
|
||||
"\"100ms\" 100", //
|
||||
"\"200ms\" 200", //
|
||||
"\"500ms\" 500", //
|
||||
"\"1s\" 1000", //
|
||||
"\"2s\" 2000", //
|
||||
"\"5s\" 5000", //
|
||||
"\"10s\" 10000", //
|
||||
"\"30s\" 30000", //
|
||||
"\"1m\" 60000", //
|
||||
"\"2m\" 120000", //
|
||||
"\"5m\" 300000", //
|
||||
"\"10m\" 600000", //
|
||||
"\"30m\" 1800000", //
|
||||
"\"1h\" 3600000", //
|
||||
"\"2h\" 7200000", //
|
||||
"\"4h\" 14400000", //
|
||||
"\"8h\" 28800000", //
|
||||
"\"16h\" 57600000", //
|
||||
"\"1d\" 86400000", //
|
||||
"\"2d\" 172800000", //
|
||||
"\"1 week\" 604800000", //
|
||||
"\"2 week\" 1209600000.0", //
|
||||
"\"4 week\" 2419200000.0", //
|
||||
"\"3 month\" 7776000000.0", //
|
||||
"\"1 year\" 31536000000.0", //
|
||||
"\"5 year\" 157680000000.0", //
|
||||
"\"10 year\" 315360000000.0"
|
||||
);
|
||||
|
||||
|
||||
return ticsLabels;
|
||||
}
|
||||
|
||||
private static List<String> computeLinearYTicks(final long height, final long yRangeMin, final long yRangeMax) {
|
||||
|
||||
final long plotHeight = height - GnuplotSettings.GNUPLOT_TOP_BOTTOM_MARGIN;
|
||||
final long maxLabels = plotHeight / (GnuplotSettings.TICKS_FONT_SIZE * 5);
|
||||
|
||||
final long range = yRangeMax - yRangeMin;
|
||||
final long msPerLabel = roundToLinearLabelSteps(range / maxLabels);
|
||||
|
||||
final List<String> ticsLabels = new ArrayList<>();
|
||||
for (long i = yRangeMin; i <= yRangeMax; i += msPerLabel) {
|
||||
ticsLabels.add("\"" + msToTic(i, msPerLabel) + "\" " + i);
|
||||
}
|
||||
|
||||
return ticsLabels;
|
||||
}
|
||||
|
||||
private static long roundToLinearLabelSteps(final long msPerLabel) {
|
||||
final List<Long> steps = Arrays.asList(2L, 5L, 10L, 20L, 50L, 100L, 200L, 500L, 1000L, 2000L, 5000L, 10_000L,
|
||||
20_000L, MINUTES.toMillis(1), MINUTES.toMillis(2), MINUTES.toMillis(5), MINUTES.toMillis(10),
|
||||
MINUTES.toMillis(15), MINUTES.toMillis(30), HOURS.toMillis(1), HOURS.toMillis(2), HOURS.toMillis(5),
|
||||
HOURS.toMillis(10), HOURS.toMillis(12), DAYS.toMillis(1), DAYS.toMillis(2), DAYS.toMillis(5),
|
||||
DAYS.toMillis(7));
|
||||
|
||||
for (final Long step : steps) {
|
||||
if (msPerLabel < step) {
|
||||
return step;
|
||||
}
|
||||
}
|
||||
|
||||
return msPerLabel;
|
||||
}
|
||||
|
||||
private static String msToTic(final long ms, final double msPerLabel) {
|
||||
|
||||
if (ms < 1000) {
|
||||
return ms + "ms";
|
||||
} else if (ms < MINUTES.toMillis(1)) {
|
||||
if (msPerLabel % 1000 == 0) {
|
||||
return String.format(Locale.US,"%ds", ms / 1_000);
|
||||
final long yRangeMax;
|
||||
final long yRangeMin;
|
||||
if (settings.hasYRange()) {
|
||||
yRangeMax = settings.getYRangeMax();
|
||||
yRangeMin = settings.getYRangeMin();
|
||||
} else {
|
||||
return String.format(Locale.US,"%.1fs", ms / 1_000.0);
|
||||
yRangeMax = DataSeries.maxValue(dataSeries);
|
||||
yRangeMin = 0;
|
||||
}
|
||||
} else if (ms < TimeUnit.HOURS.toMillis(1)) {
|
||||
final int height = settings.getHeight();
|
||||
|
||||
final long sec = (ms % MINUTES.toMillis(1)) / SECONDS.toMillis(1);
|
||||
final long min = ms / MINUTES.toMillis(1);
|
||||
if (msPerLabel % MINUTES.toMillis(1) == 0) {
|
||||
return min + "m ";
|
||||
switch (settings.getYAxisScale()) {
|
||||
case LINEAR:
|
||||
result = computeLinearYTicks(height, yRangeMin, yRangeMax);
|
||||
break;
|
||||
case LOG10:
|
||||
result = computeLog10YTicks(height, yRangeMin, yRangeMax);
|
||||
break;
|
||||
default:
|
||||
// use the default
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<String> computeLog10YTicks(final int height, final long yRangeMin, final long yRangeMax) {
|
||||
|
||||
final List<String> ticsLabels = Arrays.asList(//
|
||||
"\"1ms\" 1", //
|
||||
"\"2ms\" 2", //
|
||||
"\"5ms\" 5", //
|
||||
"\"10ms\" 10", //
|
||||
"\"20ms\" 20", //
|
||||
"\"50ms\" 50", //
|
||||
"\"100ms\" 100", //
|
||||
"\"200ms\" 200", //
|
||||
"\"500ms\" 500", //
|
||||
"\"1s\" 1000", //
|
||||
"\"2s\" 2000", //
|
||||
"\"5s\" 5000", //
|
||||
"\"10s\" 10000", //
|
||||
"\"30s\" 30000", //
|
||||
"\"1m\" 60000", //
|
||||
"\"2m\" 120000", //
|
||||
"\"5m\" 300000", //
|
||||
"\"10m\" 600000", //
|
||||
"\"30m\" 1800000", //
|
||||
"\"1h\" 3600000", //
|
||||
"\"2h\" 7200000", //
|
||||
"\"4h\" 14400000", //
|
||||
"\"8h\" 28800000", //
|
||||
"\"16h\" 57600000", //
|
||||
"\"1d\" 86400000", //
|
||||
"\"2d\" 172800000", //
|
||||
"\"1 week\" 604800000", //
|
||||
"\"2 week\" 1209600000.0", //
|
||||
"\"4 week\" 2419200000.0", //
|
||||
"\"3 month\" 7776000000.0", //
|
||||
"\"1 year\" 31536000000.0", //
|
||||
"\"5 year\" 157680000000.0", //
|
||||
"\"10 year\" 315360000000.0");
|
||||
|
||||
return ticsLabels;
|
||||
}
|
||||
|
||||
private static List<String> computeLinearYTicks(final long height, final long yRangeMin, final long yRangeMax) {
|
||||
|
||||
final long plotHeight = height - GnuplotSettings.GNUPLOT_TOP_BOTTOM_MARGIN;
|
||||
final long maxLabels = plotHeight / (GnuplotSettings.TICKS_FONT_SIZE * 5);
|
||||
|
||||
final long range = yRangeMax - yRangeMin;
|
||||
final long msPerLabel = roundToLinearLabelSteps(range / maxLabels);
|
||||
|
||||
final List<String> ticsLabels = new ArrayList<>();
|
||||
for (long i = yRangeMin; i <= yRangeMax; i += msPerLabel) {
|
||||
ticsLabels.add("\"" + msToTic(i, msPerLabel) + "\" " + i);
|
||||
}
|
||||
|
||||
return ticsLabels;
|
||||
}
|
||||
|
||||
private static long roundToLinearLabelSteps(final long msPerLabel) {
|
||||
final List<Long> steps = Arrays.asList(2L, 5L, 10L, 20L, 50L, 100L, 200L, 500L, 1000L, 2000L, 5000L, 10_000L,
|
||||
20_000L, MINUTES.toMillis(1), MINUTES.toMillis(2), MINUTES.toMillis(5), MINUTES.toMillis(10),
|
||||
MINUTES.toMillis(15), MINUTES.toMillis(30), HOURS.toMillis(1), HOURS.toMillis(2), HOURS.toMillis(5),
|
||||
HOURS.toMillis(10), HOURS.toMillis(12), DAYS.toMillis(1), DAYS.toMillis(2), DAYS.toMillis(5),
|
||||
DAYS.toMillis(7));
|
||||
|
||||
for (final Long step : steps) {
|
||||
if (msPerLabel < step) {
|
||||
return step;
|
||||
}
|
||||
}
|
||||
|
||||
return msPerLabel;
|
||||
}
|
||||
|
||||
private static String msToTic(final long ms, final double msPerLabel) {
|
||||
|
||||
if (ms < 1000) {
|
||||
return ms + "ms";
|
||||
} else if (ms < MINUTES.toMillis(1)) {
|
||||
if (msPerLabel % 1000 == 0) {
|
||||
return String.format(Locale.US, "%ds", ms / 1_000);
|
||||
} else {
|
||||
return String.format(Locale.US, "%.1fs", ms / 1_000.0);
|
||||
}
|
||||
} else if (ms < TimeUnit.HOURS.toMillis(1)) {
|
||||
|
||||
final long sec = (ms % MINUTES.toMillis(1)) / SECONDS.toMillis(1);
|
||||
final long min = ms / MINUTES.toMillis(1);
|
||||
if (msPerLabel % MINUTES.toMillis(1) == 0) {
|
||||
return min + "m ";
|
||||
} else {
|
||||
return min + "m " + sec + "s";
|
||||
}
|
||||
} else if (ms < DAYS.toMillis(1)) {
|
||||
// ms is a multiple of 1 hour, see roundToLinearLabelSteps
|
||||
final long hour = (ms % DAYS.toMillis(1)) / HOURS.toMillis(1);
|
||||
final long min = (ms % HOURS.toMillis(1)) / MINUTES.toMillis(1);
|
||||
final long sec = (ms % MINUTES.toMillis(1)) / SECONDS.toMillis(1);
|
||||
|
||||
if (msPerLabel % MINUTES.toMillis(1) == 0) {
|
||||
return hour + "h " + min + "m ";
|
||||
} else if (msPerLabel % HOURS.toMillis(1) == 0) {
|
||||
return hour + "h ";
|
||||
} else {
|
||||
return hour + "h " + min + "m " + sec + "s";
|
||||
}
|
||||
} else {
|
||||
return min + "m " + sec + "s";
|
||||
}
|
||||
} else if (ms < DAYS.toMillis(1)) {
|
||||
// ms is a multiple of 1 hour, see roundToLinearLabelSteps
|
||||
final long hour = (ms % DAYS.toMillis(1)) / HOURS.toMillis(1);
|
||||
final long min = (ms % HOURS.toMillis(1)) / MINUTES.toMillis(1);
|
||||
final long sec = (ms % MINUTES.toMillis(1)) / SECONDS.toMillis(1);
|
||||
// ms is a multiple of 1 day, see roundToLinearLabelSteps
|
||||
final long day = ms / DAYS.toMillis(1);
|
||||
final long hour = (ms % DAYS.toMillis(1)) / HOURS.toMillis(1);
|
||||
|
||||
if (msPerLabel % MINUTES.toMillis(1) == 0) {
|
||||
return hour + "h " + min + "m ";
|
||||
} else if (msPerLabel % HOURS.toMillis(1) == 0) {
|
||||
return hour + "h ";
|
||||
} else {
|
||||
return hour + "h " + min + "m " + sec + "s";
|
||||
return day + "d " + hour + "h ";
|
||||
}
|
||||
} else {
|
||||
// ms is a multiple of 1 day, see roundToLinearLabelSteps
|
||||
final long day = ms / DAYS.toMillis(1);
|
||||
final long hour = (ms % DAYS.toMillis(1)) / HOURS.toMillis(1);
|
||||
|
||||
return day + "d " + hour + "h ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user