Monitoring: automatic rule chain update
This commit is contained in:
parent
d6a4d454fd
commit
32212b9c51
@ -25,6 +25,7 @@ import org.springframework.context.event.EventListener;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.thingsboard.common.util.ThingsBoardExecutors;
|
||||
import org.thingsboard.monitoring.service.BaseMonitoringService;
|
||||
import org.thingsboard.monitoring.service.MonitoringEntityService;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -38,6 +39,8 @@ public class ThingsboardMonitoringApplication {
|
||||
|
||||
@Autowired
|
||||
private List<BaseMonitoringService<?, ?>> monitoringServices;
|
||||
@Autowired
|
||||
private MonitoringEntityService entityService;
|
||||
|
||||
@Value("${monitoring.monitoring_rate_ms}")
|
||||
private int monitoringRateMs;
|
||||
@ -50,6 +53,9 @@ public class ThingsboardMonitoringApplication {
|
||||
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void startMonitoring() {
|
||||
entityService.checkEntities();
|
||||
monitoringServices.forEach(BaseMonitoringService::init);
|
||||
|
||||
ScheduledExecutorService scheduler = ThingsBoardExecutors.newSingleThreadScheduledExecutor("monitoring-executor");
|
||||
scheduler.scheduleWithFixedDelay(() -> {
|
||||
monitoringServices.forEach(monitoringService -> {
|
||||
|
||||
@ -15,17 +15,15 @@
|
||||
*/
|
||||
package org.thingsboard.monitoring.client;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.rest.client.RestClient;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Component
|
||||
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
|
||||
public class TbClient extends RestClient {
|
||||
|
||||
@Value("${monitoring.rest.username}")
|
||||
@ -41,6 +39,11 @@ public class TbClient extends RestClient {
|
||||
.build(), baseUrl);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
logIn();
|
||||
}
|
||||
|
||||
public String logIn() {
|
||||
login(username, password);
|
||||
return getToken();
|
||||
|
||||
@ -22,7 +22,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.thingsboard.monitoring.client.TbClient;
|
||||
import org.thingsboard.monitoring.client.WsClient;
|
||||
import org.thingsboard.monitoring.config.MonitoringConfig;
|
||||
import org.thingsboard.monitoring.config.MonitoringTarget;
|
||||
@ -46,6 +45,8 @@ public abstract class BaseHealthChecker<C extends MonitoringConfig, T extends Mo
|
||||
|
||||
private Object info;
|
||||
|
||||
@Autowired
|
||||
protected MonitoringEntityService entityService;
|
||||
@Autowired
|
||||
private MonitoringReporter reporter;
|
||||
@Autowired
|
||||
@ -64,7 +65,7 @@ public abstract class BaseHealthChecker<C extends MonitoringConfig, T extends Mo
|
||||
info = getInfo();
|
||||
}
|
||||
|
||||
protected abstract void initialize(TbClient tbClient);
|
||||
protected abstract void initialize();
|
||||
|
||||
public final void check(WsClient wsClient) {
|
||||
log.debug("[{}] Checking", info);
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
package org.thingsboard.monitoring.service;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@ -85,12 +84,11 @@ public abstract class BaseMonitoringService<C extends MonitoringConfig<T>, T ext
|
||||
@Value("${monitoring.calculated_fields.enabled:true}")
|
||||
protected boolean checkCalculatedFields;
|
||||
|
||||
@PostConstruct
|
||||
private void init() {
|
||||
public void init() {
|
||||
if (configs == null || configs.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
tbClient.logIn();
|
||||
|
||||
configs.forEach(config -> {
|
||||
config.getTargets().forEach(target -> {
|
||||
BaseHealthChecker<C, T> healthChecker = initHealthChecker(target, config);
|
||||
@ -108,7 +106,7 @@ public abstract class BaseMonitoringService<C extends MonitoringConfig<T>, T ext
|
||||
private BaseHealthChecker<C, T> initHealthChecker(T target, C config) {
|
||||
BaseHealthChecker<C, T> healthChecker = (BaseHealthChecker<C, T>) createHealthChecker(config, target);
|
||||
log.info("Initializing {} for {}", healthChecker.getClass().getSimpleName(), target.getBaseUrl());
|
||||
healthChecker.initialize(tbClient);
|
||||
healthChecker.initialize();
|
||||
devices.add(target.getDeviceId());
|
||||
return healthChecker;
|
||||
}
|
||||
@ -140,7 +138,7 @@ public abstract class BaseMonitoringService<C extends MonitoringConfig<T>, T ext
|
||||
reporter.serviceIsOk(MonitoredServiceKey.EDQS);
|
||||
}
|
||||
|
||||
reporter.reportLatencies(tbClient);
|
||||
reporter.reportLatencies();
|
||||
log.debug("Finished {}", getName());
|
||||
} catch (ServiceFailureException e) {
|
||||
reporter.serviceFailure(e.getServiceKey(), e);
|
||||
|
||||
@ -0,0 +1,245 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 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.monitoring.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.common.util.RegexUtils;
|
||||
import org.thingsboard.monitoring.client.TbClient;
|
||||
import org.thingsboard.monitoring.config.transport.DeviceConfig;
|
||||
import org.thingsboard.monitoring.config.transport.TransportMonitoringConfig;
|
||||
import org.thingsboard.monitoring.config.transport.TransportMonitoringTarget;
|
||||
import org.thingsboard.monitoring.config.transport.TransportType;
|
||||
import org.thingsboard.monitoring.util.ResourceUtils;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.DeviceProfileType;
|
||||
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||
import org.thingsboard.server.common.data.TbResource;
|
||||
import org.thingsboard.server.common.data.asset.Asset;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedField;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.Argument;
|
||||
import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.Output;
|
||||
import org.thingsboard.server.common.data.cf.configuration.OutputType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey;
|
||||
import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration;
|
||||
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredentials;
|
||||
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials;
|
||||
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecBootstrapClientCredential;
|
||||
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredential;
|
||||
import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration;
|
||||
import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration;
|
||||
import org.thingsboard.server.common.data.device.data.DeviceData;
|
||||
import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||
import org.thingsboard.server.common.data.kv.KvEntry;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.rule.RuleChain;
|
||||
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
|
||||
import org.thingsboard.server.common.data.rule.RuleChainType;
|
||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
||||
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.thingsboard.monitoring.service.BaseHealthChecker.TEST_CF_TELEMETRY_KEY;
|
||||
import static org.thingsboard.monitoring.service.BaseHealthChecker.TEST_TELEMETRY_KEY;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class MonitoringEntityService {
|
||||
|
||||
private final TbClient tbClient;
|
||||
|
||||
@Value("${monitoring.calculated_fields.enabled:true}")
|
||||
private boolean calculatedFieldsMonitoringEnabled;
|
||||
|
||||
public void checkEntities() {
|
||||
RuleChain ruleChain = tbClient.getRuleChains(RuleChainType.CORE, new PageLink(10)).getData().stream()
|
||||
.filter(RuleChain::isRoot)
|
||||
.findFirst().orElseThrow();
|
||||
RuleChainId ruleChainId = ruleChain.getId();
|
||||
|
||||
JsonNode ruleChainDescriptor = ResourceUtils.getResource("rule_chain.json");
|
||||
List<String> attributeKeys = tbClient.getAttributeKeys(ruleChainId);
|
||||
Map<String, String> attributes = tbClient.getAttributeKvEntries(ruleChainId, attributeKeys).stream()
|
||||
.collect(Collectors.toMap(KvEntry::getKey, KvEntry::getValueAsString));
|
||||
|
||||
int currentVersion = Integer.parseInt(attributes.getOrDefault("version", "0"));
|
||||
int newVersion = ruleChainDescriptor.get("version").asInt();
|
||||
if (currentVersion == newVersion) {
|
||||
log.info("Not updating rule chain, version is the same ({})", currentVersion);
|
||||
return;
|
||||
} else {
|
||||
log.info("Updating rule chain '{}' from version {} to {}", ruleChain.getName(), currentVersion, newVersion);
|
||||
}
|
||||
|
||||
String metadataJson = RegexUtils.replace(ruleChainDescriptor.get("metadata").toString(),
|
||||
"\\$\\{MONITORING:(.+?)}", matchResult -> {
|
||||
String key = matchResult.group(1);
|
||||
String value = attributes.get(key);
|
||||
if (value == null) {
|
||||
throw new IllegalArgumentException("No attribute found for key " + key);
|
||||
}
|
||||
log.info("Using {}: {}", key, value);
|
||||
return value;
|
||||
});
|
||||
RuleChainMetaData metaData = JacksonUtil.fromString(metadataJson, RuleChainMetaData.class);
|
||||
metaData.setRuleChainId(ruleChainId);
|
||||
tbClient.saveRuleChainMetaData(metaData);
|
||||
}
|
||||
|
||||
public Asset getOrCreateMonitoringAsset() {
|
||||
String assetName = "[Monitoring] Latencies";
|
||||
return tbClient.findAsset(assetName).orElseGet(() -> {
|
||||
Asset asset = new Asset();
|
||||
asset.setType("Monitoring");
|
||||
asset.setName(assetName);
|
||||
asset = tbClient.saveAsset(asset);
|
||||
log.info("Created monitoring asset {}", asset.getId());
|
||||
return asset;
|
||||
});
|
||||
}
|
||||
|
||||
public void checkEntities(TransportMonitoringConfig config, TransportMonitoringTarget target) {
|
||||
Device device = getOrCreateDevice(config, target);
|
||||
DeviceCredentials credentials = tbClient.getDeviceCredentialsByDeviceId(device.getId())
|
||||
.orElseThrow(() -> new IllegalArgumentException("No credentials found for device " + device.getId()));
|
||||
|
||||
DeviceConfig deviceConfig = new DeviceConfig();
|
||||
deviceConfig.setId(device.getId().toString());
|
||||
deviceConfig.setName(device.getName());
|
||||
deviceConfig.setCredentials(credentials);
|
||||
target.setDevice(deviceConfig);
|
||||
}
|
||||
|
||||
private Device getOrCreateDevice(TransportMonitoringConfig config, TransportMonitoringTarget target) {
|
||||
TransportType transportType = config.getTransportType();
|
||||
String deviceName = String.format("%s %s (%s) - %s", target.getNamePrefix(), transportType.getName(), target.getQueue(), target.getBaseUrl()).trim();
|
||||
Device device = tbClient.getTenantDevice(deviceName).orElse(null);
|
||||
if (device != null) {
|
||||
if (calculatedFieldsMonitoringEnabled) {
|
||||
CalculatedField calculatedField = tbClient.getCalculatedFieldsByEntityId(device.getId(), new PageLink(1, 0, TEST_CF_TELEMETRY_KEY))
|
||||
.getData().stream().findFirst().orElse(null);
|
||||
if (calculatedField == null) {
|
||||
createCalculatedField(device);
|
||||
}
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
log.info("Creating new device '{}'", deviceName);
|
||||
device = new Device();
|
||||
device.setName(deviceName);
|
||||
|
||||
DeviceCredentials credentials = new DeviceCredentials();
|
||||
credentials.setCredentialsId(RandomStringUtils.randomAlphabetic(20));
|
||||
DeviceData deviceData = new DeviceData();
|
||||
deviceData.setConfiguration(new DefaultDeviceConfiguration());
|
||||
|
||||
DeviceProfile deviceProfile = getOrCreateDeviceProfile(config, target);
|
||||
device.setType(deviceProfile.getName());
|
||||
device.setDeviceProfileId(deviceProfile.getId());
|
||||
|
||||
if (transportType != TransportType.LWM2M) {
|
||||
deviceData.setTransportConfiguration(new DefaultDeviceTransportConfiguration());
|
||||
credentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
|
||||
} else {
|
||||
deviceData.setTransportConfiguration(new Lwm2mDeviceTransportConfiguration());
|
||||
credentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
|
||||
LwM2MDeviceCredentials lwm2mCreds = new LwM2MDeviceCredentials();
|
||||
NoSecClientCredential client = new NoSecClientCredential();
|
||||
client.setEndpoint(credentials.getCredentialsId());
|
||||
lwm2mCreds.setClient(client);
|
||||
LwM2MBootstrapClientCredentials bootstrap = new LwM2MBootstrapClientCredentials();
|
||||
bootstrap.setBootstrapServer(new NoSecBootstrapClientCredential());
|
||||
bootstrap.setLwm2mServer(new NoSecBootstrapClientCredential());
|
||||
lwm2mCreds.setBootstrap(bootstrap);
|
||||
credentials.setCredentialsValue(JacksonUtil.toString(lwm2mCreds));
|
||||
}
|
||||
|
||||
return tbClient.saveDeviceWithCredentials(device, credentials).get();
|
||||
}
|
||||
|
||||
private DeviceProfile getOrCreateDeviceProfile(TransportMonitoringConfig config, TransportMonitoringTarget target) {
|
||||
TransportType transportType = config.getTransportType();
|
||||
String profileName = String.format("%s %s (%s)", target.getNamePrefix(), transportType.getName(), target.getQueue()).trim();
|
||||
DeviceProfile deviceProfile = tbClient.getDeviceProfiles(new PageLink(1, 0, profileName)).getData()
|
||||
.stream().findFirst().orElse(null);
|
||||
if (deviceProfile != null) {
|
||||
return deviceProfile;
|
||||
}
|
||||
|
||||
log.info("Creating new device profile '{}'", profileName);
|
||||
if (transportType != TransportType.LWM2M) {
|
||||
deviceProfile = new DeviceProfile();
|
||||
deviceProfile.setType(DeviceProfileType.DEFAULT);
|
||||
deviceProfile.setTransportType(DeviceTransportType.DEFAULT);
|
||||
DeviceProfileData profileData = new DeviceProfileData();
|
||||
profileData.setConfiguration(new DefaultDeviceProfileConfiguration());
|
||||
profileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration());
|
||||
deviceProfile.setProfileData(profileData);
|
||||
} else {
|
||||
tbClient.getResources(new PageLink(1, 0, "LwM2M Monitoring")).getData()
|
||||
.stream().findFirst()
|
||||
.orElseGet(() -> {
|
||||
TbResource newResource = ResourceUtils.getResource("lwm2m/resource.json", TbResource.class);
|
||||
log.info("Creating LwM2M resource");
|
||||
return tbClient.saveResource(newResource);
|
||||
});
|
||||
deviceProfile = ResourceUtils.getResource("lwm2m/device_profile.json", DeviceProfile.class);
|
||||
}
|
||||
|
||||
deviceProfile.setName(profileName);
|
||||
deviceProfile.setDefaultQueueName(target.getQueue());
|
||||
return tbClient.saveDeviceProfile(deviceProfile);
|
||||
}
|
||||
|
||||
private void createCalculatedField(Device device) {
|
||||
log.info("Creating calculated field for device '{}'", device.getName());
|
||||
CalculatedField calculatedField = new CalculatedField();
|
||||
calculatedField.setName(TEST_CF_TELEMETRY_KEY);
|
||||
calculatedField.setEntityId(device.getId());
|
||||
calculatedField.setType(CalculatedFieldType.SCRIPT);
|
||||
ScriptCalculatedFieldConfiguration configuration = new ScriptCalculatedFieldConfiguration();
|
||||
Argument testDataArgument = new Argument();
|
||||
testDataArgument.setRefEntityKey(new ReferencedEntityKey(TEST_TELEMETRY_KEY, ArgumentType.TS_LATEST, null));
|
||||
configuration.setArguments(Map.of(
|
||||
TEST_TELEMETRY_KEY, testDataArgument
|
||||
));
|
||||
configuration.setExpression("return { \"" + TEST_CF_TELEMETRY_KEY + "\": " + TEST_TELEMETRY_KEY + " + \"-cf\" };");
|
||||
Output output = new Output();
|
||||
output.setType(OutputType.TIME_SERIES);
|
||||
configuration.setOutput(output);
|
||||
calculatedField.setConfiguration(configuration);
|
||||
calculatedField.setDebugMode(true);
|
||||
tbClient.saveCalculatedField(calculatedField);
|
||||
}
|
||||
|
||||
}
|
||||
@ -46,6 +46,8 @@ import java.util.stream.Collectors;
|
||||
public class MonitoringReporter {
|
||||
|
||||
private final NotificationService notificationService;
|
||||
private final TbClient tbClient;
|
||||
private final MonitoringEntityService entityService;
|
||||
|
||||
private final Map<String, Latency> latencies = new ConcurrentHashMap<>();
|
||||
private final Map<Object, AtomicInteger> failuresCounters = new ConcurrentHashMap<>();
|
||||
@ -62,7 +64,7 @@ public class MonitoringReporter {
|
||||
@Value("${monitoring.latency.reporting_asset_id}")
|
||||
private String reportingAssetId;
|
||||
|
||||
public void reportLatencies(TbClient tbClient) {
|
||||
public void reportLatencies() {
|
||||
if (latencies.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@ -81,15 +83,7 @@ public class MonitoringReporter {
|
||||
|
||||
try {
|
||||
if (StringUtils.isBlank(reportingAssetId)) {
|
||||
String assetName = "[Monitoring] Latencies";
|
||||
Asset monitoringAsset = tbClient.findAsset(assetName).orElseGet(() -> {
|
||||
Asset asset = new Asset();
|
||||
asset.setType("Monitoring");
|
||||
asset.setName(assetName);
|
||||
asset = tbClient.saveAsset(asset);
|
||||
log.info("Created monitoring asset {}", asset.getId());
|
||||
return asset;
|
||||
});
|
||||
Asset monitoringAsset = entityService.getOrCreateMonitoringAsset();
|
||||
reportingAssetId = monitoringAsset.getId().toString();
|
||||
}
|
||||
|
||||
|
||||
@ -17,46 +17,13 @@ package org.thingsboard.monitoring.service.transport;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.TextNode;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.monitoring.client.TbClient;
|
||||
import org.thingsboard.monitoring.config.transport.DeviceConfig;
|
||||
import org.thingsboard.monitoring.config.transport.TransportInfo;
|
||||
import org.thingsboard.monitoring.config.transport.TransportMonitoringConfig;
|
||||
import org.thingsboard.monitoring.config.transport.TransportMonitoringTarget;
|
||||
import org.thingsboard.monitoring.config.transport.TransportType;
|
||||
import org.thingsboard.monitoring.service.BaseHealthChecker;
|
||||
import org.thingsboard.monitoring.util.ResourceUtils;
|
||||
import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.DeviceProfileType;
|
||||
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||
import org.thingsboard.server.common.data.TbResource;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedField;
|
||||
import org.thingsboard.server.common.data.cf.CalculatedFieldType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.Argument;
|
||||
import org.thingsboard.server.common.data.cf.configuration.ArgumentType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.Output;
|
||||
import org.thingsboard.server.common.data.cf.configuration.OutputType;
|
||||
import org.thingsboard.server.common.data.cf.configuration.ReferencedEntityKey;
|
||||
import org.thingsboard.server.common.data.cf.configuration.ScriptCalculatedFieldConfiguration;
|
||||
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MBootstrapClientCredentials;
|
||||
import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials;
|
||||
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecBootstrapClientCredential;
|
||||
import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredential;
|
||||
import org.thingsboard.server.common.data.device.data.DefaultDeviceConfiguration;
|
||||
import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConfiguration;
|
||||
import org.thingsboard.server.common.data.device.data.DeviceData;
|
||||
import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileTransportConfiguration;
|
||||
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.security.DeviceCredentials;
|
||||
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
public abstract class TransportHealthChecker<C extends TransportMonitoringConfig> extends BaseHealthChecker<C, TransportMonitoringTarget> {
|
||||
@ -69,16 +36,8 @@ public abstract class TransportHealthChecker<C extends TransportMonitoringConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize(TbClient tbClient) {
|
||||
Device device = getOrCreateDevice(tbClient);
|
||||
DeviceCredentials credentials = tbClient.getDeviceCredentialsByDeviceId(device.getId())
|
||||
.orElseThrow(() -> new IllegalArgumentException("No credentials found for device " + device.getId()));
|
||||
|
||||
DeviceConfig deviceConfig = new DeviceConfig();
|
||||
deviceConfig.setId(device.getId().toString());
|
||||
deviceConfig.setName(device.getName());
|
||||
deviceConfig.setCredentials(credentials);
|
||||
target.setDevice(deviceConfig);
|
||||
protected void initialize() {
|
||||
entityService.checkEntities(config, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -98,110 +57,6 @@ public abstract class TransportHealthChecker<C extends TransportMonitoringConfig
|
||||
|
||||
protected abstract TransportType getTransportType();
|
||||
|
||||
|
||||
private Device getOrCreateDevice(TbClient tbClient) {
|
||||
TransportType transportType = config.getTransportType();
|
||||
String deviceName = String.format("%s %s (%s) - %s", target.getNamePrefix(), transportType.getName(), target.getQueue(), target.getBaseUrl()).trim();
|
||||
Device device = tbClient.getTenantDevice(deviceName).orElse(null);
|
||||
if (device != null) {
|
||||
if (isCfMonitoringEnabled()) {
|
||||
CalculatedField calculatedField = tbClient.getCalculatedFieldsByEntityId(device.getId(), new PageLink(1, 0, TEST_CF_TELEMETRY_KEY))
|
||||
.getData().stream().findFirst().orElse(null);
|
||||
if (calculatedField == null) {
|
||||
createCalculatedField(tbClient, device);
|
||||
}
|
||||
}
|
||||
return device;
|
||||
}
|
||||
|
||||
log.info("Creating new device '{}'", deviceName);
|
||||
device = new Device();
|
||||
device.setName(deviceName);
|
||||
|
||||
DeviceCredentials credentials = new DeviceCredentials();
|
||||
credentials.setCredentialsId(RandomStringUtils.randomAlphabetic(20));
|
||||
DeviceData deviceData = new DeviceData();
|
||||
deviceData.setConfiguration(new DefaultDeviceConfiguration());
|
||||
|
||||
DeviceProfile deviceProfile = getOrCreateDeviceProfile(tbClient);
|
||||
device.setType(deviceProfile.getName());
|
||||
device.setDeviceProfileId(deviceProfile.getId());
|
||||
|
||||
if (transportType != TransportType.LWM2M) {
|
||||
deviceData.setTransportConfiguration(new DefaultDeviceTransportConfiguration());
|
||||
credentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
|
||||
} else {
|
||||
deviceData.setTransportConfiguration(new Lwm2mDeviceTransportConfiguration());
|
||||
credentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
|
||||
LwM2MDeviceCredentials lwm2mCreds = new LwM2MDeviceCredentials();
|
||||
NoSecClientCredential client = new NoSecClientCredential();
|
||||
client.setEndpoint(credentials.getCredentialsId());
|
||||
lwm2mCreds.setClient(client);
|
||||
LwM2MBootstrapClientCredentials bootstrap = new LwM2MBootstrapClientCredentials();
|
||||
bootstrap.setBootstrapServer(new NoSecBootstrapClientCredential());
|
||||
bootstrap.setLwm2mServer(new NoSecBootstrapClientCredential());
|
||||
lwm2mCreds.setBootstrap(bootstrap);
|
||||
credentials.setCredentialsValue(JacksonUtil.toString(lwm2mCreds));
|
||||
}
|
||||
|
||||
return tbClient.saveDeviceWithCredentials(device, credentials).get();
|
||||
}
|
||||
|
||||
private DeviceProfile getOrCreateDeviceProfile(TbClient tbClient) {
|
||||
TransportType transportType = config.getTransportType();
|
||||
String profileName = String.format("%s %s (%s)", target.getNamePrefix(), transportType.getName(), target.getQueue()).trim();
|
||||
DeviceProfile deviceProfile = tbClient.getDeviceProfiles(new PageLink(1, 0, profileName)).getData()
|
||||
.stream().findFirst().orElse(null);
|
||||
if (deviceProfile != null) {
|
||||
return deviceProfile;
|
||||
}
|
||||
|
||||
log.info("Creating new device profile '{}'", profileName);
|
||||
if (transportType != TransportType.LWM2M) {
|
||||
deviceProfile = new DeviceProfile();
|
||||
deviceProfile.setType(DeviceProfileType.DEFAULT);
|
||||
deviceProfile.setTransportType(DeviceTransportType.DEFAULT);
|
||||
DeviceProfileData profileData = new DeviceProfileData();
|
||||
profileData.setConfiguration(new DefaultDeviceProfileConfiguration());
|
||||
profileData.setTransportConfiguration(new DefaultDeviceProfileTransportConfiguration());
|
||||
deviceProfile.setProfileData(profileData);
|
||||
} else {
|
||||
tbClient.getResources(new PageLink(1, 0, "LwM2M Monitoring")).getData()
|
||||
.stream().findFirst()
|
||||
.orElseGet(() -> {
|
||||
TbResource newResource = ResourceUtils.getResource("lwm2m/resource.json", TbResource.class);
|
||||
log.info("Creating LwM2M resource");
|
||||
return tbClient.saveResource(newResource);
|
||||
});
|
||||
deviceProfile = ResourceUtils.getResource("lwm2m/device_profile.json", DeviceProfile.class);
|
||||
}
|
||||
|
||||
deviceProfile.setName(profileName);
|
||||
deviceProfile.setDefaultQueueName(target.getQueue());
|
||||
return tbClient.saveDeviceProfile(deviceProfile);
|
||||
}
|
||||
|
||||
private void createCalculatedField(TbClient tbClient, Device device) {
|
||||
log.info("Creating calculated field for device '{}'", device.getName());
|
||||
CalculatedField calculatedField = new CalculatedField();
|
||||
calculatedField.setName(TEST_CF_TELEMETRY_KEY);
|
||||
calculatedField.setEntityId(device.getId());
|
||||
calculatedField.setType(CalculatedFieldType.SCRIPT);
|
||||
ScriptCalculatedFieldConfiguration configuration = new ScriptCalculatedFieldConfiguration();
|
||||
Argument testDataArgument = new Argument();
|
||||
testDataArgument.setRefEntityKey(new ReferencedEntityKey(TEST_TELEMETRY_KEY, ArgumentType.TS_LATEST, null));
|
||||
configuration.setArguments(Map.of(
|
||||
TEST_TELEMETRY_KEY, testDataArgument
|
||||
));
|
||||
configuration.setExpression("return { \"" + TEST_CF_TELEMETRY_KEY + "\": " + TEST_TELEMETRY_KEY + " + \"-cf\" };");
|
||||
Output output = new Output();
|
||||
output.setType(OutputType.TIME_SERIES);
|
||||
configuration.setOutput(output);
|
||||
calculatedField.setConfiguration(configuration);
|
||||
calculatedField.setDebugMode(true);
|
||||
tbClient.saveCalculatedField(calculatedField);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isCfMonitoringEnabled() {
|
||||
return calculatedFieldsMonitoringEnabled;
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.thingsboard.monitoring.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.SneakyThrows;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
|
||||
@ -24,14 +25,21 @@ public class ResourceUtils {
|
||||
|
||||
@SneakyThrows
|
||||
public static <T> T getResource(String path, Class<T> type) {
|
||||
InputStream resource = ResourceUtils.class.getClassLoader().getResourceAsStream(path);
|
||||
if (resource == null) {
|
||||
throw new IllegalArgumentException("Resource not found for path " + path);
|
||||
}
|
||||
InputStream resource = getResourceStream(path);
|
||||
return JacksonUtil.OBJECT_MAPPER.readValue(resource, type);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static JsonNode getResource(String path) {
|
||||
InputStream resource = getResourceStream(path);
|
||||
return JacksonUtil.OBJECT_MAPPER.readTree(resource);
|
||||
}
|
||||
|
||||
public static InputStream getResourceAsStream(String path) {
|
||||
return getResourceStream(path);
|
||||
}
|
||||
|
||||
private static InputStream getResourceStream(String path) {
|
||||
InputStream resource = ResourceUtils.class.getClassLoader().getResourceAsStream(path);
|
||||
if (resource == null) {
|
||||
throw new IllegalArgumentException("Resource not found for path " + path);
|
||||
|
||||
@ -1,257 +1,232 @@
|
||||
{
|
||||
"version": 1,
|
||||
"ruleChain": {
|
||||
"additionalInfo": null,
|
||||
"name": "Root Rule Chain",
|
||||
"type": "CORE",
|
||||
"firstRuleNodeId": null,
|
||||
"root": false,
|
||||
"debugMode": false,
|
||||
"configuration": null,
|
||||
"externalId": null
|
||||
"additionalInfo": null
|
||||
},
|
||||
"metadata": {
|
||||
"firstNodeIndex": 12,
|
||||
"firstNodeIndex": 9,
|
||||
"nodes": [
|
||||
{
|
||||
"additionalInfo": {
|
||||
"description": null,
|
||||
"layoutX": 1202,
|
||||
"layoutY": 221
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||
"name": "Save Timeseries",
|
||||
"debugSettings": {
|
||||
"failuresEnabled": true,
|
||||
"allEnabled": false,
|
||||
"allEnabledUntil": 1735310701003
|
||||
},
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 1,
|
||||
"configuration": {
|
||||
"defaultTTL": 0,
|
||||
"useServerTs": false,
|
||||
"processingSettings": {
|
||||
"type": "ON_EVERY_MESSAGE"
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
"additionalInfo": {
|
||||
"description": null,
|
||||
"layoutX": 1202,
|
||||
"layoutY": 221
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 1000,
|
||||
"layoutY": 167
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode",
|
||||
"name": "Save Attributes",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 3,
|
||||
"configuration": {
|
||||
"scope": "CLIENT_SCOPE",
|
||||
"notifyDevice": false,
|
||||
"processingSettings": {
|
||||
"type": "ON_EVERY_MESSAGE"
|
||||
},
|
||||
"scope": "CLIENT_SCOPE",
|
||||
"notifyDevice": false,
|
||||
"sendAttributesUpdatedNotification": false,
|
||||
"updateAttributesOnlyOnValueChange": false
|
||||
},
|
||||
"externalId": null
|
||||
"additionalInfo": {
|
||||
"layoutX": 1000,
|
||||
"layoutY": 167
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 566,
|
||||
"layoutY": 302
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode",
|
||||
"name": "Message Type Switch",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"version": 0
|
||||
},
|
||||
"externalId": null
|
||||
"additionalInfo": {
|
||||
"layoutX": 566,
|
||||
"layoutY": 302
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "org.thingsboard.rule.engine.action.TbLogNode",
|
||||
"name": "Log RPC from Device",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"scriptLang": "TBEL",
|
||||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
|
||||
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||
},
|
||||
"additionalInfo": {
|
||||
"layoutX": 1000,
|
||||
"layoutY": 381
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "org.thingsboard.rule.engine.action.TbLogNode",
|
||||
"name": "Log RPC from Device",
|
||||
"name": "Log Other",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"scriptLang": "TBEL",
|
||||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
|
||||
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 1000,
|
||||
"layoutY": 494
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.action.TbLogNode",
|
||||
"name": "Log Other",
|
||||
"singletonMode": false,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"scriptLang": "TBEL",
|
||||
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);",
|
||||
"tbelScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"
|
||||
},
|
||||
"externalId": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 1000,
|
||||
"layoutY": 583
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode",
|
||||
"name": "RPC Call Request",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"timeoutInSeconds": 60
|
||||
},
|
||||
"externalId": null
|
||||
"additionalInfo": {
|
||||
"layoutX": 1000,
|
||||
"layoutY": 583
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 255,
|
||||
"layoutY": 301
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.filter.TbOriginatorTypeFilterNode",
|
||||
"name": "Is Entity Group",
|
||||
"singletonMode": false,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"originatorTypes": [
|
||||
"ENTITY_GROUP"
|
||||
]
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 319,
|
||||
"layoutY": 109
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode",
|
||||
"name": "Post attributes or RPC request",
|
||||
"singletonMode": false,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"messageTypes": [
|
||||
"POST_ATTRIBUTES_REQUEST",
|
||||
"RPC_CALL_FROM_SERVER_TO_DEVICE"
|
||||
]
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"layoutX": 627,
|
||||
"layoutY": 108
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.transform.TbDuplicateMsgToGroupNode",
|
||||
"name": "Duplicate To Group Entities",
|
||||
"singletonMode": false,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"entityGroupId": null,
|
||||
"entityGroupIsMessageOriginator": true
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
|
||||
"layoutX": 45,
|
||||
"layoutY": 359
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
|
||||
"name": "Device Profile Node",
|
||||
"debugSettings": {
|
||||
"failuresEnabled": true,
|
||||
"allEnabled": false,
|
||||
"allEnabledUntil": 1735310701003
|
||||
},
|
||||
"singletonMode": false,
|
||||
"configurationVersion": 0,
|
||||
"queueName": null,
|
||||
"configurationVersion": 1,
|
||||
"configuration": {
|
||||
"persistAlarmRulesState": false,
|
||||
"fetchAlarmRulesStateOnStart": false
|
||||
},
|
||||
"externalId": null
|
||||
"additionalInfo": {
|
||||
"description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
|
||||
"layoutX": 45,
|
||||
"layoutY": 359
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"description": "",
|
||||
"layoutX": 160,
|
||||
"layoutY": 631
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.filter.TbJsFilterNode",
|
||||
"name": "Test JS script",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"scriptLang": "JS",
|
||||
"jsScript": "var test = {\n a: 'a',\n b: 'b'\n};\nreturn test.a === 'a' && test.b === 'b';",
|
||||
"jsScript": "var test = {\n a: 'a',\n b: 'b'\n};\n\nreturn test.a === 'a' && test.b === 'b';",
|
||||
"tbelScript": "return msg.temperature > 20;"
|
||||
},
|
||||
"externalId": null
|
||||
"additionalInfo": {
|
||||
"description": "dashboardId: ${MONITORING:dashboardId}",
|
||||
"layoutX": 251,
|
||||
"layoutY": 499
|
||||
}
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"description": "",
|
||||
"layoutX": 427,
|
||||
"layoutY": 541
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.filter.TbJsFilterNode",
|
||||
"name": "Test TBEL script",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"scriptLang": "TBEL",
|
||||
"jsScript": "return msg.temperature > 20;",
|
||||
"tbelScript": "var a = \"a\";\nvar b = \"b\";\nreturn a.equals(\"a\") && b.equals(\"b\");"
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"description": "",
|
||||
"layoutX": 40,
|
||||
"layoutY": 252
|
||||
"layoutX": 317,
|
||||
"layoutY": 355
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode",
|
||||
"name": "Add arrival timestamp",
|
||||
"debugSettings": {
|
||||
"failuresEnabled": true,
|
||||
"allEnabled": false,
|
||||
"allEnabledUntil": 1744642101587
|
||||
},
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"scriptLang": "TBEL",
|
||||
"jsScript": "return {msg: msg, metadata: metadata, msgType: msgType};",
|
||||
"tbelScript": "metadata.arrivalTs = Date.now();\nreturn {msg: msg, metadata: metadata, msgType: msgType};"
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"description": "",
|
||||
"layoutX": 1467,
|
||||
"layoutY": 267
|
||||
"layoutX": 40,
|
||||
"layoutY": 252
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "org.thingsboard.rule.engine.transform.TbTransformMsgNode",
|
||||
"name": "Calculate additional latencies",
|
||||
"debugSettings": {
|
||||
"failuresEnabled": true,
|
||||
"allEnabled": false,
|
||||
"allEnabledUntil": 1735310701003
|
||||
},
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"scriptLang": "TBEL",
|
||||
"jsScript": "return {msg: msg, metadata: metadata, msgType: msgType};",
|
||||
"tbelScript": "var arrivalLatency = metadata.arrivalTs - metadata.ts;\nvar processingTime = Date.now() - metadata.arrivalTs;\nmsg = {\n arrivalLatency: arrivalLatency,\n processingTime: processingTime\n};\nreturn {msg: msg, metadata: metadata, msgType: msgType};"
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"description": "",
|
||||
"layoutX": 1438,
|
||||
"layoutY": 403
|
||||
"layoutX": 1467,
|
||||
"layoutY": 267
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "org.thingsboard.rule.engine.transform.TbChangeOriginatorNode",
|
||||
"name": "To latencies asset",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"originatorSource": "ENTITY",
|
||||
@ -269,64 +244,79 @@
|
||||
"fetchLastLevelOnly": false
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
"additionalInfo": {
|
||||
"description": "",
|
||||
"layoutX": 1438,
|
||||
"layoutY": 403
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||
"name": "Save Timeseries",
|
||||
"debugSettings": {
|
||||
"failuresEnabled": true,
|
||||
"allEnabled": false,
|
||||
"allEnabledUntil": 1735310701003
|
||||
},
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 1,
|
||||
"configuration": {
|
||||
"defaultTTL": 0,
|
||||
"processingSettings": {
|
||||
"type": "ON_EVERY_MESSAGE"
|
||||
}
|
||||
},
|
||||
"additionalInfo": {
|
||||
"description": null,
|
||||
"layoutX": 1458,
|
||||
"layoutY": 505
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||
"name": "Save Timeseries",
|
||||
"singletonMode": false,
|
||||
"configurationVersion": 1,
|
||||
"configuration": {
|
||||
"defaultTTL": 0,
|
||||
"useServerTs": false,
|
||||
"processingSettings": {
|
||||
"type": "ON_EVERY_MESSAGE"
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"type": "org.thingsboard.rule.engine.filter.TbCheckMessageNode",
|
||||
"name": "Has testData",
|
||||
"debugSettings": null,
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"messageNames": [
|
||||
"testData",
|
||||
"testDataCf"
|
||||
],
|
||||
"metadataNames": [],
|
||||
"checkAllKeys": false
|
||||
},
|
||||
"additionalInfo": {
|
||||
"description": "",
|
||||
"layoutX": 928,
|
||||
"layoutY": 266
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.filter.TbCheckMessageNode",
|
||||
"name": "Has testData",
|
||||
"singletonMode": false,
|
||||
"configurationVersion": 0,
|
||||
"configuration": {
|
||||
"messageNames": [
|
||||
"testData"
|
||||
],
|
||||
"metadataNames": [],
|
||||
"checkAllKeys": true
|
||||
},
|
||||
"externalId": null
|
||||
},
|
||||
{
|
||||
"additionalInfo": {
|
||||
"description": null,
|
||||
"layoutX": 1203,
|
||||
"layoutY": 327
|
||||
},
|
||||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||
"name": "Save Timeseries with TTL",
|
||||
"singletonMode": false,
|
||||
"configurationVersion": 1,
|
||||
"configuration": {
|
||||
"defaultTTL": 180,
|
||||
"useServerTs": false,
|
||||
"processingSettings": {
|
||||
"type": "ON_EVERY_MESSAGE"
|
||||
}
|
||||
},
|
||||
"externalId": null
|
||||
{
|
||||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode",
|
||||
"name": "Save Timeseries with TTL",
|
||||
"debugSettings": {
|
||||
"failuresEnabled": true,
|
||||
"allEnabled": false,
|
||||
"allEnabledUntil": 1742305233839
|
||||
},
|
||||
"singletonMode": false,
|
||||
"queueName": null,
|
||||
"configurationVersion": 1,
|
||||
"configuration": {
|
||||
"processingSettings": {
|
||||
"type": "ON_EVERY_MESSAGE"
|
||||
},
|
||||
"defaultTTL": 60,
|
||||
"useServerTs": null
|
||||
},
|
||||
"additionalInfo": {
|
||||
"description": "",
|
||||
"layoutX": 1203,
|
||||
"layoutY": 327
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
@ -352,23 +342,13 @@
|
||||
},
|
||||
{
|
||||
"fromIndex": 2,
|
||||
"toIndex": 16,
|
||||
"toIndex": 13,
|
||||
"type": "Post telemetry"
|
||||
},
|
||||
{
|
||||
"fromIndex": 6,
|
||||
"toIndex": 2,
|
||||
"type": "False"
|
||||
},
|
||||
{
|
||||
"fromIndex": 6,
|
||||
"toIndex": 7,
|
||||
"type": "True"
|
||||
},
|
||||
{
|
||||
"fromIndex": 7,
|
||||
"toIndex": 2,
|
||||
"type": "False"
|
||||
"type": "Success"
|
||||
},
|
||||
{
|
||||
"fromIndex": 7,
|
||||
@ -378,51 +358,36 @@
|
||||
{
|
||||
"fromIndex": 8,
|
||||
"toIndex": 2,
|
||||
"type": "Success"
|
||||
"type": "True"
|
||||
},
|
||||
{
|
||||
"fromIndex": 9,
|
||||
"toIndex": 10,
|
||||
"toIndex": 6,
|
||||
"type": "Success"
|
||||
},
|
||||
{
|
||||
"fromIndex": 10,
|
||||
"toIndex": 11,
|
||||
"type": "True"
|
||||
"type": "Success"
|
||||
},
|
||||
{
|
||||
"fromIndex": 11,
|
||||
"toIndex": 6,
|
||||
"type": "True"
|
||||
},
|
||||
{
|
||||
"fromIndex": 12,
|
||||
"toIndex": 9,
|
||||
"toIndex": 12,
|
||||
"type": "Success"
|
||||
},
|
||||
{
|
||||
"fromIndex": 13,
|
||||
"toIndex": 14,
|
||||
"type": "Success"
|
||||
},
|
||||
{
|
||||
"fromIndex": 14,
|
||||
"toIndex": 15,
|
||||
"type": "Success"
|
||||
},
|
||||
{
|
||||
"fromIndex": 16,
|
||||
"toIndex": 0,
|
||||
"type": "False"
|
||||
},
|
||||
{
|
||||
"fromIndex": 16,
|
||||
"toIndex": 17,
|
||||
"fromIndex": 13,
|
||||
"toIndex": 14,
|
||||
"type": "True"
|
||||
},
|
||||
{
|
||||
"fromIndex": 17,
|
||||
"toIndex": 13,
|
||||
"fromIndex": 14,
|
||||
"toIndex": 10,
|
||||
"type": "Success"
|
||||
}
|
||||
],
|
||||
Loading…
x
Reference in New Issue
Block a user