Browse Source

Merge remote-tracking branch 'upstream/master'

pull/2282/head
Vladyslav_Prykhodko 7 years ago
parent
commit
5a9f4e001e
  1. 2
      application/pom.xml
  2. 2
      application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java
  3. 5
      application/src/main/java/org/thingsboard/server/controller/DeviceController.java
  4. 15
      application/src/main/java/org/thingsboard/server/controller/RpcController.java
  5. 3
      application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
  6. 4
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  7. 3
      application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java
  8. 13
      application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java
  9. 9
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  10. 3
      application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java
  11. 108
      application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java
  12. 46
      application/src/main/java/org/thingsboard/server/service/script/JsStatCallback.java
  13. 5
      application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java
  14. 3
      application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java
  15. 39
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  16. 5
      application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java
  17. 3
      application/src/main/java/org/thingsboard/server/service/transaction/BaseRuleChainTransactionService.java
  18. 3
      application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java
  19. 3
      application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java
  20. 9
      application/src/main/resources/thingsboard.yml
  21. 2
      common/dao-api/pom.xml
  22. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  23. 2
      common/data/pom.xml
  24. 4
      common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java
  25. 2
      common/message/pom.xml
  26. 2
      common/pom.xml
  27. 2
      common/queue/pom.xml
  28. 2
      common/transport/coap/pom.xml
  29. 2
      common/transport/http/pom.xml
  30. 2
      common/transport/mqtt/pom.xml
  31. 2
      common/transport/pom.xml
  32. 6
      common/transport/transport-api/pom.xml
  33. 27
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java
  34. 3
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java
  35. 1
      common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java
  36. 2
      common/util/pom.xml
  37. 54
      common/util/src/main/java/org/thingsboard/common/util/ThingsBoardThreadFactory.java
  38. 2
      dao/pom.xml
  39. 36
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  40. 3
      dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java
  41. 12
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  42. 1
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  43. 27
      dao/src/main/java/org/thingsboard/server/dao/model/nosql/AlarmEntity.java
  44. 19
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmEntity.java
  45. 3
      dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java
  46. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/ScheduledLogExecutorComponent.java
  47. 3
      dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java
  48. 62
      dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvInsertRepository.java
  49. 38
      dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java
  50. 33
      dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleInsertRepository.java
  51. 4
      dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java
  52. 2
      dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/JpaTimeseriesDao.java
  53. 58
      dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/PsqlLatestInsertRepository.java
  54. 33
      dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/PsqlTimeseriesInsertRepository.java
  55. 5
      dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java
  56. 1
      dao/src/main/resources/cassandra/schema-entities.cql
  57. 1
      dao/src/main/resources/sql/schema-entities.sql
  58. 2
      msa/black-box-tests/pom.xml
  59. 23
      msa/js-executor/api/jsInvokeMessageProcessor.js
  60. 1
      msa/js-executor/config/custom-environment-variables.yml
  61. 1
      msa/js-executor/config/default.yml
  62. 2
      msa/js-executor/package.json
  63. 2
      msa/js-executor/pom.xml
  64. 2
      msa/pom.xml
  65. 2
      msa/tb-node/pom.xml
  66. 2
      msa/tb/pom.xml
  67. 2
      msa/transport/coap/pom.xml
  68. 2
      msa/transport/http/pom.xml
  69. 2
      msa/transport/mqtt/pom.xml
  70. 2
      msa/transport/pom.xml
  71. 2
      msa/web-ui/package.json
  72. 2
      msa/web-ui/pom.xml
  73. 4
      netty-mqtt/pom.xml
  74. 11
      pom.xml
  75. 2
      rule-engine/pom.xml
  76. 2
      rule-engine/rule-engine-api/pom.xml
  77. 2
      rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java
  78. 2
      rule-engine/rule-engine-components/pom.xml
  79. 15
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java
  80. 11
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java
  81. 8
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeConfiguration.java
  82. 10
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  83. 38
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java
  84. 2
      tools/pom.xml
  85. 29
      tools/src/main/java/org/thingsboard/client/tools/RestClient.java
  86. 2
      transport/coap/pom.xml
  87. 2
      transport/http/pom.xml
  88. 2
      transport/mqtt/pom.xml
  89. 2
      transport/pom.xml
  90. 2
      ui/package.json
  91. 2
      ui/pom.xml
  92. 37
      ui/src/app/api/alarm.service.js
  93. 6
      ui/src/app/api/subscription.js
  94. 8
      ui/src/app/components/widget/widget-config.directive.js
  95. 75
      ui/src/app/components/widget/widget-config.tpl.html
  96. 4
      ui/src/app/components/widget/widget.controller.js
  97. 8
      ui/src/app/locale/locale.constant-en_US.json
  98. 8
      ui/src/app/locale/locale.constant-ru_RU.json
  99. 8
      ui/src/app/locale/locale.constant-uk_UA.json

2
application/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>application</artifactId>

2
application/src/main/java/org/thingsboard/server/actors/service/DefaultActorService.java

@ -95,8 +95,6 @@ public class DefaultActorService implements ActorService {
private ActorRef rpcManagerActor;
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
@PostConstruct
public void initActorSystem() {
log.info("Initializing Actor system.");

5
application/src/main/java/org/thingsboard/server/controller/DeviceController.java

@ -82,7 +82,8 @@ public class DeviceController extends BaseController {
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/device", method = RequestMethod.POST)
@ResponseBody
public Device saveDevice(@RequestBody Device device) throws ThingsboardException {
public Device saveDevice(@RequestBody Device device,
@RequestParam(name = "accessToken", required = false) String accessToken) throws ThingsboardException {
try {
device.setTenantId(getCurrentUser().getTenantId());
@ -91,7 +92,7 @@ public class DeviceController extends BaseController {
accessControlService.checkPermission(getCurrentUser(), Resource.DEVICE, operation,
device.getId(), device);
Device savedDevice = checkNotNull(deviceService.saveDevice(device));
Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
actorService
.onDeviceNameOrTypeUpdate(

15
application/src/main/java/org/thingsboard/server/controller/RpcController.java

@ -31,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
@ -76,20 +77,6 @@ public class RpcController extends BaseController {
@Autowired
private AccessValidator accessValidator;
private ExecutorService executor;
@PostConstruct
public void initExecutor() {
executor = Executors.newSingleThreadExecutor();
}
@PreDestroy
public void shutdownExecutor() {
if (executor != null) {
executor.shutdownNow();
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/oneway/{deviceId}", method = RequestMethod.POST)
@ResponseBody

3
application/src/main/java/org/thingsboard/server/controller/TelemetryController.java

@ -37,6 +37,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
@ -108,7 +109,7 @@ public class TelemetryController extends BaseController {
@PostConstruct
public void initExecutor() {
executor = Executors.newSingleThreadExecutor();
executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("telemetry-controller"));
}
@PreDestroy

4
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -123,6 +123,10 @@ public class ThingsboardInstallService {
log.info("Upgrading ThingsBoard from version 2.4.1 to 2.4.2 ...");
databaseUpgradeService.upgradeDatabase("2.4.1");
case "2.4.2.1":
log.info("Upgrading ThingsBoard from version 2.4.2.1 to 2.4.3 ...");
databaseUpgradeService.upgradeDatabase("2.4.2.1");
log.info("Updating system data...");

3
application/src/main/java/org/thingsboard/server/service/cluster/discovery/ZkDiscoveryService.java

@ -41,6 +41,7 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService;
@ -114,7 +115,7 @@ public class ZkDiscoveryService implements DiscoveryService, PathChildrenCacheLi
Assert.notNull(zkConnectionTimeout, MiscUtils.missingProperty("zk.connection_timeout_ms"));
Assert.notNull(zkSessionTimeout, MiscUtils.missingProperty("zk.session_timeout_ms"));
reconnectExecutorService = Executors.newSingleThreadExecutor();
reconnectExecutorService = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("zk-discovery"));
log.info("Initializing discovery service using ZK connect string: {}", zkUrl);

13
application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java

@ -271,8 +271,21 @@ public class CassandraDatabaseUpgradeService implements DatabaseUpgradeService {
log.info("Updating schema ...");
String updateAssetTableStmt = "alter table asset add label text";
try {
log.info("Updating assets ...");
cluster.getSession().execute(updateAssetTableStmt);
Thread.sleep(2500);
log.info("Assets updated.");
} catch (InvalidQueryException e) {}
log.info("Schema updated.");
break;
case "2.4.2.1":
log.info("Updating schema ...");
String updateAlarmTableStmt = "alter table alarm add propagate_relation_types text";
try {
log.info("Updating alarms ...");
cluster.getSession().execute(updateAlarmTableStmt);
Thread.sleep(2500);
log.info("Alarms updated.");
} catch (InvalidQueryException e) {}
log.info("Schema updated.");
break;

9
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -196,6 +196,15 @@ public class SqlDatabaseUpgradeService implements DatabaseUpgradeService {
log.info("Schema updated.");
}
break;
case "2.4.2.1":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Updating schema ...");
try {
conn.createStatement().execute("ALTER TABLE alarm ADD COLUMN propagate_relation_types varchar"); //NOSONAR, ignoring because method used to execute thingsboard database upgrade script
} catch (Exception e) {}
log.info("Schema updated.");
}
break;
default:
throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion);
}

3
application/src/main/java/org/thingsboard/server/service/rpc/DefaultDeviceRpcService.java

@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.RpcError;
import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg;
import org.thingsboard.server.actors.service.ActorService;
@ -83,7 +84,7 @@ public class DefaultDeviceRpcService implements DeviceRpcService {
@PostConstruct
public void initExecutor() {
rpcCallBackExecutor = Executors.newSingleThreadScheduledExecutor();
rpcCallBackExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("rpc-callback"));
}
@PreDestroy

108
application/src/main/java/org/thingsboard/server/service/script/AbstractNashornJsInvokeService.java

@ -15,21 +15,34 @@
*/
package org.thingsboard.server.service.script;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import delight.nashornsandbox.NashornSandbox;
import delight.nashornsandbox.NashornSandboxes;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeService {
@ -37,9 +50,46 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
private NashornSandbox sandbox;
private ScriptEngine engine;
private ExecutorService monitorExecutorService;
private ScheduledExecutorService timeoutExecutorService;
private final AtomicInteger jsPushedMsgs = new AtomicInteger(0);
private final AtomicInteger jsInvokeMsgs = new AtomicInteger(0);
private final AtomicInteger jsEvalMsgs = new AtomicInteger(0);
private final AtomicInteger jsFailedMsgs = new AtomicInteger(0);
private final AtomicInteger jsTimeoutMsgs = new AtomicInteger(0);
private final FutureCallback<UUID> evalCallback = new JsStatCallback<UUID>(jsEvalMsgs, jsTimeoutMsgs, jsFailedMsgs);
private final FutureCallback<Object> invokeCallback = new JsStatCallback<Object>(jsInvokeMsgs, jsTimeoutMsgs, jsFailedMsgs);
@Autowired
@Getter
private JsExecutorService jsExecutor;
@Value("${js.local.max_requests_timeout:0}")
private long maxRequestsTimeout;
@Value("${js.local.stats.enabled:false}")
private boolean statsEnabled;
@Scheduled(fixedDelayString = "${js.remote.stats.print_interval_ms:10000}")
public void printStats() {
if (statsEnabled) {
int pushedMsgs = jsPushedMsgs.getAndSet(0);
int invokeMsgs = jsInvokeMsgs.getAndSet(0);
int evalMsgs = jsEvalMsgs.getAndSet(0);
int failed = jsFailedMsgs.getAndSet(0);
int timedOut = jsTimeoutMsgs.getAndSet(0);
if (pushedMsgs > 0 || invokeMsgs > 0 || evalMsgs > 0 || failed > 0 || timedOut > 0) {
log.info("Nashorn JS Invoke Stats: pushed [{}] received [{}] invoke [{}] eval [{}] failed [{}] timedOut [{}]",
pushedMsgs, invokeMsgs + evalMsgs, invokeMsgs, evalMsgs, failed, timedOut);
}
}
}
@PostConstruct
public void init() {
if (maxRequestsTimeout > 0) {
timeoutExecutorService = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nashorn-js-timeout"));
}
if (useJsSandbox()) {
sandbox = NashornSandboxes.create();
monitorExecutorService = Executors.newWorkStealingPool(getMonitorThreadPoolSize());
@ -59,6 +109,9 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
if (monitorExecutorService != null) {
monitorExecutorService.shutdownNow();
}
if (timeoutExecutorService != null) {
timeoutExecutorService.shutdownNow();
}
}
protected abstract boolean useJsSandbox();
@ -69,34 +122,49 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer
@Override
protected ListenableFuture<UUID> doEval(UUID scriptId, String functionName, String jsScript) {
try {
if (useJsSandbox()) {
sandbox.eval(jsScript);
} else {
engine.eval(jsScript);
jsPushedMsgs.incrementAndGet();
ListenableFuture<UUID> result = jsExecutor.executeAsync(() -> {
try {
if (useJsSandbox()) {
sandbox.eval(jsScript);
} else {
engine.eval(jsScript);
}
scriptIdToNameMap.put(scriptId, functionName);
return scriptId;
} catch (Exception e) {
log.warn("Failed to compile JS script: {}", e.getMessage(), e);
throw new ExecutionException(e);
}
scriptIdToNameMap.put(scriptId, functionName);
} catch (Exception e) {
log.warn("Failed to compile JS script: {}", e.getMessage(), e);
return Futures.immediateFailedFuture(e);
});
if (maxRequestsTimeout > 0) {
result = Futures.withTimeout(result, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
}
return Futures.immediateFuture(scriptId);
Futures.addCallback(result, evalCallback);
return result;
}
@Override
protected ListenableFuture<Object> doInvokeFunction(UUID scriptId, String functionName, Object[] args) {
try {
Object result;
if (useJsSandbox()) {
result = sandbox.getSandboxedInvocable().invokeFunction(functionName, args);
} else {
result = ((Invocable) engine).invokeFunction(functionName, args);
jsPushedMsgs.incrementAndGet();
ListenableFuture<Object> result = jsExecutor.executeAsync(() -> {
try {
if (useJsSandbox()) {
return sandbox.getSandboxedInvocable().invokeFunction(functionName, args);
} else {
return ((Invocable) engine).invokeFunction(functionName, args);
}
} catch (Exception e) {
onScriptExecutionError(scriptId);
throw new ExecutionException(e);
}
return Futures.immediateFuture(result);
} catch (Exception e) {
onScriptExecutionError(scriptId);
return Futures.immediateFailedFuture(e);
});
if (maxRequestsTimeout > 0) {
result = Futures.withTimeout(result, maxRequestsTimeout, TimeUnit.MILLISECONDS, timeoutExecutorService);
}
Futures.addCallback(result, invokeCallback);
return result;
}
protected void doRelease(UUID scriptId, String functionName) throws ScriptException {

46
application/src/main/java/org/thingsboard/server/service/script/JsStatCallback.java

@ -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.service.script;
import com.google.common.util.concurrent.FutureCallback;
import lombok.AllArgsConstructor;
import javax.annotation.Nullable;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
@AllArgsConstructor
public class JsStatCallback<T> implements FutureCallback<T> {
private final AtomicInteger jsSuccessMsgs;
private final AtomicInteger jsTimeoutMsgs;
private final AtomicInteger jsFailedMsgs;
@Override
public void onSuccess(@Nullable T result) {
jsSuccessMsgs.incrementAndGet();
}
@Override
public void onFailure(Throwable t) {
if (t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException)) {
jsTimeoutMsgs.incrementAndGet();
} else {
jsFailedMsgs.incrementAndGet();
}
}
}

5
application/src/main/java/org/thingsboard/server/service/script/RuleNodeJsScriptEngine.java

@ -139,6 +139,11 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
return executeScript(msg);
}
@Override
public ListenableFuture<JsonNode> executeJsonAsync(TbMsg msg) throws ScriptException {
return executeScriptAsync(msg);
}
@Override
public String executeToString(TbMsg msg) throws ScriptException {
JsonNode result = executeScript(msg);

3
application/src/main/java/org/thingsboard/server/service/security/AccessValidator.java

@ -24,6 +24,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
@ -105,7 +106,7 @@ public class AccessValidator {
@PostConstruct
public void initExecutor() {
executor = Executors.newSingleThreadExecutor();
executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("access-validator"));
}
@PreDestroy

39
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -31,6 +31,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.actors.service.ActorService;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
@ -140,11 +141,13 @@ public class DefaultDeviceStateService implements DeviceStateService {
private ListeningScheduledExecutorService queueExecutor;
private ConcurrentMap<TenantId, Set<DeviceId>> tenantDevices = new ConcurrentHashMap<>();
private ConcurrentMap<DeviceId, DeviceStateData> deviceStates = new ConcurrentHashMap<>();
private ConcurrentMap<DeviceId, Long> deviceLastReportedActivity = new ConcurrentHashMap<>();
private ConcurrentMap<DeviceId, Long> deviceLastSavedActivity = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
// Should be always single threaded due to absence of locks.
queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor());
queueExecutor = MoreExecutors.listeningDecorator(Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("device-state")));
queueExecutor.submit(this::initStateFromDB);
queueExecutor.scheduleAtFixedRate(this::updateState, new Random().nextInt(defaultStateCheckIntervalInSec), defaultStateCheckIntervalInSec, TimeUnit.SECONDS);
//TODO: schedule persistence in v2.1;
@ -174,6 +177,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
@Override
public void onDeviceActivity(DeviceId deviceId) {
deviceLastReportedActivity.put(deviceId, System.currentTimeMillis());
queueExecutor.submit(() -> onDeviceActivitySync(deviceId));
}
@ -244,6 +248,8 @@ public class DefaultDeviceStateService implements DeviceStateService {
tenantDeviceSet.remove(device.getId());
}
deviceStates.remove(device.getId());
deviceLastReportedActivity.remove(device.getId());
deviceLastSavedActivity.remove(device.getId());
}
}
try {
@ -289,7 +295,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
private void updateState() {
long ts = System.currentTimeMillis();
Set<DeviceId> deviceIds = new HashSet<>(deviceStates.keySet());
log.info("Calculating state updates for {} devices", deviceStates.size());
log.debug("Calculating state updates for {} devices", deviceStates.size());
for (DeviceId deviceId : deviceIds) {
DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
if (stateData != null) {
@ -304,6 +310,8 @@ public class DefaultDeviceStateService implements DeviceStateService {
} else {
log.debug("[{}] Device that belongs to other server is detected and removed.", deviceId);
deviceStates.remove(deviceId);
deviceLastReportedActivity.remove(deviceId);
deviceLastSavedActivity.remove(deviceId);
}
}
}
@ -329,17 +337,20 @@ public class DefaultDeviceStateService implements DeviceStateService {
}
private void onDeviceActivitySync(DeviceId deviceId) {
DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
if (stateData != null) {
DeviceState state = stateData.getState();
long ts = System.currentTimeMillis();
stateData.getState().setLastActivityTime(ts);
pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
save(deviceId, LAST_ACTIVITY_TIME, ts);
if (!state.isActive()) {
state.setActive(true);
save(deviceId, ACTIVITY_STATE, state.isActive());
long lastReportedActivity = deviceLastReportedActivity.getOrDefault(deviceId, 0L);
long lastSavedActivity = deviceLastSavedActivity.getOrDefault(deviceId, 0L);
if (lastReportedActivity > 0 && lastReportedActivity > lastSavedActivity) {
DeviceStateData stateData = getOrFetchDeviceStateData(deviceId);
if (stateData != null) {
DeviceState state = stateData.getState();
stateData.getState().setLastActivityTime(lastReportedActivity);
pushRuleEngineMessage(stateData, ACTIVITY_EVENT);
save(deviceId, LAST_ACTIVITY_TIME, lastReportedActivity);
deviceLastSavedActivity.put(deviceId, lastReportedActivity);
if (!state.isActive()) {
state.setActive(true);
save(deviceId, ACTIVITY_STATE, state.isActive());
}
}
}
}
@ -430,6 +441,8 @@ public class DefaultDeviceStateService implements DeviceStateService {
Optional<ServerAddress> address = routingService.resolveById(deviceId);
if (!address.isPresent()) {
deviceStates.remove(deviceId);
deviceLastReportedActivity.remove(deviceId);
deviceLastSavedActivity.remove(deviceId);
Set<DeviceId> deviceIds = tenantDevices.get(tenantId);
if (deviceIds != null) {
deviceIds.remove(deviceId);

5
application/src/main/java/org/thingsboard/server/service/telemetry/DefaultTelemetrySubscriptionService.java

@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg;
import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.server.actors.service.ActorService;
@ -110,8 +111,8 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio
@PostConstruct
public void initExecutor() {
tsCallBackExecutor = Executors.newSingleThreadExecutor();
wsCallBackExecutor = Executors.newSingleThreadExecutor();
tsCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("ts-sub-callback"));
wsCallBackExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("ws-sub-callback"));
}
@PreDestroy

3
application/src/main/java/org/thingsboard/server/service/transaction/BaseRuleChainTransactionService.java

@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.rule.engine.api.RuleChainTransactionService;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.msg.TbMsg;
@ -71,7 +72,7 @@ public class BaseRuleChainTransactionService implements RuleChainTransactionServ
@PostConstruct
public void init() {
timeoutExecutor = Executors.newSingleThreadExecutor();
timeoutExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("rule-chain-transaction"));
executeOnTimeout();
}

3
application/src/main/java/org/thingsboard/server/service/transport/RemoteRuleEngineTransportService.java

@ -32,6 +32,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.actors.ActorSystemContext;
import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceActorToTransportMsg;
@ -103,7 +104,7 @@ public class RemoteRuleEngineTransportService implements RuleEngineTransportServ
private TBKafkaConsumerTemplate<ToRuleEngineMsg> ruleEngineConsumer;
private TBKafkaProducerTemplate<ToTransportMsg> notificationsProducer;
private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-main-consumer"));
private volatile boolean stopped = false;

3
application/src/main/java/org/thingsboard/server/service/update/DefaultUpdateService.java

@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.UpdateMessage;
import javax.annotation.PostConstruct;
@ -50,7 +51,7 @@ public class DefaultUpdateService implements UpdateService {
@Value("${updates.enabled}")
private boolean updatesEnabled;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("tb-update-service"));
private ScheduledFuture checkUpdatesFuture = null;
private RestTemplate restClient = new RestTemplate();

9
application/src/main/resources/thingsboard.yml

@ -195,10 +195,6 @@ cassandra:
# SQL configuration parameters
sql:
# Specify executor service type used to perform timeseries insert tasks: SINGLE or FIXED
ts_inserts_executor_type: "${SQL_TS_INSERTS_EXECUTOR_TYPE:fixed}"
# Specify thread pool size for FIXED executor service type
ts_inserts_fixed_thread_pool_size: "${SQL_TS_INSERTS_FIXED_THREAD_POOL_SIZE:200}"
# Specify batch size for persisting attribute updates
attributes:
batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:10000}"
@ -464,6 +460,11 @@ js:
max_cpu_time: "${LOCAL_JS_SANDBOX_MAX_CPU_TIME:3000}"
# Maximum allowed JavaScript execution errors before JavaScript will be blacklisted
max_errors: "${LOCAL_JS_SANDBOX_MAX_ERRORS:3}"
# JS Eval max request timeout. 0 - no timeout
max_requests_timeout: "${LOCAL_JS_MAX_REQUEST_TIMEOUT:0}"
stats:
enabled: "${TB_JS_LOCAL_STATS_ENABLED:false}"
print_interval_ms: "${TB_JS_LOCAL_STATS_PRINT_INTERVAL_MS:10000}"
# Remote JavaScript environment properties
remote:
# JS Eval request topic

2
common/dao-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -37,6 +37,8 @@ public interface DeviceService {
Device saveDevice(Device device);
Device saveDeviceWithAccessToken(Device device, String accessToken);
Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId);
Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId);

2
common/data/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

4
common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java

@ -26,6 +26,8 @@ import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List;
/**
* Created by ashvayka on 11.05.17.
*/
@ -45,6 +47,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId {
private long clearTs;
private transient JsonNode details;
private boolean propagate;
private List<String> propagateRelationTypes;
public Alarm() {
super();
@ -68,6 +71,7 @@ public class Alarm extends BaseData<AlarmId> implements HasName, HasTenantId {
this.clearTs = alarm.getClearTs();
this.details = alarm.getDetails();
this.propagate = alarm.isPropagate();
this.propagateRelationTypes = alarm.getPropagateRelationTypes();
}
@Override

2
common/message/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>common</artifactId>

2
common/queue/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

2
common/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>

2
common/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>

2
common/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>

2
common/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

6
common/transport/transport-api/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>
@ -44,6 +44,10 @@
<groupId>org.thingsboard.common</groupId>
<artifactId>message</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>util</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>queue</artifactId>

27
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/AbstractTransportService.java

@ -17,6 +17,7 @@ package org.thingsboard.server.common.transport.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
@ -27,6 +28,7 @@ import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.*;
@ -176,10 +178,23 @@ public abstract class AbstractTransportService implements TransportService {
sessions.remove(uuid);
sessionMD.getListener().onRemoteSessionCloseCommand(TransportProtos.SessionCloseNotificationProto.getDefaultInstance());
} else {
process(sessionMD.getSessionInfo(), TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(sessionMD.isSubscribedToAttributes())
.setRpcSubscription(sessionMD.isSubscribedToRPC())
.setLastActivityTime(sessionMD.getLastActivityTime()).build(), null);
if (sessionMD.getLastActivityTime() > sessionMD.getLastReportedActivityTime()) {
final long lastActivityTime = sessionMD.getLastActivityTime();
process(sessionMD.getSessionInfo(), TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(sessionMD.isSubscribedToAttributes())
.setRpcSubscription(sessionMD.isSubscribedToRPC())
.setLastActivityTime(sessionMD.getLastActivityTime()).build(), new TransportServiceCallback<Void>() {
@Override
public void onSuccess(Void msg) {
sessionMD.setLastReportedActivityTime(lastActivityTime);
}
@Override
public void onError(Throwable e) {
log.warn("[{}] Failed to report last activity time", uuid, e);
}
});
}
}
});
}
@ -286,9 +301,9 @@ public abstract class AbstractTransportService implements TransportService {
new TbRateLimits(perTenantLimitsConf);
new TbRateLimits(perDevicesLimitsConf);
}
this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor();
this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("transport-scheduler"));
this.transportCallbackExecutor = Executors.newWorkStealingPool(20);
this.schedulerExecutor.scheduleAtFixedRate(this::checkInactivityAndReportActivity, sessionReportTimeout, sessionReportTimeout, TimeUnit.MILLISECONDS);
this.schedulerExecutor.scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS);
}
public void destroy() {

3
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/RemoteTransportService.java

@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
@ -100,7 +101,7 @@ public class RemoteTransportService extends AbstractTransportService {
private TBKafkaProducerTemplate<ToRuleEngineMsg> ruleEngineProducer;
private TBKafkaConsumerTemplate<ToTransportMsg> mainConsumer;
private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor();
private ExecutorService mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("remote-transport-consumer"));
private volatile boolean stopped = false;

1
common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/service/SessionMetaData.java

@ -34,6 +34,7 @@ class SessionMetaData {
private ScheduledFuture scheduledFuture;
private volatile long lastActivityTime;
private volatile long lastReportedActivityTime;
private volatile boolean subscribedToAttributes;
private volatile boolean subscribedToRPC;

2
common/util/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>common</artifactId>
</parent>
<groupId>org.thingsboard.common</groupId>

54
common/util/src/main/java/org/thingsboard/common/util/ThingsBoardThreadFactory.java

@ -0,0 +1,54 @@
/**
* 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.common.util;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Copy of Executors.DefaultThreadFactory but with ability to set name of the pool
*/
public class ThingsBoardThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public static ThingsBoardThreadFactory forName(String name) {
return new ThingsBoardThreadFactory(name);
}
private ThingsBoardThreadFactory(String name) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = name + "-" +
poolNumber.getAndIncrement() +
"-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}

2
dao/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>dao</artifactId>

36
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -23,8 +23,9 @@ import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
@ -52,6 +53,7 @@ import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
@ -59,6 +61,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.thingsboard.server.dao.service.Validator.validateId;
@ -81,7 +84,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
@PostConstruct
public void startExecutor() {
readResultsProcessingExecutor = Executors.newCachedThreadPool();
readResultsProcessingExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("alarm-service"));
}
@PreDestroy
@ -154,8 +157,14 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
private List<EntityId> getParentEntities(Alarm alarm) throws InterruptedException, ExecutionException {
EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE, false));
return relationService.findByQuery(alarm.getTenantId(), query).get().stream().map(EntityRelation::getFrom).collect(Collectors.toList());
RelationsSearchParameters parameters = new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE, false);
query.setParameters(parameters);
List<String> propagateRelationTypes = alarm.getPropagateRelationTypes();
Stream<EntityRelation> relations = relationService.findByQuery(alarm.getTenantId(), query).get().stream();
if (!CollectionUtils.isEmpty(propagateRelationTypes)) {
relations = relations.filter(entityRelation -> propagateRelationTypes.contains(entityRelation.getType()));
}
return relations.map(EntityRelation::getFrom).collect(Collectors.toList());
}
private ListenableFuture<Alarm> updateAlarm(Alarm update) {
@ -360,13 +369,30 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
existing.setSeverity(alarm.getSeverity());
existing.setDetails(alarm.getDetails());
existing.setPropagate(existing.isPropagate() || alarm.isPropagate());
List<String> existingPropagateRelationTypes = existing.getPropagateRelationTypes();
List<String> newRelationTypes = alarm.getPropagateRelationTypes();
if (!CollectionUtils.isEmpty(newRelationTypes)) {
if (!CollectionUtils.isEmpty(existingPropagateRelationTypes)) {
existing.setPropagateRelationTypes(Stream.concat(existingPropagateRelationTypes.stream(), newRelationTypes.stream())
.distinct()
.collect(Collectors.toList()));
} else {
existing.setPropagateRelationTypes(newRelationTypes);
}
}
return existing;
}
private void updateRelations(Alarm alarm, AlarmStatus oldStatus, AlarmStatus newStatus) {
try {
List<EntityRelation> relations = relationService.findByToAsync(alarm.getTenantId(), alarm.getId(), RelationTypeGroup.ALARM).get();
Set<EntityId> parents = relations.stream().map(EntityRelation::getFrom).collect(Collectors.toSet());
List<String> propagateRelationTypes = alarm.getPropagateRelationTypes();
Stream<EntityRelation> relationStream = relations.stream();
if (!CollectionUtils.isEmpty(propagateRelationTypes)) {
relationStream = relationStream.filter(entityRelation -> propagateRelationTypes.contains(entityRelation.getType()));
}
Set<EntityId> parents = relationStream.map(EntityRelation::getFrom).collect(Collectors.toSet());
for (EntityId parentId : parents) {
updateAlarmRelation(alarm.getTenantId(), parentId, alarm.getId(), oldStatus, newStatus);
}

3
dao/src/main/java/org/thingsboard/server/dao/audit/CassandraAuditLogDao.java

@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.audit.AuditLog;
import org.thingsboard.server.common.data.id.CustomerId;
@ -117,7 +118,7 @@ public class CassandraAuditLogDao extends CassandraAbstractSearchTimeDao<AuditLo
throw new RuntimeException("Failed to parse partitioning property: " + partitioning + "!");
}
}
readResultsProcessingExecutor = Executors.newCachedThreadPool();
readResultsProcessingExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("audit-log"));
}
@PreDestroy

12
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -119,9 +119,19 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
return deviceOpt.orElse(null);
}
@CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
@Override
public Device saveDeviceWithAccessToken(Device device, String accessToken) {
return doSaveDevice(device, accessToken);
}
@CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
@Override
public Device saveDevice(Device device) {
return doSaveDevice(device, null);
}
private Device doSaveDevice(Device device, String accessToken) {
log.trace("Executing saveDevice [{}]", device);
deviceValidator.validate(device, Device::getTenantId);
Device savedDevice;
@ -143,7 +153,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(RandomStringUtils.randomAlphanumeric(20));
deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
}
return savedDevice;

1
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -231,6 +231,7 @@ public class ModelConstants {
public static final String ALARM_ACK_TS_PROPERTY = "ack_ts";
public static final String ALARM_CLEAR_TS_PROPERTY = "clear_ts";
public static final String ALARM_PROPAGATE_PROPERTY = "propagate";
public static final String ALARM_PROPAGATE_RELATION_TYPES = "propagate_relation_types";
public static final String ALARM_BY_ID_VIEW_NAME = "alarm_by_id";

27
dao/src/main/java/org/thingsboard/server/dao/model/nosql/AlarmEntity.java

@ -23,6 +23,8 @@ import com.datastax.driver.mapping.annotations.Table;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId;
@ -36,6 +38,8 @@ import org.thingsboard.server.dao.model.type.AlarmStatusCodec;
import org.thingsboard.server.dao.model.type.EntityTypeCodec;
import org.thingsboard.server.dao.model.type.JsonCodec;
import java.util.Arrays;
import java.util.Collections;
import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY;
@ -46,6 +50,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPE
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_RELATION_TYPES;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY;
@ -102,6 +107,9 @@ public final class AlarmEntity implements BaseEntity<Alarm> {
@Column(name = ALARM_PROPAGATE_PROPERTY)
private Boolean propagate;
@Column(name = ALARM_PROPAGATE_RELATION_TYPES)
private String propagateRelationTypes;
public AlarmEntity() {
super();
}
@ -125,6 +133,12 @@ public final class AlarmEntity implements BaseEntity<Alarm> {
this.ackTs = alarm.getAckTs();
this.clearTs = alarm.getClearTs();
this.details = alarm.getDetails();
this.details = alarm.getDetails();
if (!CollectionUtils.isEmpty(alarm.getPropagateRelationTypes())) {
this.propagateRelationTypes = String.join(",", alarm.getPropagateRelationTypes());
} else {
this.propagateRelationTypes = null;
}
}
public UUID getId() {
@ -231,6 +245,14 @@ public final class AlarmEntity implements BaseEntity<Alarm> {
this.propagate = propagate;
}
public String getPropagateRelationTypes() {
return propagateRelationTypes;
}
public void setPropagateRelationTypes(String propagateRelationTypes) {
this.propagateRelationTypes = propagateRelationTypes;
}
@Override
public Alarm toData() {
Alarm alarm = new Alarm(new AlarmId(id));
@ -248,6 +270,11 @@ public final class AlarmEntity implements BaseEntity<Alarm> {
alarm.setAckTs(ackTs);
alarm.setClearTs(clearTs);
alarm.setDetails(details);
if (!StringUtils.isEmpty(propagateRelationTypes)) {
alarm.setPropagateRelationTypes(Arrays.asList(propagateRelationTypes.split(",")));
} else {
alarm.setPropagateRelationTypes(Collections.emptyList());
}
return alarm;
}

19
dao/src/main/java/org/thingsboard/server/dao/model/sql/AlarmEntity.java

@ -21,6 +21,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.UUIDConverter;
import org.thingsboard.server.common.data.alarm.Alarm;
@ -40,6 +42,9 @@ import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Table;
import java.util.Arrays;
import java.util.Collections;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ACK_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_CLEAR_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_COLUMN_FAMILY_NAME;
@ -47,6 +52,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.ALARM_END_TS_PROPE
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_ID_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_ORIGINATOR_TYPE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_PROPAGATE_RELATION_TYPES;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_SEVERITY_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_START_TS_PROPERTY;
import static org.thingsboard.server.dao.model.ModelConstants.ALARM_STATUS_PROPERTY;
@ -99,6 +105,9 @@ public final class AlarmEntity extends BaseSqlEntity<Alarm> implements BaseEntit
@Column(name = ALARM_PROPAGATE_PROPERTY)
private Boolean propagate;
@Column(name = ALARM_PROPAGATE_RELATION_TYPES)
private String propagateRelationTypes;
public AlarmEntity() {
super();
}
@ -122,6 +131,11 @@ public final class AlarmEntity extends BaseSqlEntity<Alarm> implements BaseEntit
this.ackTs = alarm.getAckTs();
this.clearTs = alarm.getClearTs();
this.details = alarm.getDetails();
if (!CollectionUtils.isEmpty(alarm.getPropagateRelationTypes())) {
this.propagateRelationTypes = String.join(",", alarm.getPropagateRelationTypes());
} else {
this.propagateRelationTypes = null;
}
}
@Override
@ -141,6 +155,11 @@ public final class AlarmEntity extends BaseSqlEntity<Alarm> implements BaseEntit
alarm.setAckTs(ackTs);
alarm.setClearTs(clearTs);
alarm.setDetails(details);
if(!StringUtils.isEmpty(propagateRelationTypes)) {
alarm.setPropagateRelationTypes(Arrays.asList(propagateRelationTypes.split(",")));
} else {
alarm.setPropagateRelationTypes(Collections.emptyList());
}
return alarm;
}

3
dao/src/main/java/org/thingsboard/server/dao/nosql/CassandraAbstractAsyncDao.java

@ -20,6 +20,7 @@ import com.datastax.driver.core.ResultSetFuture;
import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
@ -36,7 +37,7 @@ public abstract class CassandraAbstractAsyncDao extends CassandraAbstractDao {
@PostConstruct
public void startExecutor() {
readResultsProcessingExecutor = Executors.newCachedThreadPool();
readResultsProcessingExecutor = Executors.newCachedThreadPool(ThingsBoardThreadFactory.forName("cassandra-callback"));
}
@PreDestroy

3
dao/src/main/java/org/thingsboard/server/dao/sql/ScheduledLogExecutorComponent.java

@ -16,6 +16,7 @@
package org.thingsboard.server.dao.sql;
import org.springframework.stereotype.Component;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@ -30,7 +31,7 @@ public class ScheduledLogExecutorComponent {
@PostConstruct
public void init() {
schedulerLogExecutor = Executors.newSingleThreadScheduledExecutor();
schedulerLogExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("sql-log"));
}
@PreDestroy

3
dao/src/main/java/org/thingsboard/server/dao/sql/TbSqlBlockingQueue.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import java.util.ArrayList;
import java.util.List;
@ -49,7 +50,7 @@ public class TbSqlBlockingQueue<E> implements TbSqlQueue<E> {
@Override
public void init(ScheduledLogExecutorComponent logExecutor, Consumer<List<E>> saveFunction) {
this.logExecutor = logExecutor;
executor = Executors.newSingleThreadExecutor();
executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("sql-queue-" + params.getLogName().toLowerCase()));
executor.submit(() -> {
String logName = params.getLogName();
int batchSize = params.getBatchSize();

62
dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvInsertRepository.java

@ -143,31 +143,32 @@ public abstract class AttributeKvInsertRepository {
int[] result = jdbcTemplate.batchUpdate(BATCH_UPDATE, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, replaceNullChars(entities.get(i).getStrValue()));
AttributeKvEntity kvEntity = entities.get(i);
ps.setString(1, replaceNullChars(kvEntity.getStrValue()));
if (entities.get(i).getLongValue() != null) {
ps.setLong(2, entities.get(i).getLongValue());
if (kvEntity.getLongValue() != null) {
ps.setLong(2, kvEntity.getLongValue());
} else {
ps.setNull(2, Types.BIGINT);
}
if (entities.get(i).getDoubleValue() != null) {
ps.setDouble(3, entities.get(i).getDoubleValue());
if (kvEntity.getDoubleValue() != null) {
ps.setDouble(3, kvEntity.getDoubleValue());
} else {
ps.setNull(3, Types.DOUBLE);
}
if (entities.get(i).getBooleanValue() != null) {
ps.setBoolean(4, entities.get(i).getBooleanValue());
if (kvEntity.getBooleanValue() != null) {
ps.setBoolean(4, kvEntity.getBooleanValue());
} else {
ps.setNull(4, Types.BOOLEAN);
}
ps.setLong(5, entities.get(i).getLastUpdateTs());
ps.setString(6, entities.get(i).getId().getEntityType().name());
ps.setString(7, entities.get(i).getId().getEntityId());
ps.setString(8, entities.get(i).getId().getAttributeType());
ps.setString(9, entities.get(i).getId().getAttributeKey());
ps.setLong(5, kvEntity.getLastUpdateTs());
ps.setString(6, kvEntity.getId().getEntityType().name());
ps.setString(7, kvEntity.getId().getEntityId());
ps.setString(8, kvEntity.getId().getAttributeType());
ps.setString(9, kvEntity.getId().getAttributeKey());
}
@Override
@ -193,39 +194,40 @@ public abstract class AttributeKvInsertRepository {
jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, insertEntities.get(i).getId().getEntityType().name());
ps.setString(2, insertEntities.get(i).getId().getEntityId());
ps.setString(3, insertEntities.get(i).getId().getAttributeType());
ps.setString(4, insertEntities.get(i).getId().getAttributeKey());
ps.setString(5, replaceNullChars(insertEntities.get(i).getStrValue()));
ps.setString(10, replaceNullChars(insertEntities.get(i).getStrValue()));
if (insertEntities.get(i).getLongValue() != null) {
ps.setLong(6, insertEntities.get(i).getLongValue());
ps.setLong(11, insertEntities.get(i).getLongValue());
AttributeKvEntity kvEntity = insertEntities.get(i);
ps.setString(1, kvEntity.getId().getEntityType().name());
ps.setString(2, kvEntity.getId().getEntityId());
ps.setString(3, kvEntity.getId().getAttributeType());
ps.setString(4, kvEntity.getId().getAttributeKey());
ps.setString(5, replaceNullChars(kvEntity.getStrValue()));
ps.setString(10, replaceNullChars(kvEntity.getStrValue()));
if (kvEntity.getLongValue() != null) {
ps.setLong(6, kvEntity.getLongValue());
ps.setLong(11, kvEntity.getLongValue());
} else {
ps.setNull(6, Types.BIGINT);
ps.setNull(11, Types.BIGINT);
}
if (insertEntities.get(i).getDoubleValue() != null) {
ps.setDouble(7, insertEntities.get(i).getDoubleValue());
ps.setDouble(12, insertEntities.get(i).getDoubleValue());
if (kvEntity.getDoubleValue() != null) {
ps.setDouble(7, kvEntity.getDoubleValue());
ps.setDouble(12, kvEntity.getDoubleValue());
} else {
ps.setNull(7, Types.DOUBLE);
ps.setNull(12, Types.DOUBLE);
}
if (insertEntities.get(i).getBooleanValue() != null) {
ps.setBoolean(8, insertEntities.get(i).getBooleanValue());
ps.setBoolean(13, insertEntities.get(i).getBooleanValue());
if (kvEntity.getBooleanValue() != null) {
ps.setBoolean(8, kvEntity.getBooleanValue());
ps.setBoolean(13, kvEntity.getBooleanValue());
} else {
ps.setNull(8, Types.BOOLEAN);
ps.setNull(13, Types.BOOLEAN);
}
ps.setLong(9, insertEntities.get(i).getLastUpdateTs());
ps.setLong(14, insertEntities.get(i).getLastUpdateTs());
ps.setLong(9, kvEntity.getLastUpdateTs());
ps.setLong(14, kvEntity.getLastUpdateTs());
}
@Override

38
dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java

@ -44,44 +44,6 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx
private static final String DESC_ORDER = "DESC";
@Value("${sql.ts_inserts_executor_type}")
private String insertExecutorType;
@Value("${sql.ts_inserts_fixed_thread_pool_size}")
private int insertFixedThreadPoolSize;
@Value("${spring.datasource.hikari.maximumPoolSize}")
private int maximumPoolSize;
protected ListeningExecutorService insertService;
@PostConstruct
void init() {
Optional<TsInsertExecutorType> executorTypeOptional = TsInsertExecutorType.parse(insertExecutorType);
TsInsertExecutorType executorType;
executorType = executorTypeOptional.orElse(TsInsertExecutorType.FIXED);
switch (executorType) {
case SINGLE:
insertService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
break;
case FIXED:
case CACHED:
int poolSize = insertFixedThreadPoolSize;
if (poolSize <= 0) {
poolSize = maximumPoolSize * 4;
}
insertService = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool(poolSize));
break;
}
}
@PreDestroy
void preDestroy() {
if (insertService != null) {
insertService.shutdown();
}
}
protected ListenableFuture<List<TsKvEntry>> processFindAllAsync(TenantId tenantId, EntityId entityId, List<ReadTsKvQuery> queries) {
List<ListenableFuture<List<TsKvEntry>>> futures = queries
.stream()

33
dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleInsertRepository.java

@ -57,34 +57,35 @@ public class TimescaleInsertRepository extends AbstractTimeseriesInsertRepositor
jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, entities.get(i).getTenantId());
ps.setString(2, entities.get(i).getEntityId());
ps.setString(3, entities.get(i).getKey());
ps.setLong(4, entities.get(i).getTs());
if (entities.get(i).getBooleanValue() != null) {
ps.setBoolean(5, entities.get(i).getBooleanValue());
ps.setBoolean(9, entities.get(i).getBooleanValue());
TimescaleTsKvEntity tsKvEntity = entities.get(i);
ps.setString(1, tsKvEntity.getTenantId());
ps.setString(2, tsKvEntity.getEntityId());
ps.setString(3, tsKvEntity.getKey());
ps.setLong(4, tsKvEntity.getTs());
if (tsKvEntity.getBooleanValue() != null) {
ps.setBoolean(5, tsKvEntity.getBooleanValue());
ps.setBoolean(9, tsKvEntity.getBooleanValue());
} else {
ps.setNull(5, Types.BOOLEAN);
ps.setNull(9, Types.BOOLEAN);
}
ps.setString(6, replaceNullChars(entities.get(i).getStrValue()));
ps.setString(10, replaceNullChars(entities.get(i).getStrValue()));
ps.setString(6, replaceNullChars(tsKvEntity.getStrValue()));
ps.setString(10, replaceNullChars(tsKvEntity.getStrValue()));
if (entities.get(i).getLongValue() != null) {
ps.setLong(7, entities.get(i).getLongValue());
ps.setLong(11, entities.get(i).getLongValue());
if (tsKvEntity.getLongValue() != null) {
ps.setLong(7, tsKvEntity.getLongValue());
ps.setLong(11, tsKvEntity.getLongValue());
} else {
ps.setNull(7, Types.BIGINT);
ps.setNull(11, Types.BIGINT);
}
if (entities.get(i).getDoubleValue() != null) {
ps.setDouble(8, entities.get(i).getDoubleValue());
ps.setDouble(12, entities.get(i).getDoubleValue());
if (tsKvEntity.getDoubleValue() != null) {
ps.setDouble(8, tsKvEntity.getDoubleValue());
ps.setDouble(12, tsKvEntity.getDoubleValue());
} else {
ps.setNull(8, Types.DOUBLE);
ps.setNull(12, Types.DOUBLE);

4
dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java

@ -170,12 +170,12 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements
@Override
public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
return insertService.submit(() -> null);
return Futures.immediateFuture(null);
}
@Override
public ListenableFuture<Void> saveLatest(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) {
return insertService.submit(() -> null);
return Futures.immediateFuture(null);
}
@Override

2
dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/JpaTimeseriesDao.java

@ -334,7 +334,7 @@ public class JpaTimeseriesDao extends AbstractSqlTimeseriesDao implements Timese
@Override
public ListenableFuture<Void> savePartition(TenantId tenantId, EntityId entityId, long tsKvEntryTs, String key, long ttl) {
return insertService.submit(() -> null);
return Futures.immediateFuture(null);
}
@Override

58
dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/PsqlLatestInsertRepository.java

@ -65,31 +65,32 @@ public class PsqlLatestInsertRepository extends AbstractLatestInsertRepository {
int[] result = jdbcTemplate.batchUpdate(BATCH_UPDATE, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setLong(1, entities.get(i).getTs());
TsKvLatestEntity tsKvLatestEntity = entities.get(i);
ps.setLong(1, tsKvLatestEntity.getTs());
if (entities.get(i).getBooleanValue() != null) {
ps.setBoolean(2, entities.get(i).getBooleanValue());
if (tsKvLatestEntity.getBooleanValue() != null) {
ps.setBoolean(2, tsKvLatestEntity.getBooleanValue());
} else {
ps.setNull(2, Types.BOOLEAN);
}
ps.setString(3, replaceNullChars(entities.get(i).getStrValue()));
ps.setString(3, replaceNullChars(tsKvLatestEntity.getStrValue()));
if (entities.get(i).getLongValue() != null) {
ps.setLong(4, entities.get(i).getLongValue());
if (tsKvLatestEntity.getLongValue() != null) {
ps.setLong(4, tsKvLatestEntity.getLongValue());
} else {
ps.setNull(4, Types.BIGINT);
}
if (entities.get(i).getDoubleValue() != null) {
ps.setDouble(5, entities.get(i).getDoubleValue());
if (tsKvLatestEntity.getDoubleValue() != null) {
ps.setDouble(5, tsKvLatestEntity.getDoubleValue());
} else {
ps.setNull(5, Types.DOUBLE);
}
ps.setString(6, entities.get(i).getEntityType().name());
ps.setString(7, entities.get(i).getEntityId());
ps.setString(8, entities.get(i).getKey());
ps.setString(6, tsKvLatestEntity.getEntityType().name());
ps.setString(7, tsKvLatestEntity.getEntityId());
ps.setString(8, tsKvLatestEntity.getKey());
}
@Override
@ -115,35 +116,36 @@ public class PsqlLatestInsertRepository extends AbstractLatestInsertRepository {
jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, insertEntities.get(i).getEntityType().name());
ps.setString(2, insertEntities.get(i).getEntityId());
ps.setString(3, insertEntities.get(i).getKey());
ps.setLong(4, insertEntities.get(i).getTs());
ps.setLong(9, insertEntities.get(i).getTs());
if (insertEntities.get(i).getBooleanValue() != null) {
ps.setBoolean(5, insertEntities.get(i).getBooleanValue());
ps.setBoolean(10, insertEntities.get(i).getBooleanValue());
TsKvLatestEntity tsKvLatestEntity = insertEntities.get(i);
ps.setString(1, tsKvLatestEntity.getEntityType().name());
ps.setString(2, tsKvLatestEntity.getEntityId());
ps.setString(3, tsKvLatestEntity.getKey());
ps.setLong(4, tsKvLatestEntity.getTs());
ps.setLong(9, tsKvLatestEntity.getTs());
if (tsKvLatestEntity.getBooleanValue() != null) {
ps.setBoolean(5, tsKvLatestEntity.getBooleanValue());
ps.setBoolean(10, tsKvLatestEntity.getBooleanValue());
} else {
ps.setNull(5, Types.BOOLEAN);
ps.setNull(10, Types.BOOLEAN);
}
ps.setString(6, replaceNullChars(entities.get(i).getStrValue()));
ps.setString(11, replaceNullChars(entities.get(i).getStrValue()));
ps.setString(6, replaceNullChars(tsKvLatestEntity.getStrValue()));
ps.setString(11, replaceNullChars(tsKvLatestEntity.getStrValue()));
if (insertEntities.get(i).getLongValue() != null) {
ps.setLong(7, insertEntities.get(i).getLongValue());
ps.setLong(12, insertEntities.get(i).getLongValue());
if (tsKvLatestEntity.getLongValue() != null) {
ps.setLong(7, tsKvLatestEntity.getLongValue());
ps.setLong(12, tsKvLatestEntity.getLongValue());
} else {
ps.setNull(7, Types.BIGINT);
ps.setNull(12, Types.BIGINT);
}
if (insertEntities.get(i).getDoubleValue() != null) {
ps.setDouble(8, insertEntities.get(i).getDoubleValue());
ps.setDouble(13, insertEntities.get(i).getDoubleValue());
if (tsKvLatestEntity.getDoubleValue() != null) {
ps.setDouble(8, tsKvLatestEntity.getDoubleValue());
ps.setDouble(13, tsKvLatestEntity.getDoubleValue());
} else {
ps.setNull(8, Types.DOUBLE);
ps.setNull(13, Types.DOUBLE);

33
dao/src/main/java/org/thingsboard/server/dao/sqlts/ts/PsqlTimeseriesInsertRepository.java

@ -99,34 +99,35 @@ public class PsqlTimeseriesInsertRepository extends AbstractTimeseriesInsertRepo
jdbcTemplate.batchUpdate(INSERT_OR_UPDATE, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, entities.get(i).getEntityType().name());
ps.setString(2, entities.get(i).getEntityId());
ps.setString(3, entities.get(i).getKey());
ps.setLong(4, entities.get(i).getTs());
if (entities.get(i).getBooleanValue() != null) {
ps.setBoolean(5, entities.get(i).getBooleanValue());
ps.setBoolean(9, entities.get(i).getBooleanValue());
TsKvEntity tsKvEntity = entities.get(i);
ps.setString(1, tsKvEntity.getEntityType().name());
ps.setString(2, tsKvEntity.getEntityId());
ps.setString(3, tsKvEntity.getKey());
ps.setLong(4, tsKvEntity.getTs());
if (tsKvEntity.getBooleanValue() != null) {
ps.setBoolean(5, tsKvEntity.getBooleanValue());
ps.setBoolean(9, tsKvEntity.getBooleanValue());
} else {
ps.setNull(5, Types.BOOLEAN);
ps.setNull(9, Types.BOOLEAN);
}
ps.setString(6, replaceNullChars(entities.get(i).getStrValue()));
ps.setString(10, replaceNullChars(entities.get(i).getStrValue()));
ps.setString(6, replaceNullChars(tsKvEntity.getStrValue()));
ps.setString(10, replaceNullChars(tsKvEntity.getStrValue()));
if (entities.get(i).getLongValue() != null) {
ps.setLong(7, entities.get(i).getLongValue());
ps.setLong(11, entities.get(i).getLongValue());
if (tsKvEntity.getLongValue() != null) {
ps.setLong(7, tsKvEntity.getLongValue());
ps.setLong(11, tsKvEntity.getLongValue());
} else {
ps.setNull(7, Types.BIGINT);
ps.setNull(11, Types.BIGINT);
}
if (entities.get(i).getDoubleValue() != null) {
ps.setDouble(8, entities.get(i).getDoubleValue());
ps.setDouble(12, entities.get(i).getDoubleValue());
if (tsKvEntity.getDoubleValue() != null) {
ps.setDouble(8, tsKvEntity.getDoubleValue());
ps.setDouble(12, tsKvEntity.getDoubleValue());
} else {
ps.setNull(8, Types.DOUBLE);
ps.setNull(12, Types.DOUBLE);

5
dao/src/main/java/org/thingsboard/server/dao/util/AbstractBufferedRateExecutor.java

@ -21,6 +21,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.tools.TbRateLimits;
import org.thingsboard.server.dao.nosql.CassandraStatementTask;
@ -67,9 +68,9 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend
this.concurrencyLimit = concurrencyLimit;
this.printQueriesFreq = printQueriesFreq;
this.queue = new LinkedBlockingDeque<>(queueLimit);
this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads);
this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-dispatcher"));
this.callbackExecutor = Executors.newWorkStealingPool(callbackThreads);
this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nosql-timeout"));
this.perTenantLimitsEnabled = perTenantLimitsEnabled;
this.perTenantLimitsConfiguration = perTenantLimitsConfiguration;
for (int i = 0; i < dispatcherThreads; i++) {

1
dao/src/main/resources/cassandra/schema-entities.cql

@ -306,6 +306,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.alarm (
clear_ts bigint,
details text,
propagate boolean,
propagate_relation_types text,
PRIMARY KEY ((tenant_id, originator_id, originator_type), type, id)
) WITH CLUSTERING ORDER BY ( type ASC, id DESC);

1
dao/src/main/resources/sql/schema-entities.sql

@ -34,6 +34,7 @@ CREATE TABLE IF NOT EXISTS alarm (
start_ts bigint,
status varchar(255),
tenant_id varchar(31),
propagate_relation_types varchar,
type varchar(255)
);

2
msa/black-box-tests/pom.xml

@ -21,7 +21,7 @@
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

23
msa/js-executor/api/jsInvokeMessageProcessor.js

@ -27,11 +27,13 @@ const config = require('config'),
const scriptBodyTraceFrequency = Number(config.get('script.script_body_trace_frequency'));
const useSandbox = config.get('script.use_sandbox') === 'true';
const maxActiveScripts = Number(config.get('script.max_active_scripts'));
function JsInvokeMessageProcessor(producer) {
this.producer = producer;
this.executor = new JsExecutor(useSandbox);
this.scriptMap = {};
this.scriptIds = [];
this.executedScriptsCounter = 0;
}
@ -70,7 +72,7 @@ JsInvokeMessageProcessor.prototype.processCompileRequest = function(requestId, r
this.executor.compileScript(compileRequest.scriptBody).then(
(script) => {
this.scriptMap[scriptId] = script;
this.cacheScript(scriptId, script);
var compileResponse = createCompileResponse(scriptId, true);
logger.debug('[%s] Sending success compile response, scriptId: [%s]', requestId, scriptId);
this.sendResponse(requestId, responseTopic, scriptId, compileResponse);
@ -126,6 +128,10 @@ JsInvokeMessageProcessor.prototype.processReleaseRequest = function(requestId, r
var scriptId = getScriptId(releaseRequest);
logger.debug('[%s] Processing release request, scriptId: [%s]', requestId, scriptId);
if (this.scriptMap[scriptId]) {
var index = this.scriptIds.indexOf(scriptId);
if (index > -1) {
this.scriptIds.splice(index, 1);
}
delete this.scriptMap[scriptId];
}
var releaseResponse = createReleaseResponse(scriptId, true);
@ -165,7 +171,7 @@ JsInvokeMessageProcessor.prototype.getOrCompileScript = function(scriptId, scrip
} else {
self.executor.compileScript(scriptBody).then(
(script) => {
self.scriptMap[scriptId] = script;
self.cacheScript(scriptId, script);
resolve(script);
},
(err) => {
@ -176,6 +182,19 @@ JsInvokeMessageProcessor.prototype.getOrCompileScript = function(scriptId, scrip
});
}
JsInvokeMessageProcessor.prototype.cacheScript = function(scriptId, script) {
if (!this.scriptMap[scriptId]) {
this.scriptIds.push(scriptId);
while (this.scriptIds.length > maxActiveScripts) {
logger.info('Active scripts count [%s] exceeds maximum limit [%s]', this.scriptIds.length, maxActiveScripts);
const prevScriptId = this.scriptIds.shift();
logger.info('Removing active script with id [%s]', prevScriptId);
delete this.scriptMap[prevScriptId];
}
}
this.scriptMap[scriptId] = script;
}
function createRemoteResponse(requestId, compileResponse, invokeResponse, releaseResponse) {
const requestIdBits = Utils.UUIDToBits(requestId);
return {

1
msa/js-executor/config/custom-environment-variables.yml

@ -27,3 +27,4 @@ logger:
script:
use_sandbox: "SCRIPT_USE_SANDBOX"
script_body_trace_frequency: "SCRIPT_BODY_TRACE_FREQUENCY"
max_active_scripts: "MAX_ACTIVE_SCRIPTS"

1
msa/js-executor/config/default.yml

@ -28,3 +28,4 @@ logger:
script:
use_sandbox: "true"
script_body_trace_frequency: "1000"
max_active_scripts: "1000"

2
msa/js-executor/package.json

@ -1,7 +1,7 @@
{
"name": "thingsboard-js-executor",
"private": true,
"version": "2.4.2",
"version": "2.5.0",
"description": "ThingsBoard JavaScript Executor Microservice",
"main": "server.js",
"bin": "server.js",

2
msa/js-executor/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

2
msa/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>msa</artifactId>

2
msa/tb-node/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

2
msa/tb/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

2
msa/transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.msa</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.msa</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.msa</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>

2
msa/transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

2
msa/web-ui/package.json

@ -1,7 +1,7 @@
{
"name": "thingsboard-web-ui",
"private": true,
"version": "2.4.2",
"version": "2.5.0",
"description": "ThingsBoard Web UI Microservice",
"main": "server.js",
"bin": "server.js",

2
msa/web-ui/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>

4
netty-mqtt/pom.xml

@ -19,12 +19,12 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<groupId>org.thingsboard</groupId>
<artifactId>netty-mqtt</artifactId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Netty MQTT Client</name>

11
pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.thingsboard</groupId>
<artifactId>thingsboard</artifactId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Thingsboard</name>
@ -115,6 +115,15 @@
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<!-- download sources under target/dependencies -->
<!-- mvn package -Pdownload-dependencies -Dclassifier=sources dependency:copy-dependencies -->
<profile>
<id>download-dependencies</id>
<properties>
<downloadSources>true</downloadSources>
<downloadJavadocs>true</downloadJavadocs>
</properties>
</profile>
</profiles>
<build>

2
rule-engine/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>rule-engine</artifactId>

2
rule-engine/rule-engine-api/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>rule-engine</artifactId>
</parent>
<groupId>org.thingsboard.rule-engine</groupId>

2
rule-engine/rule-engine-api/src/main/java/org/thingsboard/rule/engine/api/ScriptEngine.java

@ -38,6 +38,8 @@ public interface ScriptEngine {
JsonNode executeJson(TbMsg msg) throws ScriptException;
ListenableFuture<JsonNode> executeJsonAsync(TbMsg msg) throws ScriptException;
String executeToString(TbMsg msg) throws ScriptException;
void destroy();

2
rule-engine/rule-engine-components/pom.xml

@ -22,7 +22,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>rule-engine</artifactId>
</parent>
<groupId>org.thingsboard.rule-engine</groupId>

15
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractAlarmNode.java

@ -17,6 +17,7 @@ package org.thingsboard.rule.engine.action;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.ScriptEngine;
@ -27,6 +28,9 @@ import org.thingsboard.rule.engine.api.TbNodeException;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import javax.script.ScriptException;
import static org.thingsboard.common.util.DonAsynchron.withCallback;
@ -67,21 +71,24 @@ public abstract class TbAbstractAlarmNode<C extends TbAbstractAlarmNodeConfigura
ctx.tellNext(toAlarmMsg(ctx, alarmResult, msg), "Cleared");
}
},
t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor());
t -> ctx.tellFailure(msg, t)
, ctx.getDbCallbackExecutor());
}
protected abstract ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg);
protected ListenableFuture<JsonNode> buildAlarmDetails(TbContext ctx, TbMsg msg, JsonNode previousDetails) {
return ctx.getJsExecutor().executeAsync(() -> {
try {
TbMsg dummyMsg = msg;
if (previousDetails != null) {
TbMsgMetaData metaData = msg.getMetaData().copy();
metaData.putValue(PREV_ALARM_DETAILS, mapper.writeValueAsString(previousDetails));
dummyMsg = ctx.transformMsg(msg, msg.getType(), msg.getOriginator(), metaData, msg.getData());
}
return buildDetailsJsEngine.executeJson(dummyMsg);
});
return buildDetailsJsEngine.executeJsonAsync(dummyMsg);
} catch (Exception e) {
return Futures.immediateFailedFuture(e);
}
}
private TbMsg toAlarmMsg(TbContext ctx, AlarmResult alarmResult, TbMsg originalMsg) {

11
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNode.java

@ -21,11 +21,11 @@ import com.google.common.base.Function;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.util.TbNodeUtils;
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.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmStatus;
import org.thingsboard.server.common.data.id.TenantId;
@ -33,6 +33,7 @@ import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
import java.io.IOException;
import java.util.List;
@Slf4j
@RuleNode(
@ -53,10 +54,13 @@ import java.io.IOException;
public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConfiguration> {
private static ObjectMapper mapper = new ObjectMapper();
private List<String> relationTypes;
@Override
protected TbCreateAlarmNodeConfiguration loadAlarmNodeConfig(TbNodeConfiguration configuration) throws TbNodeException {
return TbNodeUtils.convert(configuration, TbCreateAlarmNodeConfiguration.class);
TbCreateAlarmNodeConfiguration nodeConfiguration = TbNodeUtils.convert(configuration, TbCreateAlarmNodeConfiguration.class);
relationTypes = nodeConfiguration.getRelationTypes();
return nodeConfiguration;
}
@Override
@ -125,9 +129,11 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
if (msgAlarm != null) {
existingAlarm.setSeverity(msgAlarm.getSeverity());
existingAlarm.setPropagate(msgAlarm.isPropagate());
existingAlarm.setPropagateRelationTypes(msgAlarm.getPropagateRelationTypes());
} else {
existingAlarm.setSeverity(config.getSeverity());
existingAlarm.setPropagate(config.isPropagate());
existingAlarm.setPropagateRelationTypes(relationTypes);
}
existingAlarm.setDetails(details);
existingAlarm.setEndTs(System.currentTimeMillis());
@ -145,6 +151,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
.severity(config.getSeverity())
.propagate(config.isPropagate())
.type(TbNodeUtils.processPattern(this.config.getAlarmType(), msg.getMetaData()))
.propagateRelationTypes(relationTypes)
//todo-vp: alarm date should be taken from Message or current Time should be used?
// .startTs(System.currentTimeMillis())
// .endTs(System.currentTimeMillis())

8
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateAlarmNodeConfiguration.java

@ -19,6 +19,9 @@ import lombok.Data;
import org.thingsboard.rule.engine.api.NodeConfiguration;
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
import java.util.Collections;
import java.util.List;
@Data
public class TbCreateAlarmNodeConfiguration extends TbAbstractAlarmNodeConfiguration implements NodeConfiguration<TbCreateAlarmNodeConfiguration> {
@ -26,18 +29,21 @@ public class TbCreateAlarmNodeConfiguration extends TbAbstractAlarmNodeConfigura
private boolean propagate;
private boolean useMessageAlarmData;
private List<String> relationTypes;
@Override
public TbCreateAlarmNodeConfiguration defaultConfiguration() {
TbCreateAlarmNodeConfiguration configuration = new TbCreateAlarmNodeConfiguration();
configuration.setAlarmDetailsBuildJs("var details = {};\n" +
"if (metadata.prevAlarmDetails) {\n" +
" details = JSON.parse(metadata.prevAlarmDetails);\n" +
"}\n"+
"}\n" +
"return details;");
configuration.setAlarmType("General Alarm");
configuration.setSeverity(AlarmSeverity.CRITICAL);
configuration.setPropagate(false);
configuration.setUseMessageAlarmData(false);
configuration.setRelationTypes(Collections.emptyList());
return configuration;
}
}

10
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

38
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbAlarmNodeTest.java

@ -26,6 +26,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import org.thingsboard.common.util.ListeningExecutor;
@ -60,8 +61,6 @@ public class TbAlarmNodeTest {
@Mock
private TbContext ctx;
@Mock
private ListeningExecutor executor;
@Mock
private AlarmService alarmService;
@Mock
@ -102,7 +101,7 @@ public class TbAlarmNodeTest {
metaData.putValue("key", "value");
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", originator, metaData, rawJson, ruleChainId, ruleNodeId, 0L);
when(detailsJs.executeJson(msg)).thenReturn(null);
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null));
doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class));
@ -136,8 +135,6 @@ public class TbAlarmNodeTest {
.build();
assertEquals(expectedAlarm, actualAlarm);
verify(executor, times(1)).executeAsync(any(Callable.class));
}
@Test
@ -146,7 +143,7 @@ public class TbAlarmNodeTest {
metaData.putValue("key", "value");
TbMsg msg = new TbMsg(UUIDs.timeBased(), "USER", originator, metaData, rawJson, ruleChainId, ruleNodeId, 0L);
when(detailsJs.executeJson(msg)).thenThrow(new NotImplementedException("message"));
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFailedFuture(new NotImplementedException("message")));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(null));
node.onMsg(ctx, msg);
@ -154,7 +151,6 @@ public class TbAlarmNodeTest {
verifyError(msg, "message", NotImplementedException.class);
verify(ctx).createJsScriptEngine("DETAILS");
verify(ctx, times(1)).getJsExecutor();
verify(ctx).getAlarmService();
verify(ctx, times(3)).getDbCallbackExecutor();
verify(ctx).logJsEvalRequest();
@ -172,7 +168,7 @@ public class TbAlarmNodeTest {
Alarm clearedAlarm = Alarm.builder().status(CLEARED_ACK).build();
when(detailsJs.executeJson(msg)).thenReturn(null);
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(clearedAlarm));
doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(any(Alarm.class));
@ -207,8 +203,6 @@ public class TbAlarmNodeTest {
.build();
assertEquals(expectedAlarm, actualAlarm);
verify(executor, times(1)).executeAsync(any(Callable.class));
}
@Test
@ -220,7 +214,7 @@ public class TbAlarmNodeTest {
long oldEndDate = System.currentTimeMillis();
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).status(ACTIVE_UNACK).severity(WARNING).endTs(oldEndDate).build();
when(detailsJs.executeJson(msg)).thenReturn(null);
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm));
doAnswer((Answer<Alarm>) invocationOnMock -> (Alarm) (invocationOnMock.getArguments())[0]).when(alarmService).createOrUpdateAlarm(activeAlarm);
@ -256,8 +250,6 @@ public class TbAlarmNodeTest {
.build();
assertEquals(expectedAlarm, actualAlarm);
verify(executor, times(1)).executeAsync(any(Callable.class));
}
@Test
@ -269,7 +261,7 @@ public class TbAlarmNodeTest {
long oldEndDate = System.currentTimeMillis();
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).status(ACTIVE_UNACK).severity(WARNING).endTs(oldEndDate).build();
// when(detailsJs.executeJson(msg)).thenReturn(null);
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
when(alarmService.findLatestByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(Futures.immediateFuture(activeAlarm));
when(alarmService.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), org.mockito.Mockito.any(JsonNode.class), anyLong())).thenReturn(Futures.immediateFuture(true));
when(alarmService.findAlarmByIdAsync(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()))).thenReturn(Futures.immediateFuture(activeAlarm));
@ -320,12 +312,9 @@ public class TbAlarmNodeTest {
when(ctx.createJsScriptEngine("DETAILS")).thenReturn(detailsJs);
when(ctx.getTenantId()).thenReturn(tenantId);
when(ctx.getJsExecutor()).thenReturn(executor);
when(ctx.getAlarmService()).thenReturn(alarmService);
when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor);
mockJsExecutor();
node = new TbCreateAlarmNode();
node.init(ctx, nodeConfiguration);
} catch (TbNodeException ex) {
@ -344,12 +333,9 @@ public class TbAlarmNodeTest {
when(ctx.createJsScriptEngine("DETAILS")).thenReturn(detailsJs);
when(ctx.getTenantId()).thenReturn(tenantId);
when(ctx.getJsExecutor()).thenReturn(executor);
when(ctx.getAlarmService()).thenReturn(alarmService);
when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor);
mockJsExecutor();
node = new TbClearAlarmNode();
node.init(ctx, nodeConfiguration);
} catch (TbNodeException ex) {
@ -357,18 +343,6 @@ public class TbAlarmNodeTest {
}
}
private void mockJsExecutor() {
when(ctx.getJsExecutor()).thenReturn(executor);
doAnswer((Answer<ListenableFuture<Boolean>>) invocationOnMock -> {
try {
Callable task = (Callable) (invocationOnMock.getArguments())[0];
return Futures.immediateFuture((Boolean) task.call());
} catch (Throwable th) {
return Futures.immediateFailedFuture(th);
}
}).when(executor).executeAsync(any(Callable.class));
}
private void verifyError(TbMsg msg, String message, Class expectedClass) {
ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass(Throwable.class);
verify(ctx).tellFailure(same(msg), captor.capture());

2
tools/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>tools</artifactId>

29
tools/src/main/java/org/thingsboard/client/tools/RestClient.java

@ -225,13 +225,6 @@ public class RestClient implements ClientHttpRequestInterceptor {
return restTemplate.postForEntity(baseURL + "/api/customer", customer, Customer.class).getBody();
}
public Device createDevice(String name, String type) {
Device device = new Device();
device.setName(name);
device.setType(type);
return restTemplate.postForEntity(baseURL + "/api/device", device, Device.class).getBody();
}
public DeviceCredentials updateDeviceCredentials(DeviceId deviceId, String token) {
DeviceCredentials deviceCredentials = getCredentials(deviceId);
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
@ -239,10 +232,30 @@ public class RestClient implements ClientHttpRequestInterceptor {
return saveDeviceCredentials(deviceCredentials);
}
public Device createDevice(String name, String type) {
Device device = new Device();
device.setName(name);
device.setType(type);
return doCreateDevice(device, null);
}
public Device createDevice(Device device) {
return restTemplate.postForEntity(baseURL + "/api/device", device, Device.class).getBody();
return doCreateDevice(device, null);
}
public Device createDevice(Device device, String accessToken) {
return doCreateDevice(device, accessToken);
}
private Device doCreateDevice(Device device, String accessToken) {
Map<String, String> params = new HashMap<>();
String deviceCreationUrl = "/api/device";
if (!StringUtils.isEmpty(accessToken)) {
deviceCreationUrl = deviceCreationUrl + "?accessToken={accessToken}";
params.put("accessToken", accessToken);
}
return restTemplate.postForEntity(baseURL + deviceCreationUrl, device, Device.class, params).getBody();
}
public Asset createAsset(Asset asset) {
return restTemplate.postForEntity(baseURL + "/api/asset", asset, Asset.class).getBody();
}

2
transport/coap/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>

2
transport/http/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>

2
transport/mqtt/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>

2
transport/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<artifactId>transport</artifactId>

2
ui/package.json

@ -1,7 +1,7 @@
{
"name": "thingsboard",
"private": true,
"version": "2.4.2",
"version": "2.5.0",
"description": "ThingsBoard UI",
"licenses": [
{

2
ui/pom.xml

@ -20,7 +20,7 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.4.2-SNAPSHOT</version>
<version>2.5.0-SNAPSHOT</version>
<artifactId>thingsboard</artifactId>
</parent>
<groupId>org.thingsboard</groupId>

37
ui/src/app/api/alarm.service.js

@ -183,7 +183,7 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
return deferred.promise;
}
function fetchAlarms(alarmsQuery, pageLink, deferred, alarmsList) {
function fetchAlarms(alarmsQuery, pageLink, deferred, leftToLoad, alarmsList) {
getAlarms(alarmsQuery.entityType, alarmsQuery.entityId,
pageLink, alarmsQuery.alarmSearchStatus, alarmsQuery.alarmStatus,
alarmsQuery.fetchOriginator, false, {ignoreLoading: true}).then(
@ -192,8 +192,19 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
alarmsList = [];
}
alarmsList = alarmsList.concat(alarms.data);
if (angular.isDefined(leftToLoad)) {
leftToLoad -= pageLink.limit;
if (leftToLoad === 0) {
alarmsList = $filter('orderBy')(alarmsList, ['-createdTime']);
deferred.resolve(alarmsList);
return;
}
if (leftToLoad < pageLink.limit) {
alarms.nextPageLink.limit = leftToLoad;
}
}
if (alarms.hasNext && !alarmsQuery.limit) {
fetchAlarms(alarmsQuery, alarms.nextPageLink, deferred, alarmsList);
fetchAlarms(alarmsQuery, alarms.nextPageLink, deferred, leftToLoad, alarmsList);
} else {
alarmsList = $filter('orderBy')(alarmsList, ['-createdTime']);
deferred.resolve(alarmsList);
@ -209,26 +220,34 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
var deferred = $q.defer();
var time = Date.now();
var pageLink;
var leftToLoad;
if (alarmsQuery.limit) {
pageLink = {
limit: alarmsQuery.limit
};
} else if (alarmsQuery.interval) {
pageLink = {
limit: 100,
limit: alarmsQuery.alarmsFetchSize || 100,
startTime: time - alarmsQuery.interval
};
} else if (alarmsQuery.startTime) {
pageLink = {
limit: 100,
limit: alarmsQuery.alarmsFetchSize || 100,
startTime: Math.round(alarmsQuery.startTime)
}
};
if (alarmsQuery.endTime) {
pageLink.endTime = Math.round(alarmsQuery.endTime);
}
}
fetchAlarms(alarmsQuery, pageLink, deferred);
if (angular.isDefined(alarmsQuery.alarmsMaxCountLoad) && alarmsQuery.alarmsMaxCountLoad !== 0) {
leftToLoad = alarmsQuery.alarmsMaxCountLoad;
if (leftToLoad < pageLink.limit) {
pageLink.limit = leftToLoad;
}
}
fetchAlarms(alarmsQuery, pageLink, deferred, leftToLoad);
return deferred.promise;
}
@ -276,8 +295,10 @@ function AlarmService($http, $q, $interval, $filter, $timeout, utils, types) {
entityType: alarmSource.entityType,
entityId: alarmSource.entityId,
alarmSearchStatus: alarmSourceListener.alarmSearchStatus,
alarmStatus: null
}
alarmStatus: null,
alarmsMaxCountLoad: alarmSourceListener.alarmsMaxCountLoad,
alarmsFetchSize: alarmSourceListener.alarmsFetchSize
};
var originatorKeys = $filter('filter')(alarmSource.dataKeys, {name: 'originator'});
if (originatorKeys && originatorKeys.length) {
alarmSourceListener.alarmsQuery.fetchOriginator = true;

6
ui/src/app/api/subscription.js

@ -75,6 +75,10 @@ export default class Subscription {
options.alarmSearchStatus : this.ctx.types.alarmSearchStatus.any;
this.alarmsPollingInterval = angular.isDefined(options.alarmsPollingInterval) ?
options.alarmsPollingInterval : 5000;
this.alarmsMaxCountLoad = angular.isDefined(options.alarmsMaxCountLoad) ?
options.alarmsMaxCountLoad : 0;
this.alarmsFetchSize = angular.isDefined(options.alarmsFetchSize) ?
options.alarmsFetchSize : 100;
this.alarmSourceListener = null;
this.alarms = [];
@ -915,6 +919,8 @@ export default class Subscription {
alarmSource: this.alarmSource,
alarmSearchStatus: this.alarmSearchStatus,
alarmsPollingInterval: this.alarmsPollingInterval,
alarmsMaxCountLoad: this.alarmsMaxCountLoad,
alarmsFetchSize: this.alarmsFetchSize,
alarmsUpdated: function(alarms, apply) {
subscription.alarmsUpdated(alarms, apply);
}

8
ui/src/app/components/widget/widget-config.directive.js

@ -173,6 +173,10 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
config.alarmSearchStatus : types.alarmSearchStatus.any;
scope.alarmsPollingInterval = angular.isDefined(config.alarmsPollingInterval) ?
config.alarmsPollingInterval : 5;
scope.alarmsMaxCountLoad = angular.isDefined(config.alarmsMaxCountLoad) ?
config.alarmsMaxCountLoad : 0;
scope.alarmsFetchSize = angular.isDefined(config.alarmsFetchSize) ?
config.alarmsFetchSize : 100;
if (config.alarmSource) {
scope.alarmSource.value = config.alarmSource;
} else {
@ -243,7 +247,7 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
scope.$watch('title + showTitleIcon + titleIcon + iconColor + iconSize + titleTooltip + showTitle + dropShadow + enableFullscreen + backgroundColor + ' +
'color + padding + margin + widgetStyle + titleStyle + mobileOrder + mobileHeight + units + decimals + useDashboardTimewindow + ' +
'displayTimewindow + alarmSearchStatus + alarmsPollingInterval + showLegend', function () {
'displayTimewindow + alarmSearchStatus + alarmsPollingInterval + alarmsMaxCountLoad + alarmsFetchSize + showLegend', function () {
if (ngModelCtrl.$viewValue) {
var value = ngModelCtrl.$viewValue;
if (value.config) {
@ -277,6 +281,8 @@ function WidgetConfig($compile, $templateCache, $rootScope, $translate, $timeout
config.displayTimewindow = scope.displayTimewindow;
config.alarmSearchStatus = scope.alarmSearchStatus;
config.alarmsPollingInterval = scope.alarmsPollingInterval;
config.alarmsMaxCountLoad = scope.alarmsMaxCountLoad;
config.alarmsFetchSize = scope.alarmsFetchSize;
config.showLegend = scope.showLegend;
}
if (value.layout) {

75
ui/src/app/components/widget/widget-config.tpl.html

@ -37,29 +37,58 @@
is-edit="true" flex ng-model="timewindow"></tb-timewindow>
</section>
</div>
<div ng-show="widgetType === types.widgetType.alarm.value" layout='column' layout-align="center"
layout-gt-sm='row' layout-align-gt-sm="start center">
<md-input-container class="md-block" flex>
<label translate>alarm.alarm-status</label>
<md-select ng-model="alarmSearchStatus" style="padding-bottom: 24px;">
<md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
{{ ('alarm.search-status.' + searchStatus) | translate }}
</md-option>
</md-select>
</md-input-container>
<md-input-container flex class="md-block">
<label translate>alarm.polling-interval</label>
<input ng-required="widgetType === types.widgetType.alarm.value"
type="number"
step="1"
min="1"
name="alarmsPollingInterval"
ng-model="alarmsPollingInterval"/>
<div ng-messages="theForm.alarmsPollingInterval.$error" multiple md-auto-hide="false">
<div ng-message="required" translate>alarm.polling-interval-required</div>
<div ng-message="min" translate>alarm.min-polling-interval-message</div>
</div>
</md-input-container>
<div ng-show="widgetType === types.widgetType.alarm.value" layout='column' layout-align="center">
<div layout-gt-sm='row' layout-align-gt-sm="start center">
<md-input-container class="md-block" flex>
<label translate>alarm.alarm-status</label>
<md-select ng-model="alarmSearchStatus" style="padding-bottom: 24px;">
<md-option ng-repeat="searchStatus in types.alarmSearchStatus" ng-value="searchStatus">
{{ ('alarm.search-status.' + searchStatus) | translate }}
</md-option>
</md-select>
</md-input-container>
<md-input-container flex class="md-block">
<label translate>alarm.polling-interval</label>
<input ng-required="widgetType === types.widgetType.alarm.value"
type="number"
step="1"
min="1"
name="alarmsPollingInterval"
ng-model="alarmsPollingInterval"/>
<div ng-messages="theForm.alarmsPollingInterval.$error" multiple md-auto-hide="false">
<div ng-message="required" translate>alarm.polling-interval-required</div>
<div ng-message="min" translate>alarm.min-polling-interval-message</div>
</div>
</md-input-container>
</div>
<div layout-gt-sm='row' layout-align-gt-sm="start center">
<md-input-container flex class="md-block">
<label translate>alarm.max-count-load</label>
<input ng-required="widgetType === types.widgetType.alarm.value"
type="number"
step="1"
min="0"
name="alarmsMaxCountLoad"
ng-model="alarmsMaxCountLoad"/>
<div ng-messages="theForm.alarmsMaxCountLoad.$error" multiple md-auto-hide="false">
<div ng-message="required" translate>alarm.max-count-load-required</div>
<div ng-message="min" translate>alarm.max-count-load-error-min</div>
</div>
</md-input-container>
<md-input-container flex class="md-block">
<label translate>alarm.fetch-size</label>
<input ng-required="widgetType === types.widgetType.alarm.value"
type="number"
step="1"
min="10"
name="alarmsFetchSize"
ng-model="alarmsFetchSize"/>
<div ng-messages="theForm.alarmsFetchSize.$error" multiple md-auto-hide="false">
<div ng-message="required" translate>alarm.fetch-size-required</div>
<div ng-message="min" translate>alarm.fetch-size-error-min</div>
</div>
</md-input-container>
</div>
</div>
<v-accordion id="datasources-accordion" control="datasourcesAccordion" class="vAccordion--default"
ng-show="widgetType !== types.widgetType.rpc.value

4
ui/src/app/components/widget/widget.controller.js

@ -367,6 +367,10 @@ export default function WidgetController($scope, $state, $timeout, $window, $ocL
widget.config.alarmSearchStatus : types.alarmSearchStatus.any;
options.alarmsPollingInterval = angular.isDefined(widget.config.alarmsPollingInterval) ?
widget.config.alarmsPollingInterval * 1000 : 5000;
options.alarmsMaxCountLoad = angular.isDefined(widget.config.alarmsMaxCountLoad) ?
widget.config.alarmsMaxCountLoad : 0;
options.alarmsFetchSize = angular.isDefined(widget.config.alarmsFetchSize) ?
widget.config.alarmsFetchSize : 100;
} else {
options.datasources = angular.copy(widget.config.datasources)
}

8
ui/src/app/locale/locale.constant-en_US.json

@ -162,7 +162,13 @@
"clear-alarms-text": "Are you sure you want to clear { count, plural, 1 {1 alarm} other {# alarms} }?",
"clear-alarm-title": "Clear Alarm",
"clear-alarm-text": "Are you sure you want to clear Alarm?",
"alarm-status-filter": "Alarm Status Filter"
"alarm-status-filter": "Alarm Status Filter",
"max-count-load": "Maximum number of alarms to load (0 - unlimited)",
"max-count-load-required": "Maximum number of alarms to load is required.",
"max-count-load-error-min": "Minimum value is 0.",
"fetch-size": "Fetch size",
"fetch-size-required": "Fetch size is required.",
"fetch-size-error-min": "Minimum value is 10."
},
"alias": {
"add": "Add alias",

8
ui/src/app/locale/locale.constant-ru_RU.json

@ -162,7 +162,13 @@
"clear-alarms-text": "Вы точно хотите сбросить { count, plural, 1 {1 оповещение} other {# оповещений} }?",
"clear-alarm-title": "Сбросить оповещение",
"clear-alarm-text": "Вы точно хотите сбросить оповещение?",
"alarm-status-filter": "Фильтр оповещений"
"alarm-status-filter": "Фильтр оповещений",
"max-count-load": "Максимальное количество оповещений для загрузки (0 - неограниченно)",
"max-count-load-required": "Максимальное количество оповещений для загрузки обязателен.",
"max-count-load-error-min": "Минимальное значение 0.",
"fetch-size": "Размер пакета для загрузки",
"fetch-size-required": "Размер пакета для загрузки обязателен.",
"fetch-size-error-min": "Минимальное значение 10."
},
"alias": {
"add": "Добавить псевдоним",

8
ui/src/app/locale/locale.constant-uk_UA.json

@ -178,7 +178,13 @@
"clear-alarms-text": "Ви впевнені, що хочете деактивувати { count, plural, 1 {1 сигнал тривоги} other {# сигнали тривоги} }?",
"clear-alarm-title": "Деактивувати сигнал тривоги",
"clear-alarm-text": "Ви впевнені, що хочете деактивувати сигнал тривоги?",
"alarm-status-filter": "Фільтр статусу сигналу тривоги"
"alarm-status-filter": "Фільтр статусу сигналу тривоги",
"max-count-load": "Максимальна кількість сигналів тривоги для завантаження (0 - необмежено)",
"max-count-load-required": "Необхідно задати максимальну кількість сигналів тривоги для завантаження.",
"max-count-load-error-min": "Мінімальне значення 0.",
"fetch-size": "Розмір пакету для завантаження",
"fetch-size-required": "Необхідно задати розмір пакету для завантаження.",
"fetch-size-error-min": "Мінімальне значення 10."
},
"alias": {
"add": "Додати псевдонім ",

Loading…
Cancel
Save