|
|
@@ -1,1021 +1,1029 @@
|
|
|
-// Copyright (c) 2020 UWB & LESP.
|
|
|
-// The UWB & LESP license this file to you under the MIT license.
|
|
|
-
|
|
|
-package io.connector.module.afarcloud.gateway;
|
|
|
-
|
|
|
-import io.connector.core.AbstractGateway;
|
|
|
-import io.connector.core.http.RequestUriComponent;
|
|
|
-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.Supplier;
|
|
|
-
|
|
|
-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;
|
|
|
-
|
|
|
-/**
|
|
|
- *
|
|
|
- * @author Lukas Cerny
|
|
|
- * @version 1.0
|
|
|
- * @since 1.0
|
|
|
- */
|
|
|
-public class OGCSensorThingsGateway extends AbstractGateway {
|
|
|
-
|
|
|
- private final static Logger logger = LogManager.getLogger(OGCSensorThingsGateway.class);
|
|
|
-
|
|
|
- /** Attribute of a client that accesses to the AFarCloud system. */
|
|
|
- private final AFCHttpClient client;
|
|
|
-
|
|
|
- /**
|
|
|
- * Public constructor of the class that initializes final attributes.
|
|
|
- * @param id - identifier of the gateway
|
|
|
- * @param client - client allows access to the AFarCloud system
|
|
|
- */
|
|
|
- public OGCSensorThingsGateway(String id, AFCHttpClient client) {
|
|
|
- super(id);
|
|
|
- this.client = client;
|
|
|
- }
|
|
|
-
|
|
|
- @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());
|
|
|
- List<MultiSensor> afcMultiSensors = client.getAllSensors();
|
|
|
- List<Thing> ogcThings = Converter.convertToThing(afcMultiSensors, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(encode(ogcThings));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Things(:id)")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- Thing ogcThing = Converter.convertToThing(afcMultiSensor, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcThing.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Datastreams(:id)/Thing")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
- String[] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0];
|
|
|
- ctx.reroute(format("%s/Things(%s)", uriComponent.getGatewayPath(), resourceUrn));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("HistoricalLocations(:id)/Thing")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
- String [] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0];
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- Thing ogcThing = Converter.convertToThing(afcMultiSensor, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcThing.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Datastreams(:id)")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
- String[] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0], observedProperty = idCmp[1];
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- List<ResourceMeasurement> afcMeasurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
- .limit(1).entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
- );
|
|
|
- final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Datastream with @iot.id \"" + id + "\".");
|
|
|
-
|
|
|
- Optional<MultiSensor.SensorSchema> afcSensorOpt = afcMultiSensor.getObservations().stream().filter(s -> s.getObservedProperty().equals(observedProperty)).findFirst();
|
|
|
- MultiSensor.SensorSchema afcSensor = afcSensorOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
- ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Optional<SensorTelemetry> afcTelemetryOpt = ofNullable(afcMeasurement.getMeasurements().size() == 1 ? afcMeasurement.getMeasurements().get(0) : null);
|
|
|
- SensorTelemetry afcTelemetry = afcTelemetryOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Datastream datastream = Converter.convertToDataStream(afcMultiSensor, afcSensor, afcTelemetry, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(datastream.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Things(:id)/Datastreams")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- List<ResourceMeasurement> afcMeasurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
- .limit(1)
|
|
|
- .entityNames(afcMultiSensor.getResourceId())
|
|
|
- );
|
|
|
- ResourceMeasurement afcMeasurement = afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null;
|
|
|
- List<Datastream> ogcDataStream = Converter.convertToDataStream(afcMultiSensor, afcMeasurement, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(encode(ogcDataStream));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Sensors(:id)/Datastreams")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
- ctx.reroute(format("%s/Things(%s)/Datastreams", uriComponent.getGatewayPath(), resourceUrn));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("ObservedProperties(:id)/Datastream")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
- ctx.reroute(format("%s/Datastreams(%s)", uriComponent.getGatewayPath(), id));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Observations(:id)/Datastream")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resource + measurement + time
|
|
|
- String[] idCmp = Converter.disassemblyId(id);
|
|
|
- String resource = idCmp[0], measurement = idCmp[1];
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceId(resource);
|
|
|
- String resourceUrn = afcMultiSensor.getResourceUrn();
|
|
|
- ctx.reroute(format("%s/Datastreams(%s)", uriComponent.getGatewayPath(), Converter.assemblyId(resourceUrn, measurement)));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Sensors(:id)")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- Sensor ogcSensor = Converter.convertToSensor(afcMultiSensor, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcSensor.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Datastreams(:id)/Sensor")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
- String[] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0];
|
|
|
- ctx.reroute(format("%s/Sensors(%s)", uriComponent.getGatewayPath(), resourceUrn));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("ObservedProperties(:id)")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
- String[] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0], observedProperty = idCmp[1];
|
|
|
- final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Datastream with @iot.id \"" + id + "\".");
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- Optional<MultiSensor.SensorSchema> afcSensorOpt = afcMultiSensor.getObservations().stream().filter(s -> s.getObservedProperty().equals(observedProperty)).findFirst();
|
|
|
- MultiSensor.SensorSchema afcSensor = afcSensorOpt.orElseThrow(exception);
|
|
|
- ObservedProperty property = Converter.convertToObservedProperty(afcMultiSensor, afcSensor, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(property.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Datastreams(:id)/ObservedProperty")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
- ctx.reroute(format("%s/ObservedProperties(%s)", uriComponent.getGatewayPath(), id));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Observations(:id)")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resource + measurement + time
|
|
|
- String[] idCmp = Converter.disassemblyId(id);
|
|
|
- String resource = idCmp[0], measurement = idCmp[1], time = idCmp[2];
|
|
|
-
|
|
|
- Instant timestamp = Instant.parse(time);
|
|
|
-
|
|
|
- List<ResourceMeasurement> measurements = client.getObservationsBySensor(new Filter()
|
|
|
- .limit(1).order("ASC").entityNames(resource).measurements(measurement)
|
|
|
- .startTime(timestamp).endTime(timestamp.plusSeconds(60))
|
|
|
- );
|
|
|
- final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException(format(
|
|
|
- "Can not find Observation with @iot.id '%s'.", id));
|
|
|
-
|
|
|
- Optional<ResourceMeasurement> measurementOpt = ofNullable(measurements.size() == 1 ? measurements.get(0) : null);
|
|
|
- ResourceMeasurement afcMeasurement = measurementOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Optional<SensorTelemetry> telemetryOpt = ofNullable(afcMeasurement.getMeasurements().size() == 1 ? afcMeasurement.getMeasurements().get(0) : null);
|
|
|
- SensorTelemetry afcTelemetry = telemetryOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Optional<io.connector.model.afarcloud.Observation> observationOpt = ofNullable(afcTelemetry.getObservations().size() > 0 ? afcTelemetry.getObservations().get(0) : null);
|
|
|
- io.connector.model.afarcloud.Observation afcObservation = observationOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Observation ogcObservation = Converter.convertToObservation(afcMeasurement, afcTelemetry, afcObservation, uriComponent);
|
|
|
- ctx.response().end(ogcObservation.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Datastreams(:id)/Observations")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
- String filterParam = ctx.request().getParam(Params.FILTER);
|
|
|
-
|
|
|
- io.connector.model.sensorthings.Filter filter = null;
|
|
|
- try {
|
|
|
- filter = io.connector.model.sensorthings.Filter.parse(filterParam);
|
|
|
- } catch (RuntimeException e) {
|
|
|
- ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
- .put("message", e.getMessage()).encode()); return;
|
|
|
- }
|
|
|
-
|
|
|
- String [] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0], observedProperty = idCmp[1];
|
|
|
-
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
-
|
|
|
- List<ResourceMeasurement> measurements;
|
|
|
- if (filter.isExists()) {
|
|
|
- Filter afcFilter;
|
|
|
- try {
|
|
|
- afcFilter = Converter.convertFilter(filter);
|
|
|
- } catch (RuntimeException e) {
|
|
|
- ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
- .put("message", e.getMessage()).encode()); return;
|
|
|
- }
|
|
|
- afcFilter.entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty);
|
|
|
- measurements = client.getObservationsBySensor(afcFilter);
|
|
|
- } else {
|
|
|
- measurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
- .limit(1).entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- if (measurements.isEmpty()) {
|
|
|
- ctx.response().end(new JsonArray().encode());
|
|
|
- } else {
|
|
|
- final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Datastream with @iot.id \"" + id + "\".");
|
|
|
-
|
|
|
- Optional<ResourceMeasurement> measurementOpt = ofNullable(measurements.size() == 1 ? measurements.get(0) : null);
|
|
|
- ResourceMeasurement afcMeasurement = measurementOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Optional<SensorTelemetry> telemetryOpt = ofNullable(afcMeasurement.getMeasurements().size() == 1 ? afcMeasurement.getMeasurements().get(0) : null);
|
|
|
- SensorTelemetry afcTelemetry = telemetryOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- List<Observation> ogcObservations = Converter.convertToObservations(afcMeasurement, afcTelemetry, uriComponent);
|
|
|
- ctx.response().end(encode(ogcObservations));
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("FeaturesOfInterest(:id)")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id");
|
|
|
- FeatureOfInterest featureOfInterest = new FeatureOfInterest();
|
|
|
- featureOfInterest.setId(id);
|
|
|
- featureOfInterest.setSelfLink("https://storage07-afarcloud.qa.pdmfc.com/storage/rest/registry/getAllObservationTypes");
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(featureOfInterest.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Locations(:id)")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
- String [] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0], observedProperty = idCmp[1], time = idCmp[2];
|
|
|
-
|
|
|
- Instant startTime = Instant.parse(time);
|
|
|
- Instant endTime = startTime.plusSeconds(60);
|
|
|
-
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- List<ResourceMeasurement> afcMeasurements = client.getObservationsBySensor(new Filter()
|
|
|
- .startTime(startTime).endTime(endTime)
|
|
|
- .entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
- );
|
|
|
- final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Location with @iot.id \"" + resourceUrn + "\".");
|
|
|
-
|
|
|
- Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
- ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
- Optional<AFCAggrLocation> afcLastLocationOpt = ofNullable(afcLocations.getLast());
|
|
|
- AFCAggrLocation afcLastLocation = afcLastLocationOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Location ogcLocation = Converter.convertToLocation(afcMultiSensor, afcLastLocation, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcLocation.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Things(:id)/Locations")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
-
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- List<ResourceMeasurement> afcMeasurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
- .limit(1).entityNames(afcMultiSensor.getResourceId())
|
|
|
- );
|
|
|
- final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Thing with @iot.id \"" + resourceUrn + "\".");
|
|
|
-
|
|
|
- Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
- ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
- Optional<AFCAggrLocation> afcLastLocationOpt = ofNullable(afcLocations.getFirst());
|
|
|
- AFCAggrLocation afcLastLocation = afcLastLocationOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- Location ogcLocation = Converter.convertToLocation(afcMultiSensor, afcLastLocation, uriComponent);
|
|
|
- List<Location> ogcLocations = singletonList(ogcLocation);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(encode(ogcLocations));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("HistoricalLocations(:id)/Locations")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
- ctx.reroute(format("%s/Locations(%s)", uriComponent.getGatewayPath(), id));
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("HistoricalLocations(:id)")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
-
|
|
|
- String [] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0], observedProperty = idCmp[1], time = idCmp[2];
|
|
|
-
|
|
|
- Instant startTime = Instant.parse(time);
|
|
|
- Instant endTime = startTime.plusSeconds(60);
|
|
|
-
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- List<ResourceMeasurement> afcMeasurements = client.getObservationsBySensor(new Filter()
|
|
|
- .startTime(startTime).endTime(endTime)
|
|
|
- .entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
- );
|
|
|
- final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Location with @iot.id \"" + resourceUrn + "\".");
|
|
|
-
|
|
|
- Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
- ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
- Optional<AFCAggrLocation> afcLastLocationOpt = ofNullable(afcLocations.getLast());
|
|
|
- AFCAggrLocation afcLocation = afcLastLocationOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- HistoricalLocation ogcLocation = Converter.convertToHistoricalLocation(afcMultiSensor, afcLocation, uriComponent);
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcLocation.encode());
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Things(:id)/HistoricalLocations")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
- String filterParam = ctx.request().getParam(Params.FILTER);
|
|
|
-
|
|
|
- io.connector.model.sensorthings.Filter filter = null;
|
|
|
- try {
|
|
|
- filter = io.connector.model.sensorthings.Filter.parse(filterParam);
|
|
|
- } catch (RuntimeException e) {
|
|
|
- ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
- .put("message", e.getMessage()).encode()); return;
|
|
|
- }
|
|
|
-
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
- List<ResourceMeasurement> afcMeasurements;
|
|
|
- if (filter.isExists()) {
|
|
|
- Filter afcFilter;
|
|
|
- try {
|
|
|
- afcFilter = Converter.convertFilter(filter);
|
|
|
- } catch (RuntimeException e) {
|
|
|
- ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
- .put("message", e.getMessage()).encode()); return;
|
|
|
- }
|
|
|
- afcFilter.entityNames(afcMultiSensor.getResourceId());
|
|
|
- afcMeasurements = client.getObservationsBySensor(afcFilter);
|
|
|
- } else {
|
|
|
- afcMeasurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
- .limit(1).entityNames(afcMultiSensor.getResourceId())
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- if (afcMeasurements.isEmpty()) {
|
|
|
- ctx.response().end(new JsonArray().encode());
|
|
|
- } else {
|
|
|
- final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find HistoricalLocations of the Thing with @iot.id \"" + resourceUrn + "\".");
|
|
|
-
|
|
|
- Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
- ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
-
|
|
|
- List<HistoricalLocation> locations = Converter.convertToHistoricalLocation(afcMultiSensor, afcLocations.getList(), uriComponent);
|
|
|
- ctx.response().end(encode(locations));
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- router().get(create("Locations(:id)/HistoricalLocations")).handler(ctx -> {
|
|
|
- logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
- ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
- RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
- String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
- String filterParam = ctx.request().getParam(Params.FILTER);
|
|
|
-
|
|
|
- io.connector.model.sensorthings.Filter filter = null;
|
|
|
- try {
|
|
|
- filter = io.connector.model.sensorthings.Filter.parse(filterParam);
|
|
|
- } catch (RuntimeException e) {
|
|
|
- ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
- .put("message", e.getMessage()).encode()); return;
|
|
|
- }
|
|
|
-
|
|
|
- String [] idCmp = Converter.disassemblyId(id);
|
|
|
- String resourceUrn = idCmp[0], observedProperty = idCmp[1], time = idCmp[2];
|
|
|
-
|
|
|
- final Supplier<IllegalArgumentException> exception =
|
|
|
- () -> new IllegalArgumentException("Can not find HistoricalLocations of the Thing with @iot.id \"" + resourceUrn + "\".");
|
|
|
-
|
|
|
- MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
-
|
|
|
- Filter afcFilter;
|
|
|
- if (filter.isExists()) {
|
|
|
- try {
|
|
|
- afcFilter = Converter.convertFilter(filter);
|
|
|
- } catch (RuntimeException e) {
|
|
|
- ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
- .put("message", e.getMessage()).encode()); return;
|
|
|
- }
|
|
|
- afcFilter.entityNames(afcMultiSensor.getResourceId());
|
|
|
- } else {
|
|
|
- Instant startTime = Instant.parse(time);
|
|
|
- Instant endTime = startTime.plusSeconds(60*60); // 1h
|
|
|
-
|
|
|
- afcFilter = new Filter().startTime(startTime).endTime(endTime);
|
|
|
- }
|
|
|
-
|
|
|
- List<ResourceMeasurement> afcMeasurements = client.getObservationsBySensor(afcFilter
|
|
|
- .entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
- );
|
|
|
-
|
|
|
- if (afcMeasurements.isEmpty()) {
|
|
|
- ctx.response().end(new JsonArray().encode());
|
|
|
- } else {
|
|
|
-
|
|
|
- Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
- ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
-
|
|
|
- AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
-
|
|
|
- List<HistoricalLocation> locations = Converter.convertToHistoricalLocation(afcMultiSensor, afcLocations.getList(), uriComponent);
|
|
|
- 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> {
|
|
|
- private final String measurement;
|
|
|
- private final io.connector.model.afarcloud.Observation observation;
|
|
|
-
|
|
|
- public AFCAggrLocation(String measurement, io.connector.model.afarcloud.Observation observation) {
|
|
|
- this.measurement = measurement;
|
|
|
- this.observation = observation;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean equals(Object o) {
|
|
|
- if (this == o) return true;
|
|
|
- if (o == null || getClass() != o.getClass()) return false;
|
|
|
- AFCAggrLocation that = (AFCAggrLocation) o;
|
|
|
- return this.compareTo(that) == 0;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int hashCode() {
|
|
|
- return Objects.hash(measurement, observation);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int compareTo(AFCAggrLocation o) {
|
|
|
- io.connector.model.afarcloud.Observation o1 = this.observation;
|
|
|
- io.connector.model.afarcloud.Observation o2 = o.observation;
|
|
|
-
|
|
|
- Instant o1Time = Instant.parse(o1.getTime());
|
|
|
- Instant o2Time = Instant.parse(o2.getTime());
|
|
|
-
|
|
|
- if (o1Time.equals(o2Time)) {
|
|
|
- Double o1Dist = sqrt(pow(o1.getLongitude(), 2) + pow(o1.getLatitude(), 2) + pow(o1.getAltitude(), 2));
|
|
|
- Double o2Dist = sqrt(pow(o2.getLongitude(), 2) + pow(o2.getLatitude(), 2) + pow(o2.getAltitude(), 2));
|
|
|
- return o1Dist.compareTo(o2Dist);
|
|
|
- } else {
|
|
|
- return o1Time.compareTo(o2Time);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static class AFCLocationList {
|
|
|
- private final List<AFCAggrLocation> list;
|
|
|
-
|
|
|
- AFCLocationList() {
|
|
|
- this.list = new ArrayList<>();
|
|
|
- }
|
|
|
-
|
|
|
- public void sort() {
|
|
|
- Collections.sort(list);
|
|
|
- }
|
|
|
-
|
|
|
- public void add(AFCAggrLocation location) {
|
|
|
- list.add(location);
|
|
|
- }
|
|
|
-
|
|
|
- public AFCAggrLocation getFirst() {
|
|
|
- return list.isEmpty() ? null : list.get(0);
|
|
|
- }
|
|
|
-
|
|
|
- public AFCAggrLocation getLast() {
|
|
|
- return list.isEmpty() ? null : list.get(list.size()-1);
|
|
|
- }
|
|
|
-
|
|
|
- public AFCAggrLocation get(int index) {
|
|
|
- return list.get(index);
|
|
|
- }
|
|
|
-
|
|
|
- public List<AFCAggrLocation> getList() {
|
|
|
- return list;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static class AFCLocationUtils {
|
|
|
-
|
|
|
- static AFCLocationList sort(ResourceMeasurement afcMeasurement) {
|
|
|
- Objects.requireNonNull(afcMeasurement);
|
|
|
-
|
|
|
- AFCLocationList aggrLocations = new AFCLocationList();
|
|
|
- for (SensorTelemetry measurement : afcMeasurement.getMeasurements()) {
|
|
|
- for (io.connector.model.afarcloud.Observation observation : measurement.getObservations()) {
|
|
|
- aggrLocations.add(new AFCAggrLocation(measurement.getMeasurement(), observation));
|
|
|
- }
|
|
|
- }
|
|
|
- aggrLocations.sort();
|
|
|
-
|
|
|
- List<AFCAggrLocation> list = aggrLocations.list;
|
|
|
- if (list.size() > 1) {
|
|
|
- Iterator<AFCAggrLocation> iterator = list.iterator();
|
|
|
- AFCAggrLocation previous = iterator.next();
|
|
|
- while (iterator.hasNext()) {
|
|
|
- AFCAggrLocation current = iterator.next();
|
|
|
- if (previous.equals(current)) {
|
|
|
- iterator.remove(); continue;
|
|
|
- }
|
|
|
- previous = current;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return aggrLocations;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static class Converter {
|
|
|
-
|
|
|
- private final static Logger logger = LogManager.getLogger(Converter.class);
|
|
|
-
|
|
|
- private static final String DELIMITER = "&";
|
|
|
-
|
|
|
- static String assemblyId(String... parts) {
|
|
|
- return String.join(DELIMITER, parts);
|
|
|
- }
|
|
|
-
|
|
|
- static String[] disassemblyId(String id) {
|
|
|
- return id.split(DELIMITER);
|
|
|
- }
|
|
|
-
|
|
|
- static Filter convertFilter(io.connector.model.sensorthings.Filter ogcFilter) {
|
|
|
- if (ogcFilter == null) {
|
|
|
- throw new IllegalArgumentException("Unsupported filter attributes in filter expression '" + ogcFilter + "'.");
|
|
|
- }
|
|
|
-
|
|
|
- Filter afcFilter = new Filter();
|
|
|
-
|
|
|
- for (io.connector.model.sensorthings.Filter.Expression expression : ogcFilter.getAddExpressions()) {
|
|
|
- switch (expression.getAttribute()) {
|
|
|
- case "time":
|
|
|
- case "resultTime": {
|
|
|
- switch (expression.getOperator()) {
|
|
|
- case "lt": {
|
|
|
- afcFilter.endTime(Instant.parse(expression.getValue()));
|
|
|
- } break;
|
|
|
- case "gt": {
|
|
|
- afcFilter.startTime(Instant.parse(expression.getValue()));
|
|
|
- }break;
|
|
|
- default: throw new IllegalArgumentException(format(
|
|
|
- "Unsupported operator '%s' in the filter expression '%s'.", expression.getOperator(), ogcFilter));
|
|
|
- }
|
|
|
- } break;
|
|
|
- default: throw new IllegalArgumentException(format(
|
|
|
- "Unsupported attribute '%s' in the filter expression '%s'.", expression.getAttribute(), ogcFilter));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return afcFilter;
|
|
|
- }
|
|
|
-
|
|
|
- static HistoricalLocation convertToHistoricalLocation(MultiSensor afcMultiSensor, AFCAggrLocation afcLocation, RequestUriComponent uriComponent) {
|
|
|
- HistoricalLocation historicalLocation = new HistoricalLocation();
|
|
|
- String locationId = assemblyId(afcMultiSensor.getResourceUrn(), afcLocation.measurement, afcLocation.observation.getTime());
|
|
|
- String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
- historicalLocation.setId(locationId);
|
|
|
- historicalLocation.setSelfLink(format("%s/HistoricalLocations(%s)", absoluteUrl, locationId));
|
|
|
- historicalLocation.setLocationsNavigationLink(format("%s/HistoricalLocations(%s)/Locations", absoluteUrl, locationId));
|
|
|
- historicalLocation.setThingNavigationLink(format("%s/HistoricalLocations(%s)/Thing", absoluteUrl, locationId));
|
|
|
- historicalLocation.setTime(afcLocation.observation.getTime());
|
|
|
- return historicalLocation;
|
|
|
- }
|
|
|
-
|
|
|
- static List<HistoricalLocation> convertToHistoricalLocation(MultiSensor afcMultiSensor, List<AFCAggrLocation> afcLocations, RequestUriComponent uriComponent) {
|
|
|
- List<HistoricalLocation> historicalLocations = new ArrayList<>(afcLocations.size());
|
|
|
- for (AFCAggrLocation afcLocation : afcLocations) {
|
|
|
- historicalLocations.add(convertToHistoricalLocation(afcMultiSensor, afcLocation, uriComponent));
|
|
|
- }
|
|
|
- return historicalLocations;
|
|
|
- }
|
|
|
-
|
|
|
- static Location convertToLocation(MultiSensor afcMultiSensor, AFCAggrLocation afcLocation, RequestUriComponent uriComponent) {
|
|
|
- Location location = new Location();
|
|
|
- String locationId = assemblyId(afcMultiSensor.getResourceUrn(), afcLocation.measurement, afcLocation.observation.getTime());
|
|
|
- String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
- location.setId(locationId);
|
|
|
- location.setSelfLink(format("%s/Locations(%s)", absoluteUrl, locationId));
|
|
|
- location.setHistoricalLocationsNavigationLink(format("%s/Locations(%s)/HistoricalLocations", absoluteUrl, locationId));
|
|
|
- location.setName(afcMultiSensor.getResourceType());
|
|
|
- location.setDescription(afcLocation.observation.getProvider());
|
|
|
- location.setEncodingType("application/vnd.geo+json");
|
|
|
-
|
|
|
- LocationInfo info = new LocationInfo();
|
|
|
- info.setType("Feature");
|
|
|
- location.setLocation(info);
|
|
|
-
|
|
|
- Geometry geometry = new Geometry();
|
|
|
- geometry.setType("Point");
|
|
|
- io.connector.model.afarcloud.Observation afcObservation = afcLocation.observation;
|
|
|
- geometry.setCoordinates(asList(afcObservation.getLongitude(), afcObservation.getLatitude(), afcObservation.getAltitude()));
|
|
|
- info.setGeometry(geometry);
|
|
|
-
|
|
|
- return location;
|
|
|
- }
|
|
|
-
|
|
|
- static Observation convertToObservation(ResourceMeasurement afcMeasurement, SensorTelemetry afcTelemetry, io.connector.model.afarcloud.Observation afcObservation, RequestUriComponent uriComponent) {
|
|
|
- Observation observation = new Observation();
|
|
|
- String observationId = assemblyId(afcMeasurement.getResource(), afcTelemetry.getMeasurement(), afcObservation.getTime());
|
|
|
- String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
- observation.setId(observationId);
|
|
|
- observation.setSelfLink(format("%s/Observations(%s)", absoluteUrl, observationId));
|
|
|
- // observation.setFeatureOfInterestNavigationLink(format("Observations(%s)/FeatureOfInterest", observationId));
|
|
|
- observation.setFeatureOfInterestNavigationLink("https://storage07-afarcloud.qa.pdmfc.com/storage/rest/registry/getAllObservationTypes");
|
|
|
- observation.setDataStreamNavigationLink(format("%s/Observations(%s)/Datastream", absoluteUrl, observationId));
|
|
|
- observation.setPhenomenonTime(afcObservation.getTime());
|
|
|
- observation.setResultTime(afcObservation.getTime());
|
|
|
- observation.setResult(afcObservation.getValue());
|
|
|
- return observation;
|
|
|
- }
|
|
|
-
|
|
|
- static List<Observation> convertToObservations(ResourceMeasurement afcMeasurement, SensorTelemetry afcTelemetry, RequestUriComponent uriComponent) {
|
|
|
- List<Observation> ogcObservations = new ArrayList<>(afcTelemetry.getObservations().size());
|
|
|
- for (io.connector.model.afarcloud.Observation afcObservation : afcTelemetry.getObservations()) {
|
|
|
- ogcObservations.add(convertToObservation(afcMeasurement, afcTelemetry, afcObservation, uriComponent));
|
|
|
- }
|
|
|
- return ogcObservations;
|
|
|
- }
|
|
|
-
|
|
|
- static ObservedProperty convertToObservedProperty(MultiSensor afcMultiSensor, MultiSensor.SensorSchema afcSensor, RequestUriComponent uriComponent) {
|
|
|
- Objects.requireNonNull(afcMultiSensor);
|
|
|
- Objects.requireNonNull(afcSensor);
|
|
|
- Objects.requireNonNull(uriComponent);
|
|
|
-
|
|
|
- ObservedProperty observedProperty = new ObservedProperty();
|
|
|
- String observedPropertyId = assemblyId(afcMultiSensor.getResourceUrn(), afcSensor.getObservedProperty());
|
|
|
- String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
- observedProperty.setId(observedPropertyId);
|
|
|
- observedProperty.setSelfLink(format("%s/ObservedProperties(%s)", absoluteUrl, observedPropertyId));
|
|
|
- observedProperty.setDataStreamNavigationLink(format("%s/ObservedProperties(%s)/Datastream", absoluteUrl, observedPropertyId));
|
|
|
- observedProperty.setName(afcSensor.getObservedProperty());
|
|
|
- observedProperty.setDescription(afcSensor.getObservedProperty());
|
|
|
- observedProperty.setDefinition(afcSensor.getUom());
|
|
|
- return observedProperty;
|
|
|
- }
|
|
|
-
|
|
|
- static Thing convertToThing(MultiSensor afcMultiSensor, RequestUriComponent uriComponent) {
|
|
|
- Objects.requireNonNull(afcMultiSensor);
|
|
|
- Objects.requireNonNull(uriComponent);
|
|
|
-
|
|
|
- Thing thing = new Thing();
|
|
|
- String thingId = afcMultiSensor.getResourceUrn();
|
|
|
- String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
- thing.setId(thingId);
|
|
|
- thing.setSelfLink(format("%s/Things(%s)", absoluteUrl, thingId));
|
|
|
- thing.setLocationsNavigationLink(format("%s/Things(%s)/Locations", absoluteUrl, thingId));
|
|
|
- thing.setDataStreamNavigationLink(format("%s/Things(%s)/Datastreams", absoluteUrl, thingId));
|
|
|
- thing.setHistoricalLocationsNavigationLink(format("%s/Things(%s)/HistoricalLocations", absoluteUrl, thingId));
|
|
|
- thing.setName(afcMultiSensor.getResourceType());
|
|
|
- thing.setDescription(afcMultiSensor.getResourceType());
|
|
|
- thing.setProperties(null);
|
|
|
- return thing;
|
|
|
- }
|
|
|
-
|
|
|
- static List<Thing> convertToThing(List<MultiSensor> afcMultiSensors, RequestUriComponent uriComponent) {
|
|
|
- List<Thing> ogcThings = new ArrayList<>(afcMultiSensors.size());
|
|
|
- for (MultiSensor afcMultiSensor : afcMultiSensors) {
|
|
|
- ogcThings.add(convertToThing(afcMultiSensor, uriComponent));
|
|
|
- }
|
|
|
- return ogcThings;
|
|
|
- }
|
|
|
-
|
|
|
- static Datastream convertToDataStream(MultiSensor afcMultiSensor, MultiSensor.SensorSchema afcSensor, SensorTelemetry telemetry, RequestUriComponent uriComponent) {
|
|
|
- Objects.requireNonNull(afcMultiSensor);
|
|
|
- Objects.requireNonNull(afcSensor);
|
|
|
- Objects.requireNonNull(uriComponent);
|
|
|
-
|
|
|
- Datastream datastream = new Datastream();
|
|
|
- String dataStreamId = assemblyId(afcMultiSensor.getResourceUrn(), afcSensor.getObservedProperty());
|
|
|
- String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
- datastream.setId(dataStreamId);
|
|
|
- datastream.setSelfLink(format("%s/Datastreams(%s)", absoluteUrl, dataStreamId));
|
|
|
- datastream.setThingNavigationLink(format("%s/Datastreams(%s)/Thing", absoluteUrl, dataStreamId));
|
|
|
- datastream.setSensorNavigationLink(format("%s/Datastreams(%s)/Sensor", absoluteUrl, dataStreamId));
|
|
|
- datastream.setObservedPropertyNavigationLink(format("%s/Datastreams(%s)/ObservedProperty", absoluteUrl, dataStreamId));
|
|
|
- datastream.setObservationsNavigationLink(format("%s/Datastreams(%s)/Observations", absoluteUrl, dataStreamId));
|
|
|
- datastream.setName(afcSensor.getObservedProperty());
|
|
|
- datastream.setDescription(afcSensor.getObservedProperty());
|
|
|
-
|
|
|
- UnitOfMeasurement uom = new UnitOfMeasurement();
|
|
|
- uom.setName(afcSensor.getObservedProperty());
|
|
|
- uom.setSymbol("");
|
|
|
- uom.setDefinition(afcSensor.getUom());
|
|
|
- datastream.setUnitOfMeasurement(uom);
|
|
|
-
|
|
|
- datastream.setObservationType("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
|
|
|
-
|
|
|
- Geometry geometry = new Geometry();
|
|
|
- geometry.setType("Point");
|
|
|
- geometry.setCoordinates(asList(afcMultiSensor.getLongitude(), afcMultiSensor.getLatitude()));
|
|
|
- datastream.setObservedArea(geometry);
|
|
|
-
|
|
|
- String startDate = "<none>";
|
|
|
- String endDate = telemetry == null || telemetry.getObservations().isEmpty() ? "<none>" : telemetry.getObservations().get(0).getTime();
|
|
|
- String time = startDate + "/" + endDate;
|
|
|
- datastream.setPhenomenonTime(time);
|
|
|
- datastream.setResultTime(time);
|
|
|
-
|
|
|
- return datastream;
|
|
|
- }
|
|
|
-
|
|
|
- static List<Datastream> convertToDataStream(MultiSensor afcMultiSensor, ResourceMeasurement measurement, RequestUriComponent uriComponent) {
|
|
|
- Objects.requireNonNull(afcMultiSensor);
|
|
|
- Objects.requireNonNull(uriComponent);
|
|
|
-
|
|
|
- List<Datastream> datastreams = new ArrayList<>(afcMultiSensor.getObservations().size());
|
|
|
-
|
|
|
- Map<String, SensorTelemetry> sensors = new HashMap<>();
|
|
|
- if (measurement != null) {
|
|
|
- for (SensorTelemetry telemetry : measurement.getMeasurements()) {
|
|
|
- sensors.put(telemetry.getMeasurement(), telemetry);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- for (MultiSensor.SensorSchema sensor : afcMultiSensor.getObservations()) {
|
|
|
- datastreams.add(convertToDataStream(afcMultiSensor, sensor, sensors.get(sensor.getObservedProperty()), uriComponent));
|
|
|
- }
|
|
|
-
|
|
|
- return datastreams;
|
|
|
- }
|
|
|
-
|
|
|
- static Sensor convertToSensor(MultiSensor afcMultiSensor, RequestUriComponent uriComponent) {
|
|
|
- Objects.requireNonNull(afcMultiSensor);
|
|
|
- Objects.requireNonNull(uriComponent);
|
|
|
-
|
|
|
- String afcDomain = "https://storage07-afarcloud.qa.pdmfc.com/storage/rest";
|
|
|
-
|
|
|
- Sensor sensor = new Sensor();
|
|
|
- String thingId = afcMultiSensor.getResourceUrn();
|
|
|
- String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
- sensor.setId(thingId);
|
|
|
- sensor.setSelfLink(format("%s/Sensors(%s)", absoluteUrl, thingId));
|
|
|
- sensor.setDataStreamNavigationLink(format("%s/Sensors(%s)/Datastreams", absoluteUrl, thingId));
|
|
|
- sensor.setName(afcMultiSensor.getResourceType());
|
|
|
- sensor.setDescription(afcMultiSensor.getResourceType());
|
|
|
- sensor.setEncodingType("application/json");
|
|
|
- sensor.setMetadata(format("%s/registry/getSensor/%s", afcDomain, afcMultiSensor.getResourceId()));
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
+// Copyright (c) 2020 UWB & LESP.
|
|
|
+// The UWB & LESP license this file to you under the MIT license.
|
|
|
+
|
|
|
+package io.connector.module.afarcloud.gateway;
|
|
|
+
|
|
|
+import io.connector.core.AbstractGateway;
|
|
|
+import io.connector.core.http.RequestUriComponent;
|
|
|
+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.Route;
|
|
|
+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.Supplier;
|
|
|
+
|
|
|
+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;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author Lukas Cerny
|
|
|
+ * @version 1.0
|
|
|
+ * @since 1.0
|
|
|
+ */
|
|
|
+public class OGCSensorThingsGateway extends AbstractGateway {
|
|
|
+
|
|
|
+ private final static Logger logger = LogManager.getLogger(OGCSensorThingsGateway.class);
|
|
|
+
|
|
|
+ /** Attribute of a client that accesses to the AFarCloud system. */
|
|
|
+ private final AFCHttpClient client;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Public constructor of the class that initializes final attributes.
|
|
|
+ * @param id - identifier of the gateway
|
|
|
+ * @param client - client allows access to the AFarCloud system
|
|
|
+ */
|
|
|
+ public OGCSensorThingsGateway(String id, AFCHttpClient client) {
|
|
|
+ super(id);
|
|
|
+ this.client = client;
|
|
|
+ }
|
|
|
+
|
|
|
+ @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());
|
|
|
+ List<MultiSensor> afcMultiSensors = client.getAllSensors();
|
|
|
+ List<Thing> ogcThings = Converter.convertToThing(afcMultiSensors, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(encode(ogcThings));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Things(:id)")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ Thing ogcThing = Converter.convertToThing(afcMultiSensor, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcThing.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Datastreams(:id)/Thing")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
+ String[] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0];
|
|
|
+ ctx.reroute(format("%s/Things(%s)", uriComponent.getGatewayPath(), resourceUrn));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("HistoricalLocations(:id)/Thing")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
+ String [] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0];
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ Thing ogcThing = Converter.convertToThing(afcMultiSensor, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcThing.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Datastreams(:id)")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
+ String[] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0], observedProperty = idCmp[1];
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ List<ResourceMeasurement> afcMeasurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
+ .limit(1).entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
+ );
|
|
|
+ final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Datastream with @iot.id \"" + id + "\".");
|
|
|
+
|
|
|
+ Optional<MultiSensor.SensorSchema> afcSensorOpt = afcMultiSensor.getObservations().stream().filter(s -> s.getObservedProperty().equals(observedProperty)).findFirst();
|
|
|
+ MultiSensor.SensorSchema afcSensor = afcSensorOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
+ ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Optional<SensorTelemetry> afcTelemetryOpt = ofNullable(afcMeasurement.getMeasurements().size() == 1 ? afcMeasurement.getMeasurements().get(0) : null);
|
|
|
+ SensorTelemetry afcTelemetry = afcTelemetryOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Datastream datastream = Converter.convertToDataStream(afcMultiSensor, afcSensor, afcTelemetry, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(datastream.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Things(:id)/Datastreams")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ List<ResourceMeasurement> afcMeasurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
+ .limit(1)
|
|
|
+ .entityNames(afcMultiSensor.getResourceId())
|
|
|
+ );
|
|
|
+ ResourceMeasurement afcMeasurement = afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null;
|
|
|
+ List<Datastream> ogcDataStream = Converter.convertToDataStream(afcMultiSensor, afcMeasurement, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(encode(ogcDataStream));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Sensors(:id)/Datastreams")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
+ ctx.reroute(format("%s/Things(%s)/Datastreams", uriComponent.getGatewayPath(), resourceUrn));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("ObservedProperties(:id)/Datastream")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
+ ctx.reroute(format("%s/Datastreams(%s)", uriComponent.getGatewayPath(), id));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Observations(:id)/Datastream")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resource + measurement + time
|
|
|
+ String[] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resource = idCmp[0], measurement = idCmp[1];
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceId(resource);
|
|
|
+ String resourceUrn = afcMultiSensor.getResourceUrn();
|
|
|
+ ctx.reroute(format("%s/Datastreams(%s)", uriComponent.getGatewayPath(), Converter.assemblyId(resourceUrn, measurement)));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Sensors(:id)")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ Sensor ogcSensor = Converter.convertToSensor(afcMultiSensor, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcSensor.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Datastreams(:id)/Sensor")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
+ String[] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0];
|
|
|
+ ctx.reroute(format("%s/Sensors(%s)", uriComponent.getGatewayPath(), resourceUrn));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("ObservedProperties(:id)")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
+ String[] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0], observedProperty = idCmp[1];
|
|
|
+ final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Datastream with @iot.id \"" + id + "\".");
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ Optional<MultiSensor.SensorSchema> afcSensorOpt = afcMultiSensor.getObservations().stream().filter(s -> s.getObservedProperty().equals(observedProperty)).findFirst();
|
|
|
+ MultiSensor.SensorSchema afcSensor = afcSensorOpt.orElseThrow(exception);
|
|
|
+ ObservedProperty property = Converter.convertToObservedProperty(afcMultiSensor, afcSensor, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(property.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Datastreams(:id)/ObservedProperty")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
+ ctx.reroute(format("%s/ObservedProperties(%s)", uriComponent.getGatewayPath(), id));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Observations(:id)")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resource + measurement + time
|
|
|
+ String[] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resource = idCmp[0], measurement = idCmp[1], time = idCmp[2];
|
|
|
+
|
|
|
+ Instant timestamp = Instant.parse(time);
|
|
|
+
|
|
|
+ List<ResourceMeasurement> measurements = client.getObservationsBySensor(new Filter()
|
|
|
+ .limit(1).order("ASC").entityNames(resource).measurements(measurement)
|
|
|
+ .startTime(timestamp).endTime(timestamp.plusSeconds(60))
|
|
|
+ );
|
|
|
+ final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException(format(
|
|
|
+ "Can not find Observation with @iot.id '%s'.", id));
|
|
|
+
|
|
|
+ Optional<ResourceMeasurement> measurementOpt = ofNullable(measurements.size() == 1 ? measurements.get(0) : null);
|
|
|
+ ResourceMeasurement afcMeasurement = measurementOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Optional<SensorTelemetry> telemetryOpt = ofNullable(afcMeasurement.getMeasurements().size() == 1 ? afcMeasurement.getMeasurements().get(0) : null);
|
|
|
+ SensorTelemetry afcTelemetry = telemetryOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Optional<io.connector.model.afarcloud.Observation> observationOpt = ofNullable(afcTelemetry.getObservations().size() > 0 ? afcTelemetry.getObservations().get(0) : null);
|
|
|
+ io.connector.model.afarcloud.Observation afcObservation = observationOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Observation ogcObservation = Converter.convertToObservation(afcMeasurement, afcTelemetry, afcObservation, uriComponent);
|
|
|
+ ctx.response().end(ogcObservation.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Datastreams(:id)/Observations")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty
|
|
|
+ String filterParam = ctx.request().getParam(Params.FILTER);
|
|
|
+
|
|
|
+ io.connector.model.sensorthings.Filter filter = null;
|
|
|
+ try {
|
|
|
+ filter = io.connector.model.sensorthings.Filter.parse(filterParam);
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
+ .put("message", e.getMessage()).encode()); return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String [] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0], observedProperty = idCmp[1];
|
|
|
+
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+
|
|
|
+ List<ResourceMeasurement> measurements;
|
|
|
+ if (filter.isExists()) {
|
|
|
+ Filter afcFilter;
|
|
|
+ try {
|
|
|
+ afcFilter = Converter.convertFilter(filter);
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
+ .put("message", e.getMessage()).encode()); return;
|
|
|
+ }
|
|
|
+ afcFilter.entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty);
|
|
|
+ measurements = client.getObservationsBySensor(afcFilter);
|
|
|
+ } else {
|
|
|
+ measurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
+ .limit(1).entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (measurements.isEmpty()) {
|
|
|
+ ctx.response().end(new JsonArray().encode());
|
|
|
+ } else {
|
|
|
+ final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Datastream with @iot.id \"" + id + "\".");
|
|
|
+
|
|
|
+ Optional<ResourceMeasurement> measurementOpt = ofNullable(measurements.size() == 1 ? measurements.get(0) : null);
|
|
|
+ ResourceMeasurement afcMeasurement = measurementOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Optional<SensorTelemetry> telemetryOpt = ofNullable(afcMeasurement.getMeasurements().size() == 1 ? afcMeasurement.getMeasurements().get(0) : null);
|
|
|
+ SensorTelemetry afcTelemetry = telemetryOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ List<Observation> ogcObservations = Converter.convertToObservations(afcMeasurement, afcTelemetry, uriComponent);
|
|
|
+ ctx.response().end(encode(ogcObservations));
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("FeaturesOfInterest(:id)")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id");
|
|
|
+ FeatureOfInterest featureOfInterest = new FeatureOfInterest();
|
|
|
+ featureOfInterest.setId(id);
|
|
|
+ featureOfInterest.setSelfLink("https://storage07-afarcloud.qa.pdmfc.com/storage/rest/registry/getAllObservationTypes");
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(featureOfInterest.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Locations(:id)")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
+ String [] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0], observedProperty = idCmp[1], time = idCmp[2];
|
|
|
+
|
|
|
+ Instant startTime = Instant.parse(time);
|
|
|
+ Instant endTime = startTime.plusSeconds(60);
|
|
|
+
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ List<ResourceMeasurement> afcMeasurements = client.getObservationsBySensor(new Filter()
|
|
|
+ .startTime(startTime).endTime(endTime)
|
|
|
+ .entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
+ );
|
|
|
+ final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Location with @iot.id \"" + resourceUrn + "\".");
|
|
|
+
|
|
|
+ Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
+ ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
+ Optional<AFCAggrLocation> afcLastLocationOpt = ofNullable(afcLocations.getLast());
|
|
|
+ AFCAggrLocation afcLastLocation = afcLastLocationOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Location ogcLocation = Converter.convertToLocation(afcMultiSensor, afcLastLocation, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcLocation.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Things(:id)/Locations")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
+
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ List<ResourceMeasurement> afcMeasurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
+ .limit(1).entityNames(afcMultiSensor.getResourceId())
|
|
|
+ );
|
|
|
+ final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Thing with @iot.id \"" + resourceUrn + "\".");
|
|
|
+
|
|
|
+ Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
+ ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
+ Optional<AFCAggrLocation> afcLastLocationOpt = ofNullable(afcLocations.getFirst());
|
|
|
+ AFCAggrLocation afcLastLocation = afcLastLocationOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ Location ogcLocation = Converter.convertToLocation(afcMultiSensor, afcLastLocation, uriComponent);
|
|
|
+ List<Location> ogcLocations = singletonList(ogcLocation);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(encode(ogcLocations));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("HistoricalLocations(:id)/Locations")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
+ ctx.reroute(format("%s/Locations(%s)", uriComponent.getGatewayPath(), id));
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("HistoricalLocations(:id)")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
+
|
|
|
+ String [] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0], observedProperty = idCmp[1], time = idCmp[2];
|
|
|
+
|
|
|
+ Instant startTime = Instant.parse(time);
|
|
|
+ Instant endTime = startTime.plusSeconds(60);
|
|
|
+
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ List<ResourceMeasurement> afcMeasurements = client.getObservationsBySensor(new Filter()
|
|
|
+ .startTime(startTime).endTime(endTime)
|
|
|
+ .entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
+ );
|
|
|
+ final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find Location with @iot.id \"" + resourceUrn + "\".");
|
|
|
+
|
|
|
+ Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
+ ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
+ Optional<AFCAggrLocation> afcLastLocationOpt = ofNullable(afcLocations.getLast());
|
|
|
+ AFCAggrLocation afcLocation = afcLastLocationOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ HistoricalLocation ogcLocation = Converter.convertToHistoricalLocation(afcMultiSensor, afcLocation, uriComponent);
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON).end(ogcLocation.encode());
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Things(:id)/HistoricalLocations")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String resourceUrn = ctx.pathParam("id"); // resourceUrn
|
|
|
+ String filterParam = ctx.request().getParam(Params.FILTER);
|
|
|
+
|
|
|
+ io.connector.model.sensorthings.Filter filter = null;
|
|
|
+ try {
|
|
|
+ filter = io.connector.model.sensorthings.Filter.parse(filterParam);
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
+ .put("message", e.getMessage()).encode()); return;
|
|
|
+ }
|
|
|
+
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+ List<ResourceMeasurement> afcMeasurements;
|
|
|
+ if (filter.isExists()) {
|
|
|
+ Filter afcFilter;
|
|
|
+ try {
|
|
|
+ afcFilter = Converter.convertFilter(filter);
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
+ .put("message", e.getMessage()).encode()); return;
|
|
|
+ }
|
|
|
+ afcFilter.entityNames(afcMultiSensor.getResourceId());
|
|
|
+ afcMeasurements = client.getObservationsBySensor(afcFilter);
|
|
|
+ } else {
|
|
|
+ afcMeasurements = client.getLatestObservationsBySensor(new Filter()
|
|
|
+ .limit(1).entityNames(afcMultiSensor.getResourceId())
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (afcMeasurements.isEmpty()) {
|
|
|
+ ctx.response().end(new JsonArray().encode());
|
|
|
+ } else {
|
|
|
+ final Supplier<IllegalArgumentException> exception = () -> new IllegalArgumentException("Can not find HistoricalLocations of the Thing with @iot.id \"" + resourceUrn + "\".");
|
|
|
+
|
|
|
+ Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
+ ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
+
|
|
|
+ List<HistoricalLocation> locations = Converter.convertToHistoricalLocation(afcMultiSensor, afcLocations.getList(), uriComponent);
|
|
|
+ ctx.response().end(encode(locations));
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ router().get(create("Locations(:id)/HistoricalLocations")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+ RequestUriComponent uriComponent = parseUriToComponents(ctx.request());
|
|
|
+ String id = ctx.pathParam("id"); // resourceUrn + observedProperty + time
|
|
|
+ String filterParam = ctx.request().getParam(Params.FILTER);
|
|
|
+
|
|
|
+ io.connector.model.sensorthings.Filter filter = null;
|
|
|
+ try {
|
|
|
+ filter = io.connector.model.sensorthings.Filter.parse(filterParam);
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
+ .put("message", e.getMessage()).encode()); return;
|
|
|
+ }
|
|
|
+
|
|
|
+ String [] idCmp = Converter.disassemblyId(id);
|
|
|
+ String resourceUrn = idCmp[0], observedProperty = idCmp[1], time = idCmp[2];
|
|
|
+
|
|
|
+ final Supplier<IllegalArgumentException> exception =
|
|
|
+ () -> new IllegalArgumentException("Can not find HistoricalLocations of the Thing with @iot.id \"" + resourceUrn + "\".");
|
|
|
+
|
|
|
+ MultiSensor afcMultiSensor = client.getSensorByResourceUrn(resourceUrn);
|
|
|
+
|
|
|
+ Filter afcFilter;
|
|
|
+ if (filter.isExists()) {
|
|
|
+ try {
|
|
|
+ afcFilter = Converter.convertFilter(filter);
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
+ .put("message", e.getMessage()).encode()); return;
|
|
|
+ }
|
|
|
+ afcFilter.entityNames(afcMultiSensor.getResourceId());
|
|
|
+ } else {
|
|
|
+ Instant startTime = Instant.parse(time);
|
|
|
+ Instant endTime = startTime.plusSeconds(60*60); // 1h
|
|
|
+
|
|
|
+ afcFilter = new Filter().startTime(startTime).endTime(endTime);
|
|
|
+ }
|
|
|
+
|
|
|
+ List<ResourceMeasurement> afcMeasurements = client.getObservationsBySensor(afcFilter
|
|
|
+ .entityNames(afcMultiSensor.getResourceId()).measurements(observedProperty)
|
|
|
+ );
|
|
|
+
|
|
|
+ if (afcMeasurements.isEmpty()) {
|
|
|
+ ctx.response().end(new JsonArray().encode());
|
|
|
+ } else {
|
|
|
+
|
|
|
+ Optional<ResourceMeasurement> afcMeasurementOpt = ofNullable(afcMeasurements.size() == 1 ? afcMeasurements.get(0) : null);
|
|
|
+ ResourceMeasurement afcMeasurement = afcMeasurementOpt.orElseThrow(exception);
|
|
|
+
|
|
|
+ AFCLocationList afcLocations = AFCLocationUtils.sort(afcMeasurement);
|
|
|
+
|
|
|
+ List<HistoricalLocation> locations = Converter.convertToHistoricalLocation(afcMultiSensor, afcLocations.getList(), uriComponent);
|
|
|
+ ctx.response().end(encode(locations));
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ router().post(create("Observations")).handler(ctx -> {
|
|
|
+ logger.info("Handling a request: {}.", ctx.request().absoluteURI());
|
|
|
+ ctx.response().putHeader(CONTENT_TYPE, APPLICATION_JSON);
|
|
|
+
|
|
|
+ JsonObject bodyJson = ctx.getBodyAsJson();
|
|
|
+ if (bodyJson.isEmpty()) {
|
|
|
+ ctx.response().setStatusCode(204).send(); return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ ObservationInsert ogcObservation = ObservationInsert.parse(bodyJson);
|
|
|
+ MultiSimpleObservation afcObservation = Converter.convertToMultiSimpleObservation(ogcObservation);
|
|
|
+ client.uploadAggregatedMeasurements(singletonList(afcObservation));
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ ctx.response().setStatusCode(501).end(new JsonObject()
|
|
|
+ .put("message", e.getMessage()).encode()); return;
|
|
|
+ }
|
|
|
+ ctx.response().setStatusCode(201).send();
|
|
|
+ });
|
|
|
+
|
|
|
+ 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> {
|
|
|
+ private final String measurement;
|
|
|
+ private final io.connector.model.afarcloud.Observation observation;
|
|
|
+
|
|
|
+ public AFCAggrLocation(String measurement, io.connector.model.afarcloud.Observation observation) {
|
|
|
+ this.measurement = measurement;
|
|
|
+ this.observation = observation;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean equals(Object o) {
|
|
|
+ if (this == o) return true;
|
|
|
+ if (o == null || getClass() != o.getClass()) return false;
|
|
|
+ AFCAggrLocation that = (AFCAggrLocation) o;
|
|
|
+ return this.compareTo(that) == 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int hashCode() {
|
|
|
+ return Objects.hash(measurement, observation);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int compareTo(AFCAggrLocation o) {
|
|
|
+ io.connector.model.afarcloud.Observation o1 = this.observation;
|
|
|
+ io.connector.model.afarcloud.Observation o2 = o.observation;
|
|
|
+
|
|
|
+ Instant o1Time = Instant.parse(o1.getTime());
|
|
|
+ Instant o2Time = Instant.parse(o2.getTime());
|
|
|
+
|
|
|
+ if (o1Time.equals(o2Time)) {
|
|
|
+ Double o1Dist = sqrt(pow(o1.getLongitude(), 2) + pow(o1.getLatitude(), 2) + pow(o1.getAltitude(), 2));
|
|
|
+ Double o2Dist = sqrt(pow(o2.getLongitude(), 2) + pow(o2.getLatitude(), 2) + pow(o2.getAltitude(), 2));
|
|
|
+ return o1Dist.compareTo(o2Dist);
|
|
|
+ } else {
|
|
|
+ return o1Time.compareTo(o2Time);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class AFCLocationList {
|
|
|
+ private final List<AFCAggrLocation> list;
|
|
|
+
|
|
|
+ AFCLocationList() {
|
|
|
+ this.list = new ArrayList<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ public void sort() {
|
|
|
+ Collections.sort(list);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void add(AFCAggrLocation location) {
|
|
|
+ list.add(location);
|
|
|
+ }
|
|
|
+
|
|
|
+ public AFCAggrLocation getFirst() {
|
|
|
+ return list.isEmpty() ? null : list.get(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ public AFCAggrLocation getLast() {
|
|
|
+ return list.isEmpty() ? null : list.get(list.size()-1);
|
|
|
+ }
|
|
|
+
|
|
|
+ public AFCAggrLocation get(int index) {
|
|
|
+ return list.get(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ public List<AFCAggrLocation> getList() {
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class AFCLocationUtils {
|
|
|
+
|
|
|
+ static AFCLocationList sort(ResourceMeasurement afcMeasurement) {
|
|
|
+ Objects.requireNonNull(afcMeasurement);
|
|
|
+
|
|
|
+ AFCLocationList aggrLocations = new AFCLocationList();
|
|
|
+ for (SensorTelemetry measurement : afcMeasurement.getMeasurements()) {
|
|
|
+ for (io.connector.model.afarcloud.Observation observation : measurement.getObservations()) {
|
|
|
+ aggrLocations.add(new AFCAggrLocation(measurement.getMeasurement(), observation));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ aggrLocations.sort();
|
|
|
+
|
|
|
+ List<AFCAggrLocation> list = aggrLocations.list;
|
|
|
+ if (list.size() > 1) {
|
|
|
+ Iterator<AFCAggrLocation> iterator = list.iterator();
|
|
|
+ AFCAggrLocation previous = iterator.next();
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ AFCAggrLocation current = iterator.next();
|
|
|
+ if (previous.equals(current)) {
|
|
|
+ iterator.remove(); continue;
|
|
|
+ }
|
|
|
+ previous = current;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return aggrLocations;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class Converter {
|
|
|
+
|
|
|
+ private final static Logger logger = LogManager.getLogger(Converter.class);
|
|
|
+
|
|
|
+ private static final String DELIMITER = "&";
|
|
|
+
|
|
|
+ static String assemblyId(String... parts) {
|
|
|
+ return String.join(DELIMITER, parts);
|
|
|
+ }
|
|
|
+
|
|
|
+ static String[] disassemblyId(String id) {
|
|
|
+ return id.split(DELIMITER);
|
|
|
+ }
|
|
|
+
|
|
|
+ static Filter convertFilter(io.connector.model.sensorthings.Filter ogcFilter) {
|
|
|
+ if (ogcFilter == null) {
|
|
|
+ throw new IllegalArgumentException("Unsupported filter attributes in filter expression '" + ogcFilter + "'.");
|
|
|
+ }
|
|
|
+
|
|
|
+ Filter afcFilter = new Filter();
|
|
|
+
|
|
|
+ for (io.connector.model.sensorthings.Filter.Expression expression : ogcFilter.getAddExpressions()) {
|
|
|
+ switch (expression.getAttribute()) {
|
|
|
+ case "time":
|
|
|
+ case "resultTime": {
|
|
|
+ switch (expression.getOperator()) {
|
|
|
+ case "lt": {
|
|
|
+ afcFilter.endTime(Instant.parse(expression.getValue()));
|
|
|
+ } break;
|
|
|
+ case "gt": {
|
|
|
+ afcFilter.startTime(Instant.parse(expression.getValue()));
|
|
|
+ }break;
|
|
|
+ default: throw new IllegalArgumentException(format(
|
|
|
+ "Unsupported operator '%s' in the filter expression '%s'.", expression.getOperator(), ogcFilter));
|
|
|
+ }
|
|
|
+ } break;
|
|
|
+ default: throw new IllegalArgumentException(format(
|
|
|
+ "Unsupported attribute '%s' in the filter expression '%s'.", expression.getAttribute(), ogcFilter));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return afcFilter;
|
|
|
+ }
|
|
|
+
|
|
|
+ static HistoricalLocation convertToHistoricalLocation(MultiSensor afcMultiSensor, AFCAggrLocation afcLocation, RequestUriComponent uriComponent) {
|
|
|
+ HistoricalLocation historicalLocation = new HistoricalLocation();
|
|
|
+ String locationId = assemblyId(afcMultiSensor.getResourceUrn(), afcLocation.measurement, afcLocation.observation.getTime());
|
|
|
+ String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
+ historicalLocation.setId(locationId);
|
|
|
+ historicalLocation.setSelfLink(format("%s/HistoricalLocations(%s)", absoluteUrl, locationId));
|
|
|
+ historicalLocation.setLocationsNavigationLink(format("%s/HistoricalLocations(%s)/Locations", absoluteUrl, locationId));
|
|
|
+ historicalLocation.setThingNavigationLink(format("%s/HistoricalLocations(%s)/Thing", absoluteUrl, locationId));
|
|
|
+ historicalLocation.setTime(afcLocation.observation.getTime());
|
|
|
+ return historicalLocation;
|
|
|
+ }
|
|
|
+
|
|
|
+ static List<HistoricalLocation> convertToHistoricalLocation(MultiSensor afcMultiSensor, List<AFCAggrLocation> afcLocations, RequestUriComponent uriComponent) {
|
|
|
+ List<HistoricalLocation> historicalLocations = new ArrayList<>(afcLocations.size());
|
|
|
+ for (AFCAggrLocation afcLocation : afcLocations) {
|
|
|
+ historicalLocations.add(convertToHistoricalLocation(afcMultiSensor, afcLocation, uriComponent));
|
|
|
+ }
|
|
|
+ return historicalLocations;
|
|
|
+ }
|
|
|
+
|
|
|
+ static Location convertToLocation(MultiSensor afcMultiSensor, AFCAggrLocation afcLocation, RequestUriComponent uriComponent) {
|
|
|
+ Location location = new Location();
|
|
|
+ String locationId = assemblyId(afcMultiSensor.getResourceUrn(), afcLocation.measurement, afcLocation.observation.getTime());
|
|
|
+ String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
+ location.setId(locationId);
|
|
|
+ location.setSelfLink(format("%s/Locations(%s)", absoluteUrl, locationId));
|
|
|
+ location.setHistoricalLocationsNavigationLink(format("%s/Locations(%s)/HistoricalLocations", absoluteUrl, locationId));
|
|
|
+ location.setName(afcMultiSensor.getResourceType());
|
|
|
+ location.setDescription(afcLocation.observation.getProvider());
|
|
|
+ location.setEncodingType("application/vnd.geo+json");
|
|
|
+
|
|
|
+ LocationInfo info = new LocationInfo();
|
|
|
+ info.setType("Feature");
|
|
|
+ location.setLocation(info);
|
|
|
+
|
|
|
+ Geometry geometry = new Geometry();
|
|
|
+ geometry.setType("Point");
|
|
|
+ io.connector.model.afarcloud.Observation afcObservation = afcLocation.observation;
|
|
|
+ geometry.setCoordinates(asList(afcObservation.getLongitude(), afcObservation.getLatitude(), afcObservation.getAltitude()));
|
|
|
+ info.setGeometry(geometry);
|
|
|
+
|
|
|
+ return location;
|
|
|
+ }
|
|
|
+
|
|
|
+ static Observation convertToObservation(ResourceMeasurement afcMeasurement, SensorTelemetry afcTelemetry, io.connector.model.afarcloud.Observation afcObservation, RequestUriComponent uriComponent) {
|
|
|
+ Observation observation = new Observation();
|
|
|
+ String observationId = assemblyId(afcMeasurement.getResource(), afcTelemetry.getMeasurement(), afcObservation.getTime());
|
|
|
+ String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
+ observation.setId(observationId);
|
|
|
+ observation.setSelfLink(format("%s/Observations(%s)", absoluteUrl, observationId));
|
|
|
+ // observation.setFeatureOfInterestNavigationLink(format("Observations(%s)/FeatureOfInterest", observationId));
|
|
|
+ observation.setFeatureOfInterestNavigationLink("https://storage07-afarcloud.qa.pdmfc.com/storage/rest/registry/getAllObservationTypes");
|
|
|
+ observation.setDataStreamNavigationLink(format("%s/Observations(%s)/Datastream", absoluteUrl, observationId));
|
|
|
+ observation.setPhenomenonTime(afcObservation.getTime());
|
|
|
+ observation.setResultTime(afcObservation.getTime());
|
|
|
+ observation.setResult(afcObservation.getValue());
|
|
|
+ return observation;
|
|
|
+ }
|
|
|
+
|
|
|
+ static List<Observation> convertToObservations(ResourceMeasurement afcMeasurement, SensorTelemetry afcTelemetry, RequestUriComponent uriComponent) {
|
|
|
+ List<Observation> ogcObservations = new ArrayList<>(afcTelemetry.getObservations().size());
|
|
|
+ for (io.connector.model.afarcloud.Observation afcObservation : afcTelemetry.getObservations()) {
|
|
|
+ ogcObservations.add(convertToObservation(afcMeasurement, afcTelemetry, afcObservation, uriComponent));
|
|
|
+ }
|
|
|
+ return ogcObservations;
|
|
|
+ }
|
|
|
+
|
|
|
+ static ObservedProperty convertToObservedProperty(MultiSensor afcMultiSensor, MultiSensor.SensorSchema afcSensor, RequestUriComponent uriComponent) {
|
|
|
+ Objects.requireNonNull(afcMultiSensor);
|
|
|
+ Objects.requireNonNull(afcSensor);
|
|
|
+ Objects.requireNonNull(uriComponent);
|
|
|
+
|
|
|
+ ObservedProperty observedProperty = new ObservedProperty();
|
|
|
+ String observedPropertyId = assemblyId(afcMultiSensor.getResourceUrn(), afcSensor.getObservedProperty());
|
|
|
+ String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
+ observedProperty.setId(observedPropertyId);
|
|
|
+ observedProperty.setSelfLink(format("%s/ObservedProperties(%s)", absoluteUrl, observedPropertyId));
|
|
|
+ observedProperty.setDataStreamNavigationLink(format("%s/ObservedProperties(%s)/Datastream", absoluteUrl, observedPropertyId));
|
|
|
+ observedProperty.setName(afcSensor.getObservedProperty());
|
|
|
+ observedProperty.setDescription(afcSensor.getObservedProperty());
|
|
|
+ observedProperty.setDefinition(afcSensor.getUom());
|
|
|
+ return observedProperty;
|
|
|
+ }
|
|
|
+
|
|
|
+ static Thing convertToThing(MultiSensor afcMultiSensor, RequestUriComponent uriComponent) {
|
|
|
+ Objects.requireNonNull(afcMultiSensor);
|
|
|
+ Objects.requireNonNull(uriComponent);
|
|
|
+
|
|
|
+ Thing thing = new Thing();
|
|
|
+ String thingId = afcMultiSensor.getResourceUrn();
|
|
|
+ String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
+ thing.setId(thingId);
|
|
|
+ thing.setSelfLink(format("%s/Things(%s)", absoluteUrl, thingId));
|
|
|
+ thing.setLocationsNavigationLink(format("%s/Things(%s)/Locations", absoluteUrl, thingId));
|
|
|
+ thing.setDataStreamNavigationLink(format("%s/Things(%s)/Datastreams", absoluteUrl, thingId));
|
|
|
+ thing.setHistoricalLocationsNavigationLink(format("%s/Things(%s)/HistoricalLocations", absoluteUrl, thingId));
|
|
|
+ thing.setName(afcMultiSensor.getResourceType());
|
|
|
+ thing.setDescription(afcMultiSensor.getResourceType());
|
|
|
+ thing.setProperties(null);
|
|
|
+ return thing;
|
|
|
+ }
|
|
|
+
|
|
|
+ static List<Thing> convertToThing(List<MultiSensor> afcMultiSensors, RequestUriComponent uriComponent) {
|
|
|
+ List<Thing> ogcThings = new ArrayList<>(afcMultiSensors.size());
|
|
|
+ for (MultiSensor afcMultiSensor : afcMultiSensors) {
|
|
|
+ ogcThings.add(convertToThing(afcMultiSensor, uriComponent));
|
|
|
+ }
|
|
|
+ return ogcThings;
|
|
|
+ }
|
|
|
+
|
|
|
+ static Datastream convertToDataStream(MultiSensor afcMultiSensor, MultiSensor.SensorSchema afcSensor, SensorTelemetry telemetry, RequestUriComponent uriComponent) {
|
|
|
+ Objects.requireNonNull(afcMultiSensor);
|
|
|
+ Objects.requireNonNull(afcSensor);
|
|
|
+ Objects.requireNonNull(uriComponent);
|
|
|
+
|
|
|
+ Datastream datastream = new Datastream();
|
|
|
+ String dataStreamId = assemblyId(afcMultiSensor.getResourceUrn(), afcSensor.getObservedProperty());
|
|
|
+ String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
+ datastream.setId(dataStreamId);
|
|
|
+ datastream.setSelfLink(format("%s/Datastreams(%s)", absoluteUrl, dataStreamId));
|
|
|
+ datastream.setThingNavigationLink(format("%s/Datastreams(%s)/Thing", absoluteUrl, dataStreamId));
|
|
|
+ datastream.setSensorNavigationLink(format("%s/Datastreams(%s)/Sensor", absoluteUrl, dataStreamId));
|
|
|
+ datastream.setObservedPropertyNavigationLink(format("%s/Datastreams(%s)/ObservedProperty", absoluteUrl, dataStreamId));
|
|
|
+ datastream.setObservationsNavigationLink(format("%s/Datastreams(%s)/Observations", absoluteUrl, dataStreamId));
|
|
|
+ datastream.setName(afcSensor.getObservedProperty());
|
|
|
+ datastream.setDescription(afcSensor.getObservedProperty());
|
|
|
+
|
|
|
+ UnitOfMeasurement uom = new UnitOfMeasurement();
|
|
|
+ uom.setName(afcSensor.getObservedProperty());
|
|
|
+ uom.setSymbol("");
|
|
|
+ uom.setDefinition(afcSensor.getUom());
|
|
|
+ datastream.setUnitOfMeasurement(uom);
|
|
|
+
|
|
|
+ datastream.setObservationType("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
|
|
|
+
|
|
|
+ Geometry geometry = new Geometry();
|
|
|
+ geometry.setType("Point");
|
|
|
+ geometry.setCoordinates(asList(afcMultiSensor.getLongitude(), afcMultiSensor.getLatitude()));
|
|
|
+ datastream.setObservedArea(geometry);
|
|
|
+
|
|
|
+ String startDate = "<none>";
|
|
|
+ String endDate = telemetry == null || telemetry.getObservations().isEmpty() ? "<none>" : telemetry.getObservations().get(0).getTime();
|
|
|
+ String time = startDate + "/" + endDate;
|
|
|
+ datastream.setPhenomenonTime(time);
|
|
|
+ datastream.setResultTime(time);
|
|
|
+
|
|
|
+ return datastream;
|
|
|
+ }
|
|
|
+
|
|
|
+ static List<Datastream> convertToDataStream(MultiSensor afcMultiSensor, ResourceMeasurement measurement, RequestUriComponent uriComponent) {
|
|
|
+ Objects.requireNonNull(afcMultiSensor);
|
|
|
+ Objects.requireNonNull(uriComponent);
|
|
|
+
|
|
|
+ List<Datastream> datastreams = new ArrayList<>(afcMultiSensor.getObservations().size());
|
|
|
+
|
|
|
+ Map<String, SensorTelemetry> sensors = new HashMap<>();
|
|
|
+ if (measurement != null) {
|
|
|
+ for (SensorTelemetry telemetry : measurement.getMeasurements()) {
|
|
|
+ sensors.put(telemetry.getMeasurement(), telemetry);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ for (MultiSensor.SensorSchema sensor : afcMultiSensor.getObservations()) {
|
|
|
+ datastreams.add(convertToDataStream(afcMultiSensor, sensor, sensors.get(sensor.getObservedProperty()), uriComponent));
|
|
|
+ }
|
|
|
+
|
|
|
+ return datastreams;
|
|
|
+ }
|
|
|
+
|
|
|
+ static Sensor convertToSensor(MultiSensor afcMultiSensor, RequestUriComponent uriComponent) {
|
|
|
+ Objects.requireNonNull(afcMultiSensor);
|
|
|
+ Objects.requireNonNull(uriComponent);
|
|
|
+
|
|
|
+ String afcDomain = "https://storage07-afarcloud.qa.pdmfc.com/storage/rest";
|
|
|
+
|
|
|
+ Sensor sensor = new Sensor();
|
|
|
+ String thingId = afcMultiSensor.getResourceUrn();
|
|
|
+ String absoluteUrl = uriComponent.getGatewayUri();
|
|
|
+ sensor.setId(thingId);
|
|
|
+ sensor.setSelfLink(format("%s/Sensors(%s)", absoluteUrl, thingId));
|
|
|
+ sensor.setDataStreamNavigationLink(format("%s/Sensors(%s)/Datastreams", absoluteUrl, thingId));
|
|
|
+ sensor.setName(afcMultiSensor.getResourceType());
|
|
|
+ sensor.setDescription(afcMultiSensor.getResourceType());
|
|
|
+ sensor.setEncodingType("application/json");
|
|
|
+ sensor.setMetadata(format("%s/registry/getSensor/%s", afcDomain, afcMultiSensor.getResourceId()));
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|