From ae695a99db52277e6ef6b4b84272f6dafe490f84 Mon Sep 17 00:00:00 2001 From: Jakob Deiner Date: Wed, 18 Oct 2023 11:51:55 +0200 Subject: [PATCH 1/5] Replace Open Hardware Monitor by fork Libre Hardware Monitor: Refactor Code --- .../msg/jpowermonitor/MeasureMethod.java | 2 +- .../jpowermonitor/MeasureMethodProvider.java | 6 ++-- .../agent/JPowerMonitorAgent.java | 2 +- .../config/JPowerMonitorConfig.java | 10 +++--- ...rCfg.java => LibreHardwareMonitorCfg.java} | 4 +-- .../msg/jpowermonitor/config/Measurement.java | 4 +-- .../msg/jpowermonitor/config/PathElement.java | 2 +- .../csv/CommaSeparatedValuesReader.java | 2 +- .../measurement/{ohm => lhm}/DataElem.java | 4 +-- .../LibreHardwareMonitorReader.java} | 32 ++++++++--------- .../resources/jpowermonitor-template.yaml | 14 ++++---- .../jpowermonitor/agent/MeasurePowerTest.java | 2 +- .../config/DefaultConfigProviderTest.java | 10 +++--- .../config/JPowerMonitorConfigTest.java | 36 +++++++++---------- .../CommaSeparatedValuesReaderTest.yaml | 8 ++--- .../resources/DefaultConfigProviderTest.yaml | 8 ++--- src/test/resources/EndlessLoopTest.yaml | 6 ++-- .../resources/JPowerMonitorConfigTest.yaml | 14 ++++---- src/test/resources/MyTest.yaml | 6 ++-- 19 files changed, 86 insertions(+), 86 deletions(-) rename src/main/java/group/msg/jpowermonitor/config/{OpenHardwareMonitorCfg.java => LibreHardwareMonitorCfg.java} (74%) rename src/main/java/group/msg/jpowermonitor/measurement/{ohm => lhm}/DataElem.java (83%) rename src/main/java/group/msg/jpowermonitor/measurement/{ohm/OpenHardwareMonitorReader.java => lhm/LibreHardwareMonitorReader.java} (82%) diff --git a/src/main/java/group/msg/jpowermonitor/MeasureMethod.java b/src/main/java/group/msg/jpowermonitor/MeasureMethod.java index 26ca6b7..0cb82bd 100644 --- a/src/main/java/group/msg/jpowermonitor/MeasureMethod.java +++ b/src/main/java/group/msg/jpowermonitor/MeasureMethod.java @@ -11,7 +11,7 @@ /** * Interface for different types of measuring the consumed energy.
- * E.g. Open Hardware Monitor or HWiNFO. + * E.g Libre Hardware Monitor or HWiNFO. */ public interface MeasureMethod { /** diff --git a/src/main/java/group/msg/jpowermonitor/MeasureMethodProvider.java b/src/main/java/group/msg/jpowermonitor/MeasureMethodProvider.java index b3e312f..3920c80 100644 --- a/src/main/java/group/msg/jpowermonitor/MeasureMethodProvider.java +++ b/src/main/java/group/msg/jpowermonitor/MeasureMethodProvider.java @@ -2,7 +2,7 @@ import group.msg.jpowermonitor.config.JPowerMonitorConfig; import group.msg.jpowermonitor.measurement.csv.CommaSeparatedValuesReader; -import group.msg.jpowermonitor.measurement.ohm.OpenHardwareMonitorReader; +import group.msg.jpowermonitor.measurement.lhm.LibreHardwareMonitorReader; /** * Factory for creating the MeasureMethod from the config. @@ -13,8 +13,8 @@ public class MeasureMethodProvider { public static MeasureMethod resolveMeasureMethod(JPowerMonitorConfig config) { if ("csv".equals(config.getMeasurement().getMethod())) { return new CommaSeparatedValuesReader(config); - } else if ("ohm".equals(config.getMeasurement().getMethod())) { - return new OpenHardwareMonitorReader(config); + } else if ("lhm".equals(config.getMeasurement().getMethod())) { + return new LibreHardwareMonitorReader(config); } else { throw new JPowerMonitorException("Unknown measure method " + config.getMeasurement().getMethod()); } diff --git a/src/main/java/group/msg/jpowermonitor/agent/JPowerMonitorAgent.java b/src/main/java/group/msg/jpowermonitor/agent/JPowerMonitorAgent.java index eb9a502..d24eed3 100644 --- a/src/main/java/group/msg/jpowermonitor/agent/JPowerMonitorAgent.java +++ b/src/main/java/group/msg/jpowermonitor/agent/JPowerMonitorAgent.java @@ -18,7 +18,7 @@ * Implements java agent to introspect power consumption of any java application. *

* Usage:
- * java -javaagent:jpowermonitor-0.1.0-SNAPSHOT-all.jar[=path-to-jpowermonitor.yaml] -jar MyApp.jar [args] + * java -javaagent:jpowermonitor-1.0.3-SNAPSHOT-all.jar[=path-to-jpowermonitor.yaml] -jar MyApp.jar [args] * * @author deinerj */ diff --git a/src/main/java/group/msg/jpowermonitor/config/JPowerMonitorConfig.java b/src/main/java/group/msg/jpowermonitor/config/JPowerMonitorConfig.java index 022394b..c2c203b 100644 --- a/src/main/java/group/msg/jpowermonitor/config/JPowerMonitorConfig.java +++ b/src/main/java/group/msg/jpowermonitor/config/JPowerMonitorConfig.java @@ -32,12 +32,12 @@ void initializeConfiguration() { if (measurement == null || measurement.getMethod() == null) { throw new JPowerMonitorException("A measuring method must be defined!"); } - if ("ohm".equals(measurement.getMethod())) { - if (measurement.getOhm() == null || measurement.getOhm().getUrl() == null) { - throw new JPowerMonitorException("OpenHardwareMonitor REST endpoint URL must be configured"); + if ("lhm".equals(measurement.getMethod())) { + if (measurement.getLhm() == null || measurement.getLhm().getUrl() == null) { + throw new JPowerMonitorException("Libre Hardware Monitor REST endpoint URL must be configured"); } - measurement.getOhm().setUrl(measurement.getOhm().getUrl() + "/data.json"); - List pathElems = measurement.getOhm().getPaths(); + measurement.getLhm().setUrl(measurement.getLhm().getUrl() + "/data.json"); + List pathElems = measurement.getLhm().getPaths(); if (pathElems == null || pathElems.isEmpty() || pathElems.get(0) == null diff --git a/src/main/java/group/msg/jpowermonitor/config/OpenHardwareMonitorCfg.java b/src/main/java/group/msg/jpowermonitor/config/LibreHardwareMonitorCfg.java similarity index 74% rename from src/main/java/group/msg/jpowermonitor/config/OpenHardwareMonitorCfg.java rename to src/main/java/group/msg/jpowermonitor/config/LibreHardwareMonitorCfg.java index ca0cffc..0df9c0d 100644 --- a/src/main/java/group/msg/jpowermonitor/config/OpenHardwareMonitorCfg.java +++ b/src/main/java/group/msg/jpowermonitor/config/LibreHardwareMonitorCfg.java @@ -6,12 +6,12 @@ import java.util.List; /** - * Data element for open hardware monitor config. + * Data element for Libre Hardware Monitor config. * * @see PathElement */ @Data -public class OpenHardwareMonitorCfg { +public class LibreHardwareMonitorCfg { @Nullable private String url; @Nullable diff --git a/src/main/java/group/msg/jpowermonitor/config/Measurement.java b/src/main/java/group/msg/jpowermonitor/config/Measurement.java index d6a9d5b..2c97fb0 100644 --- a/src/main/java/group/msg/jpowermonitor/config/Measurement.java +++ b/src/main/java/group/msg/jpowermonitor/config/Measurement.java @@ -6,11 +6,11 @@ * Data class for measurement method. * * @see CsvMeasurementCfg - * @see OpenHardwareMonitorCfg + * @see LibreHardwareMonitorCfg */ @Data public class Measurement { private String method; private CsvMeasurementCfg csv; - private OpenHardwareMonitorCfg ohm; + private LibreHardwareMonitorCfg lhm; } diff --git a/src/main/java/group/msg/jpowermonitor/config/PathElement.java b/src/main/java/group/msg/jpowermonitor/config/PathElement.java index 27032e9..d3b75ad 100644 --- a/src/main/java/group/msg/jpowermonitor/config/PathElement.java +++ b/src/main/java/group/msg/jpowermonitor/config/PathElement.java @@ -7,7 +7,7 @@ import java.util.List; /** - * Data class for path element for open hardware monitor path. + * Data class for path element for Libre Hardware Monitor path. */ @Data public class PathElement { diff --git a/src/main/java/group/msg/jpowermonitor/measurement/csv/CommaSeparatedValuesReader.java b/src/main/java/group/msg/jpowermonitor/measurement/csv/CommaSeparatedValuesReader.java index 676e60d..6b7cd40 100644 --- a/src/main/java/group/msg/jpowermonitor/measurement/csv/CommaSeparatedValuesReader.java +++ b/src/main/java/group/msg/jpowermonitor/measurement/csv/CommaSeparatedValuesReader.java @@ -205,7 +205,7 @@ String readLastLine(Path inputPath, Charset encoding) throws IOException { @Override public @NotNull List configuredSensors() { - return config.getMeasurement().getCsv().getColumns().stream().map(CsvColumn::getName).collect(Collectors.toList()); // only ohm + return config.getMeasurement().getCsv().getColumns().stream().map(CsvColumn::getName).collect(Collectors.toList()); // only lhm } @Override diff --git a/src/main/java/group/msg/jpowermonitor/measurement/ohm/DataElem.java b/src/main/java/group/msg/jpowermonitor/measurement/lhm/DataElem.java similarity index 83% rename from src/main/java/group/msg/jpowermonitor/measurement/ohm/DataElem.java rename to src/main/java/group/msg/jpowermonitor/measurement/lhm/DataElem.java index b55b1b8..05c6305 100644 --- a/src/main/java/group/msg/jpowermonitor/measurement/ohm/DataElem.java +++ b/src/main/java/group/msg/jpowermonitor/measurement/lhm/DataElem.java @@ -1,4 +1,4 @@ -package group.msg.jpowermonitor.measurement.ohm; +package group.msg.jpowermonitor.measurement.lhm; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; @@ -6,7 +6,7 @@ import lombok.Data; /** - * Data element for json communication with open hardware monitor. + * Data element for json communication with Libre Hardware Monitor. */ @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/src/main/java/group/msg/jpowermonitor/measurement/ohm/OpenHardwareMonitorReader.java b/src/main/java/group/msg/jpowermonitor/measurement/lhm/LibreHardwareMonitorReader.java similarity index 82% rename from src/main/java/group/msg/jpowermonitor/measurement/ohm/OpenHardwareMonitorReader.java rename to src/main/java/group/msg/jpowermonitor/measurement/lhm/LibreHardwareMonitorReader.java index 6ff84ba..39302a8 100644 --- a/src/main/java/group/msg/jpowermonitor/measurement/ohm/OpenHardwareMonitorReader.java +++ b/src/main/java/group/msg/jpowermonitor/measurement/lhm/LibreHardwareMonitorReader.java @@ -1,11 +1,11 @@ -package group.msg.jpowermonitor.measurement.ohm; +package group.msg.jpowermonitor.measurement.lhm; import com.fasterxml.jackson.databind.ObjectMapper; import group.msg.jpowermonitor.JPowerMonitorException; import group.msg.jpowermonitor.MeasureMethod; import group.msg.jpowermonitor.agent.Unit; import group.msg.jpowermonitor.config.JPowerMonitorConfig; -import group.msg.jpowermonitor.config.OpenHardwareMonitorCfg; +import group.msg.jpowermonitor.config.LibreHardwareMonitorCfg; import group.msg.jpowermonitor.config.PathElement; import group.msg.jpowermonitor.dto.DataPoint; import lombok.NonNull; @@ -29,19 +29,19 @@ import java.util.stream.Collectors; /** - * Implementation of the Open Hardware Monitor measure method. + * Implementation of the Libre Hardware Monitor measure method. * * @see MeasureMethod */ -public class OpenHardwareMonitorReader implements MeasureMethod { +public class LibreHardwareMonitorReader implements MeasureMethod { HttpClient client; JPowerMonitorConfig config; - OpenHardwareMonitorCfg ohmConfig; + LibreHardwareMonitorCfg lhmConfig; - public OpenHardwareMonitorReader(JPowerMonitorConfig config) { + public LibreHardwareMonitorReader(JPowerMonitorConfig config) { this.config = config; - Objects.requireNonNull(config.getMeasurement().getOhm(), "Open hardware monitor config must be set!"); - this.ohmConfig = config.getMeasurement().getOhm(); + Objects.requireNonNull(config.getMeasurement().getLhm(), "Libre Hardware Monitor config must be set!"); + this.lhmConfig = config.getMeasurement().getLhm(); this.client = HttpClientBuilder.create().build(); } @@ -49,17 +49,17 @@ public OpenHardwareMonitorReader(JPowerMonitorConfig config) { public @NotNull List measure() throws JPowerMonitorException { try { LocalDateTime time = LocalDateTime.now(); - HttpResponse response = client.execute(new HttpGet(ohmConfig.getUrl())); + HttpResponse response = client.execute(new HttpGet(lhmConfig.getUrl())); ObjectMapper objectMapper = new ObjectMapper(); DataElem root = objectMapper.readValue(response.getEntity().getContent(), DataElem.class); List result = new ArrayList<>(); - for (PathElement pathElement : ohmConfig.getPaths()) { + for (PathElement pathElement : lhmConfig.getPaths()) { DataPoint dp = createDataPoint(root, pathElement, time); result.add(dp); } return result; } catch (IOException e) { - throw new JPowerMonitorException("Unable to reach hardware monitor at url: " + ohmConfig.getUrl() + "!", e); + throw new JPowerMonitorException("Unable to reach Libre Hardware Monitor at url: " + lhmConfig.getUrl() + "!", e); } } @@ -67,14 +67,14 @@ public OpenHardwareMonitorReader(JPowerMonitorConfig config) { public @NotNull DataPoint measureFirstConfiguredPath() throws JPowerMonitorException { try { LocalDateTime time = LocalDateTime.now(); - HttpResponse response = client.execute(new HttpGet(ohmConfig.getUrl())); + HttpResponse response = client.execute(new HttpGet(lhmConfig.getUrl())); ObjectMapper objectMapper = new ObjectMapper(); DataElem root = objectMapper.readValue(response.getEntity().getContent(), DataElem.class); // config assures that getPaths is not null and has at least one element! - PathElement pathElement = ohmConfig.getPaths().get(0); + PathElement pathElement = lhmConfig.getPaths().get(0); return createDataPoint(root, pathElement, time); } catch (IOException e) { - throw new JPowerMonitorException("Unable to reach hardware monitor at url: " + ohmConfig.getUrl() + "!", e); + throw new JPowerMonitorException("Unable to reach Libre Hardware Monitor at url: " + lhmConfig.getUrl() + "!", e); } } @@ -92,7 +92,7 @@ private DataPoint createDataPoint(DataElem root, PathElement pathElement, LocalD @Override public @NotNull List configuredSensors() { - return ohmConfig.getPaths() + return lhmConfig.getPaths() .stream() .map(p -> String.join("->", p.getPath())) .collect(Collectors.toList()); @@ -101,7 +101,7 @@ private DataPoint createDataPoint(DataElem root, PathElement pathElement, LocalD @Override public @NotNull Map defaultEnergyInIdleModeForMeasuredSensors() { Map energyInIdleModeForMeasuredSensors = new HashMap<>(); - ohmConfig.getPaths().stream() + lhmConfig.getPaths().stream() .filter(x -> x.getEnergyInIdleMode() != null) .forEach(p -> energyInIdleModeForMeasuredSensors.put(String.join("->", p.getPath()), p.getEnergyInIdleMode())); return energyInIdleModeForMeasuredSensors; diff --git a/src/main/resources/jpowermonitor-template.yaml b/src/main/resources/jpowermonitor-template.yaml index 98dcfe5..fdc56dd 100644 --- a/src/main/resources/jpowermonitor-template.yaml +++ b/src/main/resources/jpowermonitor-template.yaml @@ -1,4 +1,4 @@ -# Number of initial calls to open hardware monitor for measuring the power consumption in idle mode (without running any tests) +# Number of initial calls to Libre Hardware Monitor for measuring the power consumption in idle mode (without running any tests) initCycles: 10 # Sampling interval in milliseconds for the initialization period. This is the interval the data source for the sensor values is questioned for new values while measuring idle energy. # Should be set longer than the normal sampling interval! Too short intervals also affect the energy consumption! @@ -15,8 +15,8 @@ samplingIntervalInMs: 300 carbonDioxideEmissionFactor: 485 measurement: - # Specify which measurement method to use. Possible values: ohm, csv - method: 'ohm' + # Specify which measurement method to use. Possible values: lhm, csv + method: 'lhm' # Configuration for reading from csv file. E.g. output from HWInfo csv: # Path to csv file to read measure values from @@ -30,11 +30,11 @@ measurement: encoding: 'UTF-8' # Delimiter to use for separating the columns in the csv input file delimiter: ',' - # Configuration for reading from open hardware monitor - ohm: - # URL to open hardware monitor (** started in administrator mode **) + # Configuration for reading from Libre Hardware Monitor + lhm: + # URL to Libre Hardware Monitor (** started in administrator mode **) url: 'http://localhost:8085' - # The paths define the path to the leaf node underneath the root 'Sensor' node in Open Hardware Monitor to access and store with every sample. + # The paths define the path to the leaf node underneath the root 'Sensor' node in Libre Hardware Monitor to access and store with every sample. # The more paths defined (no more than about 10), the greater the impact on power consumption, since the values must be extracted from the json data. paths: - { path: [ 'MSGN13205', 'Intel Core i7-9850H', 'Powers', 'CPU Package' ], energyInIdleMode: } # if energyInIdleMode is specified, it does not need to be measured before each test. diff --git a/src/test/java/group/msg/jpowermonitor/agent/MeasurePowerTest.java b/src/test/java/group/msg/jpowermonitor/agent/MeasurePowerTest.java index 2846084..b5917d3 100644 --- a/src/test/java/group/msg/jpowermonitor/agent/MeasurePowerTest.java +++ b/src/test/java/group/msg/jpowermonitor/agent/MeasurePowerTest.java @@ -16,7 +16,7 @@ class MeasurePowerTest { /** * Steps down wait interval (in 50ms steps) between measurements until implemented/configured MeasureMethod delivers the same value again. - * -> e. g. for OpenHardwareMonitor between 750-850ms seems to be the minimal possible interval to get updatet values + * -> e. g. for Libre Hardware Monitor between 750-850ms seems to be the minimal possible interval to get updatet values */ @Disabled("Use this test to find the minimum viable measurement interval for your platform and your configured measure method") void findReasonableMeasurementIntervalForMeasureMethodTest() { diff --git a/src/test/java/group/msg/jpowermonitor/config/DefaultConfigProviderTest.java b/src/test/java/group/msg/jpowermonitor/config/DefaultConfigProviderTest.java index 3e971d8..55d535c 100644 --- a/src/test/java/group/msg/jpowermonitor/config/DefaultConfigProviderTest.java +++ b/src/test/java/group/msg/jpowermonitor/config/DefaultConfigProviderTest.java @@ -31,14 +31,14 @@ public void readConfig_fromResourceIfNoFile() { expected.setCarbonDioxideEmissionFactor(new BigDecimal("777")); Measurement measurement = new Measurement(); - measurement.setMethod("ohm"); + measurement.setMethod("lhm"); - OpenHardwareMonitorCfg ohm = new OpenHardwareMonitorCfg(); + LibreHardwareMonitorCfg lhm = new LibreHardwareMonitorCfg(); PathElement pe = new PathElement(); pe.path = List.of("pc", "cpu", "path1", "path2"); - ohm.setPaths(List.of(pe)); - ohm.setUrl("some.test.url" + "/data.json"); // /data.json is internally added - measurement.setOhm(ohm); + lhm.setPaths(List.of(pe)); + lhm.setUrl("some.test.url" + "/data.json"); // /data.json is internally added + measurement.setLhm(lhm); CsvMeasurementCfg csv = new CsvMeasurementCfg(); csv.setInputFile("mycsv.csv"); diff --git a/src/test/java/group/msg/jpowermonitor/config/JPowerMonitorConfigTest.java b/src/test/java/group/msg/jpowermonitor/config/JPowerMonitorConfigTest.java index ef464d5..223346a 100644 --- a/src/test/java/group/msg/jpowermonitor/config/JPowerMonitorConfigTest.java +++ b/src/test/java/group/msg/jpowermonitor/config/JPowerMonitorConfigTest.java @@ -23,21 +23,21 @@ public void initialization_noHWGroup() { public void initialization_noUrl() { JPowerMonitorConfig config = new JPowerMonitorConfig(); Measurement measurement = new Measurement(); - measurement.setMethod("ohm"); - measurement.setOhm(new OpenHardwareMonitorCfg()); + measurement.setMethod("lhm"); + measurement.setLhm(new LibreHardwareMonitorCfg()); config.setMeasurement(measurement); assertThatThrownBy(config::initializeConfiguration).isInstanceOf(JPowerMonitorException.class); } @Test public void initialization_noPath() { - OpenHardwareMonitorCfg ohmConfig = new OpenHardwareMonitorCfg(); - ohmConfig.setUrl("some.url"); - ohmConfig.setPaths(List.of(new PathElement())); + LibreHardwareMonitorCfg lhmConfig = new LibreHardwareMonitorCfg(); + lhmConfig.setUrl("some.url"); + lhmConfig.setPaths(List.of(new PathElement())); JPowerMonitorConfig config = new JPowerMonitorConfig(); Measurement measurement = new Measurement(); - measurement.setMethod("ohm"); - measurement.setOhm(ohmConfig); + measurement.setMethod("lhm"); + measurement.setLhm(lhmConfig); config.setMeasurement(measurement); assertThatThrownBy(config::initializeConfiguration).isInstanceOf(JPowerMonitorException.class); } @@ -46,29 +46,29 @@ public void initialization_noPath() { public void initialization_urlPreparation() { PathElement path = new PathElement(); path.setPath(List.of("path")); - OpenHardwareMonitorCfg ohmConfig = new OpenHardwareMonitorCfg(); - ohmConfig.setUrl("some.url"); - ohmConfig.setPaths(List.of(path)); + LibreHardwareMonitorCfg lhmConfig = new LibreHardwareMonitorCfg(); + lhmConfig.setUrl("some.url"); + lhmConfig.setPaths(List.of(path)); JPowerMonitorConfig config = new JPowerMonitorConfig(); Measurement measurement = new Measurement(); - measurement.setMethod("ohm"); - measurement.setOhm(ohmConfig); + measurement.setMethod("lhm"); + measurement.setLhm(lhmConfig); config.setMeasurement(measurement); config.initializeConfiguration(); - assertThat(config.getMeasurement().getOhm().getUrl()).isEqualTo("some.url/data.json"); + assertThat(config.getMeasurement().getLhm().getUrl()).isEqualTo("some.url/data.json"); } @Test public void initialization_defaultValues() { PathElement path = new PathElement(); path.setPath(List.of("path")); - OpenHardwareMonitorCfg ohmConfig = new OpenHardwareMonitorCfg(); - ohmConfig.setUrl("some.url"); - ohmConfig.setPaths(List.of(path)); + LibreHardwareMonitorCfg lhmConfig = new LibreHardwareMonitorCfg(); + lhmConfig.setUrl("some.url"); + lhmConfig.setPaths(List.of(path)); JPowerMonitorConfig config = new JPowerMonitorConfig(); Measurement measurement = new Measurement(); - measurement.setMethod("ohm"); - measurement.setOhm(ohmConfig); + measurement.setMethod("lhm"); + measurement.setLhm(lhmConfig); config.setMeasurement(measurement); config.initializeConfiguration(); diff --git a/src/test/resources/CommaSeparatedValuesReaderTest.yaml b/src/test/resources/CommaSeparatedValuesReaderTest.yaml index 4618a8e..0771f78 100644 --- a/src/test/resources/CommaSeparatedValuesReaderTest.yaml +++ b/src/test/resources/CommaSeparatedValuesReaderTest.yaml @@ -4,8 +4,8 @@ calmDownIntervalInMs: 1 percentageOfSamplesAtBeginningToDiscard: 1 samplingIntervalInMs: 1 measurement: - # Specify which measurement method to use. Possible values: ohm, csv - method: 'ohm' + # Specify which measurement method to use. Possible values: lhm, csv + method: 'lhm' # Configuration for reading from csv file. E.g. output from HWInfo csv: # Path to csv file to read measure values from @@ -17,8 +17,8 @@ measurement: - { index: 2, name: 'CPU Power', energyInIdleMode: 1.01 } encoding: 'UTF-8' delimiter: ',' - # Configuration for reading from open hardware monitor - ohm: + # Configuration for reading from Libre Hardware Monitor + lhm: url: 'some.test.url' paths: - { path: [ 'pc', 'cpu', 'path1', 'path2' ], energyInIdleMode: } diff --git a/src/test/resources/DefaultConfigProviderTest.yaml b/src/test/resources/DefaultConfigProviderTest.yaml index ad8d9d1..133706f 100644 --- a/src/test/resources/DefaultConfigProviderTest.yaml +++ b/src/test/resources/DefaultConfigProviderTest.yaml @@ -5,8 +5,8 @@ percentageOfSamplesAtBeginningToDiscard: 3 samplingIntervalInMs: 4 carbonDioxideEmissionFactor: 777 measurement: - # Specify which measurement method to use. Possible values: ohm, csv - method: 'ohm' + # Specify which measurement method to use. Possible values: lhm, csv + method: 'lhm' # Configuration for reading from csv file. E.g. output from HWInfo csv: # Path to csv file to read measure values from @@ -20,8 +20,8 @@ measurement: encoding: 'UTF-16' # Delimiter to use for separating the columns in the csv input file delimiter: ';' - # Configuration for reading from open hardware monitor - ohm: + # Configuration for reading from Libre Hardware Monitor + lhm: url: 'some.test.url' paths: - { path: [ 'pc', 'cpu', 'path1', 'path2' ], energyInIdleMode: } diff --git a/src/test/resources/EndlessLoopTest.yaml b/src/test/resources/EndlessLoopTest.yaml index a4d38d9..831a009 100644 --- a/src/test/resources/EndlessLoopTest.yaml +++ b/src/test/resources/EndlessLoopTest.yaml @@ -4,7 +4,7 @@ calmDownIntervalInMs: 1 percentageOfSamplesAtBeginningToDiscard: 1 samplingIntervalInMs: 1 measurement: - # Specify which measurement method to use. Possible values: ohm, csv + # Specify which measurement method to use. Possible values: lhm, csv method: 'csv' # Configuration for reading from csv file. E.g. output from HWInfo csv: @@ -19,8 +19,8 @@ measurement: encoding: 'UTF-8' # Delimiter to use for separating the columns in the csv input file delimiter: ',' - # Configuration for reading from open hardware monitor - ohm: + # Configuration for reading from Libre Hardware Monitor + lhm: url: 'some.test.url' paths: - { path: [ 'pc', 'cpu', 'path1', 'path2' ], energyInIdleMode: } diff --git a/src/test/resources/JPowerMonitorConfigTest.yaml b/src/test/resources/JPowerMonitorConfigTest.yaml index 0d337f9..4053438 100644 --- a/src/test/resources/JPowerMonitorConfigTest.yaml +++ b/src/test/resources/JPowerMonitorConfigTest.yaml @@ -1,4 +1,4 @@ -# Number of initial calls to open hardware monitor for measuring the power consumption in idle mode (without running any tests) +# Number of initial calls to Libre Hardware Monitor for measuring the power consumption in idle mode (without running any tests) initCycles: 10 # Sampling interval in milliseconds for the initialization period. This is the interval the data source for the sensor values is questioned for new values while measuring idle energy. # Should be set longer than the normal sampling interval! Too short intervals also affect the energy consumption! @@ -12,8 +12,8 @@ percentageOfSamplesAtBeginningToDiscard: 20 samplingIntervalInMs: 300 # measurement: - # Specify which measurement method to use. Possible values: ohm, csv - method: 'ohm' + # Specify which measurement method to use. Possible values: lhm, csv + method: 'lhm' # Configuration for reading from csv file. E.g. output from HWInfo csv: # Path to csv file to read measure values from @@ -27,11 +27,11 @@ measurement: encoding: 'UTF-8' # Delimiter to use for separating the columns in the csv input file delimiter: ',' - # Configuration for reading from open hardware monitor - ohm: - # URL to open hardware monitor (** started in administrator mode **) + # Configuration for reading from Libre Hardware Monitor + lhm: + # URL to Libre Hardware Monitor (** started in administrator mode **) url: 'http://localhost:8085' - # The paths define the path to the leaf node underneath the root 'Sensor' node in Open Hardware Monitor to access and store with every sample. + # The paths define the path to the leaf node underneath the root 'Sensor' node in Libre Hardware Monitor to access and store with every sample. # The more paths defined (no more than about 10), the greater the impact on power consumption, since the values must be extracted from the json data. paths: - { path: [ 'MSGN13205', 'Intel Core i7-9850H', 'Powers', 'CPU Package' ], energyInIdleMode: } # if energyInIdleMode is specified, it does not need to be measured before each test. diff --git a/src/test/resources/MyTest.yaml b/src/test/resources/MyTest.yaml index 1dbea1a..8fabcb1 100644 --- a/src/test/resources/MyTest.yaml +++ b/src/test/resources/MyTest.yaml @@ -4,7 +4,7 @@ calmDownIntervalInMs: 1 percentageOfSamplesAtBeginningToDiscard: 1 samplingIntervalInMs: 1 measurement: - # Specify which measurement method to use. Possible values: ohm, csv + # Specify which measurement method to use. Possible values: lhm, csv method: 'csv' # Configuration for reading from csv file. E.g. output from HWInfo csv: @@ -19,8 +19,8 @@ measurement: encoding: 'UTF-8' # Delimiter to use for separating the columns in the csv input file delimiter: ',' - # Configuration for reading from open hardware monitor - ohm: + # Configuration for reading from Libre Hardware Monitor + lhm: url: 'some.test.url' paths: - { path: [ 'pc', 'cpu', 'path1', 'path2' ], energyInIdleMode: } From 3c852bbb3febabcc534505682f983d053e34be79 Mon Sep 17 00:00:00 2001 From: Jakob Deiner Date: Wed, 18 Oct 2023 11:52:43 +0200 Subject: [PATCH 2/5] Replace Open Hardware Monitor by fork Libre Hardware Monitor: Documenation --- CHANGELOG.md | 1 + README.md | 28 ++++++++++++++-------------- Readme.html | 20 ++++++++++---------- algorithm.txt | 2 +- docs/lhm-webserver.png | Bin 0 -> 23542 bytes docs/omh-webserver.png | Bin 17408 -> 0 bytes 6 files changed, 26 insertions(+), 25 deletions(-) create mode 100644 docs/lhm-webserver.png delete mode 100644 docs/omh-webserver.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e1eda0..e083262 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - no infinite loop on misconfigured csv delimiter, - fix NaN on first measurements with zero duration. - upgrade dependencies +- replace discontinued Open Hardware Monitor by fork Libre Hardware Monitor ## 2023-03-07 - refactoring and release 1.0.0 diff --git a/README.md b/README.md index 34e4c1e..081c25c 100644 --- a/README.md +++ b/README.md @@ -9,21 +9,21 @@ The CPU usage of the program and the current power consumption are aggregated to The result of the measurement is the energy consumption in watt hours or joule. ### Prerequisites -- Tool Open Hardware Monitor is installed: https://openhardwaremonitor.org/downloads/ -- Open Hardware Monitor is configured to start a web server: - ![Webserver aktivieren](docs/omh-webserver.png) +- Tool Libre Hardware Monitor is installed: https://github.com/LibreHardwareMonitor/LibreHardwareMonitor +- Libre Hardware Monitor is configured to start a web server: + ![Webserver aktivieren](docs/lhm-webserver.png) - If necessary, an alternative port (default is 8085) can be also activated there. -- __IMPORTANT: To start the web server, Open Hardware Monitor may have to be started as administrator.__ +- __IMPORTANT: To start the web server, Libre Hardware Monitor may have to be started as administrator.__ - After that the tool is also accessible in the browser: http://localhost:8085/. - The JUnit extension internally reads the json document, which can be retrieved at http://localhost:8085/data.json. - __To start the Java agent, the "fat jar" (incl. dependencies) first must be built using the Gradle task "shadowJar"__. - __Copy "src/main/resources/jpowermonitor-template.yaml" to the execution directory and rename to "./jpowermonitor.yaml"__. -- __Configure (at least) openHardwareMonitor.paths.path to fit your machine__. +- __Configure (at least) measurement -> lhm -> paths -> path to fit your machine__. - Tool HWiNFO could be used alternatively, __measurement -> method__ must be set to 'csv' and Logging to CSV in HWiNFO must be active: https://www.hwinfo.com/ ### Java Agent -- For testing call with `java -javaagent:.\build\libs\jpowermonitor-0.1.0-SNAPSHOT-all.jar[=path-to-jpowermonitor.yaml] -jar .\build\libs\jpowermonitor-0.1.0-SNAPSHOT-all.jar [runtimeSeconds] [cpuThreads]` -- .\build\libs\jpowermonitor-0.1.0-SNAPSHOT-all.jar is just an example and can be replaced by any *.jar of your choice +- For testing call with `java -javaagent:.\build\libs\jpowermonitor-1.0.3-SNAPSHOT-all.jar[=path-to-jpowermonitor.yaml] -jar .\build\libs\jpowermonitor-1.0.3-SNAPSHOT-all.jar [runtimeSeconds] [cpuThreads]` +- .\build\libs\jpowermonitor-1.0.3-SNAPSHOT-all.jar is just an example and can be replaced by any *.jar of your choice - For starting the agent with Spring Boot, Servlet-Container etc. please consult the respective documentation for adding a java agent. ### Limitations @@ -47,7 +47,7 @@ For the configuration of the JUnit extension a yaml file with the name of the ex | percentageOfSamplesAtBeginningToDiscard | What percentage of samples should be discarded at the beginning of the measurement to get more meaningful results. Meaningful: 5-20%.
_(Parameter is only used in JUnitExtension, not in JavaAgent)_ | X | 15 | | samplingIntervalInMs | Polling interval for test phase.
_(Parameter is only used in JUnitExtension, not in JavaAgent)_ | X | 300 | | carbonDioxideEmissionFactor | Conversion factor to calculate approximated CO2 consumption in grams from energy consumption per kWh. Depends on the energy mix of your location, for germany compare e. g. https://www.umweltbundesamt.de/themen/klima-energie/energieversorgung/strom-waermeversorgung-in-zahlen#Strommix | X | 485 | -| measurement -> method | Specify which measurement method to use. Possible values: ohm, csv | | 'ohm' | +| measurement -> method | Specify which measurement method to use. Possible values: lhm, csv | | 'lhm' | | measurement -> csv | | X | Configuration for reading from csv file. E.g. output from HWInfo | | measurement -> csv -> inputFile | Path to csv file to read measure values from | | 'hwinfo.csv' | | measurement -> csv -> lineToRead | Which line in the csv input file contains the current measured values? The first or the last? This depends on the measurement tool. Possible value: first, last | X | 'last' | @@ -57,11 +57,11 @@ For the configuration of the JUnit extension a yaml file with the name of the ex | measurement -> csv -> columns -> energyInIdleMode | For the current measuring sensors the base load per sensor path can be configured (self-measured). If nothing is specified, then a base load measurement is performed in `@BeforeAll` (see also initCycles and samplingIntervalForInitInMs) and this value is used. | X | | | measurement -> csv -> encoding | Encoding to use for reading the csv input file | X | 'UTF-8' | | measurement -> csv -> delimiter | Delimiter to use for separating the columns in the csv input file | X | ',' | -| measurement -> ohm | | X | Configuration for reading from open hardware monitor | -| measurement -> ohm -> url | (** started in administrator mode **) | | Url of the Open Hardware Monitor incl. port | -| measurement -> ohm -> paths | | | Multiple paths to the sensors can be specified. This depends on the machine and must be viewed in the Open Hardware Monitor. | -| measurement -> ohm -> paths -> path | | | Path to a sensor | -| measurement -> ohm -> paths -> energyInIdleMode | For the current measuring sensors the base load per sensor path can be configured (self-measured). If nothing is specified, then a base load measurement is performed in `@BeforeAll` (see also initCycles and samplingIntervalForInitInMs) and this value is used. | X | | +| measurement -> lhm | | X | Configuration for reading from Libre Libre Hardware Monitor | +| measurement -> lhm -> url | (** started in administrator mode **) | | Url of the Libre Hardware Monitor incl. port | +| measurement -> lhm -> paths | | | Multiple paths to the sensors can be specified. This depends on the machine and must be viewed in the Libre Hardware Monitor. | +| measurement -> lhm -> paths -> path | | | Path to a sensor | +| measurement -> lhm -> paths -> energyInIdleMode | For the current measuring sensors the base load per sensor path can be configured (self-measured). If nothing is specified, then a base load measurement is performed in `@BeforeAll` (see also initCycles and samplingIntervalForInitInMs) and this value is used. | X | | | csvRecording -> resultCsv | | X | Result CSV Name (specify paths with slash, they will be created automatically) | | csvRecording -> measurementCsv | Measurement CSV Name (specify paths with slash, they will be created automatically) | X | | | javaAgent -> packageFilter | Filter power and energy for methods starting with this packageFilter names, write results of filtered methods to separate CSV files. | X | 'group.msg', de.gillardon' | @@ -78,7 +78,7 @@ You may build the jpowermonitor fat jar using the build target `shadowJar` and t The add the test dependency to your gradle build (analogue for maven builds): ``` - testImplementation files('libs/jpowermonitor-1.0.1-all.jar') + testImplementation files('libs/jpowermonitor-1.0.3-all.jar') ``` Alternatively you call the build target `publishLocal` in the jPowerMonitor project and publish the jar to your local maven repository. diff --git a/Readme.html b/Readme.html index ad88268..a2139f5 100644 --- a/Readme.html +++ b/Readme.html @@ -25,10 +25,10 @@

PowerMeasurement

JUnit Extension für Energieverbrauchsmessung.

Voraussetzungen

    -
  • Tool Open Hardware Monitor ist installiert: https://openhardwaremonitor.org/downloads/
  • -
  • Open Hardware Monitor ist konfiguriert, so dass ein Webserver gestartet wird: Webserver aktivieren
  • +
  • Tool Libre Hardware Monitor ist installiert: Libre Hardware Monitor
  • +
  • Libre Hardware Monitor ist konfiguriert, so dass ein Webserver gestartet wird: Webserver aktivieren
  • Ggf. ist dort auch ein alternativer Port (Default ist 8085) aktiviert.
  • -
  • WICHTIG: Um den Webserver zu starten, muss Open Hardware Monitor ggf. als Administrator gestartet werden.
  • +
  • WICHTIG: Um den Webserver zu starten, muss Libre Hardware Monitor ggf. als Administrator gestartet werden.
  • Danach ist das Tool auch im Browser erreichbar: http://localhost:8085/
  • Die JUnit extension liest intern das json Dokument aus, das unter http://localhost:8085/data.json abgerufen werden kann.
@@ -85,25 +85,25 @@

Konfiguration

Abfrageintervall für Testphase. -openHardwareMonitor -> url +lhm -> url -Url des Open Hardware Monitor inkl. Port +Url des Libre Hardware Monitor inkl. Port -openHardwareMonitor -> paths +lhm -> paths -Es können mehrere Pfade zu den Sensoren angegeben werden. Dies ist abhängig von der Maschine und muss im Open Hardware Monitor angesehen werden. +Es können mehrere Pfade zu den Sensoren angegeben werden. Dies ist abhängig von der Maschine und muss im Libre Hardware Monitor angesehen werden. -openHardwareMonitor -> paths -> path +lhm -> paths -> path Pfad zu einem Sensor -openHardwareMonitor -> paths -> energyInIdleMode +lhm -> paths -> energyInIdleMode X Für die strommessenden Sensoren kann die Grundlast pro Sensor-Pfad konfiguriert werden (selbst gemessen). Wenn nichts angegeben wird, dann wird im @BeforeAll eine Grundlastmessung durchgeführt (siehe auch initCycles und samplingIntervalForInitInMs) und dieser Wert verwendet. @@ -126,7 +126,7 @@

Konfiguration

Einbindung in eigenes Projekt

Gradle

Dependency:

-
testImplementation files('libs/powermeasurement-1.0.1-all.jar')
+
testImplementation files('libs/powermeasurement-1.0.3-all.jar')

JUnit Tests

Die Extension ist für JUnit 5 (jupiter) Tests ausgelegt und wird wie folgt eingebunden:

@ExtendWith({PowerMeasurementExtension.class})
diff --git a/algorithm.txt b/algorithm.txt index 5d3ba8c..5f77578 100644 --- a/algorithm.txt +++ b/algorithm.txt @@ -15,7 +15,7 @@ MEASUREMENT_INTERVAL - gather power consumption - aggregate method activity and power consumption to energy consumption per method - POWER_TOTAL (W) - get current CPU power per MEASUREMENT_INTERVAL - - call e.g. OpenHardwareMonitor for current CPU power usage + - call e.g. Libre Hardware Monitor for current CPU power usage - assume power was the same for the entirety of MEASUREMENT_INTERVAL - get CPU times - THREAD_TIME (ns) - total CPU time of each running threads -> should time since last measurement? diff --git a/docs/lhm-webserver.png b/docs/lhm-webserver.png new file mode 100644 index 0000000000000000000000000000000000000000..58726136952bfc5d1e7e52ee47b03d0c7ebc4005 GIT binary patch literal 23542 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 cEjnY=(==H0p<2MX#fBK literal 0 HcmV?d00001 diff --git a/docs/omh-webserver.png b/docs/omh-webserver.png deleted file mode 100644 index 7255fb6985616c1aeb8258639721d532e1a5b97c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17408 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 From 3ea62f51d2e191eebb22cef13c7657dc8a6d05b9 Mon Sep 17 00:00:00 2001 From: Jakob Deiner Date: Wed, 18 Oct 2023 11:53:15 +0200 Subject: [PATCH 3/5] Bump Version to 1.0.3 --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 276823b..99eda91 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.2-SNAPSHOT +1.0.3-SNAPSHOT From b438ea6b7497a78ac0c8a7c7ebc87ff14c9a825f Mon Sep 17 00:00:00 2001 From: Jakob Deiner Date: Wed, 18 Oct 2023 12:11:33 +0200 Subject: [PATCH 4/5] Removed "Readme.html" since that is not maintained properly and Github shows formatted "Readme.MD" properly --- Readme.html | 142 ---------------------------------------------------- 1 file changed, 142 deletions(-) delete mode 100644 Readme.html diff --git a/Readme.html b/Readme.html deleted file mode 100644 index a2139f5..0000000 --- a/Readme.html +++ /dev/null @@ -1,142 +0,0 @@ - - - - - - - - README - - - - - - -

-

PowerMeasurement

-

JUnit Extension für Energieverbrauchsmessung.

-

Voraussetzungen

-
    -
  • Tool Libre Hardware Monitor ist installiert: Libre Hardware Monitor
  • -
  • Libre Hardware Monitor ist konfiguriert, so dass ein Webserver gestartet wird: Webserver aktivieren
  • -
  • Ggf. ist dort auch ein alternativer Port (Default ist 8085) aktiviert.
  • -
  • WICHTIG: Um den Webserver zu starten, muss Libre Hardware Monitor ggf. als Administrator gestartet werden.
  • -
  • Danach ist das Tool auch im Browser erreichbar: http://localhost:8085/
  • -
  • Die JUnit extension liest intern das json Dokument aus, das unter http://localhost:8085/data.json abgerufen werden kann.
  • -
-

Einschränkungen

-
    -
  • Das Tool funktioniert momentan nur mit deutscher Locale Einstellung.
  • -
-

Konfiguration

-

Die Konfiguration erfolgt über die Datei powermeasurement.yaml. Diese muss im Classpath liegen (also zum Beispiel unter src/test/resources).

- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KonfigurationspfadoptionalDefaultwertBeschreibung
initCyclesX10Die Zahl der Zyklen für die Initialisierung für die Messung der Grundlast auf dem System.
samplingIntervalForInitInMsX1000Abfrageintervall für Initialisierungsphase.
calmDownIntervalInMsX1000Nach Initialisierung wird noch so lange gewartet, bis der Test startet.
percentageOfSamples AtBeginningToDiscardX15Wie viel Prozent der Samples sollen zu Beginn der Messung verworfen werden, um aussagekräftigere Ergebnisse zu erhalten. Sinnvoll: 5-20%.
samplingIntervalInMsX300Abfrageintervall für Testphase.
lhm -> urlUrl des Libre Hardware Monitor inkl. Port
lhm -> pathsEs können mehrere Pfade zu den Sensoren angegeben werden. Dies ist abhängig von der Maschine und muss im Libre Hardware Monitor angesehen werden.
lhm -> paths -> pathPfad zu einem Sensor
lhm -> paths -> energyInIdleModeXFür die strommessenden Sensoren kann die Grundlast pro Sensor-Pfad konfiguriert werden (selbst gemessen). Wenn nichts angegeben wird, dann wird im @BeforeAll eine Grundlastmessung durchgeführt (siehe auch initCycles und samplingIntervalForInitInMs) und dieser Wert verwendet.
csvRecording -> resultCsvXErgebnis CSV Name (Pfade mit Slash angeben, diese werden automatisch angelegt)
csvRecording -> measurementCsvXMesswert CSV Name (Pfade mit Slash angeben, diese werden automatisch angelegt)
-

Wenn für einen Pfad keine Grundlast (energyInIdleMode) angegeben wird, wird diese vor jedem Test gemessen. So ist auch ein Mischbetrieb möglich zwischen Konfiguration der Grundlast und Messung und es können die Ergebnisse verglichen werden (manche Sensoren liefern sehr ähnliche Werte). Für nicht strommessende Sensoren (z.B. Temperatur) wird die Grundlast nicht extra berechnet und auch nicht vom Messwert abgezogen! Sie wird nur ausgegeben, wenn auch für einen strommessenden Sensor eine Grundlast berechnet werden muss, weil diese nicht in der Konfiguration angegeben ist.

-

Einbindung in eigenes Projekt

-

Gradle

-

Dependency:

-
testImplementation files('libs/powermeasurement-1.0.3-all.jar')
-

JUnit Tests

-

Die Extension ist für JUnit 5 (jupiter) Tests ausgelegt und wird wie folgt eingebunden:

-
@ExtendWith({PowerMeasurementExtension.class})
-

Die Tests werden am besten als @RepeatedTests(...) ausgeführt. Zum Beispiel immer 10 mal. In dem Ergebnis CSV (Konfiguration: resultCsv) kann dann in Excel ein Mittelwert über die Ergebnisse gebildet werden.

-

@SensorValues annotierte Felder in der Testklasse vom Typ List<SensorValue> sind nach jedem Test in der @AfterEach Methode abrufbar.

-

Das measurementCsv gibt alle berücksichtigen Messpunkte aus (auch die der Grundlastmessung, falls sie stattfindet). Bitte beachten: es werden immer die ersten percentageOfSamplesAtBeginningToDiscard % Messpunkte verworfen.

-

Messen

-

Sehr gute Ergebnisse haben sich bei mir ergeben, nachdem ich den PC mit dem (deprecated und nicht mehr unterstützten) Microsoft Tool Joulemeter (Download z.B. hier: https://www.chip.de/downloads/Microsoft-Joulemeter_79226593.html) kalibriert habe. Danach gingen alle Stromwerte extrem nach unten, die CPU Temperatur erniedrigte sich massiv (vorher um 98 C, danach um 55 C) und die Ergebnisse waren um einiges besser vergleichbar!

-

Hinweis

-

Dieses markdown kann mit pandoc --self-contained -t slidy -c docs/slidy.css -o Readme.html README.md nach html und daraus über Druckfunktion des Browsers zu pdf gewandelt werden.

-
- - From 7c57ca97d4068baff33f1e573af9a25cd591d513 Mon Sep 17 00:00:00 2001 From: Jakob Deiner Date: Wed, 18 Oct 2023 12:18:15 +0200 Subject: [PATCH 5/5] Set version back to 1.0.2-SNAPSHOT since 1.0.2 will be next release --- README.md | 6 +++--- .../group/msg/jpowermonitor/agent/JPowerMonitorAgent.java | 2 +- version.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 081c25c..8c12f4b 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ The result of the measurement is the energy consumption in watt hours or joule. - Tool HWiNFO could be used alternatively, __measurement -> method__ must be set to 'csv' and Logging to CSV in HWiNFO must be active: https://www.hwinfo.com/ ### Java Agent -- For testing call with `java -javaagent:.\build\libs\jpowermonitor-1.0.3-SNAPSHOT-all.jar[=path-to-jpowermonitor.yaml] -jar .\build\libs\jpowermonitor-1.0.3-SNAPSHOT-all.jar [runtimeSeconds] [cpuThreads]` -- .\build\libs\jpowermonitor-1.0.3-SNAPSHOT-all.jar is just an example and can be replaced by any *.jar of your choice +- For testing call with `java -javaagent:.\build\libs\jpowermonitor-1.0.2-SNAPSHOT-all.jar[=path-to-jpowermonitor.yaml] -jar .\build\libs\jpowermonitor-1.0.2-SNAPSHOT-all.jar [runtimeSeconds] [cpuThreads]` +- .\build\libs\jpowermonitor-1.0.2-SNAPSHOT-all.jar is just an example and can be replaced by any *.jar of your choice - For starting the agent with Spring Boot, Servlet-Container etc. please consult the respective documentation for adding a java agent. ### Limitations @@ -78,7 +78,7 @@ You may build the jpowermonitor fat jar using the build target `shadowJar` and t The add the test dependency to your gradle build (analogue for maven builds): ``` - testImplementation files('libs/jpowermonitor-1.0.3-all.jar') + testImplementation files('libs/jpowermonitor-1.0.2-all.jar') ``` Alternatively you call the build target `publishLocal` in the jPowerMonitor project and publish the jar to your local maven repository. diff --git a/src/main/java/group/msg/jpowermonitor/agent/JPowerMonitorAgent.java b/src/main/java/group/msg/jpowermonitor/agent/JPowerMonitorAgent.java index d24eed3..776a4c8 100644 --- a/src/main/java/group/msg/jpowermonitor/agent/JPowerMonitorAgent.java +++ b/src/main/java/group/msg/jpowermonitor/agent/JPowerMonitorAgent.java @@ -18,7 +18,7 @@ * Implements java agent to introspect power consumption of any java application. *

* Usage:
- * java -javaagent:jpowermonitor-1.0.3-SNAPSHOT-all.jar[=path-to-jpowermonitor.yaml] -jar MyApp.jar [args] + * java -javaagent:jpowermonitor-1.0.2-SNAPSHOT-all.jar[=path-to-jpowermonitor.yaml] -jar MyApp.jar [args] * * @author deinerj */ diff --git a/version.txt b/version.txt index 99eda91..276823b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.3-SNAPSHOT +1.0.2-SNAPSHOT