Commit 876b9eeda4253926ee797609aa33187d6e2cf4c1

Authored by Andrew Shvayka
1 parent f41cd97c

Initial rule nodes commit

... ... @@ -71,6 +71,8 @@
71 71 <jar-plugin.version>3.0.2</jar-plugin.version>
72 72 <springfox-swagger.version>2.6.1</springfox-swagger.version>
73 73 <springfox-swagger-ui-rfc6570.version>1.0.0</springfox-swagger-ui-rfc6570.version>
  74 + <spatial4j.version>0.7</spatial4j.version>
  75 + <jts.version>1.15.0</jts.version>
74 76 <bouncycastle.version>1.56</bouncycastle.version>
75 77 <winsw.version>2.0.1</winsw.version>
76 78 <hsqldb.version>2.4.0</hsqldb.version>
... ... @@ -828,6 +830,16 @@
828 830 <artifactId>springfox-swagger-ui-rfc6570</artifactId>
829 831 <version>${springfox-swagger-ui-rfc6570.version}</version>
830 832 </dependency>
  833 + <dependency>
  834 + <groupId>org.locationtech.spatial4j</groupId>
  835 + <artifactId>spatial4j</artifactId>
  836 + <version>${spatial4j.version}</version>
  837 + </dependency>
  838 + <dependency>
  839 + <groupId>org.locationtech.jts</groupId>
  840 + <artifactId>jts-core</artifactId>
  841 + <version>${jts.version}</version>
  842 + </dependency>
831 843 </dependencies>
832 844 </dependencyManagement>
833 845
... ...
... ... @@ -98,6 +98,14 @@
98 98 <artifactId>bcpkix-jdk15on</artifactId>
99 99 </dependency>
100 100 <dependency>
  101 + <groupId>org.locationtech.spatial4j</groupId>
  102 + <artifactId>spatial4j</artifactId>
  103 + </dependency>
  104 + <dependency>
  105 + <groupId>org.locationtech.jts</groupId>
  106 + <artifactId>jts-core</artifactId>
  107 + </dependency>
  108 + <dependency>
101 109 <groupId>junit</groupId>
102 110 <artifactId>junit</artifactId>
103 111 <version>${junit.version}</version>
... ... @@ -142,10 +150,10 @@
142 150 <executable>true</executable>
143 151 <excludeDevtools>true</excludeDevtools>
144 152 <!--<embeddedLaunchScriptProperties>-->
145   - <!--<confFolder>${pkg.installFolder}/conf</confFolder>-->
146   - <!--<logFolder>${pkg.unixLogFolder}</logFolder>-->
147   - <!--<logFilename>${pkg.name}.out</logFilename>-->
148   - <!--<initInfoProvides>${pkg.name}</initInfoProvides>-->
  153 + <!--<confFolder>${pkg.installFolder}/conf</confFolder>-->
  154 + <!--<logFolder>${pkg.unixLogFolder}</logFolder>-->
  155 + <!--<logFilename>${pkg.name}.out</logFilename>-->
  156 + <!--<initInfoProvides>${pkg.name}</initInfoProvides>-->
149 157 <!--</embeddedLaunchScriptProperties>-->
150 158 </configuration>
151 159 <executions>
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import com.google.gson.JsonElement;
  19 +import com.google.gson.JsonObject;
  20 +import com.google.gson.JsonParser;
  21 +import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
  22 +import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
  23 +import org.springframework.util.StringUtils;
  24 +import org.thingsboard.rule.engine.api.TbContext;
  25 +import org.thingsboard.rule.engine.api.TbNode;
  26 +import org.thingsboard.rule.engine.api.TbNodeConfiguration;
  27 +import org.thingsboard.rule.engine.api.TbNodeException;
  28 +import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  29 +import org.thingsboard.server.common.msg.TbMsg;
  30 +
  31 +import java.util.Collections;
  32 +import java.util.List;
  33 +
  34 +public abstract class AbstractGeofencingNode<T extends TbGpsGeofencingFilterNodeConfiguration> implements TbNode {
  35 +
  36 + protected T config;
  37 + protected JtsSpatialContext jtsCtx;
  38 +
  39 + @Override
  40 + public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
  41 + this.config = TbNodeUtils.convert(configuration, getConfigClazz());
  42 + JtsSpatialContextFactory factory = new JtsSpatialContextFactory();
  43 + factory.normWrapLongitude = true;
  44 + jtsCtx = factory.newSpatialContext();
  45 + }
  46 +
  47 + abstract protected Class<T> getConfigClazz();
  48 +
  49 + protected boolean checkMatches(TbMsg msg) throws TbNodeException {
  50 + JsonElement msgDataElement = new JsonParser().parse(msg.getData());
  51 + if (!msgDataElement.isJsonObject()) {
  52 + throw new TbNodeException("Incoming Message is not a valid JSON object");
  53 + }
  54 + JsonObject msgDataObj = msgDataElement.getAsJsonObject();
  55 + double latitude = getValueFromMessageByName(msg, msgDataObj, config.getLatitudeKeyName());
  56 + double longitude = getValueFromMessageByName(msg, msgDataObj, config.getLongitudeKeyName());
  57 + List<Perimeter> perimeters = getPerimeters(msg, msgDataObj);
  58 + boolean matches = false;
  59 + for (Perimeter perimeter : perimeters) {
  60 + if (checkMatches(perimeter, latitude, longitude)) {
  61 + matches = true;
  62 + break;
  63 + }
  64 + }
  65 + return matches;
  66 + }
  67 +
  68 + protected boolean checkMatches(Perimeter perimeter, double latitude, double longitude) throws TbNodeException {
  69 + if (perimeter.getPerimeterType() == PerimeterType.CIRCLE) {
  70 + Coordinates entityCoordinates = new Coordinates(latitude, longitude);
  71 + Coordinates perimeterCoordinates = new Coordinates(perimeter.getCenterLatitude(), perimeter.getCenterLongitude());
  72 + return perimeter.getRange() > GeoUtil.distance(entityCoordinates, perimeterCoordinates, perimeter.getRangeUnit());
  73 + } else if (perimeter.getPerimeterType() == PerimeterType.POLYGON) {
  74 + return GeoUtil.contains(perimeter.getPolygonsDefinition(), new Coordinates(latitude, longitude));
  75 + } else {
  76 + throw new TbNodeException("Unsupported perimeter type: " + perimeter.getPerimeterType());
  77 + }
  78 + }
  79 +
  80 + protected List<Perimeter> getPerimeters(TbMsg msg, JsonObject msgDataObj) throws TbNodeException {
  81 + if (config.isFetchPerimeterInfoFromMessage()) {
  82 + //TODO: add fetching perimeters from the message itself, if configuration is empty.
  83 + if (!StringUtils.isEmpty(msg.getMetaData().getValue("perimeter"))) {
  84 + Perimeter perimeter = new Perimeter();
  85 + perimeter.setPerimeterType(PerimeterType.POLYGON);
  86 + perimeter.setPolygonsDefinition(msg.getMetaData().getValue("perimeter"));
  87 + return Collections.singletonList(perimeter);
  88 + } else if (!StringUtils.isEmpty(msg.getMetaData().getValue("centerLatitude"))) {
  89 + Perimeter perimeter = new Perimeter();
  90 + perimeter.setPerimeterType(PerimeterType.CIRCLE);
  91 + perimeter.setCenterLatitude(Double.parseDouble(msg.getMetaData().getValue("centerLatitude")));
  92 + perimeter.setCenterLongitude(Double.parseDouble(msg.getMetaData().getValue("centerLongitude")));
  93 + perimeter.setRange(Double.parseDouble(msg.getMetaData().getValue("range")));
  94 + perimeter.setRangeUnit(RangeUnit.valueOf(msg.getMetaData().getValue("rangeUnit")));
  95 + return Collections.singletonList(perimeter);
  96 + } else {
  97 + throw new TbNodeException("Missing perimeter definition!");
  98 + }
  99 + } else {
  100 + return config.getPerimeters();
  101 + }
  102 + }
  103 +
  104 + protected Double getValueFromMessageByName(TbMsg msg, JsonObject msgDataObj, String keyName) throws TbNodeException {
  105 + double value;
  106 + if (msgDataObj.has(keyName) && msgDataObj.get(keyName).isJsonPrimitive()) {
  107 + value = msgDataObj.get(keyName).getAsDouble();
  108 + } else {
  109 + String valueStr = msg.getMetaData().getValue(keyName);
  110 + if (!StringUtils.isEmpty(valueStr)) {
  111 + value = Double.parseDouble(valueStr);
  112 + } else {
  113 + throw new TbNodeException("Incoming Message has no " + keyName + " in data or metadata!");
  114 + }
  115 + }
  116 + return value;
  117 + }
  118 +
  119 +
  120 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class Coordinates {
  22 + private final double latitude;
  23 + private final double longitude;
  24 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import lombok.AllArgsConstructor;
  19 +import lombok.Data;
  20 +
  21 +@Data
  22 +@AllArgsConstructor
  23 +public class EntityGeofencingState {
  24 +
  25 + private boolean inside;
  26 + private long stateSwitchTime;
  27 + private boolean staid;
  28 +
  29 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import com.google.gson.JsonArray;
  19 +import com.google.gson.JsonElement;
  20 +import com.google.gson.JsonParser;
  21 +import org.locationtech.spatial4j.context.SpatialContext;
  22 +import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
  23 +import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
  24 +import org.locationtech.spatial4j.distance.DistanceUtils;
  25 +import org.locationtech.spatial4j.shape.Point;
  26 +import org.locationtech.spatial4j.shape.Shape;
  27 +import org.locationtech.spatial4j.shape.ShapeFactory;
  28 +import org.locationtech.spatial4j.shape.SpatialRelation;
  29 +
  30 +public class GeoUtil {
  31 +
  32 + private static final SpatialContext distCtx = SpatialContext.GEO;
  33 + private static final JtsSpatialContext jtsCtx;
  34 +
  35 + static {
  36 + JtsSpatialContextFactory factory = new JtsSpatialContextFactory();
  37 + factory.normWrapLongitude = true;
  38 + jtsCtx = factory.newSpatialContext();
  39 + }
  40 +
  41 + public static synchronized double distance(Coordinates x, Coordinates y, RangeUnit unit) {
  42 + Point xLL = distCtx.getShapeFactory().pointXY(x.getLongitude(), x.getLatitude());
  43 + Point yLL = distCtx.getShapeFactory().pointXY(y.getLongitude(), y.getLatitude());
  44 + return unit.fromKm(distCtx.getDistCalc().distance(xLL, yLL) * DistanceUtils.DEG_TO_KM);
  45 + }
  46 +
  47 + public static synchronized boolean contains(String polygon, Coordinates coordinates) {
  48 + ShapeFactory.PolygonBuilder polygonBuilder = jtsCtx.getShapeFactory().polygon();
  49 + JsonArray polygonArray = new JsonParser().parse(polygon).getAsJsonArray();
  50 + boolean first = true;
  51 + double firstLat = 0.0;
  52 + double firstLng = 0.0;
  53 + for (JsonElement jsonElement : polygonArray) {
  54 + double lat = jsonElement.getAsJsonArray().get(0).getAsDouble();
  55 + double lng = jsonElement.getAsJsonArray().get(1).getAsDouble();
  56 + if (first) {
  57 + firstLat = lat;
  58 + firstLng = lng;
  59 + first = false;
  60 + }
  61 + polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(lng), jtsCtx.getShapeFactory().normY(lat));
  62 + }
  63 + polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(firstLng), jtsCtx.getShapeFactory().normY(firstLat));
  64 + Shape shape = polygonBuilder.buildOrRect();
  65 + Point point = jtsCtx.makePoint(coordinates.getLongitude(), coordinates.getLatitude());
  66 + return shape.relate(point).equals(SpatialRelation.CONTAINS);
  67 + }
  68 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import lombok.Data;
  19 +
  20 +@Data
  21 +public class Perimeter {
  22 +
  23 + private PerimeterType perimeterType;
  24 +
  25 + //For Polygons
  26 + private String polygonsDefinition;
  27 +
  28 + //For Circles
  29 + private Double centerLatitude;
  30 + private Double centerLongitude;
  31 + private Double range;
  32 + private RangeUnit rangeUnit;
  33 +
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +public enum PerimeterType {
  19 + CIRCLE, POLYGON
  20 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +public enum RangeUnit {
  19 + METER(1000.0), KILOMETER(1.0), FOOT(3280.84), MILE(0.62137), NAUTICAL_MILE(0.539957);
  20 +
  21 + private final double fromKm;
  22 +
  23 + RangeUnit(double fromKm) {
  24 + this.fromKm = fromKm;
  25 + }
  26 +
  27 + public double fromKm(double v) {
  28 + return v * fromKm;
  29 + }
  30 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import com.google.gson.Gson;
  19 +import com.google.gson.JsonObject;
  20 +import com.google.gson.JsonParser;
  21 +import lombok.extern.slf4j.Slf4j;
  22 +import org.thingsboard.rule.engine.api.RuleNode;
  23 +import org.thingsboard.rule.engine.api.TbContext;
  24 +import org.thingsboard.rule.engine.api.TbNodeException;
  25 +import org.thingsboard.server.common.data.DataConstants;
  26 +import org.thingsboard.server.common.data.id.EntityId;
  27 +import org.thingsboard.server.common.data.kv.AttributeKvEntry;
  28 +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
  29 +import org.thingsboard.server.common.data.kv.StringDataEntry;
  30 +import org.thingsboard.server.common.data.plugin.ComponentType;
  31 +import org.thingsboard.server.common.msg.TbMsg;
  32 +
  33 +import java.util.Collections;
  34 +import java.util.HashMap;
  35 +import java.util.List;
  36 +import java.util.Map;
  37 +import java.util.Optional;
  38 +import java.util.concurrent.ExecutionException;
  39 +import java.util.concurrent.TimeUnit;
  40 +import java.util.concurrent.TimeoutException;
  41 +
  42 +/**
  43 + * Created by ashvayka on 19.01.18.
  44 + */
  45 +@Slf4j
  46 +@RuleNode(
  47 + type = ComponentType.ACTION,
  48 + name = "gps geofencing events",
  49 + configClazz = TbGpsGeofencingFilterNodeConfiguration.class,
  50 + relationTypes = {"Entered", "Left", "Stays Inside", "Stays Outside"},
  51 + nodeDescription = "Produces incoming messages using GPS based geofencing",
  52 + nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters")
  53 +public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofencingActionNodeConfiguration> {
  54 +
  55 + private final Map<EntityId, EntityGeofencingState> entityStates = new HashMap<>();
  56 + private final Gson gson = new Gson();
  57 + private final JsonParser parser = new JsonParser();
  58 +
  59 + @Override
  60 + public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
  61 + boolean matches = checkMatches(msg);
  62 + long ts = System.currentTimeMillis();
  63 +
  64 + EntityGeofencingState entityState = entityStates.computeIfAbsent(msg.getOriginator(), key -> {
  65 + try {
  66 + Optional<AttributeKvEntry> entry = ctx.getAttributesService()
  67 + .find(ctx.getTenantId(), msg.getOriginator(), DataConstants.SERVER_SCOPE, ctx.getNodeId())
  68 + .get(1, TimeUnit.MINUTES);
  69 + if (entry.isPresent()) {
  70 + JsonObject element = parser.parse(entry.get().getValueAsString()).getAsJsonObject();
  71 + return new EntityGeofencingState(element.get("inside").getAsBoolean(), element.get("stateSwitchTime").getAsLong(), element.get("staid").getAsBoolean());
  72 + } else {
  73 + return new EntityGeofencingState(false, 0L, false);
  74 + }
  75 + } catch (InterruptedException | TimeoutException | ExecutionException e) {
  76 + throw new RuntimeException(e);
  77 + }
  78 + });
  79 + if (entityState.getStateSwitchTime() == 0L || entityState.isInside() != matches) {
  80 + switchState(ctx, msg.getOriginator(), entityState, matches, ts);
  81 + ctx.tellNext(msg, matches ? "Entered" : "Left");
  82 + } else if (!entityState.isStaid()) {
  83 + long stayTime = ts - entityState.getStateSwitchTime();
  84 + if (stayTime > (entityState.isInside() ? config.getMinInsideDuration() : config.getMinOutsideDuration())) {
  85 + setStaid(ctx, msg.getOriginator(), entityState);
  86 + }
  87 + }
  88 + }
  89 +
  90 + private void switchState(TbContext ctx, EntityId entityId, EntityGeofencingState entityState, boolean matches, long ts) {
  91 + entityState.setInside(matches);
  92 + entityState.setStateSwitchTime(ts);
  93 + entityState.setStaid(false);
  94 + persist(ctx, entityId, entityState);
  95 + }
  96 +
  97 + private void setStaid(TbContext ctx, EntityId entityId, EntityGeofencingState entityState) {
  98 + entityState.setStaid(true);
  99 + persist(ctx, entityId, entityState);
  100 + }
  101 +
  102 + private void persist(TbContext ctx, EntityId entityId, EntityGeofencingState entityState) {
  103 + JsonObject object = new JsonObject();
  104 + object.addProperty("inside", entityState.isInside());
  105 + object.addProperty("stateSwitchTime", entityState.getStateSwitchTime());
  106 + object.addProperty("staid", entityState.isStaid());
  107 + AttributeKvEntry entry = new BaseAttributeKvEntry(new StringDataEntry(ctx.getNodeId(), gson.toJson(object)), System.currentTimeMillis());
  108 + List<AttributeKvEntry> attributeKvEntryList = Collections.singletonList(entry);
  109 + ctx.getAttributesService().save(ctx.getTenantId(), entityId, DataConstants.SERVER_SCOPE, attributeKvEntryList);
  110 + }
  111 +
  112 + @Override
  113 + public void destroy() {
  114 +
  115 + }
  116 +
  117 + @Override
  118 + protected Class<TbGpsGeofencingActionNodeConfiguration> getConfigClazz() {
  119 + return TbGpsGeofencingActionNodeConfiguration.class;
  120 + }
  121 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.rule.engine.api.NodeConfiguration;
  20 +
  21 +import java.util.Collections;
  22 +import java.util.List;
  23 +import java.util.concurrent.TimeUnit;
  24 +
  25 +/**
  26 + * Created by ashvayka on 19.01.18.
  27 + */
  28 +@Data
  29 +public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilterNodeConfiguration {
  30 +
  31 + private double minInsideDuration;
  32 + private double minOutsideDuration;
  33 +
  34 + @Override
  35 + public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() {
  36 + TbGpsGeofencingActionNodeConfiguration configuration = new TbGpsGeofencingActionNodeConfiguration();
  37 + configuration.setLatitudeKeyName("latitude");
  38 + configuration.setLongitudeKeyName("longitude");
  39 + configuration.setFetchPerimeterInfoFromMessage(true);
  40 + configuration.setPerimeters(Collections.emptyList());
  41 + configuration.setMinInsideDuration(TimeUnit.MINUTES.toMillis(1));
  42 + configuration.setMinOutsideDuration(TimeUnit.MINUTES.toMillis(1));
  43 + return configuration;
  44 + }
  45 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import com.google.gson.JsonArray;
  19 +import com.google.gson.JsonElement;
  20 +import com.google.gson.JsonObject;
  21 +import com.google.gson.JsonParser;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
  24 +import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
  25 +import org.locationtech.spatial4j.shape.Point;
  26 +import org.locationtech.spatial4j.shape.Shape;
  27 +import org.locationtech.spatial4j.shape.ShapeFactory;
  28 +import org.locationtech.spatial4j.shape.SpatialRelation;
  29 +import org.springframework.util.StringUtils;
  30 +import org.thingsboard.rule.engine.api.RuleNode;
  31 +import org.thingsboard.rule.engine.api.TbContext;
  32 +import org.thingsboard.rule.engine.api.TbNode;
  33 +import org.thingsboard.rule.engine.api.TbNodeConfiguration;
  34 +import org.thingsboard.rule.engine.api.TbNodeException;
  35 +import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  36 +import org.thingsboard.rule.engine.filter.TbMsgTypeFilterNodeConfiguration;
  37 +import org.thingsboard.server.common.data.plugin.ComponentType;
  38 +import org.thingsboard.server.common.msg.TbMsg;
  39 +
  40 +import java.util.Collections;
  41 +import java.util.List;
  42 +
  43 +/**
  44 + * Created by ashvayka on 19.01.18.
  45 + */
  46 +@Slf4j
  47 +@RuleNode(
  48 + type = ComponentType.FILTER,
  49 + name = "gps geofencing filter",
  50 + configClazz = TbGpsGeofencingFilterNodeConfiguration.class,
  51 + relationTypes = {"True", "False"},
  52 + nodeDescription = "Filter incoming messages by GPS based geofencing",
  53 + nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns 'True' if they are inside configured perimeters, 'False' otherwise.")
  54 +public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode<TbGpsGeofencingFilterNodeConfiguration> {
  55 +
  56 + @Override
  57 + public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
  58 + boolean matches = checkMatches(msg);
  59 + ctx.tellNext(msg, matches ? "True" : "False");
  60 + }
  61 +
  62 + @Override
  63 + public void destroy() {
  64 +
  65 + }
  66 +
  67 + @Override
  68 + protected Class<TbGpsGeofencingFilterNodeConfiguration> getConfigClazz() {
  69 + return TbGpsGeofencingFilterNodeConfiguration.class;
  70 + }
  71 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2019 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.rule.engine.geo;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.rule.engine.api.NodeConfiguration;
  20 +import org.thingsboard.server.common.data.EntityType;
  21 +import org.thingsboard.server.common.msg.session.SessionMsgType;
  22 +
  23 +import java.util.Arrays;
  24 +import java.util.Collections;
  25 +import java.util.List;
  26 +
  27 +/**
  28 + * Created by ashvayka on 19.01.18.
  29 + */
  30 +@Data
  31 +public class TbGpsGeofencingFilterNodeConfiguration implements NodeConfiguration<TbGpsGeofencingFilterNodeConfiguration> {
  32 +
  33 + private String latitudeKeyName;
  34 + private String longitudeKeyName;
  35 + private boolean fetchPerimeterInfoFromMessage;
  36 + private List<Perimeter> perimeters;
  37 +
  38 + @Override
  39 + public TbGpsGeofencingFilterNodeConfiguration defaultConfiguration() {
  40 + TbGpsGeofencingFilterNodeConfiguration configuration = new TbGpsGeofencingFilterNodeConfiguration();
  41 + configuration.setLatitudeKeyName("latitude");
  42 + configuration.setLongitudeKeyName("longitude");
  43 + configuration.setFetchPerimeterInfoFromMessage(true);
  44 + configuration.setPerimeters(Collections.emptyList());
  45 + return configuration;
  46 + }
  47 +}
... ...