Browse Source

lwm2m: add objectKeyId to back and front profile

pull/4277/head
nickAS21 6 years ago
committed by Andrew Shvayka
parent
commit
201476f912
  1. 41
      application/src/main/java/org/thingsboard/server/controller/ResourceController.java
  2. 36
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  3. 1
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  4. 14
      common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java
  5. 4
      common/data/pom.xml
  6. 61
      common/data/src/main/java/org/thingsboard/server/common/data/Resource.java
  7. 1
      common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java
  8. 4
      dao/pom.xml
  9. 2
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  10. 6
      dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceEntity.java
  11. 103
      dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java
  12. 9
      dao/src/main/java/org/thingsboard/server/dao/resource/ResourceDao.java
  13. 32
      dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceDaoImpl.java
  14. 49
      dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceRepository.java
  15. 1
      dao/src/main/resources/sql/schema-entities-hsql.sql
  16. 1
      dao/src/main/resources/sql/schema-entities.sql
  17. 30
      ui-ngx/src/app/core/http/device-profile.service.ts
  18. 82
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts
  19. 12
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts
  20. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html
  21. 16
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts
  22. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html
  23. 61
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts
  24. 2
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html
  25. 22
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts
  26. 4
      ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts

41
application/src/main/java/org/thingsboard/server/controller/ResourceController.java

@ -27,11 +27,14 @@ import org.thingsboard.server.common.data.Resource;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import java.util.List;
@Slf4j @Slf4j
@RestController @RestController
@TbCoreComponent @TbCoreComponent
@ -59,21 +62,55 @@ public class ResourceController extends BaseController {
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/resource", method = RequestMethod.GET) @RequestMapping(value = "/resource/page", method = RequestMethod.GET)
@ResponseBody @ResponseBody
public PageData<Resource> getResources(@RequestParam(required = false) boolean system, public PageData<Resource> getResources(@RequestParam(required = false) boolean system,
@RequestParam int pageSize, @RequestParam int pageSize,
@RequestParam int page, @RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty, @RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException { @RequestParam(required = false) String sortOrder) throws ThingsboardException {
try { try {
PageLink pageLink = createPageLink(pageSize, page, null, sortProperty, sortOrder); // int[] objectIds;
// ResourceType resourceType
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(resourceService.findResourcesByTenantId(system ? TenantId.SYS_TENANT_ID : getTenantId(), pageLink)); return checkNotNull(resourceService.findResourcesByTenantId(system ? TenantId.SYS_TENANT_ID : getTenantId(), pageLink));
} catch (Exception e) { } catch (Exception e) {
throw handleException(e); throw handleException(e);
} }
} }
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/resource/lwm2m/page", method = RequestMethod.GET)
@ResponseBody
public List<LwM2mObject> getLwm2mListObjectsPage(@RequestParam int pageSize,
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
try {
PageLink pageLink = new PageLink(pageSize, page, textSearch);
return checkNotNull(resourceService.findLwM2mObjectPage(getTenantId(), sortProperty, sortOrder, pageLink));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@RequestMapping(value = "/resource/lwm2m", method = RequestMethod.GET)
@ResponseBody
public List<LwM2mObject> getLwm2mListObjects(@RequestParam String sortOrder,
@RequestParam String sortProperty,
@RequestParam(required = false) String[] objectIds,
@RequestParam(required = false) String searchText)
throws ThingsboardException {
try {
return checkNotNull(resourceService.findLwM2mObject(getTenantId(), sortOrder, sortProperty, objectIds, searchText));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/resource/{resourceType}/{resourceId}", method = RequestMethod.DELETE) @RequestMapping(value = "/resource/{resourceType}/{resourceId}", method = RequestMethod.DELETE)
@ResponseBody @ResponseBody

36
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.widget.WidgetType; import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle; import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
@ -202,7 +203,6 @@ public class InstallScripts {
} }
public void loadSystemLwm2mResources() throws Exception { public void loadSystemLwm2mResources() throws Exception {
// Path modelsDir = Paths.get("/home/nick/Igor_project/thingsboard_ce_3_2_docker/thingsboard/common/transport/lwm2m/src/main/resources/models/");
Path modelsDir = Paths.get(getDataDir(), MODELS_DIR); Path modelsDir = Paths.get(getDataDir(), MODELS_DIR);
if (Files.isDirectory(modelsDir)) { if (Files.isDirectory(modelsDir)) {
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(modelsDir, path -> path.toString().endsWith(XML_EXT))) { try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(modelsDir, path -> path.toString().endsWith(XML_EXT))) {
@ -210,18 +210,16 @@ public class InstallScripts {
path -> { path -> {
try { try {
byte[] fileBytes = Files.readAllBytes(path); byte[] fileBytes = Files.readAllBytes(path);
String key = getObjectModelLwm2mValid(fileBytes, path.getFileName().toString(), new DefaultDDFFileValidator()); String source = new String(fileBytes);
if (key != null) { Resource resource = new Resource();
Resource resource = new Resource(); resource.setTenantId(TenantId.SYS_TENANT_ID);
resource.setTenantId(TenantId.SYS_TENANT_ID); resource.setResourceType(ResourceType.LWM2M_MODEL);
resource.setResourceType(ResourceType.LWM2M_MODEL); resource.setResourceId(getValueByTeg(source, "ObjectID") + "_" + getValueByTeg(source, "ObjectVersion"));
resource.setResourceId(key); resource.setTextSearch(resource.getResourceId() + ":" + getValueByTeg(source, "Name"));
resource.setValue(Base64.getEncoder().encodeToString(fileBytes)); resource.setValue(Base64.getEncoder().encodeToString(fileBytes));
resourceService.saveResource(resource); resourceService.saveResource(resource);
}
} catch (Exception e) { } catch (Exception e) {
log.error("Unable to load lwm2m model [{}]", path.toString()); throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", path.toString()));
throw new RuntimeException("Unable to load lwm2m model", e);
} }
} }
); );
@ -234,6 +232,7 @@ public class InstallScripts {
resource.setTenantId(TenantId.SYS_TENANT_ID); resource.setTenantId(TenantId.SYS_TENANT_ID);
resource.setResourceType(ResourceType.JKS); resource.setResourceType(ResourceType.JKS);
resource.setResourceId(jksPath.getFileName().toString()); resource.setResourceId(jksPath.getFileName().toString());
resource.setTextSearch(jksPath.getFileName().toString());
resource.setValue(Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath))); resource.setValue(Base64.getEncoder().encodeToString(Files.readAllBytes(jksPath)));
resourceService.saveResource(resource); resourceService.saveResource(resource);
} catch (Exception e) { } catch (Exception e) {
@ -242,11 +241,18 @@ public class InstallScripts {
} }
} }
private String getObjectModelLwm2mValid(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) { private String getValueByTeg(String source, String tagHtml) {
int lenTag = ("<" + tagHtml + ">").length();
int indStart = source.indexOf("<" + tagHtml + ">");
int indEnd = source.indexOf("</" + tagHtml + ">");
return (indStart > 0 && indEnd > 0) ? source.substring(indStart + lenTag, indEnd) : null;
}
private ObjectModel getObjectModelLwm2mValid(byte[] xmlByte, String streamName, DefaultDDFFileValidator ddfValidator) {
try { try {
DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator); DDFFileParser ddfFileParser = new DDFFileParser(ddfValidator);
ObjectModel objectModel = ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0); return ddfFileParser.parseEx(new ByteArrayInputStream(xmlByte), streamName).get(0);
return objectModel.id + "##" + objectModel.getVersion();
} catch (IOException | InvalidDDFFileException e) { } catch (IOException | InvalidDDFFileException e) {
log.error("Could not parse the XML file [{}]", streamName, e); log.error("Could not parse the XML file [{}]", streamName, e);
return null; return null;

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

@ -442,6 +442,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
" tenant_id uuid NOT NULL," + " tenant_id uuid NOT NULL," +
" resource_type varchar(32) NOT NULL," + " resource_type varchar(32) NOT NULL," +
" resource_id varchar(255) NOT NULL," + " resource_id varchar(255) NOT NULL," +
" text_search varchar(255)," +
" resource_value varchar," + " resource_value varchar," +
" CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)" + " CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)" +
" );"); " );");

14
common/dao-api/src/main/java/org/thingsboard/server/dao/resource/ResourceService.java

@ -18,6 +18,7 @@ package org.thingsboard.server.dao.resource;
import org.thingsboard.server.common.data.Resource; import org.thingsboard.server.common.data.Resource;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
@ -31,7 +32,18 @@ public interface ResourceService {
PageData<Resource> findResourcesByTenantId(TenantId tenantId, PageLink pageLink); PageData<Resource> findResourcesByTenantId(TenantId tenantId, PageLink pageLink);
List<Resource> findResourcesByTenantIdResourceType(TenantId tenantId, ResourceType resourceType); List<LwM2mObject> findLwM2mObject(TenantId tenantId,
String sortOrder,
String sortProperty,
String[] objectIds,
String searchText);
List<LwM2mObject> findLwM2mObjectPage(TenantId tenantId,
String sortProperty,
String sortOrder,
PageLink pageLink);
List<Resource> findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType);
void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId); void deleteResource(TenantId tenantId, ResourceType resourceType, String resourceId);

4
common/data/pom.xml

@ -79,6 +79,10 @@
<groupId>org.thingsboard</groupId> <groupId>org.thingsboard</groupId>
<artifactId>protobuf-dynamic</artifactId> <artifactId>protobuf-dynamic</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-core</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

61
common/data/src/main/java/org/thingsboard/server/common/data/Resource.java

@ -16,10 +16,24 @@
package org.thingsboard.server.common.data; package org.thingsboard.server.common.data;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.model.DDFFileParser;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.ObjectModel;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.lwm2m.LwM2mInstance;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
import org.thingsboard.server.common.data.lwm2m.LwM2mResource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
@Slf4j
@Data @Data
public class Resource implements HasTenantId, Serializable { public class Resource implements HasTenantId, Serializable {
@ -28,14 +42,51 @@ public class Resource implements HasTenantId, Serializable {
private TenantId tenantId; private TenantId tenantId;
private ResourceType resourceType; private ResourceType resourceType;
private String resourceId; private String resourceId;
private String textSearch;
private String value; private String value;
@Override @Override
public String toString() { public String toString() {
return "Resource{" + final StringBuilder res = new StringBuilder("Resource{");
"tenantId=" + tenantId + res.append("tenantId=").append(tenantId);
", resourceType=" + resourceType + res.append(", resourceType='").append(resourceType).append('\'');
", resourceId='" + resourceId + '\'' + res.append(", resourceId='").append(resourceId).append('\'');
'}'; res.append(", textSearch='").append(textSearch).append('\'');
res.append('}');
return res.toString();
}
public LwM2mObject toLwM2mObject () {
try {
DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
List<ObjectModel> objectModels = ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(this.value)), this.textSearch);
if (objectModels.size() == 0) {
return null;
}
else {
ObjectModel obj = objectModels.get(0);
LwM2mObject lwM2mObject = new LwM2mObject();
lwM2mObject.setId(obj.id);
lwM2mObject.setKeyId(this.resourceId);
lwM2mObject.setName(obj.name);
lwM2mObject.setMultiple(obj.multiple);
lwM2mObject.setMandatory(obj.mandatory);
LwM2mInstance instance = new LwM2mInstance();
instance.setId(0);
List<LwM2mResource> resources = new ArrayList<>();
obj.resources.forEach((k, v) -> {
if (!v.operations.isExecutable()) {
LwM2mResource resource = new LwM2mResource(k, v.name, false, false, false);
resources.add(resource);
}
});
instance.setResources(resources.stream().toArray(LwM2mResource[]::new));
lwM2mObject.setInstances(new LwM2mInstance[]{instance});
return lwM2mObject;
}
} catch (IOException | InvalidDDFFileException e) {
log.error("Could not parse the XML of objectModel with name [{}]", this.textSearch, e);
return null;
}
} }
} }

1
common/data/src/main/java/org/thingsboard/server/common/data/lwm2m/LwM2mObject.java

@ -20,6 +20,7 @@ import lombok.Data;
@Data @Data
public class LwM2mObject { public class LwM2mObject {
int id; int id;
String keyId;
String name; String name;
boolean multiple; boolean multiple;
boolean mandatory; boolean mandatory;

4
dao/pom.xml

@ -214,6 +214,10 @@
<groupId>org.elasticsearch.client</groupId> <groupId>org.elasticsearch.client</groupId>
<artifactId>rest</artifactId> <artifactId>rest</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.leshan</groupId>
<artifactId>leshan-core</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>
<plugins> <plugins>

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

@ -460,8 +460,10 @@ public class ModelConstants {
public static final String RESOURCE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; public static final String RESOURCE_TENANT_ID_COLUMN = TENANT_ID_COLUMN;
public static final String RESOURCE_TYPE_COLUMN = "resource_type"; public static final String RESOURCE_TYPE_COLUMN = "resource_type";
public static final String RESOURCE_ID_COLUMN = "resource_id"; public static final String RESOURCE_ID_COLUMN = "resource_id";
public static final String TEXT_SEARCH_COLUMN = "text_search";
public static final String RESOURCE_VALUE_COLUMN = "resource_value"; public static final String RESOURCE_VALUE_COLUMN = "resource_value";
/** /**
* Cassandra attributes and timeseries constants. * Cassandra attributes and timeseries constants.
*/ */

6
dao/src/main/java/org/thingsboard/server/dao/model/sql/ResourceEntity.java

@ -33,6 +33,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TABLE_NAM
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TENANT_ID_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_TYPE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.RESOURCE_VALUE_COLUMN;
import static org.thingsboard.server.dao.model.ModelConstants.TEXT_SEARCH_COLUMN;
@Data @Data
@Entity @Entity
@ -52,6 +53,9 @@ public class ResourceEntity implements ToData<Resource> {
@Column(name = RESOURCE_ID_COLUMN) @Column(name = RESOURCE_ID_COLUMN)
private String resourceId; private String resourceId;
@Column(name = TEXT_SEARCH_COLUMN)
private String textSearch;
@Column(name = RESOURCE_VALUE_COLUMN) @Column(name = RESOURCE_VALUE_COLUMN)
private String value; private String value;
@ -62,6 +66,7 @@ public class ResourceEntity implements ToData<Resource> {
this.tenantId = resource.getTenantId().getId(); this.tenantId = resource.getTenantId().getId();
this.resourceType = resource.getResourceType().name(); this.resourceType = resource.getResourceType().name();
this.resourceId = resource.getResourceId(); this.resourceId = resource.getResourceId();
this.textSearch = resource.getTextSearch();
this.value = resource.getValue(); this.value = resource.getValue();
} }
@ -71,6 +76,7 @@ public class ResourceEntity implements ToData<Resource> {
resource.setTenantId(new TenantId(tenantId)); resource.setTenantId(new TenantId(tenantId));
resource.setResourceType(ResourceType.valueOf(resourceType)); resource.setResourceType(ResourceType.valueOf(resourceType));
resource.setResourceId(resourceId); resource.setResourceId(resourceId);
resource.setTextSearch(textSearch);
resource.setValue(value); resource.setValue(value);
return resource; return resource;
} }

103
dao/src/main/java/org/thingsboard/server/dao/resource/BaseResourceService.java

@ -16,15 +16,28 @@
package org.thingsboard.server.dao.resource; package org.thingsboard.server.dao.resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.model.DDFFileParser;
import org.eclipse.leshan.core.model.DefaultDDFFileValidator;
import org.eclipse.leshan.core.model.InvalidDDFFileException;
import org.eclipse.leshan.core.model.ObjectModel;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Resource; import org.thingsboard.server.common.data.Resource;
import org.thingsboard.server.common.data.ResourceType; import org.thingsboard.server.common.data.ResourceType;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.lwm2m.LwM2mInstance;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
import org.thingsboard.server.common.data.lwm2m.LwM2mResource;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID; import static org.thingsboard.server.dao.device.DeviceServiceImpl.INCORRECT_TENANT_ID;
import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateId;
@ -69,10 +82,35 @@ public class BaseResourceService implements ResourceService {
@Override @Override
public List<Resource> findResourcesByTenantIdResourceType(TenantId tenantId, ResourceType resourceType) { public List<Resource> findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType) {
log.trace("Executing findByTenantId [{}]", tenantId); log.trace("Executing findByTenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
return resourceDao.findAllByTenantIdResourceType(tenantId, resourceType); return resourceDao.findAllByTenantIdAndResourceType(tenantId, resourceType);
}
@Override
public List<LwM2mObject> findLwM2mObjectPage(TenantId tenantId, String sortProperty, String sortOrder, PageLink pageLink) {
log.trace("Executing findByTenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
PageData<Resource> resourcePageData = resourceDao.findResourcesByTenantIdAndResourceType(
tenantId,
ResourceType.LWM2M_MODEL, pageLink);
List<LwM2mObject> lwM2mObjects = resourcePageData.getData().stream().map(this::toLwM2mObject).collect(Collectors.toList());
return lwM2mObjects.size() > 1 ? this.sortList (lwM2mObjects, sortProperty, sortOrder) : lwM2mObjects;
}
@Override
public List<LwM2mObject> findLwM2mObject(TenantId tenantId, String sortOrder,
String sortProperty,
String[] objectIds,
String searchText) {
log.trace("Executing findByTenantId [{}]", tenantId);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
List<Resource> resources = resourceDao.findResourcesByTenantIdAndResourceType(tenantId, ResourceType.LWM2M_MODEL,
objectIds,
searchText);
List<LwM2mObject> lwM2mObjects = resources.stream().map(this::toLwM2mObject).collect(Collectors.toList());
return lwM2mObjects.size() > 1 ? this.sortList (lwM2mObjects, sortProperty, sortOrder) : lwM2mObjects;
} }
@Override @Override
@ -86,11 +124,13 @@ public class BaseResourceService implements ResourceService {
if (resource == null) { if (resource == null) {
throw new DataValidationException("Resource should be specified!"); throw new DataValidationException("Resource should be specified!");
} }
if (resource.getValue() == null) { if (resource.getValue() == null) {
throw new DataValidationException("Resource value should be specified!"); throw new DataValidationException("Resource value should be specified!");
} }
validate(resource.getTenantId(), resource.getResourceType(), resource.getResourceId()); validate(resource.getTenantId(), resource.getResourceType(), resource.getResourceId());
if (resource.getResourceType().equals(ResourceType.LWM2M_MODEL) && resource.toLwM2mObject() == null) {
throw new DataValidationException(String.format("Could not parse the XML of objectModel with name %s", resource.getTextSearch()));
}
} }
protected void validate(TenantId tenantId, ResourceType resourceType, String resourceId) { protected void validate(TenantId tenantId, ResourceType resourceType, String resourceId) {
@ -103,4 +143,61 @@ public class BaseResourceService implements ResourceService {
validateId(tenantId, "Incorrect tenantId "); validateId(tenantId, "Incorrect tenantId ");
} }
private LwM2mObject toLwM2mObject(Resource resource) {
try {
DDFFileParser ddfFileParser = new DDFFileParser(new DefaultDDFFileValidator());
List<ObjectModel> objectModels =
ddfFileParser.parseEx(new ByteArrayInputStream(Base64.getDecoder().decode(resource.getValue())), resource.getTextSearch());
if (objectModels.size() == 0) {
return null;
} else {
ObjectModel obj = objectModels.get(0);
LwM2mObject lwM2mObject = new LwM2mObject();
lwM2mObject.setId(obj.id);
lwM2mObject.setKeyId(resource.getResourceId());
lwM2mObject.setName(obj.name);
lwM2mObject.setMultiple(obj.multiple);
lwM2mObject.setMandatory(obj.mandatory);
LwM2mInstance instance = new LwM2mInstance();
instance.setId(0);
List<LwM2mResource> resources = new ArrayList<>();
obj.resources.forEach((k, v) -> {
if (!v.operations.isExecutable()) {
LwM2mResource lwM2mResource = new LwM2mResource(k, v.name, false, false, false);
resources.add(lwM2mResource);
}
});
instance.setResources(resources.stream().toArray(LwM2mResource[]::new));
lwM2mObject.setInstances(new LwM2mInstance[]{instance});
return lwM2mObject;
}
} catch (IOException | InvalidDDFFileException e) {
log.error("Could not parse the XML of objectModel with name [{}]", resource.getTextSearch(), e);
return null;
}
}
private List<LwM2mObject> sortList (List<LwM2mObject> lwM2mObjects, String sortProperty, String sortOrder) {
switch (sortProperty) {
case "name":
switch (sortOrder) {
case "ASC":
lwM2mObjects.sort((o1, o2) -> o1.getName().compareTo(o2.getName()));
break;
case "DESC":
lwM2mObjects.stream().sorted(Comparator.comparing(LwM2mObject::getName).reversed());
break;
}
case "id":
switch (sortOrder) {
case "ASC":
lwM2mObjects.sort((o1, o2) -> Long.compare(o1.getId(), o2.getId()));
break;
case "DESC":
lwM2mObjects.sort((o1, o2) -> Long.compare(o2.getId(), o1.getId()));
}
}
return lwM2mObjects;
}
} }

9
dao/src/main/java/org/thingsboard/server/dao/resource/ResourceDao.java

@ -33,8 +33,15 @@ public interface ResourceDao {
PageData<Resource> findAllByTenantId(TenantId tenantId, PageLink pageLink); PageData<Resource> findAllByTenantId(TenantId tenantId, PageLink pageLink);
PageData<Resource> findResourcesByTenantIdAndResourceType(TenantId tenantId,
ResourceType resourceType,
PageLink pageLink);
List<Resource> findAllByTenantIdResourceType(TenantId tenantId, ResourceType resourceType); List<Resource> findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType);
List<Resource> findResourcesByTenantIdAndResourceType(TenantId tenantId,
ResourceType resourceType,
String[] objectIds,
String searchText);
void removeAllByTenantId(TenantId tenantId); void removeAllByTenantId(TenantId tenantId);
} }

32
dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceDaoImpl.java

@ -29,6 +29,7 @@ import org.thingsboard.server.dao.model.sql.ResourceEntity;
import org.thingsboard.server.dao.resource.ResourceDao; import org.thingsboard.server.dao.resource.ResourceDao;
import java.util.List; import java.util.List;
import java.util.Objects;
@Slf4j @Slf4j
@Component @Component
@ -73,10 +74,39 @@ public class ResourceDaoImpl implements ResourceDao {
} }
@Override @Override
public List<Resource> findAllByTenantIdResourceType(TenantId tenantId, ResourceType resourceType) { public List<Resource> findAllByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType) {
return DaoUtil.convertDataList(resourceRepository.findAllByTenantIdAndResourceType(tenantId.getId(), resourceType.name())); return DaoUtil.convertDataList(resourceRepository.findAllByTenantIdAndResourceType(tenantId.getId(), resourceType.name()));
} }
@Override
public PageData<Resource> findResourcesByTenantIdAndResourceType(TenantId tenantId,
ResourceType resourceType,
PageLink pageLink) {
return DaoUtil.toPageData(resourceRepository.findResourcesPage(
tenantId.getId(),
TenantId.SYS_TENANT_ID.getId(),
resourceType.name(),
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)
));
}
@Override
public List<Resource> findResourcesByTenantIdAndResourceType(TenantId tenantId, ResourceType resourceType,
String[] objectIds,
String searchText) {
return objectIds == null ?
DaoUtil.convertDataList(resourceRepository.findResources(
tenantId.getId(),
TenantId.SYS_TENANT_ID.getId(),
resourceType.name(),
Objects.toString(searchText, ""))) :
DaoUtil.convertDataList(resourceRepository.findResourcesByIds(
tenantId.getId(),
TenantId.SYS_TENANT_ID.getId(),
resourceType.name(), objectIds));
}
@Override @Override
public void removeAllByTenantId(TenantId tenantId) { public void removeAllByTenantId(TenantId tenantId) {
resourceRepository.removeAllByTenantId(tenantId.getId()); resourceRepository.removeAllByTenantId(tenantId.getId());

49
dao/src/main/java/org/thingsboard/server/dao/sql/resource/ResourceRepository.java

@ -17,7 +17,9 @@ package org.thingsboard.server.dao.sql.resource;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.dao.model.sql.ResourceCompositeKey; import org.thingsboard.server.dao.model.sql.ResourceCompositeKey;
import org.thingsboard.server.dao.model.sql.ResourceEntity; import org.thingsboard.server.dao.model.sql.ResourceEntity;
@ -26,10 +28,57 @@ import java.util.UUID;
public interface ResourceRepository extends CrudRepository<ResourceEntity, ResourceCompositeKey> { public interface ResourceRepository extends CrudRepository<ResourceEntity, ResourceCompositeKey> {
Page<ResourceEntity> findAllByTenantId(UUID tenantId, Pageable pageable); Page<ResourceEntity> findAllByTenantId(UUID tenantId, Pageable pageable);
@Query("SELECT tr FROM ResourceEntity tr " +
"WHERE tr.resourceType = :resourceType " +
"AND LOWER(tr.textSearch) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
"AND (tr.tenantId = :tenantId " +
"OR (tr.tenantId = :systemAdminId " +
"AND NOT EXISTS " +
"(SELECT sr FROM ResourceEntity sr " +
"WHERE sr.tenantId = :tenantId " +
"AND sr.resourceType = :resourceType " +
"AND tr.resourceId = sr.resourceId)))")
Page<ResourceEntity> findResourcesPage(
@Param("tenantId") UUID tenantId,
@Param("systemAdminId") UUID sysAdminId,
@Param("resourceType") String resourceType,
@Param("searchText") String search,
Pageable pageable);
List<ResourceEntity> findAllByTenantIdAndResourceType(UUID tenantId, String resourceType); List<ResourceEntity> findAllByTenantIdAndResourceType(UUID tenantId, String resourceType);
void removeAllByTenantId(UUID tenantId); void removeAllByTenantId(UUID tenantId);
@Query("SELECT tr FROM ResourceEntity tr " +
"WHERE tr.resourceType = :resourceType " +
"AND LOWER(tr.textSearch) LIKE LOWER(CONCAT('%', :searchText, '%')) " +
"AND (tr.tenantId = :tenantId " +
"OR (tr.tenantId = :systemAdminId " +
"AND NOT EXISTS " +
"(SELECT sr FROM ResourceEntity sr " +
"WHERE sr.tenantId = :tenantId " +
"AND sr.resourceType = :resourceType " +
"AND tr.resourceId = sr.resourceId)))")
List<ResourceEntity> findResources(@Param("tenantId") UUID tenantId,
@Param("systemAdminId") UUID sysAdminId,
@Param("resourceType") String resourceType,
@Param("searchText") String search);
@Query("SELECT tr FROM ResourceEntity tr " +
"WHERE tr.resourceType = :resourceType " +
"AND tr.resourceId in (:resourceIds) " +
"AND (tr.tenantId = :tenantId " +
"OR (tr.tenantId = :systemAdminId " +
"AND NOT EXISTS " +
"(SELECT sr FROM ResourceEntity sr " +
"WHERE sr.tenantId = :tenantId " +
"AND sr.resourceType = :resourceType " +
"AND tr.resourceId = sr.resourceId)))")
List<ResourceEntity> findResourcesByIds(@Param("tenantId") UUID tenantId,
@Param("systemAdminId") UUID sysAdminId,
@Param("resourceType") String resourceType,
@Param("resourceIds") String[] objectIds);
} }

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

@ -425,6 +425,7 @@ CREATE TABLE IF NOT EXISTS resource (
tenant_id uuid NOT NULL, tenant_id uuid NOT NULL,
resource_type varchar(32) NOT NULL, resource_type varchar(32) NOT NULL,
resource_id varchar(255) NOT NULL, resource_id varchar(255) NOT NULL,
text_search varchar(255),
resource_value varchar, resource_value varchar,
CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id) CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)
); );

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

@ -451,6 +451,7 @@ CREATE TABLE IF NOT EXISTS resource (
tenant_id uuid NOT NULL, tenant_id uuid NOT NULL,
resource_type varchar(32) NOT NULL, resource_type varchar(32) NOT NULL,
resource_id varchar(255) NOT NULL, resource_id varchar(255) NOT NULL,
text_search varchar(255),
resource_value varchar, resource_value varchar,
CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id) CONSTRAINT resource_unq_key UNIQUE (tenant_id, resource_type, resource_id)
); );

30
ui-ngx/src/app/core/http/device-profile.service.ts

@ -14,16 +14,16 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Injectable } from '@angular/core'; import {Injectable} from '@angular/core';
import { HttpClient } from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link'; import {PageLink} from '@shared/models/page/page-link';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import {defaultHttpOptionsFromConfig, RequestConfig} from './http-utils';
import { Observable } from 'rxjs'; import {Observable} from 'rxjs';
import { PageData } from '@shared/models/page/page-data'; import {PageData} from '@shared/models/page/page-data';
import { DeviceProfile, DeviceProfileInfo, DeviceTransportType } from '@shared/models/device.models'; import {DeviceProfile, DeviceProfileInfo, DeviceTransportType} from '@shared/models/device.models';
import { isDefinedAndNotNull, isEmptyStr } from '@core/utils'; import {isDefinedAndNotNull, isEmptyStr} from '@core/utils';
import { ObjectLwM2M, ServerSecurityConfig } from '@home/components/profile/device/lwm2m/profile-config.models'; import {ObjectLwM2M, ServerSecurityConfig} from '@home/components/profile/device/lwm2m/profile-config.models';
import { SortOrder } from '@shared/models/page/sort-order'; import {SortOrder} from '@shared/models/page/sort-order';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -43,9 +43,9 @@ export class DeviceProfileService {
return this.http.get<DeviceProfile>(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config)); return this.http.get<DeviceProfile>(`/api/deviceProfile/${deviceProfileId}`, defaultHttpOptionsFromConfig(config));
} }
public getLwm2mObjects(sortOrder: SortOrder, objectIds?: number[], searchText?: string, config?: RequestConfig): public getLwm2mObjects(sortOrder: SortOrder, objectIds?: string[], searchText?: string, config?: RequestConfig):
Observable<Array<ObjectLwM2M>> { Observable<Array<ObjectLwM2M>> {
let url = `/api/lwm2m/deviceProfile/?sortProperty=${sortOrder.property}&sortOrder=${sortOrder.direction}`; let url = `/api/resource/lwm2m/?sortProperty=${sortOrder.property}&sortOrder=${sortOrder.direction}`;
if (isDefinedAndNotNull(objectIds) && objectIds.length > 0) { if (isDefinedAndNotNull(objectIds) && objectIds.length > 0) {
url += `&objectIds=${objectIds}`; url += `&objectIds=${objectIds}`;
} }
@ -63,9 +63,9 @@ export class DeviceProfileService {
); );
} }
public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable<PageData<ObjectLwM2M>> { public getLwm2mObjectsPage(pageLink: PageLink, config?: RequestConfig): Observable<Array<ObjectLwM2M>> {
return this.http.get<PageData<ObjectLwM2M>>( return this.http.get<Array<ObjectLwM2M>>(
`/api/lwm2m/deviceProfile/objects${pageLink.toQuery()}`, `/api/resource/lwm2m/page${pageLink.toQuery()}`,
defaultHttpOptionsFromConfig(config) defaultHttpOptionsFromConfig(config)
); );
} }

82
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-device-profile-transport-configuration.component.ts

@ -172,7 +172,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => { private updateObserveAttrTelemetryObjectFormGroup = (objectsList: ObjectLwM2M[]): void => {
this.lwm2mDeviceProfileFormGroup.patchValue({ this.lwm2mDeviceProfileFormGroup.patchValue({
observeAttrTelemetry: this.getObserveAttrTelemetryObjects(objectsList) observeAttrTelemetry: deepClone(this.getObserveAttrTelemetryObjects(objectsList))
}, },
{emitEvent: false}); {emitEvent: false});
} }
@ -195,32 +195,32 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
} }
} }
private getObserveAttrTelemetryObjects = (listObject: ObjectLwM2M[]): object => { private getObserveAttrTelemetryObjects = (objectList: ObjectLwM2M[]): object => {
const clientObserveAttrTelemetry = listObject; const objectLwM2MS = deepClone(objectList);
if (this.configurationValue.observeAttr) { if (this.configurationValue.observeAttr && objectLwM2MS.length > 0) {
const observeArray = this.configurationValue.observeAttr.observe; const observeArray = this.configurationValue.observeAttr.observe;
const attributeArray = this.configurationValue.observeAttr.attribute; const attributeArray = this.configurationValue.observeAttr.attribute;
const telemetryArray = this.configurationValue.observeAttr.telemetry; const telemetryArray = this.configurationValue.observeAttr.telemetry;
let keyNameJson = this.configurationValue.observeAttr.keyName; let keyNameJson = this.configurationValue.observeAttr.keyName;
if (this.includesNotZeroInstance(attributeArray, telemetryArray)) { if (this.includesNotZeroInstance(attributeArray, telemetryArray)) {
this.addInstances(attributeArray, telemetryArray, clientObserveAttrTelemetry); this.addInstances(attributeArray, telemetryArray, objectLwM2MS);
} }
if (isDefinedAndNotNull(observeArray)) { if (isDefinedAndNotNull(observeArray)) {
this.updateObserveAttrTelemetryObjects(observeArray, clientObserveAttrTelemetry, OBSERVE); this.updateObserveAttrTelemetryObjects(observeArray, objectLwM2MS, OBSERVE);
} }
if (isDefinedAndNotNull(attributeArray)) { if (isDefinedAndNotNull(attributeArray)) {
this.updateObserveAttrTelemetryObjects(attributeArray, clientObserveAttrTelemetry, ATTRIBUTE); this.updateObserveAttrTelemetryObjects(attributeArray, objectLwM2MS, ATTRIBUTE);
} }
if (isDefinedAndNotNull(telemetryArray)) { if (isDefinedAndNotNull(telemetryArray)) {
this.updateObserveAttrTelemetryObjects(telemetryArray, clientObserveAttrTelemetry, TELEMETRY); this.updateObserveAttrTelemetryObjects(telemetryArray, objectLwM2MS, TELEMETRY);
} }
if (isDefinedAndNotNull(keyNameJson)) { if (isDefinedAndNotNull(keyNameJson)) {
this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray); this.configurationValue.observeAttr.keyName = this.validateKeyNameObjects(keyNameJson, attributeArray, telemetryArray);
this.upDateJsonAllConfig(); this.upDateJsonAllConfig();
this.updateKeyNameObjects(clientObserveAttrTelemetry); this.updateKeyNameObjects(objectLwM2MS);
} }
} }
return {clientLwM2M: clientObserveAttrTelemetry}; return {clientLwM2M: objectLwM2MS};
} }
private includesNotZeroInstance = (attribute: string[], telemetry: string[]): boolean => { private includesNotZeroInstance = (attribute: string[], telemetry: string[]): boolean => {
@ -235,35 +235,39 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
.sort(this.sortPath); .sort(this.sortPath);
new Set(instancesPath).forEach(path => { new Set(instancesPath).forEach(path => {
const pathParameter = Array.from(path.split('/'), Number); const pathParameter = Array.from(path.split('/'), String);
const objectLwM2M = clientObserveAttrTelemetry.find(x => x.id === pathParameter[0]); const objectLwM2M = clientObserveAttrTelemetry.find(x => x.keyId === pathParameter[0]);
if (objectLwM2M) { if (objectLwM2M) {
const instance = deepClone(objectLwM2M.instances[0]); const instance = deepClone(objectLwM2M.instances[0]);
instance.id = pathParameter[1]; instance.id = +pathParameter[1];
objectLwM2M.instances.push(instance); objectLwM2M.instances.push(instance);
} }
}); });
} }
private updateObserveAttrTelemetryObjects = (parameters: string[], clientObserveAttrTelemetry: ObjectLwM2M[], private updateObserveAttrTelemetryObjects = (parameters: string[], objectLwM2MS: ObjectLwM2M[],
nameParameter: string): void => { nameParameter: string): void => {
parameters.forEach(parameter => { parameters.forEach(parameter => {
const [objectId, instanceId, resourceId] = Array.from(parameter.substring(1).split('/'), Number); const [objectKeyId, instanceId, resourceId] = Array.from(parameter.substring(1).split('/'), String);
clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId) const objectLwM2M = objectLwM2MS.find(objectLwm2m => objectLwm2m.keyId === objectKeyId);
.instances.find(itrInstance => itrInstance.id === instanceId) if (objectLwM2M) {
.resources.find(resource => resource.id === resourceId) objectLwM2M.instances.find(itrInstance => itrInstance.id === +instanceId)
[nameParameter] = true; .resources.find(resource => resource.id === +resourceId)
[nameParameter] = true;
}
}); });
} }
private updateKeyNameObjects = (clientObserveAttrTelemetry: ObjectLwM2M[]): void => { private updateKeyNameObjects = (clientObserveAttrTelemetry: ObjectLwM2M[]): void => {
Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => { Object.keys(this.configurationValue.observeAttr.keyName).forEach(key => {
const [objectId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), Number); const [objectKeyId, instanceId, resourceId] = Array.from(key.substring(1).split('/'), String);
clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.id === objectId) const objectLwM2M = clientObserveAttrTelemetry.find(objectLwm2m => objectLwm2m.keyId === objectKeyId)
.instances.find(instance => instance.id === instanceId) if (objectLwM2M) {
.resources.find(resource => resource.id === resourceId) objectLwM2M.instances.find(instance => instance.id === +instanceId)
.resources.find(resource => resource.id === +resourceId)
.keyName = this.configurationValue.observeAttr.keyName[key]; .keyName = this.configurationValue.observeAttr.keyName[key];
}); }});
} }
private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => { private validateKeyNameObjects = (nameJson: JsonObject, attributeArray: JsonArray, telemetryArray: JsonArray): {} => {
@ -290,7 +294,7 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) { if (instance.hasOwnProperty(RESOURCES) && Array.isArray(instance.resources)) {
instance.resources.forEach(resource => { instance.resources.forEach(resource => {
if (resource.attribute || resource.telemetry) { if (resource.attribute || resource.telemetry) {
let pathRes = `/${obj.id}/${instance.id}/${resource.id}`; let pathRes = `/${obj.keyId}/${instance.id}/${resource.id}`;
if (resource.observe) { if (resource.observe) {
observeArray.push(pathRes); observeArray.push(pathRes);
} }
@ -345,22 +349,22 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
}); });
} }
private getObjectsFromJsonAllConfig = (): number[] => { private getObjectsFromJsonAllConfig = (): string[] => {
const objectsIds = new Set<number>(); const objectsIds = new Set<string>();
if (this.configurationValue.observeAttr) { if (this.configurationValue.observeAttr) {
if (this.configurationValue.observeAttr.observe) { if (this.configurationValue.observeAttr.observe) {
this.configurationValue.observeAttr.observe.forEach(obj => { this.configurationValue.observeAttr.observe.forEach(obj => {
objectsIds.add(Array.from(obj.substring(1).split('/'), Number)[0]); objectsIds.add(Array.from(obj.substring(1).split('/'), String)[0]);
}); });
} }
if (this.configurationValue.observeAttr.attribute) { if (this.configurationValue.observeAttr.attribute) {
this.configurationValue.observeAttr.attribute.forEach(obj => { this.configurationValue.observeAttr.attribute.forEach(obj => {
objectsIds.add(Array.from(obj.substring(1).split('/'), Number)[0]); objectsIds.add(Array.from(obj.substring(1).split('/'), String)[0]);
}); });
} }
if (this.configurationValue.observeAttr.telemetry) { if (this.configurationValue.observeAttr.telemetry) {
this.configurationValue.observeAttr.telemetry.forEach(obj => { this.configurationValue.observeAttr.telemetry.forEach(obj => {
objectsIds.add(Array.from(obj.substring(1).split('/'), Number)[0]); objectsIds.add(Array.from(obj.substring(1).split('/'), String)[0]);
}); });
} }
} }
@ -379,21 +383,21 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
removeObjectsList = (value: ObjectLwM2M): void => { removeObjectsList = (value: ObjectLwM2M): void => {
const objectsOld = this.lwm2mDeviceProfileFormGroup.get(OBSERVE_ATTR_TELEMETRY).value.clientLwM2M; const objectsOld = this.lwm2mDeviceProfileFormGroup.get(OBSERVE_ATTR_TELEMETRY).value.clientLwM2M;
const isIdIndex = (element) => element.id === value.id; const isIdIndex = (element) => element.keyId === value.keyId;
const index = objectsOld.findIndex(isIdIndex); const index = objectsOld.findIndex(isIdIndex);
if (index >= 0) { if (index >= 0) {
objectsOld.splice(index, 1); objectsOld.splice(index, 1);
} }
this.removeObserveAttrTelemetryFromJson(OBSERVE, value.id); this.removeObserveAttrTelemetryFromJson(OBSERVE, value.keyId);
this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.id); this.removeObserveAttrTelemetryFromJson(TELEMETRY, value.keyId);
this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.id); this.removeObserveAttrTelemetryFromJson(ATTRIBUTE, value.keyId);
this.removeKeyNameFromJson(value.id); this.removeKeyNameFromJson(value.keyId);
this.updateObserveAttrTelemetryObjectFormGroup(objectsOld); this.updateObserveAttrTelemetryObjectFormGroup(objectsOld);
this.upDateJsonAllConfig(); this.upDateJsonAllConfig();
} }
private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, id: number): void => { private removeObserveAttrTelemetryFromJson = (observeAttrTel: string, keyId: string): void => {
const isIdIndex = (element) => element.startsWith(`/${id}`); const isIdIndex = (element) => element.startsWith(`/${keyId}`);
let index = this.configurationValue.observeAttr[observeAttrTel].findIndex(isIdIndex); let index = this.configurationValue.observeAttr[observeAttrTel].findIndex(isIdIndex);
while (index >= 0) { while (index >= 0) {
this.configurationValue.observeAttr[observeAttrTel].splice(index, 1); this.configurationValue.observeAttr[observeAttrTel].splice(index, 1);
@ -401,10 +405,10 @@ export class Lwm2mDeviceProfileTransportConfigurationComponent implements Contro
} }
} }
private removeKeyNameFromJson = (id: number): void => { private removeKeyNameFromJson = (keyId: string): void => {
const keyNameJson = this.configurationValue.observeAttr.keyName; const keyNameJson = this.configurationValue.observeAttr.keyName;
Object.keys(keyNameJson).forEach(key => { Object.keys(keyNameJson).forEach(key => {
if (key.startsWith(`/${id}`)) { if (key.startsWith(`/${keyId}`)) {
delete keyNameJson[key]; delete keyNameJson[key];
} }
}); });

12
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances-list.component.ts

@ -14,12 +14,11 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, forwardRef, } from '@angular/core'; import {Component, forwardRef,} from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import { Store } from '@ngrx/store'; import {Store} from '@ngrx/store';
import { AppState } from '@core/core.state'; import {AppState} from '@core/core.state';
import { INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER } from './profile-config.models'; import {INSTANCES_ID_VALUE_MAX, INSTANCES_ID_VALUE_MIN, KEY_REGEXP_NUMBER} from './profile-config.models';
import { DeviceProfileService } from '@core/http/device-profile.service';
@Component({ @Component({
selector: 'tb-profile-lwm2m-object-add-instances-list', selector: 'tb-profile-lwm2m-object-add-instances-list',
@ -43,7 +42,6 @@ export class Lwm2mObjectAddInstancesListComponent implements ControlValueAccesso
private propagateChange = (v: any) => { }; private propagateChange = (v: any) => { };
constructor(private store: Store<AppState>, constructor(private store: Store<AppState>,
private deviceProfileService: DeviceProfileService,
private fb: FormBuilder) { private fb: FormBuilder) {
this.instancesListFormGroup = this.fb.group({ this.instancesListFormGroup = this.fb.group({
instanceIdInput: [null, [ instanceIdInput: [null, [

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.html

@ -17,7 +17,7 @@
--> -->
<form [formGroup]="instancesFormGroup" (ngSubmit)="add()" style="min-width: 400px;"> <form [formGroup]="instancesFormGroup" (ngSubmit)="add()" style="min-width: 400px;">
<mat-toolbar fxLayout="row" color="primary"> <mat-toolbar fxLayout="row" color="primary">
<b><i>{{data.objectName}}</i></b> &nbsp;&nbsp; (object&nbsp;[<b>{{data.objectId}}</b>]) <b><i>{{data.objectName}}</i></b> &nbsp;&nbsp; (object&nbsp;[<b>{{data.objectKeyId}}</b>])
<span fxFlex></span> <span fxFlex></span>
<button mat-button mat-icon-button <button mat-button mat-icon-button
(click)="cancel()" (click)="cancel()"

16
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-add-instances.component.ts

@ -14,18 +14,18 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Inject, OnInit } from '@angular/core'; import {Component, Inject, OnInit} from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component'; import {DialogComponent} from '@shared/components/dialog.component';
import { FormBuilder, FormGroup } from '@angular/forms'; import {FormBuilder, FormGroup} from '@angular/forms';
import { Store } from '@ngrx/store'; import {Store} from '@ngrx/store';
import { AppState } from '@core/core.state'; import {AppState} from '@core/core.state';
import { Router } from '@angular/router'; import {Router} from '@angular/router';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
export interface Lwm2mObjectAddInstancesData { export interface Lwm2mObjectAddInstancesData {
instancesIds: Set<number>; instancesIds: Set<number>;
objectName?: string; objectName?: string;
objectId?: number; objectKeyId?: string;
} }
@Component({ @Component({

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.html

@ -40,7 +40,7 @@
class="tb-autocomplete" class="tb-autocomplete"
[displayWith]="displayObjectLwm2mFn"> [displayWith]="displayObjectLwm2mFn">
<mat-option *ngFor="let objectLwm2m of filteredObjectsList | async" [value]="objectLwm2m"> <mat-option *ngFor="let objectLwm2m of filteredObjectsList | async" [value]="objectLwm2m">
<span [innerHTML]="objectLwm2m.id + ': ' + objectLwm2m.name | highlight:searchText"></span> <span [innerHTML]="objectLwm2m.keyId + ': ' + objectLwm2m.name | highlight:searchText"></span>
</mat-option> </mat-option>
<mat-option *ngIf="!(filteredObjectsList | async)?.length" [value]="null"> <mat-option *ngIf="!(filteredObjectsList | async)?.length" [value]="null">
<span> <span>

61
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-object-list.component.ts

@ -14,17 +14,18 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild } from '@angular/core'; import {Component, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, ViewChild} from '@angular/core';
import { ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import {coerceBooleanProperty} from '@angular/cdk/coercion';
import { Store } from '@ngrx/store'; import {Store} from '@ngrx/store';
import { AppState } from '@core/core.state'; import {AppState} from '@core/core.state';
import { Observable } from 'rxjs'; import {Observable} from 'rxjs';
import { filter, map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; import {filter, map, mergeMap, publishReplay, refCount, tap} from 'rxjs/operators';
import { ModelValue, ObjectLwM2M } from './profile-config.models'; import {ModelValue, ObjectLwM2M, PAGE_SIZE_LIMIT} from './profile-config.models';
import { DeviceProfileService } from '@core/http/device-profile.service'; import {DeviceProfileService} from '@core/http/device-profile.service';
import { Direction } from '@shared/models/page/sort-order'; import {Direction} from '@shared/models/page/sort-order';
import { isDefined, isDefinedAndNotNull, isEmptyStr, isString } from '@core/utils'; import {isDefined, isDefinedAndNotNull, isString} from '@core/utils';
import {PageLink} from "@shared/models/page/page-link";
@Component({ @Component({
selector: 'tb-profile-lwm2m-object-list', selector: 'tb-profile-lwm2m-object-list',
@ -41,7 +42,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
private requiredValue: boolean; private requiredValue: boolean;
private dirty = false; private dirty = false;
private lw2mModels: Observable<Array<ObjectLwM2M>>; private lw2mModels: Observable<Array<ObjectLwM2M>>;
private modelValue: Array<number> = []; private modelValue: Array<string> = [];
lwm2mListFormGroup: FormGroup; lwm2mListFormGroup: FormGroup;
objectsList: Array<ObjectLwM2M> = []; objectsList: Array<ObjectLwM2M> = [];
@ -134,8 +135,8 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
} }
private add(object: ObjectLwM2M): void { private add(object: ObjectLwM2M): void {
if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.id) === -1) { if (isDefinedAndNotNull(this.modelValue) && this.modelValue.indexOf(object.keyId) === -1) {
this.modelValue.push(object.id); this.modelValue.push(object.keyId);
this.objectsList.push(object); this.objectsList.push(object);
this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList);
this.addList.next(this.objectsList); this.addList.next(this.objectsList);
@ -148,7 +149,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
if (index >= 0) { if (index >= 0) {
this.objectsList.splice(index, 1); this.objectsList.splice(index, 1);
this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList); this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList);
index = this.modelValue.indexOf(object.id); index = this.modelValue.indexOf(object.keyId);
this.modelValue.splice(index, 1); this.modelValue.splice(index, 1);
this.removeList.next(object); this.removeList.next(object);
this.clear(); this.clear();
@ -159,25 +160,29 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V
return object ? object.name : undefined; return object ? object.name : undefined;
} }
private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => { private fetchListObjects = (searchText?: string): Observable<Array<ObjectLwM2M>> => {
this.searchText = searchText; this.searchText = searchText;
const filters = {names: [], ids: []}; return this.getLwM2mModelsPage().pipe(
if (isDefinedAndNotNull(searchText) && !isEmptyStr(searchText)) { map(objectLwM2Ms => objectLwM2Ms)
const ids = searchText.match(/\d+/g); );
filters.ids = ids !== null ? ids.map(Number) : filters.ids; }
filters.names = searchText.trim().toUpperCase().split(' ');
} private getLwM2mModelsPage(): Observable<Array<ObjectLwM2M>> {
const predicate = objectLwM2M => filters.names.find(word => objectLwM2M.name.toUpperCase().includes(word)) const pageLink = new PageLink(PAGE_SIZE_LIMIT, 0, this.searchText, {
|| filters.ids.includes(objectLwM2M.id); property: 'id',
return this.getLwM2mModels().pipe( direction: Direction.ASC
map(objectLwM2Ms => searchText ? objectLwM2Ms.filter(predicate) : objectLwM2Ms) });
); this.lw2mModels = this.deviceProfileService.getLwm2mObjectsPage(pageLink).pipe(
publishReplay(1),
refCount()
);
return this.lw2mModels;
} }
private getLwM2mModels(): Observable<Array<ObjectLwM2M>> { private getLwM2mModels(): Observable<Array<ObjectLwM2M>> {
if (!this.lw2mModels) { if (!this.lw2mModels) {
const sortOrder = { const sortOrder = {
property: 'name', property: 'id',
direction: Direction.ASC direction: Direction.ASC
}; };
this.lw2mModels = this.deviceProfileService.getLwm2mObjects(sortOrder).pipe( this.lw2mModels = this.deviceProfileService.getLwm2mObjects(sortOrder).pipe(

2
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.html

@ -22,7 +22,7 @@
[formGroupName]="i"> [formGroupName]="i">
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title fxLayoutAlign="start center"> <mat-panel-title fxLayoutAlign="start center">
<b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;id: {{ objectLwM2M.get('id').value}}> <b><i>{{ objectLwM2M.get('name').value}}</i></b>&nbsp;&lt;id: {{ objectLwM2M.get('keyId').value}}>
</mat-panel-title> </mat-panel-title>
<mat-panel-description fxLayoutAlign="end center" *ngIf="!disabled"> <mat-panel-description fxLayoutAlign="end center" *ngIf="!disabled">
<button type="button" <button type="button"

22
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/lwm2m-observe-attr-telemetry.component.ts

@ -15,7 +15,7 @@
/// ///
import { Component, forwardRef, Input } from '@angular/core'; import {Component, forwardRef, Input} from '@angular/core';
import { import {
AbstractControl, AbstractControl,
ControlValueAccessor, ControlValueAccessor,
@ -25,13 +25,13 @@ import {
NG_VALUE_ACCESSOR, NG_VALUE_ACCESSOR,
Validators Validators
} from '@angular/forms'; } from '@angular/forms';
import { Store } from '@ngrx/store'; import {Store} from '@ngrx/store';
import { AppState } from '@core/core.state'; import {AppState} from '@core/core.state';
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import {coerceBooleanProperty} from '@angular/cdk/coercion';
import { CLIENT_LWM2M, Instance, INSTANCES, ObjectLwM2M, ResourceLwM2M, RESOURCES } from './profile-config.models'; import {CLIENT_LWM2M, Instance, INSTANCES, ObjectLwM2M, ResourceLwM2M, RESOURCES} from './profile-config.models';
import { deepClone, isDefinedAndNotNull, isEqual, isUndefined } from '@core/utils'; import {deepClone, isDefinedAndNotNull, isEqual, isUndefined} from '@core/utils';
import { MatDialog } from '@angular/material/dialog'; import {MatDialog} from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import { import {
Lwm2mObjectAddInstancesComponent, Lwm2mObjectAddInstancesComponent,
Lwm2mObjectAddInstancesData Lwm2mObjectAddInstancesData
@ -139,7 +139,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
private createObjectsLwM2M = (objectsLwM2M: ObjectLwM2M[]): FormArray => { private createObjectsLwM2M = (objectsLwM2M: ObjectLwM2M[]): FormArray => {
return this.fb.array(objectsLwM2M.map((objectLwM2M) => { return this.fb.array(objectsLwM2M.map((objectLwM2M) => {
return this.fb.group({ return this.fb.group({
id: objectLwM2M.id, keyId: objectLwM2M.keyId,
name: objectLwM2M.name, name: objectLwM2M.name,
multiple: objectLwM2M.multiple, multiple: objectLwM2M.multiple,
mandatory: objectLwM2M.mandatory, mandatory: objectLwM2M.mandatory,
@ -228,7 +228,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
data: { data: {
instancesIds: this.instancesToSetId(object.instances), instancesIds: this.instancesToSetId(object.instances),
objectName: object.name, objectName: object.name,
objectId: object.id objectKeyId: object.keyId
} }
}).afterClosed().subscribe( }).afterClosed().subscribe(
(res: Lwm2mObjectAddInstancesData | undefined) => { (res: Lwm2mObjectAddInstancesData | undefined) => {
@ -241,7 +241,7 @@ export class Lwm2mObserveAttrTelemetryComponent implements ControlValueAccessor
private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => { private updateInstancesIds = (data: Lwm2mObjectAddInstancesData): void => {
const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls const objectLwM2MFormGroup = (this.observeAttrTelemetryFormGroup.get(CLIENT_LWM2M) as FormArray).controls
.find(e => e.value.id === data.objectId) as FormGroup; .find(e => e.value.keyId === data.objectKeyId) as FormGroup;
const instancesArray = objectLwM2MFormGroup.value.instances as Instance []; const instancesArray = objectLwM2MFormGroup.value.instances as Instance [];
const instancesFormArray = objectLwM2MFormGroup.get(INSTANCES) as FormArray; const instancesFormArray = objectLwM2MFormGroup.get(INSTANCES) as FormArray;
const instance0 = deepClone(instancesFormArray.at(0).value as Instance); const instance0 = deepClone(instancesFormArray.at(0).value as Instance);

4
ui-ngx/src/app/modules/home/components/profile/device/lwm2m/profile-config.models.ts

@ -14,6 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
export const PAGE_SIZE_LIMIT = 50;
export const INSTANCES = 'instances'; export const INSTANCES = 'instances';
export const RESOURCES = 'resources'; export const RESOURCES = 'resources';
export const CLIENT_LWM2M = 'clientLwM2M'; export const CLIENT_LWM2M = 'clientLwM2M';
@ -58,7 +59,7 @@ export const SECURITY_CONFIG_MODE_NAMES = new Map<SECURITY_CONFIG_MODE, string>(
); );
export interface ModelValue { export interface ModelValue {
objectIds: number[] | null, objectIds: string[] | null,
objectsList: ObjectLwM2M[] objectsList: ObjectLwM2M[]
} }
@ -190,6 +191,7 @@ export interface Instance {
*/ */
export interface ObjectLwM2M { export interface ObjectLwM2M {
id: number; id: number;
keyId: string;
name: string; name: string;
multiple?: boolean; multiple?: boolean;
mandatory?: boolean; mandatory?: boolean;

Loading…
Cancel
Save