|
|
@@ -3,31 +3,36 @@
|
|
|
|
|
|
package io.connector.module.afarcloud.gateway;
|
|
|
|
|
|
+import com.sun.org.apache.xpath.internal.operations.Bool;
|
|
|
import io.connector.core.AbstractGateway;
|
|
|
import io.connector.core.http.RequestUriComponent;
|
|
|
-import io.connector.model.afarcloud.MultiSensor;
|
|
|
-import io.connector.model.afarcloud.ResourceMeasurement;
|
|
|
-import io.connector.model.afarcloud.SensorTelemetry;
|
|
|
+import io.connector.model.afarcloud.*;
|
|
|
import io.connector.model.sensorthings.*;
|
|
|
+import io.connector.model.sensorthings.Location;
|
|
|
+import io.connector.model.sensorthings.Observation;
|
|
|
import io.connector.module.afarcloud.AFCHttpClient;
|
|
|
import io.connector.module.afarcloud.Filter;
|
|
|
+import io.vertx.core.json.Json;
|
|
|
import io.vertx.core.json.JsonArray;
|
|
|
import io.vertx.core.json.JsonObject;
|
|
|
+import io.vertx.ext.web.handler.BodyHandler;
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
|
|
import java.time.Instant;
|
|
|
import java.util.*;
|
|
|
+import java.util.function.Function;
|
|
|
import java.util.function.Supplier;
|
|
|
|
|
|
-import static io.connector.core.http.ContentType.APPLICATION_JSON;
|
|
|
-import static io.connector.core.http.Header.CONTENT_TYPE;
|
|
|
+import static io.connector.core.http.HttpContentType.APPLICATION_JSON;
|
|
|
+import static io.connector.core.http.HttpHeader.CONTENT_TYPE;
|
|
|
import static io.connector.core.AddressPath.Creator.create;
|
|
|
import static io.vertx.core.json.Json.encode;
|
|
|
import static java.lang.Math.sqrt;
|
|
|
import static java.lang.StrictMath.pow;
|
|
|
import static java.lang.String.format;
|
|
|
import static java.util.Arrays.asList;
|
|
|
+import static java.util.Collections.singletonList;
|
|
|
import static java.util.Optional.ofNullable;
|
|
|
|
|
|
/**
|
|
|
@@ -56,6 +61,18 @@ public class OGCSensorThingsGateway extends AbstractGateway {
|
|
|
@Override
|
|
|
protected void run() {
|
|
|
|
|
|
+ router().post().handler(BodyHandler.create()).handler(ctx -> {
|
|
|
+ String contentType = ctx.request().getHeader(CONTENT_TYPE);
|
|
|
+ if (!contentType.equals(APPLICATION_JSON)) {
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).setStatusCode(415)
|
|
|
+ .end(new JsonObject().put("message", String.format(
|
|
|
+ "Unsupported content type. Use one of these [%s].", APPLICATION_JSON
|
|
|
+ )).encode()
|
|
|
+ );
|
|
|
+ }
|
|
|
+ ctx.next();
|
|
|
+ });
|
|
|
+
|
|
|
router().get(create("Things")).handler(ctx -> {
|
|
|
logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
@@ -336,7 +353,7 @@ public class OGCSensorThingsGateway extends AbstractGateway {
|
|
|
AFCAggrLocation afcLastLocation = afcLastLocationOpt.orElseThrow(exception);
|
|
|
|
|
|
Location ogcLocation = Converter.convertToLocation(afcMultiSensor, afcLastLocation, uriComponent);
|
|
|
- List<Location> ogcLocations = Collections.singletonList(ogcLocation);
|
|
|
+ List<Location> ogcLocations = singletonList(ogcLocation);
|
|
|
ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(encode(ogcLocations));
|
|
|
});
|
|
|
|
|
|
@@ -480,6 +497,28 @@ public class OGCSensorThingsGateway extends AbstractGateway {
|
|
|
ctx.response().end(encode(locations));
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+ router().post(create("Datastream")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+
|
|
|
+ // TODO catch exception and create a wrapper with http/error code
|
|
|
+ ObservationInsert ogcObservation = ObservationInsert.parse(ctx.getBodyAsJson());
|
|
|
+ MultiSimpleObservation afcObservation = Converter.convertToMultiSimpleObservation(ogcObservation);
|
|
|
+ client.uploadAggregatedMeasurements(singletonList(afcObservation));
|
|
|
+ // TODO response http 201 (created) and link to the Datastream
|
|
|
+ ctx.response().end();
|
|
|
+ });
|
|
|
+
|
|
|
+ router().post(create("Things")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+
|
|
|
+ ThingInsert ogcThing = ThingInsert.parse(ctx.getBodyAsJson());
|
|
|
+ MultiSensor afcMultiSensor = Converter.convertToMultiSensor(ogcThing);
|
|
|
+
|
|
|
+ ctx.response().end(Json.encode(afcMultiSensor));
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
private static class AFCAggrLocation implements Comparable<AFCAggrLocation> {
|
|
|
@@ -659,7 +698,7 @@ public class OGCSensorThingsGateway extends AbstractGateway {
|
|
|
location.setDescription(afcLocation.observation.getProvider());
|
|
|
location.setEncodingType("application/vnd.geo+json");
|
|
|
|
|
|
- Location.Info info = new Location.Info();
|
|
|
+ LocationInfo info = new LocationInfo();
|
|
|
info.setType("Feature");
|
|
|
location.setLocation(info);
|
|
|
|
|
|
@@ -755,7 +794,7 @@ public class OGCSensorThingsGateway extends AbstractGateway {
|
|
|
datastream.setName(afcSensor.getObservedProperty());
|
|
|
datastream.setDescription(afcSensor.getObservedProperty());
|
|
|
|
|
|
- Datastream.UnitOfMeasurement uom = new Datastream.UnitOfMeasurement();
|
|
|
+ UnitOfMeasurement uom = new UnitOfMeasurement();
|
|
|
uom.setName(afcSensor.getObservedProperty());
|
|
|
uom.setSymbol("");
|
|
|
uom.setDefinition(afcSensor.getUom());
|
|
|
@@ -816,5 +855,169 @@ public class OGCSensorThingsGateway extends AbstractGateway {
|
|
|
|
|
|
return sensor;
|
|
|
}
|
|
|
+
|
|
|
+ static MultiSimpleObservation convertToMultiSimpleObservation(ObservationInsert ogcObservation) {
|
|
|
+ String [] idPartsAfc = disassemblyId(ogcObservation.getDatastreamId());
|
|
|
+ String resourceUrn = idPartsAfc[0], observedProperty = idPartsAfc[1];
|
|
|
+ String [] urnPartsAfc = resourceUrn.split(":");
|
|
|
+ String resourceId = urnPartsAfc[urnPartsAfc.length-1];
|
|
|
+
|
|
|
+ MultiSimpleObservation multiSimpleObservation = new MultiSimpleObservation();
|
|
|
+ multiSimpleObservation.setResourceId(resourceId);
|
|
|
+
|
|
|
+ SimpleObservation simpleObservation = new SimpleObservation();
|
|
|
+ simpleObservation.setObservedProperty(observedProperty);
|
|
|
+ simpleObservation.setResultTime(ogcObservation.getPhenomenonTime().toEpochSecond());
|
|
|
+ simpleObservation.setResult(ogcObservation.getResult());
|
|
|
+
|
|
|
+ multiSimpleObservation.setObservations(singletonList(simpleObservation));
|
|
|
+
|
|
|
+ return multiSimpleObservation;
|
|
|
+ }
|
|
|
+
|
|
|
+ static MultiSensor convertToMultiSensor(ThingInsert ogcThing) {
|
|
|
+ String afcResourceType = ogcThing.getName();
|
|
|
+ if (!afcResourceType.equals(ogcThing.getDescription())) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'description' has to be equal with 'name'. Contains 'resourceType' attribute from AFC.");
|
|
|
+ }
|
|
|
+ if (ogcThing.getLocations().size() != 1) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'Locations' has to contain one location.");
|
|
|
+ }
|
|
|
+ Location ogcLocation = ogcThing.getLocations().get(0);
|
|
|
+ if (!afcResourceType.equals(ogcLocation.getName())) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'Location/name' has to be equal with 'name'. Contains 'resourceType attribute from AFC.");
|
|
|
+ }
|
|
|
+ LocationInfo ogcLocationInfo = ogcLocation.getLocation();
|
|
|
+ String ogcLocationInfoType = ogcLocationInfo.getType();
|
|
|
+ if (!ogcLocationInfoType.equals("Feature")) {
|
|
|
+ throw new IllegalArgumentException("Allowed only 'Feature' type of location.");
|
|
|
+ }
|
|
|
+ Geometry ogcLocationGeometry = ogcLocationInfo.getGeometry();
|
|
|
+ if (!ogcLocationGeometry.getType().equals("Point")) {
|
|
|
+ throw new IllegalArgumentException("Allowed only 'Point' type of geometry.");
|
|
|
+ }
|
|
|
+ List<Double> ogcCoordinates = ogcLocationGeometry.getCoordinates();
|
|
|
+ if (ogcCoordinates.size() != 3) {
|
|
|
+ throw new IllegalArgumentException("Coordinates of location have to be in following format [longitude, latitude, altitude].");
|
|
|
+ }
|
|
|
+
|
|
|
+ double afcLongitude = ogcCoordinates.get(0);
|
|
|
+ double afcLatitude = ogcCoordinates.get(1);
|
|
|
+ double afcAltitude = ogcCoordinates.get(2);
|
|
|
+
|
|
|
+ List<DatastreamInsert> ogcDatastreams = ogcThing.getDatastreams();
|
|
|
+ List<MultiSensor.SensorSchema> afcSensorSchemas = new ArrayList<>(ogcDatastreams.size());
|
|
|
+ for (DatastreamInsert ogcDatastream : ogcDatastreams) {
|
|
|
+ String afcObservedProperty = ogcDatastream.getName();
|
|
|
+ if (!afcObservedProperty.equals(ogcDatastream.getDescription())) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'Datastream/description' has to be equal with 'Datastream/name'. Contains 'observedProperty attribute from AFC.");
|
|
|
+ }
|
|
|
+ UnitOfMeasurement ogcUnitOfMeasurement = ogcDatastream.getUnitOfMeasurement();
|
|
|
+ String afcUom = ogcUnitOfMeasurement.getDefinition();
|
|
|
+
|
|
|
+ String ogcObservationType = ogcDatastream.getObservationType();
|
|
|
+ if (!ogcObservationType.equals("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement")) {
|
|
|
+ throw new IllegalArgumentException("For the attribute 'Datastream/observationType' is allowed following values [http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement].");
|
|
|
+ }
|
|
|
+
|
|
|
+ Geometry ogcObservedArea = ogcDatastream.getObservedArea();
|
|
|
+ if (!ogcObservedArea.getType().equals("Point")) {
|
|
|
+ throw new IllegalArgumentException("Allowed only 'Point' type of observedArea for the datastream '"+afcObservedProperty+"'.");
|
|
|
+ }
|
|
|
+ List<Double> ogcObcAreaCoordinates = ogcObservedArea.getCoordinates();
|
|
|
+ if (ogcObcAreaCoordinates.size() != 3) {
|
|
|
+ throw new IllegalArgumentException("Coordinates of location for the datastream '"+afcObservedProperty+"' have to be in following format [longitude, latitude, altitude].");
|
|
|
+ }
|
|
|
+ if (!(ogcObcAreaCoordinates.get(0).equals(afcLongitude) || ogcObcAreaCoordinates.get(1).equals(afcLatitude) || ogcObcAreaCoordinates.get(2).equals(afcAltitude))) {
|
|
|
+ throw new IllegalArgumentException("Coordinates of the observedArea have to be same as the last location.");
|
|
|
+ }
|
|
|
+
|
|
|
+ ObservedProperty ogcObservedProperty = ogcDatastream.getObservedProperty();
|
|
|
+ if (!ogcObservedProperty.getName().equals(afcObservedProperty)) {
|
|
|
+ throw new IllegalArgumentException("Name of ObservedProperty has to be same as the name of datastream.");
|
|
|
+ }
|
|
|
+ if (!ogcObservedProperty.getDescription().equals(afcObservedProperty)) {
|
|
|
+ throw new IllegalArgumentException("Description of ObservedProperty has to be same as the name of the datastream.");
|
|
|
+ }
|
|
|
+ if (!ogcObservedProperty.getDefinition().equals(afcUom)) {
|
|
|
+ throw new IllegalArgumentException("Definition of ObservedProperty has to be same as the definition of unit measurement.");
|
|
|
+ }
|
|
|
+
|
|
|
+ SensorInsert ogcSensor = ogcDatastream.getSensor();
|
|
|
+ if (!ogcSensor.getName().equals(afcResourceType)) {
|
|
|
+ throw new IllegalArgumentException("Name of sensor has to be same as the name of thing.");
|
|
|
+ }
|
|
|
+ if (!ogcSensor.getDescription().equals(afcResourceType)) {
|
|
|
+ throw new IllegalArgumentException("Description of sensor has to be same as the name of thing.");
|
|
|
+ }
|
|
|
+ if (!ogcSensor.getEncodingType().equals("application/json")) {
|
|
|
+ throw new IllegalArgumentException("For the attribute 'datastream/sensor/encodingType is allowed only 'application/json' type.");
|
|
|
+ }
|
|
|
+
|
|
|
+ MultiSensor.SensorSchema afcSensor = new MultiSensor.SensorSchema();
|
|
|
+ afcSensor.setObservedProperty(afcObservedProperty);
|
|
|
+ afcSensor.setUom(afcUom);
|
|
|
+ afcSensor.setMin_value(Double.MIN_VALUE); // TODO set min_value
|
|
|
+ afcSensor.setMax_value(Double.MAX_VALUE); // TODO set max_value
|
|
|
+ afcSensor.setAccuracy(0.1); // TODO set accuracy
|
|
|
+ afcSensorSchemas.add(afcSensor);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ogcThing.getProperties() == null) {
|
|
|
+ final String[] propAttrs = new String[]{"resourceUrn","resourceId", "supportedProtocol", "hardwareVersion", "softwareVersion", "firmwareVersion", "preprocessing", "pythonScript"};
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties' is required and has to contain following attributes " + Arrays.toString(propAttrs) + ".");
|
|
|
+ }
|
|
|
+ Map<String, Object> ogcProperties = ogcThing.getProperties();
|
|
|
+
|
|
|
+ String afcResourceId = (String) ogcProperties.get("resourceId");
|
|
|
+ if (afcResourceId == null) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties/resourceId' is required.");
|
|
|
+ }
|
|
|
+ String afcResourceUrn = (String) ogcProperties.get("resourceUrn");
|
|
|
+ if (afcResourceUrn == null) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties/resourceUrn' is required.");
|
|
|
+ }
|
|
|
+ String afcSupportedProtocol = (String) ogcProperties.get("supportedProtocol");
|
|
|
+ if (afcSupportedProtocol == null) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties/supportedProtocol' is required.");
|
|
|
+ }
|
|
|
+ String afcHardwareVersion = (String) ogcProperties.get("hardwareVersion");
|
|
|
+ if (afcHardwareVersion == null) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties/hardwareVersion' is required.");
|
|
|
+ }
|
|
|
+ String afcSoftwareVersion = (String) ogcProperties.get("softwareVersion");
|
|
|
+ if (afcSoftwareVersion == null) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties/softwareVersion' is required.");
|
|
|
+ }
|
|
|
+ String afcFirmwareVersion = (String) ogcProperties.get("firmwareVersion");
|
|
|
+ if (afcFirmwareVersion == null) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties/firmwareVersion' is required.");
|
|
|
+ }
|
|
|
+ String afcPythonScript = (String) ogcProperties.get("pythonScript");
|
|
|
+ if (afcPythonScript == null) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties/pythonScript' is required.");
|
|
|
+ }
|
|
|
+ Boolean afcPreprocessing = (Boolean) ogcProperties.get("preprocessing");
|
|
|
+ if (afcPreprocessing == null) {
|
|
|
+ throw new IllegalArgumentException("Attribute 'properties/preprocessing' is required.");
|
|
|
+ }
|
|
|
+
|
|
|
+ MultiSensor afcMultiSensor = new MultiSensor();
|
|
|
+ afcMultiSensor.setResourceId(afcResourceId);
|
|
|
+ afcMultiSensor.setResourceType(afcResourceType);
|
|
|
+ afcMultiSensor.setResourceUrn(afcResourceUrn);
|
|
|
+ afcMultiSensor.setLongitude(afcLongitude);
|
|
|
+ afcMultiSensor.setLatitude(afcLatitude);
|
|
|
+ afcMultiSensor.setAltitude(afcAltitude);
|
|
|
+ afcMultiSensor.setPreprocessing(afcPreprocessing);
|
|
|
+ afcMultiSensor.setPythonScript(afcPythonScript);
|
|
|
+ afcMultiSensor.setObservations(afcSensorSchemas);
|
|
|
+ afcMultiSensor.setSupportedProtocol(afcSupportedProtocol);
|
|
|
+ afcMultiSensor.setHardwareVersion(afcHardwareVersion);
|
|
|
+ afcMultiSensor.setSoftwareVersion(afcSoftwareVersion);
|
|
|
+ afcMultiSensor.setFirmwareVersion(afcFirmwareVersion);
|
|
|
+
|
|
|
+ return afcMultiSensor;
|
|
|
+ }
|
|
|
}
|
|
|
}
|