Add historic reader manager service provider
diff --git a/platform/sensinact-historic-manager/src/main/java/org/eclipse/sensinact/gateway/historic/storage/manager/HistoricTaskCaller.java b/platform/sensinact-historic-manager/src/main/java/org/eclipse/sensinact/gateway/historic/storage/manager/HistoricTaskCaller.java
new file mode 100644
index 0000000..9bf7628
--- /dev/null
+++ b/platform/sensinact-historic-manager/src/main/java/org/eclipse/sensinact/gateway/historic/storage/manager/HistoricTaskCaller.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2021 Kentyou.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+* Kentyou - initial API and implementation
+ */
+package org.eclipse.sensinact.gateway.historic.storage.manager;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Arrays;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.eclipse.sensinact.gateway.common.bundle.Mediator;
+import org.eclipse.sensinact.gateway.common.execution.Executable;
+import org.eclipse.sensinact.gateway.generic.Task;
+import org.eclipse.sensinact.gateway.generic.annotation.TaskCommand;
+import org.eclipse.sensinact.gateway.generic.annotation.TaskExecution;
+import org.eclipse.sensinact.gateway.historic.storage.reader.api.HistoricProvider;
+import org.eclipse.sensinact.gateway.historic.storage.reader.api.HistoricSpatialRequest;
+import org.eclipse.sensinact.gateway.historic.storage.reader.api.HistoricSpatioTemporalRequest;
+import org.eclipse.sensinact.gateway.historic.storage.reader.api.HistoricTemporalRequest;
+import org.eclipse.sensinact.gateway.historic.storage.reader.api.TemporalDTO;
+import org.json.JSONObject;
+import org.osgi.dto.DTO;
+
+/**
+ *
+ */
+@TaskExecution
+public class HistoricTaskCaller {
+
+ private static final ZoneOffset OFFSET = ZoneOffset.systemDefault().getRules().getOffset(Instant.now());
+
+ private enum AggregationType {
+ COUNT,
+ MEAN,
+ SUM,
+ SUM_SQUARE,
+ MIN,
+ MAX,
+ MEDIAN,
+ DISTINCT
+ };
+
+ private class HistoricKey {
+
+ String sensinactId, konceptId, kapabilityId, function, region;
+ LocalDateTime fromTime, toTime;
+ long timeWindow;
+
+ HistoricKey(String sensinactId, String konceptId, String kapabilityId, LocalDateTime fromTime, LocalDateTime toTime,
+ String function, long timeWindow, String region){
+ this.sensinactId = sensinactId;
+ this.konceptId = konceptId;
+ this.kapabilityId = kapabilityId;
+ this.fromTime = fromTime;
+ this.toTime = toTime;
+ this.function = function;
+ this.timeWindow = timeWindow;
+ this.region = region;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = this.sensinactId.hashCode();
+ hash += this.konceptId.hashCode();
+ hash += this.kapabilityId.hashCode();
+ hash += this.fromTime.hashCode();
+ hash += this.toTime.hashCode();
+ hash += this.function!=null?this.function.hashCode():0;
+ hash += this.function!=null?this.timeWindow:0;
+ hash += this.region!=null?this.region.hashCode():0;
+ return hash;
+ }
+ }
+
+ private Mediator mediator;
+
+ private WeakHashMap<HistoricKey, DTO[]> cache;
+
+ public HistoricTaskCaller(Mediator mediator){
+ this.mediator = mediator;
+ this.cache = new WeakHashMap<>();
+ }
+
+ @TaskCommand(method = Task.CommandType.GET, target = "/historicManager/history/requester")
+ public String get(String uri, String attributeName, String provider, String service, String resource, String from, String to,
+ String function, String window, String region) {
+ try {
+ String result = null;
+ long f = Long.parseLong(from);
+ long t = Long.parseLong(to);
+ long w = Long.parseLong(window);
+
+ LocalDateTime dtf = LocalDateTime.ofEpochSecond(f/1000l, (int) (f - ((f/1000) * 1000))*1000, OFFSET);
+ LocalDateTime dtt = LocalDateTime.ofEpochSecond(t/1000l, (int) (t - ((t/1000) * 1000))*1000, OFFSET);
+
+ if(function!=null && !"#NONE#".equals(function) && w > 0) {
+ AggregationType aggregation = null;
+ try {
+ aggregation = AggregationType.valueOf(function);
+ }catch(Exception e) {
+ aggregation = AggregationType.MEAN;
+ }
+ result = getAggregatedTemporalHistory(provider,service,resource, dtf, dtt, aggregation, w);
+ } else
+ result = getTemporalHistory(provider,service,resource, dtf, dtt);
+
+ //JSONObject obj = new JSONObject();
+ //obj.put("history", result);
+ return result;
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ private String getTemporalHistory(String provider, String service, String resource, LocalDateTime from, LocalDateTime to) {
+ HistoricTemporalRequest request = createTemporalRequest(provider, service, resource, from, to);
+ if(request == null)
+ return "[]";
+ HistoricKey historic = new HistoricKey(provider, service, resource, from, to, null, 0, null);
+ DTO[] data = this.cache.get(historic);
+ if(data == null)
+ data = request.execute().toArray(new TemporalDTO[] {});
+ this.cache.put(historic,data);
+ final AtomicBoolean first = new AtomicBoolean(true);
+ return Arrays.stream(data).<StringBuilder>collect(
+ ()->{return new StringBuilder();},
+ (sb,t)->{
+ if(first.get()) {
+ sb.append("[");
+ first.set(false);
+ }else
+ sb.append(",");
+ sb.append("{");
+ sb.append("\"tagID\":" );
+ sb.append(((TemporalDTO)t).tagID);
+ sb.append(",\"timestamp\":" );
+ sb.append(((TemporalDTO)t).timestamp);
+ sb.append(",\"value\":\"" );
+ sb.append(((TemporalDTO)t).value);
+ sb.append("\"}");
+ },
+ (sb1,sb2)->{sb1.append(sb2.toString());}
+ ).append("]").toString();
+ }
+
+ private String getAggregatedTemporalHistory(String provider, String service, String resource, LocalDateTime from, LocalDateTime to,
+ AggregationType method, long period) {
+
+ HistoricTemporalRequest request = createTemporalRequest(provider, service, resource, from, to);
+ if(request == null)
+ return "[]";
+
+ request.setFunction(method.name().toLowerCase());
+ request.setTemporalWindow(period);
+
+ HistoricKey historic = new HistoricKey(provider, service, resource, from, to, method.name(), period, null);
+ DTO[] data = this.cache.get(historic);
+ if(data == null)
+ data = request.execute().toArray(new TemporalDTO[] {});
+ this.cache.put(historic,data);
+ final AtomicBoolean first = new AtomicBoolean(true);
+ return Arrays.stream(data).<StringBuilder>collect(
+ ()->{return new StringBuilder();},
+ (sb,t)->{
+ if(first.get()) {
+ sb.append("[");
+ first.set(false);
+ } else
+ sb.append(",");
+ sb.append("{");
+ sb.append("\"tagID\":" );
+ sb.append(((TemporalDTO)t).tagID);
+ sb.append(",\"timestamp\":" );
+ sb.append(((TemporalDTO)t).timestamp);
+ sb.append(",\"value\":\"" );
+ sb.append(((TemporalDTO)t).value);
+ sb.append("\"}");
+ },
+ (sb1,sb2)->{sb1.append(sb2.toString());}
+ ).append("]").toString();
+ }
+
+ private HistoricTemporalRequest createTemporalRequest(String provider, String service, String resource,
+ LocalDateTime fromTime, LocalDateTime toTime){
+
+ HistoricTemporalRequest request = this.mediator.callService(HistoricProvider.class,
+ new Executable<HistoricProvider,HistoricTemporalRequest>(){
+ @Override
+ public HistoricTemporalRequest execute(HistoricProvider provider) throws Exception {
+ return provider.newTemporalRequest();
+ }
+ });
+ if(request == null)
+ return null;
+ request.setServiceProviderIdentifier(provider);
+ request.setServiceIdentifier(service);
+ request.setResourceIdentifier(resource);
+ request.setHistoricStartTime(fromTime);
+ request.setHistoricEndTime(toTime);
+
+ return request;
+ }
+
+ private HistoricSpatialRequest createSpatialRequest(String provider, String service, String resource,
+ String region, LocalDateTime fromTime, LocalDateTime toTime){
+
+ HistoricSpatialRequest request = this.mediator.callService(HistoricProvider.class,
+ new Executable<HistoricProvider,HistoricSpatialRequest>(){
+ @Override
+ public HistoricSpatialRequest execute(HistoricProvider provider) throws Exception {
+ return provider.newSpatialRequest();
+ }
+ });
+ if(request == null)
+ return null;
+ request.setServiceProviderIdentifier(provider);
+ request.setServiceIdentifier(service);
+ request.setResourceIdentifier(resource);
+ request.setHistoricStartTime(fromTime);
+ request.setHistoricEndTime(toTime);
+ request.setRegion(region);
+
+ return request;
+ }
+
+ private HistoricSpatioTemporalRequest createSpatioTemporalRequest(String provider, String service, String resource,
+ String region, LocalDateTime fromTime, LocalDateTime toTime){
+
+ HistoricSpatioTemporalRequest request = this.mediator.callService(HistoricProvider.class,
+ new Executable<HistoricProvider,HistoricSpatioTemporalRequest>(){
+ @Override
+ public HistoricSpatioTemporalRequest execute(HistoricProvider provider) throws Exception {
+ return provider.newSpatioTemporalRequest();
+ }
+ });
+ if(request == null)
+ return null;
+ request.setServiceProviderIdentifier(provider);
+ request.setServiceIdentifier(service);
+ request.setResourceIdentifier(resource);
+ request.setHistoricStartTime(fromTime);
+ request.setHistoricEndTime(toTime);
+ request.setRegion(region);
+
+ return request;
+ }
+}
diff --git a/platform/sensinact-historic-manager/src/main/java/org/eclipse/sensinact/gateway/historic/storage/manager/osgi/Activator.java b/platform/sensinact-historic-manager/src/main/java/org/eclipse/sensinact/gateway/historic/storage/manager/osgi/Activator.java
new file mode 100644
index 0000000..c58509c
--- /dev/null
+++ b/platform/sensinact-historic-manager/src/main/java/org/eclipse/sensinact/gateway/historic/storage/manager/osgi/Activator.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2021 Kentyou.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+* Kentyou - initial API and implementation
+ */
+package org.eclipse.sensinact.gateway.historic.storage.manager.osgi;
+
+import org.eclipse.sensinact.gateway.generic.BasisActivator;
+import org.eclipse.sensinact.gateway.generic.annotation.SensiNactBridgeConfiguration;
+import org.eclipse.sensinact.gateway.generic.packet.Packet;
+
+/**
+ * Handle the bundle activation/deactivation
+ */
+@SensiNactBridgeConfiguration(outputOnly = true)
+public class Activator extends BasisActivator<Packet> {}
diff --git a/platform/sensinact-historic-manager/src/main/resources/resources.xml b/platform/sensinact-historic-manager/src/main/resources/resources.xml
new file mode 100644
index 0000000..86b9d61
--- /dev/null
+++ b/platform/sensinact-historic-manager/src/main/resources/resources.xml
@@ -0,0 +1,104 @@
+<resourceInfos
+ xmlns="http://org.eclipse.sensinact/resource"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://org.eclipse.sensinact/resource ../../../../sensinact-generic/src/main/resources/sensinact-resource.xsd"
+ empty_profile="true">
+ <resourceInfo xsi:type="resourceInfoProperty" name="REQUESTER" target="history">
+ <policy name="PROPERTY" update="NONE" />
+ <identifier xsi:type="stringContent">requester</identifier>
+ <attributes>
+ <attribute name="value" type="string" modifiable="UPDATABLE" />
+ </attributes>
+ <methods>
+ <method type="GET">
+ <parameter name="attributeName" type="string">
+ <constraints>
+ <fixed value="value"/>
+ </constraints>
+ </parameter>
+ <parameter name="provider" type="string"/>
+ <parameter name="service" type="string"/>
+ <parameter name="resource" type="string"/>
+ <parameter name="from" type="string"/>
+ <parameter name="to" type="string"/>
+ <parameter name="function" type="string"/>
+ <parameter name="window" type="string"/>
+ <parameter name="region" type="string"/>
+ </method>
+ <method type="GET">
+ <parameter name="attributeName" type="string">
+ <constraints>
+ <fixed value="value"/>
+ </constraints>
+ </parameter>
+ <parameter name="provider" type="string"/>
+ <parameter name="service" type="string"/>
+ <parameter name="resource" type="string"/>
+ <parameter name="from" type="string"/>
+ <parameter name="to" type="string"/>
+ <parameter name="function" type="string"/>
+ <parameter name="window" type="string"/>
+ <parameter name="region" type="string">
+ <constraints>
+ <fixed value="#NONE#"/>
+ </constraints>
+ </parameter>
+ </method>
+ <method type="GET">
+ <parameter name="attributeName" type="string">
+ <constraints>
+ <fixed value="value"/>
+ </constraints>
+ </parameter>
+ <parameter name="provider" type="string"/>
+ <parameter name="service" type="string"/>
+ <parameter name="resource" type="string"/>
+ <parameter name="from" type="string"/>
+ <parameter name="to" type="string"/>
+ <parameter name="function" type="string">
+ <constraints>
+ <fixed value="#NONE#"/>
+ </constraints>
+ </parameter>
+ <parameter name="window" type="string">
+ <constraints>
+ <fixed value="-1"/>
+ </constraints>
+ </parameter>
+ <parameter name="region" type="string"/>
+ </method>
+ <method type="GET">
+ <parameter name="attributeName" type="string">
+ <constraints>
+ <fixed value="value"/>
+ </constraints>
+ </parameter>
+ <parameter name="provider" type="string"/>
+ <parameter name="service" type="string"/>
+ <parameter name="resource" type="string"/>
+ <parameter name="from" type="string"/>
+ <parameter name="to" type="string"/>
+ <parameter name="function" type="string">
+ <constraints>
+ <fixed value="#NONE#"/>
+ </constraints>
+ </parameter>
+ <parameter name="window" type="string">
+ <constraints>
+ <fixed value="-1"/>
+ </constraints>
+ </parameter>
+ <parameter name="region" type="string">
+ <constraints>
+ <fixed value="#NONE#"/>
+ </constraints>
+ </parameter>
+ </method>
+ </methods>
+ </resourceInfo>
+ <devices>
+ <device identifier="historicManager">
+ <service name="history"/>
+ </device>
+ </devices>
+</resourceInfos>
\ No newline at end of file