Lwm2m: backEnd: add saveDeviceWithCredential

This commit is contained in:
nickAS21 2020-12-22 16:23:03 +02:00
parent ca46f5fb75
commit 1150d4e4f3
12 changed files with 313 additions and 161 deletions

View File

@ -278,7 +278,6 @@ public class DeviceController extends BaseController {
try {
Device device = checkDeviceId(deviceCredentials.getDeviceId(), Operation.WRITE_CREDENTIALS);
DeviceCredentials result = checkNotNull(deviceCredentialsService.updateDeviceCredentials(getCurrentUser().getTenantId(), deviceCredentials));
//log.info("0 LwM2M CredentialsUpdate start)
tbClusterService.pushMsgToCore(new DeviceCredentialsUpdateNotificationMsg(getCurrentUser().getTenantId(), deviceCredentials.getDeviceId(), result), null);
logEntityAction(device.getId(), device,
device.getCustomerId(),

View File

@ -15,17 +15,32 @@
*/
package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.rule.engine.api.msg.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.lwm2m.LwM2mObject;
import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List;
import java.util.Map;
@Slf4j
@RestController
@ -72,4 +87,40 @@ public class DeviceLwm2mController extends BaseController {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/lwm2m/device-credentials", method = RequestMethod.POST)
@ResponseBody
public Device saveDeviceWithCredentials(@RequestBody (required=false) Map<Class<?>, Object> deviceWithDeviceCredentials) throws ThingsboardException {
ObjectMapper mapper = new ObjectMapper();
Device device = checkNotNull(mapper.convertValue(deviceWithDeviceCredentials.get(Device.class), Device.class));
DeviceCredentials credentials = checkNotNull(mapper.convertValue( deviceWithDeviceCredentials.get(DeviceCredentials.class), DeviceCredentials.class));
try {
device.setTenantId(getCurrentUser().getTenantId());
checkEntity(device.getId(), device, Resource.DEVICE);
Device savedDevice = deviceService.saveDeviceWithCredentials(device, credentials);
checkNotNull(savedDevice);
tbClusterService.onDeviceChange(savedDevice, null);
tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(),
savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(),
device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
logEntityAction(savedDevice.getId(), savedDevice,
savedDevice.getCustomerId(),
device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null);
if (device.getId() == null) {
deviceStateService.onDeviceAdded(savedDevice);
} else {
deviceStateService.onDeviceUpdated(savedDevice);
}
return savedDevice;
} catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), device,
null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
throw handleException(e);
}
}
}

View File

@ -27,6 +27,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.device.provision.ProvisionRequest;
import java.util.List;
@ -45,6 +46,8 @@ public interface DeviceService {
Device saveDeviceWithAccessToken(Device device, String accessToken);
Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials);
Device assignDeviceToCustomer(TenantId tenantId, DeviceId deviceId, CustomerId customerId);
Device unassignDeviceFromCustomer(TenantId tenantId, DeviceId deviceId);

View File

@ -20,7 +20,6 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@ -64,6 +63,17 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
this.setDeviceData(device.getDeviceData());
}
public Device updateDevice(Device device) {
this.tenantId = device.getTenantId();
this.customerId = device.getCustomerId();
this.name = device.getName();
this.type = device.getType();
this.label = device.getLabel();
this.deviceProfileId = device.getDeviceProfileId();
this.setDeviceData(device.getDeviceData());
return this;
}
public TenantId getTenantId() {
return tenantId;
}

View File

@ -45,15 +45,9 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Slf4j
@ -127,14 +121,19 @@ public class LwM2MTransportHandler{
@PostConstruct
public void init() {
LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service);
this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener);
this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener);
lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service);
this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener);
this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener);
try {
LwM2mServerListener lwM2mServerListener = new LwM2mServerListener(lhServerCert, service);
this.lhServerCert.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
this.lhServerCert.getPresenceService().addListener(lwM2mServerListener.presenceListener);
this.lhServerCert.getObservationService().addListener(lwM2mServerListener.observationListener);
lwM2mServerListener = new LwM2mServerListener(lhServerNoSecPskRpk, service);
this.lhServerNoSecPskRpk.getRegistrationService().addListener(lwM2mServerListener.registrationListener);
this.lhServerNoSecPskRpk.getPresenceService().addListener(lwM2mServerListener.presenceListener);
this.lhServerNoSecPskRpk.getObservationService().addListener(lwM2mServerListener.observationListener);
}
catch (java.lang.NullPointerException e) {
log.error("init [{}]", e.toString());
}
}
public static NetworkConfig getCoapConfig() {
@ -150,7 +149,8 @@ public class LwM2MTransportHandler{
return coapConfig;
}
public static String getValueTypeToString (Object value, ResourceModel.Type type) {
public static String getValueTypeToString (Object value, ResourceModel.Type type, int val) {
try{
switch (type) {
case STRING: // String
case OBJLNK: // ObjectLink
@ -160,16 +160,21 @@ public class LwM2MTransportHandler{
case BOOLEAN: // Boolean
return Boolean.toString((Boolean) value);
case FLOAT: // Double
return Double.toString((Float)value);
return Double.toString((Double) value);
case TIME: // Date
String DATE_FORMAT = "MMM d, yyyy HH:mm a";
DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
return formatter.format(new Date(Integer.toUnsignedLong((Integer) value)));
return Long.toString(((Date) value).getTime());
// String DATE_FORMAT = "MMM d, yyyy HH:mm a";
// DateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
// return formatter.format(new Date(Integer.toUnsignedLong((Integer) value)));
case OPAQUE: // byte[] value, base64
return Hex.encodeHexString((byte[])value);
default:
return null;
}
} catch (Exception e) {
log.error(e.getStackTrace().toString());
return null;
}
}
public static LwM2mNode getLvM2mNodeToObject(LwM2mNode content) {
@ -305,28 +310,6 @@ public class LwM2MTransportHandler{
}
}
/**
* Equals to Map for values
* @param map1 -
* @param map2 -
* @param <V> -
* @return - true if equals
*/
public static <V extends Comparable<V>> boolean mapsEquals(Map<?,V> map1, Map<?,V> map2) {
List<V> values1 = new ArrayList<>(map1.values());
List<V> values2 = new ArrayList<>(map2.values());
Collections.sort(values1);
Collections.sort(values2);
return values1.equals(values2);
}
public static String convertCamelCase (String str) {
str = str.toLowerCase().replace("/[^a-z ]+/g", " ");
str = str.replace("/^(.)|\\s(.)/g", "$1");
str = str.replace("/[^a-zA-Z]+/g", "");
return str;
}
public static String splitCamelCaseString(String s){
LinkedList<String> linkedListOut = new LinkedList<>();
LinkedList<String> linkedList = new LinkedList<String>((Arrays.asList(s.split(" "))));

View File

@ -20,6 +20,7 @@ import org.eclipse.californium.core.coap.Response;
import org.eclipse.leshan.core.attributes.Attribute;
import org.eclipse.leshan.core.attributes.AttributeSet;
import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.node.LwM2mSingleResource;
import org.eclipse.leshan.core.node.ObjectLink;
import org.eclipse.leshan.core.observation.Observation;
@ -50,6 +51,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClient;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
@ -79,16 +81,17 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2MTransportHandle
public class LwM2MTransportRequest {
private final ExecutorService executorService;
private static final String RESPONSE_CHANNEL = "THINGSBOARD_RESP";
private final LwM2mValueConverterImpl converter;
@Autowired
LwM2MTransportService service;
public LwM2MTransportRequest() {
this.converter = new LwM2mValueConverterImpl();
executorService = Executors.newCachedThreadPool(
new NamedThreadFactory(String.format("LwM2M %s channel response", RESPONSE_CHANNEL)));
}
@PostConstruct
public void init() {
}
@ -115,12 +118,12 @@ public class LwM2MTransportRequest {
*/
public void sendAllRequest(LeshanServer lwServer, Registration registration, String target, String typeOper, String contentFormatParam,
LwM2MClient lwM2MClient, Observation observation, Object params, long timeoutInMs, boolean isDelayedUpdate) {
ResultIds resultIds = new ResultIds(target);
LwM2mPath resultIds = new LwM2mPath(target);
if (registration != null && resultIds.getObjectId() >= 0) {
DownlinkRequest request = null;
ContentFormat contentFormat = contentFormatParam != null ? ContentFormat.fromName(contentFormatParam.toUpperCase()) : null;
ResourceModel resource = (resultIds.resourceId >= 0) ? (lwM2MClient != null) ?
lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.resourceId) : null : null;
ResourceModel resource = (resultIds.getResourceId() !=null && lwM2MClient != null) ?
lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()) : null;
ResourceModel.Type resType = (resource == null) ? null : resource.type;
boolean resMultiple = (resource == null) ? false : resource.multiple;
timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT;
@ -132,10 +135,10 @@ public class LwM2MTransportRequest {
request = new DiscoverRequest(target);
break;
case GET_TYPE_OPER_OBSERVE:
if (resultIds.getResourceId() >= 0) {
request = new ObserveRequest(resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId());
} else if (resultIds.getInstanceId() >= 0) {
request = new ObserveRequest(resultIds.getObjectId(), resultIds.getInstanceId());
if (resultIds.isResource()) {
request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
} else if (resultIds.isObjectInstance()) {
request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId());
} else if (resultIds.getObjectId() >= 0) {
request = new ObserveRequest(resultIds.getObjectId());
}
@ -145,7 +148,8 @@ public class LwM2MTransportRequest {
break;
case POST_TYPE_OPER_EXECUTE:
if (params != null && !resMultiple) {
request = new ExecuteRequest(target, LwM2MTransportHandler.getValueTypeToString(params, resType));
// request = new ExecuteRequest(target, LwM2MTransportHandler.getValueTypeToString(params, resType));
request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resType, ResourceModel.Type.STRING, resultIds));
} else {
request = new ExecuteRequest(target);
}
@ -153,11 +157,11 @@ public class LwM2MTransportRequest {
case POST_TYPE_OPER_WRITE_REPLACE:
// Request to write a <b>String Single-Instance Resource</b> using the TLV content format.
if (contentFormat.equals(ContentFormat.TLV) && !resMultiple) {
request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType, registration);
request = this.getWriteRequestSingleResource(null, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resType, registration);
}
// Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON)
else if (!contentFormat.equals(ContentFormat.TLV) && !resMultiple) {
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), params, resType, registration);
request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resType, registration);
}
break;
case PUT_TYPE_OPER_WRITE_UPDATE:
@ -202,10 +206,10 @@ public class LwM2MTransportRequest {
Attribute pmin = new Attribute(MINIMUM_PERIOD, Integer.toUnsignedLong(Integer.valueOf("1")));
Attribute[] attrs = {pmin};
AttributeSet attrSet = new AttributeSet(attrs);
if (resultIds.getResourceId() >= 0) {
request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getInstanceId(), resultIds.getResourceId(), attrSet);
} else if (resultIds.getInstanceId() >= 0) {
request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getInstanceId(), attrSet);
if (resultIds.isResource()) {
request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId(), attrSet);
} else if (resultIds.isObjectInstance()) {
request = new WriteAttributesRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), attrSet);
} else if (resultIds.getObjectId() >= 0) {
request = new WriteAttributesRequest(resultIds.getObjectId(), attrSet);
}

View File

@ -64,7 +64,6 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
@ -186,9 +185,9 @@ public class LwM2MTransportService {
if (lwM2mInMemorySecurityStore.getProfiles().size() > 0) {
this.syncSessionsAndProfiles();
}
log.info("Client: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
} else {
log.error("Client: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null);
}
}
@ -242,15 +241,15 @@ public class LwM2MTransportService {
private void setLwM2MClient(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient) {
// #1
Arrays.stream(registration.getObjectLinks()).forEach(url -> {
ResultIds pathIds = new ResultIds(url.getUrl());
if (pathIds.instanceId > -1 && pathIds.resourceId == -1) {
LwM2mPath pathIds = new LwM2mPath(url.getUrl());
if (pathIds.isObjectInstance() && !pathIds.isResource()) {
lwM2MClient.getPendingRequests().add(url.getUrl());
}
});
// #2
Arrays.stream(registration.getObjectLinks()).forEach(url -> {
ResultIds pathIds = new ResultIds(url.getUrl());
if (pathIds.instanceId > -1 && pathIds.resourceId == -1) {
LwM2mPath pathIds = new LwM2mPath(url.getUrl());
if (pathIds.isObjectInstance() && !pathIds.isResource()) {
lwM2MTransportRequest.sendAllRequest(lwServer, registration, url.getUrl(), GET_TYPE_OPER_READ, ContentFormat.TLV.getName(),
lwM2MClient, null, null, this.context.getCtxServer().getTimeout(), false);
}
@ -361,10 +360,10 @@ public class LwM2MTransportService {
}
});
// #2.1
lwM2MClient.getDelayedRequests().forEach((k, v)->{
lwM2MClient.getDelayedRequests().forEach((k, v) -> {
List listV = new ArrayList<TransportProtos.KeyValueProto>();
listV.add(v.getKv());
this.putDelayedUpdateResourcesClient (lwM2MClient, lwM2MClient.getResourceValue(k), getJsonObject(listV).get(v.getKv().getKey()), k);
this.putDelayedUpdateResourcesClient(lwM2MClient, lwM2MClient.getResourceValue(k), getJsonObject(listV).get(v.getKv().getKey()), k);
System.out.printf(" k: %s, v: %s%n, v1: %s%n", k, v.getKv().getStringV(), lwM2MClient.getResourceValue(k));
});
lwM2MClient.getDelayedRequestsId().remove(attributesResponse.getRequestId());
@ -374,7 +373,7 @@ public class LwM2MTransportService {
}
}
private void putDelayedUpdateResourcesClient (LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path){
private void putDelayedUpdateResourcesClient(LwM2MClient lwM2MClient, Object valueOld, Object valueNew, String path) {
if (!valueOld.toString().equals(valueNew.toString())) {
lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
ContentFormat.TLV.getName(), lwM2MClient, null, valueNew, this.context.getCtxServer().getTimeout(),
@ -465,18 +464,20 @@ public class LwM2MTransportService {
private void getParametersFromProfile(JsonObject attributes, JsonObject telemetry, Registration registration, Set<String> path) {
AttrTelemetryObserveValue attrTelemetryObserveValue = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid());
attrTelemetryObserveValue.getPostAttributeProfile().forEach(p -> {
ResultIds pathIds = new ResultIds(p.getAsString().toString());
if (pathIds.getResourceId() > -1) {
// ResultIds pathIds = new ResultIds(p.getAsString().toString());
LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
if (pathIds.isResource()) {
if (path == null || path.contains(p.getAsString())) {
this.addParameters(p.getAsString().toString(), attributes, registration);
this.addParameters(p.getAsString().toString(), attributes, registration, "attributes");
}
}
});
attrTelemetryObserveValue.getPostTelemetryProfile().forEach(p -> {
ResultIds pathIds = new ResultIds(p.getAsString().toString());
if (pathIds.getResourceId() > -1) {
// ResultIds pathIds = new ResultIds(p.getAsString().toString());
LwM2mPath pathIds = new LwM2mPath(p.getAsString().toString());
if (pathIds.isResource()) {
if (path == null || path.contains(p.getAsString())) {
this.addParameters(p.getAsString().toString(), telemetry, registration);
this.addParameters(p.getAsString().toString(), telemetry, registration, "telemetry");
}
}
});
@ -486,13 +487,19 @@ public class LwM2MTransportService {
* @param parameters - JsonObject attributes/telemetry
* @param registration - Registration LwM2M Client
*/
private void addParameters(String path, JsonObject parameters, Registration registration) {
private void addParameters(String path, JsonObject parameters, Registration registration, String nameParam) {
JsonObject names = lwM2mInMemorySecurityStore.getProfiles().get(lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getProfileUuid()).getPostKeyNameProfile();
String resName = String.valueOf(names.get(path));
if (resName != null && !resName.isEmpty()) {
String resValue = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path);
if (resValue != null) {
parameters.addProperty(resName, resValue);
String resValue = null;
try {
resValue = lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path);
if (resValue != null) {
// log.info("addParameters Path: [{}] ResValue : [{}] nameParam [{}]", path, lwM2mInMemorySecurityStore.getSessions().get(registration.getId()).getResourceValue(path), nameParam);
parameters.addProperty(resName, resValue);
}
} catch (Exception e) {
log.error(e.getStackTrace().toString());
}
}
}
@ -629,42 +636,73 @@ public class LwM2MTransportService {
* @param path - resource
*/
private void onObservationSetResourcesValue(Registration registration, Object value, Map<Integer, ?> values, String path) {
ResultIds resultIds = new ResultIds(path);
// #1
LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId());
ModelObject modelObject = lwM2MClient.getModelObjects().get(resultIds.getObjectId());
Map<Integer, LwM2mObjectInstance> instancesModelObject = modelObject.getInstances();
LwM2mObjectInstance instanceOld = (instancesModelObject.get(resultIds.instanceId) != null) ? instancesModelObject.get(resultIds.instanceId) : null;
Map<Integer, LwM2mResource> resourcesOld = (instanceOld != null) ? instanceOld.getResources() : null;
LwM2mResource resourceOld = (resourcesOld != null && resourcesOld.get(resultIds.getResourceId()) != null) ? resourcesOld.get(resultIds.getResourceId()) : null;
// #2
LwM2mResource resourceNew;
if (Objects.requireNonNull(resourceOld).isMultiInstances()) {
resourceNew = LwM2mMultipleResource.newResource(resultIds.getResourceId(), values, resourceOld.getType());
} else {
resourceNew = LwM2mSingleResource.newResource(resultIds.getResourceId(), value, resourceOld.getType());
}
//#3
Map<Integer, LwM2mResource> resourcesNew = new HashMap<>(resourcesOld);
// #4
resourcesNew.remove(resourceOld);
// #5
resourcesNew.put(resultIds.getResourceId(), resourceNew);
// #6
LwM2mObjectInstance instanceNew = new LwM2mObjectInstance(resultIds.instanceId, resourcesNew.values());
// #7
CountDownLatch respLatch = new CountDownLatch(1);
lwM2MClient.getModelObjects().get(resultIds.getObjectId()).removeInstance(resultIds.instanceId);
instancesModelObject.put(resultIds.instanceId, instanceNew);
respLatch.countDown();
try {
respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
ex.printStackTrace();
CountDownLatch respLatch = new CountDownLatch(1);
try {
// #1
LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId());
LwM2mPath resultIds = new LwM2mPath(path);
log.warn("#0 nameDevice: [{}] resultIds: [{}] value: [{}], values: [{}] ", lwM2MClient.getDeviceName(), resultIds, value, values);
ResourceModel.Type resType = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).type;
Map<Integer, LwM2mObjectInstance> instancesModelObject = (lwM2MClient.getModelObjects().get(resultIds.getObjectId()) != null) ? lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances() : null;
Map<Integer, LwM2mResource> resourcesOld = null;
try {
CountDownLatch respResLatch = new CountDownLatch(1);
try {
resourcesOld = (instancesModelObject != null &&
instancesModelObject.get(resultIds.getObjectInstanceId()) != null &&
instancesModelObject.get(resultIds.getObjectInstanceId()).getResources() != null) ? instancesModelObject.get(resultIds.getObjectInstanceId()).getResources() : null;
} finally {
respResLatch.countDown();
}
try {
respResLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
ex.printStackTrace();
log.error("#1_2 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value);
}
} catch (Exception e) {
e.printStackTrace();
log.error("#1_2_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value);
}
LwM2mResource resourceOld = (resourcesOld != null && resourcesOld.get(resultIds.getResourceId()) != null) ? resourcesOld.get(resultIds.getResourceId()) : null;
// #2
LwM2mResource resourceNew = null;
if ((resourceOld != null && resourceOld.isMultiInstances() && !resourceOld.getValues().equals(values)) ||
(resourceOld == null && value == null)) {
resourceNew = LwM2mMultipleResource.newResource(resultIds.getResourceId(), values, resType);
} else if ((resourceOld != null && !resourceOld.isMultiInstances() && !resourceOld.getValue().equals(values)) ||
(resourceOld == null && value != null)) {
resourceNew = LwM2mSingleResource.newResource(resultIds.getResourceId(), value, resType);
}
if (resourceNew != null) {
//#3
Map<Integer, LwM2mResource> resourcesNew = (resourcesOld == null) ? new HashMap<>() : new HashMap<>(resourcesOld);
// #4
if ((resourceOld != null)) resourcesNew.remove(resourceOld);
// #5
resourcesNew.put(resultIds.getResourceId(), resourceNew);
// #6
LwM2mObjectInstance instanceNew = new LwM2mObjectInstance(resultIds.getObjectInstanceId(), resourcesNew.values());
// #7
lwM2MClient.getModelObjects().get(resultIds.getObjectId()).removeInstance(resultIds.getObjectInstanceId());
instancesModelObject.put(resultIds.getObjectInstanceId(), instanceNew);
Set<String> paths = new HashSet<>();
paths.add(path);
this.updateAttrTelemetry(registration, false, paths);
}
} finally {
respLatch.countDown();
}
try {
respLatch.await(DEFAULT_TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
ex.printStackTrace();
log.error("#1_1 Update ResourcesValue after Observation in CountDownLatch is unsuccessfully path: [{}] value: [{}]", path, value);
}
} catch (Exception ignored) {
}
Set<String> paths = new HashSet<>();
paths.add(path);
this.updateAttrTelemetry(registration, false, paths);
}
/**
@ -696,7 +734,7 @@ public class LwM2MTransportService {
lwM2MTransportRequest.sendAllRequest(lwM2MClient.getLwServer(), lwM2MClient.getRegistration(), path, POST_TYPE_OPER_WRITE_REPLACE,
ContentFormat.TLV.getName(), lwM2MClient, null, value, this.context.getCtxServer().getTimeout(),
false);
log.info("[{}] path onAttributeUpdate", path);
// log.info("[{}] path onAttributeUpdate", path);
} else {
log.error(LOG_LW2M_ERROR + ": Resource path - [{}] value - [{}] is not Writable and cannot be updated", path, value);
String logMsg = String.format(LOG_LW2M_ERROR + ": attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", path, value);
@ -728,7 +766,7 @@ public class LwM2MTransportService {
/**
* @param profile -
* @param path -
* @param path -
* @return true if path isPresent in postAttributeProfile
*/
private boolean validatePathInAttrProfile(AttrTelemetryObserveValue profile, String path) {
@ -738,7 +776,7 @@ public class LwM2MTransportService {
/**
* @param profile -
* @param path -
* @param path -
* @return true if path isPresent in postAttributeProfile
*/
private boolean validatePathInTelemetryProfile(AttrTelemetryObserveValue profile, String path) {
@ -793,9 +831,10 @@ public class LwM2MTransportService {
* @param request -
*/
public void onAttributeUpdateOk(Registration registration, String path, WriteRequest request, boolean isDelayedUpdate) {
ResultIds resultIds = new ResultIds(path);
// ResultIds resultIds = new ResultIds(path);
LwM2mPath resultIds = new LwM2mPath(path);
LwM2MClient lwM2MClient = lwM2mInMemorySecurityStore.getLwM2MClient(registration.getId());
LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getInstanceId()).getResource(resultIds.getResourceId());
LwM2mResource resource = lwM2MClient.getModelObjects().get(resultIds.getObjectId()).getInstances().get(resultIds.getObjectInstanceId()).getResource(resultIds.getResourceId());
if (resource.isMultiInstances()) {
this.onObservationSetResourcesValue(registration, null, ((LwM2mSingleResource) request.getNode()).getValues(), path);
} else {
@ -996,9 +1035,10 @@ public class LwM2MTransportService {
*/
private void updateResourceValueObserve(LeshanServer lwServer, Registration registration, LwM2MClient lwM2MClient, Set<String> targets, String typeOper) {
targets.forEach(target -> {
ResultIds pathIds = new ResultIds(target);
if (pathIds.resourceId >= 0 && lwM2MClient.getModelObjects().get(pathIds.getObjectId())
.getInstances().get(pathIds.getInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) {
// ResultIds pathIds = new ResultIds(target);
LwM2mPath pathIds = new LwM2mPath(target);
if (pathIds.isResource() && lwM2MClient.getModelObjects().get(pathIds.getObjectId())
.getInstances().get(pathIds.getObjectInstanceId()).getResource(pathIds.getResourceId()).getValue() != null) {
if (GET_TYPE_OPER_READ.equals(typeOper)) {
lwM2MTransportRequest.sendAllRequest(lwServer, registration, target, typeOper,
ContentFormat.TLV.getName(), null, null, null, this.context.getCtxServer().getTimeout(),
@ -1024,9 +1064,10 @@ public class LwM2MTransportService {
private ResourceValue getResourceValue(LwM2MClient lwM2MClient, String path) {
ResourceValue resourceValue = null;
ResultIds pathIds = new ResultIds(path);
if (pathIds.getResourceId() > -1) {
LwM2mResource resource = lwM2MClient.getModelObjects().get(pathIds.getObjectId()).getInstances().get(pathIds.getInstanceId()).getResource(pathIds.getResourceId());
// ResultIds pathIds = new ResultIds(path);
LwM2mPath pathIds = new LwM2mPath(path);
if (pathIds.isResource()) {
LwM2mResource resource = lwM2MClient.getModelObjects().get(pathIds.getObjectId()).getInstances().get(pathIds.getObjectInstanceId()).getResource(pathIds.getResourceId());
if (resource.isMultiInstances()) {
if (resource.getValues().size() > 0) {
resourceValue = new ResourceValue();

View File

@ -44,7 +44,6 @@ public class LwM2mServerListener {
@Override
public void registered(Registration registration, Registration previousReg,
Collection<Observation> previousObsersations) {
service.onRegistered(lhServer, registration, previousObsersations);
}
@ -54,6 +53,7 @@ public class LwM2mServerListener {
@Override
public void updated(RegistrationUpdate update, Registration updatedRegistration,
Registration previousRegistration) {
log.info("updated");
service.updatedReg(lhServer, updatedRegistration);
}
@ -63,6 +63,7 @@ public class LwM2mServerListener {
@Override
public void unregistered(Registration registration, Collection<Observation> observations, boolean expired,
Registration newReg) {
log.info("unregistered");
service.unReg(registration, observations);
}
@ -71,11 +72,13 @@ public class LwM2mServerListener {
public final PresenceListener presenceListener = new PresenceListener() {
@Override
public void onSleeping(Registration registration) {
log.info("onSleeping");
service.onSleepingDev(registration);
}
@Override
public void onAwake(Registration registration) {
log.info("onAwake");
service.onAwakeDev(registration);
}
};
@ -92,8 +95,10 @@ public class LwM2mServerListener {
if (registration != null) {
try {
service.onObservationResponse(registration, observation.getPath().toString(), response);
} catch (java.lang.NullPointerException e) {
log.error(e.toString());
} catch (Exception e) {
e.printStackTrace();
log.error("onResponse");
}
}
}

View File

@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.leshan.core.model.ObjectModel;
import org.eclipse.leshan.core.model.ResourceModel;
import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import org.eclipse.leshan.core.node.LwM2mPath;
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.core.response.ReadResponse;
import org.eclipse.leshan.core.util.Hex;
@ -29,12 +30,11 @@ import org.eclipse.leshan.server.security.SecurityInfo;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
import org.thingsboard.server.transport.lwm2m.server.LwM2MTransportService;
import org.thingsboard.server.transport.lwm2m.server.ResultIds;
import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
import java.util.UUID;
import java.util.Map;
import java.util.Set;
import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@ -61,6 +61,7 @@ public class LwM2MClient implements Cloneable {
private Map<String, TransportProtos.TsKvProto> delayedRequests;
private Set<Integer> delayedRequestsId;
private Map<String, LwM2mResponse> responses;
private final LwM2mValueConverterImpl converter;
public Object clone() throws CloneNotSupportedException {
return super.clone();
@ -81,11 +82,13 @@ public class LwM2MClient implements Cloneable {
* Key <objectId>, response<Value -> instance -> resources: value...>
*/
this.responses = new ConcurrentHashMap<>();
this.converter = new LwM2mValueConverterImpl();
}
/**
* Fill with data -> Model client
* @param path -
* Fill with data -> Model client
*
* @param path -
* @param response -
*/
public void onSuccessHandler(String path, LwM2mResponse response) {
@ -99,9 +102,9 @@ public class LwM2MClient implements Cloneable {
private void initValue() {
this.responses.forEach((key, resp) -> {
ResultIds pathIds = new ResultIds(key);
if (pathIds.getObjectId() > -1) {
ObjectModel objectModel = ((Collection<ObjectModel>) this.lwServer.getModelProvider().getObjectModel(registration).getObjectModels()).stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0);
LwM2mPath pathIds = new LwM2mPath(key);
if (pathIds.isObject() || pathIds.isObjectInstance() || pathIds.isResource()) {
ObjectModel objectModel = this.lwServer.getModelProvider().getObjectModel(registration).getObjectModels().stream().filter(v -> v.id == pathIds.getObjectId()).collect(Collectors.toList()).get(0);
if (this.modelObjects.get(pathIds.getObjectId()) != null) {
this.modelObjects.get(pathIds.getObjectId()).getInstances().put(((ReadResponse) resp).getContent().getId(), (LwM2mObjectInstance) ((ReadResponse) resp).getContent());
} else {
@ -122,30 +125,39 @@ public class LwM2MClient implements Cloneable {
}
public ResourceModel.Operations getOperation(String path) {
ResultIds resultIds = new ResultIds(path);
return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).operations : ResourceModel.Operations.NONE;
LwM2mPath resultIds = new LwM2mPath(path);
return (this.getModelObjects().get(resultIds.getObjectId()) != null) ?
this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).operations :
ResourceModel.Operations.NONE;
}
public String getResourceName (String path) {
ResultIds resultIds = new ResultIds(path);
public String getResourceName(String path) {
LwM2mPath resultIds = new LwM2mPath(path);
return (this.getModelObjects().get(resultIds.getObjectId()) != null) ? this.getModelObjects().get(resultIds.getObjectId()).getObjectModel().resources.get(resultIds.getResourceId()).name : "";
}
/**
* @param path - path resource
* @return - value of Resource or null
*/public String getResourceValue(String path) {
*/
public String getResourceValue(String path) {
String resValue = null;
ResultIds pathIds = new ResultIds(path);
LwM2mPath pathIds = new LwM2mPath(path);
ModelObject modelObject = this.getModelObjects().get(pathIds.getObjectId());
if (modelObject != null && modelObject.getInstances().get(pathIds.getInstanceId()) != null) {
LwM2mObjectInstance instance = modelObject.getInstances().get(pathIds.getInstanceId());
if (modelObject != null && modelObject.getInstances().get(pathIds.getObjectInstanceId()) != null) {
LwM2mObjectInstance instance = modelObject.getInstances().get(pathIds.getObjectInstanceId());
if (instance.getResource(pathIds.getResourceId()) != null) {
resValue = instance.getResource(pathIds.getResourceId()).getType() == OPAQUE ?
Hex.encodeHexString((byte[]) instance.getResource(pathIds.getResourceId()).getValue()).toLowerCase() :
(instance.getResource(pathIds.getResourceId()).isMultiInstances()) ?
instance.getResource(pathIds.getResourceId()).getValues().toString() :
instance.getResource(pathIds.getResourceId()).getValue().toString();
try {
resValue = instance.getResource(pathIds.getResourceId()).getType() == OPAQUE ?
Hex.encodeHexString((byte[]) instance.getResource(pathIds.getResourceId()).getValue()).toLowerCase() :
(instance.getResource(pathIds.getResourceId()).isMultiInstances()) ?
instance.getResource(pathIds.getResourceId()).getValues().toString() :
// getValueTypeToString(instance.getResource(pathIds.getResourceId()).getValue(), instance.getResource(pathIds.getResourceId()).getType());
(String) converter.convertValue(instance.getResource(pathIds.getResourceId()).getValue(), instance.getResource(pathIds.getResourceId()).getType(), ResourceModel.Type.STRING, pathIds);
} catch (Exception e) {
log.warn("getResourceValue [{}]", e.getStackTrace().toString());
}
}
}
return resValue;

View File

@ -21,7 +21,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance;
import java.util.Map;
@Data
public class ModelObject {
public class ModelObject implements Cloneable {
/**
* model one on all instance
* for each instance only id resource with parameters of resources (observe, attr, telemetry)
@ -38,4 +38,8 @@ public class ModelObject {
LwM2mObjectInstance instance = this.instances.get(id);
return this.instances.remove(id, instance);
}
public ModelObject clone() throws CloneNotSupportedException {
return (ModelObject) super.clone();
}
}

View File

@ -177,10 +177,40 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
return doSaveDevice(device, null);
}
@CacheEvict(cacheNames = DEVICE_CACHE, key = "{#device.tenantId, #device.name}")
@Override
public Device saveDeviceWithCredentials(Device device, DeviceCredentials deviceCredentials) {
if (device.getId() == null) {
Device deviceWithName = this.findDeviceByTenantIdAndName(device.getTenantId(), device.getName());
device = deviceWithName == null ? device : deviceWithName.updateDevice(device);
}
Device savedDevice = this.saveDeviceWithoutCredentials(device);
deviceCredentials.setDeviceId(savedDevice.getId());
if (device.getId() == null) {
deviceCredentials = deviceCredentialsService.createDeviceCredentials(savedDevice.getTenantId(), deviceCredentials);
}
else {
deviceCredentials.setId(deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), savedDevice.getId()).getId());
deviceCredentials = deviceCredentialsService.updateDeviceCredentials(device.getTenantId(), deviceCredentials);
}
return savedDevice;
}
private Device doSaveDevice(Device device, String accessToken) {
Device savedDevice = this.saveDeviceWithoutCredentials(device);
if (device.getId() == null) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
}
return savedDevice;
}
private Device saveDeviceWithoutCredentials(Device device) {
log.trace("Executing saveDevice [{}]", device);
deviceValidator.validate(device, Device::getTenantId);
Device savedDevice;
try {
DeviceProfile deviceProfile;
if (device.getDeviceProfileId() == null) {
@ -198,8 +228,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
}
device.setType(deviceProfile.getName());
device.setDeviceData(syncDeviceData(deviceProfile, device.getDeviceData()));
savedDevice = deviceDao.save(device.getTenantId(), device);
return deviceDao.save(device.getTenantId(), device);
} catch (Exception t) {
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("device_name_unq_key")) {
@ -208,14 +237,6 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
throw t;
}
}
if (device.getId() == null) {
DeviceCredentials deviceCredentials = new DeviceCredentials();
deviceCredentials.setDeviceId(new DeviceId(savedDevice.getUuidId()));
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId(!StringUtils.isEmpty(accessToken) ? accessToken : RandomStringUtils.randomAlphanumeric(20));
deviceCredentialsService.createDeviceCredentials(device.getTenantId(), deviceCredentials);
}
return savedDevice;
}
private DeviceData syncDeviceData(DeviceProfile deviceProfile, DeviceData deviceData) {
@ -530,7 +551,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
@Override
protected void validateCreate(TenantId tenantId, Device device) {
DefaultTenantProfileConfiguration profileConfiguration =
(DefaultTenantProfileConfiguration)tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
(DefaultTenantProfileConfiguration) tenantProfileCache.get(tenantId).getProfileData().getConfiguration();
long maxDevices = profileConfiguration.getMaxDevices();
validateNumberOfEntitiesPerTenant(tenantId, deviceDao, maxDevices, EntityType.DEVICE);
}

View File

@ -121,6 +121,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@ -1091,6 +1092,24 @@ public class RestClient implements ClientHttpRequestInterceptor, Closeable {
return restTemplate.postForEntity(baseURL + "/api/device/credentials", deviceCredentials, DeviceCredentials.class).getBody();
}
public Optional<Device> saveDeviceWithCredentials(Device device, DeviceCredentials credentials) {
try {
Map<Class<?>, Object> deviceCredentials = new ConcurrentHashMap<>();
deviceCredentials.put(Device.class, device);
deviceCredentials.put(DeviceCredentials.class, credentials);
// return restTemplate.postForEntity(baseURL + "/api/lwm2m/device-credentials", deviceCredentials, Device.class).getBody();
ResponseEntity<Device> deviceOpt = restTemplate.postForEntity(baseURL + "/api/lwm2m/device-credentials", deviceCredentials, Device.class);
return Optional.ofNullable(deviceOpt.getBody());
} catch (HttpClientErrorException exception) {
if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
return Optional.empty();
} else {
throw exception;
}
}
}
public PageData<Device> getTenantDevices(String type, PageLink pageLink) {
Map<String, String> params = new HashMap<>();
params.put("type", type);