Permalink
Browse files

Cleanup of location predicate code and further WIP on JSON rules

  • Loading branch information...
richturner committed Jan 2, 2019
1 parent 6baec89 commit c0e485c6d57d5bee032f62a7d49cd0af3ed0b556
Showing with 366 additions and 292 deletions.
  1. +31 −38 manager/src/main/java/org/openremote/manager/asset/AssetStorageService.java
  2. +16 −15 manager/src/main/java/org/openremote/manager/rules/AssetQueryPredicate.java
  3. +6 −5 manager/src/main/java/org/openremote/manager/rules/RulesEngine.java
  4. +82 −78 manager/src/main/java/org/openremote/manager/rules/RulesFacts.java
  5. +3 −3 manager/src/main/java/org/openremote/manager/rules/RulesService.java
  6. +11 −2 manager/src/main/java/org/openremote/manager/rules/RulesetDeployment.java
  7. +3 −3 manager/src/main/java/org/openremote/manager/rules/geofence/GeofenceAssetAdapter.java
  8. +5 −5 manager/src/main/java/org/openremote/manager/rules/geofence/ORConsoleGeofenceAssetAdapter.java
  9. +59 −58 manager/src/main/resources/demo/rules/DemoConsoleLocation.groovy
  10. +4 −3 model/src/main/java/org/openremote/model/notification/AbstractNotificationMessage.java
  11. +0 −6 model/src/main/java/org/openremote/model/query/BaseAssetQuery.java
  12. +3 −2 model/src/main/java/org/openremote/model/query/filter/BooleanPredicate.java
  13. +3 −2 model/src/main/java/org/openremote/model/query/filter/DateTimePredicate.java
  14. +7 −13 ...src/main/java/org/openremote/model/query/filter/{LocationPredicate.java → GeofencePredicate.java}
  15. +45 −0 model/src/main/java/org/openremote/model/query/filter/LocationAttributePredicate.java
  16. +3 −2 model/src/main/java/org/openremote/model/query/filter/NumberPredicate.java
  17. +1 −1 model/src/main/java/org/openremote/model/query/filter/ObjectValueKeyPredicate.java
  18. +19 −6 ...a/org/openremote/model/query/filter/{RadialLocationPredicate.java → RadialGeofencePredicate.java}
  19. +20 −6 ...remote/model/query/filter/{RectangularLocationPredicate.java → RectangularGeofencePredicate.java}
  20. +3 −2 model/src/main/java/org/openremote/model/query/filter/StringArrayPredicate.java
  21. +3 −2 model/src/main/java/org/openremote/model/query/filter/StringPredicate.java
  22. +1 −2 model/src/main/java/org/openremote/model/query/filter/ValueEmptyPredicate.java
  23. +1 −2 model/src/main/java/org/openremote/model/query/filter/ValueNotEmptyPredicate.java
  24. +8 −7 model/src/main/java/org/openremote/model/query/filter/ValuePredicate.java
  25. +1 −1 model/src/main/java/org/openremote/model/rules/json/JsonRulesetDefinition.java
  26. +1 −3 model/src/main/java/org/openremote/model/rules/json/predicate/AttributePredicate.java
  27. +8 −7 test/src/test/groovy/org/openremote/test/assets/AssetQueryTest.groovy
  28. +4 −4 test/src/test/groovy/org/openremote/test/console/ConsoleTest.groovy
  29. +2 −3 test/src/test/groovy/org/openremote/test/rules/residence/ResidenceLightsOnGeofenceTest.groovy
  30. +6 −5 test/src/test/resources/org/openremote/test/rules/BasicLocationPredicates.groovy
  31. +1 −1 test/src/test/resources/org/openremote/test/rules/BasicLocationPredicates.json
  32. +6 −5 test/src/test/resources/org/openremote/test/rules/BasicLocationPredicates2.groovy
@@ -57,7 +57,6 @@
import org.openremote.model.security.User;
import org.openremote.model.util.Pair;
import org.openremote.model.util.TextUtil;
import org.openremote.model.util.TimeUtil;
import org.openremote.model.value.ObjectValue;
import org.openremote.model.value.Value;
import org.openremote.model.value.Values;
@@ -67,13 +66,8 @@
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import java.sql.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
import java.util.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -89,6 +83,7 @@
import static org.openremote.model.query.BaseAssetQuery.Access.RESTRICTED_READ;
import static org.openremote.model.query.BaseAssetQuery.Include.ALL;
import static org.openremote.model.query.BaseAssetQuery.Include.ALL_EXCEPT_PATH_AND_ATTRIBUTES;
import static org.openremote.model.query.filter.LocationAttributePredicate.getLocationPredicate;
import static org.openremote.model.util.TextUtil.isNullOrEmpty;

public class AssetStorageService extends RouteBuilder implements ContainerService {
@@ -894,37 +889,6 @@ protected String buildWhereClause(BaseAssetQuery query, int level, List<Paramete
binders.add(st -> st.setString(pos, query.name.prepareValue()));
}

if (level == 1 && query.location != null) {
if (query.location instanceof RadialLocationPredicate) {
RadialLocationPredicate location = (RadialLocationPredicate) query.location;
sb.append(" and ST_Distance_Sphere(ST_MakePoint(");
sb.append("(A.attributes #>> '{location,value,coordinates,0}')::numeric");
sb.append(", (A.attributes #>> '{location,value,coordinates,1}')::numeric");
sb.append("), ST_MakePoint(");
sb.append(location.lng);
sb.append(",");
sb.append(location.lat);
sb.append(location.negated ? ")) > " : ")) <= ");
sb.append(location.radius);
} else if (query.location instanceof RectangularLocationPredicate) {
RectangularLocationPredicate location = (RectangularLocationPredicate) query.location;
sb.append(location.negated ? " and NOT" : " and");
sb.append(" ST_Within(ST_MakePoint(");
sb.append("(A.attributes #>> '{location,value,coordinates,0}')::numeric");
sb.append(", (A.attributes #>> '{location,value,coordinates,1}')::numeric");
sb.append(")");
sb.append(", ST_MakeEnvelope(");
sb.append(location.lngMin);
sb.append(",");
sb.append(location.latMin);
sb.append(",");
sb.append(location.lngMax);
sb.append(",");
sb.append(location.latMax);
sb.append("))");
}
}

if (query.parent != null) {
// Can only restrict recursive query parent by asset type
if (level == 1 && query.parent.id != null) {
@@ -1178,7 +1142,7 @@ protected String buildAttributeFilter(AttributePredicate attributePredicate, Lis
binders.add(st -> st.setDouble(pos, numberPredicate.value));
if (numberPredicate.operator == Operator.BETWEEN) {
final int pos2 = binders.size() + 1;
binders.add(st -> st.setDouble(pos, numberPredicate.rangeValue));
binders.add(st -> st.setDouble(pos2, numberPredicate.rangeValue));
}
break;
case INTEGER:
@@ -1198,6 +1162,35 @@ protected String buildAttributeFilter(AttributePredicate attributePredicate, Lis
}
final int pos = binders.size() + 1;
binders.add(st -> st.setString(pos, keyPredicate.key));
} else if (attributePredicate.value instanceof GeofencePredicate) {
if (attributePredicate.value instanceof RadialGeofencePredicate) {
RadialGeofencePredicate location = (RadialGeofencePredicate) attributePredicate.value;
attributeBuilder.append(" and ST_Distance_Sphere(ST_MakePoint(");
attributeBuilder.append("(AX.VALUE #>> '{value,coordinates,0}')::numeric");
attributeBuilder.append(", (AX.VALUE #>> '{value,coordinates,1}')::numeric");
attributeBuilder.append("), ST_MakePoint(");
attributeBuilder.append(location.lng);
attributeBuilder.append(",");
attributeBuilder.append(location.lat);
attributeBuilder.append(location.negated ? ")) > " : ")) <= ");
attributeBuilder.append(location.radius);
} else if (attributePredicate.value instanceof RectangularGeofencePredicate) {
RectangularGeofencePredicate location = (RectangularGeofencePredicate) attributePredicate.value;
attributeBuilder.append(location.negated ? " and NOT" : " and");
attributeBuilder.append(" ST_Within(ST_MakePoint(");
attributeBuilder.append("(AX.VALUE #>> '{value,coordinates,0}')::numeric");
attributeBuilder.append(", (AX.VALUE #>> '{value,coordinates,1}')::numeric");
attributeBuilder.append(")");
attributeBuilder.append(", ST_MakeEnvelope(");
attributeBuilder.append(location.lngMin);
attributeBuilder.append(",");
attributeBuilder.append(location.latMin);
attributeBuilder.append(",");
attributeBuilder.append(location.lngMax);
attributeBuilder.append(",");
attributeBuilder.append(location.latMax);
attributeBuilder.append("))");
}
}
}

@@ -40,6 +40,7 @@
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Locale;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;

@@ -107,16 +108,6 @@ public boolean test(AssetState assetState) {
throw new UnsupportedOperationException("Sorting with 'orderBy' not supported in rules matching");
}

if (query.location != null) {
GeoJSONPoint coords = AttributeType.LOCATION.getName().equals(assetState.getAttributeName()) ? assetState.getValue().flatMap(GeoJSONPoint::fromValue).orElse(null) : null;

if (coords != null) {
Coordinate coordinate = new Coordinate(coords.getY(), coords.getX());
return asPredicate(query.location).test(coordinate);
}
return false;
}

return true;
}

@@ -257,18 +248,22 @@ public boolean test(AssetState assetState) {
&& (predicate.realmId == null || predicate.realmId.equals(assetState.getRealmId()));
}

public static Predicate<Coordinate> asPredicate(LocationPredicate predicate) {
public static Predicate<Coordinate> asPredicate(GeofencePredicate predicate) {
return coordinate -> {
if (predicate instanceof RadialLocationPredicate) {
if (coordinate == null) {
return false;
}

if (predicate instanceof RadialGeofencePredicate) {
//TODO geotools version to gradle properties
RadialLocationPredicate radialLocationPredicate = (RadialLocationPredicate) predicate;
RadialGeofencePredicate radialLocationPredicate = (RadialGeofencePredicate) predicate;
GeodeticCalculator calculator = new GeodeticCalculator();
calculator.setStartingGeographicPoint(radialLocationPredicate.lng, radialLocationPredicate.lat);
calculator.setDestinationGeographicPoint(coordinate.y, coordinate.x);
return calculator.getOrthodromicDistance() < radialLocationPredicate.radius;
} else if (predicate instanceof RectangularLocationPredicate) {
} else if (predicate instanceof RectangularGeofencePredicate) {
// Again this is a euclidean plane so doesn't work perfectly for WGS lat/lng - the bigger the rectangle to less accurate it is)
RectangularLocationPredicate rectangularLocationPredicate = (RectangularLocationPredicate) predicate;
RectangularGeofencePredicate rectangularLocationPredicate = (RectangularGeofencePredicate) predicate;
Envelope envelope = new Envelope(rectangularLocationPredicate.latMin,
rectangularLocationPredicate.lngMin,
rectangularLocationPredicate.latMax,
@@ -320,7 +315,13 @@ public boolean test(AssetState assetState) {

DateTimePredicate p = (DateTimePredicate) predicate;
return asPredicate(currentMillisProducer, p).test(Values.getNumber(value).map(Double::longValue).orElse(null));
} else if (predicate instanceof GeofencePredicate) {

GeofencePredicate p = (GeofencePredicate) predicate;
return asPredicate(p).test(Optional.ofNullable(value)
.flatMap(GeoJSONPoint::fromValue)
.map(point -> new Coordinate(point.getY(), point.getX()))
.orElse(null));
} else {
// TODO Implement more
throw new UnsupportedOperationException(
@@ -34,7 +34,8 @@
import org.openremote.model.asset.Asset;
import org.openremote.model.asset.AssetMeta;
import org.openremote.model.attribute.AttributeType;
import org.openremote.model.query.filter.LocationPredicate;
import org.openremote.model.query.filter.GeofencePredicate;
import org.openremote.model.query.filter.LocationAttributePredicate;
import org.openremote.model.rules.*;

import java.util.*;
@@ -49,14 +50,14 @@
public class RulesEngine<T extends Ruleset> {

/**
* Identifies a set of {@link LocationPredicate}s associated with a particular {@link Asset}
* Identifies a set of {@link LocationAttributePredicate}s associated with a particular {@link Asset}
*/
public static final class AssetStateLocationPredicates {

protected final String assetId;
protected final Set<LocationPredicate> locationPredicates;
protected final Set<GeofencePredicate> locationPredicates;

public AssetStateLocationPredicates(String assetId, Set<LocationPredicate> locationPredicates) {
public AssetStateLocationPredicates(String assetId, Set<GeofencePredicate> locationPredicates) {
this.assetId = assetId;
this.locationPredicates = locationPredicates;
}
@@ -65,7 +66,7 @@ public String getAssetId() {
return assetId;
}

public Set<LocationPredicate> getLocationPredicates() {
public Set<GeofencePredicate> getLocationPredicates() {
return locationPredicates;
}
}
Oops, something went wrong.

0 comments on commit c0e485c

Please sign in to comment.