Browse Source
# Conflicts: # application/src/main/data/json/system/widget_bundles/maps.jsonpull/1610/head
162 changed files with 9356 additions and 267 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,25 @@ |
|||
{ |
|||
"widgetsBundle": { |
|||
"alias": "date", |
|||
"title": "Date", |
|||
"image": null |
|||
}, |
|||
"widgetTypes": [ |
|||
{ |
|||
"alias": "date_range_navigator", |
|||
"name": "Date-range-navigator", |
|||
"descriptor": { |
|||
"type": "static", |
|||
"sizeX": 5, |
|||
"sizeY": 5.5, |
|||
"resources": [], |
|||
"templateHtml": "<date-range-navigator-widget class=\"date-range-navigator-widget\" ctx=\"ctx\"></date-range-navigator-widget>", |
|||
"templateCss": "", |
|||
"controllerScript": "self.onInit = function() {\n scope = self.ctx.$scope;\n scope.ctx = self.ctx;\n}", |
|||
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"Settings\",\n \"properties\": {\n \"hidePicker\": {\n \"title\": \"Hide date range picker\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"onePanel\": {\n \"title\": \"Date range picker one panel\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"autoConfirm\": {\n \"title\": \"Date range picker auto confirm\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"showTemplate\": {\n \"title\": \"Date range picker show template\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"firstDayOfWeek\": {\n \"title\": \"First day of the week\",\n \"type\": \"number\",\n \"default\": 1\n },\n \"hideInterval\": {\n \"title\": \"Hide interval\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"initialInterval\": {\n\t\t\t\t\"title\": \"Initial interval\",\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"default\": \"week\"\n\t\t\t},\n \"hideStepSize\": {\n \"title\": \"Hide step size\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"stepSize\": {\n\t\t\t\t\"title\": \"Initial step size\",\n\t\t\t\t\"type\": \"string\",\n\t\t\t\t\"default\": \"day\"\n\t\t\t},\n \"hideLabels\": {\n \"title\": \"Hide labels\",\n \"type\": \"boolean\",\n \"default\": false\n },\n \"useSessionStorage\": {\n \"title\": \"Use session storage\",\n \"type\": \"boolean\",\n \"default\": true\n }\n }\n },\n \"form\": [\n \"hidePicker\",\n\t\t\"onePanel\",\n\t\t\"autoConfirm\",\n\t\t\"showTemplate\",\n\t\t\"firstDayOfWeek\",\n \"hideInterval\",\n {\n\t\t\t\"key\": \"initialInterval\",\n\t\t\t\"type\": \"rc-select\",\n\t\t\t\"multiple\": false,\n\t\t\t\"items\": [\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"hour\",\n\t\t\t\t\t\"label\": \"Hour\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"day\",\n\t\t\t\t\t\"label\": \"Day\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"week\",\n\t\t\t\t\t\"label\": \"Week\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"twoWeeks\",\n\t\t\t\t\t\"label\": \"2 weeks\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"month\",\n\t\t\t\t\t\"label\": \"Month\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"threeMonths\",\n\t\t\t\t\t\"label\": \"3 months\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"sixMonths\",\n\t\t\t\t\t\"label\": \"6 months\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n \"hideStepSize\",\n {\n\t\t\t\"key\": \"stepSize\",\n\t\t\t\"type\": \"rc-select\",\n\t\t\t\"multiple\": false,\n\t\t\t\"items\": [\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"hour\",\n\t\t\t\t\t\"label\": \"Hour\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"day\",\n\t\t\t\t\t\"label\": \"Day\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"week\",\n\t\t\t\t\t\"label\": \"Week\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"twoWeeks\",\n\t\t\t\t\t\"label\": \"2 weeks\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"month\",\n\t\t\t\t\t\"label\": \"Month\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"threeMonths\",\n\t\t\t\t\t\"label\": \"3 months\"\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"value\": \"sixMonths\",\n\t\t\t\t\t\"label\": \"6 months\"\n\t\t\t\t}\n\t\t\t]\n\t\t},\n\t\t\"hideLabels\",\n\t\t\"useSessionStorage\"\n ]\n}", |
|||
"dataKeySettingsSchema": "{}\n", |
|||
"defaultConfig": "{\"datasources\":[{\"type\":\"static\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Random\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.15479322438769105,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"defaultInterval\":\"week\",\"stepSize\":\"day\"},\"title\":\"Date-range-navigator\",\"dropShadow\":true,\"enableFullscreen\":true,\"widgetStyle\":{},\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"useDashboardTimewindow\":true,\"showLegend\":false,\"actions\":{}}" |
|||
} |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/** |
|||
* 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.server.config; |
|||
|
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.scheduling.TaskScheduler; |
|||
import org.springframework.scheduling.annotation.EnableScheduling; |
|||
import org.springframework.scheduling.annotation.SchedulingConfigurer; |
|||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; |
|||
import org.springframework.scheduling.config.ScheduledTaskRegistrar; |
|||
|
|||
import java.util.concurrent.Executor; |
|||
import java.util.concurrent.Executors; |
|||
|
|||
@Configuration |
|||
@EnableScheduling |
|||
public class SchedulingConfiguration implements SchedulingConfigurer { |
|||
|
|||
@Override |
|||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { |
|||
taskRegistrar.setScheduler(taskScheduler()); |
|||
} |
|||
|
|||
@Bean(destroyMethod="shutdown") |
|||
public TaskScheduler taskScheduler() { |
|||
ThreadPoolTaskScheduler threadPoolScheduler = new ThreadPoolTaskScheduler(); |
|||
threadPoolScheduler.setThreadNamePrefix("TB-Scheduling-"); |
|||
threadPoolScheduler.setPoolSize(Runtime.getRuntime().availableProcessors()); |
|||
threadPoolScheduler.setRemoveOnCancelPolicy(true); |
|||
return threadPoolScheduler; |
|||
} |
|||
} |
|||
@ -0,0 +1,5 @@ |
|||
|
|||
# Database used by ThingsBoard, can be either postgres (PostgreSQL) or cassandra (Cassandra). |
|||
# According to the database type corresponding kubernetes resources will be deployed (see postgres.yml, cassandra.yml for details). |
|||
|
|||
DATABASE=postgres |
|||
@ -0,0 +1,100 @@ |
|||
# Kubernetes resources configuration for ThingsBoard Microservices |
|||
|
|||
This folder containing scripts and Kubernetes resources configurations to run ThingsBoard in Microservices mode. |
|||
|
|||
## Prerequisites |
|||
|
|||
ThingsBoard Microservices are running on Kubernetes cluster. |
|||
You need to have a Kubernetes cluster, and the kubectl command-line tool must be configured to communicate with your cluster. |
|||
If you do not already have a cluster, you can create one by using [Minikube](https://kubernetes.io/docs/setup/minikube), |
|||
or you can choose any other available [Kubernetes cluster deployment solutions](https://kubernetes.io/docs/setup/pick-right-solution/). |
|||
|
|||
## Installation |
|||
|
|||
Before performing initial installation you can configure the type of database to be used with ThingsBoard. |
|||
In order to set database type change the value of `DATABASE` variable in `.env` file to one of the following: |
|||
|
|||
- `postgres` - use PostgreSQL database; |
|||
- `cassandra` - use Cassandra database; |
|||
|
|||
**NOTE**: According to the database type corresponding kubernetes resources will be deployed (see `postgres.yml`, `cassandra.yml` for details). |
|||
|
|||
Execute the following command to run installation: |
|||
|
|||
` |
|||
$ ./k8s-install-tb.sh --loadDemo |
|||
` |
|||
|
|||
Where: |
|||
|
|||
- `--loadDemo` - optional argument. Whether to load additional demo data. |
|||
|
|||
## Running |
|||
|
|||
Execute the following command to deploy resources: |
|||
|
|||
` |
|||
$ ./k8s-deploy-resources.sh |
|||
` |
|||
|
|||
After a while when all resources will be successfully started you can open `http://{your-cluster-ip}` in you browser (for ex. `http://192.168.99.101`). |
|||
You should see ThingsBoard login page. |
|||
|
|||
Use the following default credentials: |
|||
|
|||
- **System Administrator**: sysadmin@thingsboard.org / sysadmin |
|||
|
|||
If you installed DataBase with demo data (using `--loadDemo` flag) you can also use the following credentials: |
|||
|
|||
- **Tenant Administrator**: tenant@thingsboard.org / tenant |
|||
- **Customer User**: customer@thingsboard.org / customer |
|||
|
|||
In case of any issues you can examine service logs for errors. |
|||
For example to see ThingsBoard node logs execute the following commands: |
|||
|
|||
1) Get list of the running tb-node pods: |
|||
|
|||
` |
|||
$ kubectl get pods -l app=tb-node |
|||
` |
|||
|
|||
2) Fetch logs of tb-node pod: |
|||
|
|||
` |
|||
$ kubectl logs -f [tb-node-pod-name] |
|||
` |
|||
|
|||
Where: |
|||
|
|||
- `tb-node-pod-name` - tb-node pod name obtained from the list of the running tb-node pods. |
|||
|
|||
Or use `kubectl get pods` to see the state of all the pods. |
|||
Or use `kubectl get services` to see the state of all the services. |
|||
Or use `kubectl get deployments` to see the state of all the deployments. |
|||
See [kubectl Cheat Sheet](https://kubernetes.io/docs/reference/kubectl/cheatsheet/) command reference for details. |
|||
|
|||
Execute the following command to delete all deployed microservices: |
|||
|
|||
` |
|||
$ ./k8s-delete-resources.sh |
|||
` |
|||
|
|||
Execute the following command to delete all resources (including database): |
|||
|
|||
` |
|||
$ ./k8s-delete-all.sh |
|||
` |
|||
|
|||
## Upgrading |
|||
|
|||
In case when database upgrade is needed, execute the following commands: |
|||
|
|||
``` |
|||
$ ./k8s-delete-resources.sh |
|||
$ ./k8s-upgrade-tb.sh --fromVersion=[FROM_VERSION] |
|||
$ ./k8s-deploy-resources.sh |
|||
``` |
|||
|
|||
Where: |
|||
|
|||
- `FROM_VERSION` - from which version upgrade should be started. See [Upgrade Instructions](https://thingsboard.io/docs/user-guide/install/upgrade-instructions) for valid `fromVersion` values. |
|||
@ -0,0 +1,164 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: storage.k8s.io/v1 |
|||
kind: StorageClass |
|||
metadata: |
|||
name: fast |
|||
namespace: thingsboard |
|||
provisioner: k8s.io/minikube-hostpath |
|||
parameters: |
|||
type: pd-ssd |
|||
--- |
|||
apiVersion: v1 |
|||
kind: ConfigMap |
|||
metadata: |
|||
name: cassandra-probe-config |
|||
namespace: thingsboard |
|||
labels: |
|||
name: cassandra-probe-config |
|||
data: |
|||
probe: | |
|||
if [[ $(nodetool status | grep $POD_IP) == *"UN"* ]]; then |
|||
if [[ $DEBUG ]]; then |
|||
echo "UN"; |
|||
fi |
|||
exit 0; |
|||
else |
|||
if [[ $DEBUG ]]; then |
|||
echo "Not Up"; |
|||
fi |
|||
exit 1; |
|||
fi |
|||
--- |
|||
apiVersion: apps/v1 |
|||
kind: StatefulSet |
|||
metadata: |
|||
name: cassandra |
|||
namespace: thingsboard |
|||
labels: |
|||
app: cassandra |
|||
spec: |
|||
serviceName: cassandra |
|||
replicas: 1 |
|||
selector: |
|||
matchLabels: |
|||
app: cassandra |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: cassandra |
|||
spec: |
|||
volumes: |
|||
- name: cassandra-probe-config |
|||
configMap: |
|||
name: cassandra-probe-config |
|||
items: |
|||
- key: probe |
|||
path: ready-probe.sh |
|||
mode: 0777 |
|||
terminationGracePeriodSeconds: 1800 |
|||
containers: |
|||
- name: cassandra |
|||
image: cassandra:3.11.3 |
|||
imagePullPolicy: Always |
|||
ports: |
|||
- containerPort: 7000 |
|||
name: intra-node |
|||
- containerPort: 7001 |
|||
name: tls-intra-node |
|||
- containerPort: 7199 |
|||
name: jmx |
|||
- containerPort: 9042 |
|||
name: cql |
|||
- containerPort: 9160 |
|||
name: thrift |
|||
resources: |
|||
limits: |
|||
cpu: "1000m" |
|||
memory: 2Gi |
|||
requests: |
|||
cpu: "1000m" |
|||
memory: 2Gi |
|||
securityContext: |
|||
capabilities: |
|||
add: |
|||
- IPC_LOCK |
|||
lifecycle: |
|||
preStop: |
|||
exec: |
|||
command: |
|||
- /bin/sh |
|||
- -c |
|||
- nodetool drain |
|||
env: |
|||
- name: CASSANDRA_SEEDS |
|||
value: "cassandra-0.cassandra.thingsboard.svc.cluster.local" |
|||
- name: MAX_HEAP_SIZE |
|||
value: 1024M |
|||
- name: HEAP_NEWSIZE |
|||
value: 256M |
|||
- name: CASSANDRA_CLUSTER_NAME |
|||
value: "Thingsboard Cluster" |
|||
- name: CASSANDRA_DC |
|||
value: "DC1-Thingsboard-Cluster" |
|||
- name: CASSANDRA_RACK |
|||
value: "Rack-Thingsboard-Cluster" |
|||
- name: CASSANDRA_AUTO_BOOTSTRAP |
|||
value: "false" |
|||
- name: CASSANDRA_ENDPOINT_SNITCH |
|||
value: GossipingPropertyFileSnitch |
|||
- name: POD_IP |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: status.podIP |
|||
readinessProbe: |
|||
exec: |
|||
command: |
|||
- /bin/bash |
|||
- -c |
|||
- /probe/ready-probe.sh |
|||
initialDelaySeconds: 60 |
|||
timeoutSeconds: 5 |
|||
volumeMounts: |
|||
- name: cassandra-probe-config |
|||
mountPath: /probe |
|||
- name: cassandra-data |
|||
mountPath: /var/lib/cassandra |
|||
volumeClaimTemplates: |
|||
- metadata: |
|||
name: cassandra-data |
|||
spec: |
|||
accessModes: [ "ReadWriteOnce" ] |
|||
storageClassName: fast |
|||
resources: |
|||
requests: |
|||
storage: 1Gi |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
labels: |
|||
app: cassandra |
|||
name: cassandra |
|||
namespace: thingsboard |
|||
spec: |
|||
clusterIP: None |
|||
ports: |
|||
- port: 9042 |
|||
selector: |
|||
app: cassandra |
|||
--- |
|||
@ -0,0 +1,43 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: Pod |
|||
metadata: |
|||
name: tb-db-setup |
|||
namespace: thingsboard |
|||
spec: |
|||
volumes: |
|||
- name: tb-node-config |
|||
configMap: |
|||
name: tb-node-config |
|||
items: |
|||
- key: conf |
|||
path: thingsboard.conf |
|||
- key: logback |
|||
path: logback.xml |
|||
containers: |
|||
- name: tb-db-setup |
|||
imagePullPolicy: Always |
|||
image: thingsboard/tb-node:latest |
|||
envFrom: |
|||
- configMapRef: |
|||
name: tb-node-db-config |
|||
volumeMounts: |
|||
- mountPath: /config |
|||
name: tb-node-config |
|||
command: ['sh', '-c', 'while [ ! -f /install-finished ]; do sleep 2; done;'] |
|||
restartPolicy: Never |
|||
@ -0,0 +1,18 @@ |
|||
#!/bin/bash |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
kubectl -n thingsboard delete svc,sts,deploy,pv,pvc,cm,po,ing --all --include-uninitialized |
|||
@ -0,0 +1,21 @@ |
|||
#!/bin/bash |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
set -e |
|||
|
|||
kubectl config set-context $(kubectl config current-context) --namespace=thingsboard |
|||
kubectl delete -f thingsboard.yml |
|||
@ -0,0 +1,26 @@ |
|||
#!/bin/bash |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
set -e |
|||
|
|||
kubectl apply -f tb-namespace.yml |
|||
kubectl config set-context $(kubectl config current-context) --namespace=thingsboard |
|||
kubectl apply -f tb-node-configmap.yml |
|||
kubectl apply -f tb-mqtt-transport-configmap.yml |
|||
kubectl apply -f tb-http-transport-configmap.yml |
|||
kubectl apply -f tb-coap-transport-configmap.yml |
|||
kubectl apply -f thingsboard.yml |
|||
@ -0,0 +1,93 @@ |
|||
#!/bin/bash |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
function installTb() { |
|||
|
|||
loadDemo=$1 |
|||
|
|||
kubectl apply -f tb-node-configmap.yml |
|||
kubectl apply -f database-setup.yml && |
|||
kubectl wait --for=condition=Ready pod/tb-db-setup --timeout=120s && |
|||
kubectl exec tb-db-setup -- sh -c 'export INSTALL_TB=true; export LOAD_DEMO='"$loadDemo"'; start-tb-node.sh; touch /install-finished;' |
|||
|
|||
kubectl delete pod tb-db-setup |
|||
|
|||
} |
|||
|
|||
function installPostgres() { |
|||
|
|||
kubectl apply -f postgres.yml |
|||
kubectl apply -f tb-node-postgres-configmap.yml |
|||
|
|||
kubectl rollout status deployment/postgres |
|||
} |
|||
|
|||
function installCassandra() { |
|||
|
|||
kubectl apply -f cassandra.yml |
|||
kubectl apply -f tb-node-cassandra-configmap.yml |
|||
|
|||
kubectl rollout status statefulset/cassandra |
|||
|
|||
kubectl exec -it cassandra-0 -- bash -c "cqlsh -e \ |
|||
\"CREATE KEYSPACE IF NOT EXISTS thingsboard \ |
|||
WITH replication = { \ |
|||
'class' : 'SimpleStrategy', \ |
|||
'replication_factor' : 1 \ |
|||
};\"" |
|||
} |
|||
|
|||
while [[ $# -gt 0 ]] |
|||
do |
|||
key="$1" |
|||
|
|||
case $key in |
|||
--loadDemo) |
|||
LOAD_DEMO=true |
|||
shift # past argument |
|||
;; |
|||
*) |
|||
# unknown option |
|||
;; |
|||
esac |
|||
shift # past argument or value |
|||
done |
|||
|
|||
if [ "$LOAD_DEMO" == "true" ]; then |
|||
loadDemo=true |
|||
else |
|||
loadDemo=false |
|||
fi |
|||
|
|||
source .env |
|||
|
|||
kubectl apply -f tb-namespace.yml |
|||
kubectl config set-context $(kubectl config current-context) --namespace=thingsboard |
|||
|
|||
case $DATABASE in |
|||
postgres) |
|||
installPostgres |
|||
installTb ${loadDemo} |
|||
;; |
|||
cassandra) |
|||
installCassandra |
|||
installTb ${loadDemo} |
|||
;; |
|||
*) |
|||
echo "Unknown DATABASE value specified: '${DATABASE}'. Should be either postgres or cassandra." >&2 |
|||
exit 1 |
|||
esac |
|||
@ -0,0 +1,43 @@ |
|||
#!/bin/bash |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
for i in "$@" |
|||
do |
|||
case $i in |
|||
--fromVersion=*) |
|||
FROM_VERSION="${i#*=}" |
|||
shift |
|||
;; |
|||
*) |
|||
# unknown option |
|||
;; |
|||
esac |
|||
done |
|||
|
|||
if [[ -z "${FROM_VERSION// }" ]]; then |
|||
echo "--fromVersion parameter is invalid or unspecified!" |
|||
echo "Usage: k8s-upgrade-tb.sh --fromVersion={VERSION}" |
|||
exit 1 |
|||
else |
|||
fromVersion="${FROM_VERSION// }" |
|||
fi |
|||
|
|||
kubectl apply -f database-setup.yml && |
|||
kubectl wait --for=condition=Ready pod/tb-db-setup --timeout=120s && |
|||
kubectl exec tb-db-setup -- sh -c 'export UPGRADE_TB=true; export FROM_VERSION='"$fromVersion"'; start-tb-node.sh; touch /install-finished;' |
|||
|
|||
kubectl delete pod tb-db-setup |
|||
@ -0,0 +1,97 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: PersistentVolumeClaim |
|||
metadata: |
|||
name: postgres-pv-claim |
|||
namespace: thingsboard |
|||
labels: |
|||
app: postgres |
|||
spec: |
|||
accessModes: |
|||
- ReadWriteOnce |
|||
resources: |
|||
requests: |
|||
storage: 5Gi |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: postgres |
|||
namespace: thingsboard |
|||
labels: |
|||
app: postgres |
|||
spec: |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: postgres |
|||
spec: |
|||
volumes: |
|||
- name: postgres-data |
|||
persistentVolumeClaim: |
|||
claimName: postgres-pv-claim |
|||
containers: |
|||
- name: postgres |
|||
imagePullPolicy: Always |
|||
image: postgres:9.6 |
|||
ports: |
|||
- containerPort: 5432 |
|||
name: postgres |
|||
env: |
|||
- name: POSTGRES_DB |
|||
value: "thingsboard" |
|||
- name: PGDATA |
|||
value: /var/lib/postgresql/data/pgdata |
|||
volumeMounts: |
|||
- mountPath: /var/lib/postgresql/data |
|||
name: postgres-data |
|||
livenessProbe: |
|||
exec: |
|||
command: |
|||
- pg_isready |
|||
- -h |
|||
- localhost |
|||
- -U |
|||
- postgres |
|||
initialDelaySeconds: 60 |
|||
timeoutSeconds: 30 |
|||
readinessProbe: |
|||
exec: |
|||
command: |
|||
- pg_isready |
|||
- -h |
|||
- localhost |
|||
- -U |
|||
- postgres |
|||
initialDelaySeconds: 5 |
|||
timeoutSeconds: 1 |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: tb-database |
|||
namespace: thingsboard |
|||
spec: |
|||
type: ClusterIP |
|||
selector: |
|||
app: postgres |
|||
ports: |
|||
- port: 5432 |
|||
name: postgres |
|||
--- |
|||
@ -0,0 +1,65 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: ConfigMap |
|||
metadata: |
|||
name: tb-coap-transport-config |
|||
namespace: thingsboard |
|||
labels: |
|||
name: tb-coap-transport-config |
|||
data: |
|||
conf: | |
|||
export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-coap-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-coap-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" |
|||
export LOG_FILENAME=tb-coap-transport.out |
|||
export LOADER_PATH=/usr/share/tb-coap-transport/conf |
|||
logback: | |
|||
<!DOCTYPE configuration> |
|||
<configuration scan="true" scanPeriod="10 seconds"> |
|||
|
|||
<appender name="fileLogAppender" |
|||
class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>/var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.log</file> |
|||
<rollingPolicy |
|||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> |
|||
<fileNamePattern>/var/log/tb-coap-transport/${TB_HOST}/tb-coap-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
|||
<maxFileSize>100MB</maxFileSize> |
|||
<maxHistory>30</maxHistory> |
|||
<totalSizeCap>3GB</totalSizeCap> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
|||
<encoder> |
|||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<logger name="org.thingsboard.server" level="INFO" /> |
|||
|
|||
<root level="INFO"> |
|||
<appender-ref ref="fileLogAppender"/> |
|||
<appender-ref ref="STDOUT"/> |
|||
</root> |
|||
|
|||
</configuration> |
|||
@ -0,0 +1,65 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: ConfigMap |
|||
metadata: |
|||
name: tb-http-transport-config |
|||
namespace: thingsboard |
|||
labels: |
|||
name: tb-http-transport-config |
|||
data: |
|||
conf: | |
|||
export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-http-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-http-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" |
|||
export LOG_FILENAME=tb-http-transport.out |
|||
export LOADER_PATH=/usr/share/tb-http-transport/conf |
|||
logback: | |
|||
<!DOCTYPE configuration> |
|||
<configuration scan="true" scanPeriod="10 seconds"> |
|||
|
|||
<appender name="fileLogAppender" |
|||
class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>/var/log/tb-http-transport/${TB_HOST}/tb-http-transport.log</file> |
|||
<rollingPolicy |
|||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> |
|||
<fileNamePattern>/var/log/tb-http-transport/${TB_HOST}/tb-http-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
|||
<maxFileSize>100MB</maxFileSize> |
|||
<maxHistory>30</maxHistory> |
|||
<totalSizeCap>3GB</totalSizeCap> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
|||
<encoder> |
|||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<logger name="org.thingsboard.server" level="INFO" /> |
|||
|
|||
<root level="INFO"> |
|||
<appender-ref ref="fileLogAppender"/> |
|||
<appender-ref ref="STDOUT"/> |
|||
</root> |
|||
|
|||
</configuration> |
|||
@ -0,0 +1,65 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: ConfigMap |
|||
metadata: |
|||
name: tb-mqtt-transport-config |
|||
namespace: thingsboard |
|||
labels: |
|||
name: tb-mqtt-transport-config |
|||
data: |
|||
conf: | |
|||
export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/tb-mqtt-transport/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tb-mqtt-transport/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" |
|||
export LOG_FILENAME=tb-mqtt-transport.out |
|||
export LOADER_PATH=/usr/share/tb-mqtt-transport/conf |
|||
logback: | |
|||
<!DOCTYPE configuration> |
|||
<configuration scan="true" scanPeriod="10 seconds"> |
|||
|
|||
<appender name="fileLogAppender" |
|||
class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>/var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.log</file> |
|||
<rollingPolicy |
|||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> |
|||
<fileNamePattern>/var/log/tb-mqtt-transport/${TB_HOST}/tb-mqtt-transport.%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
|||
<maxFileSize>100MB</maxFileSize> |
|||
<maxHistory>30</maxHistory> |
|||
<totalSizeCap>3GB</totalSizeCap> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
|||
<encoder> |
|||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<logger name="org.thingsboard.server" level="INFO" /> |
|||
|
|||
<root level="INFO"> |
|||
<appender-ref ref="fileLogAppender"/> |
|||
<appender-ref ref="STDOUT"/> |
|||
</root> |
|||
|
|||
</configuration> |
|||
@ -0,0 +1,22 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: Namespace |
|||
metadata: |
|||
name: thingsboard |
|||
labels: |
|||
name: thingsboard |
|||
@ -0,0 +1,28 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: ConfigMap |
|||
metadata: |
|||
name: tb-node-db-config |
|||
namespace: thingsboard |
|||
labels: |
|||
name: tb-node-db-config |
|||
data: |
|||
DATABASE_TS_TYPE: cassandra |
|||
DATABASE_ENTITIES_TYPE: cassandra |
|||
CASSANDRA_URL: cassandra:9042 |
|||
CASSANDRA_SOCKET_READ_TIMEOUT: "60000" |
|||
@ -0,0 +1,67 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: ConfigMap |
|||
metadata: |
|||
name: tb-node-config |
|||
namespace: thingsboard |
|||
labels: |
|||
name: tb-node-config |
|||
data: |
|||
conf: | |
|||
export JAVA_OPTS="$JAVA_OPTS -Dplatform=deb -Dinstall.data_dir=/usr/share/thingsboard/data" |
|||
export JAVA_OPTS="$JAVA_OPTS -Xloggc:/var/log/thingsboard/${TB_HOST}/gc.log -XX:+IgnoreUnrecognizedVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/thingsboard/${TB_HOST}/heapdump.bin -XX:+PrintGCDetails -XX:+PrintGCDateStamps" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+PrintGCApplicationStoppedTime -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:GCLogFileSize=10M -XX:-UseBiasedLocking -XX:+UseTLAB -XX:+ResizeTLAB -XX:+PerfDisableSharedMem -XX:+UseCondCardMark" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:CMSWaitDuration=10000 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+CMSParallelInitialMarkEnabled" |
|||
export JAVA_OPTS="$JAVA_OPTS -XX:+CMSEdenChunksRecordAlways -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -XX:+ExitOnOutOfMemoryError" |
|||
export LOG_FILENAME=thingsboard.out |
|||
export LOADER_PATH=/usr/share/thingsboard/conf,/usr/share/thingsboard/extensions |
|||
logback: | |
|||
<!DOCTYPE configuration> |
|||
<configuration scan="true" scanPeriod="10 seconds"> |
|||
|
|||
<appender name="fileLogAppender" |
|||
class="ch.qos.logback.core.rolling.RollingFileAppender"> |
|||
<file>/var/log/thingsboard/${TB_HOST}/thingsboard.log</file> |
|||
<rollingPolicy |
|||
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"> |
|||
<fileNamePattern>/var/log/thingsboard/${TB_HOST}/thingsboard.%d{yyyy-MM-dd}.%i.log</fileNamePattern> |
|||
<maxFileSize>100MB</maxFileSize> |
|||
<maxHistory>30</maxHistory> |
|||
<totalSizeCap>3GB</totalSizeCap> |
|||
</rollingPolicy> |
|||
<encoder> |
|||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> |
|||
<encoder> |
|||
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern> |
|||
</encoder> |
|||
</appender> |
|||
|
|||
<logger name="org.thingsboard.server" level="INFO" /> |
|||
<logger name="com.google.common.util.concurrent.AggregateFuture" level="OFF" /> |
|||
|
|||
<root level="INFO"> |
|||
<appender-ref ref="fileLogAppender"/> |
|||
<appender-ref ref="STDOUT"/> |
|||
</root> |
|||
|
|||
</configuration> |
|||
@ -0,0 +1,31 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: v1 |
|||
kind: ConfigMap |
|||
metadata: |
|||
name: tb-node-db-config |
|||
namespace: thingsboard |
|||
labels: |
|||
name: tb-node-db-config |
|||
data: |
|||
DATABASE_TS_TYPE: sql |
|||
DATABASE_ENTITIES_TYPE: sql |
|||
SPRING_JPA_DATABASE_PLATFORM: org.hibernate.dialect.PostgreSQLDialect |
|||
SPRING_DRIVER_CLASS_NAME: org.postgresql.Driver |
|||
SPRING_DATASOURCE_URL: jdbc:postgresql://tb-database:5432/thingsboard |
|||
SPRING_DATASOURCE_USERNAME: postgres |
|||
SPRING_DATASOURCE_PASSWORD: postgres |
|||
@ -0,0 +1,608 @@ |
|||
# |
|||
# 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. |
|||
# |
|||
|
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: zookeeper |
|||
namespace: thingsboard |
|||
spec: |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: zookeeper |
|||
spec: |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: zookeeper:3.5 |
|||
ports: |
|||
- containerPort: 2181 |
|||
readinessProbe: |
|||
periodSeconds: 5 |
|||
tcpSocket: |
|||
port: 2181 |
|||
livenessProbe: |
|||
periodSeconds: 5 |
|||
tcpSocket: |
|||
port: 2181 |
|||
env: |
|||
- name: ZOO_MY_ID |
|||
value: "1" |
|||
- name: ZOO_SERVERS |
|||
value: "server.1=0.0.0.0:2888:3888;0.0.0.0:2181" |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: zookeeper |
|||
namespace: thingsboard |
|||
spec: |
|||
type: ClusterIP |
|||
selector: |
|||
app: zookeeper |
|||
ports: |
|||
- name: zk-port |
|||
port: 2181 |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: tb-kafka |
|||
namespace: thingsboard |
|||
spec: |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: tb-kafka |
|||
spec: |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: wurstmeister/kafka |
|||
ports: |
|||
- containerPort: 9092 |
|||
readinessProbe: |
|||
periodSeconds: 20 |
|||
tcpSocket: |
|||
port: 9092 |
|||
livenessProbe: |
|||
periodSeconds: 5 |
|||
tcpSocket: |
|||
port: 9092 |
|||
env: |
|||
- name: KAFKA_ZOOKEEPER_CONNECT |
|||
value: "zookeeper:2181" |
|||
- name: KAFKA_LISTENERS |
|||
value: "INSIDE://:9093,OUTSIDE://:9092" |
|||
- name: KAFKA_ADVERTISED_LISTENERS |
|||
value: "INSIDE://:9093,OUTSIDE://tb-kafka:9092" |
|||
- name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP |
|||
value: "INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT" |
|||
- name: KAFKA_INTER_BROKER_LISTENER_NAME |
|||
value: "INSIDE" |
|||
- name: KAFKA_CREATE_TOPICS |
|||
value: "js.eval.requests:100:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.transport.api.requests:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600,tb.rule-engine:30:1:delete --config=retention.ms=60000 --config=segment.bytes=26214400 --config=retention.bytes=104857600" |
|||
- name: KAFKA_AUTO_CREATE_TOPICS_ENABLE |
|||
value: "false" |
|||
- name: KAFKA_LOG_RETENTION_BYTES |
|||
value: "1073741824" |
|||
- name: KAFKA_LOG_SEGMENT_BYTES |
|||
value: "268435456" |
|||
- name: KAFKA_LOG_RETENTION_MS |
|||
value: "300000" |
|||
- name: KAFKA_LOG_CLEANUP_POLICY |
|||
value: "delete" |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: tb-kafka |
|||
namespace: thingsboard |
|||
spec: |
|||
type: ClusterIP |
|||
selector: |
|||
app: tb-kafka |
|||
ports: |
|||
- name: tb-kafka-port |
|||
port: 9092 |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: tb-redis |
|||
namespace: thingsboard |
|||
spec: |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: tb-redis |
|||
spec: |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: redis:4.0 |
|||
ports: |
|||
- containerPort: 6379 |
|||
readinessProbe: |
|||
periodSeconds: 5 |
|||
tcpSocket: |
|||
port: 6379 |
|||
livenessProbe: |
|||
periodSeconds: 5 |
|||
tcpSocket: |
|||
port: 6379 |
|||
volumeMounts: |
|||
- mountPath: /data |
|||
name: redis-data |
|||
volumes: |
|||
- name: redis-data |
|||
emptyDir: {} |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: tb-redis |
|||
namespace: thingsboard |
|||
spec: |
|||
type: ClusterIP |
|||
selector: |
|||
app: tb-redis |
|||
ports: |
|||
- name: tb-redis-port |
|||
port: 6379 |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: tb-js-executor |
|||
namespace: thingsboard |
|||
spec: |
|||
replicas: 20 |
|||
selector: |
|||
matchLabels: |
|||
app: tb-js-executor |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: tb-js-executor |
|||
spec: |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: thingsboard/tb-js-executor:latest |
|||
env: |
|||
- name: REMOTE_JS_EVAL_REQUEST_TOPIC |
|||
value: "js.eval.requests" |
|||
- name: TB_KAFKA_SERVERS |
|||
value: "tb-kafka:9092" |
|||
- name: LOGGER_LEVEL |
|||
value: "info" |
|||
- name: LOG_FOLDER |
|||
value: "logs" |
|||
- name: LOGGER_FILENAME |
|||
value: "tb-js-executor-%DATE%.log" |
|||
- name: DOCKER_MODE |
|||
value: "true" |
|||
- name: SCRIPT_BODY_TRACE_FREQUENCY |
|||
value: "1000" |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: tb-node |
|||
namespace: thingsboard |
|||
spec: |
|||
replicas: 2 |
|||
selector: |
|||
matchLabels: |
|||
app: tb-node |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: tb-node |
|||
spec: |
|||
volumes: |
|||
- name: tb-node-config |
|||
configMap: |
|||
name: tb-node-config |
|||
items: |
|||
- key: conf |
|||
path: thingsboard.conf |
|||
- key: logback |
|||
path: logback.xml |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: thingsboard/tb-node:latest |
|||
ports: |
|||
- containerPort: 8080 |
|||
name: http |
|||
- containerPort: 9001 |
|||
name: rpc |
|||
env: |
|||
- name: RPC_HOST |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: status.podIP |
|||
- name: CLUSTER_NODE_ID |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: metadata.name |
|||
- name: TB_HOST |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: metadata.name |
|||
- name: ZOOKEEPER_ENABLED |
|||
value: "true" |
|||
- name: ZOOKEEPER_URL |
|||
value: "zookeeper:2181" |
|||
- name: TB_KAFKA_SERVERS |
|||
value: "tb-kafka:9092" |
|||
- name: JS_EVALUATOR |
|||
value: "remote" |
|||
- name: TRANSPORT_TYPE |
|||
value: "remote" |
|||
- name: CACHE_TYPE |
|||
value: "redis" |
|||
- name: REDIS_HOST |
|||
value: "tb-redis" |
|||
- name: HTTP_LOG_CONTROLLER_ERROR_STACK_TRACE |
|||
value: "false" |
|||
envFrom: |
|||
- configMapRef: |
|||
name: tb-node-db-config |
|||
volumeMounts: |
|||
- mountPath: /config |
|||
name: tb-node-config |
|||
livenessProbe: |
|||
httpGet: |
|||
path: /login |
|||
port: http |
|||
initialDelaySeconds: 120 |
|||
timeoutSeconds: 10 |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: tb-node |
|||
namespace: thingsboard |
|||
spec: |
|||
type: ClusterIP |
|||
selector: |
|||
app: tb-node |
|||
ports: |
|||
- port: 8080 |
|||
name: http |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: tb-mqtt-transport |
|||
namespace: thingsboard |
|||
spec: |
|||
replicas: 2 |
|||
selector: |
|||
matchLabels: |
|||
app: tb-mqtt-transport |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: tb-mqtt-transport |
|||
spec: |
|||
volumes: |
|||
- name: tb-mqtt-transport-config |
|||
configMap: |
|||
name: tb-mqtt-transport-config |
|||
items: |
|||
- key: conf |
|||
path: tb-mqtt-transport.conf |
|||
- key: logback |
|||
path: logback.xml |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: thingsboard/tb-mqtt-transport:latest |
|||
ports: |
|||
- containerPort: 1883 |
|||
name: mqtt |
|||
env: |
|||
- name: CLUSTER_NODE_ID |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: metadata.name |
|||
- name: TB_HOST |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: metadata.name |
|||
- name: MQTT_BIND_ADDRESS |
|||
value: "0.0.0.0" |
|||
- name: MQTT_BIND_PORT |
|||
value: "1883" |
|||
- name: MQTT_TIMEOUT |
|||
value: "10000" |
|||
- name: TB_KAFKA_SERVERS |
|||
value: "tb-kafka:9092" |
|||
volumeMounts: |
|||
- mountPath: /config |
|||
name: tb-mqtt-transport-config |
|||
readinessProbe: |
|||
periodSeconds: 20 |
|||
tcpSocket: |
|||
port: 1883 |
|||
livenessProbe: |
|||
periodSeconds: 20 |
|||
tcpSocket: |
|||
port: 1883 |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: tb-mqtt-transport |
|||
namespace: thingsboard |
|||
spec: |
|||
type: LoadBalancer |
|||
selector: |
|||
app: tb-mqtt-transport |
|||
ports: |
|||
- port: 1883 |
|||
targetPort: 1883 |
|||
name: mqtt |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: tb-http-transport |
|||
namespace: thingsboard |
|||
spec: |
|||
replicas: 2 |
|||
selector: |
|||
matchLabels: |
|||
app: tb-http-transport |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: tb-http-transport |
|||
spec: |
|||
volumes: |
|||
- name: tb-http-transport-config |
|||
configMap: |
|||
name: tb-http-transport-config |
|||
items: |
|||
- key: conf |
|||
path: tb-http-transport.conf |
|||
- key: logback |
|||
path: logback.xml |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: thingsboard/tb-http-transport:latest |
|||
ports: |
|||
- containerPort: 8080 |
|||
name: http |
|||
env: |
|||
- name: CLUSTER_NODE_ID |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: metadata.name |
|||
- name: TB_HOST |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: metadata.name |
|||
- name: HTTP_BIND_ADDRESS |
|||
value: "0.0.0.0" |
|||
- name: HTTP_BIND_PORT |
|||
value: "8080" |
|||
- name: HTTP_REQUEST_TIMEOUT |
|||
value: "60000" |
|||
- name: TB_KAFKA_SERVERS |
|||
value: "tb-kafka:9092" |
|||
volumeMounts: |
|||
- mountPath: /config |
|||
name: tb-http-transport-config |
|||
readinessProbe: |
|||
periodSeconds: 20 |
|||
tcpSocket: |
|||
port: 8080 |
|||
livenessProbe: |
|||
periodSeconds: 20 |
|||
tcpSocket: |
|||
port: 8080 |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: tb-http-transport |
|||
namespace: thingsboard |
|||
spec: |
|||
type: ClusterIP |
|||
selector: |
|||
app: tb-http-transport |
|||
ports: |
|||
- port: 8080 |
|||
name: http |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: tb-coap-transport |
|||
namespace: thingsboard |
|||
spec: |
|||
replicas: 2 |
|||
selector: |
|||
matchLabels: |
|||
app: tb-coap-transport |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: tb-coap-transport |
|||
spec: |
|||
volumes: |
|||
- name: tb-coap-transport-config |
|||
configMap: |
|||
name: tb-coap-transport-config |
|||
items: |
|||
- key: conf |
|||
path: tb-coap-transport.conf |
|||
- key: logback |
|||
path: logback.xml |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: thingsboard/tb-coap-transport:latest |
|||
ports: |
|||
- containerPort: 5683 |
|||
name: coap |
|||
protocol: UDP |
|||
env: |
|||
- name: CLUSTER_NODE_ID |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: metadata.name |
|||
- name: TB_HOST |
|||
valueFrom: |
|||
fieldRef: |
|||
fieldPath: metadata.name |
|||
- name: COAP_BIND_ADDRESS |
|||
value: "0.0.0.0" |
|||
- name: COAP_BIND_PORT |
|||
value: "5683" |
|||
- name: COAP_TIMEOUT |
|||
value: "10000" |
|||
- name: TB_KAFKA_SERVERS |
|||
value: "tb-kafka:9092" |
|||
volumeMounts: |
|||
- mountPath: /config |
|||
name: tb-coap-transport-config |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: tb-coap-transport |
|||
namespace: thingsboard |
|||
spec: |
|||
type: LoadBalancer |
|||
selector: |
|||
app: tb-coap-transport |
|||
ports: |
|||
- port: 5683 |
|||
name: coap |
|||
protocol: UDP |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Deployment |
|||
metadata: |
|||
name: tb-web-ui |
|||
namespace: thingsboard |
|||
spec: |
|||
replicas: 2 |
|||
selector: |
|||
matchLabels: |
|||
app: tb-web-ui |
|||
template: |
|||
metadata: |
|||
labels: |
|||
app: tb-web-ui |
|||
spec: |
|||
containers: |
|||
- name: server |
|||
imagePullPolicy: Always |
|||
image: thingsboard/tb-web-ui:latest |
|||
ports: |
|||
- containerPort: 8080 |
|||
name: http |
|||
env: |
|||
- name: HTTP_BIND_ADDRESS |
|||
value: "0.0.0.0" |
|||
- name: HTTP_BIND_PORT |
|||
value: "8080" |
|||
- name: TB_ENABLE_PROXY |
|||
value: "false" |
|||
- name: LOGGER_LEVEL |
|||
value: "info" |
|||
- name: LOG_FOLDER |
|||
value: "logs" |
|||
- name: LOGGER_FILENAME |
|||
value: "tb-web-ui-%DATE%.log" |
|||
- name: DOCKER_MODE |
|||
value: "true" |
|||
livenessProbe: |
|||
httpGet: |
|||
path: /index.html |
|||
port: http |
|||
initialDelaySeconds: 120 |
|||
timeoutSeconds: 10 |
|||
restartPolicy: Always |
|||
--- |
|||
apiVersion: v1 |
|||
kind: Service |
|||
metadata: |
|||
name: tb-web-ui |
|||
namespace: thingsboard |
|||
spec: |
|||
type: ClusterIP |
|||
selector: |
|||
app: tb-web-ui |
|||
ports: |
|||
- port: 8080 |
|||
name: http |
|||
--- |
|||
apiVersion: extensions/v1beta1 |
|||
kind: Ingress |
|||
metadata: |
|||
name: tb-ingress |
|||
namespace: thingsboard |
|||
annotations: |
|||
nginx.ingress.kubernetes.io/use-regex: "true" |
|||
nginx.ingress.kubernetes.io/ssl-redirect: "false" |
|||
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600" |
|||
spec: |
|||
rules: |
|||
- http: |
|||
paths: |
|||
- path: /api/v1/.* |
|||
backend: |
|||
serviceName: tb-http-transport |
|||
servicePort: 8080 |
|||
- path: /static/rulenode/.* |
|||
backend: |
|||
serviceName: tb-node |
|||
servicePort: 8080 |
|||
- path: /static/.* |
|||
backend: |
|||
serviceName: tb-web-ui |
|||
servicePort: 8080 |
|||
- path: /index.html.* |
|||
backend: |
|||
serviceName: tb-web-ui |
|||
servicePort: 8080 |
|||
- path: / |
|||
backend: |
|||
serviceName: tb-web-ui |
|||
servicePort: 8080 |
|||
- path: /.* |
|||
backend: |
|||
serviceName: tb-node |
|||
servicePort: 8080 |
|||
--- |
|||
@ -0,0 +1,266 @@ |
|||
/** |
|||
* 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.action; |
|||
|
|||
import com.datastax.driver.core.BoundStatement; |
|||
import com.datastax.driver.core.CodecRegistry; |
|||
import com.datastax.driver.core.ConsistencyLevel; |
|||
import com.datastax.driver.core.PreparedStatement; |
|||
import com.datastax.driver.core.ResultSet; |
|||
import com.datastax.driver.core.ResultSetFuture; |
|||
import com.datastax.driver.core.Session; |
|||
import com.datastax.driver.core.Statement; |
|||
import com.datastax.driver.core.TypeCodec; |
|||
import com.datastax.driver.core.exceptions.CodecNotFoundException; |
|||
import com.google.common.base.Function; |
|||
import com.google.common.util.concurrent.Futures; |
|||
import com.google.common.util.concurrent.ListenableFuture; |
|||
import com.google.gson.JsonElement; |
|||
import com.google.gson.JsonObject; |
|||
import com.google.gson.JsonParser; |
|||
import com.google.gson.JsonPrimitive; |
|||
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.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.data.plugin.ComponentType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.dao.cassandra.CassandraCluster; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.model.type.AuthorityCodec; |
|||
import org.thingsboard.server.dao.model.type.ComponentLifecycleStateCodec; |
|||
import org.thingsboard.server.dao.model.type.ComponentScopeCodec; |
|||
import org.thingsboard.server.dao.model.type.ComponentTypeCodec; |
|||
import org.thingsboard.server.dao.model.type.DeviceCredentialsTypeCodec; |
|||
import org.thingsboard.server.dao.model.type.EntityTypeCodec; |
|||
import org.thingsboard.server.dao.model.type.JsonCodec; |
|||
import org.thingsboard.server.dao.nosql.CassandraStatementTask; |
|||
|
|||
import javax.annotation.Nullable; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ExecutionException; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.Executors; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; |
|||
import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback; |
|||
|
|||
@Slf4j |
|||
@RuleNode(type = ComponentType.ACTION, |
|||
name = "save to custom table", |
|||
configClazz = TbSaveToCustomCassandraTableNodeConfiguration.class, |
|||
nodeDescription = "Node stores data from incoming Message payload to the Cassandra database into the predefined custom table" + |
|||
" that should have <b>cs_tb_</b> prefix, to avoid the data insertion to the common TB tables.<br>" + |
|||
"<b>Note:</b> rule node can be used only for Cassandra DB.", |
|||
nodeDetails = "Administrator should set the custom table name without prefix: <b>cs_tb_</b>. <br>" + |
|||
"Administrator can configure the mapping between the Message field names and Table columns name.<br>" + |
|||
"<b>Note:</b>If the mapping key is <b>$entity_id</b>, that is identified by the Message Originator, then to the appropriate column name(mapping value) will be write the message originator id.<br><br>" + |
|||
"If specified message field does not exist or is not a JSON Primitive, the outbound message will be routed via <b>failure</b> chain," + |
|||
" otherwise, the message will be routed via <b>success</b> chain.", |
|||
uiResources = {"static/rulenode/rulenode-core-config.js"}, |
|||
configDirective = "tbActionNodeCustomTableConfig", |
|||
icon = "file_upload") |
|||
public class TbSaveToCustomCassandraTableNode implements TbNode { |
|||
|
|||
private static final String TABLE_PREFIX = "cs_tb_"; |
|||
private static final JsonParser parser = new JsonParser(); |
|||
private static final String ENTITY_ID = "$entityId"; |
|||
|
|||
private TbSaveToCustomCassandraTableNodeConfiguration config; |
|||
private Session session; |
|||
private CassandraCluster cassandraCluster; |
|||
private ConsistencyLevel defaultWriteLevel; |
|||
private PreparedStatement saveStmt; |
|||
private ExecutorService readResultsProcessingExecutor; |
|||
private Map<String, String> fieldsMap; |
|||
|
|||
@Override |
|||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { |
|||
config = TbNodeUtils.convert(configuration, TbSaveToCustomCassandraTableNodeConfiguration.class); |
|||
cassandraCluster = ctx.getCassandraCluster(); |
|||
if (cassandraCluster == null) { |
|||
throw new RuntimeException("Unable to connect to Cassandra database"); |
|||
} else { |
|||
startExecutor(); |
|||
saveStmt = getSaveStmt(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onMsg(TbContext ctx, TbMsg msg) throws ExecutionException, InterruptedException, TbNodeException { |
|||
withCallback(save(msg, ctx), aVoid -> { |
|||
ctx.tellNext(msg, SUCCESS); |
|||
}, e -> ctx.tellFailure(msg, e), ctx.getDbCallbackExecutor()); |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
stopExecutor(); |
|||
saveStmt = null; |
|||
} |
|||
|
|||
private void startExecutor() { |
|||
readResultsProcessingExecutor = Executors.newCachedThreadPool(); |
|||
} |
|||
|
|||
private void stopExecutor() { |
|||
if (readResultsProcessingExecutor != null) { |
|||
readResultsProcessingExecutor.shutdownNow(); |
|||
} |
|||
} |
|||
|
|||
private PreparedStatement prepare(String query) { |
|||
return getSession().prepare(query); |
|||
} |
|||
|
|||
private Session getSession() { |
|||
if (session == null) { |
|||
session = cassandraCluster.getSession(); |
|||
defaultWriteLevel = cassandraCluster.getDefaultWriteConsistencyLevel(); |
|||
CodecRegistry registry = session.getCluster().getConfiguration().getCodecRegistry(); |
|||
registerCodecIfNotFound(registry, new JsonCodec()); |
|||
registerCodecIfNotFound(registry, new DeviceCredentialsTypeCodec()); |
|||
registerCodecIfNotFound(registry, new AuthorityCodec()); |
|||
registerCodecIfNotFound(registry, new ComponentLifecycleStateCodec()); |
|||
registerCodecIfNotFound(registry, new ComponentTypeCodec()); |
|||
registerCodecIfNotFound(registry, new ComponentScopeCodec()); |
|||
registerCodecIfNotFound(registry, new EntityTypeCodec()); |
|||
} |
|||
return session; |
|||
} |
|||
|
|||
private void registerCodecIfNotFound(CodecRegistry registry, TypeCodec<?> codec) { |
|||
try { |
|||
registry.codecFor(codec.getCqlType(), codec.getJavaType()); |
|||
} catch (CodecNotFoundException e) { |
|||
registry.register(codec); |
|||
} |
|||
} |
|||
|
|||
|
|||
private PreparedStatement getSaveStmt() { |
|||
fieldsMap = config.getFieldsMapping(); |
|||
if (fieldsMap.isEmpty()) { |
|||
throw new RuntimeException("Fields(key,value) map is empty!"); |
|||
} else { |
|||
return prepareStatement(new ArrayList<>(fieldsMap.values())); |
|||
} |
|||
} |
|||
|
|||
private PreparedStatement prepareStatement(List<String> fieldsList) { |
|||
return prepare(createQuery(fieldsList)); |
|||
} |
|||
|
|||
private String createQuery(List<String> fieldsList) { |
|||
int size = fieldsList.size(); |
|||
StringBuilder query = new StringBuilder(); |
|||
query.append("INSERT INTO ") |
|||
.append(TABLE_PREFIX) |
|||
.append(config.getTableName()) |
|||
.append("("); |
|||
for (String field : fieldsList) { |
|||
query.append(field); |
|||
if (fieldsList.get(size - 1).equals(field)) { |
|||
query.append(")"); |
|||
} else { |
|||
query.append(","); |
|||
} |
|||
} |
|||
query.append(" VALUES("); |
|||
for (int i = 0; i < size; i++) { |
|||
if (i == size - 1) { |
|||
query.append("?)"); |
|||
} else { |
|||
query.append("?, "); |
|||
} |
|||
} |
|||
return query.toString(); |
|||
} |
|||
|
|||
private ListenableFuture<Void> save(TbMsg msg, TbContext ctx) { |
|||
JsonElement data = parser.parse(msg.getData()); |
|||
if (!data.isJsonObject()) { |
|||
throw new IllegalStateException("Invalid message structure, it is not a JSON Object:" + data); |
|||
} else { |
|||
JsonObject dataAsObject = data.getAsJsonObject(); |
|||
BoundStatement stmt = saveStmt.bind(); |
|||
AtomicInteger i = new AtomicInteger(0); |
|||
fieldsMap.forEach((key, value) -> { |
|||
if (key.equals(ENTITY_ID)) { |
|||
stmt.setUUID(i.get(), msg.getOriginator().getId()); |
|||
} else if (dataAsObject.has(key)) { |
|||
if (dataAsObject.get(key).isJsonPrimitive()) { |
|||
JsonPrimitive primitive = dataAsObject.get(key).getAsJsonPrimitive(); |
|||
if (primitive.isNumber()) { |
|||
stmt.setLong(i.get(), dataAsObject.get(key).getAsLong()); |
|||
} else if (primitive.isBoolean()) { |
|||
stmt.setBool(i.get(), dataAsObject.get(key).getAsBoolean()); |
|||
} else if (primitive.isString()) { |
|||
stmt.setString(i.get(), dataAsObject.get(key).getAsString()); |
|||
} else { |
|||
stmt.setToNull(i.get()); |
|||
} |
|||
} else { |
|||
throw new IllegalStateException("Message data key: '" + key + "' with value: '" + value + "' is not a JSON Primitive!"); |
|||
} |
|||
} else { |
|||
throw new RuntimeException("Message data doesn't contain key: " + "'" + key + "'!"); |
|||
} |
|||
i.getAndIncrement(); |
|||
}); |
|||
return getFuture(executeAsyncWrite(ctx, stmt), rs -> null); |
|||
} |
|||
} |
|||
|
|||
private ResultSetFuture executeAsyncWrite(TbContext ctx, Statement statement) { |
|||
return executeAsync(ctx, statement, defaultWriteLevel); |
|||
} |
|||
|
|||
private ResultSetFuture executeAsync(TbContext ctx, Statement statement, ConsistencyLevel level) { |
|||
if (log.isDebugEnabled()) { |
|||
log.debug("Execute cassandra async statement {}", statementToString(statement)); |
|||
} |
|||
if (statement.getConsistencyLevel() == null) { |
|||
statement.setConsistencyLevel(level); |
|||
} |
|||
return ctx.getCassandraBufferedRateExecutor().submit(new CassandraStatementTask(ctx.getTenantId(), getSession(), statement)); |
|||
} |
|||
|
|||
private static String statementToString(Statement statement) { |
|||
if (statement instanceof BoundStatement) { |
|||
return ((BoundStatement) statement).preparedStatement().getQueryString(); |
|||
} else { |
|||
return statement.toString(); |
|||
} |
|||
} |
|||
|
|||
private <T> ListenableFuture<T> getFuture(ResultSetFuture future, java.util.function.Function<ResultSet, T> transformer) { |
|||
return Futures.transform(future, new Function<ResultSet, T>() { |
|||
@Nullable |
|||
@Override |
|||
public T apply(@Nullable ResultSet input) { |
|||
return transformer.apply(input); |
|||
} |
|||
}, readResultsProcessingExecutor); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* 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.action; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.rule.engine.api.NodeConfiguration; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Data |
|||
public class TbSaveToCustomCassandraTableNodeConfiguration implements NodeConfiguration<TbSaveToCustomCassandraTableNodeConfiguration> { |
|||
|
|||
|
|||
private String tableName; |
|||
private Map<String, String> fieldsMapping; |
|||
|
|||
|
|||
@Override |
|||
public TbSaveToCustomCassandraTableNodeConfiguration defaultConfiguration() { |
|||
TbSaveToCustomCassandraTableNodeConfiguration configuration = new TbSaveToCustomCassandraTableNodeConfiguration(); |
|||
configuration.setTableName(""); |
|||
Map<String, String> map = new HashMap<>(); |
|||
map.put("", ""); |
|||
configuration.setFieldsMapping(map); |
|||
return configuration; |
|||
} |
|||
} |
|||
@ -0,0 +1,131 @@ |
|||
/** |
|||
* 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.isFetchPerimeterInfoFromMessageMetadata()) { |
|||
//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 { |
|||
Perimeter perimeter = new Perimeter(); |
|||
perimeter.setPerimeterType(config.getPerimeterType()); |
|||
perimeter.setCenterLatitude(config.getCenterLatitude()); |
|||
perimeter.setCenterLongitude(config.getCenterLongitude()); |
|||
perimeter.setRange(config.getRange()); |
|||
perimeter.setRangeUnit(config.getRangeUnit()); |
|||
perimeter.setPolygonsDefinition(config.getPolygonsDefinition()); |
|||
return Collections.singletonList(perimeter); |
|||
} |
|||
} |
|||
|
|||
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; |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
|
|||
} |
|||
|
|||
} |
|||
@ -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 stayed; |
|||
|
|||
} |
|||
@ -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,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.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 = TbGpsGeofencingActionNodeConfiguration.class, |
|||
relationTypes = {"Entered", "Left", "Inside", "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", |
|||
uiResources = {"static/rulenode/rulenode-core-config.js"}, |
|||
configDirective = "tbActionNodeGpsGeofencingConfig") |
|||
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("stayed").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.isStayed()) { |
|||
long stayTime = ts - entityState.getStateSwitchTime(); |
|||
if (stayTime > (entityState.isInside() ? |
|||
TimeUnit.valueOf(config.getMinInsideDurationTimeUnit()).toMillis(config.getMinInsideDuration()) : TimeUnit.valueOf(config.getMinOutsideDurationTimeUnit()).toMillis(config.getMinOutsideDuration()))) { |
|||
setStaid(ctx, msg.getOriginator(), entityState); |
|||
ctx.tellNext(msg, entityState.isInside() ? "Inside" : "Outside"); |
|||
} |
|||
} |
|||
} |
|||
|
|||
private void switchState(TbContext ctx, EntityId entityId, EntityGeofencingState entityState, boolean matches, long ts) { |
|||
entityState.setInside(matches); |
|||
entityState.setStateSwitchTime(ts); |
|||
entityState.setStayed(false); |
|||
persist(ctx, entityId, entityState); |
|||
} |
|||
|
|||
private void setStaid(TbContext ctx, EntityId entityId, EntityGeofencingState entityState) { |
|||
entityState.setStayed(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("stayed", entityState.isStayed()); |
|||
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 |
|||
protected Class<TbGpsGeofencingActionNodeConfiguration> getConfigClazz() { |
|||
return TbGpsGeofencingActionNodeConfiguration.class; |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
/** |
|||
* 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 int minInsideDuration; |
|||
private int minOutsideDuration; |
|||
|
|||
private String minInsideDurationTimeUnit; |
|||
private String minOutsideDurationTimeUnit; |
|||
|
|||
@Override |
|||
public TbGpsGeofencingActionNodeConfiguration defaultConfiguration() { |
|||
TbGpsGeofencingActionNodeConfiguration configuration = new TbGpsGeofencingActionNodeConfiguration(); |
|||
configuration.setLatitudeKeyName("latitude"); |
|||
configuration.setLongitudeKeyName("longitude"); |
|||
configuration.setFetchPerimeterInfoFromMessageMetadata(true); |
|||
configuration.setMinInsideDurationTimeUnit(TimeUnit.MINUTES.name()); |
|||
configuration.setMinOutsideDurationTimeUnit(TimeUnit.MINUTES.name()); |
|||
configuration.setMinInsideDuration(1); |
|||
configuration.setMinOutsideDuration(1); |
|||
return configuration; |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
/** |
|||
* 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.", |
|||
uiResources = {"static/rulenode/rulenode-core-config.js"}, |
|||
configDirective = "tbFilterNodeGpsGeofencingConfig") |
|||
public class TbGpsGeofencingFilterNode extends AbstractGeofencingNode<TbGpsGeofencingFilterNodeConfiguration> { |
|||
|
|||
@Override |
|||
public void onMsg(TbContext ctx, TbMsg msg) throws TbNodeException { |
|||
ctx.tellNext(msg, checkMatches(msg) ? "True" : "False"); |
|||
} |
|||
|
|||
@Override |
|||
protected Class<TbGpsGeofencingFilterNodeConfiguration> getConfigClazz() { |
|||
return TbGpsGeofencingFilterNodeConfiguration.class; |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
/** |
|||
* 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 fetchPerimeterInfoFromMessageMetadata; |
|||
|
|||
private PerimeterType perimeterType; |
|||
|
|||
//For Polygons
|
|||
private String polygonsDefinition; |
|||
|
|||
//For Circles
|
|||
private Double centerLatitude; |
|||
private Double centerLongitude; |
|||
private Double range; |
|||
private RangeUnit rangeUnit; |
|||
|
|||
@Override |
|||
public TbGpsGeofencingFilterNodeConfiguration defaultConfiguration() { |
|||
TbGpsGeofencingFilterNodeConfiguration configuration = new TbGpsGeofencingFilterNodeConfiguration(); |
|||
configuration.setLatitudeKeyName("latitude"); |
|||
configuration.setLongitudeKeyName("longitude"); |
|||
configuration.setFetchPerimeterInfoFromMessageMetadata(true); |
|||
return configuration; |
|||
} |
|||
} |
|||
@ -0,0 +1,135 @@ |
|||
/** |
|||
* 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.metadata; |
|||
|
|||
import com.google.gson.Gson; |
|||
import com.google.gson.JsonElement; |
|||
import com.google.gson.JsonObject; |
|||
import com.google.gson.JsonParser; |
|||
import com.google.gson.reflect.TypeToken; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
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.util.EntityDetails; |
|||
import org.thingsboard.server.common.data.ContactBased; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.common.msg.TbMsgMetaData; |
|||
|
|||
import java.lang.reflect.Type; |
|||
import java.util.Map; |
|||
import java.util.concurrent.ExecutionException; |
|||
|
|||
import static org.thingsboard.rule.engine.api.TbRelationTypes.SUCCESS; |
|||
|
|||
@Slf4j |
|||
public abstract class TbAbstractGetEntityDetailsNode<C extends TbAbstractGetEntityDetailsNodeConfiguration> implements TbNode { |
|||
|
|||
private static final Gson gson = new Gson(); |
|||
private static final JsonParser jsonParser = new JsonParser(); |
|||
private static final Type TYPE = new TypeToken<Map<String, String>>() {}.getType(); |
|||
|
|||
protected C config; |
|||
|
|||
@Override |
|||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { |
|||
this.config = loadGetEntityDetailsNodeConfiguration(configuration); |
|||
} |
|||
|
|||
@Override |
|||
public void onMsg(TbContext ctx, TbMsg msg) { |
|||
try { |
|||
ctx.tellNext(getDetails(ctx, msg), SUCCESS); |
|||
} catch (Exception e) { |
|||
ctx.tellFailure(msg, e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() {} |
|||
|
|||
protected abstract C loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException; |
|||
|
|||
protected abstract TbMsg getDetails(TbContext ctx, TbMsg msg); |
|||
|
|||
protected MessageData getDataAsJson(TbMsg msg) { |
|||
if (this.config.isAddToMetadata()) { |
|||
return new MessageData(gson.toJsonTree(msg.getMetaData().getData(), TYPE), "metadata"); |
|||
} else { |
|||
return new MessageData(jsonParser.parse(msg.getData()), "data"); |
|||
} |
|||
} |
|||
|
|||
protected TbMsg transformMsg(TbContext ctx, TbMsg msg, JsonElement resultObject, MessageData messageData) { |
|||
if (messageData.getDataType().equals("metadata")) { |
|||
Map<String, String> metadataMap = gson.fromJson(resultObject.toString(), TYPE); |
|||
return ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), new TbMsgMetaData(metadataMap), msg.getData()); |
|||
} else { |
|||
return ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), msg.getMetaData(), gson.toJson(resultObject)); |
|||
} |
|||
} |
|||
|
|||
protected JsonElement addContactProperties(JsonElement data, ContactBased entity, EntityDetails entityDetails, String prefix) { |
|||
JsonObject dataAsObject = data.getAsJsonObject(); |
|||
switch (entityDetails) { |
|||
case ADDRESS: |
|||
if (entity.getAddress() != null) |
|||
dataAsObject.addProperty(prefix + "address", entity.getAddress()); |
|||
break; |
|||
case ADDRESS2: |
|||
if (entity.getAddress2() != null) |
|||
dataAsObject.addProperty(prefix + "address2", entity.getAddress2()); |
|||
break; |
|||
case CITY: |
|||
if (entity.getCity() != null) dataAsObject.addProperty(prefix + "city", entity.getCity()); |
|||
break; |
|||
case COUNTRY: |
|||
if (entity.getCountry() != null) |
|||
dataAsObject.addProperty(prefix + "country", entity.getCountry()); |
|||
break; |
|||
case STATE: |
|||
if (entity.getState() != null) dataAsObject.addProperty(prefix + "state", entity.getState()); |
|||
break; |
|||
case EMAIL: |
|||
if (entity.getEmail() != null) dataAsObject.addProperty(prefix + "email", entity.getEmail()); |
|||
break; |
|||
case PHONE: |
|||
if (entity.getPhone() != null) dataAsObject.addProperty(prefix + "phone", entity.getPhone()); |
|||
break; |
|||
case ZIP: |
|||
if (entity.getZip() != null) dataAsObject.addProperty(prefix + "zip", entity.getZip()); |
|||
break; |
|||
case ADDITIONAL_INFO: |
|||
if (entity.getAdditionalInfo().hasNonNull("description")) { |
|||
dataAsObject.addProperty(prefix + "additionalInfo", entity.getAdditionalInfo().get("description").asText()); |
|||
} |
|||
break; |
|||
} |
|||
return dataAsObject; |
|||
} |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
protected static class MessageData { |
|||
private JsonElement data; |
|||
private String dataType; |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* 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.metadata; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.rule.engine.util.EntityDetails; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public abstract class TbAbstractGetEntityDetailsNodeConfiguration { |
|||
|
|||
|
|||
private List<EntityDetails> detailsList; |
|||
|
|||
private boolean addToMetadata; |
|||
|
|||
} |
|||
@ -0,0 +1,101 @@ |
|||
/** |
|||
* 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.metadata; |
|||
|
|||
import com.google.gson.JsonElement; |
|||
import com.google.gson.JsonObject; |
|||
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.TbNodeConfiguration; |
|||
import org.thingsboard.rule.engine.api.TbNodeException; |
|||
import org.thingsboard.rule.engine.api.util.TbNodeUtils; |
|||
import org.thingsboard.rule.engine.util.EntityDetails; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.EntityView; |
|||
import org.thingsboard.server.common.data.asset.Asset; |
|||
import org.thingsboard.server.common.data.id.AssetId; |
|||
import org.thingsboard.server.common.data.id.DeviceId; |
|||
import org.thingsboard.server.common.data.id.EntityViewId; |
|||
import org.thingsboard.server.common.data.plugin.ComponentType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
|
|||
@Slf4j |
|||
@RuleNode(type = ComponentType.ENRICHMENT, |
|||
name = "customer details", |
|||
configClazz = TbGetCustomerDetailsNodeConfiguration.class, |
|||
nodeDescription = "Adds fields from Customer details to the message body or metadata", |
|||
nodeDetails = "If checkbox: <b>Add selected details to the message metadata</b> is selected, existing fields will be added to the message metadata instead of message data.<br><br>" + |
|||
"<b>Note:</b> only Device, Asset, and Entity View type are allowed.<br><br>" + |
|||
"If the originator of the message is not assigned to Customer, or originator type is not supported - Message will be forwarded to <b>Failure</b> chain, otherwise, <b>Success</b> chain will be used.", |
|||
uiResources = {"static/rulenode/rulenode-core-config.js"}, |
|||
configDirective = "tbEnrichmentNodeEntityDetailsConfig") |
|||
public class TbGetCustomerDetailsNode extends TbAbstractGetEntityDetailsNode<TbGetCustomerDetailsNodeConfiguration> { |
|||
|
|||
private static final String CUSTOMER_PREFIX = "customer_"; |
|||
|
|||
@Override |
|||
protected TbGetCustomerDetailsNodeConfiguration loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { |
|||
return TbNodeUtils.convert(configuration, TbGetCustomerDetailsNodeConfiguration.class); |
|||
} |
|||
|
|||
@Override |
|||
protected TbMsg getDetails(TbContext ctx, TbMsg msg) { |
|||
return getCustomerTbMsg(ctx, msg, getDataAsJson(msg)); |
|||
} |
|||
|
|||
private TbMsg getCustomerTbMsg(TbContext ctx, TbMsg msg, MessageData messageData) { |
|||
JsonElement resultObject = null; |
|||
if (!config.getDetailsList().isEmpty()) { |
|||
for (EntityDetails entityDetails : config.getDetailsList()) { |
|||
resultObject = addContactProperties(messageData.getData(), getCustomer(ctx, msg), entityDetails, CUSTOMER_PREFIX); |
|||
} |
|||
return transformMsg(ctx, msg, resultObject, messageData); |
|||
} else { |
|||
return msg; |
|||
} |
|||
} |
|||
|
|||
private Customer getCustomer(TbContext ctx, TbMsg msg) { |
|||
switch (msg.getOriginator().getEntityType()) { |
|||
case DEVICE: |
|||
Device device = ctx.getDeviceService().findDeviceById(ctx.getTenantId(), new DeviceId(msg.getOriginator().getId())); |
|||
if (!device.getCustomerId().isNullUid()) { |
|||
return ctx.getCustomerService().findCustomerById(ctx.getTenantId(), device.getCustomerId()); |
|||
} else { |
|||
throw new RuntimeException("Device with name '" + device.getName() + "' is not assigned to Customer."); |
|||
} |
|||
case ASSET: |
|||
Asset asset = ctx.getAssetService().findAssetById(ctx.getTenantId(), new AssetId(msg.getOriginator().getId())); |
|||
if (!asset.getCustomerId().isNullUid()) { |
|||
return ctx.getCustomerService().findCustomerById(ctx.getTenantId(), asset.getCustomerId()); |
|||
} else { |
|||
throw new RuntimeException("Asset with name '" + asset.getName() + "' is not assigned to Customer."); |
|||
} |
|||
case ENTITY_VIEW: |
|||
EntityView entityView = ctx.getEntityViewService().findEntityViewById(ctx.getTenantId(), new EntityViewId(msg.getOriginator().getId())); |
|||
if (!entityView.getCustomerId().isNullUid()) { |
|||
return ctx.getCustomerService().findCustomerById(ctx.getTenantId(), entityView.getCustomerId()); |
|||
} else { |
|||
throw new RuntimeException("EntityView with name '" + entityView.getName() + "' is not assigned to Customer."); |
|||
} |
|||
default: |
|||
throw new RuntimeException("Entity with entityType '" + msg.getOriginator().getEntityType() + "' is not supported."); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* 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.metadata; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.rule.engine.api.NodeConfiguration; |
|||
|
|||
import java.util.Collections; |
|||
|
|||
@Data |
|||
public class TbGetCustomerDetailsNodeConfiguration extends TbAbstractGetEntityDetailsNodeConfiguration implements NodeConfiguration<TbGetCustomerDetailsNodeConfiguration> { |
|||
|
|||
|
|||
@Override |
|||
public TbGetCustomerDetailsNodeConfiguration defaultConfiguration() { |
|||
TbGetCustomerDetailsNodeConfiguration configuration = new TbGetCustomerDetailsNodeConfiguration(); |
|||
configuration.setDetailsList(Collections.emptyList()); |
|||
return configuration; |
|||
} |
|||
} |
|||
@ -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.metadata; |
|||
|
|||
import com.google.gson.JsonElement; |
|||
import com.google.gson.JsonObject; |
|||
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.TbNodeConfiguration; |
|||
import org.thingsboard.rule.engine.api.TbNodeException; |
|||
import org.thingsboard.rule.engine.api.util.TbNodeUtils; |
|||
import org.thingsboard.rule.engine.util.EntityDetails; |
|||
import org.thingsboard.server.common.data.ContactBased; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.plugin.ComponentType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
|
|||
@Slf4j |
|||
@RuleNode(type = ComponentType.ENRICHMENT, |
|||
name = "tenant details", |
|||
configClazz = TbGetTenantDetailsNodeConfiguration.class, |
|||
nodeDescription = "Adds fields from Tenant details to the message body or metadata", |
|||
nodeDetails = "If checkbox: <b>Add selected details to the message metadata</b> is selected, existing fields will be added to the message metadata instead of message data.<br><br>" + |
|||
"<b>Note:</b> only Device, Asset, and Entity View type are allowed.<br><br>" + |
|||
"If the originator of the message is not assigned to Tenant, or originator type is not supported - Message will be forwarded to <b>Failure</b> chain, otherwise, <b>Success</b> chain will be used.", |
|||
uiResources = {"static/rulenode/rulenode-core-config.js"}, |
|||
configDirective = "tbEnrichmentNodeEntityDetailsConfig") |
|||
public class TbGetTenantDetailsNode extends TbAbstractGetEntityDetailsNode<TbGetTenantDetailsNodeConfiguration> { |
|||
|
|||
private static final String TENANT_PREFIX = "tenant_"; |
|||
|
|||
@Override |
|||
protected TbGetTenantDetailsNodeConfiguration loadGetEntityDetailsNodeConfiguration(TbNodeConfiguration configuration) throws TbNodeException { |
|||
return TbNodeUtils.convert(configuration, TbGetTenantDetailsNodeConfiguration.class); |
|||
} |
|||
|
|||
@Override |
|||
protected TbMsg getDetails(TbContext ctx, TbMsg msg) { |
|||
return getTenantTbMsg(ctx, msg, getDataAsJson(msg)); |
|||
} |
|||
|
|||
private TbMsg getTenantTbMsg(TbContext ctx, TbMsg msg, MessageData messageData) { |
|||
JsonElement resultObject = null; |
|||
Tenant tenant = ctx.getTenantService().findTenantById(ctx.getTenantId()); |
|||
if (!config.getDetailsList().isEmpty()) { |
|||
for (EntityDetails entityDetails : config.getDetailsList()) { |
|||
resultObject = addContactProperties(messageData.getData(), tenant, entityDetails, TENANT_PREFIX); |
|||
} |
|||
return transformMsg(ctx, msg, resultObject, messageData); |
|||
} else { |
|||
return msg; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* 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.metadata; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.rule.engine.api.NodeConfiguration; |
|||
|
|||
import java.util.Collections; |
|||
|
|||
@Data |
|||
public class TbGetTenantDetailsNodeConfiguration extends TbAbstractGetEntityDetailsNodeConfiguration implements NodeConfiguration<TbGetTenantDetailsNodeConfiguration> { |
|||
|
|||
|
|||
@Override |
|||
public TbGetTenantDetailsNodeConfiguration defaultConfiguration() { |
|||
TbGetTenantDetailsNodeConfiguration configuration = new TbGetTenantDetailsNodeConfiguration(); |
|||
configuration.setDetailsList(Collections.emptyList()); |
|||
return configuration; |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
/** |
|||
* 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.util; |
|||
|
|||
public enum EntityDetails { |
|||
|
|||
COUNTRY, CITY, STATE, ZIP, ADDRESS, ADDRESS2, PHONE, EMAIL, ADDITIONAL_INFO |
|||
|
|||
} |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1,23 @@ |
|||
/** |
|||
* 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. |
|||
*/ |
|||
|
|||
@import "../../scss/constants"; |
|||
|
|||
md-card.settings-card { |
|||
@media (min-width: $layout-breakpoint-sm) { |
|||
width: 60%; |
|||
} |
|||
} |
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue