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