Commit 023915c2e1051a5d36be3920273ab2d67ce4dde8

Authored by Andrii Shvaika
2 parents afeca4cc 7f38366a

Read Composite support

Showing 27 changed files with 730 additions and 83 deletions
... ... @@ -27,5 +27,6 @@ public class OtherConfiguration {
27 27 private PowerMode powerMode;
28 28 private String fwUpdateResource;
29 29 private String swUpdateResource;
  30 + private boolean composite;
30 31
31 32 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.config;
  17 +
  18 +import lombok.Getter;
  19 +import org.eclipse.leshan.core.LwM2m.Version;
  20 +import org.eclipse.leshan.core.request.ContentFormat;
  21 +
  22 +public enum LwM2mVersion {
  23 + VERSION_1_0(0, Version.V1_0, ContentFormat.TLV),
  24 + VERSION_1_1(1, Version.V1_1, ContentFormat.TEXT);
  25 +
  26 + @Getter
  27 + private final int code;
  28 + @Getter
  29 + private final Version version;
  30 + @Getter
  31 + private final ContentFormat contentFormat;
  32 +
  33 + LwM2mVersion(int code, Version version, ContentFormat contentFormat) {
  34 + this.code = code;
  35 + this.version = version;
  36 + this.contentFormat = contentFormat;
  37 + }
  38 +
  39 + public static LwM2mVersion fromVersion(Version version) {
  40 + for (LwM2mVersion to : LwM2mVersion.values()) {
  41 + if (to.version.equals(version)) {
  42 + return to;
  43 + }
  44 + }
  45 + throw new IllegalArgumentException(String.format("Unsupported typeLwM2mVersion type : %s", version));
  46 + }
  47 +
  48 + public static LwM2mVersion fromVersionStr(String versionStr) {
  49 + for (LwM2mVersion to : LwM2mVersion.values()) {
  50 + if (to.version.toString().equals(versionStr)) {
  51 + return to;
  52 + }
  53 + }
  54 + throw new IllegalArgumentException(String.format("Unsupported contentFormatLwM2mVersion version : %s", versionStr));
  55 + }
  56 +
  57 + public static LwM2mVersion fromCode(int code) {
  58 + for (LwM2mVersion to : LwM2mVersion.values()) {
  59 + if (to.code == code) {
  60 + return to;
  61 + }
  62 + }
  63 + throw new IllegalArgumentException(String.format("Unsupported codeLwM2mVersion code : %d", code));
  64 + }
  65 +}
  66 +
... ...
... ... @@ -23,37 +23,41 @@ import lombok.Getter;
23 23 public enum LwM2mOperationType {
24 24
25 25 READ(0, "Read", true),
26   - DISCOVER(1, "Discover", true),
27   - DISCOVER_ALL(2, "DiscoverAll", false),
28   - OBSERVE_READ_ALL(3, "ObserveReadAll", false),
  26 + READ_COMPOSITE(1, "ReadComposite", false, true),
  27 + DISCOVER(2, "Discover", true),
  28 + DISCOVER_ALL(3, "DiscoverAll", false),
  29 + OBSERVE_READ_ALL(4, "ObserveReadAll", false),
29 30
30   - OBSERVE(4, "Observe", true),
31   - OBSERVE_CANCEL(5, "ObserveCancel", true),
32   - OBSERVE_CANCEL_ALL(6, "ObserveCancelAll", false),
33   - EXECUTE(7, "Execute", true),
  31 + OBSERVE(5, "Observe", true),
  32 + OBSERVE_COMPOSITE(6, "ObserveComposite", false, true),
  33 + OBSERVE_CANCEL(7, "ObserveCancel", true),
  34 + OBSERVE_COMPOSITE_CANCEL(8, "ObserveCompositeCancel", false, true),
  35 + OBSERVE_CANCEL_ALL(9, "ObserveCancelAll", false),
  36 + EXECUTE(10, "Execute", true),
34 37 /**
35 38 * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see
36 39 * section 5.3.3 of the LW M2M spec).
37 40 * if all resources are to be replaced
38 41 */
39   - WRITE_REPLACE(8, "WriteReplace", true),
  42 + WRITE_REPLACE(11, "WriteReplace", true),
40 43
41 44 /**
42 45 * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section
43 46 * 5.3.3 of the LW M2M spec).
44 47 * if this is a partial update request
45 48 */
46   - WRITE_UPDATE(9, "WriteUpdate", true),
47   - WRITE_ATTRIBUTES(10, "WriteAttributes", true),
48   - DELETE(11, "Delete", true),
  49 + WRITE_UPDATE(12, "WriteUpdate", true),
  50 + WRITE_COMPOSITE(14, "WriteComposite", false, true),
  51 + WRITE_ATTRIBUTES(15, "WriteAttributes", true),
  52 + DELETE(16, "Delete", true),
49 53
50 54 // only for RPC
51   - FW_UPDATE(12, "FirmwareUpdate", false);
  55 + FW_UPDATE(17, "FirmwareUpdate", false);
52 56
53   -// FW_READ_INFO(12, "FirmwareReadInfo"),
54   -// SW_READ_INFO(15, "SoftwareReadInfo"),
55   -// SW_UPDATE(16, "SoftwareUpdate"),
56   -// SW_UNINSTALL(18, "SoftwareUninstall");
  57 +// FW_READ_INFO(18, "FirmwareReadInfo"),
  58 +// SW_READ_INFO(19, "SoftwareReadInfo"),
  59 +// SW_UPDATE(20, "SoftwareUpdate"),
  60 +// SW_UNINSTALL(21, "SoftwareUninstall");
57 61
58 62 @Getter
59 63 private final int code;
... ... @@ -62,10 +66,21 @@ public enum LwM2mOperationType {
62 66 @Getter
63 67 private final boolean hasObjectId;
64 68
  69 + @Getter
  70 + private final boolean composite;
  71 +
65 72 LwM2mOperationType(int code, String type, boolean hasObjectId) {
  73 + this(code, type, hasObjectId, false);
  74 + }
  75 +
  76 + LwM2mOperationType(int code, String type, boolean hasObjectId, boolean composite) {
66 77 this.code = code;
67 78 this.type = type;
68 79 this.hasObjectId = hasObjectId;
  80 + this.composite = composite;
  81 + if(hasObjectId && composite){
  82 + throw new IllegalArgumentException("Can't set both Composite and hasObjectId for the same operation!");
  83 + }
69 84 }
70 85
71 86 public static LwM2mOperationType fromType(String type) {
... ...
... ... @@ -44,6 +44,7 @@ import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfigurati
44 44 import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration;
45 45 import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
46 46 import org.thingsboard.server.common.transport.TransportServiceCallback;
  47 +import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion;
47 48 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
48 49 import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue;
49 50 import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult;
... ... @@ -85,7 +86,7 @@ import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaU
85 86 @Slf4j
86 87 public class LwM2mTransportUtil {
87 88
88   - public static final String LWM2M_VERSION_DEFAULT = "1.0";
  89 + public static final String LWM2M_OBJECT_VERSION_DEFAULT = "1.0";
89 90
90 91 public static final String LOG_LWM2M_TELEMETRY = "transportLog";
91 92 public static final String LOG_LWM2M_INFO = "info";
... ... @@ -234,7 +235,7 @@ public class LwM2mTransportUtil {
234 235
235 236 public static String convertObjectIdToVersionedId(String path, Registration registration) {
236 237 String ver = registration.getSupportedObject().get(new LwM2mPath(path).getObjectId());
237   - ver = ver != null ? ver : LWM2M_VERSION_DEFAULT;
  238 + ver = ver != null ? ver : LwM2mVersion.VERSION_1_0.getVersion().toString();
238 239 try {
239 240 String[] keyArray = path.split(LWM2M_SEPARATOR_PATH);
240 241 if (keyArray.length > 1) {
... ... @@ -381,4 +382,5 @@ public class LwM2mTransportUtil {
381 382 }
382 383 return lwm2mResourceValue;
383 384 }
  385 +
384 386 }
... ...
... ... @@ -37,6 +37,7 @@ import org.thingsboard.server.common.data.id.TenantId;
37 37 import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
38 38 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
39 39 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
  40 +import org.thingsboard.server.transport.lwm2m.config.LwM2mVersion;
40 41
41 42 import java.io.IOException;
42 43 import java.io.ObjectInputStream;
... ... @@ -52,7 +53,7 @@ import java.util.concurrent.locks.ReentrantLock;
52 53 import java.util.stream.Collectors;
53 54
54 55 import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH;
55   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_VERSION_DEFAULT;
  56 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_OBJECT_VERSION_DEFAULT;
56 57 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId;
57 58 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName;
58 59 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
... ... @@ -226,6 +227,10 @@ public class LwM2mClient implements Serializable {
226 227 .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null;
227 228 }
228 229
  230 + public boolean isResourceMultiInstances(String pathIdVer, LwM2mModelProvider modelProvider) {
  231 + return getResourceModel(pathIdVer, modelProvider).multiple;
  232 + }
  233 +
229 234 public ObjectModel getObjectModel(String pathIdVer, LwM2mModelProvider modelProvider) {
230 235 LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer));
231 236 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
... ... @@ -234,11 +239,11 @@ public class LwM2mClient implements Serializable {
234 239 .getObjectModel(pathIds.getObjectId()) : null;
235 240 }
236 241
237   - public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverter converter, String pathIdVer) {
  242 + public String objectToString(LwM2mObject lwM2mObject) {
238 243 StringBuilder builder = new StringBuilder();
239 244 builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={");
240 245 lwM2mObject.getInstances().forEach((instId, inst) -> {
241   - builder.append(instId).append("=").append(this.instanceToString(inst, converter, pathIdVer)).append(", ");
  246 + builder.append(instId).append("=").append(this.instanceToString(inst)).append(", ");
242 247 });
243 248 int startInd = builder.lastIndexOf(", ");
244 249 if (startInd > 0) {
... ... @@ -248,11 +253,11 @@ public class LwM2mClient implements Serializable {
248 253 return builder.toString();
249 254 }
250 255
251   - public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverter converter, String pathIdVer) {
  256 + public String instanceToString(LwM2mObjectInstance objectInstance) {
252 257 StringBuilder builder = new StringBuilder();
253 258 builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={");
254 259 objectInstance.getResources().forEach((resId, res) -> {
255   - builder.append(resId).append("=").append(this.resourceToString(res, converter, pathIdVer)).append(", ");
  260 + builder.append(resId).append("=").append(this.resourceToString(res)).append(", ");
256 261 });
257 262 int startInd = builder.lastIndexOf(", ");
258 263 if (startInd > 0) {
... ... @@ -262,8 +267,8 @@ public class LwM2mClient implements Serializable {
262 267 return builder.toString();
263 268 }
264 269
265   - public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverter converter, String pathIdVer) {
266   - return lwM2mResource.getValue().toString();
  270 + public String resourceToString(LwM2mResource lwM2mResource) {
  271 + return lwM2mResource.isMultiInstances() ? lwM2mResource.getInstances().toString() : lwM2mResource.getValue().toString();
267 272 }
268 273
269 274 public Collection<LwM2mResource> getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider,
... ... @@ -299,11 +304,14 @@ public class LwM2mClient implements Serializable {
299 304 return resources;
300 305 }
301 306
302   - public boolean isValidObjectVersion(String path) {
  307 + public void isValidObjectVersion(String path) {
303 308 LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path));
304 309 String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId());
305 310 String verRez = getVerFromPathIdVerOrId(path);
306   - return verRez == null ? LWM2M_VERSION_DEFAULT.equals(verSupportedObject) : verRez.equals(verSupportedObject);
  311 + if ((verRez != null && !verRez.equals(verSupportedObject)) ||
  312 + (verRez == null && !LWM2M_OBJECT_VERSION_DEFAULT.equals(verSupportedObject))) {
  313 + throw new IllegalArgumentException(String.format("Specified resource id %s is not valid version! Must be version: %s", path, verSupportedObject));
  314 + }
307 315 }
308 316
309 317 /**
... ... @@ -348,10 +356,8 @@ public class LwM2mClient implements Serializable {
348 356 public ContentFormat getDefaultContentFormat() {
349 357 if (registration == null) {
350 358 return ContentFormat.DEFAULT;
351   - } else if (registration.getLwM2mVersion().equals("1.0")) {
352   - return ContentFormat.TLV;
353 359 } else {
354   - return ContentFormat.TEXT;
  360 + return LwM2mVersion.fromVersionStr(registration.getLwM2mVersion()).getContentFormat();
355 361 }
356 362 }
357 363
... ...
... ... @@ -26,24 +26,31 @@ import org.eclipse.leshan.core.node.LwM2mResource;
26 26 import org.eclipse.leshan.core.node.ObjectLink;
27 27 import org.eclipse.leshan.core.node.codec.CodecException;
28 28 import org.eclipse.leshan.core.observation.Observation;
  29 +import org.eclipse.leshan.core.request.CompositeDownlinkRequest;
29 30 import org.eclipse.leshan.core.request.ContentFormat;
30 31 import org.eclipse.leshan.core.request.DeleteRequest;
31 32 import org.eclipse.leshan.core.request.DiscoverRequest;
  33 +import org.eclipse.leshan.core.request.DownlinkRequest;
32 34 import org.eclipse.leshan.core.request.ExecuteRequest;
33 35 import org.eclipse.leshan.core.request.ObserveRequest;
  36 +import org.eclipse.leshan.core.request.ReadCompositeRequest;
34 37 import org.eclipse.leshan.core.request.ReadRequest;
35 38 import org.eclipse.leshan.core.request.SimpleDownlinkRequest;
36 39 import org.eclipse.leshan.core.request.WriteAttributesRequest;
  40 +import org.eclipse.leshan.core.request.WriteCompositeRequest;
37 41 import org.eclipse.leshan.core.request.WriteRequest;
38 42 import org.eclipse.leshan.core.response.DeleteResponse;
39 43 import org.eclipse.leshan.core.response.DiscoverResponse;
40 44 import org.eclipse.leshan.core.response.ExecuteResponse;
41 45 import org.eclipse.leshan.core.response.LwM2mResponse;
42 46 import org.eclipse.leshan.core.response.ObserveResponse;
  47 +import org.eclipse.leshan.core.response.ReadCompositeResponse;
43 48 import org.eclipse.leshan.core.response.ReadResponse;
44 49 import org.eclipse.leshan.core.response.WriteAttributesResponse;
  50 +import org.eclipse.leshan.core.response.WriteCompositeResponse;
45 51 import org.eclipse.leshan.core.response.WriteResponse;
46 52 import org.eclipse.leshan.core.util.Hex;
  53 +import org.eclipse.leshan.server.model.LwM2mModelProvider;
47 54 import org.eclipse.leshan.server.registration.Registration;
48 55 import org.springframework.stereotype.Service;
49 56 import org.thingsboard.common.util.JacksonUtil;
... ... @@ -53,7 +60,10 @@ import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
53 60 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
54 61 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
55 62 import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService;
  63 +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeRequest;
  64 +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MWriteResponseCompositeCallback;
56 65 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
  66 +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
57 67 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
58 68
59 69 import javax.annotation.PostConstruct;
... ... @@ -63,6 +73,7 @@ import java.util.Collection;
63 73 import java.util.Date;
64 74 import java.util.LinkedList;
65 75 import java.util.List;
  76 +import java.util.Map;
66 77 import java.util.Set;
67 78 import java.util.function.Function;
68 79 import java.util.function.Predicate;
... ... @@ -73,6 +84,7 @@ import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN;
73 84 import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD;
74 85 import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD;
75 86 import static org.eclipse.leshan.core.attributes.Attribute.STEP;
  87 +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId;
76 88
77 89 @Slf4j
78 90 @Service
... ... @@ -110,8 +122,31 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
110 122 @Override
111 123 public void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback<ReadRequest, ReadResponse> callback) {
112 124 validateVersionedId(client, request);
113   - ReadRequest downlink = new ReadRequest(getContentFormat(client, request), request.getObjectId());
114   - sendRequest(client, downlink, request.getTimeout(), callback);
  125 + ReadRequest downlink = new ReadRequest(getRequestContentFormat(client, request, this.config.getModelProvider()), request.getObjectId());
  126 + sendSimpleRequest(client, downlink, request.getTimeout(), callback);
  127 + }
  128 +
  129 + @Override
  130 + public void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request, DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback) {
  131 + validateVersionedIds(client, request);
  132 + ContentFormat requestContentFormat = ContentFormat.SENML_JSON;
  133 + ContentFormat responseContentFormat = ContentFormat.SENML_JSON;
  134 +
  135 + ReadCompositeRequest downlink = new ReadCompositeRequest(requestContentFormat, responseContentFormat, request.getObjectIds());
  136 + sendCompositeRequest(client, downlink, this.config.getTimeout(), callback);
  137 + }
  138 +
  139 + @Override
  140 + public void sendWriteCompositeRequest(LwM2mClient client, Map<String, Object> nodes, DefaultLwM2MUplinkMsgHandler handler) {
  141 +// ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider());
  142 + TbLwM2MWriteResponseCompositeCallback callback = new TbLwM2MWriteResponseCompositeCallback(handler, logService, client, null);
  143 + ContentFormat contentFormat = ContentFormat.SENML_JSON;
  144 + try {
  145 + WriteCompositeRequest downlink = new WriteCompositeRequest(contentFormat, nodes);
  146 + sendWriteCompositeRequest(client, downlink, this.config.getTimeout(), callback);
  147 + } catch (Exception e) {
  148 + callback.onError(JacksonUtil.toString(nodes), e);
  149 + }
115 150 }
116 151
117 152 @Override
... ... @@ -121,7 +156,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
121 156 Set<Observation> observations = context.getServer().getObservationService().getObservations(client.getRegistration());
122 157 if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) {
123 158 ObserveRequest downlink;
124   - ContentFormat contentFormat = getContentFormat(client, request);
  159 + ContentFormat contentFormat = getRequestContentFormat(client, request, this.config.getModelProvider());
125 160 if (resultIds.isResource()) {
126 161 downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId());
127 162 } else if (resultIds.isObjectInstance()) {
... ... @@ -130,7 +165,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
130 165 downlink = new ObserveRequest(contentFormat, resultIds.getObjectId());
131 166 }
132 167 log.info("[{}] Send observation: {}.", client.getEndpoint(), request.getVersionedId());
133   - sendRequest(client, downlink, request.getTimeout(), callback);
  168 + sendSimpleRequest(client, downlink, request.getTimeout(), callback);
134 169 } else {
135 170 callback.onValidationError(resultIds.toString(), "Observation is already registered!");
136 171 }
... ... @@ -158,13 +193,13 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
158 193 } else {
159 194 downlink = new ExecuteRequest(request.getVersionedId());
160 195 }
161   - sendRequest(client, downlink, request.getTimeout(), callback);
  196 + sendSimpleRequest(client, downlink, request.getTimeout(), callback);
162 197 }
163 198 }
164 199
165 200 @Override
166 201 public void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback<DeleteRequest, DeleteResponse> callback) {
167   - sendRequest(client, new DeleteRequest(request.getObjectId()), request.getTimeout(), callback);
  202 + sendSimpleRequest(client, new DeleteRequest(request.getObjectId()), request.getTimeout(), callback);
168 203 }
169 204
170 205 @Override
... ... @@ -182,7 +217,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
182 217 @Override
183 218 public void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback<DiscoverRequest, DiscoverResponse> callback) {
184 219 validateVersionedId(client, request);
185   - sendRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback);
  220 + sendSimpleRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback);
186 221 }
187 222
188 223 @Override
... ... @@ -202,7 +237,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
202 237 addAttribute(attributes, LESSER_THAN, params.getLt());
203 238 addAttribute(attributes, STEP, params.getSt());
204 239 AttributeSet attributeSet = new AttributeSet(attributes);
205   - sendRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback);
  240 + sendSimpleRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback);
206 241 }
207 242
208 243 @Override
... ... @@ -214,7 +249,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
214 249 LwM2mPath path = new LwM2mPath(request.getObjectId());
215 250 WriteRequest downlink = this.getWriteRequestSingleResource(resourceModelWrite.type, contentFormat,
216 251 path.getObjectId(), path.getObjectInstanceId(), path.getResourceId(), request.getValue());
217   - sendRequest(client, downlink, request.getTimeout(), callback);
  252 + sendSimpleRequest(client, downlink, request.getTimeout(), callback);
218 253 } catch (Exception e) {
219 254 callback.onError(JacksonUtil.toString(request), e);
220 255 }
... ... @@ -236,7 +271,7 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
236 271 ContentFormat contentFormat = request.getObjectContentFormat() != null ? request.getObjectContentFormat() : convertResourceModelTypeToContentFormat(client, resourceModelWrite.type);
237 272 WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(),
238 273 resultIds.getObjectInstanceId(), resources);
239   - sendRequest(client, downlink, request.getTimeout(), callback);
  274 + sendSimpleRequest(client, downlink, request.getTimeout(), callback);
240 275 } else if (resultIds.isObjectInstance()) {
241 276 /*
242 277 * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}"
... ... @@ -245,9 +280,9 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
245 280 */
246 281 Collection<LwM2mResource> resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter);
247 282 if (resources.size() > 0) {
248   - ContentFormat contentFormat = request.getObjectContentFormat() != null ? request.getObjectContentFormat() : client.getDefaultContentFormat();
  283 + ContentFormat contentFormat = request.getObjectContentFormat() != null ? request.getObjectContentFormat() : ContentFormat.DEFAULT;
249 284 WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources);
250   - sendRequest(client, downlink, request.getTimeout(), callback);
  285 + sendSimpleRequest(client, downlink, request.getTimeout(), callback);
251 286 } else {
252 287 callback.onValidationError(JacksonUtil.toString(request), "No resources to update!");
253 288 }
... ... @@ -256,10 +291,42 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
256 291 }
257 292 }
258 293
259   - private <R extends SimpleDownlinkRequest<T>, T extends LwM2mResponse> void sendRequest(LwM2mClient client, R request, long timeoutInMs, DownlinkRequestCallback<R, T> callback) {
  294 +
  295 + private <R extends SimpleDownlinkRequest<T>, T extends LwM2mResponse> void sendSimpleRequest(LwM2mClient client, R request, long timeoutInMs, DownlinkRequestCallback<R, T> callback) {
  296 + sendRequest(client, request, timeoutInMs, callback, r -> request.getPath().toString());
  297 + }
  298 +
  299 + private <R extends CompositeDownlinkRequest<T>, T extends LwM2mResponse> void sendCompositeRequest(LwM2mClient client, R request, long timeoutInMs, DownlinkRequestCallback<R, T> callback) {
  300 + sendRequest(client, request, timeoutInMs, callback, r -> request.getPaths().toString());
  301 + }
  302 +
  303 + private <R extends DownlinkRequest<T>, T extends LwM2mResponse> void sendRequest(LwM2mClient client, R request, long timeoutInMs, DownlinkRequestCallback<R, T> callback, Function<R, String> pathToStringFunction) {
  304 + Registration registration = client.getRegistration();
  305 + try {
  306 + logService.log(client, String.format("[%s][%s] Sending request: %s to %s", registration.getId(), registration.getSocketAddress(), request.getClass().getSimpleName(), pathToStringFunction.apply(request)));
  307 + context.getServer().send(registration, request, timeoutInMs, response -> {
  308 + executor.submit(() -> {
  309 + try {
  310 + callback.onSuccess(request, response);
  311 + } catch (Exception e) {
  312 + log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e);
  313 + }
  314 + });
  315 + }, e -> {
  316 + executor.submit(() -> {
  317 + callback.onError(JacksonUtil.toString(request), e);
  318 + });
  319 + });
  320 + } catch (Exception e) {
  321 + callback.onError(JacksonUtil.toString(request), e);
  322 + }
  323 + }
  324 +
  325 + private <R extends SimpleDownlinkRequest<T>, T extends LwM2mResponse> void sendWriteCompositeRequest(LwM2mClient client, WriteCompositeRequest request, long timeoutInMs,
  326 + DownlinkRequestCallback<WriteCompositeRequest, WriteCompositeResponse> callback) {
260 327 Registration registration = client.getRegistration();
261 328 try {
262   - logService.log(client, String.format("[%s][%s] Sending request: %s to %s", registration.getId(), registration.getSocketAddress(), request.getClass().getSimpleName(), request.getPath()));
  329 + logService.log(client, String.format("[%s][%s] Sending request: %s to %s", registration.getId(), registration.getSocketAddress(), request.getClass().getSimpleName(), request.getPaths()));
263 330 context.getServer().send(registration, request, timeoutInMs, response -> {
264 331 executor.submit(() -> {
265 332 try {
... ... @@ -278,6 +345,52 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
278 345 }
279 346 }
280 347
  348 +// private <R extends DownlinkRequest<T>, T extends LwM2mResponse> void sendReadRequestComposite(LwM2mClient client, ReadCompositeRequest request, long timeoutInMs,
  349 +// DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback) {
  350 +// Registration registration = client.getRegistration();
  351 +// try {
  352 +// logService.log(client, String.format("[%s][%s] Sending request: %s to %s", registration.getId(), registration.getSocketAddress(), request.getClass().getSimpleName(), request.getPaths()));
  353 +// context.getServer().send(registration, request, timeoutInMs, response -> {
  354 +// executor.submit(() -> {
  355 +// try {
  356 +// /**
  357 +// * [{"bn":"/3/0/","n":"0","vs":"Thingsboard Test Device"},
  358 +// * {"n":"1","vs":"Model 500"},
  359 +// * {"n":"2","vs":"TH-500-000-0001"},
  360 +// * {"n":"3","vs":"TestThingsboard@TestMore1024_2.04"},
  361 +// * {"n":"6","v":1},{"n":"7","v":56},
  362 +// * {"n":"8","v":42},{"n":"9","v":16},
  363 +// * {"n":"10","v":127619},{"n":"13","v":1624520988},
  364 +// * {"n":"14","vs":"+03"},{"n":"15","vs":"Europe/Kiev"},
  365 +// * {"n":"16","vs":"U"},{"n":"17","vs":"smart meters"},
  366 +// * {"n":"18","vs":"1.01"},{"n":"19","vs":"1.02"},
  367 +// * {"n":"20","v":3},{"n":"21","v":256000},
  368 +// * {"bn":"/5/0/","n":"1","vs":""},
  369 +// * {"n":"3","v":0},{"n":"5","v":0},
  370 +// * {"n":"6","vs":""},{"n":"7","vs":""},
  371 +// * {"n":"8/0","v":0},{"n":"8/1","v":1},
  372 +// * {"n":"9","v":2},
  373 +// * {"bn":"/1/0/","n":"0","v":123},
  374 +// * {"n":"1","v":300},
  375 +// * {"n":"6","vb":false},
  376 +// * {"n":"22","vs":"U"},
  377 +// * {"n":"7","vs":"U"}]
  378 +// */
  379 +// callback.onSuccess(request, response);
  380 +// } catch (Exception e) {
  381 +// log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e);
  382 +// }
  383 +// });
  384 +// }, e -> {
  385 +// executor.submit(() -> {
  386 +// callback.onError(JacksonUtil.toString(request), e);
  387 +// });
  388 +// });
  389 +// } catch (Exception e) {
  390 +// callback.onError(JacksonUtil.toString(request), e);
  391 +// }
  392 +// }
  393 +
281 394 private WriteRequest getWriteRequestSingleResource(ResourceModel.Type type, ContentFormat contentFormat, int objectId, int instanceId, int resourceId, Object value) {
282 395 switch (type) {
283 396 case STRING: // String
... ... @@ -308,14 +421,23 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
308 421 }
309 422
310 423 private void validateVersionedId(LwM2mClient client, HasVersionedId request) {
311   - if (!client.isValidObjectVersion(request.getVersionedId())) {
312   - throw new IllegalArgumentException("Specified resource id is not configured in the device profile!");
313   - }
  424 + client.isValidObjectVersion(request.getVersionedId());
314 425 if (request.getObjectId() == null) {
315 426 throw new IllegalArgumentException("Specified object id is null!");
316 427 }
317 428 }
318 429
  430 + private void validateVersionedIds(LwM2mClient client, HasVersionedIds request) {
  431 + for (String versionedId : request.getVersionedIds()) {
  432 + client.isValidObjectVersion(versionedId);
  433 + }
  434 + for (String objectId : request.getObjectIds()) {
  435 + if (objectId == null) {
  436 + throw new IllegalArgumentException("Specified object id is null!");
  437 + }
  438 + }
  439 + }
  440 +
319 441 private static <T> void addAttribute(List<Attribute> attributes, String attributeName, T value) {
320 442 addAttribute(attributes, attributeName, value, null, null);
321 443 }
... ... @@ -347,7 +469,23 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
347 469 throw new CodecException("Invalid ResourceModel_Type for %s ContentFormat.", type);
348 470 }
349 471
350   - private static ContentFormat getContentFormat(LwM2mClient client, HasContentFormat request) {
351   - return request.getContentFormat() != null ? request.getContentFormat() : client.getDefaultContentFormat();
  472 + private static ContentFormat getRequestContentFormat(LwM2mClient client, HasContentFormat request, LwM2mModelProvider modelProvider) {
  473 + if (request.getRequestContentFormat() != null) {
  474 + return request.getRequestContentFormat();
  475 + } else {
  476 + String versionedId = null;
  477 + if (request instanceof TbLwM2MReadRequest) {
  478 + versionedId = ((TbLwM2MReadRequest) request).getVersionedId();
  479 + } else if (request instanceof TbLwM2MObserveRequest) {
  480 + versionedId = ((TbLwM2MObserveRequest) request).getVersionedId();
  481 + }
  482 + String id = fromVersionedIdToObjectId(versionedId);
  483 + if (id != null && new LwM2mPath(id).isResource() && !client.isResourceMultiInstances(versionedId, modelProvider)) {
  484 + return client.getDefaultContentFormat();
  485 + }
  486 + else {
  487 + return ContentFormat.DEFAULT;
  488 + }
  489 + }
352 490 }
353 491 }
... ...
... ... @@ -19,5 +19,9 @@ import org.eclipse.leshan.core.request.ContentFormat;
19 19
20 20 public interface HasContentFormat {
21 21
22   - ContentFormat getContentFormat();
  22 + ContentFormat getRequestContentFormat();
  23 +
  24 + default ContentFormat getResponseContentFormat() {
  25 + return null;
  26 + }
23 27 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.downlink;
  17 +
  18 +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
  19 +
  20 +import java.util.Set;
  21 +import java.util.concurrent.ConcurrentHashMap;
  22 +
  23 +public interface HasVersionedIds {
  24 +
  25 + String[] getVersionedIds();
  26 +
  27 + default String[] getObjectIds() {
  28 + Set objectIds = ConcurrentHashMap.newKeySet();
  29 + for (String versionedId : getVersionedIds()) {
  30 + objectIds.add(LwM2mTransportUtil.fromVersionedIdToObjectId(versionedId));
  31 + }
  32 + return (String[]) objectIds.toArray(String[]::new);
  33 + }
  34 +
  35 +}
... ...
... ... @@ -20,6 +20,7 @@ import org.eclipse.leshan.core.request.DeleteRequest;
20 20 import org.eclipse.leshan.core.request.DiscoverRequest;
21 21 import org.eclipse.leshan.core.request.ExecuteRequest;
22 22 import org.eclipse.leshan.core.request.ObserveRequest;
  23 +import org.eclipse.leshan.core.request.ReadCompositeRequest;
23 24 import org.eclipse.leshan.core.request.ReadRequest;
24 25 import org.eclipse.leshan.core.request.WriteAttributesRequest;
25 26 import org.eclipse.leshan.core.request.WriteRequest;
... ... @@ -27,18 +28,24 @@ import org.eclipse.leshan.core.response.DeleteResponse;
27 28 import org.eclipse.leshan.core.response.DiscoverResponse;
28 29 import org.eclipse.leshan.core.response.ExecuteResponse;
29 30 import org.eclipse.leshan.core.response.ObserveResponse;
  31 +import org.eclipse.leshan.core.response.ReadCompositeResponse;
30 32 import org.eclipse.leshan.core.response.ReadResponse;
31 33 import org.eclipse.leshan.core.response.WriteAttributesResponse;
32 34 import org.eclipse.leshan.core.response.WriteResponse;
33 35 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  36 +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeRequest;
  37 +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler;
34 38
35 39 import java.util.List;
  40 +import java.util.Map;
36 41 import java.util.Set;
37 42
38 43 public interface LwM2mDownlinkMsgHandler {
39 44
40 45 void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback<ReadRequest, ReadResponse> callback);
41 46
  47 + void sendReadCompositeRequest(LwM2mClient client, TbLwM2MReadCompositeRequest request, DownlinkRequestCallback<ReadCompositeRequest, ReadCompositeResponse> callback);
  48 +
42 49 void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback<ObserveRequest, ObserveResponse> callback);
43 50
44 51 void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback<TbLwM2MObserveAllRequest, Set<String>> callback);
... ... @@ -59,6 +66,8 @@ public interface LwM2mDownlinkMsgHandler {
59 66
60 67 void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback);
61 68
  69 + void sendWriteCompositeRequest(LwM2mClient client, Map<String, Object> nodes, DefaultLwM2MUplinkMsgHandler handler);
  70 +
62 71 void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback);
63 72
64 73
... ...
... ... @@ -24,12 +24,12 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType;
24 24 public class TbLwM2MObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest<ObserveResponse> implements HasContentFormat {
25 25
26 26 @Getter
27   - private final ContentFormat contentFormat;
  27 + private final ContentFormat requestContentFormat;
28 28
29 29 @Builder
30   - private TbLwM2MObserveRequest(String versionedId, long timeout, ContentFormat contentFormat) {
  30 + private TbLwM2MObserveRequest(String versionedId, long timeout, ContentFormat requestContentFormat) {
31 31 super(versionedId, timeout);
32   - this.contentFormat = contentFormat;
  32 + this.requestContentFormat = requestContentFormat;
33 33 }
34 34
35 35 @Override
... ...
... ... @@ -24,12 +24,12 @@ import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType;
24 24 public class TbLwM2MReadRequest extends AbstractTbLwM2MTargetedDownlinkRequest<ReadResponse> implements HasContentFormat {
25 25
26 26 @Getter
27   - private final ContentFormat contentFormat;
  27 + private final ContentFormat requestContentFormat;
28 28
29 29 @Builder
30   - private TbLwM2MReadRequest(String versionedId, long timeout, ContentFormat contentFormat) {
  30 + private TbLwM2MReadRequest(String versionedId, long timeout, ContentFormat requestContentFormat) {
31 31 super(versionedId, timeout);
32   - this.contentFormat = contentFormat;
  32 + this.requestContentFormat = requestContentFormat;
33 33 }
34 34
35 35 @Override
... ...
... ... @@ -18,7 +18,8 @@ package org.thingsboard.server.transport.lwm2m.server.downlink;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
20 20 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
21   -import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
  21 +
  22 +import java.util.Arrays;
22 23
23 24 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
24 25
... ... @@ -26,18 +27,26 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.L
26 27 public abstract class TbLwM2MTargetedCallback<R, T> extends AbstractTbLwM2MRequestCallback<R, T> {
27 28
28 29 protected final String versionedId;
  30 + protected final String[] versionedIds;
29 31
30 32 public TbLwM2MTargetedCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) {
31 33 super(logService, client);
32 34 this.versionedId = versionedId;
  35 + this.versionedIds = null;
  36 + }
  37 +
  38 + public TbLwM2MTargetedCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String[] versionedIds) {
  39 + super(logService, client);
  40 + this.versionedId = null;
  41 + this.versionedIds = versionedIds;
33 42 }
34 43
35 44 @Override
36 45 public void onSuccess(R request, T response) {
37 46 //TODO convert camelCase to "camel case" using .split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")
38 47 String requestName = request.getClass().getSimpleName();
39   - log.trace("[{}] {} [{}] successful: {}", client.getEndpoint(), requestName, versionedId, response);
40   - logService.log(client, String.format("[%s]: %s [%s] successful. Result: [%s]", LOG_LWM2M_INFO, requestName, versionedId, response));
  48 + log.trace("[{}] {} [{}] successful: {}", client.getEndpoint(), requestName, versionedId != null ? versionedId : versionedIds, response);
  49 + logService.log(client, String.format("[%s]: %s [%s] successful. Result: [%s]", LOG_LWM2M_INFO, requestName, versionedId != null ? versionedId : Arrays.toString(versionedIds), response));
41 50 }
42 51
43 52 }
... ...
... ... @@ -20,8 +20,6 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
20 20 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
21 21 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
22 22
23   -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO;
24   -
25 23 @Slf4j
26 24 public abstract class TbLwM2MUplinkTargetedCallback<R, T> extends TbLwM2MTargetedCallback<R, T> {
27 25
... ... @@ -32,4 +30,9 @@ public abstract class TbLwM2MUplinkTargetedCallback<R, T> extends TbLwM2MTargete
32 30 this.handler = handler;
33 31 }
34 32
  33 + public TbLwM2MUplinkTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String[] versionedIds) {
  34 + super(logService, client, versionedIds);
  35 + this.handler = handler;
  36 + }
  37 +
35 38 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.downlink.composite;
  17 +
  18 +import lombok.Getter;
  19 +import org.thingsboard.server.transport.lwm2m.server.downlink.HasVersionedIds;
  20 +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDownlinkRequest;
  21 +
  22 +public abstract class AbstractTbLwM2MTargetedDownlinkCompositeRequest<T> implements TbLwM2MDownlinkRequest<T>, HasVersionedIds {
  23 +
  24 + @Getter
  25 + private final String [] versionedIds;
  26 + @Getter
  27 + private final long timeout;
  28 +
  29 + public AbstractTbLwM2MTargetedDownlinkCompositeRequest(String [] versionedIds, long timeout) {
  30 + this.versionedIds = versionedIds;
  31 + this.timeout = timeout;
  32 + }
  33 +
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.downlink.composite;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.leshan.core.request.ReadCompositeRequest;
  20 +import org.eclipse.leshan.core.response.ReadCompositeResponse;
  21 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  22 +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MUplinkTargetedCallback;
  23 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
  24 +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
  25 +
  26 +@Slf4j
  27 +public class TbLwM2MReadCompositeCallback extends TbLwM2MUplinkTargetedCallback<ReadCompositeRequest, ReadCompositeResponse> {
  28 +
  29 + public TbLwM2MReadCompositeCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String[] versionedIds) {
  30 + super(handler, logService, client, versionedIds);
  31 + }
  32 +
  33 + @Override
  34 + public void onSuccess(ReadCompositeRequest request, ReadCompositeResponse response) {
  35 + super.onSuccess(request, response);
  36 + handler.onUpdateValueAfterReadCompositeResponse(client.getRegistration(), response);
  37 + }
  38 +
  39 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.downlink.composite;
  17 +
  18 +import lombok.Builder;
  19 +import lombok.Getter;
  20 +import org.eclipse.leshan.core.request.ContentFormat;
  21 +import org.eclipse.leshan.core.response.ReadCompositeResponse;
  22 +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType;
  23 +import org.thingsboard.server.transport.lwm2m.server.downlink.HasContentFormat;
  24 +
  25 +public class TbLwM2MReadCompositeRequest extends AbstractTbLwM2MTargetedDownlinkCompositeRequest<ReadCompositeResponse> implements HasContentFormat {
  26 +
  27 + @Getter
  28 + private final ContentFormat requestContentFormat;
  29 +
  30 + @Getter
  31 + private final ContentFormat responseContentFormat;
  32 +
  33 + @Builder
  34 + private TbLwM2MReadCompositeRequest(String [] versionedIds, long timeout, ContentFormat requestContentFormat, ContentFormat responseContentFormat) {
  35 + super(versionedIds, timeout);
  36 + this.requestContentFormat = requestContentFormat;
  37 + this.responseContentFormat = responseContentFormat;
  38 + }
  39 +
  40 + @Override
  41 + public LwM2mOperationType getType() {
  42 + return LwM2mOperationType.READ_COMPOSITE;
  43 + }
  44 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.downlink.composite;
  17 +
  18 +import lombok.Builder;
  19 +import lombok.Getter;
  20 +import org.eclipse.leshan.core.request.ContentFormat;
  21 +import org.eclipse.leshan.core.response.WriteCompositeResponse;
  22 +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType;
  23 +import org.thingsboard.server.transport.lwm2m.server.downlink.AbstractTbLwM2MTargetedDownlinkRequest;
  24 +
  25 +public class TbLwM2MWriteCompositeRequest extends AbstractTbLwM2MTargetedDownlinkRequest<WriteCompositeResponse> {
  26 +
  27 + @Getter
  28 + private final ContentFormat contentFormat;
  29 + @Getter
  30 + private final Object value;
  31 +
  32 + @Builder
  33 + private TbLwM2MWriteCompositeRequest(String versionedId, long timeout, ContentFormat contentFormat, Object value) {
  34 + super(versionedId, timeout);
  35 + this.contentFormat = contentFormat;
  36 + this.value = value;
  37 + }
  38 +
  39 + @Override
  40 + public LwM2mOperationType getType() {
  41 + return LwM2mOperationType.WRITE_REPLACE;
  42 + }
  43 +
  44 +
  45 +
  46 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.downlink.composite;
  17 +
  18 +import org.eclipse.leshan.core.request.WriteCompositeRequest;
  19 +import org.eclipse.leshan.core.response.WriteCompositeResponse;
  20 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  21 +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MUplinkTargetedCallback;
  22 +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
  23 +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
  24 +
  25 +public class TbLwM2MWriteResponseCompositeCallback extends TbLwM2MUplinkTargetedCallback<WriteCompositeRequest, WriteCompositeResponse> {
  26 +
  27 + public TbLwM2MWriteResponseCompositeCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) {
  28 + super(handler, logService, client, targetId);
  29 + }
  30 +
  31 + @Override
  32 + public void onSuccess(WriteCompositeRequest request, WriteCompositeResponse response) {
  33 + super.onSuccess(request, response);
  34 + handler.onWriteCompositeResponseOk(client, request);
  35 + }
  36 +
  37 +}
... ...
... ... @@ -50,7 +50,12 @@ import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttrib
50 50 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest;
51 51 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback;
52 52 import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest;
  53 +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeCallback;
  54 +import org.thingsboard.server.transport.lwm2m.server.downlink.composite.TbLwM2MReadCompositeRequest;
53 55 import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
  56 +import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcReadCompositeRequest;
  57 +import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcReadResponseCompositeCallback;
  58 +import org.thingsboard.server.transport.lwm2m.server.rpc.composite.RpcWriteCompositeRequest;
54 59 import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
55 60
56 61 import java.util.Map;
... ... @@ -125,6 +130,17 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
125 130 default:
126 131 throw new IllegalArgumentException("Unsupported operation: " + operationType.name());
127 132 }
  133 + } else if (operationType.isComposite()) {
  134 + switch (operationType) {
  135 + case READ_COMPOSITE:
  136 + sendReadCompositeRequest(client, rpcRequst);
  137 + break;
  138 + case WRITE_COMPOSITE:
  139 + sendWriteCompositeRequest(client, rpcRequst);
  140 + break;
  141 + default:
  142 + throw new IllegalArgumentException("Unsupported operation: " + operationType.name());
  143 + }
128 144 } else {
129 145 switch (operationType) {
130 146 case OBSERVE_CANCEL_ALL:
... ... @@ -151,14 +167,22 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
151 167 private void sendReadRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
152 168 TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build();
153 169 var mainCallback = new TbLwM2MReadCallback(uplinkHandler, logService, client, versionedId);
154   - var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback);
  170 + var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, mainCallback);
155 171 downlinkHandler.sendReadRequest(client, request, rpcCallback);
156 172 }
157 173
  174 + private void sendReadCompositeRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) {
  175 + String[] versionedIds = getIdsFromParameters(client, requestMsg);
  176 + TbLwM2MReadCompositeRequest request = TbLwM2MReadCompositeRequest.builder().versionedIds(versionedIds).timeout(this.config.getTimeout()).build();
  177 + var mainCallback = new TbLwM2MReadCompositeCallback(uplinkHandler, logService, client, versionedIds);
  178 + var rpcCallback = new RpcReadResponseCompositeCallback(transportService, client, requestMsg, mainCallback);
  179 + downlinkHandler.sendReadCompositeRequest(client, request, rpcCallback);
  180 + }
  181 +
158 182 private void sendObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
159 183 TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build();
160 184 var mainCallback = new TbLwM2MObserveCallback(uplinkHandler, logService, client, versionedId);
161   - var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback);
  185 + var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, mainCallback);
162 186 downlinkHandler.sendObserveRequest(client, request, rpcCallback);
163 187 }
164 188
... ... @@ -215,6 +239,16 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
215 239 downlinkHandler.sendWriteReplaceRequest(client, request, rpcCallback);
216 240 }
217 241
  242 + private void sendWriteCompositeRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) {
  243 + RpcWriteCompositeRequest nodes = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteCompositeRequest.class);
  244 +// TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId)
  245 +// .value(requestBody.getValue())
  246 +// .timeout(this.config.getTimeout()).build();
  247 +// var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId);
  248 +// var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback);
  249 +// downlinkHandler.sendWriteReplaceRequest(client, request, rpcCallback);
  250 + }
  251 +
218 252 private void sendCancelObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) {
219 253 TbLwM2MCancelObserveRequest downlink = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build();
220 254 var mainCallback = new TbLwM2MCancelObserveCallback(logService, client, versionedId);
... ... @@ -249,6 +283,24 @@ public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler {
249 283 return targetId;
250 284 }
251 285
  286 + private String[] getIdsFromParameters(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequst) {
  287 + RpcReadCompositeRequest requestParams = JacksonUtil.fromString(rpcRequst.getParams(), RpcReadCompositeRequest.class);
  288 + if (requestParams.getKeys() != null && requestParams.getKeys().length > 0) {
  289 + Set targetIds = ConcurrentHashMap.newKeySet();
  290 + for (String key : requestParams.getKeys()) {
  291 + String targetId = clientContext.getObjectIdByKeyNameFromProfile(client, key);
  292 + if (targetId != null) {
  293 + targetIds.add(targetId);
  294 + }
  295 + }
  296 + return (String[]) targetIds.toArray(String[]::new);
  297 + } else if (requestParams.getIds() != null && requestParams.getIds().length > 0) {
  298 + return requestParams.getIds();
  299 + } else {
  300 + throw new IllegalArgumentException("Can't find 'key' or 'id' in the requestParams parameters!");
  301 + }
  302 + }
  303 +
252 304 private void sendErrorRpcResponse(TransportProtos.SessionInfoProto sessionInfo, int requestId, String result, String error) {
253 305 String payload = JacksonUtil.toString(JacksonUtil.newObjectNode().put("result", result).put("error", error));
254 306 TransportProtos.ToDeviceRpcResponseMsg msg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build();
... ...
... ... @@ -24,5 +24,4 @@ public class IdOrKeyRequest {
24 24
25 25 private String key;
26 26 private String id;
27   -
28 27 }
... ...
... ... @@ -16,13 +16,11 @@
16 16 package org.thingsboard.server.transport.lwm2m.server.rpc;
17 17
18 18 import org.eclipse.leshan.core.ResponseCode;
19   -import org.eclipse.leshan.core.node.codec.LwM2mValueConverter;
20 19 import org.thingsboard.common.util.JacksonUtil;
21 20 import org.thingsboard.server.common.transport.TransportService;
22 21 import org.thingsboard.server.gen.transport.TransportProtos;
23 22 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
24 23 import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback;
25   -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
26 24
27 25 public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkRequestCallback<R, T> {
28 26
... ... @@ -31,14 +29,12 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR
31 29 private final DownlinkRequestCallback<R, T> callback;
32 30
33 31 protected final LwM2mClient client;
34   - protected final LwM2mValueConverter converter;
35 32
36 33 public RpcDownlinkRequestCallbackProxy(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<R, T> callback) {
37 34 this.transportService = transportService;
38 35 this.client = client;
39 36 this.request = requestMsg;
40 37 this.callback = callback;
41   - this.converter = LwM2mValueConverterImpl.getInstance();
42 38 }
43 39
44 40 @Override
... ...
... ... @@ -29,22 +29,19 @@ import java.util.Optional;
29 29
30 30 public class RpcReadResponseCallback<R extends LwM2mRequest<T>, T extends ReadResponse> extends RpcLwM2MDownlinkCallback<R, T> {
31 31
32   - private final String versionedId;
33   -
34   - public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId, DownlinkRequestCallback<R, T> callback) {
  32 + public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<R, T> callback) {
35 33 super(transportService, client, requestMsg, callback);
36   - this.versionedId = versionedId;
37 34 }
38 35
39 36 @Override
40 37 protected Optional<String> serializeSuccessfulResponse(T response) {
41 38 Object value = null;
42 39 if (response.getContent() instanceof LwM2mObject) {
43   - value = client.objectToString((LwM2mObject) response.getContent(), this.converter, versionedId);
  40 + value = client.objectToString((LwM2mObject) response.getContent());
44 41 } else if (response.getContent() instanceof LwM2mObjectInstance) {
45   - value = client.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, versionedId);
  42 + value = client.instanceToString((LwM2mObjectInstance) response.getContent());
46 43 } else if (response.getContent() instanceof LwM2mResource) {
47   - value = client.resourceToString((LwM2mResource) response.getContent(), this.converter, versionedId);
  44 + value = client.resourceToString((LwM2mResource) response.getContent());
48 45 }
49 46 return Optional.of(String.format("%s", value));
50 47 }
... ...
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/rpc/composite/RpcReadCompositeRequest.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/client/LwM2mSoftwareUpdate.java
... ... @@ -13,15 +13,16 @@
13 13 * See the License for the specific language governing permissions and
14 14 * limitations under the License.
15 15 */
16   -package org.thingsboard.server.transport.lwm2m.client;
  16 +package org.thingsboard.server.transport.lwm2m.server.rpc.composite;
17 17
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
18 19 import lombok.Data;
19 20
20   -import java.util.UUID;
21   -
22 21 @Data
23   -public class LwM2mSoftwareUpdate {
24   - private volatile String clientSwVersion;
25   - private volatile String currentSwVersion;
26   - private volatile UUID currentSwId;
27   -}
\ No newline at end of file
  22 +@JsonIgnoreProperties(ignoreUnknown = true)
  23 +public class RpcReadCompositeRequest {
  24 +
  25 + private String [] keys;
  26 + private String [] ids;
  27 +
  28 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.rpc.composite;
  17 +
  18 +import org.eclipse.leshan.core.request.LwM2mRequest;
  19 +import org.eclipse.leshan.core.request.ReadCompositeRequest;
  20 +import org.eclipse.leshan.core.response.ReadCompositeResponse;
  21 +import org.thingsboard.server.common.transport.TransportService;
  22 +import org.thingsboard.server.gen.transport.TransportProtos;
  23 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
  24 +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback;
  25 +import org.thingsboard.server.transport.lwm2m.server.rpc.RpcLwM2MDownlinkCallback;
  26 +
  27 +import java.util.Optional;
  28 +
  29 +public class RpcReadResponseCompositeCallback<R extends LwM2mRequest<T>, T extends ReadCompositeResponse> extends RpcLwM2MDownlinkCallback<R, T> {
  30 +
  31 + public RpcReadResponseCompositeCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<R, T> callback) {
  32 + super(transportService, client, requestMsg, callback);
  33 + }
  34 +
  35 + @Override
  36 + protected Optional<String> serializeSuccessfulResponse(T response) {
  37 + return Optional.of(String.format("%s", response.getContent().toString()));
  38 + }
  39 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.rpc.composite;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  19 +import lombok.Data;
  20 +
  21 +import java.util.Map;
  22 +
  23 +@Data
  24 +@JsonIgnoreProperties(ignoreUnknown = true)
  25 +public class RpcWriteCompositeRequest {
  26 +
  27 + private Map<String, Object> nodes;
  28 +
  29 +}
... ...
... ... @@ -30,8 +30,10 @@ import org.eclipse.leshan.core.node.LwM2mResource;
30 30 import org.eclipse.leshan.core.observation.Observation;
31 31 import org.eclipse.leshan.core.request.ObserveRequest;
32 32 import org.eclipse.leshan.core.request.ReadRequest;
  33 +import org.eclipse.leshan.core.request.WriteCompositeRequest;
33 34 import org.eclipse.leshan.core.request.WriteRequest;
34 35 import org.eclipse.leshan.core.response.ObserveResponse;
  36 +import org.eclipse.leshan.core.response.ReadCompositeResponse;
35 37 import org.eclipse.leshan.core.response.ReadResponse;
36 38 import org.eclipse.leshan.server.registration.Registration;
37 39 import org.springframework.context.annotation.Lazy;
... ... @@ -89,6 +91,7 @@ import javax.annotation.PreDestroy;
89 91 import java.util.ArrayList;
90 92 import java.util.Collection;
91 93 import java.util.Collections;
  94 +import java.util.HashMap;
92 95 import java.util.HashSet;
93 96 import java.util.List;
94 97 import java.util.Map;
... ... @@ -315,6 +318,24 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
315 318 }
316 319 }
317 320
  321 + public void onUpdateValueAfterReadCompositeResponse(Registration registration, ReadCompositeResponse response) {
  322 + log.warn("201) ReadCompositeResponse: [{}]", response);
  323 + if (response.getContent() != null) {
  324 + LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint());
  325 + response.getContent().forEach((k, v) -> {
  326 + if (v != null) {
  327 + if (v instanceof LwM2mObject) {
  328 + this.updateObjectResourceValue(lwM2MClient, (LwM2mObject) v, k.toString());
  329 + } else if (v instanceof LwM2mObjectInstance) {
  330 + this.updateObjectInstanceResourceValue(lwM2MClient, (LwM2mObjectInstance) v, k.toString());
  331 + } else if (v instanceof LwM2mResource) {
  332 + this.updateResourcesValue(lwM2MClient, (LwM2mResource) v, k.toString());
  333 + }
  334 + }
  335 + });
  336 + }
  337 + }
  338 +
318 339 /**
319 340 * @param sessionInfo -
320 341 * @param deviceProfile -
... ... @@ -406,6 +427,17 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
406 427 if (supportedObjects != null && supportedObjects.size() > 0) {
407 428 // #1
408 429 this.sendReadRequests(lwM2MClient, profile, supportedObjects);
  430 + // test composite
  431 + String[] paths = new String[]{"/3/0", "/1/0", "/5/0"};
  432 +// String [] paths = new String[] {"/5"};
  433 +// String [] paths = new String[] {"/"};
  434 +// String [] paths = new String[] {"/9"};
  435 +// defaultLwM2MDownlinkMsgHandler.sendReadCompositeRequest(lwM2MClient, paths, this);
  436 + Map<String, Object> nodes = new HashMap<>();
  437 + nodes.put("/3/0/14", "+02");
  438 + nodes.put("/1/0/2", 100);
  439 + nodes.put("/5/0/1", "coap://localhost:5685");
  440 +// defaultLwM2MDownlinkMsgHandler.sendWriteCompositeRequest(lwM2MClient, nodes, this);
409 441 this.sendObserveRequests(lwM2MClient, profile, supportedObjects);
410 442 this.sendWriteAttributeRequests(lwM2MClient, profile, supportedObjects);
411 443 // Removed. Used only for debug.
... ... @@ -682,6 +714,14 @@ public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService impl
682 714 }
683 715 }
684 716
  717 + @Override
  718 + public void onWriteCompositeResponseOk(LwM2mClient client, WriteCompositeRequest request) {
  719 + log.warn("202) ReadCompositeResponse: [{}]", request.getNodes());
  720 + request.getNodes().forEach((k, v) -> {
  721 + this.updateResourcesValue(client, (LwM2mResource) v, k.toString());
  722 + });
  723 + }
  724 +
685 725 //TODO: review and optimize the logic to minimize number of the requests to device.
686 726 private void onDeviceProfileUpdate(List<LwM2mClient> clients, DeviceProfile deviceProfile) {
687 727 var oldProfile = clientContext.getProfile(deviceProfile.getUuidId());
... ...
... ... @@ -16,7 +16,9 @@
16 16 package org.thingsboard.server.transport.lwm2m.server.uplink;
17 17
18 18 import org.eclipse.leshan.core.observation.Observation;
  19 +import org.eclipse.leshan.core.request.WriteCompositeRequest;
19 20 import org.eclipse.leshan.core.request.WriteRequest;
  21 +import org.eclipse.leshan.core.response.ReadCompositeResponse;
20 22 import org.eclipse.leshan.core.response.ReadResponse;
21 23 import org.eclipse.leshan.server.registration.Registration;
22 24 import org.thingsboard.server.common.data.Device;
... ... @@ -40,6 +42,8 @@ public interface LwM2mUplinkMsgHandler {
40 42
41 43 void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response);
42 44
  45 + void onUpdateValueAfterReadCompositeResponse(Registration registration, ReadCompositeResponse response);
  46 +
43 47 void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile);
44 48
45 49 void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt);
... ... @@ -52,6 +56,8 @@ public interface LwM2mUplinkMsgHandler {
52 56
53 57 void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request);
54 58
  59 + void onWriteCompositeResponseOk(LwM2mClient client, WriteCompositeRequest request);
  60 +
55 61 void onToTransportUpdateCredentials(TransportProtos.SessionInfoProto sessionInfo, TransportProtos.ToTransportUpdateCredentialsProto updateCredentials);
56 62
57 63 LwM2MTransportServerConfig getConfig();
... ...