Initial rule nodes commit
This commit is contained in:
parent
f41cd97cbb
commit
876b9eeda4
12
pom.xml
12
pom.xml
@ -71,6 +71,8 @@
|
||||
<jar-plugin.version>3.0.2</jar-plugin.version>
|
||||
<springfox-swagger.version>2.6.1</springfox-swagger.version>
|
||||
<springfox-swagger-ui-rfc6570.version>1.0.0</springfox-swagger-ui-rfc6570.version>
|
||||
<spatial4j.version>0.7</spatial4j.version>
|
||||
<jts.version>1.15.0</jts.version>
|
||||
<bouncycastle.version>1.56</bouncycastle.version>
|
||||
<winsw.version>2.0.1</winsw.version>
|
||||
<hsqldb.version>2.4.0</hsqldb.version>
|
||||
@ -828,6 +830,16 @@
|
||||
<artifactId>springfox-swagger-ui-rfc6570</artifactId>
|
||||
<version>${springfox-swagger-ui-rfc6570.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.locationtech.spatial4j</groupId>
|
||||
<artifactId>spatial4j</artifactId>
|
||||
<version>${spatial4j.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.locationtech.jts</groupId>
|
||||
<artifactId>jts-core</artifactId>
|
||||
<version>${jts.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
|
||||
@ -97,6 +97,14 @@
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.locationtech.spatial4j</groupId>
|
||||
<artifactId>spatial4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.locationtech.jts</groupId>
|
||||
<artifactId>jts-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
@ -142,10 +150,10 @@
|
||||
<executable>true</executable>
|
||||
<excludeDevtools>true</excludeDevtools>
|
||||
<!--<embeddedLaunchScriptProperties>-->
|
||||
<!--<confFolder>${pkg.installFolder}/conf</confFolder>-->
|
||||
<!--<logFolder>${pkg.unixLogFolder}</logFolder>-->
|
||||
<!--<logFilename>${pkg.name}.out</logFilename>-->
|
||||
<!--<initInfoProvides>${pkg.name}</initInfoProvides>-->
|
||||
<!--<confFolder>${pkg.installFolder}/conf</confFolder>-->
|
||||
<!--<logFolder>${pkg.unixLogFolder}</logFolder>-->
|
||||
<!--<logFilename>${pkg.name}.out</logFilename>-->
|
||||
<!--<initInfoProvides>${pkg.name}</initInfoProvides>-->
|
||||
<!--</embeddedLaunchScriptProperties>-->
|
||||
</configuration>
|
||||
<executions>
|
||||
|
||||
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.thingsboard.rule.engine.api.TbContext;
|
||||
import org.thingsboard.rule.engine.api.TbNode;
|
||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractGeofencingNode<T extends TbGpsGeofencingFilterNodeConfiguration> implements TbNode {
|
||||
|
||||
protected T config;
|
||||
protected JtsSpatialContext jtsCtx;
|
||||
|
||||
@Override
|
||||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
|
||||
this.config = TbNodeUtils.convert(configuration, getConfigClazz());
|
||||
JtsSpatialContextFactory factory = new JtsSpatialContextFactory();
|
||||
factory.normWrapLongitude = true;
|
||||
jtsCtx = factory.newSpatialContext();
|
||||
}
|
||||
|
||||
abstract protected Class<T> getConfigClazz();
|
||||
|
||||
protected boolean checkMatches(TbMsg msg) throws TbNodeException {
|
||||
JsonElement msgDataElement = new JsonParser().parse(msg.getData());
|
||||
if (!msgDataElement.isJsonObject()) {
|
||||
throw new TbNodeException("Incoming Message is not a valid JSON object");
|
||||
}
|
||||
JsonObject msgDataObj = msgDataElement.getAsJsonObject();
|
||||
double latitude = getValueFromMessageByName(msg, msgDataObj, config.getLatitudeKeyName());
|
||||
double longitude = getValueFromMessageByName(msg, msgDataObj, config.getLongitudeKeyName());
|
||||
List<Perimeter> perimeters = getPerimeters(msg, msgDataObj);
|
||||
boolean matches = false;
|
||||
for (Perimeter perimeter : perimeters) {
|
||||
if (checkMatches(perimeter, latitude, longitude)) {
|
||||
matches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
protected boolean checkMatches(Perimeter perimeter, double latitude, double longitude) throws TbNodeException {
|
||||
if (perimeter.getPerimeterType() == PerimeterType.CIRCLE) {
|
||||
Coordinates entityCoordinates = new Coordinates(latitude, longitude);
|
||||
Coordinates perimeterCoordinates = new Coordinates(perimeter.getCenterLatitude(), perimeter.getCenterLongitude());
|
||||
return perimeter.getRange() > GeoUtil.distance(entityCoordinates, perimeterCoordinates, perimeter.getRangeUnit());
|
||||
} else if (perimeter.getPerimeterType() == PerimeterType.POLYGON) {
|
||||
return GeoUtil.contains(perimeter.getPolygonsDefinition(), new Coordinates(latitude, longitude));
|
||||
} else {
|
||||
throw new TbNodeException("Unsupported perimeter type: " + perimeter.getPerimeterType());
|
||||
}
|
||||
}
|
||||
|
||||
protected List<Perimeter> getPerimeters(TbMsg msg, JsonObject msgDataObj) throws TbNodeException {
|
||||
if (config.isFetchPerimeterInfoFromMessage()) {
|
||||
//TODO: add fetching perimeters from the message itself, if configuration is empty.
|
||||
if (!StringUtils.isEmpty(msg.getMetaData().getValue("perimeter"))) {
|
||||
Perimeter perimeter = new Perimeter();
|
||||
perimeter.setPerimeterType(PerimeterType.POLYGON);
|
||||
perimeter.setPolygonsDefinition(msg.getMetaData().getValue("perimeter"));
|
||||
return Collections.singletonList(perimeter);
|
||||
} else if (!StringUtils.isEmpty(msg.getMetaData().getValue("centerLatitude"))) {
|
||||
Perimeter perimeter = new Perimeter();
|
||||
perimeter.setPerimeterType(PerimeterType.CIRCLE);
|
||||
perimeter.setCenterLatitude(Double.parseDouble(msg.getMetaData().getValue("centerLatitude")));
|
||||
perimeter.setCenterLongitude(Double.parseDouble(msg.getMetaData().getValue("centerLongitude")));
|
||||
perimeter.setRange(Double.parseDouble(msg.getMetaData().getValue("range")));
|
||||
perimeter.setRangeUnit(RangeUnit.valueOf(msg.getMetaData().getValue("rangeUnit")));
|
||||
return Collections.singletonList(perimeter);
|
||||
} else {
|
||||
throw new TbNodeException("Missing perimeter definition!");
|
||||
}
|
||||
} else {
|
||||
return config.getPerimeters();
|
||||
}
|
||||
}
|
||||
|
||||
protected Double getValueFromMessageByName(TbMsg msg, JsonObject msgDataObj, String keyName) throws TbNodeException {
|
||||
double value;
|
||||
if (msgDataObj.has(keyName) && msgDataObj.get(keyName).isJsonPrimitive()) {
|
||||
value = msgDataObj.get(keyName).getAsDouble();
|
||||
} else {
|
||||
String valueStr = msg.getMetaData().getValue(keyName);
|
||||
if (!StringUtils.isEmpty(valueStr)) {
|
||||
value = Double.parseDouble(valueStr);
|
||||
} else {
|
||||
throw new TbNodeException("Incoming Message has no " + keyName + " in data or metadata!");
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Coordinates {
|
||||
private final double latitude;
|
||||
private final double longitude;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public class EntityGeofencingState {
|
||||
|
||||
private boolean inside;
|
||||
private long stateSwitchTime;
|
||||
private boolean staid;
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import org.locationtech.spatial4j.context.SpatialContext;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
|
||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.ShapeFactory;
|
||||
import org.locationtech.spatial4j.shape.SpatialRelation;
|
||||
|
||||
public class GeoUtil {
|
||||
|
||||
private static final SpatialContext distCtx = SpatialContext.GEO;
|
||||
private static final JtsSpatialContext jtsCtx;
|
||||
|
||||
static {
|
||||
JtsSpatialContextFactory factory = new JtsSpatialContextFactory();
|
||||
factory.normWrapLongitude = true;
|
||||
jtsCtx = factory.newSpatialContext();
|
||||
}
|
||||
|
||||
public static synchronized double distance(Coordinates x, Coordinates y, RangeUnit unit) {
|
||||
Point xLL = distCtx.getShapeFactory().pointXY(x.getLongitude(), x.getLatitude());
|
||||
Point yLL = distCtx.getShapeFactory().pointXY(y.getLongitude(), y.getLatitude());
|
||||
return unit.fromKm(distCtx.getDistCalc().distance(xLL, yLL) * DistanceUtils.DEG_TO_KM);
|
||||
}
|
||||
|
||||
public static synchronized boolean contains(String polygon, Coordinates coordinates) {
|
||||
ShapeFactory.PolygonBuilder polygonBuilder = jtsCtx.getShapeFactory().polygon();
|
||||
JsonArray polygonArray = new JsonParser().parse(polygon).getAsJsonArray();
|
||||
boolean first = true;
|
||||
double firstLat = 0.0;
|
||||
double firstLng = 0.0;
|
||||
for (JsonElement jsonElement : polygonArray) {
|
||||
double lat = jsonElement.getAsJsonArray().get(0).getAsDouble();
|
||||
double lng = jsonElement.getAsJsonArray().get(1).getAsDouble();
|
||||
if (first) {
|
||||
firstLat = lat;
|
||||
firstLng = lng;
|
||||
first = false;
|
||||
}
|
||||
polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(lng), jtsCtx.getShapeFactory().normY(lat));
|
||||
}
|
||||
polygonBuilder.pointXY(jtsCtx.getShapeFactory().normX(firstLng), jtsCtx.getShapeFactory().normY(firstLat));
|
||||
Shape shape = polygonBuilder.buildOrRect();
|
||||
Point point = jtsCtx.makePoint(coordinates.getLongitude(), coordinates.getLatitude());
|
||||
return shape.relate(point).equals(SpatialRelation.CONTAINS);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Perimeter {
|
||||
|
||||
private PerimeterType perimeterType;
|
||||
|
||||
//For Polygons
|
||||
private String polygonsDefinition;
|
||||
|
||||
//For Circles
|
||||
private Double centerLatitude;
|
||||
private Double centerLongitude;
|
||||
private Double range;
|
||||
private RangeUnit rangeUnit;
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
public enum PerimeterType {
|
||||
CIRCLE, POLYGON
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
public enum RangeUnit {
|
||||
METER(1000.0), KILOMETER(1.0), FOOT(3280.84), MILE(0.62137), NAUTICAL_MILE(0.539957);
|
||||
|
||||
private final double fromKm;
|
||||
|
||||
RangeUnit(double fromKm) {
|
||||
this.fromKm = fromKm;
|
||||
}
|
||||
|
||||
public double fromKm(double v) {
|
||||
return v * fromKm;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.rule.engine.api.RuleNode;
|
||||
import org.thingsboard.rule.engine.api.TbContext;
|
||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||
import org.thingsboard.server.common.data.DataConstants;
|
||||
import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
|
||||
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
|
||||
import org.thingsboard.server.common.data.kv.StringDataEntry;
|
||||
import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* Created by ashvayka on 19.01.18.
|
||||
*/
|
||||
@Slf4j
|
||||
@RuleNode(
|
||||
type = ComponentType.ACTION,
|
||||
name = "gps geofencing events",
|
||||
configClazz = TbGpsGeofencingFilterNodeConfiguration.class,
|
||||
relationTypes = {"Entered", "Left", "Stays Inside", "Stays Outside"},
|
||||
nodeDescription = "Produces incoming messages using GPS based geofencing",
|
||||
nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns different events based on configuration parameters")
|
||||
public class TbGpsGeofencingActionNode extends AbstractGeofencingNode<TbGpsGeofencingActionNodeConfiguration> {
|
||||
|
||||
private final Map<EntityId, EntityGeofencingState> entityStates = new HashMap<>();
|
||||
private final Gson gson = new Gson();
|
||||
private final JsonParser parser = new JsonParser();
|
||||
|
||||
@Override
|
||||
public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
|
||||
boolean matches = checkMatches(msg);
|
||||
long ts = System.currentTimeMillis();
|
||||
|
||||
EntityGeofencingState entityState = entityStates.computeIfAbsent(msg.getOriginator(), key -> {
|
||||
try {
|
||||
Optional<AttributeKvEntry> entry = ctx.getAttributesService()
|
||||
.find(ctx.getTenantId(), msg.getOriginator(), DataConstants.SERVER_SCOPE, ctx.getNodeId())
|
||||
.get(1, TimeUnit.MINUTES);
|
||||
if (entry.isPresent()) {
|
||||
JsonObject element = parser.parse(entry.get().getValueAsString()).getAsJsonObject();
|
||||
return new EntityGeofencingState(element.get("inside").getAsBoolean(), element.get("stateSwitchTime").getAsLong(), element.get("staid").getAsBoolean());
|
||||
} else {
|
||||
return new EntityGeofencingState(false, 0L, false);
|
||||
}
|
||||
} catch (InterruptedException | TimeoutException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
if (entityState.getStateSwitchTime() == 0L || entityState.isInside() != matches) {
|
||||
switchState(ctx, msg.getOriginator(), entityState, matches, ts);
|
||||
ctx.tellNext(msg, matches ? "Entered" : "Left");
|
||||
} else if (!entityState.isStaid()) {
|
||||
long stayTime = ts - entityState.getStateSwitchTime();
|
||||
if (stayTime > (entityState.isInside() ? config.getMinInsideDuration() : config.getMinOutsideDuration())) {
|
||||
setStaid(ctx, msg.getOriginator(), entityState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void switchState(TbContext ctx, EntityId entityId, EntityGeofencingState entityState, boolean matches, long ts) {
|
||||
entityState.setInside(matches);
|
||||
entityState.setStateSwitchTime(ts);
|
||||
entityState.setStaid(false);
|
||||
persist(ctx, entityId, entityState);
|
||||
}
|
||||
|
||||
private void setStaid(TbContext ctx, EntityId entityId, EntityGeofencingState entityState) {
|
||||
entityState.setStaid(true);
|
||||
persist(ctx, entityId, entityState);
|
||||
}
|
||||
|
||||
private void persist(TbContext ctx, EntityId entityId, EntityGeofencingState entityState) {
|
||||
JsonObject object = new JsonObject();
|
||||
object.addProperty("inside", entityState.isInside());
|
||||
object.addProperty("stateSwitchTime", entityState.getStateSwitchTime());
|
||||
object.addProperty("staid", entityState.isStaid());
|
||||
AttributeKvEntry entry = new BaseAttributeKvEntry(new StringDataEntry(ctx.getNodeId(), gson.toJson(object)), System.currentTimeMillis());
|
||||
List<AttributeKvEntry> attributeKvEntryList = Collections.singletonList(entry);
|
||||
ctx.getAttributesService().save(ctx.getTenantId(), entityId, DataConstants.SERVER_SCOPE, attributeKvEntryList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<TbGpsGeofencingActionNodeConfiguration> getConfigClazz() {
|
||||
return TbGpsGeofencingActionNodeConfiguration.class;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.thingsboard.rule.engine.api.NodeConfiguration;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by ashvayka on 19.01.18.
|
||||
*/
|
||||
@Data
|
||||
public class TbGpsGeofencingActionNodeConfiguration extends TbGpsGeofencingFilterNodeConfiguration {
|
||||
|
||||
private double minInsideDuration;
|
||||
private double minOutsideDuration;
|
||||
|
||||
@Override
|
||||
public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() {
|
||||
TbGpsGeofencingActionNodeConfiguration configuration = new TbGpsGeofencingActionNodeConfiguration();
|
||||
configuration.setLatitudeKeyName("latitude");
|
||||
configuration.setLongitudeKeyName("longitude");
|
||||
configuration.setFetchPerimeterInfoFromMessage(true);
|
||||
configuration.setPerimeters(Collections.emptyList());
|
||||
configuration.setMinInsideDuration(TimeUnit.MINUTES.toMillis(1));
|
||||
configuration.setMinOutsideDuration(TimeUnit.MINUTES.toMillis(1));
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContext;
|
||||
import org.locationtech.spatial4j.context.jts.JtsSpatialContextFactory;
|
||||
import org.locationtech.spatial4j.shape.Point;
|
||||
import org.locationtech.spatial4j.shape.Shape;
|
||||
import org.locationtech.spatial4j.shape.ShapeFactory;
|
||||
import org.locationtech.spatial4j.shape.SpatialRelation;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.thingsboard.rule.engine.api.RuleNode;
|
||||
import org.thingsboard.rule.engine.api.TbContext;
|
||||
import org.thingsboard.rule.engine.api.TbNode;
|
||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
|
||||
import org.thingsboard.rule.engine.filter.TbMsgTypeFilterNodeConfiguration;
|
||||
import org.thingsboard.server.common.data.plugin.ComponentType;
|
||||
import org.thingsboard.server.common.msg.TbMsg;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by ashvayka on 19.01.18.
|
||||
*/
|
||||
@Slf4j
|
||||
@RuleNode(
|
||||
type = ComponentType.FILTER,
|
||||
name = "gps geofencing filter",
|
||||
configClazz = TbGpsGeofencingFilterNodeConfiguration.class,
|
||||
relationTypes = {"True", "False"},
|
||||
nodeDescription = "Filter incoming messages by GPS based geofencing",
|
||||
nodeDetails = "Extracts latitude and longitude parameters from incoming message and returns 'True' if they are inside configured perimeters, 'False' otherwise.")
|
||||
public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode<TbGpsGeofencingFilterNodeConfiguration> {
|
||||
|
||||
@Override
|
||||
public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException {
|
||||
boolean matches = checkMatches(msg);
|
||||
ctx.tellNext(msg, matches ? "True" : "False");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<TbGpsGeofencingFilterNodeConfiguration> getConfigClazz() {
|
||||
return TbGpsGeofencingFilterNodeConfiguration.class;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright © 2016-2019 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.rule.engine.geo;
|
||||
|
||||
import lombok.Data;
|
||||
import org.thingsboard.rule.engine.api.NodeConfiguration;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.msg.session.SessionMsgType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by ashvayka on 19.01.18.
|
||||
*/
|
||||
@Data
|
||||
public class TbGpsGeofencingFilterNodeConfiguration implements NodeConfiguration<TbGpsGeofencingFilterNodeConfiguration> {
|
||||
|
||||
private String latitudeKeyName;
|
||||
private String longitudeKeyName;
|
||||
private boolean fetchPerimeterInfoFromMessage;
|
||||
private List<Perimeter> perimeters;
|
||||
|
||||
@Override
|
||||
public TbGpsGeofencingFilterNodeConfiguration defaultConfiguration() {
|
||||
TbGpsGeofencingFilterNodeConfiguration configuration = new TbGpsGeofencingFilterNodeConfiguration();
|
||||
configuration.setLatitudeKeyName("latitude");
|
||||
configuration.setLongitudeKeyName("longitude");
|
||||
configuration.setFetchPerimeterInfoFromMessage(true);
|
||||
configuration.setPerimeters(Collections.emptyList());
|
||||
return configuration;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user