diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..859e6f7 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..34e256f --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..eb5f63d --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..288b36b --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a5e3363 --- /dev/null +++ b/README.md @@ -0,0 +1,130 @@ + +# About + +This project is supporting to fetch Cumulocity Measurements in batches/chunks. + +It is accepting as input: +* Cumulocity tenant and -authentication information (baseUrl, user, pass) +* A date time span (dateFrom, dateTo) +* A Cumulocity device (optional) +* The maximum limit for measurements per chunk + +...and has as output a list of chunks: +* covering the entire input time span +* being sorted +* with non-colliding time spans (thus no duplicates) +* with each chunk having less than the allowed measurements per chunk + +# Concept + +The implementation is based on two concepts: + +1) Use the ˙/measurement/measurements?pageSize=1&withTotalPages=true˚ endpoint parameters to count the number of measurements for a certain time span **before** actually fetching them +2) Divide the time span recursively as long as the "measurement per time span" are fitting in the allowed chunk-size (input parameter). A binary tree is used as data structure: + +![Binary Tree Sample](/resources/imgs/binaryTree_sample.png) + +# Sample Output + +Output using a chunk size o 100,00 on a measurement collection of 611,210 Measurements: +``` +*** Measurement Chunk result set *** +Configuration: +------------------------------------------------------------ +dateFrom: 2022-03-21T20:00:00.000Z +dateTo: 2022-03-25T20:00:00.000Z +deviceId: "407401764" +chunkSize: 100,000 + +Runtime: +------------------------------------------------------------ +start: 2022-03-27T13:02:57.729Z +end: 2022-03-27T13:03:00.487Z +duration: 2,758 ms + +Results Overview: +------------------------------------------------------------ +Count Measurements (total): 611,210 +Count Chunks (total): 8 +Count Chunks (total, no null): 8 +Count exec. data splits (total): 7 +Chunk size (max): 76,719 +Chunk size (min, no null): 76,070 + +Chunks: +dateFrom dateTo countElements +----------------------------------- ----------------------------------- -------------------- +2022-03-21T20:00:00.000Z 2022-03-22T08:00:00.000Z 76,113 +2022-03-22T08:00:00.001Z 2022-03-22T20:00:00.000Z 76,070 +2022-03-22T20:00:00.001Z 2022-03-23T08:00:00.000Z 76,505 +2022-03-23T08:00:00.001Z 2022-03-23T20:00:00.000Z 76,398 +2022-03-23T20:00:00.001Z 2022-03-24T08:00:00.000Z 76,398 +2022-03-24T08:00:00.001Z 2022-03-24T20:00:00.000Z 76,398 +2022-03-24T20:00:00.001Z 2022-03-25T08:00:00.000Z 76,719 +2022-03-25T08:00:00.001Z 2022-03-25T20:00:00.000Z 76,609 +``` +Note that the chunks seems ~ equally distributed. This is due to the device sending data in a fairly constant frequency. + + +Output using a chunk size o 1000 on a measurement collection of 11.223 Measurements: + +``` +*** Measurement Chunk result set *** +Configuration: +------------------------------------------------------------ +dateFrom: 2021-01-01T00:00:00.000Z +dateTo: 2022-04-01T00:00:00.000Z +deviceId: "100200" +chunkSize: 1,000 + +Runtime: +------------------------------------------------------------ +start: 2022-03-27T13:01:56.754Z +end: 2022-03-27T13:02:01.435Z +duration: 4,681 ms + +Results Overview: +------------------------------------------------------------ +Count Measurements (total): 11,223 +Count Chunks (total): 31 +Count Chunks (total, no null): 15 +Count exec. data splits (total): 30 +Chunk size (max): 947 +Chunk size (min, no null): 3 + +Chunks: +dateFrom dateTo countElements +----------------------------------- ----------------------------------- -------------------- +2021-01-01T00:00:00.000Z 2021-08-16T12:00:00.000Z 0 +2021-08-16T12:00:00.001Z 2021-12-08T06:00:00.000Z 0 +2021-12-08T06:00:00.001Z 2022-02-03T03:00:00.000Z 0 +2022-02-03T03:00:00.001Z 2022-03-03T13:30:00.000Z 0 +2022-03-03T13:30:00.001Z 2022-03-17T18:45:00.000Z 3 +2022-03-17T18:45:00.001Z 2022-03-24T21:22:30.000Z 0 +2022-03-24T21:22:30.001Z 2022-03-25T18:42:11.250Z 0 +2022-03-25T18:42:11.251Z 2022-03-26T05:22:01.875Z 0 +2022-03-26T05:22:01.876Z 2022-03-26T10:41:57.188Z 0 +2022-03-26T10:41:57.189Z 2022-03-26T13:21:54.844Z 502 +2022-03-26T13:21:54.845Z 2022-03-26T14:41:53.672Z 0 +2022-03-26T14:41:53.673Z 2022-03-26T14:51:53.526Z 0 +2022-03-26T14:51:53.527Z 2022-03-26T14:56:53.453Z 0 +2022-03-26T14:56:53.454Z 2022-03-26T14:57:30.944Z 0 +2022-03-26T14:57:30.945Z 2022-03-26T14:57:49.690Z 192 +2022-03-26T14:57:49.691Z 2022-03-26T14:58:08.435Z 945 +2022-03-26T14:58:08.436Z 2022-03-26T14:58:27.181Z 943 +2022-03-26T14:58:27.182Z 2022-03-26T14:58:45.926Z 946 +2022-03-26T14:58:45.927Z 2022-03-26T14:59:04.671Z 938 +2022-03-26T14:59:04.672Z 2022-03-26T14:59:23.416Z 938 +2022-03-26T14:59:23.417Z 2022-03-26T14:59:42.162Z 947 +2022-03-26T14:59:42.163Z 2022-03-26T15:00:00.907Z 931 +2022-03-26T15:00:00.908Z 2022-03-26T15:00:19.653Z 933 +2022-03-26T15:00:19.654Z 2022-03-26T15:00:38.398Z 944 +2022-03-26T15:00:38.399Z 2022-03-26T15:00:57.144Z 947 +2022-03-26T15:00:57.145Z 2022-03-26T15:01:15.889Z 396 +2022-03-26T15:01:15.890Z 2022-03-26T15:01:53.379Z 0 +2022-03-26T15:01:53.380Z 2022-03-26T15:21:53.086Z 0 +2022-03-26T15:21:53.087Z 2022-03-26T16:01:52.500Z 0 +2022-03-26T16:01:52.501Z 2022-03-28T10:41:15.000Z 718 +2022-03-28T10:41:15.001Z 2022-04-01T00:00:00.000Z 0 +``` +Above sample device is sending less constant data -> that's why there are chunks with 0 elements existing. It's configurable to skip chunks of size 0 automatically. \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..58e7fc1 --- /dev/null +++ b/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + org.example + measurement-collector + 1.0-SNAPSHOT + + + 14 + 14 + + + + + org.apache.commons + commons-lang3 + 3.12.0 + + + org.json + json + 20190722 + + + org.javatuples + javatuples + 1.2 + + + org.projectlombok + lombok + 1.18.20 + + + + \ No newline at end of file diff --git a/resources/imgs/binaryTree_sample.png b/resources/imgs/binaryTree_sample.png new file mode 100644 index 0000000..b758bb1 Binary files /dev/null and b/resources/imgs/binaryTree_sample.png differ diff --git a/src/main/java/collector/C8yHttpClient.java b/src/main/java/collector/C8yHttpClient.java new file mode 100644 index 0000000..7089622 --- /dev/null +++ b/src/main/java/collector/C8yHttpClient.java @@ -0,0 +1,66 @@ +package collector; + + +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Base64; +import java.util.Optional; +import org.json.*; + +public class C8yHttpClient { + + private final String baseUrl; + private final String base64Auth; + + public C8yHttpClient(String baseUrl, String user, String pass) { + this.baseUrl = baseUrl; + this.base64Auth = basicAuth(user, pass); + } + + + public Optional fetchNumberOfMeasurements(String dateFrom, String dateTo, String oid) { + URI uri = URI.create(String.format("%s/measurement/measurements?pageSize=1&withTotalPages=true%s%s%s", + baseUrl, + StringUtils.isNoneEmpty(dateFrom) ? "&dateFrom=" + dateFrom : "", + StringUtils.isNoneEmpty(dateTo) ? "&dateTo=" + dateTo : "", + StringUtils.isNoneEmpty(oid) ? "&source=" + oid : "")); + + try { + HttpRequest request = HttpRequest.newBuilder() + .uri(uri) + .header("Authorization", base64Auth) + .method("GET", HttpRequest.BodyPublishers.noBody()) + .build(); + HttpResponse response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString()); + + if(response == null || response.statusCode() != 200 || StringUtils.isEmpty(response.body())){ + return Optional.empty(); + } + + Integer totalPages = getTotalNumberOfPages(response.body()); + return Optional.of(totalPages); + } catch (IOException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + return Optional.empty(); + } + + private Integer getTotalNumberOfPages(String responseBody) { + JSONObject obj = new JSONObject(responseBody); + Number number = obj.getJSONObject("statistics").getNumber("totalPages"); + int res = number.intValue(); + return res; + } + + private String basicAuth(String username, String password) { + return "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes()); + } + +} diff --git a/src/main/java/collector/ChunkCollector.java b/src/main/java/collector/ChunkCollector.java new file mode 100644 index 0000000..f7b7e50 --- /dev/null +++ b/src/main/java/collector/ChunkCollector.java @@ -0,0 +1,58 @@ +package collector; + +import collector.recordset.ChunkResultSet; +import collector.recordset.MeasurementChunkDescription; +import collector.util.DateUtil; +import collector.util.TimeSpan; +import org.javatuples.Pair; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class ChunkCollector { + + private final C8yHttpClient client; + + public ChunkCollector(C8yHttpClient client) { + this.client = client; + } + + public ChunkResultSet collect(ChunkCollectorConfig collectorCfg) { + ChunkResultSet resultSet = new ChunkResultSet(collectorCfg); + resultSet.registerRunStart(Instant.now()); + + resultSet.addMeasurementChunkDescriptions( + collectChunks(collectorCfg.getDateFrom(), collectorCfg.getDateTo(), collectorCfg.getOid(), collectorCfg.getChunkSize(), resultSet) + ); + + resultSet.registerRunFinish(Instant.now()); + return resultSet; + } + + private List collectChunks(Instant dateFrom, Instant dateTo, String oid, int chunkSize, ChunkResultSet resultSet) { + List lst = new ArrayList<>(); + Optional countElements = client.fetchNumberOfMeasurements(DateUtil.getFormattedUtcString(dateFrom), DateUtil.getFormattedUtcString(dateTo), oid); + + if (countElements.isEmpty()) { + // if no measurements are found its '0', if its empty => exception (you might want to catch it already earlier...) + } + + int ctElements = countElements.get(); + MeasurementChunkDescription d = new MeasurementChunkDescription(new TimeSpan(dateFrom, dateTo), ctElements); + + if (ctElements > chunkSize) { + Pair timeTuple = DateUtil.divideTimesByTwo(dateFrom, dateTo); + lst.addAll(collectChunks(timeTuple.getValue0().getDateFrom(), timeTuple.getValue0().getDateTo(), oid, chunkSize, resultSet)); + lst.addAll(collectChunks(timeTuple.getValue1().getDateFrom(), timeTuple.getValue1().getDateTo(), oid, chunkSize, resultSet)); + resultSet.registerDataSplitAction(); + } else { + lst.add(d); + } + + return lst; + } + + +} diff --git a/src/main/java/collector/ChunkCollectorConfig.java b/src/main/java/collector/ChunkCollectorConfig.java new file mode 100644 index 0000000..758a666 --- /dev/null +++ b/src/main/java/collector/ChunkCollectorConfig.java @@ -0,0 +1,19 @@ +package collector; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +import java.time.Instant; + +@AllArgsConstructor +@Getter +@ToString +@Builder +public class ChunkCollectorConfig { + private Instant dateFrom; + private Instant dateTo; + private String oid; + private int chunkSize; +} diff --git a/src/main/java/collector/Main.java b/src/main/java/collector/Main.java new file mode 100644 index 0000000..cd3cca8 --- /dev/null +++ b/src/main/java/collector/Main.java @@ -0,0 +1,56 @@ +package collector; + +import collector.recordset.ChunkResultSet; +import collector.recordset.ConsoleChunkResultDescriptor; +import collector.util.DateUtil; + +public class Main { + + private final String baseUrl = "https://example.cumulocity.com"; + private final String user = "t12345/my@user.de"; + private final String pass = "mysecretpass"; + + private final String dateFrom = "2021-01-01T00:00:00.000Z"; + private final String dateTo = "2022-04-01T00:00:00.0000Z"; + private final String oid = "100200"; + + private final int chunkSize = 1000; + + public static void main(String[] args) { + new Main().run(); + } + + public void run() { + C8yHttpClient client = new C8yHttpClient(baseUrl, user, pass); + + /* + * Spin up a binary tree for all records: + * 1) Request the total amount of measurement records between time A and B + * 2) Divide the time span (always by two) until the amount of measurementRecords is <= max allowed chunk size + * => Result will be a list of time spans with no entry having more than the allowed number of measurements + * + * Note that only the number of measurements between certain timestamps requested, not the measurements themselves. + * + * */ + ChunkCollector collector = new ChunkCollector(client); + ChunkResultSet resultSet = collector.collect( + ChunkCollectorConfig.builder() + .dateFrom(DateUtil.getDateTime(dateFrom)) + .dateTo(DateUtil.getDateTime(dateTo)) + .oid(oid) + .chunkSize(chunkSize) + .build() + ); + + ConsoleChunkResultDescriptor.instance().print(resultSet, true); + + /* + * Run over all chunks, collect their (CSV-) Measurements and dump to File + * The chunks should be: + * a) already sorted ascending by time and + * b) have no time overlaps (thus should not have duplicates) + * */ + new MeasurementProcessor().run(resultSet); + } + +} diff --git a/src/main/java/collector/MeasurementProcessor.java b/src/main/java/collector/MeasurementProcessor.java new file mode 100644 index 0000000..0cc003b --- /dev/null +++ b/src/main/java/collector/MeasurementProcessor.java @@ -0,0 +1,29 @@ +package collector; + +import collector.recordset.ChunkResultSet; +import collector.recordset.MeasurementChunkDescription; +import collector.util.DateUtil; + + +import java.util.List; + +public class MeasurementProcessor { + + public void run(ChunkResultSet chunkResultSet) { + + List chunks = chunkResultSet.getRecords(); + for(MeasurementChunkDescription c : chunks){ + if (c.getCountMeasurements() == null || c.getCountMeasurements() <= 0){ + // skip empty chunk records + continue; + } + + String dateFrom = DateUtil.getFormattedUtcString(c.getTimespan().getDateFrom()); + String dateTo = DateUtil.getFormattedUtcString(c.getTimespan().getDateTo()); + String deviceId = chunkResultSet.getCollectorCfg().getOid(); + + // TODO: request Measurements as CSV, append them in a file + } + + } +} diff --git a/src/main/java/collector/recordset/ChunkResultSet.java b/src/main/java/collector/recordset/ChunkResultSet.java new file mode 100644 index 0000000..560ec25 --- /dev/null +++ b/src/main/java/collector/recordset/ChunkResultSet.java @@ -0,0 +1,42 @@ +package collector.recordset; + +import collector.ChunkCollectorConfig; +import collector.util.DateUtil; +import collector.util.TimeSpan; +import lombok.Getter; + +import java.text.DecimalFormat; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; + +@Getter +public class ChunkResultSet { + + private final ChunkCollectorConfig collectorCfg; + private final List records = new ArrayList<>(); + private final TimeSpan runningTime = new TimeSpan(); + private int ctDataSplits = 0; + + public ChunkResultSet(ChunkCollectorConfig collectorCfg) { + this.collectorCfg = collectorCfg; + } + + public void addMeasurementChunkDescriptions(List l) { + this.records.addAll(l); + } + + public void registerRunStart(Instant startTime) { + this.runningTime.setDateFrom(startTime); + } + + public void registerRunFinish(Instant finishTime) { + this.runningTime.setDateTo(finishTime); + } + + public void registerDataSplitAction() { + this.ctDataSplits++; + } + +} diff --git a/src/main/java/collector/recordset/ConsoleChunkResultDescriptor.java b/src/main/java/collector/recordset/ConsoleChunkResultDescriptor.java new file mode 100644 index 0000000..b5232c5 --- /dev/null +++ b/src/main/java/collector/recordset/ConsoleChunkResultDescriptor.java @@ -0,0 +1,103 @@ +package collector.recordset; + +import collector.util.DateUtil; + +import java.text.DecimalFormat; +import java.time.temporal.ChronoUnit; + + +public class ConsoleChunkResultDescriptor { + + private static final ConsoleChunkResultDescriptor instance = new ConsoleChunkResultDescriptor(); + + public static ConsoleChunkResultDescriptor instance(){ + return instance; + } + + public void print(ChunkResultSet resultSet, boolean includeChunks) { + System.out.println(describe(resultSet, includeChunks)); + } + + public String describe (ChunkResultSet resultSet, boolean includeChunks) { + StringBuilder sb = new StringBuilder(); + DecimalFormat formatter = new DecimalFormat("#,###"); + + sb.append(System.lineSeparator()); + sb.append("*** Measurement Chunk result set ***").append(System.lineSeparator()); + + // ### Input Config ### + sb.append("Configuration:").append(System.lineSeparator()); + sb.append("-".repeat(60)).append(System.lineSeparator()); + sb.append(String.format("%-15s %s" + System.lineSeparator(), "dateFrom:", DateUtil.getFormattedUtcString(resultSet.getCollectorCfg().getDateFrom()))); + sb.append(String.format("%-15s %s" + System.lineSeparator(), "dateTo:", DateUtil.getFormattedUtcString(resultSet.getCollectorCfg().getDateTo()))); + sb.append(String.format("%-15s \"%s\"" + System.lineSeparator(), "deviceId:", resultSet.getCollectorCfg().getOid())); + sb.append(String.format("%-15s %s" + System.lineSeparator(), "chunkSize:", formatter.format(resultSet.getCollectorCfg().getChunkSize()))); + + // ### Runtime infos ### + sb.append(System.lineSeparator()); + sb.append("Runtime:").append(System.lineSeparator()); + sb.append("-".repeat(60)).append(System.lineSeparator()); + sb.append(String.format("%-15s %s" + System.lineSeparator(), "start:", DateUtil.getFormattedUtcString(resultSet.getRunningTime().getDateFrom()))); + sb.append(String.format("%-15s %s" + System.lineSeparator(), "end:", DateUtil.getFormattedUtcString(resultSet.getRunningTime().getDateTo()))); + sb.append(String.format("%-15s %s ms" + System.lineSeparator(), "duration:", formatter.format(resultSet.getRunningTime().between(ChronoUnit.MILLIS)))); + + // ### Results Overview ### + sb.append(System.lineSeparator()); + sb.append("Results Overview:").append(System.lineSeparator()); + sb.append("-".repeat(60)).append(System.lineSeparator()); + sb.append(String.format("%-35s %s" + System.lineSeparator(), "Count Measurements (total):", + formatter.format( + resultSet.getRecords().stream() + .map(MeasurementChunkDescription::getCountMeasurements) + .reduce(Integer::sum) + .orElse(null)) + ) + ); + sb.append(String.format("%-35s %s" + System.lineSeparator(), "Count Chunks (total):", formatter.format(resultSet.getRecords().size()))); + sb.append(String.format("%-35s %s" + System.lineSeparator(), "Count Chunks (total, no null):", + formatter.format( + resultSet.getRecords().stream() + .map(MeasurementChunkDescription::getCountMeasurements) + .filter(l -> l > 0) + .count()) + ) + ); + sb.append(String.format("%-35s %s" + System.lineSeparator(), "Count exec. data splits (total):", resultSet.getCtDataSplits())); + sb.append(String.format("%-35s %s" + System.lineSeparator(), "Chunk size (max):", + formatter.format( + resultSet.getRecords().stream() + .map(MeasurementChunkDescription::getCountMeasurements) + .reduce(Integer::max) + .orElse(null)) + ) + ); + sb.append(String.format("%-35s %s" + System.lineSeparator(), "Chunk size (min, no null):", + formatter.format( + resultSet.getRecords().stream() + .map(MeasurementChunkDescription::getCountMeasurements) + .filter(l -> l > 0) + .reduce(Integer::min) + .orElse(null)) + ) + ); + + // ### Chunk Details ### + if (includeChunks) { + sb.append(System.lineSeparator()); + sb.append("Chunks:").append(System.lineSeparator()); + sb.append(String.format("%-35s %-35s %-20s" + System.lineSeparator(), "dateFrom", "dateTo", "countElements")); + sb.append(String.format("%-35s %-35s %-20s" + System.lineSeparator(), "-".repeat(35), "-".repeat(35), "-".repeat(20))); + resultSet.getRecords().forEach( + d -> sb.append( + String.format("%-35s %-35s %20s" + System.lineSeparator(), + DateUtil.getFormattedUtcString(d.getTimespan().getDateFrom()), + DateUtil.getFormattedUtcString(d.getTimespan().getDateTo()), + formatter.format(d.getCountMeasurements()) + ) + ) + ); + } + + return sb.toString(); + } +} diff --git a/src/main/java/collector/recordset/MeasurementChunkDescription.java b/src/main/java/collector/recordset/MeasurementChunkDescription.java new file mode 100644 index 0000000..6de06e7 --- /dev/null +++ b/src/main/java/collector/recordset/MeasurementChunkDescription.java @@ -0,0 +1,14 @@ +package collector.recordset; + +import collector.util.TimeSpan; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +@ToString +@AllArgsConstructor +@Getter +public class MeasurementChunkDescription { + private final TimeSpan timespan; + private final Integer countMeasurements; +} diff --git a/src/main/java/collector/util/DateUtil.java b/src/main/java/collector/util/DateUtil.java new file mode 100644 index 0000000..2590167 --- /dev/null +++ b/src/main/java/collector/util/DateUtil.java @@ -0,0 +1,54 @@ +package collector.util; + +import org.javatuples.Pair; + +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +public class DateUtil { + + public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter + .ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); + + + public static Instant getDateTime(String utcString){ + return Instant.parse(utcString); + } + +// public static long getTimeMillis(String utcString){ +// return Instant.parse(utcString).toEpochMilli(); +// } +// +// public static Instant getDateTime(long millis){ +// return Instant.ofEpochMilli(millis); +// } +// +// public static String getFormattedUtcString(long millis){ +// return getFormattedUtcString(Instant.ofEpochMilli(millis)); +// } + + public static String getFormattedUtcString(Instant date){ + ZonedDateTime utc = date.atZone(ZoneOffset.UTC); + String res = utc.format(DATE_TIME_FORMATTER); + return res; + } + + public static Pair divideTimesByTwo(String utcDateFrom, String utcDateTo){ + Instant instDateFrom = DateUtil.getDateTime(utcDateFrom); + Instant instDateTo = DateUtil.getDateTime(utcDateTo); + + return divideTimesByTwo(instDateFrom, instDateTo); + } + + public static Pair divideTimesByTwo(Instant dateFrom, Instant dateTo){ + Instant dateALower = dateFrom; + Instant dateAUpper = dateFrom.plusMillis(ChronoUnit.MILLIS.between(dateFrom, dateTo) / 2); + Instant dateBLower = dateAUpper.plusMillis(1); + Instant dateBUpper = dateTo; + Pair res = Pair.with(new TimeSpan(dateALower, dateAUpper), new TimeSpan(dateBLower, dateBUpper)); + return res; + } +} diff --git a/src/main/java/collector/util/TimeSpan.java b/src/main/java/collector/util/TimeSpan.java new file mode 100644 index 0000000..a7d3fae --- /dev/null +++ b/src/main/java/collector/util/TimeSpan.java @@ -0,0 +1,47 @@ +package collector.util; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +public class TimeSpan { + + private Instant dateFrom; + private Instant dateTo; + + public TimeSpan(){ + } + + public TimeSpan(Instant dateFrom, Instant dateTo){ + this.dateFrom = dateFrom; + this.dateTo = dateTo; + } + + public Instant getDateFrom() { + return dateFrom; + } + + public Instant getDateTo() { + return dateTo; + } + + public void setDateFrom(Instant dateFrom) { + this.dateFrom = dateFrom; + } + + public void setDateTo(Instant dateTo) { + this.dateTo = dateTo; + } + + public long between(ChronoUnit cu){ + return cu.between(dateFrom, dateTo); + } + + @Override + public String toString() { + return "Timespan{" + + "dateFrom=" + dateFrom + + ", dateTo=" + dateTo + + '}'; + } + +} diff --git a/target/classes/collector/C8yHttpClient.class b/target/classes/collector/C8yHttpClient.class new file mode 100644 index 0000000..c1afd92 Binary files /dev/null and b/target/classes/collector/C8yHttpClient.class differ diff --git a/target/classes/collector/ChunkCollector.class b/target/classes/collector/ChunkCollector.class new file mode 100644 index 0000000..487b438 Binary files /dev/null and b/target/classes/collector/ChunkCollector.class differ diff --git a/target/classes/collector/ChunkCollectorConfig$ChunkCollectorConfigBuilder.class b/target/classes/collector/ChunkCollectorConfig$ChunkCollectorConfigBuilder.class new file mode 100644 index 0000000..49c5457 Binary files /dev/null and b/target/classes/collector/ChunkCollectorConfig$ChunkCollectorConfigBuilder.class differ diff --git a/target/classes/collector/ChunkCollectorConfig.class b/target/classes/collector/ChunkCollectorConfig.class new file mode 100644 index 0000000..f74a669 Binary files /dev/null and b/target/classes/collector/ChunkCollectorConfig.class differ diff --git a/target/classes/collector/Main.class b/target/classes/collector/Main.class new file mode 100644 index 0000000..6e9857d Binary files /dev/null and b/target/classes/collector/Main.class differ diff --git a/target/classes/collector/MeasurementProcessor.class b/target/classes/collector/MeasurementProcessor.class new file mode 100644 index 0000000..f4bff24 Binary files /dev/null and b/target/classes/collector/MeasurementProcessor.class differ diff --git a/target/classes/collector/recordset/ChunkResultSet.class b/target/classes/collector/recordset/ChunkResultSet.class new file mode 100644 index 0000000..44f2185 Binary files /dev/null and b/target/classes/collector/recordset/ChunkResultSet.class differ diff --git a/target/classes/collector/recordset/ConsoleChunkResultDescriptor.class b/target/classes/collector/recordset/ConsoleChunkResultDescriptor.class new file mode 100644 index 0000000..dbb918d Binary files /dev/null and b/target/classes/collector/recordset/ConsoleChunkResultDescriptor.class differ diff --git a/target/classes/collector/recordset/MeasurementChunkDescription.class b/target/classes/collector/recordset/MeasurementChunkDescription.class new file mode 100644 index 0000000..2ef87c6 Binary files /dev/null and b/target/classes/collector/recordset/MeasurementChunkDescription.class differ diff --git a/target/classes/collector/util/DateUtil.class b/target/classes/collector/util/DateUtil.class new file mode 100644 index 0000000..d772a6b Binary files /dev/null and b/target/classes/collector/util/DateUtil.class differ diff --git a/target/classes/collector/util/TimeSpan.class b/target/classes/collector/util/TimeSpan.class new file mode 100644 index 0000000..b5058ac Binary files /dev/null and b/target/classes/collector/util/TimeSpan.class differ diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..fe8242d --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Sat Mar 26 17:50:15 CET 2022 +groupId=org.example +artifactId=measurement-collector +version=1.0-SNAPSHOT diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..d87efcd --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,11 @@ +collector/recordset/MeasurementChunkDescription.class +collector/C8yHttpClient.class +collector/util/DateUtil.class +collector/MeasurementProcessor.class +collector/ChunkCollector.class +collector/util/Tuple.class +collector/Main.class +collector/recordset/ChunkResultSet.class +collector/util/StringUtil.class +collector/CollectorConfig.class +collector/util/MyTimespan.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..c57423d --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,11 @@ +/Users/kobu/dev/measurement-collector/src/main/java/collector/MeasurementProcessor.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/ChunkCollector.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/util/StringUtil.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/CollectorConfig.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/C8yHttpClient.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/util/MyTimespan.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/util/DateUtil.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/Main.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/recordset/ChunkResultSet.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/util/Tuple.java +/Users/kobu/dev/measurement-collector/src/main/java/collector/recordset/MeasurementChunkDescription.java diff --git a/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst new file mode 100644 index 0000000..e69de29 diff --git a/target/measurement-collector-1.0-SNAPSHOT.jar b/target/measurement-collector-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..e2fc521 Binary files /dev/null and b/target/measurement-collector-1.0-SNAPSHOT.jar differ