diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java index 694d7b96c7..f9b3f93854 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mOperationType.java @@ -15,56 +15,57 @@ */ package org.thingsboard.server.transport.lwm2m.server; +import lombok.Getter; + /** * Define the behavior of a write request. */ public enum LwM2mOperationType { - /** - * GET - */ - READ(0, "Read"), - DISCOVER(1, "Discover"), - DISCOVER_ALL(2, "DiscoverAll"), - OBSERVE_READ_ALL(3, "ObserveReadAll"), - /** - * POST - */ - OBSERVE(4, "Observe"), - OBSERVE_CANCEL(5, "ObserveCancel"), - OBSERVE_CANCEL_ALL(6, "ObserveCancelAll"), - EXECUTE(7, "Execute"), + + READ(0, "Read", true), + DISCOVER(1, "Discover", true), + DISCOVER_ALL(2, "DiscoverAll", false), + OBSERVE_READ_ALL(3, "ObserveReadAll", false), + + OBSERVE(4, "Observe", true), + OBSERVE_CANCEL(5, "ObserveCancel", true), + OBSERVE_CANCEL_ALL(6, "ObserveCancelAll", false), + EXECUTE(7, "Execute", true), /** * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see * section 5.3.3 of the LW M2M spec). * if all resources are to be replaced */ - WRITE_REPLACE(8, "WriteReplace"), - /* - PUT - */ + WRITE_REPLACE(8, "WriteReplace", true), + /** * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section * 5.3.3 of the LW M2M spec). * if this is a partial update request */ - WRITE_UPDATE(9, "WriteUpdate"), - WRITE_ATTRIBUTES(10, "WriteAttributes"), - DELETE(11, "Delete"), + WRITE_UPDATE(9, "WriteUpdate", true), + WRITE_ATTRIBUTES(10, "WriteAttributes", true), + DELETE(11, "Delete", true), // only for RPC - FW_UPDATE(12, "FirmwareUpdate"); -// FW_READ_INFO(12, "FirmwareReadInfo"), + FW_UPDATE(12, "FirmwareUpdate", false); +// FW_READ_INFO(12, "FirmwareReadInfo"), // SW_READ_INFO(15, "SoftwareReadInfo"), // SW_UPDATE(16, "SoftwareUpdate"), // SW_UNINSTALL(18, "SoftwareUninstall"); - public int code; - public String type; + @Getter + private final int code; + @Getter + private final String type; + @Getter + private final boolean hasObjectId; - LwM2mOperationType(int code, String type) { + LwM2mOperationType(int code, String type, boolean hasObjectId) { this.code = code; this.type = type; + this.hasObjectId = hasObjectId; } public static LwM2mOperationType fromType(String type) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java index 1ce9358078..77454d8b83 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClient.java @@ -26,6 +26,7 @@ import org.eclipse.leshan.core.node.LwM2mObjectInstance; import org.eclipse.leshan.core.node.LwM2mPath; import org.eclipse.leshan.core.node.LwM2mResource; import org.eclipse.leshan.core.node.LwM2mSingleResource; +import org.eclipse.leshan.core.node.codec.LwM2mValueConverter; import org.eclipse.leshan.core.request.ContentFormat; import org.eclipse.leshan.server.model.LwM2mModelProvider; import org.eclipse.leshan.server.registration.Registration; @@ -40,7 +41,6 @@ import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; import java.util.Collection; import java.util.List; @@ -257,7 +257,7 @@ public class LwM2mClient implements Cloneable { .getObjectModel(pathIds.getObjectId()) : null; } - public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) { + public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverter converter, String pathIdVer) { StringBuilder builder = new StringBuilder(); builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={"); lwM2mObject.getInstances().forEach((instId, inst) -> { @@ -271,7 +271,7 @@ public class LwM2mClient implements Cloneable { return builder.toString(); } - public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) { + public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverter converter, String pathIdVer) { StringBuilder builder = new StringBuilder(); builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={"); objectInstance.getResources().forEach((resId, res) -> { @@ -285,19 +285,12 @@ public class LwM2mClient implements Cloneable { return builder.toString(); } - public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) { - if (!OPAQUE.equals(lwM2mResource.getType())) { - return lwM2mResource.isMultiInstances() ? ((LwM2mMultipleResource) lwM2mResource).toString() : - ((LwM2mSingleResource) lwM2mResource).toString(); - } else { - return String.format("LwM2mSingleResource [id=%s, value=%s, type=%s]", lwM2mResource.getId(), - converter.convertValue(lwM2mResource.getValue(), - OPAQUE, STRING, new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))), lwM2mResource.getType().name()); - } + public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverter converter, String pathIdVer) { + return lwM2mResource.getValue().toString(); } public Collection getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, - LwM2mValueConverterImpl converter) { + LwM2mValueConverter converter) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) @@ -313,7 +306,7 @@ public class LwM2mClient implements Cloneable { } public Collection getNewResourcesForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, - LwM2mValueConverterImpl converter) { + LwM2mValueConverter converter) { LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); Collection resources = ConcurrentHashMap.newKeySet(); Map resourceModels = modelProvider.getObjectModel(registration) @@ -395,7 +388,7 @@ public class LwM2mClient implements Cloneable { } } - public LwM2mFwSwUpdate getFwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { + public LwM2mFwSwUpdate getFwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { if (this.fwUpdate == null) { var profile = clientContext.getProfile(this.getProfileId()); this.fwUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy()); @@ -403,7 +396,7 @@ public class LwM2mClient implements Cloneable { return this.fwUpdate; } - public LwM2mFwSwUpdate getSwUpdate (LwM2mUplinkMsgHandler handler,LwM2mClientContext clientContext) { + public LwM2mFwSwUpdate getSwUpdate(LwM2mUplinkMsgHandler handler, LwM2mClientContext clientContext) { if (this.swUpdate == null) { var profile = clientContext.getProfile(this.getProfileId()); this.swUpdate = new LwM2mFwSwUpdate(handler, this, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy()); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java index d4efda61ab..6896c3d8de 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java @@ -33,7 +33,7 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import java.util.ArrayList; @@ -201,14 +201,14 @@ public class LwM2mFwSwUpdate { byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(firmwareChunk).timeout(handler.config.getTimeout()).build(); - request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteReplaceCallback(handler, lwM2MClient, targetIdVer)); + request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { String apiFont = "coap://176.36.143.9:5685"; String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); log.warn("89) coapUri: [{}]", uri); //TODO: user this.rpcRequest??? TbLwM2MWriteReplaceRequest downlink = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(uri).timeout(handler.config.getTimeout()).build(); - request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteReplaceCallback(handler, lwM2MClient, targetIdVer)); + request.sendWriteReplaceRequest(lwM2MClient, downlink, new TbLwM2MWriteResponseCallback(handler, lwM2MClient, targetIdVer)); } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java index 141fa61dac..837e2a17bb 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/DefaultLwM2mDownlinkMsgHandler.java @@ -47,6 +47,7 @@ import org.eclipse.leshan.core.util.Hex; import org.eclipse.leshan.core.util.NamedThreadFactory; import org.eclipse.leshan.server.registration.Registration; import org.springframework.stereotype.Service; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; @@ -98,30 +99,30 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { @Override public void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback callback) { - if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { - ReadRequest downlink = new ReadRequest(getContentFormat(client, request), request.getObjectId()); - sendRequest(client, downlink, request.getTimeout(), callback); - } + validateVersionedId(client, request); + ReadRequest downlink = new ReadRequest(getContentFormat(client, request), request.getObjectId()); + sendRequest(client, downlink, request.getTimeout(), callback); } @Override public void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback callback) { - if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { - LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); - Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); - if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { - ObserveRequest downlink; - ContentFormat contentFormat = getContentFormat(client, request); - if (resultIds.isResource()) { - downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); - } else if (resultIds.isObjectInstance()) { - downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); - } else { - downlink = new ObserveRequest(contentFormat, resultIds.getObjectId()); - } - log.info("[{}] Send observation: {}.", client.getEndpoint(), request.getVersionedId()); - sendRequest(client, downlink, request.getTimeout(), callback); + validateVersionedId(client, request); + LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); + Set observations = context.getServer().getObservationService().getObservations(client.getRegistration()); + if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { + ObserveRequest downlink; + ContentFormat contentFormat = getContentFormat(client, request); + if (resultIds.isResource()) { + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); + } else if (resultIds.isObjectInstance()) { + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); + } else { + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId()); } + log.info("[{}] Send observation: {}.", client.getEndpoint(), request.getVersionedId()); + sendRequest(client, downlink, request.getTimeout(), callback); + } else { + throw new IllegalArgumentException("Observation is already registered!"); } } @@ -133,10 +134,8 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { } @Override - public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback) { - Link[] objectLinks = client.getRegistration().getSortedObjectLinks(); - Set paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); - callback.onSuccess(paths); + public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback) { + callback.onSuccess(Arrays.asList(client.getRegistration().getSortedObjectLinks())); } @Override @@ -172,27 +171,28 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { @Override public void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback) { - if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId())) { - sendRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback); - } + validateVersionedId(client, request); + sendRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback); } @Override public void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback) { - if (request.getObjectId() != null && client.isValidObjectVersion(request.getVersionedId()) && request.getAttributes() != null) { - ObjectAttributes params = request.getAttributes(); - List attributes = new LinkedList<>(); + validateVersionedId(client, request); + if (request.getAttributes() == null) { + throw new IllegalArgumentException("Attributes to write are not specified!"); + } + ObjectAttributes params = request.getAttributes(); + List attributes = new LinkedList<>(); // Dimension and Object version are read only attributes. // addAttribute(attributes, DIMENSION, params.getDim(), dim -> dim >= 0 && dim <= 255); // addAttribute(attributes, OBJECT_VERSION, params.getVer(), StringUtils::isNotEmpty, Function.identity()); - addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax()); - addAttribute(attributes, MINIMUM_PERIOD, params.getPmin()); - addAttribute(attributes, GREATER_THAN, params.getGt()); - addAttribute(attributes, LESSER_THAN, params.getLt()); - addAttribute(attributes, STEP, params.getSt()); - AttributeSet attributeSet = new AttributeSet(attributes); - sendRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback); - } + addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax()); + addAttribute(attributes, MINIMUM_PERIOD, params.getPmin()); + addAttribute(attributes, GREATER_THAN, params.getGt()); + addAttribute(attributes, LESSER_THAN, params.getLt()); + addAttribute(attributes, STEP, params.getSt()); + AttributeSet attributeSet = new AttributeSet(attributes); + sendRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback); } @Override @@ -642,7 +642,14 @@ public class DefaultLwM2mDownlinkMsgHandler implements LwM2mDownlinkMsgHandler { // handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); // } // } - + private void validateVersionedId(LwM2mClient client, HasVersionedId request) { + if (!client.isValidObjectVersion(request.getVersionedId())) { + throw new IllegalArgumentException("Specified resource id is not configured in the device profile!"); + } + if (request.getObjectId() == null) { + throw new IllegalArgumentException("Specified object id is null!"); + } + } private static void addAttribute(List attributes, String attributeName, T value) { addAttribute(attributes, attributeName, value, null, null); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java index 473205eaa1..724dc78993 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/LwM2mDownlinkMsgHandler.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.transport.lwm2m.server.downlink; +import org.eclipse.leshan.core.Link; import org.eclipse.leshan.core.response.DeleteResponse; import org.eclipse.leshan.core.response.DiscoverResponse; import org.eclipse.leshan.core.response.ExecuteResponse; @@ -24,6 +25,7 @@ import org.eclipse.leshan.core.response.WriteAttributesResponse; import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import java.util.List; import java.util.Set; public interface LwM2mDownlinkMsgHandler { @@ -44,7 +46,7 @@ public interface LwM2mDownlinkMsgHandler { void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback callback); - void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback); + void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback> callback); void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback callback); diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java similarity index 84% rename from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java rename to common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java index c8ef5622f2..2cee4887c9 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteReplaceCallback.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/downlink/TbLwM2MWriteResponseCallback.java @@ -19,11 +19,11 @@ import org.eclipse.leshan.core.response.WriteResponse; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; -public class TbLwM2MWriteReplaceCallback extends AbstractTbLwM2MRequestCallback { +public class TbLwM2MWriteResponseCallback extends AbstractTbLwM2MRequestCallback { private final String targetId; - public TbLwM2MWriteReplaceCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { + public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2mClient client, String targetId) { super(handler, client); this.targetId = targetId; } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java index 4da5409712..0c371dac4e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/DefaultLwM2MRpcRequestHandler.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -29,8 +29,27 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllObserveCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import java.util.Map; @@ -57,62 +76,167 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { private final LwM2mDownlinkMsgHandler downlinkHandler; private final Map rpcSubscriptions = new ConcurrentHashMap<>(); - @Override public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequst, TransportProtos.SessionInfoProto sessionInfo) { this.cleanupOldSessions(); UUID requestUUID = new UUID(rpcRequst.getRequestIdMSB(), rpcRequst.getRequestIdLSB()); log.warn("Received params: {}", rpcRequst.getParams()); - // TODO: We use this map to protect from browser issue that the same command is sent twice. This is probably not the best place and should be moved to DeviceActor + // We use this map to protect from browser issue that the same command is sent twice. + // TODO: This is probably not the best place and should be moved to DeviceActor if (!this.rpcSubscriptions.containsKey(requestUUID)) { LwM2mOperationType operationType = LwM2mOperationType.fromType(rpcRequst.getMethodName()); if (operationType == null) { this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.METHOD_NOT_ALLOWED.getName(), "Unsupported operation type: " + rpcRequst.getMethodName()); + return; } LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); if (client.getRegistration() == null) { this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.INTERNAL_SERVER_ERROR.getName(), "Registration is empty"); + return; } - switch (operationType) { - case READ: - sendReadRequest(client, rpcRequst); - break; + try { + if (operationType.isHasObjectId()) { + String objectId = getIdFromParameters(client, rpcRequst); + switch (operationType) { + case READ: + sendReadRequest(client, rpcRequst, objectId); + break; + case OBSERVE: + sendObserveRequest(client, rpcRequst, objectId); + break; + case DISCOVER: + sendDiscoverRequest(client, rpcRequst, objectId); + break; + case EXECUTE: + sendExecuteRequest(client, rpcRequst, objectId); + break; + case WRITE_ATTRIBUTES: + sendWriteAttributesRequest(client, rpcRequst, objectId); + break; + case OBSERVE_CANCEL: + sendCancelObserveRequest(client, rpcRequst, objectId); + break; + case DELETE: + sendDeleteRequest(client, rpcRequst, objectId); + break; + case WRITE_UPDATE: + sendWriteUpdateRequest(client, rpcRequst, objectId); + break; + case WRITE_REPLACE: + sendWriteReplaceRequest(client, rpcRequst, objectId); + break; + default: + throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); + } + } else { + switch (operationType) { + case OBSERVE_CANCEL_ALL: + sendCancelAllObserveRequest(client, rpcRequst); + break; + case OBSERVE_READ_ALL: + sendObserveAllRequest(client, rpcRequst); + break; + case DISCOVER_ALL: + sendDiscoverAllRequest(client, rpcRequst); + break; + case FW_UPDATE: + //TODO: implement and add break statement + default: + throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); + } + } + } catch (IllegalArgumentException e) { + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.BAD_REQUEST.getName(), e.getMessage()); } + } + } + private void sendReadRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MReadCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback); + downlinkHandler.sendReadRequest(client, request, rpcCallback); + } -// log.warn("4) rpcRequst: [{}], sessionUUID: [{}]", rpcRequst, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); -// String bodyParams = StringUtils.trimToNull(rpcRequst.getParams()) != null ? rpcRequst.getParams() : "null"; -// LwM2mOperationType lwM2mTypeOper = setValidTypeOper(rpcRequst.getMethodName()); -// this.rpcSubscriptions.put(requestUUID, rpcRequst.getExpirationTime()); -// LwM2mClientRpcRequest lwm2mClientRpcRequest = null; -// try { -// LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); -// Registration registration = client.getRegistration(); -// if (registration != null) { -// lwm2mClientRpcRequest = new LwM2mClientRpcRequest(lwM2mTypeOper, bodyParams, rpcRequst.getRequestId(), sessionInfo, registration, uplinkHandler); -// if (lwm2mClientRpcRequest.getErrorMsg() != null) { -// lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); -// this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); -// } else { -// //TODO: use different methods and RPC callback wrapper. -//// defaultLwM2MDownlinkMsgHandler.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), -//// null, -//// lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), -//// this.config.getTimeout(), lwm2mClientRpcRequest); -// } -// } else { -// this.sendErrorRpcResponse(lwm2mClientRpcRequest, "registration == null", sessionInfo); -// } -// } catch (Exception e) { -// this.sendErrorRpcResponse(lwm2mClientRpcRequest, e.getMessage(), sessionInfo); -// } - } + private void sendObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MObserveCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback); + downlinkHandler.sendObserveRequest(client, request, rpcCallback); + } + + private void sendObserveAllRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { + TbLwM2MObserveAllRequest request = TbLwM2MObserveAllRequest.builder().timeout(this.config.getTimeout()).build(); + downlinkHandler.sendObserveAllRequest(client, request, new RpcLinkSetCallback(transportService, client, requestMsg, null)); + } + + private void sendDiscoverAllRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { + TbLwM2MDiscoverAllRequest request = TbLwM2MDiscoverAllRequest.builder().timeout(this.config.getTimeout()).build(); + downlinkHandler.sendDiscoverAllRequest(client, request, new RpcLinkSetCallback(transportService, client, requestMsg, null)); + } + + private void sendDiscoverRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MDiscoverRequest request = TbLwM2MDiscoverRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MDiscoverCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcDiscoverCallback(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendDiscoverRequest(client, request, rpcCallback); + } + + private void sendExecuteRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MExecuteCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendExecuteRequest(client, downlink, rpcCallback); + } + + private void sendWriteAttributesRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + RpcWriteAttributesRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteAttributesRequest.class); + TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(versionedId) + .attributes(requestBody.getAttributes()) + .timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MWriteAttributesCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendWriteAttributesRequest(client, request, rpcCallback); + } + + private void sendWriteUpdateRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + RpcWriteUpdateRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteUpdateRequest.class); + TbLwM2MWriteUpdateRequest.TbLwM2MWriteUpdateRequestBuilder builder = TbLwM2MWriteUpdateRequest.builder().versionedId(versionedId); + builder.value(requestBody.getValue()).timeout(this.config.getTimeout()); + var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendWriteUpdateRequest(client, builder.build(), rpcCallback); + } + + private void sendWriteReplaceRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + RpcWriteUpdateRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteUpdateRequest.class); + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) + .value(requestBody.getValue()) + .timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendWriteReplaceRequest(client, request, rpcCallback); + } + + private void sendCancelObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MCancelObserveRequest downlink = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MCancelObserveCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcCancelObserveCallback(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendCancelObserveRequest(client, downlink, rpcCallback); + } + + private void sendDeleteRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { + TbLwM2MDeleteRequest downlink = TbLwM2MDeleteRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MDeleteCallback(uplinkHandler, client, versionedId); + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendDeleteRequest(client, downlink, rpcCallback); } - private void sendReadRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequst) { - String id = getIdFromParameters(client, rpcRequst); - TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(id).timeout(this.config.getTimeout()).build(); - downlinkHandler.sendReadRequest(client, request, new TbLwM2MReadCallback(uplinkHandler, client, id)); + private void sendCancelAllObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { + TbLwM2MCancelAllRequest downlink = TbLwM2MCancelAllRequest.builder().timeout(this.config.getTimeout()).build(); + var mainCallback = new TbLwM2MCancelAllObserveCallback(uplinkHandler, client); + var rpcCallback = new RpcCancelObserveCallback(transportService, client, requestMsg, mainCallback); + downlinkHandler.sendCancelAllRequest(client, downlink, rpcCallback); } private String getIdFromParameters(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequst) { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcResponseBody.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcResponseBody.java new file mode 100644 index 0000000000..16b743101b --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/LwM2MRpcResponseBody.java @@ -0,0 +1,32 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +@JsonInclude(JsonInclude.Include.NON_NULL) +public class LwM2MRpcResponseBody { + + private String result; + private String value; + private String error; + private String info; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java new file mode 100644 index 0000000000..8fafa294ee --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcCancelObserveCallback.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public class RpcCancelObserveCallback extends RpcDownlinkRequestCallbackProxy { + + public RpcCancelObserveCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + @Override + protected void sendRpcReplyOnSuccess(Integer response) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.CONTENT.getName()).value(response.toString()).build()); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java new file mode 100644 index 0000000000..54c621d8f9 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDiscoverCallback.java @@ -0,0 +1,43 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.eclipse.leshan.core.response.ReadResponse; +import org.jetbrains.annotations.NotNull; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public class RpcDiscoverCallback extends RpcLwM2MDownlinkCallback { + + public RpcDiscoverCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + protected Optional serializeSuccessfulResponse(DiscoverResponse response) { + return Optional.of(Link.serialize(response.getObjectLinks())); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java new file mode 100644 index 0000000000..050e265b34 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcDownlinkRequestCallbackProxy.java @@ -0,0 +1,86 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.node.codec.LwM2mValueConverter; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; + +public abstract class RpcDownlinkRequestCallbackProxy implements DownlinkRequestCallback { + + private final TransportService transportService; + private final TransportProtos.ToDeviceRpcRequestMsg request; + private final DownlinkRequestCallback callback; + + protected final LwM2mClient client; + protected final LwM2mValueConverter converter; + + public RpcDownlinkRequestCallbackProxy(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + this.transportService = transportService; + this.client = client; + this.request = requestMsg; + this.callback = callback; + this.converter = LwM2mValueConverterImpl.getInstance(); + } + + @Override + public void onSuccess(T response) { + sendRpcReplyOnSuccess(response); + if (callback != null) { + callback.onSuccess(response); + } + } + + @Override + public void onValidationError(String msg) { + sendRpcReplyOnValidationError(msg); + if (callback != null) { + callback.onValidationError(msg); + } + } + + @Override + public void onError(Exception e) { + sendRpcReplyOnError(e); + if (callback != null) { + callback.onError(e); + } + } + + protected void reply(LwM2MRpcResponseBody response) { + TransportProtos.ToDeviceRpcResponseMsg msg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() + .setPayload(JacksonUtil.toString(response)) + .setRequestId(request.getRequestId()) + .build(); + transportService.process(client.getSession(), msg, null); + } + + abstract protected void sendRpcReplyOnSuccess(T response); + + protected void sendRpcReplyOnValidationError(String msg) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.BAD_REQUEST.getName()).error(msg).build()); + } + + protected void sendRpcReplyOnError(Exception e) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.INTERNAL_SERVER_ERROR.getName()).error(e.getMessage()).build()); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java new file mode 100644 index 0000000000..a5f075b258 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcEmptyResponseCallback.java @@ -0,0 +1,38 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.eclipse.leshan.core.response.LwM2mResponse; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public class RpcEmptyResponseCallback extends RpcLwM2MDownlinkCallback { + + public RpcEmptyResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + protected Optional serializeSuccessfulResponse(T response) { + return Optional.empty(); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java new file mode 100644 index 0000000000..bcb5b2ead5 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLinkSetCallback.java @@ -0,0 +1,41 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.Link; +import org.eclipse.leshan.core.ResponseCode; +import org.eclipse.leshan.core.response.DiscoverResponse; +import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; +import java.util.Set; + +public class RpcLinkSetCallback extends RpcDownlinkRequestCallbackProxy { + + public RpcLinkSetCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + @Override + protected void sendRpcReplyOnSuccess(T response) { + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.CONTENT.getName()).value(JacksonUtil.toString(response)).build()); + } + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java new file mode 100644 index 0000000000..c8d32ef184 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcLwM2MDownlinkCallback.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.response.LwM2mResponse; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public abstract class RpcLwM2MDownlinkCallback extends RpcDownlinkRequestCallbackProxy { + + public RpcLwM2MDownlinkCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + } + + @Override + protected void sendRpcReplyOnSuccess(T response) { + LwM2MRpcResponseBody.LwM2MRpcResponseBodyBuilder builder = LwM2MRpcResponseBody.builder().result(response.getCode().getName()); + if (response.isSuccess()) { + Optional responseValue = serializeSuccessfulResponse(response); + if (responseValue.isPresent() && StringUtils.isNotEmpty(responseValue.get())) { + builder.value(responseValue.get()); + } + } else { + if (StringUtils.isNotEmpty(response.getErrorMessage())) { + builder.error(response.getErrorMessage()); + } + } + reply(builder.build()); + } + + protected abstract Optional serializeSuccessfulResponse(T response); +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java new file mode 100644 index 0000000000..6a4ed06d6b --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcReadResponseCallback.java @@ -0,0 +1,50 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import org.eclipse.leshan.core.node.LwM2mObject; +import org.eclipse.leshan.core.node.LwM2mObjectInstance; +import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.response.ReadResponse; +import org.thingsboard.server.common.transport.TransportService; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; + +import java.util.Optional; + +public class RpcReadResponseCallback extends RpcLwM2MDownlinkCallback { + + private final String versionedId; + + public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId, DownlinkRequestCallback callback) { + super(transportService, client, requestMsg, callback); + this.versionedId = versionedId; + } + + @Override + protected Optional serializeSuccessfulResponse(T response) { + Object value = null; + if (response.getContent() instanceof LwM2mObject) { + value = client.objectToString((LwM2mObject) response.getContent(), this.converter, versionedId); + } else if (response.getContent() instanceof LwM2mObjectInstance) { + value = client.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, versionedId); + } else if (response.getContent() instanceof LwM2mResource) { + value = client.resourceToString((LwM2mResource) response.getContent(), this.converter, versionedId); + } + return Optional.of(String.format("%s", value)); + } +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteAttributesRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteAttributesRequest.java new file mode 100644 index 0000000000..67642078c6 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteAttributesRequest.java @@ -0,0 +1,28 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; + +@Data +@EqualsAndHashCode(callSuper = true) +public class RpcWriteAttributesRequest extends IdOrKeyRequest { + + private ObjectAttributes attributes; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteReplaceRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteReplaceRequest.java new file mode 100644 index 0000000000..55fb2b49aa --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteReplaceRequest.java @@ -0,0 +1,27 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class RpcWriteReplaceRequest extends IdOrKeyRequest { + + private Object value; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteUpdateRequest.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteUpdateRequest.java new file mode 100644 index 0000000000..b56fea7eb6 --- /dev/null +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/RpcWriteUpdateRequest.java @@ -0,0 +1,29 @@ +/** + * Copyright © 2016-2021 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.rpc; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; + +@Data +@EqualsAndHashCode(callSuper = true) +public class RpcWriteUpdateRequest extends IdOrKeyRequest { + + private Object value; + private String contentFormat; + +} diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java index 084af742ca..5187c4c493 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2MUplinkMsgHandler.java @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -80,7 +80,7 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallbac import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; -import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceCallback; +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2mClientRpcRequest; @@ -115,7 +115,6 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN; -import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.READ; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; @@ -333,29 +332,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { this.updateResourcesValue(registration, lwM2mResource, path); } } - if (rpcRequest != null) { - //TODO: move this to separate callback. - this.sendRpcRequestAfterReadResponse(registration, lwM2MClient, path, response, rpcRequest); - } - } - } - - private void sendRpcRequestAfterReadResponse(Registration registration, LwM2mClient lwM2MClient, String pathIdVer, ReadResponse response, - LwM2mClientRpcRequest rpcRequest) { - Object value = null; - if (response.getContent() instanceof LwM2mObject) { - value = lwM2MClient.objectToString((LwM2mObject) response.getContent(), this.converter, pathIdVer); - } else if (response.getContent() instanceof LwM2mObjectInstance) { - value = lwM2MClient.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, pathIdVer); - } else if (response.getContent() instanceof LwM2mResource) { - value = lwM2MClient.resourceToString((LwM2mResource) response.getContent(), this.converter, pathIdVer); } - String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO, - READ, pathIdVer, value); - this.sendLogsToThingsboard(lwM2MClient, msg); - rpcRequest.setValueMsg(String.format("%s", value)); - //TODO: refactor -// this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); } /** @@ -964,7 +941,7 @@ public class DefaultLwM2MUplinkMsgHandler implements LwM2mUplinkMsgHandler { private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); - defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteReplaceCallback(this, lwM2MClient, versionedId)); + defaultLwM2MDownlinkMsgHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(this, lwM2MClient, versionedId)); } else { log.error("Failed update resource [{}] [{}]", versionedId, newValue); String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad",