Browse Source
- Add SolutionService with install/delete/rollback flow, entity provisioning (customers, assets, devices, dashboards, edges, rule chains, CFs, users), telemetry emulators, instructions templating, and public customer support - Add SolutionInstallDialogComponent in home/components/solution with markdown details display and "Go to main dashboard" navigation - Update IoT Hub install dialog to open solution dialog on template success - Add SOLUTION_TEMPLATE type support across frontend (item cards, detail dialog, installed items table with dashboard entity mapping) - Add solution instructions button to item detail dialog for installed templates - Update edge detail URLs and dashboard links for correct CE routingpull/15347/head
89 changed files with 5008 additions and 35 deletions
File diff suppressed because it is too large
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions; |
|||
|
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
import org.thingsboard.server.service.solutions.data.solution.SolutionInstallResponse; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface SolutionService { |
|||
|
|||
SolutionInstallResponse installSolution(SecurityUser user, TenantId tenantId, byte[] zipData, HttpServletRequest request) throws Exception; |
|||
|
|||
void deleteSolution(TenantId tenantId, List<EntityId> createdEntityIds, SecurityUser user); |
|||
|
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data; |
|||
|
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public record CreatedAlarmRuleInfo(EntityId entityId, String entityName, String alarmType, String severities) { |
|||
|
|||
public String getCfPageLink(UUID cfId) { |
|||
return "/alarms/alarm-rules/" + cfId; |
|||
} |
|||
|
|||
public String getEntityPageLink() { |
|||
if (entityId == null) { |
|||
return null; |
|||
} |
|||
return switch (entityId.getEntityType()) { |
|||
case DEVICE_PROFILE -> "/profiles/deviceProfiles/" + entityId.getId(); |
|||
case ASSET_PROFILE -> "/profiles/assetProfiles/" + entityId.getId(); |
|||
default -> null; |
|||
}; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data; |
|||
|
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public record CreatedCalculatedFieldInfo(EntityId entityId, String entityName, String type, String name) { |
|||
|
|||
public String getCfPageLink(UUID cfId) { |
|||
return "/calculatedFields/" + cfId; |
|||
} |
|||
|
|||
public String getEntityPageLink() { |
|||
if (entityId == null) { |
|||
return null; |
|||
} |
|||
return switch (entityId.getEntityType()) { |
|||
case DEVICE_PROFILE -> "/profiles/deviceProfiles/" + entityId.getId(); |
|||
case ASSET_PROFILE -> "/profiles/assetProfiles/" + entityId.getId(); |
|||
default -> null; |
|||
}; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
public class CreatedEntityInfo { |
|||
|
|||
private String name; |
|||
private EntityType type; |
|||
private String owner; |
|||
|
|||
public String getEntityPageLink(UUID id) { |
|||
return switch (type) { |
|||
case CUSTOMER -> "/customers/" + id; |
|||
case USER -> "/users/" + id; |
|||
case ASSET -> "/entities/assets/" + id; |
|||
case DEVICE -> "/entities/devices/" + id; |
|||
case DEVICE_PROFILE -> "/profiles/deviceProfiles/" + id; |
|||
case ASSET_PROFILE -> "/profiles/assetProfiles/" + id; |
|||
case DASHBOARD -> "/dashboards/" + id; |
|||
case RULE_CHAIN -> "/ruleChains/" + id; |
|||
case EDGE -> "/edgeManagement/instances/" + id; |
|||
default -> null; |
|||
}; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.DashboardId; |
|||
|
|||
@Data |
|||
public class DashboardLinkInfo { |
|||
|
|||
private final String name; |
|||
private final DashboardId dashboardId; |
|||
private final boolean isPublic; |
|||
|
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentials; |
|||
|
|||
@Data |
|||
public class DeviceCredentialsInfo { |
|||
|
|||
String name; |
|||
String type; |
|||
DeviceCredentials credentials; |
|||
String customerName; |
|||
boolean gateway; |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.EdgeId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
|
|||
@Data |
|||
public class EdgeLinkInfo { |
|||
|
|||
private final EdgeId edgeId; |
|||
private final EntityId ownerId; |
|||
|
|||
} |
|||
@ -0,0 +1,208 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.asset.Asset; |
|||
import org.thingsboard.server.common.data.asset.AssetProfile; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
import org.thingsboard.server.common.data.edge.Edge; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
import org.thingsboard.server.common.data.rule.RuleChain; |
|||
import org.thingsboard.server.service.solutions.data.definition.AssetDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.AssetProfileDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.CustomerDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.DashboardDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.DeviceDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.EdgeDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.DeviceProfileDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.EntityDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.EntitySearchKey; |
|||
import org.thingsboard.server.service.solutions.data.definition.EmulatorDefinition; |
|||
import org.thingsboard.server.service.solutions.data.definition.RelationDefinition; |
|||
import org.thingsboard.server.service.solutions.data.solution.TenantSolutionTemplateInstructions; |
|||
|
|||
import java.nio.file.Path; |
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.LinkedHashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
public class SolutionInstallContext { |
|||
|
|||
private final TenantId tenantId; |
|||
private final Path tempDir; |
|||
private final SecurityUser user; |
|||
private final TenantSolutionTemplateInstructions solutionInstructions; |
|||
private final List<EntityId> createdEntitiesList = new ArrayList<>(); |
|||
private final Map<String, String> realIds = new HashMap<>(); |
|||
private final Map<EntitySearchKey, EntityId> entityIdMap = new HashMap<>(); |
|||
private final Map<EntityId, List<RelationDefinition>> relationDefinitions = new LinkedHashMap<>(); |
|||
private final List<DashboardLinkInfo> dashboardLinks = new ArrayList<>(); |
|||
|
|||
// For instructions
|
|||
private final Map<String, DeviceCredentialsInfo> createdDevices = new LinkedHashMap<>(); |
|||
private final Map<String, UserCredentialsInfo> createdUsers = new LinkedHashMap<>(); |
|||
private final Map<String, EdgeLinkInfo> createdEdges = new LinkedHashMap<>(); |
|||
private final Map<UUID, CreatedEntityInfo> createdEntities = new LinkedHashMap<>(); |
|||
private final Map<UUID, CreatedAlarmRuleInfo> createdAlarmRules = new LinkedHashMap<>(); |
|||
private final Map<UUID, CreatedCalculatedFieldInfo> createdCalculatedFields = new LinkedHashMap<>(); |
|||
private final long installTs; |
|||
private long oldestTelemetryTs; |
|||
private Map<String, EmulatorDefinition> deviceEmulators; |
|||
private Map<String, EmulatorDefinition> assetEmulators; |
|||
|
|||
public SolutionInstallContext(TenantId tenantId, Path tempDir, SecurityUser user) { |
|||
this.tenantId = tenantId; |
|||
this.tempDir = tempDir; |
|||
this.user = user; |
|||
this.solutionInstructions = new TenantSolutionTemplateInstructions(); |
|||
this.installTs = System.currentTimeMillis(); |
|||
put(new EntitySearchKey(tenantId, EntityType.TENANT, null), tenantId); |
|||
} |
|||
|
|||
public void registerReferenceOnly(String referenceId, EntityId entityId) { |
|||
if (!StringUtils.isEmpty(referenceId)) { |
|||
realIds.put(referenceId, entityId.getId().toString()); |
|||
} |
|||
} |
|||
|
|||
public void register(String referenceId, EntityId entityId) { |
|||
registerReferenceOnly(referenceId, entityId); |
|||
register(entityId); |
|||
} |
|||
|
|||
public void register(EntityId entityId) { |
|||
createdEntitiesList.add(entityId); |
|||
} |
|||
|
|||
public void register(String referenceId, RuleChain ruleChain) { |
|||
register(referenceId, ruleChain.getId()); |
|||
createdEntities.put(ruleChain.getUuidId(), new CreatedEntityInfo(ruleChain.getName(), EntityType.RULE_CHAIN, "Tenant")); |
|||
} |
|||
|
|||
public void register(DeviceProfileDefinition definition, DeviceProfile deviceProfile) { |
|||
register(definition.getJsonId(), deviceProfile.getId()); |
|||
createdEntities.put(deviceProfile.getUuidId(), new CreatedEntityInfo(deviceProfile.getName(), EntityType.DEVICE_PROFILE, "Tenant")); |
|||
} |
|||
|
|||
public void register(AssetProfileDefinition definition, AssetProfile assetProfile) { |
|||
register(definition.getJsonId(), assetProfile.getId()); |
|||
createdEntities.put(assetProfile.getUuidId(), new CreatedEntityInfo(assetProfile.getName(), EntityType.ASSET_PROFILE, "Tenant")); |
|||
} |
|||
|
|||
public void register(AssetDefinition definition, Asset asset) { |
|||
register(definition.getJsonId(), asset.getId()); |
|||
createdEntities.put(asset.getUuidId(), new CreatedEntityInfo(asset.getName(), EntityType.ASSET, |
|||
StringUtils.isEmpty(definition.getCustomer()) ? "Tenant" : definition.getCustomer())); |
|||
} |
|||
|
|||
public void register(DeviceDefinition definition, Device device) { |
|||
register(definition.getJsonId(), device.getId()); |
|||
createdEntities.put(device.getUuidId(), new CreatedEntityInfo(device.getName(), EntityType.DEVICE, |
|||
StringUtils.isEmpty(definition.getCustomer()) ? "Tenant" : definition.getCustomer())); |
|||
} |
|||
|
|||
public void register(CustomerDefinition definition, Customer customer) { |
|||
register(definition.getJsonId(), customer.getId()); |
|||
createdEntities.put(customer.getUuidId(), new CreatedEntityInfo(customer.getName(), EntityType.CUSTOMER, "Tenant")); |
|||
} |
|||
|
|||
public void register(CustomerDefinition customerDef, User user) { |
|||
register((String) null, user.getId()); |
|||
createdEntities.put(user.getUuidId(), new CreatedEntityInfo(user.getName(), EntityType.USER, |
|||
StringUtils.isEmpty(customerDef.getName()) ? "Tenant" : customerDef.getName())); |
|||
} |
|||
|
|||
public void register(EdgeDefinition definition, Edge edge) { |
|||
register(definition.getJsonId(), edge.getId()); |
|||
createdEntities.put(edge.getUuidId(), new CreatedEntityInfo(edge.getName(), EntityType.EDGE, |
|||
StringUtils.isEmpty(definition.getCustomer()) ? "Tenant" : definition.getCustomer())); |
|||
} |
|||
|
|||
public void register(DashboardDefinition definition, Dashboard dashboard) { |
|||
register(definition.getJsonId(), dashboard.getId()); |
|||
createdEntities.put(dashboard.getUuidId(), new CreatedEntityInfo(dashboard.getTitle(), EntityType.DASHBOARD, |
|||
StringUtils.isEmpty(definition.getCustomer()) ? "Tenant" : definition.getCustomer())); |
|||
} |
|||
|
|||
public void register(CalculatedField calculatedField) { |
|||
register(calculatedField.getId()); |
|||
EntityId entityId = calculatedField.getEntityId(); |
|||
CreatedEntityInfo entityInfo = createdEntities.get(entityId.getId()); |
|||
boolean alarmRule = false; // TODO: CE doesn't have ALARM calculated field type yet
|
|||
if (entityInfo == null) { |
|||
String target = alarmRule ? "Alarm rule" : "Calculated field"; |
|||
throw new IllegalStateException("Failed to register " + target + " with name: " + |
|||
calculatedField.getName() + " for non-existing entity with id: " + entityId); |
|||
} |
|||
if (alarmRule) { |
|||
createdAlarmRules.put(calculatedField.getUuidId(), new CreatedAlarmRuleInfo(entityId, entityInfo.getName(), calculatedField.getName(), null)); |
|||
return; |
|||
} |
|||
createdCalculatedFields.put(calculatedField.getUuidId(), new CreatedCalculatedFieldInfo(entityId, entityInfo.getName(), calculatedField.getType().name(), calculatedField.getName())); |
|||
} |
|||
|
|||
public void putIdToMap(EntityDefinition entityDefinition, EntityId entityId) { |
|||
putIdToMap(entityDefinition.getEntityType(), entityDefinition.getName(), entityId); |
|||
} |
|||
|
|||
public void putIdToMap(EntityType entityType, String entityName, EntityId entityId) { |
|||
putIdToMap(tenantId, entityType, entityName, entityId); |
|||
} |
|||
|
|||
public void putIdToMap(EntityId ownerId, EntityType entityType, String entityName, EntityId entityId) { |
|||
put(new EntitySearchKey(ownerId, entityType, entityName), entityId); |
|||
} |
|||
|
|||
public void put(EntitySearchKey entitySearchKey, EntityId entityId) { |
|||
entityIdMap.put(entitySearchKey, entityId); |
|||
} |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
public <T extends EntityId> T getIdFromMap(EntityType entityType, String entityName) { |
|||
return (T) entityIdMap.get(new EntitySearchKey(tenantId, entityType, entityName)); |
|||
} |
|||
|
|||
public void put(EntityId entityId, List<RelationDefinition> relations) { |
|||
relationDefinitions.put(entityId, relations); |
|||
} |
|||
|
|||
public void addDeviceCredentials(DeviceCredentialsInfo deviceCredentialsInfo) { |
|||
createdDevices.put(deviceCredentialsInfo.getName(), deviceCredentialsInfo); |
|||
} |
|||
|
|||
public void addUserCredentials(UserCredentialsInfo userCredentialsInfo) { |
|||
createdUsers.put(userCredentialsInfo.getName(), userCredentialsInfo); |
|||
} |
|||
|
|||
public void addEdgeLinkInfo(String edgeName, EdgeLinkInfo edgeLinkInfo) { |
|||
createdEdges.put(edgeName, edgeLinkInfo); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class UserCredentialsInfo { |
|||
|
|||
String name; |
|||
String login; |
|||
String password; |
|||
String customerName; |
|||
|
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class AssetDefinition extends CustomerEntityDefinition { |
|||
|
|||
private String type; |
|||
private String label; |
|||
private String emulator; |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.ASSET; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.asset.AssetProfile; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class AssetProfileDefinition extends AssetProfile { |
|||
|
|||
private String jsonId; |
|||
|
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public abstract class BaseEntityDefinition implements EntityDefinition { |
|||
|
|||
@JsonProperty("name") |
|||
private String name; |
|||
@JsonProperty("attributes") |
|||
private JsonNode attributes; |
|||
@JsonProperty("sharedAttributes") |
|||
private JsonNode sharedAttributes; |
|||
@JsonProperty("relations") |
|||
private List<RelationDefinition> relations = Collections.emptyList(); |
|||
@JsonProperty("jsonId") |
|||
private String jsonId; |
|||
|
|||
public void setRelations(List<RelationDefinition> relations) { |
|||
if (relations != null) { |
|||
this.relations = relations; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.cf.CalculatedField; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class CalculatedFieldDefinition extends CalculatedField { |
|||
|
|||
private Integer reprocessingOrder; |
|||
|
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnore; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.service.solutions.data.names.RandomNameData; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@NoArgsConstructor |
|||
public class CustomerDefinition extends BaseEntityDefinition { |
|||
|
|||
private String email; |
|||
private String country; |
|||
private String city; |
|||
private String state; |
|||
private String zip; |
|||
private String address; |
|||
private List<UserDefinition> users = Collections.emptyList(); |
|||
|
|||
@JsonIgnore |
|||
private RandomNameData randomNameData; |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.CUSTOMER; |
|||
} |
|||
|
|||
public void setUsers(List<UserDefinition> users) { |
|||
if (users != null) { |
|||
this.users = users; |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public abstract class CustomerEntityDefinition extends BaseEntityDefinition { |
|||
|
|||
private String customer; |
|||
private boolean makePublic; |
|||
|
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class DashboardDefinition extends CustomerEntityDefinition { |
|||
|
|||
private String file; |
|||
private boolean main; |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.DASHBOARD; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class DashboardUserDetailsDefinition { |
|||
|
|||
private String name; |
|||
private boolean fullScreen; |
|||
|
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class DeviceDefinition extends CustomerEntityDefinition { |
|||
|
|||
private String type; |
|||
private String label; |
|||
private String emulator; |
|||
private JsonNode additionalInfo; |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.DEVICE; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.DeviceProfile; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class DeviceProfileDefinition extends DeviceProfile { |
|||
|
|||
private String jsonId; |
|||
|
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class EdgeDefinition extends CustomerEntityDefinition { |
|||
|
|||
private String type; |
|||
private String label; |
|||
private String rootRuleChainId; |
|||
private List<String> ruleChainIds = Collections.emptyList(); |
|||
private List<String> deviceIds = Collections.emptyList(); |
|||
private List<String> assetIds = Collections.emptyList(); |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.EDGE; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.function.Function; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Data |
|||
public class EmulatorDefinition { |
|||
private String name; |
|||
private String extendz; |
|||
private String clazz; |
|||
private int publishPeriodInDays; |
|||
private int publishFrequencyInSeconds; |
|||
private int publishPauseInMillis; |
|||
private long activityPeriodInMillis; |
|||
private List<TelemetryProfile> telemetryProfiles = Collections.emptyList(); |
|||
|
|||
public void enrich(EmulatorDefinition parent) { |
|||
if (StringUtils.isEmpty(clazz)) { |
|||
clazz = parent.getClazz(); |
|||
} |
|||
if (publishPeriodInDays == 0) { |
|||
publishPeriodInDays = parent.getPublishPeriodInDays(); |
|||
} |
|||
if (publishFrequencyInSeconds == 0) { |
|||
publishFrequencyInSeconds = parent.getPublishFrequencyInSeconds(); |
|||
} |
|||
if (publishPauseInMillis == 0) { |
|||
publishPauseInMillis = parent.getPublishPauseInMillis(); |
|||
} |
|||
if (activityPeriodInMillis == 0L) { |
|||
activityPeriodInMillis = parent.getActivityPeriodInMillis(); |
|||
} |
|||
var profilesMap = telemetryProfiles.stream().collect(Collectors.toMap(TelemetryProfile::getKey, Function.identity())); |
|||
parent.getTelemetryProfiles().forEach(tp -> profilesMap.putIfAbsent(tp.getKey(), tp)); |
|||
telemetryProfiles = new ArrayList<>(profilesMap.values()); |
|||
} |
|||
|
|||
public long getOldestTs(long startTs) { |
|||
return startTs - TimeUnit.DAYS.toMillis(publishPeriodInDays) - publishFrequencyInSeconds; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
public interface EntityDefinition { |
|||
|
|||
String getName(); |
|||
|
|||
EntityType getEntityType(); |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
public class EntitySearchKey { |
|||
|
|||
private final EntityId ownerId; |
|||
private final EntityType entityType; |
|||
private final String entityName; |
|||
|
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class ReferenceableEntityDefinition { |
|||
|
|||
private String name; |
|||
private String file; |
|||
private String update; |
|||
private String jsonId; |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
|||
|
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class RelationDefinition { |
|||
|
|||
private EntityType entityType; |
|||
private String entityName; |
|||
private EntitySearchDirection direction; |
|||
private String type; |
|||
|
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.service.solutions.data.values.ValueStrategyDefinition; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
public class TelemetryProfile { |
|||
|
|||
private String key; |
|||
private ValueStrategyDefinition valueStrategy; |
|||
|
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
public class TenantDefinition extends BaseEntityDefinition { |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.TENANT; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.definition; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class UserDefinition extends BaseEntityDefinition { |
|||
|
|||
private String firstname; |
|||
private String lastname; |
|||
private String password; |
|||
private DashboardUserDetailsDefinition dashboard; |
|||
|
|||
@Override |
|||
public EntityType getEntityType() { |
|||
return EntityType.USER; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,195 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.emulator; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import lombok.Data; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.data.util.Pair; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.cluster.TbClusterService; |
|||
import org.thingsboard.server.common.data.HasName; |
|||
import org.thingsboard.server.common.data.HasTenantId; |
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
import org.thingsboard.server.common.data.id.HasId; |
|||
import org.thingsboard.server.common.data.msg.TbMsgType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.common.msg.TbMsgDataType; |
|||
import org.thingsboard.server.common.msg.TbMsgMetaData; |
|||
import org.thingsboard.server.queue.TbQueueCallback; |
|||
import org.thingsboard.server.queue.TbQueueMsgMetadata; |
|||
import org.thingsboard.server.queue.discovery.PartitionService; |
|||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
|||
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
|||
import org.thingsboard.server.service.solutions.data.definition.EmulatorDefinition; |
|||
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
|||
|
|||
import java.util.concurrent.CompletableFuture; |
|||
import java.util.concurrent.ExecutorService; |
|||
import java.util.concurrent.ScheduledFuture; |
|||
import java.util.concurrent.TimeUnit; |
|||
import java.util.concurrent.atomic.AtomicInteger; |
|||
|
|||
@Slf4j |
|||
@Data |
|||
public abstract class AbstractEmulatorLauncher<T extends HasId<? extends EntityId> & HasTenantId & HasName> { |
|||
|
|||
protected static final TbQueueCallback EMPTY_CALLBACK = new TbQueueCallback() { |
|||
@Override |
|||
public void onSuccess(TbQueueMsgMetadata metadata) { |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(Throwable t) { |
|||
|
|||
} |
|||
}; |
|||
protected final T entity; |
|||
protected final EmulatorDefinition emulatorDefinition; |
|||
protected final ExecutorService oldTelemetryExecutor; |
|||
protected final TbClusterService tbClusterService; |
|||
protected final PartitionService partitionService; |
|||
protected final TbQueueProducerProvider tbQueueProducerProvider; |
|||
protected final TbServiceInfoProvider serviceInfoProvider; |
|||
protected final TelemetrySubscriptionService tsSubService; |
|||
protected final long publishFrequency; |
|||
|
|||
private final Emulator emulator; |
|||
private ScheduledFuture<?> scheduledFuture; |
|||
|
|||
public AbstractEmulatorLauncher(T entity, |
|||
EmulatorDefinition emulatorDefinition, |
|||
ExecutorService oldTelemetryExecutor, |
|||
TbClusterService tbClusterService, |
|||
PartitionService partitionService, |
|||
TbQueueProducerProvider tbQueueProducerProvider, |
|||
TbServiceInfoProvider serviceInfoProvider, |
|||
TelemetrySubscriptionService tsSubService) throws Exception { |
|||
this.entity = entity; |
|||
this.emulatorDefinition = emulatorDefinition; |
|||
this.oldTelemetryExecutor = oldTelemetryExecutor; |
|||
this.tbClusterService = tbClusterService; |
|||
this.partitionService = partitionService; |
|||
this.tbQueueProducerProvider = tbQueueProducerProvider; |
|||
this.serviceInfoProvider = serviceInfoProvider; |
|||
this.tsSubService = tsSubService; |
|||
this.publishFrequency = TimeUnit.SECONDS.toMillis(Math.max(emulatorDefinition.getPublishFrequencyInSeconds(), 1)); |
|||
if (StringUtils.isEmpty(emulatorDefinition.getClazz())) { |
|||
emulator = new BasicEmulator(); |
|||
} else { |
|||
emulator = (Emulator) Class.forName(emulatorDefinition.getClazz()).getDeclaredConstructor().newInstance(); |
|||
} |
|||
emulator.init(emulatorDefinition); |
|||
} |
|||
|
|||
public CompletableFuture<Void> launch() { |
|||
final long oldestTs = emulatorDefinition.getOldestTs(System.currentTimeMillis()); |
|||
CompletableFuture<Void> future = new CompletableFuture<>(); |
|||
oldTelemetryExecutor.submit(() -> { |
|||
AtomicInteger pending = new AtomicInteger(1); |
|||
try { |
|||
if (emulator instanceof SimpleEmulator) { |
|||
if (oldestTs < (System.currentTimeMillis() - publishFrequency)) { |
|||
pushOldTelemetry(oldestTs, pending, future); |
|||
} |
|||
} else if (emulator instanceof CustomEmulator customEmulator) { |
|||
Pair<Long, ObjectNode> telemetry = customEmulator.getNextValue(); |
|||
while (telemetry != null) { |
|||
pending.incrementAndGet(); |
|||
publishTelemetry(telemetry.getFirst(), telemetry.getSecond(), pending, future); |
|||
telemetry = customEmulator.getNextValue(); |
|||
} |
|||
} |
|||
} catch (Exception e) { |
|||
log.warn("Telemetry upload failed for {}", entity.getName(), e); |
|||
future.completeExceptionally(e); |
|||
} finally { |
|||
if (pending.decrementAndGet() == 0 && !future.isDone()) { |
|||
log.trace("[{}] Telemetry emulation finished. Producer and all callbacks completed successfully", entity.getName()); |
|||
future.complete(null); |
|||
} |
|||
} |
|||
|
|||
future.whenComplete((v, t) -> { |
|||
try { |
|||
postProcessEntity(entity); |
|||
} catch (Exception e) { |
|||
log.warn("Post-processing failed for {}", entity.getName(), e); |
|||
} |
|||
}); |
|||
|
|||
}); |
|||
return future; |
|||
} |
|||
|
|||
protected void postProcessEntity(T entity) { |
|||
} |
|||
|
|||
private void pushOldTelemetry(long oldestTs, AtomicInteger pending, CompletableFuture<Void> future) throws InterruptedException { |
|||
for (long ts = oldestTs; ts < System.currentTimeMillis(); ts += publishFrequency) { |
|||
pending.incrementAndGet(); |
|||
publishTelemetry(ts, pending, future); |
|||
} |
|||
} |
|||
|
|||
private void publishTelemetry(long ts, AtomicInteger pending, CompletableFuture<Void> future) throws InterruptedException { |
|||
ObjectNode values = ((SimpleEmulator) emulator).getValue(ts); |
|||
publishTelemetry(ts, values, pending, future); |
|||
Thread.sleep(emulatorDefinition.getPublishPauseInMillis()); |
|||
} |
|||
|
|||
public void stop() { |
|||
scheduledFuture.cancel(true); |
|||
} |
|||
|
|||
private void publishTelemetry(long ts, ObjectNode value, AtomicInteger pending, CompletableFuture<Void> future) { |
|||
String msgData = JacksonUtil.toString(value); |
|||
log.debug("[{}] Publishing telemetry: {}", entity.getName(), msgData); |
|||
TbMsgMetaData md = new TbMsgMetaData(); |
|||
md.putValue("ts", Long.toString(ts)); |
|||
TbMsg tbMsg = TbMsg.newMsg() |
|||
.type(TbMsgType.POST_TELEMETRY_REQUEST) |
|||
.originator(entity.getId()) |
|||
.copyMetaData(md) |
|||
.dataType(TbMsgDataType.JSON) |
|||
.data(msgData) |
|||
.build(); |
|||
|
|||
TbQueueCallback callback = new TbQueueCallback() { |
|||
|
|||
@Override |
|||
public void onSuccess(TbQueueMsgMetadata metadata) { |
|||
log.debug("[{}] Successfully pushed message to Rule Engine for ts {}", entity.getName(), ts); |
|||
if (pending.decrementAndGet() == 0 && !future.isDone()) { |
|||
log.trace("[{}] Completing emulation future from callback for msg with ts: {}", entity.getName(), ts); |
|||
future.complete(null); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(Throwable t) { |
|||
log.warn("[{}] Telemetry upload failed for ts {}", entity.getName(), ts, t); |
|||
if (pending.decrementAndGet() == 0 && !future.isDone()) { |
|||
future.completeExceptionally(t); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
tbClusterService.pushMsgToRuleEngine(entity.getTenantId(), entity.getId(), tbMsg, callback); |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.emulator; |
|||
|
|||
import lombok.Builder; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.thingsboard.server.cluster.TbClusterService; |
|||
import org.thingsboard.server.common.data.asset.Asset; |
|||
import org.thingsboard.server.queue.discovery.PartitionService; |
|||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
|||
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
|||
import org.thingsboard.server.service.solutions.data.definition.EmulatorDefinition; |
|||
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
|||
|
|||
import java.util.concurrent.ExecutorService; |
|||
|
|||
@Slf4j |
|||
public class AssetEmulatorLauncher extends AbstractEmulatorLauncher<Asset> { |
|||
|
|||
@Builder |
|||
public AssetEmulatorLauncher(Asset entity, EmulatorDefinition emulatorDefinition, ExecutorService oldTelemetryExecutor, TbClusterService tbClusterService, |
|||
PartitionService partitionService, |
|||
TbQueueProducerProvider tbQueueProducerProvider, |
|||
TbServiceInfoProvider serviceInfoProvider, |
|||
TelemetrySubscriptionService tsSubService) throws Exception { |
|||
super(entity, emulatorDefinition, oldTelemetryExecutor, tbClusterService, partitionService, tbQueueProducerProvider, serviceInfoProvider, tsSubService); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.emulator; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.service.solutions.data.definition.EmulatorDefinition; |
|||
import org.thingsboard.server.service.solutions.data.values.TelemetryGenerator; |
|||
import org.thingsboard.server.service.solutions.data.values.TelemetryGeneratorFactory; |
|||
|
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
public class BasicEmulator implements SimpleEmulator { |
|||
|
|||
private final Map<String, TelemetryGenerator> tsGenerators = new HashMap<>(); |
|||
|
|||
@Override |
|||
public void init(EmulatorDefinition emulatorDefinition) { |
|||
emulatorDefinition.getTelemetryProfiles().forEach(tp -> tsGenerators.put(tp.getKey(), TelemetryGeneratorFactory.create(tp))); |
|||
} |
|||
|
|||
@Override |
|||
public ObjectNode getValue(long ts) { |
|||
ObjectNode values = JacksonUtil.newObjectNode(); |
|||
tsGenerators.values().forEach(gen -> gen.addValue(ts, values)); |
|||
return values; |
|||
} |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.emulator; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.springframework.data.util.Pair; |
|||
|
|||
public interface CustomEmulator extends Emulator { |
|||
|
|||
Pair<Long, ObjectNode> getNextValue(); |
|||
|
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.emulator; |
|||
|
|||
import com.google.common.util.concurrent.FutureCallback; |
|||
import lombok.Builder; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.checkerframework.checker.nullness.qual.Nullable; |
|||
import org.thingsboard.rule.engine.api.AttributesSaveRequest; |
|||
import org.thingsboard.server.cluster.TbClusterService; |
|||
import org.thingsboard.server.common.data.AttributeScope; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.kv.LongDataEntry; |
|||
import org.thingsboard.server.common.msg.queue.ServiceType; |
|||
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
|||
import org.thingsboard.server.gen.transport.TransportProtos; |
|||
import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
|||
import org.thingsboard.server.queue.discovery.PartitionService; |
|||
import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
|||
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
|||
import org.thingsboard.server.service.solutions.data.definition.EmulatorDefinition; |
|||
import org.thingsboard.server.service.state.DefaultDeviceStateService; |
|||
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
|||
|
|||
import java.util.UUID; |
|||
import java.util.concurrent.ExecutorService; |
|||
|
|||
@Slf4j |
|||
public class DeviceEmulatorLauncher extends AbstractEmulatorLauncher<Device> { |
|||
|
|||
@Builder |
|||
public DeviceEmulatorLauncher(Device entity, |
|||
EmulatorDefinition emulatorDefinition, |
|||
ExecutorService oldTelemetryExecutor, |
|||
TbClusterService tbClusterService, |
|||
PartitionService partitionService, |
|||
TbQueueProducerProvider tbQueueProducerProvider, |
|||
TbServiceInfoProvider serviceInfoProvider, |
|||
TelemetrySubscriptionService tsSubService) throws Exception { |
|||
super(entity, emulatorDefinition, oldTelemetryExecutor, tbClusterService, partitionService, tbQueueProducerProvider, serviceInfoProvider, tsSubService); |
|||
} |
|||
|
|||
@Override |
|||
protected void postProcessEntity(Device entity) { |
|||
if (this.emulatorDefinition.getActivityPeriodInMillis() > 0) { |
|||
tsSubService.saveAttributes(AttributesSaveRequest.builder() |
|||
.tenantId(entity.getTenantId()) |
|||
.entityId(entity.getId()) |
|||
.scope(AttributeScope.SERVER_SCOPE) |
|||
.entry(new LongDataEntry(DefaultDeviceStateService.INACTIVITY_TIMEOUT, this.emulatorDefinition.getActivityPeriodInMillis())) |
|||
.callback(new FutureCallback<>() { |
|||
@Override |
|||
public void onSuccess(@Nullable Void unused) { |
|||
TopicPartitionInfo tpi = partitionService.resolve(ServiceType.TB_CORE, entity.getTenantId(), entity.getId()); |
|||
UUID sessionId = UUID.randomUUID(); |
|||
TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() |
|||
.setSessionInfo(TransportProtos.SessionInfoProto.newBuilder() |
|||
.setSessionIdMSB(sessionId.getMostSignificantBits()) |
|||
.setSessionIdLSB(sessionId.getLeastSignificantBits()) |
|||
.setDeviceIdMSB(entity.getId().getId().getMostSignificantBits()) |
|||
.setDeviceIdLSB(entity.getId().getId().getLeastSignificantBits()) |
|||
.setDeviceProfileIdMSB(entity.getId().getId().getMostSignificantBits()) |
|||
.setDeviceProfileIdLSB(entity.getId().getId().getLeastSignificantBits()) |
|||
.setDeviceName(entity.getName()) |
|||
.setDeviceType(entity.getType()) |
|||
.setTenantIdMSB(entity.getTenantId().getId().getMostSignificantBits()) |
|||
.setTenantIdLSB(entity.getTenantId().getId().getLeastSignificantBits()) |
|||
.setNodeId(serviceInfoProvider.getServiceId()) |
|||
.build()) |
|||
.setSubscriptionInfo(TransportProtos.SubscriptionInfoProto.newBuilder().setLastActivityTime(System.currentTimeMillis()).build()) |
|||
.build(); |
|||
tbQueueProducerProvider.getTbCoreMsgProducer().send(tpi, |
|||
new TbProtoQueueMsg<>(entity.getUuidId(), |
|||
TransportProtos.ToCoreMsg.newBuilder().setToDeviceActorMsg(msg).build()), EMPTY_CALLBACK |
|||
); |
|||
} |
|||
|
|||
@Override |
|||
public void onFailure(Throwable throwable) { |
|||
} |
|||
}) |
|||
.build()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.emulator; |
|||
|
|||
import org.thingsboard.server.service.solutions.data.definition.EmulatorDefinition; |
|||
|
|||
public interface Emulator { |
|||
|
|||
void init(EmulatorDefinition emulatorDefinition); |
|||
|
|||
} |
|||
@ -0,0 +1,108 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.emulator; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.springframework.data.util.Pair; |
|||
import org.thingsboard.common.util.JacksonUtil; |
|||
import org.thingsboard.server.service.solutions.data.definition.EmulatorDefinition; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Calendar; |
|||
import java.util.List; |
|||
import java.util.TimeZone; |
|||
|
|||
public class FieldIrrigationStateEmulator implements CustomEmulator { |
|||
|
|||
private static final int START_HOUR = 6; |
|||
private int idx; |
|||
private List<Pair<Long, ObjectNode>> data = new ArrayList<>(); |
|||
|
|||
@Override |
|||
public void init(EmulatorDefinition emulatorDefinition) { |
|||
idx = 0; |
|||
var tz = TimeZone.getTimeZone("America/New_York"); |
|||
Calendar c = Calendar.getInstance(); |
|||
c.setTimeInMillis(System.currentTimeMillis()); |
|||
c.setTimeZone(tz); |
|||
int curHour = c.get(Calendar.HOUR_OF_DAY); |
|||
c.add(Calendar.DAY_OF_MONTH, curHour < START_HOUR ? -7 : -6); |
|||
c.set(Calendar.HOUR_OF_DAY, START_HOUR); |
|||
c.set(Calendar.MINUTE, 0); |
|||
|
|||
add(c, JacksonUtil.newObjectNode() |
|||
.put("startTs", c.getTimeInMillis()) |
|||
.put("durationThreshold", 30 * 60000) |
|||
.put("consumption", 423) |
|||
.put("duration", 30 * 60000) |
|||
); |
|||
add(c, JacksonUtil.newObjectNode() |
|||
.put("startTs", c.getTimeInMillis()) |
|||
.put("durationThreshold", 30 * 60000) |
|||
.put("consumption", 407) |
|||
.put("duration", 30 * 60000) |
|||
); |
|||
add(c, JacksonUtil.newObjectNode() |
|||
.put("startTs", c.getTimeInMillis()) |
|||
.put("consumptionThreshold", 1000) |
|||
.put("consumption", 1000) |
|||
.put("duration", 63 * 60000) |
|||
); |
|||
add(c, JacksonUtil.newObjectNode() |
|||
.put("startTs", c.getTimeInMillis()) |
|||
.put("consumptionThreshold", 500) |
|||
.put("consumption", 500) |
|||
.put("duration", 36 * 60000) |
|||
); |
|||
add(c, JacksonUtil.newObjectNode() |
|||
.put("startTs", c.getTimeInMillis()) |
|||
.put("consumptionThreshold", 1000) |
|||
.put("consumption", 1000) |
|||
.put("duration", 63 * 60000) |
|||
); |
|||
add(c, JacksonUtil.newObjectNode() |
|||
.put("startTs", c.getTimeInMillis()) |
|||
.put("durationThreshold", 30 * 60000) |
|||
.put("duration", 30 * 60000) |
|||
.put("consumption", 452) |
|||
); |
|||
add(c, JacksonUtil.newObjectNode() |
|||
.put("startTs", c.getTimeInMillis()) |
|||
.put("durationThreshold", 30 * 60000) |
|||
.put("duration", 30 * 60000) |
|||
.put("consumption", 447) |
|||
); |
|||
} |
|||
|
|||
private void add(Calendar c, ObjectNode objectNode) { |
|||
ObjectNode startIrrigationMsg = JacksonUtil.newObjectNode(); |
|||
startIrrigationMsg.put("irrigationState", "DONE"); |
|||
startIrrigationMsg.set("irrigationTask", objectNode); |
|||
data.add(Pair.of(c.getTimeInMillis(), startIrrigationMsg)); |
|||
c.add(Calendar.DAY_OF_MONTH, 1); |
|||
} |
|||
|
|||
@Override |
|||
public Pair<Long, ObjectNode> getNextValue() { |
|||
if (idx < data.size()) { |
|||
var result = data.get(idx); |
|||
idx++; |
|||
return result; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.emulator; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
|
|||
public interface SimpleEmulator extends Emulator { |
|||
|
|||
ObjectNode getValue(long ts); |
|||
|
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.names; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class RandomNameData { |
|||
|
|||
private final String firstName; |
|||
private final String lastName; |
|||
private final String email; |
|||
} |
|||
@ -0,0 +1,100 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.names; |
|||
|
|||
import org.thingsboard.server.common.data.StringUtils; |
|||
|
|||
import java.util.Random; |
|||
|
|||
public class RandomNameUtil { |
|||
|
|||
private static final Random RANDOM = new Random(System.currentTimeMillis()); |
|||
|
|||
private static String[] FIRST_NAMES = new String[]{ |
|||
"Nadia", "Lynne", "Lera", "Holly", "Zina", "Mandi", "Kasie", "Josef", "Liane", "Man", |
|||
"Karan", "Nga", "Tesha", "Elva", "Bree", "Ida", "Mimi", "Altha", "Omar", "Ollie", |
|||
"Nicol", "Ned", "Teri", "Sabra", "Kari", "Eva", "Penni", "Simon", "Kaci", "Jess", |
|||
"Bea", "Ron", "Melba", "Vella", "Nada", "Cyndi", "Audry", "Helga", "Chana", "Lola", |
|||
"Terry", "Tami", "Dedra", "Erwin", "Anne", "Zoila", "Nelia", "Hyman", "Deane", "Erica", |
|||
"Amos", "Maura", "Gwenn", "Evan", "Lelia", "Grant", "Abe", "Fanny", "Cindi", "Pilar", |
|||
"Darcy", "Len", "Casie", "Jose", "Kylie", "Cami", "Casey", "Kerri", "Bruno", "Theda", |
|||
"Ardis", "Carin", "Belia", "Jeff", "Yetta", "Ola", "Lyla", "Megan", "Zita", "Rocky", |
|||
"Darci", "Dale", "Mirta", "Tanja", "Stacy", "Julie", "Aisha", "Avis", "Hugh", "Anh", |
|||
"Bud", "Earle", "Ossie", "Odell", "Dovie", "Afton", "Joel", "Aida", "Vina", "Emiko", |
|||
"Tori", "Keena", "Addie", "Nancy", "Tonda", "Josue", "Dina", "Mitch", "Thea", "Cole", |
|||
"Lyman", "Donna", "Roma", "Deena", "Lue", "Bette", "Ilene", "Vera", "Kirby", "Lenna", |
|||
"Pat", "Grace", "Gus", "Frank", "Lena", "Adele", "Kerry", "Santo", "Wade", "Trudi", |
|||
"Bruce", "Raul", "Katy", "Heidi", "Marna", "Faith", "Ronna", "Kylee", "Emma", "Luna", |
|||
"Ilda", "Mose", "Juan", "Cori", "Gayla", "Otha", "Jung", "Bambi", "Joi", "Sibyl", |
|||
"Reid", "Bonny", "Roni", "Joy", "Farah", "Jeane", "Jill", "Tisha", "Leon", "Leigh", |
|||
"Inge", "Zella", "Rubin", "Carri", "Nana", "Sofia", "Lucio", "Eboni", "Adam", "Lino", |
|||
"Elvis", "Marci", "Adina", "Nanci", "Joyce", "James", "Louis", "Aurea", "Chi", "Kathy", |
|||
"Nina", "Lise", "Reda", "Candi", "Ralph", "Velva", "Mari", "Mavis", "Kory", "Tammi", |
|||
"Lucy", "Lavon", "Ana", "Lin", "Alena", "Haley", "Myra", "Amy", "Marlo", "Ami" |
|||
}; |
|||
|
|||
private static String[] LAST_NAMES = new String[]{ |
|||
"Vinson", "Rogers", "Burkett", "Gamboa", "Gross", "Toledo", "Kiser", "Harman", "Pierce", "Lovett", |
|||
"Sexton", "Coates", "Seymour", "Holland", "Finley", "Hagen", "Schmitt", "Beach", "Rea", "Peck", |
|||
"Noel", "Archer", "Zamora", "Wood", "Rivera", "Lentz", "Alonso", "Rider", "Story", "Schwab", |
|||
"Mercado", "Caruso", "Bynum", "Spears", "Bingham", "Tyler", "Ponce", "Skaggs", "Matos", "Stinson", |
|||
"Burgos", "Coles", "Helton", "Joyce", "Galvan", "Grant", "Stahl", "Daigle", "Hayden", "Quiroz", |
|||
"Horne", "Shaw", "Addison", "Harvey", "Watkins", "Romano", "Ahmed", "Lind", "Swenson", "Kemp", |
|||
"Barnes", "Horton", "Dugan", "Farr", "Swanson", "Hale", "Hidalgo", "Schulte", "Ervin", "Osorio", |
|||
"Lincoln", "Zhang", "Oakes", "Stiles", "Lu", "Culver", "Singer", "Knott", "Bullard", "Butts", |
|||
"Coley", "Roth", "Cobb", "Darnell", "Vaughan", "Conrad", "Austin", "Rico", "Hobson", "Brunner", |
|||
"Mccray", "Mattson", "Barber", "Clement", "Ly", "Dejesus", "Painter", "Macias", "Healy", "Duncan", |
|||
"Forrest", "Hurley", "Torres", "Blue", "Rucker", "Ferrell", "Maddox", "Osborne", "Holt", "Briggs", |
|||
"Irvin", "Bassett", "Urban", "Overton", "Sloan", "Guzman", "Estrada", "Redding", "Gold", "Paz", |
|||
"Lucero", "Reyes", "Conner", "Keith", "Hays", "Hutton", "Moore", "Tuttle", "Powers", "William", |
|||
"Reeder", "Castro", "Vela", "Baca", "Frazier", "Kline", "Huggins", "Zepeda", "Pate", "Marion", |
|||
"Hollis", "Ybarra", "Cuellar", "Gallo", "Gomes", "Louis", "Bolden", "Liu", "Hayes", "Simpson", |
|||
"Otero", "Conway", "Peoples", "Mohr", "Loomis", "Engel", "Hughes", "Chapman", "Bower", "Schmidt", |
|||
"Mcmahon", "Booth", "Ward", "Drake", "Cutler", "Gordon", "Black", "Clifton", "Curran", "Pierre", |
|||
"Workman", "Tilley", "Moody", "Kuhn", "Eastman", "Scott", "Vang", "Tillman", "Cleary", "Yi", |
|||
"Griffin", "Mendoza", "Mason", "Pittman", "Landis", "Gay", "Rowland", "Jordan", "Lo", "Kumar", |
|||
"Vernon", "Boyd", "Rocha", "Mcqueen", "Cornett", "Deaton", "Borden", "Lacey", "Gaston", "Miles" |
|||
}; |
|||
|
|||
public static String nextFirstName() { |
|||
return randomElement(FIRST_NAMES); |
|||
} |
|||
|
|||
public static String nextLastName() { |
|||
return randomElement(LAST_NAMES); |
|||
} |
|||
|
|||
private static String toEmail(String firstName, String lastName) { |
|||
return firstName.toLowerCase() + "." + lastName.toLowerCase() + "@thingsboard.io"; |
|||
} |
|||
|
|||
public static RandomNameData next() { |
|||
var firstName = nextFirstName(); |
|||
var lastName = nextLastName(); |
|||
return new RandomNameData(firstName, lastName, toEmail(firstName, lastName)); |
|||
} |
|||
|
|||
public static RandomNameData nextSuperRandom() { |
|||
var firstName = nextFirstName() + StringUtils.randomAlphanumeric(10).toLowerCase(); |
|||
var lastName = nextLastName() + StringUtils.randomAlphanumeric(10).toLowerCase(); |
|||
return new RandomNameData(firstName, lastName, toEmail(firstName, lastName)); |
|||
} |
|||
|
|||
private static String randomElement(String[] array) { |
|||
return array[RANDOM.nextInt(array.length)]; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.solution; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Schema |
|||
@Data |
|||
public class SolutionInstallResponse extends TenantSolutionTemplateInstructions { |
|||
|
|||
@Schema(description = "Indicates that template was installed successfully") |
|||
private boolean success; |
|||
@Schema(description = "List of entity IDs created during solution installation") |
|||
private List<EntityId> createdEntityIds; |
|||
|
|||
public SolutionInstallResponse(TenantSolutionTemplateInstructions instructions, boolean success, List<EntityId> createdEntityIds) { |
|||
super(instructions); |
|||
this.success = success; |
|||
this.createdEntityIds = createdEntityIds; |
|||
} |
|||
|
|||
public SolutionInstallResponse() { |
|||
super(); |
|||
} |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.solution; |
|||
|
|||
import io.swagger.v3.oas.annotations.media.Schema; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.DashboardId; |
|||
|
|||
@Schema |
|||
@Data |
|||
@AllArgsConstructor |
|||
@NoArgsConstructor |
|||
public class TenantSolutionTemplateInstructions { |
|||
|
|||
@Schema(description = "Id of the main dashboard of the solution") |
|||
private DashboardId dashboardId; |
|||
@Schema(description = "Id of the public customer if solution has public entities") |
|||
private CustomerId publicId; |
|||
@Schema(description = "Is the main dashboard public") |
|||
private boolean mainDashboardPublic; |
|||
@Schema(description = "Markdown with solution usage instructions") |
|||
private String details; |
|||
|
|||
public TenantSolutionTemplateInstructions(TenantSolutionTemplateInstructions instructions) { |
|||
this.dashboardId = instructions.getDashboardId(); |
|||
this.publicId = instructions.getPublicId(); |
|||
this.mainDashboardPublic = instructions.isMainDashboardPublic(); |
|||
this.details = instructions.getDetails(); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class CompositeValueStrategyDefinition implements ValueStrategyDefinition { |
|||
|
|||
private ValueStrategyDefinition defaultHours; |
|||
private ValueStrategyDefinition workHours; |
|||
private ValueStrategyDefinition nightHours; |
|||
private ValueStrategyDefinition holidayHours; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.COMPOSITE; |
|||
} |
|||
} |
|||
@ -0,0 +1,55 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
public class CompositeValueStrategyGenerator extends TelemetryGenerator { |
|||
|
|||
TelemetryGenerator defaultGenerator; |
|||
TelemetryGenerator whGenerator; |
|||
TelemetryGenerator nhGenerator; |
|||
TelemetryGenerator hhGenerator; |
|||
|
|||
public CompositeValueStrategyGenerator(TelemetryProfile tp) { |
|||
super(tp); |
|||
var def = (CompositeValueStrategyDefinition) tp.getValueStrategy(); |
|||
defaultGenerator = TelemetryGeneratorFactory.create(new TelemetryProfile(tp.getKey(), def.getDefaultHours())); |
|||
if (def.getWorkHours() != null) { |
|||
whGenerator = TelemetryGeneratorFactory.create(new TelemetryProfile(tp.getKey(), def.getWorkHours())); |
|||
} |
|||
if (def.getNightHours() != null) { |
|||
nhGenerator = TelemetryGeneratorFactory.create(new TelemetryProfile(tp.getKey(), def.getNightHours())); |
|||
} |
|||
if (def.getHolidayHours() != null) { |
|||
hhGenerator = TelemetryGeneratorFactory.create(new TelemetryProfile(tp.getKey(), def.getHolidayHours())); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
if (hhGenerator != null && GeneratorTools.isHoliday(ts)) { |
|||
hhGenerator.addValue(ts, values); |
|||
} else if (nhGenerator != null && GeneratorTools.isNightHour(ts)) { |
|||
nhGenerator.addValue(ts, values); |
|||
} else if (whGenerator != null && GeneratorTools.isWorkHour(ts)) { |
|||
whGenerator.addValue(ts, values); |
|||
} else { |
|||
defaultGenerator.addValue(ts, values); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
public class ConstantTelemetryGenerator extends TelemetryGenerator { |
|||
|
|||
private final ConstantValueStrategyDefinition strategy; |
|||
private JsonNode value; |
|||
|
|||
public ConstantTelemetryGenerator(TelemetryProfile telemetryProfile) { |
|||
super(telemetryProfile); |
|||
this.strategy = (ConstantValueStrategyDefinition) telemetryProfile.getValueStrategy(); |
|||
this.value = strategy.getValue(); |
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
values.set(key, value); |
|||
} |
|||
|
|||
@Override |
|||
public double getValue() { |
|||
if (value != null && value.isNumber()) { |
|||
return value.doubleValue(); |
|||
} else { |
|||
return super.getValue(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void setValue(double value) { |
|||
// do nothing;
|
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class ConstantValueStrategyDefinition implements ValueStrategyDefinition { |
|||
private JsonNode value; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.CONSTANT; |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.math.RoundingMode; |
|||
|
|||
import static org.thingsboard.server.service.solutions.data.values.GeneratorTools.getMultiplier; |
|||
import static org.thingsboard.server.service.solutions.data.values.GeneratorTools.randomDouble; |
|||
|
|||
public class CounterTelemetryGenerator extends TelemetryGenerator { |
|||
|
|||
private final CounterValueStrategyDefinition strategy; |
|||
@Getter @Setter |
|||
private double value; |
|||
|
|||
public CounterTelemetryGenerator(TelemetryProfile telemetryProfile) { |
|||
super(telemetryProfile); |
|||
this.strategy = (CounterValueStrategyDefinition) telemetryProfile.getValueStrategy(); |
|||
this.value = getRandomStartValue(); |
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
double step = randomDouble(strategy.getMinIncrement(), strategy.getMaxIncrement()); |
|||
double multiplier = getMultiplier(ts, strategy.getHolidayMultiplier(), strategy.getWorkHoursMultiplier(), strategy.getNightHoursMultiplier()); |
|||
value += step * multiplier; |
|||
if (value > getRandomEndValue()) { |
|||
value = getRandomStartValue(); |
|||
} |
|||
put(values, value); |
|||
} |
|||
|
|||
private void put(ObjectNode values, double value) { |
|||
if (strategy.getPrecision() == 0) { |
|||
values.put(key, (int) value); |
|||
} else { |
|||
values.put(key, BigDecimal.valueOf(value) |
|||
.setScale(strategy.getPrecision(), RoundingMode.HALF_UP) |
|||
.doubleValue()); |
|||
} |
|||
} |
|||
|
|||
public double getRandomStartValue() { |
|||
return randomDouble(strategy.getMinStartValue(), strategy.getMaxStartValue()); |
|||
} |
|||
|
|||
public double getRandomEndValue() { |
|||
return randomDouble(strategy.getMinEndValue(), strategy.getMaxEndValue()); |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class CounterValueStrategyDefinition implements ValueStrategyDefinition { |
|||
|
|||
private int precision; |
|||
private double minStartValue; |
|||
private double maxStartValue; |
|||
private double minEndValue; |
|||
private double maxEndValue; |
|||
private double minIncrement; |
|||
private double maxIncrement; |
|||
private double holidayMultiplier; |
|||
private double workHoursMultiplier; |
|||
private double nightHoursMultiplier; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.COUNTER; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
import static org.thingsboard.server.service.solutions.data.values.GeneratorTools.randomDouble; |
|||
|
|||
public class DecrementTelemetryGenerator extends IncDecTelemetryGenerator<DecrementValueStrategyDefinition> { |
|||
|
|||
public DecrementTelemetryGenerator(TelemetryProfile telemetryProfile) { |
|||
super(telemetryProfile); |
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
double step = randomDouble(strategy.getMinDecrement(), strategy.getMaxDecrement()); |
|||
double newValue = value - step; |
|||
value = Math.max(newValue, endValue); |
|||
put(values, value); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class DecrementValueStrategyDefinition extends IncDecValueStrategyDefinition { |
|||
|
|||
private double minDecrement; |
|||
private double maxDecrement; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.DECREMENT; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
import static org.thingsboard.server.service.solutions.data.values.GeneratorTools.randomLong; |
|||
|
|||
|
|||
public class EventTelemetryGenerator extends TelemetryGenerator { |
|||
|
|||
private final EventValueStrategyDefinition strategy; |
|||
private final long currentAnomaly; |
|||
private Object value; |
|||
|
|||
public EventTelemetryGenerator(TelemetryProfile telemetryProfile) { |
|||
super(telemetryProfile); |
|||
this.strategy = (EventValueStrategyDefinition) telemetryProfile.getValueStrategy(); |
|||
currentAnomaly = randomLong(0, strategy.getAnomalyChance()); |
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
boolean anomaly = false; |
|||
if (randomLong(0, strategy.getAnomalyChance()) == currentAnomaly) { |
|||
anomaly = true; |
|||
} |
|||
this.value = anomaly ? strategy.getAnomalyValue() : strategy.getNormalValue(); |
|||
if (value instanceof Boolean) { |
|||
values.put(key, (Boolean) value); |
|||
} else if (value instanceof Double) { |
|||
values.put(key, ((Double) value)); |
|||
} else if (value instanceof Integer) { |
|||
values.put(key, ((Integer) value)); |
|||
} else { |
|||
throw new RuntimeException("Not supported value for event telemetry generator: " + value); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class EventValueStrategyDefinition implements ValueStrategyDefinition { |
|||
private Object normalValue; |
|||
private Object anomalyValue; |
|||
private double anomalyChance; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.EVENT; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,96 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import java.util.Calendar; |
|||
import java.util.Date; |
|||
import java.util.Random; |
|||
import java.util.TimeZone; |
|||
|
|||
public class GeneratorTools { |
|||
|
|||
private static final Random random = new Random(); |
|||
|
|||
public static long randomLong(double min, double max) { |
|||
return (long) randomDouble(min, max); |
|||
} |
|||
|
|||
public static double randomDouble(double min, double max) { |
|||
return min + random.nextDouble() * Math.abs(max - min); |
|||
} |
|||
|
|||
public static double getMultiplier(long ts, double holidayMultiplier, double workHoursMultiplier, double nightHoursMultiplier) { |
|||
Date date = new Date(ts); |
|||
Calendar c = Calendar.getInstance(); |
|||
c.setTime(date); |
|||
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK); |
|||
int hour = c.get(Calendar.HOUR_OF_DAY); |
|||
double multiplier = 1.0; |
|||
if (dayOfWeek == 1 || dayOfWeek == 7) { |
|||
multiplier *= holidayMultiplier; |
|||
} |
|||
|
|||
if (hour > 8 && hour < 18) { |
|||
multiplier *= workHoursMultiplier; |
|||
} else if (hour < 6 || hour > 22) { |
|||
multiplier *= nightHoursMultiplier; |
|||
} |
|||
return multiplier; |
|||
} |
|||
|
|||
public static int getHour(TimeZone tz, long ts) { |
|||
Date date = new Date(ts); |
|||
Calendar c = Calendar.getInstance(); |
|||
c.setTimeZone(tz); |
|||
c.setTime(date); |
|||
return c.get(Calendar.HOUR_OF_DAY); |
|||
} |
|||
|
|||
public static int getMinute(TimeZone tz, long ts) { |
|||
Date date = new Date(ts); |
|||
Calendar c = Calendar.getInstance(); |
|||
c.setTimeZone(tz); |
|||
c.setTime(date); |
|||
return c.get(Calendar.MINUTE); |
|||
} |
|||
|
|||
public static boolean isHoliday(long ts) { |
|||
Date date = new Date(ts); |
|||
Calendar c = Calendar.getInstance(); |
|||
c.setTime(date); |
|||
int dayOfWeek = c.get(Calendar.DAY_OF_WEEK); |
|||
return dayOfWeek == 1 || dayOfWeek == 7; |
|||
} |
|||
|
|||
public static boolean isWorkHour(long ts) { |
|||
Date date = new Date(ts); |
|||
Calendar c = Calendar.getInstance(); |
|||
c.setTime(date); |
|||
int hour = c.get(Calendar.HOUR_OF_DAY); |
|||
|
|||
return hour > 8 && hour < 18; |
|||
} |
|||
|
|||
public static boolean isNightHour(long ts) { |
|||
Date date = new Date(ts); |
|||
Calendar c = Calendar.getInstance(); |
|||
c.setTime(date); |
|||
int hour = c.get(Calendar.HOUR_OF_DAY); |
|||
|
|||
return hour < 6 || hour >= 22; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import lombok.Getter; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.math.RoundingMode; |
|||
|
|||
import static org.thingsboard.server.service.solutions.data.values.GeneratorTools.randomDouble; |
|||
|
|||
public abstract class IncDecTelemetryGenerator<T extends IncDecValueStrategyDefinition> extends TelemetryGenerator { |
|||
|
|||
protected final T strategy; |
|||
@Getter |
|||
protected double value; |
|||
protected double endValue; |
|||
|
|||
@SuppressWarnings("unchecked") |
|||
public IncDecTelemetryGenerator(TelemetryProfile telemetryProfile) { |
|||
super(telemetryProfile); |
|||
this.strategy = (T) telemetryProfile.getValueStrategy(); |
|||
this.value = getRandomStartValue(); |
|||
this.endValue = getRandomEndValue(); |
|||
} |
|||
|
|||
public void setValue(double value) { |
|||
this.value = value; |
|||
this.endValue = getRandomEndValue(); |
|||
} |
|||
|
|||
protected void put(ObjectNode values, double value) { |
|||
if (strategy.getPrecision() == 0) { |
|||
values.put(key, (int) value); |
|||
} else { |
|||
values.put(key, BigDecimal.valueOf(value) |
|||
.setScale(strategy.getPrecision(), RoundingMode.HALF_UP) |
|||
.doubleValue()); |
|||
} |
|||
} |
|||
|
|||
public double getRandomStartValue() { |
|||
return randomDouble(strategy.getMinStartValue(), strategy.getMaxStartValue()); |
|||
} |
|||
|
|||
public double getRandomEndValue() { |
|||
return randomDouble(strategy.getMinEndValue(), strategy.getMaxEndValue()); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public abstract class IncDecValueStrategyDefinition implements ValueStrategyDefinition { |
|||
|
|||
private int precision; |
|||
private double minStartValue; |
|||
private double maxStartValue; |
|||
private double minEndValue; |
|||
private double maxEndValue; |
|||
|
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
import static org.thingsboard.server.service.solutions.data.values.GeneratorTools.randomDouble; |
|||
|
|||
public class IncrementTelemetryGenerator extends IncDecTelemetryGenerator<IncrementValueStrategyDefinition> { |
|||
|
|||
public IncrementTelemetryGenerator(TelemetryProfile telemetryProfile) { |
|||
super(telemetryProfile); |
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
double step = randomDouble(strategy.getMinIncrement(), strategy.getMaxIncrement()); |
|||
double newValue = value + step; |
|||
value = Math.min(newValue, endValue); |
|||
put(values, value); |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class IncrementValueStrategyDefinition extends IncDecValueStrategyDefinition { |
|||
|
|||
private double minIncrement; |
|||
private double maxIncrement; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.INCREMENT; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,94 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import lombok.Getter; |
|||
import lombok.Setter; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
import java.math.BigDecimal; |
|||
import java.math.RoundingMode; |
|||
|
|||
import static org.thingsboard.server.service.solutions.data.values.GeneratorTools.getMultiplier; |
|||
import static org.thingsboard.server.service.solutions.data.values.GeneratorTools.randomDouble; |
|||
|
|||
public class NaturalTelemetryGenerator extends TelemetryGenerator { |
|||
|
|||
private final NaturalValueStrategyDefinition strategy; |
|||
@Getter @Setter |
|||
private double value; |
|||
private boolean isIncrement; |
|||
private double lowValue; |
|||
private double highValue; |
|||
|
|||
public NaturalTelemetryGenerator(TelemetryProfile telemetryProfile) { |
|||
super(telemetryProfile); |
|||
this.strategy = (NaturalValueStrategyDefinition) telemetryProfile.getValueStrategy(); |
|||
this.value = getRandomStartValue(); |
|||
this.lowValue = getRandomLowValue(); |
|||
this.highValue = getRandomHighValue(); |
|||
isIncrement = !strategy.isDecrementOnStart(); |
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
|
|||
double multiplier = getMultiplier(ts, strategy.getHolidayMultiplier(), strategy.getWorkHoursMultiplier(), strategy.getNightHoursMultiplier()); |
|||
|
|||
if (isIncrement) { |
|||
double step = randomDouble(strategy.getMinIncrement(), strategy.getMaxIncrement()); |
|||
value += step * multiplier; |
|||
if (value > highValue) { |
|||
value = highValue; |
|||
highValue = getRandomHighValue(); |
|||
isIncrement = false; |
|||
} |
|||
} else { |
|||
double step = randomDouble(strategy.getMinDecrement(), strategy.getMaxDecrement()); |
|||
value -= step * multiplier; |
|||
if (value < lowValue) { |
|||
value = lowValue; |
|||
lowValue = getRandomLowValue(); |
|||
isIncrement = true; |
|||
} |
|||
} |
|||
|
|||
put(values, value); |
|||
} |
|||
|
|||
private void put(ObjectNode values, double value) { |
|||
if (strategy.getPrecision() == 0) { |
|||
values.put(key, (int) value); |
|||
} else { |
|||
values.put(key, BigDecimal.valueOf(value) |
|||
.setScale(strategy.getPrecision(), RoundingMode.HALF_UP) |
|||
.doubleValue()); |
|||
} |
|||
} |
|||
|
|||
public double getRandomStartValue() { |
|||
return randomDouble(strategy.getMinStartValue(), strategy.getMaxStartValue()); |
|||
} |
|||
|
|||
public double getRandomLowValue() { |
|||
return randomDouble(strategy.getMinLowValue(), strategy.getMaxLowValue()); |
|||
} |
|||
|
|||
public double getRandomHighValue() { |
|||
return randomDouble(strategy.getMinHighValue(), strategy.getMaxHighValue()); |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class NaturalValueStrategyDefinition implements ValueStrategyDefinition { |
|||
private int precision; |
|||
private double minStartValue; |
|||
private double maxStartValue; |
|||
private double minLowValue; |
|||
private double maxLowValue; |
|||
private double minHighValue; |
|||
private double maxHighValue; |
|||
private double minIncrement; |
|||
private double maxIncrement; |
|||
private double minDecrement; |
|||
private double maxDecrement; |
|||
private double holidayMultiplier; |
|||
private double workHoursMultiplier; |
|||
private double nightHoursMultiplier; |
|||
private boolean decrementOnStart; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.NATURAL; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class ScheduleValueStrategyDefinition implements ValueStrategyDefinition { |
|||
|
|||
private List<ValueStrategySchedule> schedule; |
|||
private String timeZone; |
|||
private ValueStrategyDefinition defaultDefinition; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.SCHEDULE; |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
import java.util.LinkedHashMap; |
|||
import java.util.Map; |
|||
import java.util.TimeZone; |
|||
|
|||
public class ScheduleValueStrategyGenerator extends TelemetryGenerator { |
|||
|
|||
private TelemetryGenerator defaultGenerator; |
|||
private TimeZone timeZone; |
|||
private Map<ValueStrategySchedule, TelemetryGenerator> scheduleGenerators; |
|||
private TelemetryGenerator prevGenerator; |
|||
|
|||
public ScheduleValueStrategyGenerator(TelemetryProfile tp) { |
|||
super(tp); |
|||
var def = (ScheduleValueStrategyDefinition) tp.getValueStrategy(); |
|||
timeZone = TimeZone.getTimeZone(def.getTimeZone()); |
|||
defaultGenerator = TelemetryGeneratorFactory.create(new TelemetryProfile(tp.getKey(), def.getDefaultDefinition())); |
|||
scheduleGenerators = new LinkedHashMap<>(); |
|||
for (ValueStrategySchedule scheduleItem : def.getSchedule()) { |
|||
scheduleGenerators.put(scheduleItem, TelemetryGeneratorFactory.create(new TelemetryProfile(tp.getKey(), scheduleItem.getDefinition()))); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
int hour = GeneratorTools.getHour(timeZone, ts); |
|||
int minute = GeneratorTools.getMinute(timeZone, ts); |
|||
TelemetryGenerator generator = scheduleGenerators.entrySet().stream().filter(pair -> { |
|||
var schedule = pair.getKey(); |
|||
if (hour == schedule.getStartHour() && hour == schedule.getEndHour()) { |
|||
return schedule.getStartMinute() <= minute && minute <= schedule.getEndMinute(); |
|||
} else if (hour == schedule.getStartHour() && hour < schedule.getEndHour()) { |
|||
return schedule.getStartMinute() <= minute; |
|||
} else if (hour > schedule.getStartHour() && hour < schedule.getEndHour()) { |
|||
return true; |
|||
} else if (hour > schedule.getStartHour() && hour == schedule.getEndHour()) { |
|||
return minute <= schedule.getEndHour(); |
|||
} else { |
|||
return false; |
|||
} |
|||
}).map(Map.Entry::getValue).findFirst().orElse(defaultGenerator); |
|||
|
|||
if (prevGenerator != null && !prevGenerator.equals(generator)) { |
|||
generator.setValue(prevGenerator.getValue()); |
|||
} |
|||
|
|||
generator.addValue(ts, values); |
|||
|
|||
prevGenerator = generator; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import lombok.Data; |
|||
|
|||
|
|||
@Data |
|||
@JsonIgnoreProperties(ignoreUnknown = true) |
|||
public class SequenceValueStrategyDefinition implements ValueStrategyDefinition { |
|||
private boolean random; |
|||
private ObjectNode telemetry; |
|||
|
|||
@Override |
|||
public ValueStrategyDefinitionType getStrategyType() { |
|||
return ValueStrategyDefinitionType.SEQUENCE; |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
import java.util.Random; |
|||
|
|||
public class SequenceValueStrategyGenerator extends TelemetryGenerator { |
|||
|
|||
private final SequenceValueStrategyDefinition strategy; |
|||
private int max; |
|||
private int index; |
|||
|
|||
public SequenceValueStrategyGenerator(TelemetryProfile telemetryProfile) { |
|||
super(telemetryProfile); |
|||
this.strategy = (SequenceValueStrategyDefinition) telemetryProfile.getValueStrategy(); |
|||
max = strategy.getTelemetry().fields().next().getValue().size() - 1; |
|||
index = strategy.isRandom() ? new Random().nextInt(max + 1) : 0; |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void addValue(long ts, ObjectNode values) { |
|||
strategy.getTelemetry().fields().forEachRemaining(entry -> { |
|||
String key = entry.getKey(); |
|||
values.set(key, entry.getValue().get(index)); |
|||
}); |
|||
index++; |
|||
if (index > max) { |
|||
index = 0; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.databind.node.ObjectNode; |
|||
import lombok.Data; |
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
@Data |
|||
public abstract class TelemetryGenerator { |
|||
protected final TelemetryProfile profile; |
|||
protected final String key; |
|||
|
|||
public TelemetryGenerator(TelemetryProfile telemetryProfile) { |
|||
this.profile = telemetryProfile; |
|||
this.key = telemetryProfile.getKey(); |
|||
} |
|||
|
|||
public double getValue() { |
|||
throw new RuntimeException("Not supported"); |
|||
} |
|||
|
|||
public void setValue(double value) { |
|||
throw new RuntimeException("Not supported"); |
|||
} |
|||
|
|||
public abstract void addValue(long ts, ObjectNode values); |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
|
|||
import org.thingsboard.server.service.solutions.data.definition.TelemetryProfile; |
|||
|
|||
public class TelemetryGeneratorFactory { |
|||
|
|||
public static TelemetryGenerator create(TelemetryProfile tp) { |
|||
switch (tp.getValueStrategy().getStrategyType()) { |
|||
case COUNTER: |
|||
return new CounterTelemetryGenerator(tp); |
|||
case NATURAL: |
|||
return new NaturalTelemetryGenerator(tp); |
|||
case EVENT: |
|||
return new EventTelemetryGenerator(tp); |
|||
case CONSTANT: |
|||
return new ConstantTelemetryGenerator(tp); |
|||
case SEQUENCE: |
|||
return new SequenceValueStrategyGenerator(tp); |
|||
case COMPOSITE: |
|||
return new CompositeValueStrategyGenerator(tp); |
|||
case SCHEDULE: |
|||
return new ScheduleValueStrategyGenerator(tp); |
|||
case INCREMENT: |
|||
return new IncrementTelemetryGenerator(tp); |
|||
case DECREMENT: |
|||
return new DecrementTelemetryGenerator(tp); |
|||
default: |
|||
throw new RuntimeException("Not supported!"); |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonSubTypes; |
|||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
|||
|
|||
@JsonTypeInfo( |
|||
use = JsonTypeInfo.Id.NAME, |
|||
include = JsonTypeInfo.As.PROPERTY, |
|||
property = "type") |
|||
@JsonSubTypes({ |
|||
@JsonSubTypes.Type(value = CounterValueStrategyDefinition.class, name = "counter"), |
|||
@JsonSubTypes.Type(value = NaturalValueStrategyDefinition.class, name = "natural"), |
|||
@JsonSubTypes.Type(value = EventValueStrategyDefinition.class, name = "event"), |
|||
@JsonSubTypes.Type(value = SequenceValueStrategyDefinition.class, name = "sequence"), |
|||
@JsonSubTypes.Type(value = ConstantValueStrategyDefinition.class, name = "constant"), |
|||
@JsonSubTypes.Type(value = CompositeValueStrategyDefinition.class, name = "composite"), |
|||
@JsonSubTypes.Type(value = ScheduleValueStrategyDefinition.class, name = "schedule"), |
|||
@JsonSubTypes.Type(value = IncrementValueStrategyDefinition.class, name = "inc"), |
|||
@JsonSubTypes.Type(value = DecrementValueStrategyDefinition.class, name = "dec")}) |
|||
public interface ValueStrategyDefinition { |
|||
|
|||
ValueStrategyDefinitionType getStrategyType(); |
|||
|
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
public enum ValueStrategyDefinitionType { |
|||
|
|||
COUNTER, NATURAL, EVENT, SEQUENCE, CONSTANT, COMPOSITE, SCHEDULE, INCREMENT, DECREMENT; |
|||
|
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.solutions.data.values; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class ValueStrategySchedule { |
|||
|
|||
private int startHour; |
|||
private int startMinute; |
|||
|
|||
private int endHour; |
|||
private int endMinute; |
|||
|
|||
private ValueStrategyDefinition definition; |
|||
|
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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.common.data.iot_hub; |
|||
|
|||
import lombok.Data; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.DashboardId; |
|||
import org.thingsboard.server.common.data.id.EntityId; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Data |
|||
public class SolutionTemplateInstalledItemDescriptor implements IotHubInstalledItemDescriptor { |
|||
|
|||
private List<EntityId> createdEntityIds; |
|||
private DashboardId dashboardId; |
|||
private CustomerId publicId; |
|||
private boolean mainDashboardPublic; |
|||
private String details; |
|||
|
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2026 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. |
|||
|
|||
--> |
|||
<div style="min-width:400px;" class="tb-dialog-content"> |
|||
<mat-toolbar color="primary"> |
|||
<h2>@if (instructions) {<mat-icon>info_outline</mat-icon>}{{ (instructions ? 'iot-hub.solution-instructions' : 'iot-hub.solution-install-title') | translate }}</h2> |
|||
<span class="flex-1"></span> |
|||
<button mat-icon-button |
|||
(click)="close()" |
|||
type="button"> |
|||
<mat-icon class="material-icons">close</mat-icon> |
|||
</button> |
|||
</mat-toolbar> |
|||
<div mat-dialog-content> |
|||
<tb-markdown [data]="details" lineNumbers fallbackToPlainMarkdown></tb-markdown> |
|||
</div> |
|||
<div mat-dialog-actions class="flex flex-row items-center justify-end"> |
|||
<button mat-button color="primary" |
|||
type="button" |
|||
(click)="close()" cdkFocusInitial> |
|||
{{ 'action.close' | translate }} |
|||
</button> |
|||
@if (dashboardId) { |
|||
<button mat-raised-button color="primary" |
|||
(click)="gotoMainDashboard()"> |
|||
{{ 'iot-hub.goto-main-dashboard' | translate }} |
|||
</button> |
|||
} |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,50 @@ |
|||
/** |
|||
* Copyright © 2016-2026 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. |
|||
*/ |
|||
|
|||
:host { |
|||
h2 { |
|||
.mat-icon { |
|||
padding-right: 8px; |
|||
box-sizing: initial; |
|||
vertical-align: sub; |
|||
} |
|||
} |
|||
} |
|||
|
|||
:host ::ng-deep { |
|||
.mat-mdc-dialog-content { |
|||
tb-markdown { |
|||
.tb-markdown-view { |
|||
.table-wrapper { |
|||
overflow-y: auto; |
|||
margin: 30px 32px; |
|||
border: 1px solid rgba(42, 125, 236, .2); |
|||
border-radius: 4px; |
|||
padding: 0; |
|||
width: calc(100% - 64px); |
|||
table { |
|||
width: 100%; |
|||
table-layout: auto; |
|||
white-space: nowrap; |
|||
margin: 0; |
|||
border: 0; |
|||
border-radius: 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
///
|
|||
/// Copyright © 2016-2026 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, Inject } from '@angular/core'; |
|||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; |
|||
import { Router } from '@angular/router'; |
|||
import { SolutionTemplateInstalledItemDescriptor } from '@shared/models/iot-hub/iot-hub-installed-item.models'; |
|||
|
|||
export interface SolutionInstallDialogData { |
|||
descriptor: SolutionTemplateInstalledItemDescriptor; |
|||
instructions?: boolean; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'tb-solution-install-dialog', |
|||
templateUrl: './solution-install-dialog.component.html', |
|||
styleUrls: ['./solution-install-dialog.component.scss'], |
|||
standalone: false |
|||
}) |
|||
export class SolutionInstallDialogComponent { |
|||
|
|||
details: string; |
|||
dashboardId: string | null; |
|||
instructions: boolean; |
|||
|
|||
constructor( |
|||
@Inject(MAT_DIALOG_DATA) public data: SolutionInstallDialogData, |
|||
private dialogRef: MatDialogRef<SolutionInstallDialogComponent>, |
|||
private router: Router |
|||
) { |
|||
this.details = data.descriptor.details || ''; |
|||
this.dashboardId = data.descriptor.dashboardId?.id || null; |
|||
this.instructions = !!data.instructions; |
|||
} |
|||
|
|||
gotoMainDashboard(): void { |
|||
if (this.dashboardId) { |
|||
this.dialogRef.close(); |
|||
this.router.navigateByUrl(`/dashboards/${this.dashboardId}`); |
|||
} |
|||
} |
|||
|
|||
close(): void { |
|||
this.dialogRef.close(); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue