Bladeren bron

SensLog -> AFarCloud

Lukas Cerny 5 jaren geleden
bovenliggende
commit
343561131b
41 gewijzigde bestanden met toevoegingen van 1327 en 43 verwijderingen
  1. 1 0
      .gitignore
  2. 1 1
      config/lorawanSenslog1.yaml
  3. 47 0
      config/senslog1AFarCloud.yaml
  4. 11 7
      connector-app/src/test/java/cz/senslog/connector/app/config/ConnectorBuilderTest.java
  5. 1 2
      connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/ConnectorFetchProvider.java
  6. 6 10
      connector-fetch-azure/src/test/java/cz/senslog/connector/fetch/azure/AzureFetcherTest.java
  7. 3 6
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateConfig.java
  8. 0 6
      connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateSession.java
  9. 38 0
      connector-fetch-senslog-v1/pom.xml
  10. 38 0
      connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/AllowedStation.java
  11. 56 0
      connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SensLogSession.java
  12. 55 0
      connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SenslogConfig.java
  13. 31 0
      connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SenslogFetchProvider.java
  14. 186 0
      connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SenslogFetcher.java
  15. 1 0
      connector-fetch-senslog-v1/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider
  16. 45 0
      connector-fetch-senslog-v1/src/test/java/cz/senslog/connector/fetch/senslog/v1/SenslogFetcherTest.java
  17. 20 0
      connector-model/src/main/java/cz/senslog/connector/model/afarcloud/AFarCloudModel.java
  18. 32 0
      connector-model/src/main/java/cz/senslog/connector/model/afarcloud/Location.java
  19. 41 0
      connector-model/src/main/java/cz/senslog/connector/model/afarcloud/MultiSimpleObservation.java
  20. 45 0
      connector-model/src/main/java/cz/senslog/connector/model/afarcloud/SimpleObservation.java
  21. 0 1
      connector-model/src/main/java/cz/senslog/connector/model/api/AbstractModel.java
  22. 5 0
      connector-model/src/main/java/cz/senslog/connector/model/api/ProxySessionModel.java
  23. 6 6
      connector-model/src/main/java/cz/senslog/connector/model/azure/SensorType.java
  24. 7 0
      connector-model/src/main/java/cz/senslog/connector/model/config/UnitSensorChecker.java
  25. 149 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/AFarCloudUnitSensorConverter.java
  26. 1 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/ModelConverterProvider.java
  27. 72 0
      connector-model/src/main/java/cz/senslog/connector/model/converter/SenslogV1ModelAFarCloudModelConverter.java
  28. 16 1
      connector-model/src/main/java/cz/senslog/connector/model/v1/SenslogV1Model.java
  29. 83 0
      connector-model/src/main/java/cz/senslog/connector/model/v1/SensorInfo.java
  30. 65 0
      connector-model/src/main/java/cz/senslog/connector/model/v1/UnitInfo.java
  31. 57 0
      connector-model/src/test/java/cz/senslog/connector/model/converter/SenslogV1ModelAFarCloudModelConverterTest.java
  32. 43 0
      connector-push-afarcloud/pom.xml
  33. 17 0
      connector-push-afarcloud/src/main/java/cz/senslog/connector/push/afarcloud/AFarCloudConfig.java
  34. 30 0
      connector-push-afarcloud/src/main/java/cz/senslog/connector/push/afarcloud/AFarCloudPushProvider.java
  35. 67 0
      connector-push-afarcloud/src/main/java/cz/senslog/connector/push/afarcloud/AFarCloudPusher.java
  36. 1 0
      connector-push-afarcloud/src/main/resources/META-INF/services/cz.senslog.connector.push.api.ConnectorPushProvider
  37. 16 0
      connector-push-afarcloud/src/test/java/cz/senslog/connector/push/afarcloud/AFarCloudPusherTest.java
  38. 2 1
      connector-push-rest-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConnectorPushProvider.java
  39. 1 2
      docker/Dockerfile
  40. 21 0
      docker/DockerfileJar
  41. 10 0
      pom.xml

+ 1 - 0
.gitignore

@@ -3,3 +3,4 @@
 bin
 logs
 target
+*.log

+ 1 - 1
config/lorawanSenslog1.yaml

@@ -47,4 +47,4 @@ connectors:
         fetcher: "AzureLoraWan"
         pusher: "SenslogV1"
         period: 30
-        initDelay: 5
+        initDelay: 30

+ 47 - 0
config/senslog1AFarCloud.yaml

@@ -0,0 +1,47 @@
+api1: &sensLogApiDomain
+    domain: "http://foodie.lesprojekt.cz:8080/MapLogOT"
+
+api2: &afcApiDomain
+    domain: "https://torcos.etsist.upm.es:9207"
+
+settings:
+    - Senslog:
+        name: "SensLog v1"
+        provider: "cz.senslog.connector.fetch.senslog.v1.SenslogFetchProvider"
+
+        startDate: 2019-07-14T13:50:00.000
+        interval: 5 # hours
+
+        user: "afarcloud"
+        group: "afc"
+
+        sensorServiceHost:
+            <<: *sensLogApiDomain
+            path: "SensorService"
+
+        dataServiceHost:
+            <<: *sensLogApiDomain
+            path: "DataService"
+
+        allowedStation:
+            # station_id: [sensor_id]
+            "10002376": [340020000, 410010000, 560030000]
+#            "10002222": [340020000, 410010000, 560030000]
+#            "1305167561991327": [340380097, 410150097, 360200000, 780010097]
+#            "1305167562028072": [340380097, 410150097, 360200000, 780010097]
+#            "1305167562287832": [340420000, 410180000, 360200000, 460090000, 470160000, 470180000, 480080000, 620030000]
+
+    - AFC:
+        name: "AFarCloud"
+        provider: "cz.senslog.connector.push.afarcloud.AFarCloudPushProvider"
+
+        telemetryHost:
+            <<: *afcApiDomain
+            path: "/telemetry"
+
+connectors:
+    - SenslogToAFC:
+        fetcher: "Senslog"
+        pusher: "AFC"
+        period: 30
+        initDelay: 1

+ 11 - 7
connector-app/src/test/java/cz/senslog/connector/app/config/ConnectorBuilderTest.java

@@ -1,8 +1,6 @@
 package cz.senslog.connector.app.config;
 
 import cz.senslog.connector.app.config.api.ConfigurationService;
-import cz.senslog.connector.model.config.ConnectorDescriptor;
-import cz.senslog.connector.model.config.DefaultConfig;
 import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
 import cz.senslog.connector.fetch.api.ConnectorFetcher;
 import cz.senslog.connector.fetch.api.ExecutableFetcher;
@@ -10,6 +8,8 @@ import cz.senslog.connector.model.api.AbstractModel;
 import cz.senslog.connector.model.api.Converter;
 import cz.senslog.connector.model.api.ConverterProvider;
 import cz.senslog.connector.model.api.ProxySessionModel;
+import cz.senslog.connector.model.config.ConnectorDescriptor;
+import cz.senslog.connector.model.config.DefaultConfig;
 import cz.senslog.connector.push.api.ConnectorPushProvider;
 import cz.senslog.connector.push.api.ConnectorPusher;
 import org.junit.jupiter.api.Test;
@@ -33,15 +33,19 @@ class ConnectorBuilderTest {
         public BasicSessionModel() { super(false); }
     }
 
-    private final ConnectorFetchProvider defaultFetchProvider = config -> ExecutableFetcher.create(new ConnectorFetcher<BasicSessionModel, OutputModel>() {
-        @Override public void init() {}
+    private static class TestingFetcher implements ConnectorFetcher<BasicSessionModel, OutputModel> {
+        @Override public void init() { }
         @Override public OutputModel fetch(Optional<BasicSessionModel> session) { return new OutputModel(); }
-    });
+    }
 
-    private final ConnectorPushProvider defaultPushProvider = config -> new ConnectorPusher<InputModel>() {
+    private static class TestingPusher implements ConnectorPusher<InputModel> {
         @Override public void init() {}
         @Override public void push(InputModel model) {}
-    };
+    }
+
+    private final ConnectorFetchProvider defaultFetchProvider = config -> ExecutableFetcher.create(new TestingFetcher());
+
+    private final ConnectorPushProvider defaultPushProvider = config -> new TestingPusher();
 
     @Test
     void createConnectors_CreateTestConnector_CreatedOneConnector() {

+ 1 - 2
connector-fetch-api/src/main/java/cz/senslog/connector/fetch/api/ConnectorFetchProvider.java

@@ -1,7 +1,6 @@
 package cz.senslog.connector.fetch.api;
 
 import cz.senslog.connector.model.api.AbstractModel;
-import cz.senslog.connector.model.api.ProxySessionModel;
 import cz.senslog.connector.model.config.DefaultConfig;
 
 /**
@@ -19,5 +18,5 @@ public interface ConnectorFetchProvider {
      * @param defaultConfig - default configuration.
      * @return new instance of {@link ConnectorFetcher}.
      */
-    ExecutableFetcher<? extends ConnectorFetcher<? extends ProxySessionModel, ? extends AbstractModel>> createExecutableFetcher(DefaultConfig defaultConfig);
+    ExecutableFetcher<? extends AbstractModel> createExecutableFetcher(DefaultConfig defaultConfig);
 }

+ 6 - 10
connector-fetch-azure/src/test/java/cz/senslog/connector/fetch/azure/AzureFetcherTest.java

@@ -2,31 +2,26 @@
 package cz.senslog.connector.fetch.azure;
 
 
-import cz.senslog.connector.fetch.api.ExecutableFetcher;
-import cz.senslog.connector.fetch.azure.auth.AuthenticationService;
-import cz.senslog.connector.model.config.DefaultConfig;
-import cz.senslog.connector.model.config.HostConfig;
 import cz.senslog.common.http.HttpClient;
 import cz.senslog.common.http.HttpCode;
 import cz.senslog.common.http.HttpRequest;
 import cz.senslog.common.http.HttpResponse;
+import cz.senslog.connector.fetch.azure.auth.AuthenticationService;
 import cz.senslog.connector.model.azure.AzureModel;
 import cz.senslog.connector.model.azure.SensorInfo;
-import cz.senslog.connector.model.config.PropertyConfig;
-import cz.senslog.connector.model.fieldclimate.FieldClimateModel;
+import cz.senslog.connector.model.config.HostConfig;
 import org.junit.jupiter.api.Test;
 import org.mockito.stubbing.Answer;
 
 import java.time.LocalDateTime;
 import java.util.*;
 
-import static cz.senslog.common.http.HttpClient.newHttpClient;
-import static cz.senslog.common.http.HttpClient.newHttpSSLClient;
 import static cz.senslog.common.http.HttpCode.SERVER_ERROR;
 import static cz.senslog.common.json.BasicJson.objectToJson;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 class AzureFetcherTest {
 
@@ -269,6 +264,7 @@ class AzureFetcherTest {
         assertEquals(0, model.getSensors().get(0).getData().size());
     }
 
+    /*
     @Test
     void fetch() throws Exception {
 
@@ -312,6 +308,6 @@ class AzureFetcherTest {
 
         executableFetcher.execute();
         executableFetcher.execute();
-
     }
+    */
 }

+ 3 - 6
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateConfig.java

@@ -1,14 +1,10 @@
 package cz.senslog.connector.fetch.fieldclimate;
 
+import cz.senslog.connector.fetch.fieldclimate.auth.AuthConfig;
 import cz.senslog.connector.model.config.DefaultConfig;
 import cz.senslog.connector.model.config.HostConfig;
-import cz.senslog.connector.fetch.fieldclimate.auth.AuthConfig;
 
 import java.time.LocalDateTime;
-import java.time.OffsetTime;
-import java.time.ZoneOffset;
-import java.util.List;
-import java.util.Set;
 import java.util.TimeZone;
 
 public class FieldClimateConfig {
@@ -30,11 +26,12 @@ public class FieldClimateConfig {
         this.stationTimeRangeHost = new HostConfig(config.getPropertyConfig("stationTimeRangeHost"));
         this.authentication = new AuthConfig(config.getPropertyConfig("authentication"));
         this.period = config.getIntegerProperty("period");
-        this.allowedStation = new AllowedStation(config.getPropertyConfig("allowedStation"));
         this.timeZone = TimeZone.getTimeZone(config.getStringProperty("timeZone"));
 
         this.sessionProxy = config.containsProperty("sessionProxy") ?
                 new FieldClimateSessionProxyConfig(config.getPropertyConfig("sessionProxy")) : null;
+
+        this.allowedStation = new AllowedStation(config.getPropertyConfig("allowedStation"));
     }
 
     public LocalDateTime getStartDate() {

+ 0 - 6
connector-fetch-fieldclimate/src/main/java/cz/senslog/connector/fetch/fieldclimate/FieldClimateSession.java

@@ -66,10 +66,4 @@ public class FieldClimateSession extends ProxySessionModel {
         }
         return removed.toArray(new String[0]);
     }
-
-    @Override
-    public String toString() {
-        boolean isEmpty = !isActive() && unitSessions.isEmpty();
-        return "FieldClimateSession." + (isEmpty ? "empty" : "active");
-    }
 }

+ 38 - 0
connector-fetch-senslog-v1/pom.xml

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>connectors</artifactId>
+        <groupId>cz.senslog</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>connector-fetch-senslog-v1</artifactId>
+    <name>fetch-senslog-v1</name>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-fetch-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>common</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 38 - 0
connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/AllowedStation.java

@@ -0,0 +1,38 @@
+package cz.senslog.connector.fetch.senslog.v1;
+
+import cz.senslog.connector.model.config.PropertyConfig;
+
+import java.util.*;
+
+class AllowedStation {
+
+    private final Map<Long, Set<Long>> stations;
+
+    AllowedStation(PropertyConfig config) {
+        Set<String> attributes = config.getAttributes();
+        Map<Long, Set<Long>> stations = new HashMap<>(attributes.size());
+        for (String stationId : attributes) {
+            long unitId = Long.parseLong(stationId);
+            Object attrValue = config.getProperty(stationId);
+            if (attrValue instanceof List) {
+                List<?> sensors = (List<?>)attrValue;
+                for (Object sensor : sensors) {
+                    if (sensor instanceof Integer || sensor instanceof Long) {
+                        Long sensorId = Long.parseLong(sensor.toString());
+                        stations.computeIfAbsent(unitId, k -> new HashSet<>()).add(sensorId);
+                    }
+                }
+            }
+        }
+
+        this.stations = stations;
+    }
+
+    public boolean isAllowed(long unitId) {
+        return stations.containsKey(unitId);
+    }
+
+    public boolean isAllowed(long unitId, long sensorId) {
+        return stations.containsKey(unitId) && stations.get(unitId).contains(sensorId);
+    }
+}

+ 56 - 0
connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SensLogSession.java

@@ -0,0 +1,56 @@
+package cz.senslog.connector.fetch.senslog.v1;
+
+import cz.senslog.connector.model.api.ProxySessionModel;
+
+import java.time.OffsetDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SensLogSession extends ProxySessionModel {
+
+    public static SensLogSession emptySession() {
+        return new SensLogSession(false);
+    }
+
+    protected static class Info {
+        OffsetDateTime lastFetch;
+
+        Info() {
+            this.lastFetch = OffsetDateTime.MIN;
+        }
+    }
+
+    private OffsetDateTime globalFrom, globalTo;
+    private Map<String, Info> data;
+
+    public SensLogSession(boolean isActive) {
+        super(isActive);
+        this.data = new HashMap<>();
+        this.globalFrom = OffsetDateTime.MAX;
+        this.globalTo = OffsetDateTime.MIN;
+    }
+
+    public Info getLiveInfo(String id) {
+        return data.computeIfAbsent(id, k -> new Info());
+    }
+
+
+    public void updateLastFetch(String id, OffsetDateTime dateTime) {
+        getLiveInfo(id).lastFetch = dateTime;
+
+        if (dateTime.isBefore(globalFrom)) {
+            this.globalFrom = dateTime;
+        }
+        if (dateTime.isAfter(globalTo)) {
+            this.globalTo = dateTime;
+        }
+    }
+
+    public OffsetDateTime getGlobalFrom() {
+        return globalFrom;
+    }
+
+    public OffsetDateTime getGlobalTo() {
+        return globalTo;
+    }
+}

+ 55 - 0
connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SenslogConfig.java

@@ -0,0 +1,55 @@
+package cz.senslog.connector.fetch.senslog.v1;
+
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.config.HostConfig;
+
+import java.time.LocalDateTime;
+
+public class SenslogConfig {
+
+    private final LocalDateTime startDate;
+    private final HostConfig sensorServiceHost;
+    private final HostConfig dataServiceHost;
+    private final String user;
+    private final String group;
+    private final Integer interval;
+    private final AllowedStation allowedStation;
+
+    SenslogConfig(DefaultConfig defaultConfig) {
+        this.startDate = defaultConfig.getLocalDateTimeProperty("startDate");
+        this.sensorServiceHost = new HostConfig(defaultConfig.getPropertyConfig("sensorServiceHost"));
+        this.dataServiceHost = new HostConfig(defaultConfig.getPropertyConfig("dataServiceHost"));
+        this.user = defaultConfig.getStringProperty("user");
+        this.group = defaultConfig.getStringProperty("group");
+        this.interval = defaultConfig.getIntegerProperty("interval");
+        this.allowedStation = new AllowedStation(defaultConfig.getPropertyConfig("allowedStation"));
+    }
+
+    public LocalDateTime getStartDate() {
+        return startDate;
+    }
+
+    public HostConfig getSensorServiceHost() {
+        return sensorServiceHost;
+    }
+
+    public HostConfig getDataServiceHost() {
+        return dataServiceHost;
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public String getGroup() {
+        return group;
+    }
+
+    public Integer getInterval() {
+        return interval;
+    }
+
+    public AllowedStation getAllowedStation() {
+        return allowedStation;
+    }
+}

+ 31 - 0
connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SenslogFetchProvider.java

@@ -0,0 +1,31 @@
+package cz.senslog.connector.fetch.senslog.v1;
+
+import cz.senslog.connector.fetch.api.ConnectorFetchProvider;
+import cz.senslog.connector.fetch.api.ExecutableFetcher;
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.v1.SenslogV1Model;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static cz.senslog.common.http.HttpClient.newHttpClient;
+
+public class SenslogFetchProvider implements ConnectorFetchProvider {
+
+    private static Logger logger = LogManager.getLogger(SenslogFetchProvider.class);
+
+    @Override
+    public ExecutableFetcher<SenslogV1Model> createExecutableFetcher(DefaultConfig defaultConfig) {
+        logger.info("Initialization a new fetch provider {}.", SenslogFetchProvider.class);
+
+        logger.debug("Creating a new configuration.");
+        SenslogConfig config = new SenslogConfig(defaultConfig);
+        logger.info("Configuration for {} was created successfully.", SenslogFetcher.class);
+
+
+        logger.debug("Creating a new instance of {}.", SenslogFetcher.class);
+        SenslogFetcher fetcher = new SenslogFetcher(config, newHttpClient());
+        logger.info("Fetcher for {} was created successfully.", SenslogFetcher.class);
+
+        return ExecutableFetcher.create(fetcher);
+    }
+}

+ 186 - 0
connector-fetch-senslog-v1/src/main/java/cz/senslog/connector/fetch/senslog/v1/SenslogFetcher.java

@@ -0,0 +1,186 @@
+package cz.senslog.connector.fetch.senslog.v1;
+
+import com.google.gson.reflect.TypeToken;
+import cz.senslog.common.http.HttpClient;
+import cz.senslog.common.http.HttpRequest;
+import cz.senslog.common.http.HttpResponse;
+import cz.senslog.common.http.URLBuilder;
+import cz.senslog.common.util.Tuple;
+import cz.senslog.connector.fetch.api.ConnectorFetcher;
+import cz.senslog.connector.model.api.ProxySessionModel;
+import cz.senslog.connector.model.config.HostConfig;
+import cz.senslog.connector.model.v1.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.lang.reflect.Type;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+import static cz.senslog.common.json.BasicJson.jsonToObject;
+import static java.time.OffsetDateTime.now;
+import static java.time.OffsetDateTime.parse;
+import static java.time.format.DateTimeFormatter.ofPattern;
+
+public class SenslogFetcher implements ConnectorFetcher<SensLogSession, SenslogV1Model> {
+
+    private static Logger logger = LogManager.getLogger(SenslogFetcher.class);
+
+    private final SenslogConfig config;
+    private final HttpClient httpClient;
+
+    private final SensLogSession localSession;
+    private final Map<Long, UnitInfo> units;
+
+    SenslogFetcher(SenslogConfig config, HttpClient httpClient) {
+        this.config = config;
+        this.httpClient = httpClient;
+        this.localSession = SensLogSession.emptySession();
+        this.units = new HashMap<>();
+    }
+
+    protected static class ObservationInfo { Float value; OffsetDateTime time; }
+
+    @Override
+    public void init() {
+
+        HostConfig dataServiceHost = config.getDataServiceHost();
+        logger.info("Getting last observations from {}.", dataServiceHost.getDomain());
+
+        HttpRequest unitsRequest = HttpRequest.newBuilder().GET()
+                .url(URLBuilder.newBuilder(dataServiceHost.getDomain(), dataServiceHost.getPath())
+                        .addParam("Operation", "GetUnitsList")
+                        .addParam("user", config.getUser())
+                        .build())
+                .build();
+        logger.info("Creating a http request to {}.", unitsRequest);
+
+        HttpResponse unitsResponse = httpClient.send(unitsRequest);
+        logger.info("Received a response with a status: {} for the domain {}.", unitsResponse.getStatus(), dataServiceHost.getDomain());
+
+        if (unitsResponse.isOk()) {
+            logger.debug("Parsing body of the response to the list of class {}.", UnitInfo.class);
+            Type unitInfoType = new TypeToken<Collection<UnitInfo>>() {}.getType();
+            List<UnitInfo> unitInfos = jsonToObject(unitsResponse.getBody(), unitInfoType);
+
+            for (UnitInfo unit : unitInfos) {
+                if (!config.getAllowedStation().isAllowed(unit.getUnitId())) {
+                    logger.info("Unit {} is not allowd in configuration.", unit.getUnitId());continue;
+                }
+
+                units.put(unit.getUnitId(), unit);
+
+                HostConfig sensorServiceHost = config.getSensorServiceHost();
+                logger.info("Getting last observations from {}.", sensorServiceHost.getDomain());
+
+                HttpRequest sensorRequest = HttpRequest.newBuilder().GET()
+                        .url(URLBuilder.newBuilder(sensorServiceHost.getDomain(), sensorServiceHost.getPath())
+                                .addParam("Operation", "GetSensors")
+                                .addParam("user", config.getUser())
+                                .addParam("unit_id", unit.getUnitId())
+                                .build())
+                        .build();
+                logger.info("Creating a http request to {}.", sensorRequest);
+
+                HttpResponse sensorResponse = httpClient.send(sensorRequest);
+                logger.info("Received a response with a status: {} for the domain {}.", sensorResponse.getStatus(), sensorServiceHost.getDomain());
+
+                if (sensorResponse.isOk()) {
+                    logger.debug("Parsing body of the response to the list of class {}.", SensorInfo.class);
+                    Type sensorInfoType = new TypeToken<Collection<SensorInfo>>() {}.getType();
+                    List<SensorInfo> sensors = jsonToObject(sensorResponse.getBody(), sensorInfoType,
+                            Tuple.of(OffsetDateTime.class, el -> parse(el + "00", ofPattern("yyyy-MM-dd HH:mm:ssZ"))));
+
+                    logger.info("For the unit {} was added {} sensors.", unit.getUnitId(), sensors.size());
+                    List<SensorInfo> allowedSensors = new ArrayList<>();
+                    for (SensorInfo sensor : sensors) {
+                        if (config.getAllowedStation().isAllowed(unit.getUnitId(), sensor.getSensorId())) {
+                            allowedSensors.add(sensor);
+                        }
+                    }
+                    unit.setSensors(allowedSensors);
+                } else {
+                    logger.error("Can not get data from the server {}. Error {} {}",
+                            sensorServiceHost.getDomain(), sensorResponse.getStatus(), sensorResponse.getBody());
+                }
+            }
+        } else {
+            logger.error("Can not get data from the server {}. Error {} {}",
+                    dataServiceHost.getDomain(), unitsResponse.getStatus(), unitsResponse.getBody());
+        }
+    }
+
+    @Override
+    public SenslogV1Model fetch(Optional<SensLogSession> persistenceSession) {
+
+        SensLogSession session = persistenceSession.filter(ProxySessionModel::isActive).orElse(localSession);
+
+        List<Record> records = new ArrayList<>();
+
+        for (UnitInfo unit : units.values()) {
+            for (SensorInfo sensor : unit.getSensors()) {
+
+                String sessionId = String.format("%s_%s", unit.getUnitId(), sensor.getSensorId());
+                ZoneOffset offset = sensor.getFirstObservationTime().getOffset();
+
+                OffsetDateTime firstValueDate = sensor.getFirstObservationTime();
+                OffsetDateTime startDate = config.getStartDate().atOffset(offset);
+                OffsetDateTime lastFetch = session.getLiveInfo(sessionId).lastFetch;
+
+                long fromDateEpoch = Math.max(startDate.toEpochSecond(), Math.max(firstValueDate.toEpochSecond(), lastFetch.toEpochSecond()));
+                OffsetDateTime fromDate = OffsetDateTime.ofInstant(Instant.ofEpochSecond(fromDateEpoch), offset);
+                OffsetDateTime toDate = fromDate.plusHours(config.getInterval());
+
+                if (toDate.isAfter(now())) {
+                    continue;
+                }
+
+                logger.info("Getting new observations from {} to {}.", fromDate, toDate);
+
+                final HostConfig sensorServiceHost = config.getSensorServiceHost();
+                final DateTimeFormatter pattern = ofPattern("yyyy-MM-dd HH:mm:ssZ");
+                HttpRequest observationRequest = HttpRequest.newBuilder().GET()
+                        .url(URLBuilder.newBuilder(sensorServiceHost.getDomain(), sensorServiceHost.getPath())
+                                .addParam("Operation", "GetObservations")
+                                .addParam("user", config.getUser())
+                                .addParam("unit_id", unit.getUnitId())
+                                .addParam("sensor_id", sensor.getSensorId())
+                                .addParam("from", fromDate.format(pattern))
+                                .addParam("to", toDate.format(pattern))
+                                .build())
+                        .build();
+                logger.info("Creating a http request to {}.", observationRequest);
+
+                HttpResponse observationResponse = httpClient.send(observationRequest);
+                logger.info("Received a response with a status: {} for the domain {}.", observationResponse.getStatus(), sensorServiceHost.getDomain());
+
+                if (observationResponse.isOk()) {
+                    Type observationType = new TypeToken<Collection<ObservationInfo>>() {}.getType();
+                    List<ObservationInfo> observations = jsonToObject(observationResponse.getBody(), observationType,
+                            Tuple.of(OffsetDateTime.class, el -> parse(el + "00", ofPattern("yyyy-MM-dd HH:mm:ssZ")))
+                    );
+
+                    OffsetDateTime lastTimeStamp = toDate;
+                    for (ObservationInfo observation : observations) {
+                        Observation obs = new Observation();
+                        obs.setSensorId(sensor.getSensorId());
+                        obs.setUnitId(unit.getUnitId());
+                        obs.setValue(observation.value);
+                        obs.setTime(observation.time.toZonedDateTime().toOffsetDateTime());
+
+                        records.add(obs);
+
+                        if (observation.time.isAfter(lastTimeStamp)) {
+                            lastTimeStamp = observation.time;
+                        }
+                    }
+
+                    session.updateLastFetch(sessionId, lastTimeStamp);
+                }
+            }
+        }
+
+        return new SenslogV1Model(units, records, session.getGlobalFrom(), session.getGlobalTo());
+    }
+}

+ 1 - 0
connector-fetch-senslog-v1/src/main/resources/META-INF/services/cz.senslog.connector.fetch.api.ConnectorFetchProvider

@@ -0,0 +1 @@
+cz.senslog.connector.fetch.senslog.v1.SenslogFetchProvider

+ 45 - 0
connector-fetch-senslog-v1/src/test/java/cz/senslog/connector/fetch/senslog/v1/SenslogFetcherTest.java

@@ -0,0 +1,45 @@
+package cz.senslog.connector.fetch.senslog.v1;
+
+import cz.senslog.common.http.HttpClient;
+import cz.senslog.connector.model.config.DefaultConfig;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class SenslogFetcherTest {
+
+    /*
+    @Test
+    void fetch() {
+
+        DefaultConfig defaultConfig = new DefaultConfig("", null);
+
+        defaultConfig.setProperty("startDate", LocalDateTime.of(2020, 3, 11, 0, 0,0));
+        defaultConfig.setProperty("user", "afarcloud");
+        defaultConfig.setProperty("group", "afc");
+
+        Map<String, String> sensorServiceHost = new HashMap<>();
+        sensorServiceHost.put("domain", "http://foodie.lesprojekt.cz:8080/MapLogOT");
+        sensorServiceHost.put("path", "SensorService");
+        defaultConfig.setProperty("sensorServiceHost", sensorServiceHost);
+
+        Map<String, String> dataServiceHost = new HashMap<>();
+        dataServiceHost.put("domain", "http://foodie.lesprojekt.cz:8080/MapLogOT");
+        dataServiceHost.put("path", "DataService");
+        defaultConfig.setProperty("dataServiceHost", dataServiceHost);
+
+
+        SenslogConfig config = new SenslogConfig(defaultConfig);
+        SenslogFetcher fetcher = new SenslogFetcher(config, HttpClient.newHttpClient());
+
+        fetcher.init();
+        fetcher.fetch(Optional.empty());
+    }
+
+     */
+}

+ 20 - 0
connector-model/src/main/java/cz/senslog/connector/model/afarcloud/AFarCloudModel.java

@@ -0,0 +1,20 @@
+package cz.senslog.connector.model.afarcloud;
+
+import cz.senslog.connector.model.api.AbstractModel;
+
+import java.time.OffsetDateTime;
+import java.util.List;
+
+public class AFarCloudModel extends AbstractModel {
+
+    private final List<MultiSimpleObservation> unitAggObservations;
+
+    public AFarCloudModel(List<MultiSimpleObservation> unitAggObservations, OffsetDateTime from, OffsetDateTime to) {
+        super(from, to);
+        this.unitAggObservations = unitAggObservations;
+    }
+
+    public List<MultiSimpleObservation> getUnitAggObservations() {
+        return unitAggObservations;
+    }
+}

+ 32 - 0
connector-model/src/main/java/cz/senslog/connector/model/afarcloud/Location.java

@@ -0,0 +1,32 @@
+package cz.senslog.connector.model.afarcloud;
+
+public class Location {
+
+    private Double latitude;
+    private Double longitude;
+    private Double altitude;
+
+    public Double getLatitude() {
+        return latitude;
+    }
+
+    public void setLatitude(Double latitude) {
+        this.latitude = latitude;
+    }
+
+    public Double getLongitude() {
+        return longitude;
+    }
+
+    public void setLongitude(Double longitude) {
+        this.longitude = longitude;
+    }
+
+    public Double getAltitude() {
+        return altitude;
+    }
+
+    public void setAltitude(Double altitude) {
+        this.altitude = altitude;
+    }
+}

+ 41 - 0
connector-model/src/main/java/cz/senslog/connector/model/afarcloud/MultiSimpleObservation.java

@@ -0,0 +1,41 @@
+package cz.senslog.connector.model.afarcloud;
+
+import java.util.List;
+
+public class MultiSimpleObservation {
+
+    private String resourceId;
+    private Location location;
+    private List<SimpleObservation> observations;
+
+    public String getResourceId() {
+        return resourceId;
+    }
+
+    public void setResourceId(String resourceId) {
+        this.resourceId = resourceId;
+    }
+
+    public Location getLocation() {
+        return location;
+    }
+
+    public void setLocation(Location location) {
+        this.location = location;
+    }
+
+    public List<SimpleObservation> getObservations() {
+        return observations;
+    }
+
+    public void setObservations(List<SimpleObservation> observations) {
+        this.observations = observations;
+    }
+
+    @Override
+    public String toString() {
+        return "MultiSimpleObservation{" +
+                "resourceId='" + resourceId + '\'' +
+                '}';
+    }
+}

+ 45 - 0
connector-model/src/main/java/cz/senslog/connector/model/afarcloud/SimpleObservation.java

@@ -0,0 +1,45 @@
+package cz.senslog.connector.model.afarcloud;
+
+public class SimpleObservation {
+
+    protected static class Result {
+        Double value;
+        String uom;
+    }
+
+    private String observedProperty;
+    private Long resultTime;
+    private Result result;
+
+    public String getObservedProperty() {
+        return observedProperty;
+    }
+
+    public void setObservedProperty(String observedProperty) {
+        this.observedProperty = observedProperty;
+    }
+
+    public Long getResultTime() {
+        return resultTime;
+    }
+
+    public void setResultTime(Long resultTime) {
+        this.resultTime = resultTime;
+    }
+
+
+    public Result getResult() {
+        return result;
+    }
+
+    public void setResult(Double value, String uom) {
+        this.result = new Result();
+        this.result.value = value;
+        this.result.uom = uom;
+    }
+
+    public void setResult(Double value) {
+        this.result = new Result();
+        this.result.value = value;
+    }
+}

+ 0 - 1
connector-model/src/main/java/cz/senslog/connector/model/api/AbstractModel.java

@@ -1,6 +1,5 @@
 package cz.senslog.connector.model.api;
 
-import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 
 /**

+ 5 - 0
connector-model/src/main/java/cz/senslog/connector/model/api/ProxySessionModel.java

@@ -11,4 +11,9 @@ public abstract class ProxySessionModel {
     public boolean isActive() {
         return isActive;
     }
+
+    @Override
+    public String toString() {
+        return String.format("%s.%s", getClass().getSimpleName(), (isActive() ? "active" : "disable"));
+    }
 }

+ 6 - 6
connector-model/src/main/java/cz/senslog/connector/model/azure/SensorType.java

@@ -10,12 +10,12 @@ package cz.senslog.connector.model.azure;
  */
 public enum SensorType {
 
-    TEMPERATURE     (340020000),
-    HUMIDITY        (410010000),
+    TEMPERATURE     (340020000), // 10002376: 2020-03-11 10:05:37+01  10002222: 2020-02-10 06:32:57+01
+    HUMIDITY        (410010000), // 10002376: 2020-03-11 10:05:37+01  10002222: 2020-02-10 06:32:57+01
     CO2             (780020000),
-    RSSI            (380010000),
-    SNR             (380090000),
-    BATTERY_LEVEL   (560030000),
+    RSSI            (380010000), // 10002376: 2020-03-11 10:05:37+01  10002222: 2020-02-10 06:32:57+01
+    SNR             (380090000), // 10002376: 2020-03-11 10:05:37+01  10002222: 2020-02-10 06:32:57+01
+    BATTERY_LEVEL   (560030000), // 10002376: 2020-03-11 10:05:37+01  10002222: 2020-02-10 06:32:57+01
 
     ;
     private final long id;
@@ -31,4 +31,4 @@ public enum SensorType {
     public static int count() {
         return values().length;
     }
-}
+}

+ 7 - 0
connector-model/src/main/java/cz/senslog/connector/model/config/UnitSensorChecker.java

@@ -0,0 +1,7 @@
+package cz.senslog.connector.model.config;
+
+public interface UnitSensorChecker<U, S> {
+
+    boolean isValid(U unit);
+    boolean isValid(U unit, S sensor);
+}

+ 149 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/AFarCloudUnitSensorConverter.java

@@ -0,0 +1,149 @@
+package cz.senslog.connector.model.converter;
+
+
+import cz.senslog.common.util.Tuple;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public final class AFarCloudUnitSensorConverter {
+
+    private final static class Mapping<K, V> extends HashMap<K, V> {
+        private final Map<V, K> reverseMap = new HashMap<>();
+
+        @Override
+        public V put(K key, V value) {
+            reverseMap.put(value, key);
+            return super.put(key, value);
+        }
+
+        public V getValue(K key) {
+            return super.get(key);
+        }
+
+        public K getKey(V value) {
+            return reverseMap.get(value);
+        }
+    }
+
+    private final static Mapping<Long, String> UNITS = new Mapping<>();
+    private final static Map<Long, Mapping<Long, String>> SENSORS = new HashMap<>();
+
+    private static void register(long unitId, String resourceId, Mapping<Long, String> sensorsMapping) {
+        UNITS.put(unitId, resourceId);
+        SENSORS.put(unitId, sensorsMapping);
+    }
+
+    static {
+        // 10002376L
+        Mapping<Long, String> sensors = new Mapping<>();
+
+        sensors.put(340020000L, "air_temperature");
+        sensors.put(410010000L, "air_humidity");
+        sensors.put(560030000L, "battery");
+
+        register(10002376L, "10002376", sensors);
+    }
+
+    static {
+        // 10002222L
+        Mapping<Long, String> sensors = new Mapping<>();
+
+        sensors.put(340020000L, "air_temperature");
+        sensors.put(410010000L, "air_humidity");
+        sensors.put(560030000L, "battery");
+
+        register(10002222L, "10002222", sensors);
+    }
+
+    static {
+        // 1305167561991327L
+        Mapping<Long, String> sensors = new Mapping<>();
+
+        sensors.put(340380097L, "air_temperature");
+        sensors.put(410150097L, "air_humidity");
+        sensors.put(360200000L, "battery");
+        sensors.put(780010097L, "co2");
+
+        register(1305167561991327L, "1305167561991327", sensors);
+    }
+
+    static {
+        // 1305167562028072L
+        Mapping<Long, String> sensors = new Mapping<>();
+
+        sensors.put(340380097L, "air_temperature");
+        sensors.put(410150097L, "air_humidity");
+        sensors.put(360200000L, "battery");
+        sensors.put(780010097L, "co2");
+
+        register(1305167562028072L, "1305167562028072", sensors);
+    }
+
+    static {
+        // 1305167562287832
+        Mapping<Long, String> sensors = new Mapping<>();
+
+        sensors.put(340420000L, "air_temperature");
+        sensors.put(410180000L, "air_humidity");
+        sensors.put(360200000L, "battery");
+        sensors.put(460090000L, "air_pressure");
+        sensors.put(470160000L, "wind_speed");
+        sensors.put(470180000L, "wind_direction");
+        sensors.put(480080000L, "rainfall");
+        sensors.put(620030000L, "solar_radiation");
+
+        register(1305167562287832L, "1305167562287832", sensors);
+    }
+
+    public static long afcResourceIdToSensLogUnitId(String resourceId) {
+        Long unitId = UNITS.getKey(resourceId);
+        return unitId != null ? unitId : -1;
+    }
+
+    public static Tuple<Long, Long> afcToSensLog(String resourceId, String observedProperty) {
+        Long unitId = UNITS.getKey(resourceId);
+        if (unitId == null) {
+            return null;
+        }
+        Mapping<Long, String> sensors = SENSORS.get(unitId);
+        if (sensors == null) {
+            return null;
+        }
+
+        Long sensorId = sensors.getKey(observedProperty);
+        if (sensorId == null) {
+            return null;
+        }
+
+        return Tuple.of(unitId, sensorId);
+    }
+
+    public static String sensLogUnitIdToAfcResourceId(long unitId) {
+        return UNITS.getValue(unitId);
+    }
+
+    public static String sensLogSensorIdToAfcObservedProperty(long unitId, long sensorId) {
+        Mapping<Long, String> sensors = SENSORS.get(unitId);
+        if (sensors == null) {
+            return null;
+        }
+        return sensors.getValue(sensorId);
+    }
+
+    public static Tuple<String, String> sensLogToAfc(long unitId, long sensorId) {
+        String resourceId = UNITS.getValue(unitId);
+        if (resourceId == null) {
+            return null;
+        }
+        Mapping<Long, String> sensors = SENSORS.get(unitId);
+        if (sensors == null) {
+            return null;
+        }
+        String observedProperty = sensors.getValue(sensorId);
+        if (observedProperty == null) {
+            return null;
+        }
+        return Tuple.of(resourceId, observedProperty);
+    }
+}

+ 1 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/ModelConverterProvider.java

@@ -20,5 +20,6 @@ public class ModelConverterProvider extends ConverterProvider {
         register(AzureModelSenslogV1ModelConverter.class);
         register(AzureModelSenslogV2ModelConverter.class);
         register(FieldClimateModelSenslogV1ModelConverter.class);
+        register(SenslogV1ModelAFarCloudModelConverter.class);
     }
 }

+ 72 - 0
connector-model/src/main/java/cz/senslog/connector/model/converter/SenslogV1ModelAFarCloudModelConverter.java

@@ -0,0 +1,72 @@
+package cz.senslog.connector.model.converter;
+
+import cz.senslog.connector.model.afarcloud.AFarCloudModel;
+import cz.senslog.connector.model.afarcloud.MultiSimpleObservation;
+import cz.senslog.connector.model.afarcloud.SimpleObservation;
+import cz.senslog.connector.model.api.Converter;
+import cz.senslog.connector.model.v1.Observation;
+import cz.senslog.connector.model.v1.Record;
+import cz.senslog.connector.model.v1.SenslogV1Model;
+import cz.senslog.connector.model.v1.UnitInfo;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static cz.senslog.connector.model.converter.AFarCloudUnitSensorConverter.sensLogSensorIdToAfcObservedProperty;
+import static cz.senslog.connector.model.converter.AFarCloudUnitSensorConverter.sensLogUnitIdToAfcResourceId;
+
+public final class SenslogV1ModelAFarCloudModelConverter implements Converter<SenslogV1Model, AFarCloudModel> {
+
+    private static Logger logger = LogManager.getLogger(SenslogV1ModelAFarCloudModelConverter.class);
+
+    @Override
+    public AFarCloudModel convert(SenslogV1Model model) {
+
+        Map<UnitInfo, List<Observation>> unitObservations = new HashMap<>();
+        for (Record record : model.getObservations()) {
+            UnitInfo unitInfo = model.getUnitInfos().get(record.getUnitId());
+            if (record instanceof Observation) {
+                unitObservations.computeIfAbsent(unitInfo, k -> new ArrayList<>())
+                        .add((Observation)record);
+            }
+        }
+
+        List<MultiSimpleObservation> result = new ArrayList<>();
+        for (Map.Entry<UnitInfo, List<Observation>> unitEntry : unitObservations.entrySet()) {
+            MultiSimpleObservation multiObservation = createSimpleObservationSimplified(unitEntry.getKey(), unitEntry.getValue());
+
+            if (multiObservation != null) {
+                result.add(multiObservation);
+            }
+        }
+
+        return new AFarCloudModel(result, model.getFrom(), model.getTo());
+    }
+
+    private static MultiSimpleObservation createSimpleObservationSimplified(UnitInfo unitInfo,  List<Observation> observations) {
+        MultiSimpleObservation result = new MultiSimpleObservation();
+
+        String resourceId = sensLogUnitIdToAfcResourceId(unitInfo.getUnitId());
+        if (resourceId == null) { return null; }
+
+        result.setResourceId(resourceId);
+        result.setObservations(new ArrayList<>(observations.size()));
+
+        for (Observation observation : observations) {
+            String observedProperty = sensLogSensorIdToAfcObservedProperty(unitInfo.getUnitId(), observation.getSensorId());
+            if (observedProperty == null) { continue; }
+
+            SimpleObservation sObs = new SimpleObservation();
+            sObs.setObservedProperty(observedProperty);
+            sObs.setResult(observation.getValue().doubleValue());
+            sObs.setResultTime(observation.getTime().toEpochSecond());
+            result.getObservations().add(sObs);
+        }
+
+        return result;
+    }
+}

+ 16 - 1
connector-model/src/main/java/cz/senslog/connector/model/v1/SenslogV1Model.java

@@ -2,9 +2,10 @@ package cz.senslog.connector.model.v1;
 
 import cz.senslog.connector.model.api.AbstractModel;
 
-import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * The class {@code SenslogV1Model} represents a model which contains
@@ -19,6 +20,9 @@ public class SenslogV1Model extends AbstractModel {
     /** List of observations */
     private final List<Record> observations;
 
+    /** Map of units related to the observations  */
+    private final Map<Long, UnitInfo> unitInfos;
+
     /**
      * Constructor of the class sets all attributes.
      * @param observations - list of observations.
@@ -28,6 +32,17 @@ public class SenslogV1Model extends AbstractModel {
     public SenslogV1Model(List<Record> observations, OffsetDateTime from, OffsetDateTime to) {
         super(from, to);
         this.observations = observations;
+        this.unitInfos = Collections.emptyMap();
+    }
+
+    public SenslogV1Model(Map<Long, UnitInfo> unitInfos, List<Record> observations, OffsetDateTime from, OffsetDateTime to) {
+        super(from, to);
+        this.observations = observations;
+        this.unitInfos = unitInfos;
+    }
+
+    public Map<Long, UnitInfo> getUnitInfos() {
+        return unitInfos;
     }
 
     public List<Record> getObservations() {

+ 83 - 0
connector-model/src/main/java/cz/senslog/connector/model/v1/SensorInfo.java

@@ -0,0 +1,83 @@
+package cz.senslog.connector.model.v1;
+
+import java.time.OffsetDateTime;
+
+public class SensorInfo {
+
+    public static class Phenomenon {
+
+        private String phenomenonName;
+        private String unit;
+
+        public String getPhenomenonName() {
+            return phenomenonName;
+        }
+
+        public void setPhenomenonName(String phenomenonName) {
+            this.phenomenonName = phenomenonName;
+        }
+
+        public String getUnit() {
+            return unit;
+        }
+
+        public void setUnit(String unit) {
+            this.unit = unit;
+        }
+    }
+
+    private OffsetDateTime firstObservationTime;
+    private OffsetDateTime lastObservationTime;
+    private Phenomenon phenomenon;
+    private Long sensorId;
+    private String sensorName;
+    private String sensorType;
+
+    public OffsetDateTime getFirstObservationTime() {
+        return firstObservationTime;
+    }
+
+    public void setFirstObservationTime(OffsetDateTime firstObservationTime) {
+        this.firstObservationTime = firstObservationTime;
+    }
+
+    public OffsetDateTime getLastObservationTime() {
+        return lastObservationTime;
+    }
+
+    public void setLastObservationTime(OffsetDateTime lastObservationTime) {
+        this.lastObservationTime = lastObservationTime;
+    }
+
+    public Phenomenon getPhenomenon() {
+        return phenomenon;
+    }
+
+    public void setPhenomenon(Phenomenon phenomenon) {
+        this.phenomenon = phenomenon;
+    }
+
+    public Long getSensorId() {
+        return sensorId;
+    }
+
+    public void setSensorId(Long sensorId) {
+        this.sensorId = sensorId;
+    }
+
+    public String getSensorName() {
+        return sensorName;
+    }
+
+    public void setSensorName(String sensorName) {
+        this.sensorName = sensorName;
+    }
+
+    public String getSensorType() {
+        return sensorType;
+    }
+
+    public void setSensorType(String sensorType) {
+        this.sensorType = sensorType;
+    }
+}

+ 65 - 0
connector-model/src/main/java/cz/senslog/connector/model/v1/UnitInfo.java

@@ -0,0 +1,65 @@
+package cz.senslog.connector.model.v1;
+
+import java.util.*;
+
+public class UnitInfo {
+
+    private String description;
+    private Integer holderId;
+    private Long unitId;
+
+    Map<Long, SensorInfo> sensors;
+
+    public UnitInfo() {
+        this.sensors = new HashMap<>();
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public Integer getHolderId() {
+        return holderId;
+    }
+
+    public void setHolderId(Integer holderId) {
+        this.holderId = holderId;
+    }
+
+    public Long getUnitId() {
+        return unitId;
+    }
+
+    public void setUnitId(Long unitId) {
+        this.unitId = unitId;
+    }
+
+    public Collection<SensorInfo> getSensors() {
+        return sensors.values();
+    }
+
+    public SensorInfo getSensor(long id) {
+        return sensors.get(id);
+    }
+
+    public void setSensors(List<SensorInfo> sensorInfos) {
+        sensorInfos.forEach(s -> this.sensors.putIfAbsent(s.getSensorId(), s));
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        UnitInfo unitInfo = (UnitInfo) o;
+        return Objects.equals(unitId, unitInfo.unitId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(unitId);
+    }
+}

+ 57 - 0
connector-model/src/test/java/cz/senslog/connector/model/converter/SenslogV1ModelAFarCloudModelConverterTest.java

@@ -0,0 +1,57 @@
+package cz.senslog.connector.model.converter;
+
+import cz.senslog.connector.model.afarcloud.AFarCloudModel;
+import cz.senslog.connector.model.v1.*;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static cz.senslog.common.json.BasicJson.objectToJson;
+import static org.junit.jupiter.api.Assertions.*;
+
+class SenslogV1ModelAFarCloudModelConverterTest {
+
+    @Test
+    void convert() {
+
+        ZonedDateTime now = ZonedDateTime.now();
+
+        Map<Long, UnitInfo> unitInfos = new HashMap<>();
+        UnitInfo unitInfo = new UnitInfo();
+        unitInfo.setUnitId(42L);
+        unitInfo.setDescription("description");
+        unitInfo.setHolderId(0);
+        unitInfos.put(unitInfo.getUnitId(), unitInfo);
+
+        SensorInfo sensorInfo = new SensorInfo();
+        sensorInfo.setSensorId(10L);
+        sensorInfo.setPhenomenon(new SensorInfo.Phenomenon());
+        sensorInfo.getPhenomenon().setPhenomenonName("<phenomenon_name");
+        sensorInfo.getPhenomenon().setUnit("%");
+        List<SensorInfo> sensorInfos = new ArrayList<>();
+        sensorInfos.add(sensorInfo);
+        unitInfo.setSensors(sensorInfos);
+
+        List<Record> observations = new ArrayList<>();
+        Observation observation = new Observation();
+        observation.setUnitId(unitInfo.getUnitId());
+        observation.setSensorId(sensorInfo.getSensorId());
+        observation.setValue(10F);
+        observation.setTime(now.toOffsetDateTime());
+        observations.add(observation);
+
+        SenslogV1Model senslogV1Model = new SenslogV1Model(unitInfos, observations, OffsetDateTime.MIN, OffsetDateTime.MAX);
+
+        SenslogV1ModelAFarCloudModelConverter converter = new SenslogV1ModelAFarCloudModelConverter();
+
+        AFarCloudModel aFarCloudModel = converter.convert(senslogV1Model);
+
+
+    }
+}

+ 43 - 0
connector-push-afarcloud/pom.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>connectors</artifactId>
+        <groupId>cz.senslog</groupId>
+        <version>1.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>connector-push-afarcloud</artifactId>
+    <name>push-afarcloud</name>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>common</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-model</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>cz.senslog</groupId>
+            <artifactId>connector-push-api</artifactId>
+            <version>${project.parent.version}</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+</project>

+ 17 - 0
connector-push-afarcloud/src/main/java/cz/senslog/connector/push/afarcloud/AFarCloudConfig.java

@@ -0,0 +1,17 @@
+package cz.senslog.connector.push.afarcloud;
+
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.config.HostConfig;
+
+class AFarCloudConfig {
+
+    private final HostConfig telemetryHost;
+
+    AFarCloudConfig(DefaultConfig defaultConfig) {
+        this.telemetryHost = new HostConfig(defaultConfig.getPropertyConfig("telemetryHost"));
+    }
+
+    public HostConfig getTelemetryHost() {
+        return telemetryHost;
+    }
+}

+ 30 - 0
connector-push-afarcloud/src/main/java/cz/senslog/connector/push/afarcloud/AFarCloudPushProvider.java

@@ -0,0 +1,30 @@
+package cz.senslog.connector.push.afarcloud;
+
+import cz.senslog.connector.model.afarcloud.AFarCloudModel;
+import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.push.api.ConnectorPushProvider;
+import cz.senslog.connector.push.api.ConnectorPusher;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import static cz.senslog.common.http.HttpClient.newHttpSSLClient;
+
+public class AFarCloudPushProvider implements ConnectorPushProvider {
+
+    private static Logger logger = LogManager.getLogger(AFarCloudPushProvider.class);
+
+    @Override
+    public ConnectorPusher<AFarCloudModel> createPusher(DefaultConfig defaultConfig) {
+        logger.info("Initialization a new push provider {}.", AFarCloudPushProvider.class);
+
+        logger.debug("Creating a new configuration.");
+        AFarCloudConfig config = new AFarCloudConfig(defaultConfig);
+        logger.info("Configuration for {} was created successfully.", AFarCloudPusher.class);
+
+        logger.debug("Creating a new instance of {}.", AFarCloudPusher.class);
+        AFarCloudPusher pusher = new AFarCloudPusher(config, newHttpSSLClient());
+        logger.info("Pusher for {} was created successfully.", AFarCloudPusher.class);
+
+        return pusher;
+    }
+}

+ 67 - 0
connector-push-afarcloud/src/main/java/cz/senslog/connector/push/afarcloud/AFarCloudPusher.java

@@ -0,0 +1,67 @@
+package cz.senslog.connector.push.afarcloud;
+
+import cz.senslog.common.http.HttpClient;
+import cz.senslog.common.http.HttpRequest;
+import cz.senslog.common.http.HttpResponse;
+import cz.senslog.common.http.URLBuilder;
+import cz.senslog.connector.model.afarcloud.AFarCloudModel;
+import cz.senslog.connector.model.afarcloud.MultiSimpleObservation;
+import cz.senslog.connector.model.config.HostConfig;
+import cz.senslog.connector.push.api.ConnectorPusher;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.UUID;
+
+import static cz.senslog.common.json.BasicJson.objectToJson;
+
+
+public class AFarCloudPusher implements ConnectorPusher<AFarCloudModel> {
+
+    private static Logger logger = LogManager.getLogger(AFarCloudPusher.class);
+
+    private final AFarCloudConfig config;
+    private final HttpClient httpClient;
+
+    AFarCloudPusher(AFarCloudConfig config, HttpClient httpClient) {
+        this.config = config;
+        this.httpClient = httpClient;
+    }
+
+    @Override
+    public void init() {}
+
+    @Override
+    public void push(AFarCloudModel model) {
+
+        HostConfig host = config.getTelemetryHost();
+
+        int counter = 0;
+        for (MultiSimpleObservation aggObservations : model.getUnitAggObservations()) {
+
+            String id = UUID.randomUUID().toString();
+            logger.info("Observation {} {}", id, aggObservations);
+
+            logger.debug("Creating a request for the observation {}.", id);
+            HttpRequest request = HttpRequest.newBuilder().POST()
+                    .url(URLBuilder.newBuilder(host.getDomain(), host.getPath())
+//                            .addParam("test", "")
+                            .build())
+                    .body(objectToJson(aggObservations))
+                    .build();
+
+            logger.info("Sending request for the {}", id);
+            HttpResponse response = httpClient.send(request);
+            logger.info("Response of {} with status {}.", id, response.getStatus());
+
+            if (response.isOk()) {
+                counter += 1;
+                logger.info("Observation with id {} was uploaded successfully.", id);
+            } else {
+                logger.error("Observation with id {} was not send. Reason {}.", id, response.getBody());
+            }
+        }
+
+        logger.info("Total uploaded multiple observation {}/{}.", counter, model.getUnitAggObservations().size());
+    }
+}

+ 1 - 0
connector-push-afarcloud/src/main/resources/META-INF/services/cz.senslog.connector.push.api.ConnectorPushProvider

@@ -0,0 +1 @@
+cz.senslog.connector.push.afarcloud.AFarCloudPushProvider

+ 16 - 0
connector-push-afarcloud/src/test/java/cz/senslog/connector/push/afarcloud/AFarCloudPusherTest.java

@@ -0,0 +1,16 @@
+package cz.senslog.connector.push.afarcloud;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class AFarCloudPusherTest {
+
+    @Test
+    void push() {
+
+
+
+
+    }
+}

+ 2 - 1
connector-push-rest-senslog-v1/src/main/java/cz/senslog/connector/push/rest/senslog/v1/SenslogV1ConnectorPushProvider.java

@@ -1,6 +1,7 @@
 package cz.senslog.connector.push.rest.senslog.v1;
 
 import cz.senslog.connector.model.config.DefaultConfig;
+import cz.senslog.connector.model.v1.SenslogV1Model;
 import cz.senslog.connector.push.api.ConnectorPushProvider;
 import cz.senslog.connector.push.api.ConnectorPusher;
 import org.apache.logging.log4j.LogManager;
@@ -21,7 +22,7 @@ public final class SenslogV1ConnectorPushProvider implements ConnectorPushProvid
     private static Logger logger = LogManager.getLogger(SenslogV1ConnectorPushProvider.class);
 
     @Override
-    public ConnectorPusher createPusher(DefaultConfig config) {
+    public ConnectorPusher<SenslogV1Model> createPusher(DefaultConfig config) {
         logger.info("Initialization a new push provider {}.", SenslogV1ConnectorPushProvider.class);
 
         logger.debug("Creating a new configuration.");

+ 1 - 2
docker/Dockerfile

@@ -6,7 +6,6 @@ ENV BUILD_PROFILE $MAVEN_PROFILE
 COPY docker/filebeat.yml /etc/conf.d/
 COPY docker/start.sh /app/
 
-#COPY ["./connector-*", "/app/"]
 COPY . /app/
 
 WORKDIR /app
@@ -18,4 +17,4 @@ RUN apk add filebeat
 RUN mvn clean
 RUN mvn package -P $MAVEN_PROFILE -DskipTests=true
 
-ENTRYPOINT ["/bin/sh", "-C", "start.sh"]
+ENTRYPOINT ["/bin/sh", "-C", "start.sh"]

+ 21 - 0
docker/DockerfileJar

@@ -0,0 +1,21 @@
+FROM zenika/alpine-maven:3-jdk8
+
+ARG config_file
+ENV APP_PARAMS "-cf config/$config_file"
+
+ENV DEBUG "false"
+ENV LOG_MONITOR "false"
+
+COPY docker/filebeat.yml /etc/conf.d/
+COPY docker/start.sh /app/
+
+COPY bin/ /app/bin
+COPY config/$config_file /app/config/$config_file
+
+WORKDIR /app
+
+RUN echo "http://dl-cdn.alpinelinux.org/alpine/edge/testing" >> /etc/apk/repositories
+RUN apk update
+RUN apk add filebeat
+
+ENTRYPOINT ["/bin/sh", "-C", "start.sh"]

+ 10 - 0
pom.xml

@@ -37,6 +37,14 @@
         </profile>
 
         <profile>
+            <id>Senslog1AFarCloud</id>
+            <modules>
+                <module>connector-fetch-senslog-v1</module>
+                <module>connector-push-afarcloud</module>
+            </modules>
+        </profile>
+
+        <profile>
             <id>all</id>
             <activation>
                 <activeByDefault>true</activeByDefault>
@@ -46,6 +54,8 @@
                 <module>connector-push-rest-senslog-v1</module>
                 <module>connector-push-rest-senslog-v2</module>
                 <module>connector-fetch-fieldclimate</module>
+                <module>connector-fetch-senslog-v1</module>
+                <module>connector-push-afarcloud</module>
             </modules>
         </profile>
     </profiles>