Commit 427ab87443b6920c89fcf9a2e87b1e95dca4b254
Committed by
GitHub
Merge pull request #4760 from thingsboard/feature/lwm2m-refactoring-downlink
Lwm2m Refactoring Downlink
Showing
97 changed files
with
5038 additions
and
3674 deletions
... | ... | @@ -677,12 +677,11 @@ transport: |
677 | 677 | timeout: "${LWM2M_TIMEOUT:120000}" |
678 | 678 | recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" |
679 | 679 | recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" |
680 | - response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}" | |
681 | - registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" | |
680 | + uplink_pool_size: "${LWM2M_UPLINK_POOL_SIZE:10}" | |
681 | + downlink_pool_size: "${LWM2M_DOWNLINK_POOL_SIZE:10}" | |
682 | + ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}" | |
682 | 683 | registration_store_pool_size: "${LWM2M_REGISTRATION_STORE_POOL_SIZE:100}" |
683 | 684 | clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}" |
684 | - update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" | |
685 | - un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" | |
686 | 685 | log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}" |
687 | 686 | # Use redis for Security and Registration stores |
688 | 687 | redis.enabled: "${LWM2M_REDIS_ENABLED:false}" | ... | ... |
... | ... | @@ -103,7 +103,9 @@ public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest { |
103 | 103 | " }\n" + |
104 | 104 | " },\n" + |
105 | 105 | " \"clientLwM2mSettings\": {\n" + |
106 | - " \"clientOnlyObserveAfterConnect\": 1\n" + | |
106 | + " \"clientOnlyObserveAfterConnect\": 1,\n" + | |
107 | + " \"fwUpdateStrategy\": 1,\n" + | |
108 | + " \"swUpdateStrategy\": 1\n" + | |
107 | 109 | " }\n" + |
108 | 110 | "}"; |
109 | 111 | ... | ... |
... | ... | @@ -17,11 +17,14 @@ package org.thingsboard.server.transport.lwm2m; |
17 | 17 | |
18 | 18 | import org.eclipse.californium.core.network.config.NetworkConfig; |
19 | 19 | import org.eclipse.leshan.client.object.Security; |
20 | -import org.jetbrains.annotations.NotNull; | |
21 | 20 | import org.junit.Assert; |
22 | 21 | import org.junit.Test; |
22 | +import org.springframework.mock.web.MockMultipartFile; | |
23 | +import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; | |
24 | +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; | |
23 | 25 | import org.thingsboard.common.util.JacksonUtil; |
24 | 26 | import org.thingsboard.server.common.data.Device; |
27 | +import org.thingsboard.server.common.data.OtaPackageInfo; | |
25 | 28 | import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials; |
26 | 29 | import org.thingsboard.server.common.data.query.EntityData; |
27 | 30 | import org.thingsboard.server.common.data.query.EntityDataPageLink; |
... | ... | @@ -43,6 +46,7 @@ import java.util.List; |
43 | 46 | |
44 | 47 | import static org.eclipse.leshan.client.object.Security.noSec; |
45 | 48 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
49 | +import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; | |
46 | 50 | |
47 | 51 | public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
48 | 52 | |
... | ... | @@ -51,7 +55,6 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
51 | 55 | private final NetworkConfig COAP_CONFIG = new NetworkConfig().setString("COAP_PORT", Integer.toString(PORT)); |
52 | 56 | private final String ENDPOINT = "deviceAEndpoint"; |
53 | 57 | |
54 | - @NotNull | |
55 | 58 | private Device createDevice() throws Exception { |
56 | 59 | Device device = new Device(); |
57 | 60 | device.setName("Device A"); |
... | ... | @@ -74,6 +77,29 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
74 | 77 | return device; |
75 | 78 | } |
76 | 79 | |
80 | + private OtaPackageInfo createFirmware() throws Exception { | |
81 | + String CHECKSUM = "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"; | |
82 | + | |
83 | + OtaPackageInfo firmwareInfo = new OtaPackageInfo(); | |
84 | + firmwareInfo.setDeviceProfileId(deviceProfile.getId()); | |
85 | + firmwareInfo.setType(FIRMWARE); | |
86 | + firmwareInfo.setTitle("My firmware"); | |
87 | + firmwareInfo.setVersion("v1.0"); | |
88 | + | |
89 | + OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class); | |
90 | + | |
91 | + MockMultipartFile testData = new MockMultipartFile("file", "filename.txt", "text/plain", new byte[]{1}); | |
92 | + | |
93 | + return savaData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksum={checksum}&checksumAlgorithm={checksumAlgorithm}", testData, CHECKSUM, "SHA256"); | |
94 | + } | |
95 | + | |
96 | + protected OtaPackageInfo savaData(String urlTemplate, MockMultipartFile content, String... params) throws Exception { | |
97 | + MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params); | |
98 | + postRequest.file(content); | |
99 | + setJwtToken(postRequest); | |
100 | + return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class); | |
101 | + } | |
102 | + | |
77 | 103 | @Test |
78 | 104 | public void testConnectAndObserveTelemetry() throws Exception { |
79 | 105 | createDeviceProfile(TRANSPORT_CONFIGURATION); |
... | ... | @@ -111,4 +137,53 @@ public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
111 | 137 | client.destroy(); |
112 | 138 | } |
113 | 139 | |
140 | + @Test | |
141 | + public void testFirmwareUpdateWithClientWithoutFirmwareInfo() throws Exception { | |
142 | + createDeviceProfile(TRANSPORT_CONFIGURATION); | |
143 | + | |
144 | + Device device = createDevice(); | |
145 | + | |
146 | + OtaPackageInfo firmware = createFirmware(); | |
147 | + | |
148 | + LwM2MTestClient client = new LwM2MTestClient(executor, ENDPOINT); | |
149 | + client.init(SECURITY, COAP_CONFIG); | |
150 | + | |
151 | + Thread.sleep(1000); | |
152 | + | |
153 | + device.setFirmwareId(firmware.getId()); | |
154 | + | |
155 | + device = doPost("/api/device", device, Device.class); | |
156 | + | |
157 | + Thread.sleep(1000); | |
158 | + | |
159 | + SingleEntityFilter sef = new SingleEntityFilter(); | |
160 | + sef.setSingleEntity(device.getId()); | |
161 | + LatestValueCmd latestCmd = new LatestValueCmd(); | |
162 | + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "fw_state"))); | |
163 | + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null), | |
164 | + Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); | |
165 | + | |
166 | + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null); | |
167 | + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); | |
168 | + wrapper.setEntityDataCmds(Collections.singletonList(cmd)); | |
169 | + | |
170 | + wsClient.send(mapper.writeValueAsString(wrapper)); | |
171 | + wsClient.waitForReply(); | |
172 | + | |
173 | + wsClient.registerWaitForUpdate(); | |
174 | + | |
175 | + String msg = wsClient.waitForUpdate(); | |
176 | + | |
177 | + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); | |
178 | + Assert.assertEquals(1, update.getCmdId()); | |
179 | + List<EntityData> eData = update.getUpdate(); | |
180 | + Assert.assertNotNull(eData); | |
181 | + Assert.assertEquals(1, eData.size()); | |
182 | + Assert.assertEquals(device.getId(), eData.get(0).getEntityId()); | |
183 | + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES)); | |
184 | + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("fw_state"); | |
185 | + Assert.assertEquals("FAILED", tsValue.getValue()); | |
186 | + client.destroy(); | |
187 | + } | |
188 | + | |
114 | 189 | } | ... | ... |
... | ... | @@ -52,7 +52,6 @@ public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { |
52 | 52 | private final String endpoint = "deviceAEndpoint"; |
53 | 53 | private final String serverUri = "coaps://localhost:" + port; |
54 | 54 | |
55 | - @NotNull | |
56 | 55 | private Device createDevice(X509ClientCredentials clientCredentials) throws Exception { |
57 | 56 | Device device = new Device(); |
58 | 57 | device.setName("Device A"); | ... | ... |
... | ... | @@ -19,8 +19,10 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter; |
19 | 19 | import com.fasterxml.jackson.annotation.JsonAnySetter; |
20 | 20 | import com.fasterxml.jackson.annotation.JsonIgnore; |
21 | 21 | import lombok.Data; |
22 | -import org.thingsboard.server.common.data.DeviceProfileType; | |
23 | 22 | import org.thingsboard.server.common.data.DeviceTransportType; |
23 | +import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; | |
24 | +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration; | |
25 | +import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; | |
24 | 26 | |
25 | 27 | import java.util.HashMap; |
26 | 28 | import java.util.Map; | ... | ... |
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.common.data.device.data.lwm2m; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +import java.util.Map; | |
21 | + | |
22 | +@Data | |
23 | +public class BootstrapConfiguration { | |
24 | + | |
25 | + //TODO: define the objects; | |
26 | + private Map<String, Object> servers; | |
27 | + private Map<String, Object> lwm2mServer; | |
28 | + private Map<String, Object> bootstrapServer; | |
29 | + | |
30 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/device/data/lwm2m/ObjectAttributes.java
0 → 100644
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.common.data.device.data.lwm2m; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonInclude; | |
19 | +import lombok.Data; | |
20 | + | |
21 | +@Data | |
22 | +@JsonInclude(JsonInclude.Include.NON_NULL) | |
23 | +public class ObjectAttributes { | |
24 | + | |
25 | + private Long dim; | |
26 | + private String ver; | |
27 | + private Long pmin; | |
28 | + private Long pmax; | |
29 | + private Double gt; | |
30 | + private Double lt; | |
31 | + private Double st; | |
32 | + | |
33 | +} | ... | ... |
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.common.data.device.data.lwm2m; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +@Data | |
21 | +public class OtherConfiguration { | |
22 | + | |
23 | + private Integer fwUpdateStrategy; | |
24 | + private Integer swUpdateStrategy; | |
25 | + private Integer clientOnlyObserveAfterConnect; | |
26 | + private String fwUpdateRecourse; | |
27 | + private String swUpdateRecourse; | |
28 | + | |
29 | +} | ... | ... |
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.common.data.device.data.lwm2m; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +import java.util.Map; | |
21 | +import java.util.Set; | |
22 | + | |
23 | +@Data | |
24 | +public class TelemetryMappingConfiguration { | |
25 | + | |
26 | + private Map<String, String> keyName; | |
27 | + private Set<String> observe; | |
28 | + private Set<String> attribute; | |
29 | + private Set<String> telemetry; | |
30 | + private Map<String, ObjectAttributes> attributeLwm2m; | |
31 | + | |
32 | +} | ... | ... |
... | ... | @@ -15,31 +15,20 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.common.data.device.profile; |
17 | 17 | |
18 | -import com.fasterxml.jackson.annotation.JsonAnyGetter; | |
19 | -import com.fasterxml.jackson.annotation.JsonAnySetter; | |
20 | -import com.fasterxml.jackson.annotation.JsonIgnore; | |
21 | 18 | import lombok.Data; |
22 | -import org.thingsboard.server.common.data.DeviceProfileType; | |
23 | 19 | import org.thingsboard.server.common.data.DeviceTransportType; |
24 | - | |
25 | -import java.util.HashMap; | |
26 | -import java.util.Map; | |
20 | +import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; | |
21 | +import org.thingsboard.server.common.data.device.data.lwm2m.OtherConfiguration; | |
22 | +import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; | |
27 | 23 | |
28 | 24 | @Data |
29 | 25 | public class Lwm2mDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { |
30 | 26 | |
31 | - @JsonIgnore | |
32 | - private Map<String, Object> properties = new HashMap<>(); | |
33 | - | |
34 | - @JsonAnyGetter | |
35 | - public Map<String, Object> properties() { | |
36 | - return this.properties; | |
37 | - } | |
27 | + private static final long serialVersionUID = 6257277825459600068L; | |
38 | 28 | |
39 | - @JsonAnySetter | |
40 | - public void put(String name, Object value) { | |
41 | - this.properties.put(name, value); | |
42 | - } | |
29 | + private TelemetryMappingConfiguration observeAttr; | |
30 | + private BootstrapConfiguration bootstrap; | |
31 | + private OtherConfiguration clientLwM2mSettings; | |
43 | 32 | |
44 | 33 | @Override |
45 | 34 | public DeviceTransportType getType() { | ... | ... |
... | ... | @@ -134,8 +134,8 @@ public class OtaPackageTransportResource extends AbstractCoapTransportResource { |
134 | 134 | response.setPayload(data); |
135 | 135 | if (exchange.getRequestOptions().getBlock2() != null) { |
136 | 136 | int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); |
137 | - boolean moreFlag = data.length > chunkSize; | |
138 | - response.getOptions().setBlock2(chunkSize, moreFlag, 0); | |
137 | + boolean lastFlag = data.length > chunkSize; | |
138 | + response.getOptions().setBlock2(chunkSize, lastFlag, 0); | |
139 | 139 | } |
140 | 140 | exchange.respond(response); |
141 | 141 | } | ... | ... |
... | ... | @@ -15,9 +15,6 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.transport.lwm2m.bootstrap.secure; |
17 | 17 | |
18 | -import com.fasterxml.jackson.core.JsonProcessingException; | |
19 | -import com.fasterxml.jackson.databind.ObjectMapper; | |
20 | -import com.google.gson.JsonObject; | |
21 | 18 | import lombok.extern.slf4j.Slf4j; |
22 | 19 | import org.eclipse.leshan.core.SecurityMode; |
23 | 20 | import org.eclipse.leshan.core.util.Hex; |
... | ... | @@ -29,6 +26,8 @@ import org.eclipse.leshan.server.security.BootstrapSecurityStore; |
29 | 26 | import org.eclipse.leshan.server.security.SecurityInfo; |
30 | 27 | import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; |
31 | 28 | import org.springframework.stereotype.Service; |
29 | +import org.thingsboard.common.util.JacksonUtil; | |
30 | +import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; | |
32 | 31 | import org.thingsboard.server.gen.transport.TransportProtos; |
33 | 32 | import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; |
34 | 33 | import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator; |
... | ... | @@ -43,12 +42,9 @@ import java.util.Collections; |
43 | 42 | import java.util.Iterator; |
44 | 43 | import java.util.UUID; |
45 | 44 | |
46 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.BOOTSTRAP_SERVER; | |
47 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; | |
48 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; | |
49 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; | |
50 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LWM2M_SERVER; | |
51 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SERVERS; | |
45 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; | |
46 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; | |
47 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; | |
52 | 48 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getBootstrapParametersFromThingsboard; |
53 | 49 | |
54 | 50 | @Slf4j |
... | ... | @@ -151,35 +147,30 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore { |
151 | 147 | } |
152 | 148 | |
153 | 149 | private LwM2MBootstrapConfig getParametersBootstrap(TbLwM2MSecurityInfo store) { |
154 | - try { | |
155 | - LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); | |
156 | - if (lwM2MBootstrapConfig != null) { | |
157 | - ObjectMapper mapper = new ObjectMapper(); | |
158 | - JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); | |
159 | - lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class); | |
160 | - LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class); | |
161 | - LwM2MServerBootstrap profileLwm2mServer = mapper.readValue(bootstrapObject.get(LWM2M_SERVER).toString(), LwM2MServerBootstrap.class); | |
162 | - UUID sessionUUiD = UUID.randomUUID(); | |
163 | - TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); | |
164 | - context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, sessionInfo)); | |
165 | - if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { | |
166 | - lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); | |
167 | - lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); | |
168 | - String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndpoint()); | |
169 | - helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); | |
170 | - return lwM2MBootstrapConfig; | |
171 | - } else { | |
172 | - log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint()); | |
173 | - log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint()); | |
174 | - String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint()); | |
175 | - helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo); | |
176 | - return null; | |
177 | - } | |
150 | + LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig(); | |
151 | + if (lwM2MBootstrapConfig != null) { | |
152 | + BootstrapConfiguration bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile()); | |
153 | + lwM2MBootstrapConfig.servers = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getServers()), LwM2MBootstrapServers.class); | |
154 | + LwM2MServerBootstrap profileServerBootstrap = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getBootstrapServer()), LwM2MServerBootstrap.class); | |
155 | + LwM2MServerBootstrap profileLwm2mServer = JacksonUtil.fromString(JacksonUtil.toString(bootstrapObject.getLwm2mServer()), LwM2MServerBootstrap.class); | |
156 | + UUID sessionUUiD = UUID.randomUUID(); | |
157 | + TransportProtos.SessionInfoProto sessionInfo = helper.getValidateSessionInfo(store.getMsg(), sessionUUiD.getMostSignificantBits(), sessionUUiD.getLeastSignificantBits()); | |
158 | + context.getTransportService().registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(null, null, null, sessionInfo)); | |
159 | + if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) { | |
160 | + lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap); | |
161 | + lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer); | |
162 | + String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LWM2M_INFO, store.getEndpoint()); | |
163 | + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); | |
164 | + return lwM2MBootstrapConfig; | |
165 | + } else { | |
166 | + log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint()); | |
167 | + log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LWM2M_ERROR, store.getEndpoint()); | |
168 | + String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LWM2M_ERROR, store.getEndpoint()); | |
169 | + helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), sessionInfo); | |
170 | + return null; | |
178 | 171 | } |
179 | - } catch (JsonProcessingException e) { | |
180 | - log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndpoint(), e.getMessage()); | |
181 | - return null; | |
182 | 172 | } |
173 | + | |
183 | 174 | log.error("Unable to decode Json or Certificate for [{}]", store.getEndpoint()); |
184 | 175 | return null; |
185 | 176 | } | ... | ... |
... | ... | @@ -57,30 +57,22 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig { |
57 | 57 | private boolean recommendedSupportedGroups; |
58 | 58 | |
59 | 59 | @Getter |
60 | - @Value("${transport.lwm2m.response_pool_size:}") | |
61 | - private int responsePoolSize; | |
60 | + @Value("${transport.lwm2m.downlink_pool_size:}") | |
61 | + private int downlinkPoolSize; | |
62 | 62 | |
63 | 63 | @Getter |
64 | - @Value("${transport.lwm2m.registered_pool_size:}") | |
65 | - private int registeredPoolSize; | |
64 | + @Value("${transport.lwm2m.uplink_pool_size:}") | |
65 | + private int uplinkPoolSize; | |
66 | 66 | |
67 | 67 | @Getter |
68 | - @Value("${transport.lwm2m.registration_store_pool_size:}") | |
69 | - private int registrationStorePoolSize; | |
68 | + @Value("${transport.lwm2m.ota_pool_size:}") | |
69 | + private int otaPoolSize; | |
70 | 70 | |
71 | 71 | @Getter |
72 | 72 | @Value("${transport.lwm2m.clean_period_in_sec:}") |
73 | 73 | private int cleanPeriodInSec; |
74 | 74 | |
75 | 75 | @Getter |
76 | - @Value("${transport.lwm2m.update_registered_pool_size:}") | |
77 | - private int updateRegisteredPoolSize; | |
78 | - | |
79 | - @Getter | |
80 | - @Value("${transport.lwm2m.un_registered_pool_size:}") | |
81 | - private int unRegisteredPoolSize; | |
82 | - | |
83 | - @Getter | |
84 | 76 | @Value("${transport.lwm2m.security.key_store_type:}") |
85 | 77 | private String keyStoreType; |
86 | 78 | ... | ... |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/DefaultLwM2MTransportMsgHandler.java
deleted
100644 → 0
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; | |
17 | - | |
18 | -import com.fasterxml.jackson.core.type.TypeReference; | |
19 | -import com.google.gson.Gson; | |
20 | -import com.google.gson.GsonBuilder; | |
21 | -import com.google.gson.JsonArray; | |
22 | -import com.google.gson.JsonElement; | |
23 | -import com.google.gson.JsonObject; | |
24 | -import com.google.gson.reflect.TypeToken; | |
25 | -import lombok.extern.slf4j.Slf4j; | |
26 | -import org.apache.commons.lang3.StringUtils; | |
27 | -import org.eclipse.leshan.core.model.ObjectModel; | |
28 | -import org.eclipse.leshan.core.model.ResourceModel; | |
29 | -import org.eclipse.leshan.core.node.LwM2mObject; | |
30 | -import org.eclipse.leshan.core.node.LwM2mObjectInstance; | |
31 | -import org.eclipse.leshan.core.node.LwM2mPath; | |
32 | -import org.eclipse.leshan.core.node.LwM2mResource; | |
33 | -import org.eclipse.leshan.core.observation.Observation; | |
34 | -import org.eclipse.leshan.core.request.WriteRequest; | |
35 | -import org.eclipse.leshan.core.response.ReadResponse; | |
36 | -import org.eclipse.leshan.server.registration.Registration; | |
37 | -import org.springframework.context.annotation.Lazy; | |
38 | -import org.springframework.stereotype.Service; | |
39 | -import org.thingsboard.common.util.JacksonUtil; | |
40 | -import org.thingsboard.common.util.ThingsBoardExecutors; | |
41 | -import org.thingsboard.server.cache.ota.OtaPackageDataCache; | |
42 | -import org.thingsboard.server.common.data.Device; | |
43 | -import org.thingsboard.server.common.data.DeviceProfile; | |
44 | -import org.thingsboard.server.common.data.id.OtaPackageId; | |
45 | -import org.thingsboard.server.common.data.ota.OtaPackageKey; | |
46 | -import org.thingsboard.server.common.data.ota.OtaPackageType; | |
47 | -import org.thingsboard.server.common.data.ota.OtaPackageUtil; | |
48 | -import org.thingsboard.server.common.transport.TransportService; | |
49 | -import org.thingsboard.server.common.transport.TransportServiceCallback; | |
50 | -import org.thingsboard.server.common.transport.adaptor.AdaptorException; | |
51 | -import org.thingsboard.server.common.transport.service.DefaultTransportService; | |
52 | -import org.thingsboard.server.gen.transport.TransportProtos; | |
53 | -import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; | |
54 | -import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; | |
55 | -import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | |
56 | -import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | |
57 | -import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | |
58 | -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; | |
59 | -import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; | |
60 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; | |
61 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; | |
62 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
63 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | |
64 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; | |
65 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; | |
66 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mFwSwUpdate; | |
67 | -import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; | |
68 | -import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; | |
69 | -import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters; | |
70 | -import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; | |
71 | -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; | |
72 | - | |
73 | -import javax.annotation.PostConstruct; | |
74 | -import java.util.ArrayList; | |
75 | -import java.util.Collection; | |
76 | -import java.util.Collections; | |
77 | -import java.util.HashSet; | |
78 | -import java.util.List; | |
79 | -import java.util.Map; | |
80 | -import java.util.Optional; | |
81 | -import java.util.Random; | |
82 | -import java.util.Set; | |
83 | -import java.util.UUID; | |
84 | -import java.util.concurrent.ConcurrentHashMap; | |
85 | -import java.util.concurrent.ConcurrentMap; | |
86 | -import java.util.concurrent.ExecutorService; | |
87 | -import java.util.concurrent.TimeUnit; | |
88 | -import java.util.stream.Collectors; | |
89 | - | |
90 | -import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; | |
91 | -import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; | |
92 | -import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; | |
93 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; | |
94 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; | |
95 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; | |
96 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEVICE_ATTRIBUTES_REQUEST; | |
97 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_ID; | |
98 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; | |
99 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; | |
100 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; | |
101 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; | |
102 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_TELEMETRY; | |
103 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; | |
104 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_WARN; | |
105 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; | |
106 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; | |
107 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; | |
108 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL_ALL; | |
109 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.READ; | |
110 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; | |
111 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; | |
112 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_ID; | |
113 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertJsonArrayToSet; | |
114 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; | |
115 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; | |
116 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; | |
117 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getAckCallback; | |
118 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.isFwSwWords; | |
119 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.setValidTypeOper; | |
120 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; | |
121 | - | |
122 | - | |
123 | -@Slf4j | |
124 | -@Service | |
125 | -@TbLwM2mTransportComponent | |
126 | -public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler { | |
127 | - | |
128 | - private ExecutorService registrationExecutor; | |
129 | - private ExecutorService updateRegistrationExecutor; | |
130 | - private ExecutorService unRegistrationExecutor; | |
131 | - public LwM2mValueConverterImpl converter; | |
132 | - | |
133 | - private final TransportService transportService; | |
134 | - private final LwM2mTransportContext context; | |
135 | - public final LwM2MTransportServerConfig config; | |
136 | - public final OtaPackageDataCache otaPackageDataCache; | |
137 | - public final LwM2mTransportServerHelper helper; | |
138 | - private final LwM2MJsonAdaptor adaptor; | |
139 | - private final TbLwM2MDtlsSessionStore sessionStore; | |
140 | - public final LwM2mClientContext clientContext; | |
141 | - public final LwM2mTransportRequest lwM2mTransportRequest; | |
142 | - private final Map<UUID, Long> rpcSubscriptions; | |
143 | - public final Map<String, Integer> firmwareUpdateState; | |
144 | - | |
145 | - public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper, | |
146 | - LwM2mClientContext clientContext, | |
147 | - @Lazy LwM2mTransportRequest lwM2mTransportRequest, | |
148 | - OtaPackageDataCache otaPackageDataCache, | |
149 | - LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) { | |
150 | - this.transportService = transportService; | |
151 | - this.config = config; | |
152 | - this.helper = helper; | |
153 | - this.clientContext = clientContext; | |
154 | - this.lwM2mTransportRequest = lwM2mTransportRequest; | |
155 | - this.otaPackageDataCache = otaPackageDataCache; | |
156 | - this.context = context; | |
157 | - this.adaptor = adaptor; | |
158 | - this.rpcSubscriptions = new ConcurrentHashMap<>(); | |
159 | - this.firmwareUpdateState = new ConcurrentHashMap<>(); | |
160 | - this.sessionStore = sessionStore; | |
161 | - } | |
162 | - | |
163 | - @PostConstruct | |
164 | - public void init() { | |
165 | - this.context.getScheduler().scheduleAtFixedRate(this::reportActivity, new Random().nextInt((int) config.getSessionReportTimeout()), config.getSessionReportTimeout(), TimeUnit.MILLISECONDS); | |
166 | - this.registrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getRegisteredPoolSize(), "LwM2M registration"); | |
167 | - this.updateRegistrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getUpdateRegisteredPoolSize(), "LwM2M update registration"); | |
168 | - this.unRegistrationExecutor = ThingsBoardExecutors.newWorkStealingPool(this.config.getUnRegisteredPoolSize(), "LwM2M unRegistration"); | |
169 | - this.converter = LwM2mValueConverterImpl.getInstance(); | |
170 | - } | |
171 | - | |
172 | - /** | |
173 | - * Start registration device | |
174 | - * Create session: Map<String <registrationId >, LwM2MClient> | |
175 | - * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint) | |
176 | - * 1.1 When we initialize the registration, we register the session by endpoint. | |
177 | - * 1.2 If the server has incomplete requests (canceling the registration of the previous session), | |
178 | - * delete the previous session only by the previous registration.getId | |
179 | - * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId | |
180 | - * 1.2 Remove from sessions Model by enpPoint | |
181 | - * Next -> Create new LwM2MClient for current session -> setModelClient... | |
182 | - * | |
183 | - * @param registration - Registration LwM2M Client | |
184 | - * @param previousObservations - may be null | |
185 | - */ | |
186 | - public void onRegistered(Registration registration, Collection<Observation> previousObservations) { | |
187 | - registrationExecutor.submit(() -> { | |
188 | - LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); | |
189 | - try { | |
190 | - log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); | |
191 | - if (lwM2MClient != null) { | |
192 | - this.clientContext.register(lwM2MClient, registration); | |
193 | - this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_INFO + ": Client registered with registration id: " + registration.getId()); | |
194 | - SessionInfoProto sessionInfo = lwM2MClient.getSession(); | |
195 | - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); | |
196 | - log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); | |
197 | - TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() | |
198 | - .setSessionInfo(sessionInfo) | |
199 | - .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN)) | |
200 | - .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build()) | |
201 | - .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build()) | |
202 | - .build(); | |
203 | - transportService.process(msg, null); | |
204 | - this.getInfoFirmwareUpdate(lwM2MClient, null); | |
205 | - this.getInfoSoftwareUpdate(lwM2MClient, null); | |
206 | - this.initClientTelemetry(lwM2MClient); | |
207 | - } else { | |
208 | - log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); | |
209 | - } | |
210 | - } catch (LwM2MClientStateException stateException) { | |
211 | - if (LwM2MClientState.UNREGISTERED.equals(stateException.getState())) { | |
212 | - log.info("[{}] retry registration due to race condition: [{}].", registration.getEndpoint(), stateException.getState()); | |
213 | - // Race condition detected and the client was in progress of unregistration while new registration arrived. Let's try again. | |
214 | - onRegistered(registration, previousObservations); | |
215 | - } else { | |
216 | - this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState()); | |
217 | - } | |
218 | - } catch (Throwable t) { | |
219 | - log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); | |
220 | - this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_WARN + ": Client registration failed due to: " + t.getMessage()); | |
221 | - } | |
222 | - }); | |
223 | - } | |
224 | - | |
225 | - /** | |
226 | - * if sessionInfo removed from sessions, then new registerAsyncSession | |
227 | - * | |
228 | - * @param registration - Registration LwM2M Client | |
229 | - */ | |
230 | - public void updatedReg(Registration registration) { | |
231 | - updateRegistrationExecutor.submit(() -> { | |
232 | - LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
233 | - try { | |
234 | - clientContext.updateRegistration(lwM2MClient, registration); | |
235 | - TransportProtos.SessionInfoProto sessionInfo = lwM2MClient.getSession(); | |
236 | - this.reportActivityAndRegister(sessionInfo); | |
237 | - if (registration.usesQueueMode()) { | |
238 | - LwM2mQueuedRequest request; | |
239 | - while ((request = lwM2MClient.getQueuedRequests().poll()) != null) { | |
240 | - request.send(); | |
241 | - } | |
242 | - } | |
243 | - } catch (LwM2MClientStateException stateException) { | |
244 | - if (LwM2MClientState.REGISTERED.equals(stateException.getState())) { | |
245 | - log.info("[{}] update registration failed because client has different registration id: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage()); | |
246 | - } else { | |
247 | - onRegistered(registration, Collections.emptyList()); | |
248 | - } | |
249 | - } catch (Throwable t) { | |
250 | - log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); | |
251 | - this.sendLogsToThingsboard(lwM2MClient, LOG_LW2M_ERROR + String.format(": Client update Registration, %s", t.getMessage())); | |
252 | - } | |
253 | - }); | |
254 | - } | |
255 | - | |
256 | - /** | |
257 | - * @param registration - Registration LwM2M Client | |
258 | - * @param observations - !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect | |
259 | - */ | |
260 | - public void unReg(Registration registration, Collection<Observation> observations) { | |
261 | - unRegistrationExecutor.submit(() -> { | |
262 | - LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
263 | - try { | |
264 | - this.sendLogsToThingsboard(client, LOG_LW2M_INFO + ": Client unRegistration"); | |
265 | - clientContext.unregister(client, registration); | |
266 | - SessionInfoProto sessionInfo = client.getSession(); | |
267 | - if (sessionInfo != null) { | |
268 | - this.doCloseSession(sessionInfo); | |
269 | - transportService.deregisterSession(sessionInfo); | |
270 | - sessionStore.remove(registration.getEndpoint()); | |
271 | - log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); | |
272 | - } else { | |
273 | - log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); | |
274 | - } | |
275 | - } catch (LwM2MClientStateException stateException) { | |
276 | - log.info("[{}] delete registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage()); | |
277 | - } catch (Throwable t) { | |
278 | - log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); | |
279 | - this.sendLogsToThingsboard(client, LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage())); | |
280 | - } | |
281 | - }); | |
282 | - } | |
283 | - | |
284 | - @Override | |
285 | - public void onSleepingDev(Registration registration) { | |
286 | - log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); | |
287 | - this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is sleeping!"); | |
288 | - //TODO: associate endpointId with device information. | |
289 | - } | |
290 | - | |
291 | - /** | |
292 | - * Cancel observation for All objects for this registration | |
293 | - */ | |
294 | - @Override | |
295 | - public void setCancelObservationsAll(Registration registration) { | |
296 | - if (registration != null) { | |
297 | - LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
298 | - if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) { | |
299 | - this.lwM2mTransportRequest.sendAllRequest(client, null, OBSERVE_CANCEL_ALL, | |
300 | - null, null, this.config.getTimeout(), null); | |
301 | - } | |
302 | - } | |
303 | - } | |
304 | - | |
305 | - /** | |
306 | - * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource | |
307 | - * | |
308 | - * @param registration - Registration LwM2M Client | |
309 | - * @param path - observe | |
310 | - * @param response - observe | |
311 | - */ | |
312 | - @Override | |
313 | - public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, LwM2mClientRpcRequest rpcRequest) { | |
314 | - if (response.getContent() != null) { | |
315 | - LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
316 | - ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider()); | |
317 | - if (objectModelVersion != null) { | |
318 | - if (response.getContent() instanceof LwM2mObject) { | |
319 | - LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); | |
320 | - this.updateObjectResourceValue(registration, lwM2mObject, path); | |
321 | - } else if (response.getContent() instanceof LwM2mObjectInstance) { | |
322 | - LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent(); | |
323 | - this.updateObjectInstanceResourceValue(registration, lwM2mObjectInstance, path); | |
324 | - } else if (response.getContent() instanceof LwM2mResource) { | |
325 | - LwM2mResource lwM2mResource = (LwM2mResource) response.getContent(); | |
326 | - this.updateResourcesValue(registration, lwM2mResource, path); | |
327 | - } | |
328 | - } | |
329 | - if (rpcRequest != null) { | |
330 | - this.sendRpcRequestAfterReadResponse(registration, lwM2MClient, path, response, rpcRequest); | |
331 | - } | |
332 | - } | |
333 | - } | |
334 | - | |
335 | - private void sendRpcRequestAfterReadResponse(Registration registration, LwM2mClient lwM2MClient, String pathIdVer, ReadResponse response, | |
336 | - LwM2mClientRpcRequest rpcRequest) { | |
337 | - Object value = null; | |
338 | - if (response.getContent() instanceof LwM2mObject) { | |
339 | - value = lwM2MClient.objectToString((LwM2mObject) response.getContent(), this.converter, pathIdVer); | |
340 | - } else if (response.getContent() instanceof LwM2mObjectInstance) { | |
341 | - value = lwM2MClient.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, pathIdVer); | |
342 | - } else if (response.getContent() instanceof LwM2mResource) { | |
343 | - value = lwM2MClient.resourceToString((LwM2mResource) response.getContent(), this.converter, pathIdVer); | |
344 | - } | |
345 | - String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO, | |
346 | - READ, pathIdVer, value); | |
347 | - this.sendLogsToThingsboard(lwM2MClient, msg); | |
348 | - rpcRequest.setValueMsg(String.format("%s", value)); | |
349 | - this.sentRpcResponse(rpcRequest, response.getCode().getName(), (String) value, LOG_LW2M_VALUE); | |
350 | - } | |
351 | - | |
352 | - /** | |
353 | - * Update - send request in change value resources in Client | |
354 | - * 1. FirmwareUpdate: | |
355 | - * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 | |
356 | - * 2. Shared Other AttributeUpdate | |
357 | - * -- Path to resources from profile equal keyName or from ModelObject equal name | |
358 | - * -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) | |
359 | - * 3. Delete - nothing | |
360 | - * | |
361 | - * @param msg - | |
362 | - */ | |
363 | - @Override | |
364 | - public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { | |
365 | - LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); | |
366 | - if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { | |
367 | - log.warn("2) OnAttributeUpdate, SharedUpdatedList() [{}]", msg.getSharedUpdatedList()); | |
368 | - msg.getSharedUpdatedList().forEach(tsKvProto -> { | |
369 | - String pathName = tsKvProto.getKv().getKey(); | |
370 | - String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); | |
371 | - Object valueNew = getValueFromKvProto(tsKvProto.getKv()); | |
372 | - if ((OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) | |
373 | - && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion()))) | |
374 | - || (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE).equals(pathName) | |
375 | - && (!valueNew.equals(lwM2MClient.getFwUpdate().getCurrentTitle())))) { | |
376 | - this.getInfoFirmwareUpdate(lwM2MClient, null); | |
377 | - } else if ((OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION).equals(pathName) | |
378 | - && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentVersion()))) | |
379 | - || (OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE).equals(pathName) | |
380 | - && (!valueNew.equals(lwM2MClient.getSwUpdate().getCurrentTitle())))) { | |
381 | - this.getInfoSoftwareUpdate(lwM2MClient, null); | |
382 | - } | |
383 | - if (pathIdVer != null) { | |
384 | - ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.config | |
385 | - .getModelProvider()); | |
386 | - if (resourceModel != null && resourceModel.operations.isWritable()) { | |
387 | - this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), valueNew, pathIdVer); | |
388 | - } else { | |
389 | - log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); | |
390 | - String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", | |
391 | - LOG_LW2M_ERROR, pathIdVer, valueNew); | |
392 | - this.sendLogsToThingsboard(lwM2MClient, logMsg); | |
393 | - } | |
394 | - } else if (!isFwSwWords(pathName)) { | |
395 | - log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); | |
396 | - String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", | |
397 | - LOG_LW2M_ERROR, pathName, valueNew); | |
398 | - this.sendLogsToThingsboard(lwM2MClient, logMsg); | |
399 | - } | |
400 | - | |
401 | - }); | |
402 | - } else if (msg.getSharedDeletedCount() > 0 && lwM2MClient != null) { | |
403 | - msg.getSharedUpdatedList().forEach(tsKvProto -> { | |
404 | - String pathName = tsKvProto.getKv().getKey(); | |
405 | - Object valueNew = getValueFromKvProto(tsKvProto.getKv()); | |
406 | - if (OtaPackageUtil.getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFwUpdate().getCurrentVersion())) { | |
407 | - lwM2MClient.getFwUpdate().setCurrentVersion((String) valueNew); | |
408 | - } | |
409 | - }); | |
410 | - log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); | |
411 | - } else if (lwM2MClient == null) { | |
412 | - log.error("OnAttributeUpdate, lwM2MClient is null"); | |
413 | - } | |
414 | - } | |
415 | - | |
416 | - /** | |
417 | - * @param sessionInfo - | |
418 | - * @param deviceProfile - | |
419 | - */ | |
420 | - @Override | |
421 | - public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) { | |
422 | - List<LwM2mClient> clients = clientContext.getLwM2mClients() | |
423 | - .stream().filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toList()); | |
424 | - clients.forEach(client -> client.onDeviceProfileUpdate(deviceProfile)); | |
425 | - if (clients.size() > 0) { | |
426 | - this.onDeviceProfileUpdate(clients, deviceProfile); | |
427 | - } | |
428 | - } | |
429 | - | |
430 | - @Override | |
431 | - public void onDeviceUpdate(SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) { | |
432 | - //TODO: check, maybe device has multiple sessions/registrations? Is this possible according to the standard. | |
433 | - LwM2mClient client = clientContext.getClientByDeviceId(device.getUuidId()); | |
434 | - if (client != null) { | |
435 | - this.onDeviceUpdate(client, device, deviceProfileOpt); | |
436 | - } | |
437 | - } | |
438 | - | |
439 | - @Override | |
440 | - public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { | |
441 | - String idVer = resourceUpdateMsgOpt.get().getResourceKey(); | |
442 | - clientContext.getLwM2mClients().forEach(e -> e.updateResourceModel(idVer, this.config.getModelProvider())); | |
443 | - } | |
444 | - | |
445 | - @Override | |
446 | - public void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) { | |
447 | - String pathIdVer = resourceDeleteMsgOpt.get().getResourceKey(); | |
448 | - clientContext.getLwM2mClients().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider())); | |
449 | - } | |
450 | - | |
451 | - /** | |
452 | - * #1 del from rpcSubscriptions by timeout | |
453 | - * #2 if not present in rpcSubscriptions by requestId: create new LwM2mClientRpcRequest, after success - add requestId, timeout | |
454 | - */ | |
455 | - @Override | |
456 | - public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg, SessionInfoProto sessionInfo) { | |
457 | - // #1 | |
458 | - this.checkRpcRequestTimeout(); | |
459 | - log.warn("4) toDeviceRpcRequestMsg: [{}], sessionUUID: [{}]", toDeviceRpcRequestMsg, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); | |
460 | - String bodyParams = StringUtils.trimToNull(toDeviceRpcRequestMsg.getParams()) != null ? toDeviceRpcRequestMsg.getParams() : "null"; | |
461 | - LwM2mTypeOper lwM2mTypeOper = setValidTypeOper(toDeviceRpcRequestMsg.getMethodName()); | |
462 | - UUID requestUUID = new UUID(toDeviceRpcRequestMsg.getRequestIdMSB(), toDeviceRpcRequestMsg.getRequestIdLSB()); | |
463 | - if (!this.rpcSubscriptions.containsKey(requestUUID)) { | |
464 | - this.rpcSubscriptions.put(requestUUID, toDeviceRpcRequestMsg.getExpirationTime()); | |
465 | - LwM2mClientRpcRequest lwm2mClientRpcRequest = null; | |
466 | - try { | |
467 | - LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); | |
468 | - Registration registration = client.getRegistration(); | |
469 | - if(registration != null) { | |
470 | - lwm2mClientRpcRequest = new LwM2mClientRpcRequest(lwM2mTypeOper, bodyParams, toDeviceRpcRequestMsg.getRequestId(), sessionInfo, registration, this); | |
471 | - if (lwm2mClientRpcRequest.getErrorMsg() != null) { | |
472 | - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); | |
473 | - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); | |
474 | - } else { | |
475 | - lwM2mTransportRequest.sendAllRequest(client, lwm2mClientRpcRequest.getTargetIdVer(), lwm2mClientRpcRequest.getTypeOper(), | |
476 | - null, | |
477 | - lwm2mClientRpcRequest.getValue() == null ? lwm2mClientRpcRequest.getParams() : lwm2mClientRpcRequest.getValue(), | |
478 | - this.config.getTimeout(), lwm2mClientRpcRequest); | |
479 | - } | |
480 | - } else { | |
481 | - this.sendErrorRpcResponse(lwm2mClientRpcRequest, "registration == null", sessionInfo); | |
482 | - } | |
483 | - } catch (Exception e) { | |
484 | - this.sendErrorRpcResponse(lwm2mClientRpcRequest, e.getMessage(), sessionInfo); | |
485 | - } | |
486 | - } | |
487 | - } | |
488 | - | |
489 | - private void sendErrorRpcResponse(LwM2mClientRpcRequest lwm2mClientRpcRequest, String msgError, SessionInfoProto sessionInfo) { | |
490 | - if (lwm2mClientRpcRequest == null) { | |
491 | - lwm2mClientRpcRequest = new LwM2mClientRpcRequest(); | |
492 | - } | |
493 | - lwm2mClientRpcRequest.setResponseCode(BAD_REQUEST.name()); | |
494 | - if (lwm2mClientRpcRequest.getErrorMsg() == null) { | |
495 | - lwm2mClientRpcRequest.setErrorMsg(msgError); | |
496 | - } | |
497 | - this.onToDeviceRpcResponse(lwm2mClientRpcRequest.getDeviceRpcResponseResultMsg(), sessionInfo); | |
498 | - } | |
499 | - | |
500 | - private void checkRpcRequestTimeout() { | |
501 | - log.warn("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); | |
502 | - if (rpcSubscriptions.size() > 0) { | |
503 | - Set<UUID> rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> System.currentTimeMillis() > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); | |
504 | - log.warn("4.2) System.currentTimeMillis(): [{}]", System.currentTimeMillis()); | |
505 | - log.warn("4.3) rpcSubscriptionsToRemove: [{}]", rpcSubscriptionsToRemove); | |
506 | - rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); | |
507 | - } | |
508 | - log.warn("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); | |
509 | - } | |
510 | - | |
511 | - public void sentRpcResponse(LwM2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { | |
512 | - rpcRequest.setResponseCode(requestCode); | |
513 | - if (LOG_LW2M_ERROR.equals(typeMsg)) { | |
514 | - rpcRequest.setInfoMsg(null); | |
515 | - rpcRequest.setValueMsg(null); | |
516 | - if (rpcRequest.getErrorMsg() == null) { | |
517 | - msg = msg.isEmpty() ? null : msg; | |
518 | - rpcRequest.setErrorMsg(msg); | |
519 | - } | |
520 | - } else if (LOG_LW2M_INFO.equals(typeMsg)) { | |
521 | - if (rpcRequest.getInfoMsg() == null) { | |
522 | - rpcRequest.setInfoMsg(msg); | |
523 | - } | |
524 | - } else if (LOG_LW2M_VALUE.equals(typeMsg)) { | |
525 | - if (rpcRequest.getValueMsg() == null) { | |
526 | - rpcRequest.setValueMsg(msg); | |
527 | - } | |
528 | - } | |
529 | - this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); | |
530 | - } | |
531 | - | |
532 | - @Override | |
533 | - public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, SessionInfoProto sessionInfo) { | |
534 | - log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); | |
535 | - transportService.process(sessionInfo, toDeviceResponse, null); | |
536 | - } | |
537 | - | |
538 | - public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { | |
539 | - log.info("[{}] toServerRpcResponse", toServerResponse); | |
540 | - } | |
541 | - | |
542 | - /** | |
543 | - * Deregister session in transport | |
544 | - * | |
545 | - * @param sessionInfo - lwm2m client | |
546 | - */ | |
547 | - @Override | |
548 | - public void doDisconnect(SessionInfoProto sessionInfo) { | |
549 | - transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); | |
550 | - transportService.deregisterSession(sessionInfo); | |
551 | - } | |
552 | - | |
553 | - /** | |
554 | - * Session device in thingsboard is closed | |
555 | - * | |
556 | - * @param sessionInfo - lwm2m client | |
557 | - */ | |
558 | - private void doCloseSession(SessionInfoProto sessionInfo) { | |
559 | - TransportProtos.SessionEvent event = SessionEvent.CLOSED; | |
560 | - TransportProtos.SessionEventMsg msg = TransportProtos.SessionEventMsg.newBuilder() | |
561 | - .setSessionType(TransportProtos.SessionType.ASYNC) | |
562 | - .setEvent(event).build(); | |
563 | - transportService.process(sessionInfo, msg, null); | |
564 | - } | |
565 | - | |
566 | - /** | |
567 | - * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay, | |
568 | - * * if you need to do long time processing use a dedicated thread pool. | |
569 | - * | |
570 | - * @param registration - | |
571 | - */ | |
572 | - @Override | |
573 | - public void onAwakeDev(Registration registration) { | |
574 | - log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); | |
575 | - this.sendLogsToThingsboard(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LW2M_INFO + ": Client is awake!"); | |
576 | - //TODO: associate endpointId with device information. | |
577 | - } | |
578 | - | |
579 | - /** | |
580 | - * @param logMsg - text msg | |
581 | - * @param registrationId - Id of Registration LwM2M Client | |
582 | - */ | |
583 | - @Override | |
584 | - public void sendLogsToThingsboard(String registrationId, String logMsg) { | |
585 | - sendLogsToThingsboard(clientContext.getClientByRegistrationId(registrationId), logMsg); | |
586 | - } | |
587 | - | |
588 | - @Override | |
589 | - public void sendLogsToThingsboard(LwM2mClient client, String logMsg) { | |
590 | - if (logMsg != null && client != null && client.getSession() != null) { | |
591 | - if (logMsg.length() > 1024) { | |
592 | - logMsg = logMsg.substring(0, 1024); | |
593 | - } | |
594 | - this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), client.getSession()); | |
595 | - } | |
596 | - } | |
597 | - | |
598 | - /** | |
599 | - * #1 clientOnlyObserveAfterConnect == true | |
600 | - * - Only Observe Request to the client marked as observe from the profile configuration. | |
601 | - * #2. clientOnlyObserveAfterConnect == false | |
602 | - * После регистрации отправляю запрос на read всех ресурсов, которые после регистрации есть у клиента, | |
603 | - * а затем запрос на observe (edited) | |
604 | - * - Read Request to the client after registration to read all resource values for all objects | |
605 | - * - then Observe Request to the client marked as observe from the profile configuration. | |
606 | - * | |
607 | - * @param lwM2MClient - object with All parameters off client | |
608 | - */ | |
609 | - private void initClientTelemetry(LwM2mClient lwM2MClient) { | |
610 | - LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId()); | |
611 | - Set<String> clientObjects = clientContext.getSupportedIdVerInClient(lwM2MClient); | |
612 | - if (clientObjects != null && clientObjects.size() > 0) { | |
613 | - if (LwM2mTransportUtil.LwM2MClientStrategy.CLIENT_STRATEGY_2.code == lwM2MClientProfile.getClientStrategy()) { | |
614 | - // #2 | |
615 | - lwM2MClient.getPendingReadRequests().addAll(clientObjects); | |
616 | - clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, READ, | |
617 | - null, this.config.getTimeout(), null)); | |
618 | - } | |
619 | - // #1 | |
620 | - this.initReadAttrTelemetryObserveToClient(lwM2MClient, READ, clientObjects); | |
621 | - this.initReadAttrTelemetryObserveToClient(lwM2MClient, OBSERVE, clientObjects); | |
622 | - this.initReadAttrTelemetryObserveToClient(lwM2MClient, WRITE_ATTRIBUTES, clientObjects); | |
623 | - this.initReadAttrTelemetryObserveToClient(lwM2MClient, DISCOVER, clientObjects); | |
624 | - } | |
625 | - } | |
626 | - | |
627 | - /** | |
628 | - * @param registration - | |
629 | - * @param lwM2mObject - | |
630 | - * @param pathIdVer - | |
631 | - */ | |
632 | - private void updateObjectResourceValue(Registration registration, LwM2mObject lwM2mObject, String pathIdVer) { | |
633 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)); | |
634 | - lwM2mObject.getInstances().forEach((instanceId, instance) -> { | |
635 | - String pathInstance = pathIds.toString() + "/" + instanceId; | |
636 | - this.updateObjectInstanceResourceValue(registration, instance, pathInstance); | |
637 | - }); | |
638 | - } | |
639 | - | |
640 | - /** | |
641 | - * @param registration - | |
642 | - * @param lwM2mObjectInstance - | |
643 | - * @param pathIdVer - | |
644 | - */ | |
645 | - private void updateObjectInstanceResourceValue(Registration registration, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer) { | |
646 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)); | |
647 | - lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> { | |
648 | - String pathRez = pathIds.toString() + "/" + resourceId; | |
649 | - this.updateResourcesValue(registration, resource, pathRez); | |
650 | - }); | |
651 | - } | |
652 | - | |
653 | - /** | |
654 | - * Sending observe value of resources to thingsboard | |
655 | - * #1 Return old Value Resource from LwM2MClient | |
656 | - * #2 Update new Resources (replace old Resource Value on new Resource Value) | |
657 | - * #3 If fr_update -> UpdateFirmware | |
658 | - * #4 updateAttrTelemetry | |
659 | - * | |
660 | - * @param registration - Registration LwM2M Client | |
661 | - * @param lwM2mResource - LwM2mSingleResource response.getContent() | |
662 | - * @param path - resource | |
663 | - */ | |
664 | - private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) { | |
665 | - LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
666 | - if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) { | |
667 | - /** version != null | |
668 | - * set setClient_fw_info... = value | |
669 | - **/ | |
670 | - if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { | |
671 | - lwM2MClient.getFwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); | |
672 | - } | |
673 | - if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { | |
674 | - lwM2MClient.getSwUpdate().initReadValue(this, this.lwM2mTransportRequest, path); | |
675 | - } | |
676 | - | |
677 | - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path)) || | |
678 | - (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { | |
679 | - LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext); | |
680 | - log.warn("93) path: [{}] value: [{}]", path, lwM2mResource.getValue()); | |
681 | - fwUpdate.updateStateOta(this, lwM2mTransportRequest, registration, path, ((Long) lwM2mResource.getValue()).intValue()); | |
682 | - } | |
683 | - Set<String> paths = new HashSet<>(); | |
684 | - paths.add(path); | |
685 | - this.updateAttrTelemetry(registration, paths); | |
686 | - } else { | |
687 | - log.error("Fail update Resource [{}]", lwM2mResource); | |
688 | - } | |
689 | - } | |
690 | - | |
691 | - | |
692 | - /** | |
693 | - * send Attribute and Telemetry to Thingsboard | |
694 | - * #1 - get AttrName/TelemetryName with value from LwM2MClient: | |
695 | - * -- resourceId == path from LwM2MClientProfile.postAttributeProfile/postTelemetryProfile/postObserveProfile | |
696 | - * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId) | |
697 | - * #2 - set Attribute/Telemetry | |
698 | - * | |
699 | - * @param registration - Registration LwM2M Client | |
700 | - */ | |
701 | - private void updateAttrTelemetry(Registration registration, Set<String> paths) { | |
702 | - try { | |
703 | - ResultsAddKeyValueProto results = this.getParametersFromProfile(registration, paths); | |
704 | - SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration); | |
705 | - if (results != null && sessionInfo != null) { | |
706 | - if (results.getResultAttributes().size() > 0) { | |
707 | - this.helper.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo); | |
708 | - } | |
709 | - if (results.getResultTelemetries().size() > 0) { | |
710 | - this.helper.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo); | |
711 | - } | |
712 | - } | |
713 | - } catch (Exception e) { | |
714 | - log.error("UpdateAttrTelemetry", e); | |
715 | - } | |
716 | - } | |
717 | - | |
718 | - private void initReadAttrTelemetryObserveToClient(LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, Set<String> clientObjects) { | |
719 | - LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(lwM2MClient.getProfileId()); | |
720 | - Set<String> result = null; | |
721 | - ConcurrentHashMap<String, Object> params = null; | |
722 | - if (READ.equals(typeOper)) { | |
723 | - result = JacksonUtil.fromString(lwM2MClientProfile.getPostAttributeProfile().toString(), | |
724 | - new TypeReference<>() { | |
725 | - }); | |
726 | - result.addAll(JacksonUtil.fromString(lwM2MClientProfile.getPostTelemetryProfile().toString(), | |
727 | - new TypeReference<>() { | |
728 | - })); | |
729 | - } else if (OBSERVE.equals(typeOper)) { | |
730 | - result = JacksonUtil.fromString(lwM2MClientProfile.getPostObserveProfile().toString(), | |
731 | - new TypeReference<>() { | |
732 | - }); | |
733 | - } else if (DISCOVER.equals(typeOper)) { | |
734 | - result = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()).keySet(); | |
735 | - } else if (WRITE_ATTRIBUTES.equals(typeOper)) { | |
736 | - params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); | |
737 | - result = params.keySet(); | |
738 | - } | |
739 | - sendRequestsToClient(lwM2MClient, typeOper, clientObjects, result, params); | |
740 | - } | |
741 | - | |
742 | - private void sendRequestsToClient(LwM2mClient lwM2MClient, LwM2mTypeOper operationType, Set<String> supportedObjectIds, Set<String> desiredObjectIds, ConcurrentHashMap<String, Object> params) { | |
743 | - if (desiredObjectIds != null && !desiredObjectIds.isEmpty()) { | |
744 | - Set<String> targetObjectIds = desiredObjectIds.stream().filter(target -> isSupportedTargetId(supportedObjectIds, target) | |
745 | - ).collect(Collectors.toUnmodifiableSet()); | |
746 | - if (!targetObjectIds.isEmpty()) { | |
747 | - //TODO: remove this side effect? | |
748 | - lwM2MClient.getPendingReadRequests().addAll(targetObjectIds); | |
749 | - targetObjectIds.forEach(target -> { | |
750 | - Object additionalParams = params != null ? params.get(target) : null; | |
751 | - lwM2mTransportRequest.sendAllRequest(lwM2MClient, target, operationType, additionalParams, this.config.getTimeout(), null); | |
752 | - }); | |
753 | - if (OBSERVE.equals(operationType)) { | |
754 | - lwM2MClient.initReadValue(this, null); | |
755 | - } | |
756 | - } | |
757 | - } | |
758 | - } | |
759 | - | |
760 | - private boolean isSupportedTargetId(Set<String> supportedIds, String targetId) { | |
761 | - String[] targetIdParts = targetId.split(LWM2M_SEPARATOR_PATH); | |
762 | - if (targetIdParts.length <= 1) { | |
763 | - return false; | |
764 | - } | |
765 | - String targetIdSearch = targetIdParts[0]; | |
766 | - for (int i = 1; i < targetIdParts.length; i++) { | |
767 | - targetIdSearch += "/" + targetIdParts[i]; | |
768 | - if (supportedIds.contains(targetIdSearch)) { | |
769 | - return true; | |
770 | - } | |
771 | - } | |
772 | - return false; | |
773 | - } | |
774 | - | |
775 | - private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) { | |
776 | - ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(), | |
777 | - new TypeToken<ConcurrentHashMap<String, Object>>() { | |
778 | - }.getType()); | |
779 | - return pathAttributes; | |
780 | - } | |
781 | - | |
782 | - private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional<DeviceProfile> deviceProfileOpt) { | |
783 | - deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singletonList(lwM2MClient), deviceProfile)); | |
784 | - lwM2MClient.onDeviceUpdate(device, deviceProfileOpt); | |
785 | - } | |
786 | - | |
787 | - /** | |
788 | - * // * @param attributes - new JsonObject | |
789 | - * // * @param telemetry - new JsonObject | |
790 | - * | |
791 | - * @param registration - Registration LwM2M Client | |
792 | - * @param path - | |
793 | - */ | |
794 | - private ResultsAddKeyValueProto getParametersFromProfile(Registration registration, Set<String> path) { | |
795 | - if (path != null && path.size() > 0) { | |
796 | - ResultsAddKeyValueProto results = new ResultsAddKeyValueProto(); | |
797 | - LwM2mClientProfile lwM2MClientProfile = clientContext.getProfile(registration); | |
798 | - List<TransportProtos.KeyValueProto> resultAttributes = new ArrayList<>(); | |
799 | - lwM2MClientProfile.getPostAttributeProfile().forEach(pathIdVer -> { | |
800 | - if (path.contains(pathIdVer.getAsString())) { | |
801 | - TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer.getAsString(), registration); | |
802 | - if (kvAttr != null) { | |
803 | - resultAttributes.add(kvAttr); | |
804 | - } | |
805 | - } | |
806 | - }); | |
807 | - List<TransportProtos.KeyValueProto> resultTelemetries = new ArrayList<>(); | |
808 | - lwM2MClientProfile.getPostTelemetryProfile().forEach(pathIdVer -> { | |
809 | - if (path.contains(pathIdVer.getAsString())) { | |
810 | - TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer.getAsString(), registration); | |
811 | - if (kvAttr != null) { | |
812 | - resultTelemetries.add(kvAttr); | |
813 | - } | |
814 | - } | |
815 | - }); | |
816 | - if (resultAttributes.size() > 0) { | |
817 | - results.setResultAttributes(resultAttributes); | |
818 | - } | |
819 | - if (resultTelemetries.size() > 0) { | |
820 | - results.setResultTelemetries(resultTelemetries); | |
821 | - } | |
822 | - return results; | |
823 | - } | |
824 | - return null; | |
825 | - } | |
826 | - | |
827 | - private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) { | |
828 | - LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); | |
829 | - JsonObject names = clientContext.getProfiles().get(lwM2MClient.getProfileId()).getPostKeyNameProfile(); | |
830 | - if (names != null && names.has(pathIdVer)) { | |
831 | - String resourceName = names.get(pathIdVer).getAsString(); | |
832 | - if (resourceName != null && !resourceName.isEmpty()) { | |
833 | - try { | |
834 | - LwM2mResource resourceValue = lwM2MClient != null ? getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) : null; | |
835 | - if (resourceValue != null) { | |
836 | - ResourceModel.Type currentType = resourceValue.getType(); | |
837 | - ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); | |
838 | - Object valueKvProto = null; | |
839 | - if (resourceValue.isMultiInstances()) { | |
840 | - valueKvProto = new JsonObject(); | |
841 | - Object finalvalueKvProto = valueKvProto; | |
842 | - Gson gson = new GsonBuilder().create(); | |
843 | - ResourceModel.Type finalCurrentType = currentType; | |
844 | - resourceValue.getInstances().forEach((k, v) -> { | |
845 | - Object val = this.converter.convertValue(v, finalCurrentType, expectedType, | |
846 | - new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); | |
847 | - JsonElement element = gson.toJsonTree(val, val.getClass()); | |
848 | - ((JsonObject) finalvalueKvProto).add(String.valueOf(k), element); | |
849 | - }); | |
850 | - valueKvProto = gson.toJson(valueKvProto); | |
851 | - } else { | |
852 | - valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, | |
853 | - new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); | |
854 | - } | |
855 | - LwM2mOtaConvert lwM2mOtaConvert = convertOtaUpdateValueToString(pathIdVer, valueKvProto, currentType); | |
856 | - valueKvProto = lwM2mOtaConvert.getValue(); | |
857 | - currentType = lwM2mOtaConvert.getCurrentType(); | |
858 | - return valueKvProto != null ? this.helper.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null; | |
859 | - } | |
860 | - } catch (Exception e) { | |
861 | - log.error("Failed to add parameters.", e); | |
862 | - } | |
863 | - } | |
864 | - } else { | |
865 | - log.error("Failed to add parameters. path: [{}], names: [{}]", pathIdVer, names); | |
866 | - } | |
867 | - return null; | |
868 | - } | |
869 | - | |
870 | - /** | |
871 | - * @param pathIdVer - path resource | |
872 | - * @return - value of Resource into format KvProto or null | |
873 | - */ | |
874 | - private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { | |
875 | - LwM2mResource resourceValue = this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); | |
876 | - if (resourceValue != null) { | |
877 | - ResourceModel.Type currentType = resourceValue.getType(); | |
878 | - ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); | |
879 | - return this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, | |
880 | - new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))); | |
881 | - } else { | |
882 | - return null; | |
883 | - } | |
884 | - } | |
885 | - | |
886 | - /** | |
887 | - * @param lwM2MClient - | |
888 | - * @param path - | |
889 | - * @return - return value of Resource by idPath | |
890 | - */ | |
891 | - private LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) { | |
892 | - LwM2mResource lwm2mResourceValue = null; | |
893 | - ResourceValue resourceValue = lwM2MClient.getResources().get(path); | |
894 | - if (resourceValue != null) { | |
895 | - if (new LwM2mPath(convertPathFromIdVerToObjectId(path)).isResource()) { | |
896 | - lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource(); | |
897 | - } | |
898 | - } | |
899 | - return lwm2mResourceValue; | |
900 | - } | |
901 | - | |
902 | - /** | |
903 | - * Update resource (attribute) value on thingsboard after update value in client | |
904 | - * | |
905 | - * @param registration - | |
906 | - * @param path - | |
907 | - * @param request - | |
908 | - */ | |
909 | - public void onWriteResponseOk(Registration registration, String path, WriteRequest request) { | |
910 | - if (request.getNode() instanceof LwM2mResource) { | |
911 | - this.updateResourcesValue(registration, ((LwM2mResource) request.getNode()), path); | |
912 | - } else if (request.getNode() instanceof LwM2mObjectInstance) { | |
913 | - ((LwM2mObjectInstance) request.getNode()).getResources().forEach((resId, resource) -> { | |
914 | - this.updateResourcesValue(registration, resource, path + "/" + resId); | |
915 | - }); | |
916 | - } | |
917 | - | |
918 | - } | |
919 | - | |
920 | - /** | |
921 | - * #1 Read new, old Value (Attribute, Telemetry, Observe, KeyName) | |
922 | - * #2 Update in lwM2MClient: ...Profile if changes from update device | |
923 | - * #3 Equivalence test: old <> new Value (Attribute, Telemetry, Observe, KeyName) | |
924 | - * #3.1 Attribute isChange (add&del) | |
925 | - * #3.2 Telemetry isChange (add&del) | |
926 | - * #3.3 KeyName isChange (add) | |
927 | - * #3.4 attributeLwm2m isChange (update WrightAttribute: add/update/del) | |
928 | - * #4 update | |
929 | - * #4.1 add If #3 isChange, then analyze and update Value in Transport form Client and send Value to thingsboard | |
930 | - * #4.2 del | |
931 | - * -- if add attributes includes del telemetry - result del for observe | |
932 | - * #5 | |
933 | - * #5.1 Observe isChange (add&del) | |
934 | - * #5.2 Observe.add | |
935 | - * -- path Attr/Telemetry includes newObserve and does not include oldObserve: send Request observe to Client | |
936 | - * #5.3 Observe.del | |
937 | - * -- different between newObserve and oldObserve: send Request cancel observe to client | |
938 | - * #6 | |
939 | - * #6.1 - update WriteAttribute | |
940 | - * #6.2 - del WriteAttribute | |
941 | - * | |
942 | - * @param clients - | |
943 | - * @param deviceProfile - | |
944 | - */ | |
945 | - private void onDeviceProfileUpdate(List<LwM2mClient> clients, DeviceProfile deviceProfile) { | |
946 | - LwM2mClientProfile lwM2MClientProfileOld = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone(); | |
947 | - if (clientContext.profileUpdate(deviceProfile) != null) { | |
948 | - // #1 | |
949 | - JsonArray attributeOld = lwM2MClientProfileOld.getPostAttributeProfile(); | |
950 | - Set<String> attributeSetOld = convertJsonArrayToSet(attributeOld); | |
951 | - JsonArray telemetryOld = lwM2MClientProfileOld.getPostTelemetryProfile(); | |
952 | - Set<String> telemetrySetOld = convertJsonArrayToSet(telemetryOld); | |
953 | - JsonArray observeOld = lwM2MClientProfileOld.getPostObserveProfile(); | |
954 | - JsonObject keyNameOld = lwM2MClientProfileOld.getPostKeyNameProfile(); | |
955 | - JsonObject attributeLwm2mOld = lwM2MClientProfileOld.getPostAttributeLwm2mProfile(); | |
956 | - | |
957 | - LwM2mClientProfile lwM2MClientProfileNew = clientContext.getProfiles().get(deviceProfile.getUuidId()).clone(); | |
958 | - JsonArray attributeNew = lwM2MClientProfileNew.getPostAttributeProfile(); | |
959 | - Set<String> attributeSetNew = convertJsonArrayToSet(attributeNew); | |
960 | - JsonArray telemetryNew = lwM2MClientProfileNew.getPostTelemetryProfile(); | |
961 | - Set<String> telemetrySetNew = convertJsonArrayToSet(telemetryNew); | |
962 | - JsonArray observeNew = lwM2MClientProfileNew.getPostObserveProfile(); | |
963 | - JsonObject keyNameNew = lwM2MClientProfileNew.getPostKeyNameProfile(); | |
964 | - JsonObject attributeLwm2mNew = lwM2MClientProfileNew.getPostAttributeLwm2mProfile(); | |
965 | - | |
966 | - // #3 | |
967 | - ResultsAnalyzerParameters sendAttrToThingsboard = new ResultsAnalyzerParameters(); | |
968 | - // #3.1 | |
969 | - if (!attributeOld.equals(attributeNew)) { | |
970 | - ResultsAnalyzerParameters postAttributeAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(attributeOld, | |
971 | - new TypeToken<Set<String>>() { | |
972 | - }.getType()), attributeSetNew); | |
973 | - sendAttrToThingsboard.getPathPostParametersAdd().addAll(postAttributeAnalyzer.getPathPostParametersAdd()); | |
974 | - sendAttrToThingsboard.getPathPostParametersDel().addAll(postAttributeAnalyzer.getPathPostParametersDel()); | |
975 | - } | |
976 | - // #3.2 | |
977 | - if (!telemetryOld.equals(telemetryNew)) { | |
978 | - ResultsAnalyzerParameters postTelemetryAnalyzer = this.getAnalyzerParameters(new Gson().fromJson(telemetryOld, | |
979 | - new TypeToken<Set<String>>() { | |
980 | - }.getType()), telemetrySetNew); | |
981 | - sendAttrToThingsboard.getPathPostParametersAdd().addAll(postTelemetryAnalyzer.getPathPostParametersAdd()); | |
982 | - sendAttrToThingsboard.getPathPostParametersDel().addAll(postTelemetryAnalyzer.getPathPostParametersDel()); | |
983 | - } | |
984 | - // #3.3 | |
985 | - if (!keyNameOld.equals(keyNameNew)) { | |
986 | - ResultsAnalyzerParameters keyNameChange = this.getAnalyzerKeyName(new Gson().fromJson(keyNameOld.toString(), | |
987 | - new TypeToken<ConcurrentHashMap<String, String>>() { | |
988 | - }.getType()), | |
989 | - new Gson().fromJson(keyNameNew.toString(), new TypeToken<ConcurrentHashMap<String, String>>() { | |
990 | - }.getType())); | |
991 | - sendAttrToThingsboard.getPathPostParametersAdd().addAll(keyNameChange.getPathPostParametersAdd()); | |
992 | - } | |
993 | - | |
994 | - // #3.4, #6 | |
995 | - if (!attributeLwm2mOld.equals(attributeLwm2mNew)) { | |
996 | - this.getAnalyzerAttributeLwm2m(clients, attributeLwm2mOld, attributeLwm2mNew); | |
997 | - } | |
998 | - | |
999 | - // #4.1 add | |
1000 | - if (sendAttrToThingsboard.getPathPostParametersAdd().size() > 0) { | |
1001 | - // update value in Resources | |
1002 | - clients.forEach(client -> { | |
1003 | - this.readObserveFromProfile(client, sendAttrToThingsboard.getPathPostParametersAdd(), READ); | |
1004 | - }); | |
1005 | - } | |
1006 | - // #4.2 del | |
1007 | - if (sendAttrToThingsboard.getPathPostParametersDel().size() > 0) { | |
1008 | - ResultsAnalyzerParameters sendAttrToThingsboardDel = this.getAnalyzerParameters(sendAttrToThingsboard.getPathPostParametersAdd(), sendAttrToThingsboard.getPathPostParametersDel()); | |
1009 | - sendAttrToThingsboard.setPathPostParametersDel(sendAttrToThingsboardDel.getPathPostParametersDel()); | |
1010 | - } | |
1011 | - | |
1012 | - // #5.1 | |
1013 | - if (!observeOld.equals(observeNew)) { | |
1014 | - Set<String> observeSetOld = new Gson().fromJson(observeOld, new TypeToken<Set<String>>() { | |
1015 | - }.getType()); | |
1016 | - Set<String> observeSetNew = new Gson().fromJson(observeNew, new TypeToken<Set<String>>() { | |
1017 | - }.getType()); | |
1018 | - //#5.2 add | |
1019 | - // path Attr/Telemetry includes newObserve | |
1020 | - attributeSetOld.addAll(telemetrySetOld); | |
1021 | - ResultsAnalyzerParameters sendObserveToClientOld = this.getAnalyzerParametersIn(attributeSetOld, observeSetOld); // add observe | |
1022 | - attributeSetNew.addAll(telemetrySetNew); | |
1023 | - ResultsAnalyzerParameters sendObserveToClientNew = this.getAnalyzerParametersIn(attributeSetNew, observeSetNew); // add observe | |
1024 | - // does not include oldObserve | |
1025 | - ResultsAnalyzerParameters postObserveAnalyzer = this.getAnalyzerParameters(sendObserveToClientOld.getPathPostParametersAdd(), sendObserveToClientNew.getPathPostParametersAdd()); | |
1026 | - // send Request observe to Client | |
1027 | - clients.forEach(client -> { | |
1028 | - Registration registration = client.getRegistration(); | |
1029 | - if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { | |
1030 | - this.readObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); | |
1031 | - } | |
1032 | - // 5.3 del | |
1033 | - // send Request cancel observe to Client | |
1034 | - if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { | |
1035 | - this.cancelObserveFromProfile(client, postObserveAnalyzer.getPathPostParametersDel()); | |
1036 | - } | |
1037 | - }); | |
1038 | - } | |
1039 | - } | |
1040 | - } | |
1041 | - | |
1042 | - /** | |
1043 | - * Compare old list with new list after change AttrTelemetryObserve in config Profile | |
1044 | - * | |
1045 | - * @param parametersOld - | |
1046 | - * @param parametersNew - | |
1047 | - * @return ResultsAnalyzerParameters: add && new | |
1048 | - */ | |
1049 | - private ResultsAnalyzerParameters getAnalyzerParameters(Set<String> parametersOld, Set<String> parametersNew) { | |
1050 | - ResultsAnalyzerParameters analyzerParameters = null; | |
1051 | - if (!parametersOld.equals(parametersNew)) { | |
1052 | - analyzerParameters = new ResultsAnalyzerParameters(); | |
1053 | - analyzerParameters.setPathPostParametersAdd(parametersNew | |
1054 | - .stream().filter(p -> !parametersOld.contains(p)).collect(Collectors.toSet())); | |
1055 | - analyzerParameters.setPathPostParametersDel(parametersOld | |
1056 | - .stream().filter(p -> !parametersNew.contains(p)).collect(Collectors.toSet())); | |
1057 | - } | |
1058 | - return analyzerParameters; | |
1059 | - } | |
1060 | - | |
1061 | - private ResultsAnalyzerParameters getAnalyzerParametersIn(Set<String> parametersObserve, Set<String> parameters) { | |
1062 | - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); | |
1063 | - analyzerParameters.setPathPostParametersAdd(parametersObserve | |
1064 | - .stream().filter(parameters::contains).collect(Collectors.toSet())); | |
1065 | - return analyzerParameters; | |
1066 | - } | |
1067 | - | |
1068 | - /** | |
1069 | - * Update Resource value after change RezAttrTelemetry in config Profile | |
1070 | - * send response Read to Client and add path to pathResAttrTelemetry in LwM2MClient.getAttrTelemetryObserveValue() | |
1071 | - * | |
1072 | - * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] | |
1073 | - */ | |
1074 | - private void readObserveFromProfile(LwM2mClient client, Set<String> targets, LwM2mTypeOper typeOper) { | |
1075 | - targets.forEach(target -> { | |
1076 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); | |
1077 | - if (pathIds.isResource()) { | |
1078 | - if (READ.equals(typeOper)) { | |
1079 | - lwM2mTransportRequest.sendAllRequest(client, target, typeOper, | |
1080 | - null, this.config.getTimeout(), null); | |
1081 | - } else if (OBSERVE.equals(typeOper)) { | |
1082 | - lwM2mTransportRequest.sendAllRequest(client, target, typeOper, | |
1083 | - null, this.config.getTimeout(), null); | |
1084 | - } | |
1085 | - } | |
1086 | - }); | |
1087 | - } | |
1088 | - | |
1089 | - private ResultsAnalyzerParameters getAnalyzerKeyName(ConcurrentHashMap<String, String> keyNameOld, ConcurrentHashMap<String, String> keyNameNew) { | |
1090 | - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); | |
1091 | - Set<String> paths = keyNameNew.entrySet() | |
1092 | - .stream() | |
1093 | - .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey()))) | |
1094 | - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet(); | |
1095 | - analyzerParameters.setPathPostParametersAdd(paths); | |
1096 | - return analyzerParameters; | |
1097 | - } | |
1098 | - | |
1099 | - /** | |
1100 | - * #3.4, #6 | |
1101 | - * #6 | |
1102 | - * #6.1 - send update WriteAttribute | |
1103 | - * #6.2 - send empty WriteAttribute | |
1104 | - * | |
1105 | - * @param attributeLwm2mOld - | |
1106 | - * @param attributeLwm2mNew - | |
1107 | - * @return | |
1108 | - */ | |
1109 | - private void getAnalyzerAttributeLwm2m(List<LwM2mClient> clients, JsonObject attributeLwm2mOld, JsonObject attributeLwm2mNew) { | |
1110 | - ResultsAnalyzerParameters analyzerParameters = new ResultsAnalyzerParameters(); | |
1111 | - ConcurrentHashMap<String, Object> lwm2mAttributesOld = new Gson().fromJson(attributeLwm2mOld.toString(), | |
1112 | - new TypeToken<ConcurrentHashMap<String, Object>>() { | |
1113 | - }.getType()); | |
1114 | - ConcurrentHashMap<String, Object> lwm2mAttributesNew = new Gson().fromJson(attributeLwm2mNew.toString(), | |
1115 | - new TypeToken<ConcurrentHashMap<String, Object>>() { | |
1116 | - }.getType()); | |
1117 | - Set<String> pathOld = lwm2mAttributesOld.keySet(); | |
1118 | - Set<String> pathNew = lwm2mAttributesNew.keySet(); | |
1119 | - analyzerParameters.setPathPostParametersAdd(pathNew | |
1120 | - .stream().filter(p -> !pathOld.contains(p)).collect(Collectors.toSet())); | |
1121 | - analyzerParameters.setPathPostParametersDel(pathOld | |
1122 | - .stream().filter(p -> !pathNew.contains(p)).collect(Collectors.toSet())); | |
1123 | - Set<String> pathCommon = pathNew | |
1124 | - .stream().filter(p -> pathOld.contains(p)).collect(Collectors.toSet()); | |
1125 | - Set<String> pathCommonChange = pathCommon | |
1126 | - .stream().filter(p -> !lwm2mAttributesOld.get(p).equals(lwm2mAttributesNew.get(p))).collect(Collectors.toSet()); | |
1127 | - analyzerParameters.getPathPostParametersAdd().addAll(pathCommonChange); | |
1128 | - // #6 | |
1129 | - // #6.2 | |
1130 | - if (analyzerParameters.getPathPostParametersAdd().size() > 0) { | |
1131 | - clients.forEach(client -> { | |
1132 | - Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client); | |
1133 | - Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) | |
1134 | - .collect(Collectors.toUnmodifiableSet()); | |
1135 | - if (!pathSend.isEmpty()) { | |
1136 | - ConcurrentHashMap<String, Object> finalParams = lwm2mAttributesNew; | |
1137 | - pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES, | |
1138 | - finalParams.get(target), this.config.getTimeout(), null)); | |
1139 | - } | |
1140 | - }); | |
1141 | - } | |
1142 | - // #6.2 | |
1143 | - if (analyzerParameters.getPathPostParametersDel().size() > 0) { | |
1144 | - clients.forEach(client -> { | |
1145 | - Registration registration = client.getRegistration(); | |
1146 | - Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client); | |
1147 | - Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) | |
1148 | - .collect(Collectors.toUnmodifiableSet()); | |
1149 | - if (!pathSend.isEmpty()) { | |
1150 | - pathSend.forEach(target -> { | |
1151 | - Map<String, Object> params = (Map<String, Object>) lwm2mAttributesOld.get(target); | |
1152 | - params.clear(); | |
1153 | - params.put(OBJECT_VERSION, ""); | |
1154 | - lwM2mTransportRequest.sendAllRequest(client, target, WRITE_ATTRIBUTES, params, this.config.getTimeout(), null); | |
1155 | - }); | |
1156 | - } | |
1157 | - }); | |
1158 | - } | |
1159 | - | |
1160 | - } | |
1161 | - | |
1162 | - private void cancelObserveFromProfile(LwM2mClient lwM2mClient, Set<String> paramAnallyzer) { | |
1163 | - paramAnallyzer.forEach(pathIdVer -> { | |
1164 | - if (this.getResourceValueFromLwM2MClient(lwM2mClient, pathIdVer) != null) { | |
1165 | - lwM2mTransportRequest.sendAllRequest(lwM2mClient, pathIdVer, OBSERVE_CANCEL, null, this.config.getTimeout(), null); | |
1166 | - } | |
1167 | - } | |
1168 | - ); | |
1169 | - } | |
1170 | - | |
1171 | - private void updateResourcesValueToClient(LwM2mClient lwM2MClient, Object valueOld, Object valueNew, String path) { | |
1172 | - if (valueNew != null && (valueOld == null || !valueNew.toString().equals(valueOld.toString()))) { | |
1173 | - lwM2mTransportRequest.sendAllRequest(lwM2MClient, path, WRITE_REPLACE, valueNew, this.config.getTimeout(), null); | |
1174 | - } else { | |
1175 | - log.error("Failed update resource [{}] [{}]", path, valueNew); | |
1176 | - String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", | |
1177 | - LOG_LW2M_ERROR, path, valueNew); | |
1178 | - this.sendLogsToThingsboard(lwM2MClient, logMsg); | |
1179 | - log.info("Failed update resource [{}] [{}]", path, valueNew); | |
1180 | - } | |
1181 | - } | |
1182 | - | |
1183 | - /** | |
1184 | - * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...) | |
1185 | - * config attr/telemetry... in profile | |
1186 | - */ | |
1187 | - public void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials) { | |
1188 | - log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList()); | |
1189 | - } | |
1190 | - | |
1191 | - /** | |
1192 | - * Get path to resource from profile equal keyName | |
1193 | - * | |
1194 | - * @param sessionInfo - | |
1195 | - * @param name - | |
1196 | - * @return - | |
1197 | - */ | |
1198 | - public String getPresentPathIntoProfile(TransportProtos.SessionInfoProto sessionInfo, String name) { | |
1199 | - LwM2mClientProfile profile = clientContext.getProfile(new UUID(sessionInfo.getDeviceProfileIdMSB(), sessionInfo.getDeviceProfileIdLSB())); | |
1200 | - LwM2mClient lwM2mClient = clientContext.getClientBySessionInfo(sessionInfo); | |
1201 | - return profile.getPostKeyNameProfile().getAsJsonObject().entrySet().stream() | |
1202 | - .filter(e -> e.getValue().getAsString().equals(name) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().map(Map.Entry::getKey) | |
1203 | - .orElse(null); | |
1204 | - } | |
1205 | - | |
1206 | - /** | |
1207 | - * 1. FirmwareUpdate: | |
1208 | - * - msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 | |
1209 | - * 2. Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values | |
1210 | - * - Get path resource by result attributesResponse | |
1211 | - * | |
1212 | - * @param attributesResponse - | |
1213 | - * @param sessionInfo - | |
1214 | - */ | |
1215 | - public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { | |
1216 | - try { | |
1217 | - List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList(); | |
1218 | - this.updateAttributeFromThingsboard(tsKvProtos, sessionInfo); | |
1219 | - } catch (Exception e) { | |
1220 | - log.error("", e); | |
1221 | - } | |
1222 | - } | |
1223 | - | |
1224 | - /** | |
1225 | - * #1.1 If two names have equal path => last time attribute | |
1226 | - * #2.1 if there is a difference in values between the current resource values and the shared attribute values | |
1227 | - * => send to client Request Update of value (new value from shared attribute) | |
1228 | - * and LwM2MClient.delayedRequests.add(path) | |
1229 | - * #2.1 if there is not a difference in values between the current resource values and the shared attribute values | |
1230 | - * | |
1231 | - * @param tsKvProtos | |
1232 | - * @param sessionInfo | |
1233 | - */ | |
1234 | - public void updateAttributeFromThingsboard(List<TransportProtos.TsKvProto> tsKvProtos, TransportProtos.SessionInfoProto sessionInfo) { | |
1235 | - LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); | |
1236 | - if (lwM2MClient != null) { | |
1237 | - log.warn("1) UpdateAttributeFromThingsboard, tsKvProtos [{}]", tsKvProtos); | |
1238 | - tsKvProtos.forEach(tsKvProto -> { | |
1239 | - String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, tsKvProto.getKv().getKey()); | |
1240 | - if (pathIdVer != null) { | |
1241 | - // #1.1 | |
1242 | - if (lwM2MClient.getDelayedRequests().containsKey(pathIdVer) && tsKvProto.getTs() > lwM2MClient.getDelayedRequests().get(pathIdVer).getTs()) { | |
1243 | - lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto); | |
1244 | - } else if (!lwM2MClient.getDelayedRequests().containsKey(pathIdVer)) { | |
1245 | - lwM2MClient.getDelayedRequests().put(pathIdVer, tsKvProto); | |
1246 | - } | |
1247 | - } | |
1248 | - }); | |
1249 | - // #2.1 | |
1250 | - lwM2MClient.getDelayedRequests().forEach((pathIdVer, tsKvProto) -> { | |
1251 | - this.updateResourcesValueToClient(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), | |
1252 | - getValueFromKvProto(tsKvProto.getKv()), pathIdVer); | |
1253 | - }); | |
1254 | - } else { | |
1255 | - log.error("UpdateAttributeFromThingsboard, lwM2MClient is null"); | |
1256 | - } | |
1257 | - } | |
1258 | - | |
1259 | - /** | |
1260 | - * @param lwM2MClient - | |
1261 | - * @return SessionInfoProto - | |
1262 | - */ | |
1263 | - private SessionInfoProto getSessionInfo(LwM2mClient lwM2MClient) { | |
1264 | - if (lwM2MClient != null && lwM2MClient.getSession() != null) { | |
1265 | - return lwM2MClient.getSession(); | |
1266 | - } | |
1267 | - return null; | |
1268 | - } | |
1269 | - | |
1270 | - /** | |
1271 | - * @param registration - Registration LwM2M Client | |
1272 | - * @return - sessionInfo after access connect client | |
1273 | - */ | |
1274 | - public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) { | |
1275 | - return getSessionInfo(clientContext.getClientByEndpoint(registration.getEndpoint())); | |
1276 | - } | |
1277 | - | |
1278 | - /** | |
1279 | - * if sessionInfo removed from sessions, then new registerAsyncSession | |
1280 | - * | |
1281 | - * @param sessionInfo - | |
1282 | - */ | |
1283 | - private void reportActivityAndRegister(SessionInfoProto sessionInfo) { | |
1284 | - if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) { | |
1285 | - transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, sessionInfo)); | |
1286 | - this.reportActivitySubscription(sessionInfo); | |
1287 | - } | |
1288 | - } | |
1289 | - | |
1290 | - private void reportActivity() { | |
1291 | - clientContext.getLwM2mClients().forEach(client -> reportActivityAndRegister(client.getSession())); | |
1292 | - } | |
1293 | - | |
1294 | - /** | |
1295 | - * #1. !!! sharedAttr === profileAttr !!! | |
1296 | - * - If there is a difference in values between the current resource values and the shared attribute values | |
1297 | - * - when the client connects to the server | |
1298 | - * #1.1 get attributes name from profile include name resources in ModelObject if resource isWritable | |
1299 | - * #1.2 #1 size > 0 => send Request getAttributes to thingsboard | |
1300 | - * #2. FirmwareAttribute subscribe: | |
1301 | - * | |
1302 | - * @param lwM2MClient - LwM2M Client | |
1303 | - */ | |
1304 | - public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { | |
1305 | - SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); | |
1306 | - if (sessionInfo != null) { | |
1307 | - //#1.1 | |
1308 | - ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); | |
1309 | - if (keyNamesMap.values().size() > 0) { | |
1310 | - try { | |
1311 | - //#1.2 | |
1312 | - TransportProtos.GetAttributeRequestMsg getAttributeMsg = adaptor.convertToGetAttributes(null, keyNamesMap.values()); | |
1313 | - transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); | |
1314 | - } catch (AdaptorException e) { | |
1315 | - log.trace("Failed to decode get attributes request", e); | |
1316 | - } | |
1317 | - } | |
1318 | - | |
1319 | - } | |
1320 | - } | |
1321 | - | |
1322 | - public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient, LwM2mClientRpcRequest rpcRequest) { | |
1323 | - if (lwM2MClient.getRegistration().getSupportedVersion(FW_5_ID) != null) { | |
1324 | - SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); | |
1325 | - if (sessionInfo != null) { | |
1326 | - DefaultLwM2MTransportMsgHandler handler = this; | |
1327 | - this.transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.FIRMWARE.name()), | |
1328 | - new TransportServiceCallback<>() { | |
1329 | - @Override | |
1330 | - public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { | |
1331 | - if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) | |
1332 | - && response.getType().equals(OtaPackageType.FIRMWARE.name())) { | |
1333 | - LwM2mFwSwUpdate fwUpdate = lwM2MClient.getFwUpdate(clientContext); | |
1334 | - if (rpcRequest != null) { | |
1335 | - fwUpdate.setStateUpdate(INITIATED.name()); | |
1336 | - } | |
1337 | - if (!FAILED.name().equals(fwUpdate.getStateUpdate())) { | |
1338 | - log.warn("7) firmware start with ver: [{}]", response.getVersion()); | |
1339 | - fwUpdate.setRpcRequest(rpcRequest); | |
1340 | - fwUpdate.setCurrentVersion(response.getVersion()); | |
1341 | - fwUpdate.setCurrentTitle(response.getTitle()); | |
1342 | - fwUpdate.setCurrentId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())); | |
1343 | - if (rpcRequest == null) { | |
1344 | - fwUpdate.sendReadObserveInfo(lwM2mTransportRequest); | |
1345 | - } else { | |
1346 | - fwUpdate.writeFwSwWare(handler, lwM2mTransportRequest); | |
1347 | - } | |
1348 | - } else { | |
1349 | - String msgError = String.format("OtaPackage device: %s, version: %s, stateUpdate: %s", | |
1350 | - lwM2MClient.getDeviceName(), response.getVersion(), fwUpdate.getStateUpdate()); | |
1351 | - log.warn("7_1 [{}]", msgError); | |
1352 | - } | |
1353 | - } else { | |
1354 | - String msgError = String.format("OtaPackage device: %s, responseStatus: %s", | |
1355 | - lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); | |
1356 | - log.trace(msgError); | |
1357 | - if (rpcRequest != null) { | |
1358 | - sendErrorRpcResponse(rpcRequest, msgError, sessionInfo); | |
1359 | - } | |
1360 | - } | |
1361 | - } | |
1362 | - | |
1363 | - @Override | |
1364 | - public void onError(Throwable e) { | |
1365 | - log.trace("Failed to process firmwareUpdate ", e); | |
1366 | - } | |
1367 | - }); | |
1368 | - } | |
1369 | - } | |
1370 | - } | |
1371 | - | |
1372 | - public void getInfoSoftwareUpdate(LwM2mClient lwM2MClient, LwM2mClientRpcRequest rpcRequest) { | |
1373 | - if (lwM2MClient.getRegistration().getSupportedVersion(SW_ID) != null) { | |
1374 | - SessionInfoProto sessionInfo = this.getSessionInfo(lwM2MClient); | |
1375 | - if (sessionInfo != null) { | |
1376 | - DefaultLwM2MTransportMsgHandler handler = this; | |
1377 | - transportService.process(sessionInfo, createOtaPackageRequestMsg(sessionInfo, OtaPackageType.SOFTWARE.name()), | |
1378 | - new TransportServiceCallback<>() { | |
1379 | - @Override | |
1380 | - public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { | |
1381 | - if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus()) | |
1382 | - && response.getType().equals(OtaPackageType.SOFTWARE.name())) { | |
1383 | - lwM2MClient.getSwUpdate().setRpcRequest(rpcRequest); | |
1384 | - lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion()); | |
1385 | - lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle()); | |
1386 | - lwM2MClient.getSwUpdate().setCurrentId(new OtaPackageId(new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB())).getId()); | |
1387 | - lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); | |
1388 | - if (rpcRequest == null) { | |
1389 | - lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest); | |
1390 | - } else { | |
1391 | - lwM2MClient.getSwUpdate().writeFwSwWare(handler, lwM2mTransportRequest); | |
1392 | - } | |
1393 | - } else { | |
1394 | - log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); | |
1395 | - } | |
1396 | - } | |
1397 | - | |
1398 | - @Override | |
1399 | - public void onError(Throwable e) { | |
1400 | - log.trace("Failed to process softwareUpdate ", e); | |
1401 | - } | |
1402 | - }); | |
1403 | - } | |
1404 | - } | |
1405 | - } | |
1406 | - | |
1407 | - private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) { | |
1408 | - return TransportProtos.GetOtaPackageRequestMsg.newBuilder() | |
1409 | - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
1410 | - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) | |
1411 | - .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
1412 | - .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
1413 | - .setType(nameFwSW) | |
1414 | - .build(); | |
1415 | - } | |
1416 | - | |
1417 | - /** | |
1418 | - * !!! sharedAttr === profileAttr !!! | |
1419 | - * Get names or keyNames from profile: resources IsWritable | |
1420 | - * | |
1421 | - * @param lwM2MClient - | |
1422 | - * @return ArrayList keyNames from profile profileAttr && IsWritable | |
1423 | - */ | |
1424 | - private ConcurrentMap<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) { | |
1425 | - | |
1426 | - LwM2mClientProfile profile = clientContext.getProfile(lwM2MClient.getProfileId()); | |
1427 | - return new Gson().fromJson(profile.getPostKeyNameProfile().toString(), | |
1428 | - new TypeToken<ConcurrentHashMap<String, String>>() { | |
1429 | - }.getType()); | |
1430 | - } | |
1431 | - | |
1432 | - private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { | |
1433 | - ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config | |
1434 | - .getModelProvider()); | |
1435 | - Integer objectId = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)).getObjectId(); | |
1436 | - String objectVer = validateObjectVerFromKey(pathIdVer); | |
1437 | - return resourceModel != null && (isWritableNotOptional ? | |
1438 | - objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : | |
1439 | - objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); | |
1440 | - } | |
1441 | - | |
1442 | - public LwM2MTransportServerConfig getConfig() { | |
1443 | - return this.config; | |
1444 | - } | |
1445 | - | |
1446 | - private void reportActivitySubscription(TransportProtos.SessionInfoProto sessionInfo) { | |
1447 | - transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder() | |
1448 | - .setAttributeSubscription(true) | |
1449 | - .setRpcSubscription(true) | |
1450 | - .setLastActivityTime(System.currentTimeMillis()) | |
1451 | - .build(), TransportServiceCallback.EMPTY); | |
1452 | - } | |
1453 | -} |
... | ... | @@ -26,8 +26,8 @@ import org.eclipse.leshan.server.californium.LeshanServer; |
26 | 26 | import org.eclipse.leshan.server.californium.LeshanServerBuilder; |
27 | 27 | import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore; |
28 | 28 | import org.eclipse.leshan.server.model.LwM2mModelProvider; |
29 | -import org.eclipse.leshan.server.security.EditableSecurityStore; | |
30 | 29 | import org.springframework.stereotype.Component; |
30 | +import org.thingsboard.server.cache.ota.OtaPackageDataCache; | |
31 | 31 | import org.thingsboard.server.common.data.StringUtils; |
32 | 32 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
33 | 33 | import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
... | ... | @@ -35,8 +35,8 @@ import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC; |
35 | 35 | import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer; |
36 | 36 | import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier; |
37 | 37 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; |
38 | -import org.thingsboard.server.transport.lwm2m.server.store.TbEditableSecurityStore; | |
39 | 38 | import org.thingsboard.server.transport.lwm2m.server.store.TbSecurityStore; |
39 | +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; | |
40 | 40 | import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; |
41 | 41 | |
42 | 42 | import javax.annotation.PostConstruct; |
... | ... | @@ -81,7 +81,8 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { |
81 | 81 | private final LwM2mTransportContext context; |
82 | 82 | private final LwM2MTransportServerConfig config; |
83 | 83 | private final LwM2mTransportServerHelper helper; |
84 | - private final DefaultLwM2MTransportMsgHandler handler; | |
84 | + private final OtaPackageDataCache otaPackageDataCache; | |
85 | + private final DefaultLwM2MUplinkMsgHandler handler; | |
85 | 86 | private final CaliforniumRegistrationStore registrationStore; |
86 | 87 | private final TbSecurityStore securityStore; |
87 | 88 | private final LwM2mClientContext lwM2mClientContext; |
... | ... | @@ -104,8 +105,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService { |
104 | 105 | * "coap://host:port/{path}/{token}/{nameFile}" |
105 | 106 | */ |
106 | 107 | |
107 | - | |
108 | - LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(handler, FIRMWARE_UPDATE_COAP_RECOURSE); | |
108 | + LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RECOURSE); | |
109 | 109 | this.server.coap().getServer().add(otaCoapResource); |
110 | 110 | this.startLhServer(); |
111 | 111 | this.context.setServer(server); | ... | ... |
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; | |
17 | + | |
18 | +public enum LwM2MFirmwareUpdateStrategy { | |
19 | + OBJ_5_BINARY(1, "ObjectId 5, Binary"), | |
20 | + OBJ_5_TEMP_URL(2, "ObjectId 5, URI"), | |
21 | + OBJ_19_BINARY(3, "ObjectId 19, Binary"); | |
22 | + | |
23 | + public int code; | |
24 | + public String type; | |
25 | + | |
26 | + LwM2MFirmwareUpdateStrategy(int code, String type) { | |
27 | + this.code = code; | |
28 | + this.type = type; | |
29 | + } | |
30 | + | |
31 | + public static LwM2MFirmwareUpdateStrategy fromStrategyFwByType(String type) { | |
32 | + for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { | |
33 | + if (to.type.equals(type)) { | |
34 | + return to; | |
35 | + } | |
36 | + } | |
37 | + throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); | |
38 | + } | |
39 | + | |
40 | + public static LwM2MFirmwareUpdateStrategy fromStrategyFwByCode(int code) { | |
41 | + for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { | |
42 | + if (to.code == code) { | |
43 | + return to; | |
44 | + } | |
45 | + } | |
46 | + throw new IllegalArgumentException(String.format("Unsupported FW Strategy code : %s", code)); | |
47 | + } | |
48 | +} | ... | ... |
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; | |
17 | + | |
18 | +public enum LwM2MSoftwareUpdateStrategy { | |
19 | + BINARY(1, "ObjectId 9, Binary"), | |
20 | + TEMP_URL(2, "ObjectId 9, URI"); | |
21 | + | |
22 | + public int code; | |
23 | + public String type; | |
24 | + | |
25 | + LwM2MSoftwareUpdateStrategy(int code, String type) { | |
26 | + this.code = code; | |
27 | + this.type = type; | |
28 | + } | |
29 | + | |
30 | + public static LwM2MSoftwareUpdateStrategy fromStrategySwByType(String type) { | |
31 | + for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { | |
32 | + if (to.type.equals(type)) { | |
33 | + return to; | |
34 | + } | |
35 | + } | |
36 | + throw new IllegalArgumentException(String.format("Unsupported SW Strategy type : %s", type)); | |
37 | + } | |
38 | + | |
39 | + public static LwM2MSoftwareUpdateStrategy fromStrategySwByCode(int code) { | |
40 | + for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { | |
41 | + if (to.code == code) { | |
42 | + return to; | |
43 | + } | |
44 | + } | |
45 | + throw new IllegalArgumentException(String.format("Unsupported SW Strategy code : %s", code)); | |
46 | + } | |
47 | + | |
48 | +} | ... | ... |
... | ... | @@ -84,10 +84,10 @@ public class LwM2mNetworkConfig { |
84 | 84 | Create new instance of udp endpoint context matcher. |
85 | 85 | Params: |
86 | 86 | checkAddress |
87 | - – true with address check, (STRICT, UDP) | |
87 | + – true with address check, (STRICT, UDP) - if port Registration of client is changed - it is bad | |
88 | 88 | - false, without |
89 | 89 | */ |
90 | - coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT"); | |
90 | + coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "RELAXED"); | |
91 | 91 | /** |
92 | 92 | https://tools.ietf.org/html/rfc7959#section-2.9.3 |
93 | 93 | The block size (number of bytes) to use when doing a blockwise transfer. \ |
... | ... | @@ -103,7 +103,7 @@ public class LwM2mNetworkConfig { |
103 | 103 | */ |
104 | 104 | coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 1024); |
105 | 105 | |
106 | - coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); | |
106 | + coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 10); | |
107 | 107 | |
108 | 108 | return coapConfig; |
109 | 109 | } | ... | ... |
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; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | + | |
20 | +/** | |
21 | + * Define the behavior of a write request. | |
22 | + */ | |
23 | +public enum LwM2mOperationType { | |
24 | + | |
25 | + READ(0, "Read", true), | |
26 | + DISCOVER(1, "Discover", true), | |
27 | + DISCOVER_ALL(2, "DiscoverAll", false), | |
28 | + OBSERVE_READ_ALL(3, "ObserveReadAll", false), | |
29 | + | |
30 | + OBSERVE(4, "Observe", true), | |
31 | + OBSERVE_CANCEL(5, "ObserveCancel", true), | |
32 | + OBSERVE_CANCEL_ALL(6, "ObserveCancelAll", false), | |
33 | + EXECUTE(7, "Execute", true), | |
34 | + /** | |
35 | + * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see | |
36 | + * section 5.3.3 of the LW M2M spec). | |
37 | + * if all resources are to be replaced | |
38 | + */ | |
39 | + WRITE_REPLACE(8, "WriteReplace", true), | |
40 | + | |
41 | + /** | |
42 | + * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section | |
43 | + * 5.3.3 of the LW M2M spec). | |
44 | + * if this is a partial update request | |
45 | + */ | |
46 | + WRITE_UPDATE(9, "WriteUpdate", true), | |
47 | + WRITE_ATTRIBUTES(10, "WriteAttributes", true), | |
48 | + DELETE(11, "Delete", true), | |
49 | + | |
50 | + // only for RPC | |
51 | + FW_UPDATE(12, "FirmwareUpdate", false); | |
52 | + | |
53 | +// FW_READ_INFO(12, "FirmwareReadInfo"), | |
54 | +// SW_READ_INFO(15, "SoftwareReadInfo"), | |
55 | +// SW_UPDATE(16, "SoftwareUpdate"), | |
56 | +// SW_UNINSTALL(18, "SoftwareUninstall"); | |
57 | + | |
58 | + @Getter | |
59 | + private final int code; | |
60 | + @Getter | |
61 | + private final String type; | |
62 | + @Getter | |
63 | + private final boolean hasObjectId; | |
64 | + | |
65 | + LwM2mOperationType(int code, String type, boolean hasObjectId) { | |
66 | + this.code = code; | |
67 | + this.type = type; | |
68 | + this.hasObjectId = hasObjectId; | |
69 | + } | |
70 | + | |
71 | + public static LwM2mOperationType fromType(String type) { | |
72 | + for (LwM2mOperationType to : LwM2mOperationType.values()) { | |
73 | + if (to.type.equals(type)) { | |
74 | + return to; | |
75 | + } | |
76 | + } | |
77 | + return null; | |
78 | + } | |
79 | +} | ... | ... |
... | ... | @@ -23,18 +23,18 @@ import org.eclipse.leshan.server.queue.PresenceListener; |
23 | 23 | import org.eclipse.leshan.server.registration.Registration; |
24 | 24 | import org.eclipse.leshan.server.registration.RegistrationListener; |
25 | 25 | import org.eclipse.leshan.server.registration.RegistrationUpdate; |
26 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
26 | 27 | |
27 | 28 | import java.util.Collection; |
28 | 29 | |
29 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; | |
30 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; | |
30 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; | |
31 | 31 | |
32 | 32 | @Slf4j |
33 | 33 | public class LwM2mServerListener { |
34 | 34 | |
35 | - private final LwM2mTransportMsgHandler service; | |
35 | + private final LwM2mUplinkMsgHandler service; | |
36 | 36 | |
37 | - public LwM2mServerListener(LwM2mTransportMsgHandler service) { | |
37 | + public LwM2mServerListener(LwM2mUplinkMsgHandler service) { | |
38 | 38 | this.service = service; |
39 | 39 | } |
40 | 40 | |
... | ... | @@ -86,30 +86,24 @@ public class LwM2mServerListener { |
86 | 86 | |
87 | 87 | @Override |
88 | 88 | public void cancelled(Observation observation) { |
89 | - String msg = String.format("%s: Canceled Observation %s.", LOG_LW2M_INFO, observation.getPath()); | |
90 | - service.sendLogsToThingsboard(observation.getRegistrationId(), msg); | |
91 | - log.warn(msg); | |
89 | + log.trace("Canceled Observation {}.", observation.getPath()); | |
92 | 90 | } |
93 | 91 | |
94 | 92 | @Override |
95 | 93 | public void onResponse(Observation observation, Registration registration, ObserveResponse response) { |
96 | 94 | if (registration != null) { |
97 | - service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), | |
98 | - registration), response, null); | |
95 | + service.onUpdateValueAfterReadResponse(registration, convertObjectIdToVersionedId(observation.getPath().toString(), registration), response); | |
99 | 96 | } |
100 | 97 | } |
101 | 98 | |
102 | 99 | @Override |
103 | 100 | public void onError(Observation observation, Registration registration, Exception error) { |
104 | - log.error(String.format("Unable to handle notification of [%s:%s]", observation.getRegistrationId(), observation.getPath()), error); | |
101 | + log.error("Unable to handle notification of [{}:{}]", observation.getRegistrationId(), observation.getPath(), error); | |
105 | 102 | } |
106 | 103 | |
107 | 104 | @Override |
108 | 105 | public void newObservation(Observation observation, Registration registration) { |
109 | - String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO, | |
110 | - observation.getPath()); | |
111 | - log.warn(msg); | |
112 | - service.sendLogsToThingsboard(registration.getId(), msg); | |
106 | + log.trace("Successful start newObservation {}.", observation.getPath()); | |
113 | 107 | } |
114 | 108 | }; |
115 | 109 | } | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server; |
17 | 17 | |
18 | 18 | import io.netty.util.concurrent.Future; |
19 | 19 | import io.netty.util.concurrent.GenericFutureListener; |
20 | +import lombok.RequiredArgsConstructor; | |
20 | 21 | import lombok.extern.slf4j.Slf4j; |
21 | 22 | import org.jetbrains.annotations.NotNull; |
22 | 23 | import org.thingsboard.server.common.data.Device; |
... | ... | @@ -30,28 +31,29 @@ import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotifica |
30 | 31 | import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg; |
31 | 32 | import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg; |
32 | 33 | import org.thingsboard.server.gen.transport.TransportProtos.ToTransportUpdateCredentialsProto; |
34 | +import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; | |
35 | +import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; | |
36 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
33 | 37 | |
34 | 38 | import java.util.Optional; |
35 | 39 | import java.util.UUID; |
36 | 40 | |
37 | 41 | @Slf4j |
42 | +@RequiredArgsConstructor | |
38 | 43 | public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? super Void>>, SessionMsgListener { |
39 | - private DefaultLwM2MTransportMsgHandler handler; | |
40 | - private TransportProtos.SessionInfoProto sessionInfo; | |
41 | - | |
42 | - public LwM2mSessionMsgListener(DefaultLwM2MTransportMsgHandler handler, TransportProtos.SessionInfoProto sessionInfo) { | |
43 | - this.handler = handler; | |
44 | - this.sessionInfo = sessionInfo; | |
45 | - } | |
44 | + private final LwM2mUplinkMsgHandler handler; | |
45 | + private final LwM2MAttributesService attributesService; | |
46 | + private final LwM2MRpcRequestHandler rpcHandler; | |
47 | + private final TransportProtos.SessionInfoProto sessionInfo; | |
46 | 48 | |
47 | 49 | @Override |
48 | 50 | public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) { |
49 | - this.handler.onGetAttributesResponse(getAttributesResponse, this.sessionInfo); | |
51 | + this.attributesService.onGetAttributesResponse(getAttributesResponse, this.sessionInfo); | |
50 | 52 | } |
51 | 53 | |
52 | 54 | @Override |
53 | 55 | public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) { |
54 | - this.handler.onAttributeUpdate(attributeUpdateNotification, this.sessionInfo); | |
56 | + this.attributesService.onAttributesUpdate(attributeUpdateNotification, this.sessionInfo); | |
55 | 57 | } |
56 | 58 | |
57 | 59 | @Override |
... | ... | @@ -76,12 +78,12 @@ public class LwM2mSessionMsgListener implements GenericFutureListener<Future<? s |
76 | 78 | |
77 | 79 | @Override |
78 | 80 | public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) { |
79 | - this.handler.onToDeviceRpcRequest(toDeviceRequest,this.sessionInfo); | |
81 | + this.rpcHandler.onToDeviceRpcRequest(toDeviceRequest,this.sessionInfo); | |
80 | 82 | } |
81 | 83 | |
82 | 84 | @Override |
83 | 85 | public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) { |
84 | - this.handler.onToServerRpcResponse(toServerResponse); | |
86 | + this.rpcHandler.onToServerRpcResponse(toServerResponse); | |
85 | 87 | } |
86 | 88 | |
87 | 89 | @Override | ... | ... |
... | ... | @@ -24,6 +24,9 @@ import org.eclipse.californium.core.observe.ObserveRelation; |
24 | 24 | import org.eclipse.californium.core.server.resources.CoapExchange; |
25 | 25 | import org.eclipse.californium.core.server.resources.Resource; |
26 | 26 | import org.eclipse.californium.core.server.resources.ResourceObserver; |
27 | +import org.thingsboard.server.cache.ota.OtaPackageDataCache; | |
28 | +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; | |
29 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
27 | 30 | |
28 | 31 | import java.util.UUID; |
29 | 32 | import java.util.concurrent.ConcurrentHashMap; |
... | ... | @@ -37,11 +40,11 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.S |
37 | 40 | public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { |
38 | 41 | private final ConcurrentMap<String, ObserveRelation> tokenToObserveRelationMap = new ConcurrentHashMap<>(); |
39 | 42 | private final ConcurrentMap<String, AtomicInteger> tokenToObserveNotificationSeqMap = new ConcurrentHashMap<>(); |
40 | - private final LwM2mTransportMsgHandler handler; | |
43 | + private final OtaPackageDataCache otaPackageDataCache; | |
41 | 44 | |
42 | - public LwM2mTransportCoapResource(LwM2mTransportMsgHandler handler, String name) { | |
45 | + public LwM2mTransportCoapResource(OtaPackageDataCache otaPackageDataCache, String name) { | |
43 | 46 | super(name); |
44 | - this.handler = handler; | |
47 | + this.otaPackageDataCache = otaPackageDataCache; | |
45 | 48 | this.setObservable(true); // enable observing |
46 | 49 | this.addObserver(new CoapResourceObserver()); |
47 | 50 | } |
... | ... | @@ -140,9 +143,9 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { |
140 | 143 | response.setPayload(fwData); |
141 | 144 | if (exchange.getRequestOptions().getBlock2() != null) { |
142 | 145 | int chunkSize = exchange.getRequestOptions().getBlock2().getSzx(); |
143 | - boolean moreFlag = fwData.length > chunkSize; | |
144 | - response.getOptions().setBlock2(chunkSize, moreFlag, 0); | |
145 | - log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, moreFlag); | |
146 | + boolean lastFlag = fwData.length > chunkSize; | |
147 | + response.getOptions().setBlock2(chunkSize, lastFlag, 0); | |
148 | + log.warn("92) with blokc2 Send currentId: [{}], length: [{}], chunkSize [{}], moreFlag [{}]", currentId.toString(), fwData.length, chunkSize, lastFlag); | |
146 | 149 | } |
147 | 150 | else { |
148 | 151 | log.warn("92) with block1 Send currentId: [{}], length: [{}], ", currentId.toString(), fwData.length); |
... | ... | @@ -152,7 +155,7 @@ public class LwM2mTransportCoapResource extends AbstractLwM2mTransportResource { |
152 | 155 | } |
153 | 156 | |
154 | 157 | private byte[] getOtaData(UUID currentId) { |
155 | - return ((DefaultLwM2MTransportMsgHandler) handler).otaPackageDataCache.get(currentId.toString()); | |
158 | + return otaPackageDataCache.get(currentId.toString()); | |
156 | 159 | } |
157 | 160 | |
158 | 161 | } | ... | ... |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportRequest.java
deleted
100644 → 0
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; | |
17 | - | |
18 | -import lombok.RequiredArgsConstructor; | |
19 | -import lombok.extern.slf4j.Slf4j; | |
20 | -import org.eclipse.californium.core.coap.CoAP; | |
21 | -import org.eclipse.californium.core.coap.Response; | |
22 | -import org.eclipse.leshan.core.Link; | |
23 | -import org.eclipse.leshan.core.model.ResourceModel; | |
24 | -import org.eclipse.leshan.core.node.LwM2mNode; | |
25 | -import org.eclipse.leshan.core.node.LwM2mObject; | |
26 | -import org.eclipse.leshan.core.node.LwM2mObjectInstance; | |
27 | -import org.eclipse.leshan.core.node.LwM2mPath; | |
28 | -import org.eclipse.leshan.core.node.LwM2mResource; | |
29 | -import org.eclipse.leshan.core.node.LwM2mSingleResource; | |
30 | -import org.eclipse.leshan.core.node.ObjectLink; | |
31 | -import org.eclipse.leshan.core.observation.Observation; | |
32 | -import org.eclipse.leshan.core.request.ContentFormat; | |
33 | -import org.eclipse.leshan.core.request.DeleteRequest; | |
34 | -import org.eclipse.leshan.core.request.DiscoverRequest; | |
35 | -import org.eclipse.leshan.core.request.ExecuteRequest; | |
36 | -import org.eclipse.leshan.core.request.ObserveRequest; | |
37 | -import org.eclipse.leshan.core.request.ReadRequest; | |
38 | -import org.eclipse.leshan.core.request.SimpleDownlinkRequest; | |
39 | -import org.eclipse.leshan.core.request.WriteAttributesRequest; | |
40 | -import org.eclipse.leshan.core.request.WriteRequest; | |
41 | -import org.eclipse.leshan.core.request.exception.ClientSleepingException; | |
42 | -import org.eclipse.leshan.core.response.DeleteResponse; | |
43 | -import org.eclipse.leshan.core.response.DiscoverResponse; | |
44 | -import org.eclipse.leshan.core.response.ExecuteResponse; | |
45 | -import org.eclipse.leshan.core.response.LwM2mResponse; | |
46 | -import org.eclipse.leshan.core.response.ReadResponse; | |
47 | -import org.eclipse.leshan.core.response.ResponseCallback; | |
48 | -import org.eclipse.leshan.core.response.WriteAttributesResponse; | |
49 | -import org.eclipse.leshan.core.response.WriteResponse; | |
50 | -import org.eclipse.leshan.core.util.Hex; | |
51 | -import org.eclipse.leshan.core.util.NamedThreadFactory; | |
52 | -import org.eclipse.leshan.server.registration.Registration; | |
53 | -import org.springframework.stereotype.Service; | |
54 | -import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | |
55 | -import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | |
56 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
57 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | |
58 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; | |
59 | -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; | |
60 | - | |
61 | -import javax.annotation.PostConstruct; | |
62 | -import java.util.Arrays; | |
63 | -import java.util.Collection; | |
64 | -import java.util.Date; | |
65 | -import java.util.Set; | |
66 | -import java.util.concurrent.ConcurrentHashMap; | |
67 | -import java.util.concurrent.ExecutorService; | |
68 | -import java.util.concurrent.Executors; | |
69 | -import java.util.stream.Collectors; | |
70 | - | |
71 | -import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; | |
72 | -import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; | |
73 | -import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; | |
74 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; | |
75 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; | |
76 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getContentFormatByResourceModelType; | |
77 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.DEFAULT_TIMEOUT; | |
78 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID; | |
79 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; | |
80 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; | |
81 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; | |
82 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_VALUE; | |
83 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; | |
84 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER; | |
85 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; | |
86 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; | |
87 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; | |
88 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; | |
89 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; | |
90 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; | |
91 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESPONSE_REQUEST_CHANNEL; | |
92 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; | |
93 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID; | |
94 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; | |
95 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; | |
96 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.createWriteAttributeRequest; | |
97 | - | |
98 | -@Slf4j | |
99 | -@Service | |
100 | -@TbLwM2mTransportComponent | |
101 | -@RequiredArgsConstructor | |
102 | -public class LwM2mTransportRequest { | |
103 | - private ExecutorService responseRequestExecutor; | |
104 | - | |
105 | - public LwM2mValueConverterImpl converter; | |
106 | - | |
107 | - private final LwM2mTransportContext context; | |
108 | - private final LwM2MTransportServerConfig config; | |
109 | - private final LwM2mClientContext lwM2mClientContext; | |
110 | - private final DefaultLwM2MTransportMsgHandler handler; | |
111 | - | |
112 | - @PostConstruct | |
113 | - public void init() { | |
114 | - this.converter = LwM2mValueConverterImpl.getInstance(); | |
115 | - responseRequestExecutor = Executors.newFixedThreadPool(this.config.getResponsePoolSize(), | |
116 | - new NamedThreadFactory(String.format("LwM2M %s channel response after request", RESPONSE_REQUEST_CHANNEL))); | |
117 | - } | |
118 | - | |
119 | - public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { | |
120 | - sendAllRequest(lwM2MClient, targetIdVer, typeOper, lwM2MClient.getDefaultContentFormat(), params, timeoutInMs, lwm2mClientRpcRequest); | |
121 | - } | |
122 | - | |
123 | - | |
124 | - public void sendAllRequest(LwM2mClient lwM2MClient, String targetIdVer, LwM2mTypeOper typeOper, | |
125 | - ContentFormat contentFormat, Object params, long timeoutInMs, LwM2mClientRpcRequest lwm2mClientRpcRequest) { | |
126 | - Registration registration = lwM2MClient.getRegistration(); | |
127 | - try { | |
128 | - String target = convertPathFromIdVerToObjectId(targetIdVer); | |
129 | - if(contentFormat == null){ | |
130 | - contentFormat = ContentFormat.DEFAULT; | |
131 | - } | |
132 | - LwM2mPath resultIds = target != null ? new LwM2mPath(target) : null; | |
133 | - if (!OBSERVE_CANCEL.name().equals(typeOper.name()) && resultIds != null && registration != null && resultIds.getObjectId() >= 0 && lwM2MClient != null) { | |
134 | - if (lwM2MClient.isValidObjectVersion(targetIdVer)) { | |
135 | - timeoutInMs = timeoutInMs > 0 ? timeoutInMs : DEFAULT_TIMEOUT; | |
136 | - SimpleDownlinkRequest request = createRequest(registration, lwM2MClient, typeOper, contentFormat, target, | |
137 | - targetIdVer, resultIds, params, lwm2mClientRpcRequest); | |
138 | - if (request != null) { | |
139 | - try { | |
140 | - this.sendRequest(registration, lwM2MClient, request, timeoutInMs, lwm2mClientRpcRequest); | |
141 | - } catch (ClientSleepingException e) { | |
142 | - SimpleDownlinkRequest finalRequest = request; | |
143 | - long finalTimeoutInMs = timeoutInMs; | |
144 | - LwM2mClientRpcRequest finalRpcRequest = lwm2mClientRpcRequest; | |
145 | - lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, finalRpcRequest)); | |
146 | - } catch (Exception e) { | |
147 | - log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); | |
148 | - } | |
149 | - } else if (WRITE_UPDATE.name().equals(typeOper.name())) { | |
150 | - if (lwm2mClientRpcRequest != null) { | |
151 | - String errorMsg = String.format("Path %s params is not valid", targetIdVer); | |
152 | - handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); | |
153 | - } | |
154 | - } else if (WRITE_REPLACE.name().equals(typeOper.name()) || EXECUTE.name().equals(typeOper.name())) { | |
155 | - if (lwm2mClientRpcRequest != null) { | |
156 | - String errorMsg = String.format("Path %s object model is absent", targetIdVer); | |
157 | - handler.sentRpcResponse(lwm2mClientRpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); | |
158 | - } | |
159 | - } else if (!OBSERVE_CANCEL.name().equals(typeOper.name())) { | |
160 | - log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); | |
161 | - if (lwm2mClientRpcRequest != null) { | |
162 | - ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); | |
163 | - String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; | |
164 | - handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); | |
165 | - } | |
166 | - } | |
167 | - } else if (lwm2mClientRpcRequest != null) { | |
168 | - String errorMsg = String.format("Path %s not found in object version", targetIdVer); | |
169 | - handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); | |
170 | - } | |
171 | - } else { | |
172 | - switch (typeOper) { | |
173 | - case OBSERVE_READ_ALL: | |
174 | - case DISCOVER_ALL: | |
175 | - Set<String> paths; | |
176 | - if (OBSERVE_READ_ALL.name().equals(typeOper.name())) { | |
177 | - Set<Observation> observations = context.getServer().getObservationService().getObservations(registration); | |
178 | - paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); | |
179 | - } else { | |
180 | - assert registration != null; | |
181 | - Link[] objectLinks = registration.getSortedObjectLinks(); | |
182 | - paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet()); | |
183 | - } | |
184 | - String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO, | |
185 | - typeOper.name(), paths); | |
186 | - this.handler.sendLogsToThingsboard(lwM2MClient, msg); | |
187 | - if (lwm2mClientRpcRequest != null) { | |
188 | - String valueMsg = String.format("Paths - %s", paths); | |
189 | - handler.sentRpcResponse(lwm2mClientRpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); | |
190 | - } | |
191 | - break; | |
192 | - case OBSERVE_CANCEL: | |
193 | - case OBSERVE_CANCEL_ALL: | |
194 | - int observeCancelCnt = 0; | |
195 | - String observeCancelMsg = null; | |
196 | - if (OBSERVE_CANCEL.name().equals(typeOper)) { | |
197 | - observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration, target); | |
198 | - observeCancelMsg = String.format("%s: type operation %s paths: %s count: %d", LOG_LW2M_INFO, | |
199 | - OBSERVE_CANCEL.name(), target, observeCancelCnt); | |
200 | - } else { | |
201 | - observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration); | |
202 | - observeCancelMsg = String.format("%s: type operation %s paths: All count: %d", LOG_LW2M_INFO, | |
203 | - OBSERVE_CANCEL.name(), observeCancelCnt); | |
204 | - } | |
205 | - this.afterObserveCancel(lwM2MClient, observeCancelCnt, observeCancelMsg, lwm2mClientRpcRequest); | |
206 | - break; | |
207 | - // lwm2mClientRpcRequest != null | |
208 | - case FW_UPDATE: | |
209 | - handler.getInfoFirmwareUpdate(lwM2MClient, lwm2mClientRpcRequest); | |
210 | - break; | |
211 | - } | |
212 | - } | |
213 | - } catch (Exception e) { | |
214 | - String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, | |
215 | - typeOper.name(), e.getMessage()); | |
216 | - handler.sendLogsToThingsboard(lwM2MClient, msg); | |
217 | - if (lwm2mClientRpcRequest != null) { | |
218 | - String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage()); | |
219 | - handler.sentRpcResponse(lwm2mClientRpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); | |
220 | - } | |
221 | - } | |
222 | - } | |
223 | - | |
224 | - private SimpleDownlinkRequest createRequest(Registration registration, LwM2mClient lwM2MClient, LwM2mTypeOper typeOper, | |
225 | - ContentFormat contentFormat, String target, String targetIdVer, | |
226 | - LwM2mPath resultIds, Object params, LwM2mClientRpcRequest rpcRequest) { | |
227 | - SimpleDownlinkRequest request = null; | |
228 | - switch (typeOper) { | |
229 | - case READ: | |
230 | - request = new ReadRequest(contentFormat, target); | |
231 | - break; | |
232 | - case DISCOVER: | |
233 | - request = new DiscoverRequest(target); | |
234 | - break; | |
235 | - case OBSERVE: | |
236 | - String msg = String.format("%s: Send Observation %s.", LOG_LW2M_INFO, targetIdVer); | |
237 | - log.warn(msg); | |
238 | - if (resultIds.isResource()) { | |
239 | - Set<Observation> observations = context.getServer().getObservationService().getObservations(registration); | |
240 | - Set<Observation> paths = observations.stream().filter(observation -> observation.getPath().equals(resultIds)).collect(Collectors.toSet()); | |
241 | - if (paths.size() == 0) { | |
242 | - request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); | |
243 | - } else { | |
244 | - request = new ReadRequest(contentFormat, target); | |
245 | - } | |
246 | - } else if (resultIds.isObjectInstance()) { | |
247 | - request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); | |
248 | - } else if (resultIds.getObjectId() >= 0) { | |
249 | - request = new ObserveRequest(contentFormat, resultIds.getObjectId()); | |
250 | - } | |
251 | - break; | |
252 | - case EXECUTE: | |
253 | - ResourceModel resourceModelExecute = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); | |
254 | - if (resourceModelExecute != null) { | |
255 | - if (params != null && !resourceModelExecute.multiple) { | |
256 | - request = new ExecuteRequest(target, (String) this.converter.convertValue(params, resourceModelExecute.type, ResourceModel.Type.STRING, resultIds)); | |
257 | - } else { | |
258 | - request = new ExecuteRequest(target); | |
259 | - } | |
260 | - } | |
261 | - break; | |
262 | - case WRITE_REPLACE: | |
263 | - /** | |
264 | - * Request to write a <b>String Single-Instance Resource</b> using the TLV content format. | |
265 | - * Type from resourceModel -> STRING, INTEGER, FLOAT, BOOLEAN, OPAQUE, TIME, OBJLNK | |
266 | - * contentFormat -> TLV, TLV, TLV, TLV, OPAQUE, TLV, LINK | |
267 | - * JSON, TEXT; | |
268 | - **/ | |
269 | - ResourceModel resourceModelWrite = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()); | |
270 | - if (resourceModelWrite != null) { | |
271 | - contentFormat = getContentFormatByResourceModelType(resourceModelWrite, contentFormat); | |
272 | - request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), | |
273 | - resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModelWrite.type, | |
274 | - lwM2MClient, rpcRequest); | |
275 | - } | |
276 | - break; | |
277 | - case WRITE_UPDATE: | |
278 | - if (resultIds.isResource()) { | |
279 | - /** | |
280 | - * send request: path = '/3/0' node == wM2mObjectInstance | |
281 | - * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}} | |
282 | - **/ | |
283 | - Collection<LwM2mResource> resources = lwM2MClient.getNewResourceForInstance( | |
284 | - targetIdVer, params, | |
285 | - this.config.getModelProvider(), | |
286 | - this.converter); | |
287 | - contentFormat = getContentFormatByResourceModelType(lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider()), | |
288 | - contentFormat); | |
289 | - request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), | |
290 | - resultIds.getObjectInstanceId(), resources); | |
291 | - } | |
292 | - /** | |
293 | - * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}" | |
294 | - * int rscId = resultIds.getObjectInstanceId(); | |
295 | - * contentFormat – Format of the payload (TLV or JSON). | |
296 | - */ | |
297 | - else if (resultIds.isObjectInstance()) { | |
298 | - if (((ConcurrentHashMap) params).size() > 0) { | |
299 | - Collection<LwM2mResource> resources = lwM2MClient.getNewResourcesForInstance( | |
300 | - targetIdVer, params, | |
301 | - this.config.getModelProvider(), | |
302 | - this.converter); | |
303 | - if (resources.size() > 0) { | |
304 | - contentFormat = contentFormat.equals(ContentFormat.JSON) ? contentFormat : ContentFormat.TLV; | |
305 | - request = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), | |
306 | - resultIds.getObjectInstanceId(), resources); | |
307 | - } | |
308 | - } | |
309 | - } else if (resultIds.getObjectId() >= 0) { | |
310 | - request = new ObserveRequest(resultIds.getObjectId()); | |
311 | - } | |
312 | - break; | |
313 | - case WRITE_ATTRIBUTES: | |
314 | - request = createWriteAttributeRequest(target, params, this.handler); | |
315 | - break; | |
316 | - case DELETE: | |
317 | - request = new DeleteRequest(target); | |
318 | - break; | |
319 | - } | |
320 | - return request; | |
321 | - } | |
322 | - | |
323 | - /** | |
324 | - * @param registration - | |
325 | - * @param request - | |
326 | - * @param timeoutInMs - | |
327 | - */ | |
328 | - | |
329 | - @SuppressWarnings({"error sendRequest"}) | |
330 | - private void sendRequest(Registration registration, LwM2mClient lwM2MClient, SimpleDownlinkRequest request, | |
331 | - long timeoutInMs, LwM2mClientRpcRequest rpcRequest) { | |
332 | - context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { | |
333 | - | |
334 | - if (!lwM2MClient.isInit()) { | |
335 | - lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); | |
336 | - } | |
337 | - if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { | |
338 | - this.handleResponse(lwM2MClient, request.getPath().toString(), response, request, rpcRequest); | |
339 | - } else { | |
340 | - String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), | |
341 | - ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); | |
342 | - handler.sendLogsToThingsboard(lwM2MClient, msg); | |
343 | - log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), | |
344 | - ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); | |
345 | - if (!lwM2MClient.isInit()) { | |
346 | - lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); | |
347 | - } | |
348 | - /** Not Found */ | |
349 | - if (rpcRequest != null) { | |
350 | - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); | |
351 | - } | |
352 | - /** Not Found | |
353 | - set setClient_fw_info... = empty | |
354 | - **/ | |
355 | - if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { | |
356 | - lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); | |
357 | - } | |
358 | - if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { | |
359 | - lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); | |
360 | - } | |
361 | - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { | |
362 | - this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage()); | |
363 | - } | |
364 | - if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { | |
365 | - this.afterExecuteFwSwUpdateError(registration, request, response.getErrorMessage()); | |
366 | - } | |
367 | - } | |
368 | - }, e -> { | |
369 | - /** version == null | |
370 | - set setClient_fw_info... = empty | |
371 | - **/ | |
372 | - if (lwM2MClient.getFwUpdate() != null && lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) { | |
373 | - lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString()); | |
374 | - } | |
375 | - if (lwM2MClient.getSwUpdate() != null && lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) { | |
376 | - lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString()); | |
377 | - } | |
378 | - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { | |
379 | - this.afterWriteFwSWUpdateError(registration, request, e.getMessage()); | |
380 | - } | |
381 | - if (request.getPath().toString().equals(FW_UPDATE_ID) || request.getPath().toString().equals(SW_INSTALL_ID)) { | |
382 | - this.afterExecuteFwSwUpdateError(registration, request, e.getMessage()); | |
383 | - } | |
384 | - if (!lwM2MClient.isInit()) { | |
385 | - lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); | |
386 | - } | |
387 | - String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", | |
388 | - LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); | |
389 | - handler.sendLogsToThingsboard(lwM2MClient, msg); | |
390 | - log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); | |
391 | - if (rpcRequest != null) { | |
392 | - handler.sentRpcResponse(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); | |
393 | - } | |
394 | - }); | |
395 | - } | |
396 | - | |
397 | - private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, | |
398 | - Integer resourceId, Object value, ResourceModel.Type type, | |
399 | - LwM2mClient client, LwM2mClientRpcRequest rpcRequest) { | |
400 | - try { | |
401 | - if (type != null) { | |
402 | - switch (type) { | |
403 | - case STRING: // String | |
404 | - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, value.toString()) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, value.toString()); | |
405 | - case INTEGER: // Long | |
406 | - final long valueInt = Integer.toUnsignedLong(Integer.parseInt(value.toString())); | |
407 | - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueInt) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueInt); | |
408 | - case OBJLNK: // ObjectLink | |
409 | - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())); | |
410 | - case BOOLEAN: // Boolean | |
411 | - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString())); | |
412 | - case FLOAT: // Double | |
413 | - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Double.parseDouble(value.toString())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.parseDouble(value.toString())); | |
414 | - case TIME: // Date | |
415 | - Date date = new Date(Long.decode(value.toString())); | |
416 | - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); | |
417 | - case OPAQUE: // byte[] value, base64 | |
418 | - byte[] valueRequest = value instanceof byte[] ? (byte[]) value : Hex.decodeHex(value.toString().toCharArray()); | |
419 | - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueRequest) : | |
420 | - new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest); | |
421 | - default: | |
422 | - } | |
423 | - } | |
424 | - if (rpcRequest != null) { | |
425 | - String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; | |
426 | - String errorMsg = String.format("Bad ResourceModel Operations (E): Resource path - %s ResourceModel type - %s", patn, type); | |
427 | - rpcRequest.setErrorMsg(errorMsg); | |
428 | - } | |
429 | - return null; | |
430 | - } catch (NumberFormatException e) { | |
431 | - String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; | |
432 | - String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", | |
433 | - patn, type, value, e.toString()); | |
434 | - handler.sendLogsToThingsboard(client, msg); | |
435 | - log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); | |
436 | - if (rpcRequest != null) { | |
437 | - String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); | |
438 | - handler.sentRpcResponse(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR); | |
439 | - } | |
440 | - return null; | |
441 | - } | |
442 | - } | |
443 | - | |
444 | - private void handleResponse(LwM2mClient lwM2mClient, final String path, LwM2mResponse response, | |
445 | - SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { | |
446 | - responseRequestExecutor.submit(() -> { | |
447 | - try { | |
448 | - this.sendResponse(lwM2mClient, path, response, request, rpcRequest); | |
449 | - } catch (Exception e) { | |
450 | - log.error("[{}] endpoint [{}] path [{}] Exception Unable to after send response.", lwM2mClient.getRegistration().getEndpoint(), path, e); | |
451 | - } | |
452 | - }); | |
453 | - } | |
454 | - | |
455 | - /** | |
456 | - * processing a response from a client | |
457 | - * | |
458 | - * @param path - | |
459 | - * @param response - | |
460 | - */ | |
461 | - private void sendResponse(LwM2mClient lwM2mClient, String path, LwM2mResponse response, | |
462 | - SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { | |
463 | - Registration registration = lwM2mClient.getRegistration(); | |
464 | - String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); | |
465 | - String msgLog = ""; | |
466 | - if (response instanceof ReadResponse) { | |
467 | - handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); | |
468 | - } else if (response instanceof DeleteResponse) { | |
469 | - log.warn("11) [{}] Path [{}] DeleteResponse", pathIdVer, response); | |
470 | - if (rpcRequest != null) { | |
471 | - rpcRequest.setInfoMsg(null); | |
472 | - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), null, null); | |
473 | - } | |
474 | - } else if (response instanceof DiscoverResponse) { | |
475 | - String discoverValue = Link.serialize(((DiscoverResponse) response).getObjectLinks()); | |
476 | - msgLog = String.format("%s: type operation: %s path: %s value: %s", | |
477 | - LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue); | |
478 | - handler.sendLogsToThingsboard(lwM2mClient, msgLog); | |
479 | - log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response); | |
480 | - if (rpcRequest != null) { | |
481 | - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE); | |
482 | - } | |
483 | - } else if (response instanceof ExecuteResponse) { | |
484 | - msgLog = String.format("%s: type operation: %s path: %s", | |
485 | - LOG_LW2M_INFO, EXECUTE.name(), request.getPath().toString()); | |
486 | - log.warn("9) [{}] ", msgLog); | |
487 | - handler.sendLogsToThingsboard(lwM2mClient, msgLog); | |
488 | - if (rpcRequest != null) { | |
489 | - msgLog = String.format("Start %s path: %S. Preparation finished: %s", EXECUTE.name(), path, rpcRequest.getInfoMsg()); | |
490 | - rpcRequest.setInfoMsg(msgLog); | |
491 | - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), path, LOG_LW2M_INFO); | |
492 | - } | |
493 | - | |
494 | - } else if (response instanceof WriteAttributesResponse) { | |
495 | - msgLog = String.format("%s: type operation: %s path: %s value: %s", | |
496 | - LOG_LW2M_INFO, WRITE_ATTRIBUTES.name(), request.getPath().toString(), ((WriteAttributesRequest) request).getAttributes().toString()); | |
497 | - handler.sendLogsToThingsboard(lwM2mClient, msgLog); | |
498 | - log.warn("12) [{}] Path [{}] WriteAttributesResponse", pathIdVer, response); | |
499 | - if (rpcRequest != null) { | |
500 | - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), response.toString(), LOG_LW2M_VALUE); | |
501 | - } | |
502 | - } else if (response instanceof WriteResponse) { | |
503 | - msgLog = String.format("Type operation: Write path: %s", pathIdVer); | |
504 | - log.warn("10) [{}] response: [{}]", msgLog, response); | |
505 | - this.infoWriteResponse(lwM2mClient, response, request, rpcRequest); | |
506 | - handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); | |
507 | - } | |
508 | - } | |
509 | - | |
510 | - private void infoWriteResponse(LwM2mClient lwM2mClient, LwM2mResponse response, SimpleDownlinkRequest request, LwM2mClientRpcRequest rpcRequest) { | |
511 | - try { | |
512 | - Registration registration = lwM2mClient.getRegistration(); | |
513 | - LwM2mNode node = ((WriteRequest) request).getNode(); | |
514 | - String msg = null; | |
515 | - Object value; | |
516 | - if (node instanceof LwM2mObject) { | |
517 | - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", | |
518 | - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObject) node).toString()); | |
519 | - } else if (node instanceof LwM2mObjectInstance) { | |
520 | - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Source path: %s value: %s", | |
521 | - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), ((LwM2mObjectInstance) node).prettyPrint()); | |
522 | - } else if (node instanceof LwM2mSingleResource) { | |
523 | - LwM2mSingleResource singleResource = (LwM2mSingleResource) node; | |
524 | - if (singleResource.getType() == ResourceModel.Type.STRING || singleResource.getType() == ResourceModel.Type.OPAQUE) { | |
525 | - int valueLength; | |
526 | - if (singleResource.getType() == ResourceModel.Type.STRING) { | |
527 | - valueLength = ((String) singleResource.getValue()).length(); | |
528 | - value = ((String) singleResource.getValue()) | |
529 | - .substring(Math.min(valueLength, config.getLogMaxLength())).trim(); | |
530 | - | |
531 | - } else { | |
532 | - valueLength = ((byte[]) singleResource.getValue()).length; | |
533 | - value = new String(Arrays.copyOf(((byte[]) singleResource.getValue()), | |
534 | - Math.min(valueLength, config.getLogMaxLength()))).trim(); | |
535 | - } | |
536 | - value = valueLength > config.getLogMaxLength() ? value + "..." : value; | |
537 | - msg = String.format("%s: Update finished successfully: Lwm2m code - %d Resource path: %s length: %s value: %s", | |
538 | - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), valueLength, value); | |
539 | - } else { | |
540 | - value = this.converter.convertValue(singleResource.getValue(), | |
541 | - singleResource.getType(), ResourceModel.Type.STRING, request.getPath()); | |
542 | - msg = String.format("%s: Update finished successfully. Lwm2m code: %d Resource path: %s value: %s", | |
543 | - LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value); | |
544 | - } | |
545 | - } | |
546 | - if (msg != null) { | |
547 | - handler.sendLogsToThingsboard(lwM2mClient, msg); | |
548 | - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) { | |
549 | - this.afterWriteSuccessFwSwUpdate(registration, request); | |
550 | - if (rpcRequest != null) { | |
551 | - rpcRequest.setInfoMsg(msg); | |
552 | - } | |
553 | - } | |
554 | - else if (rpcRequest != null) { | |
555 | - handler.sentRpcResponse(rpcRequest, response.getCode().getName(), msg, LOG_LW2M_INFO); | |
556 | - } | |
557 | - } | |
558 | - } catch (Exception e) { | |
559 | - log.trace("Fail convert value from request to string. ", e); | |
560 | - } | |
561 | - } | |
562 | - | |
563 | - /** | |
564 | - * After finish operation FwSwUpdate Write (success): | |
565 | - * fw_state/sw_state = DOWNLOADED | |
566 | - * send operation Execute | |
567 | - */ | |
568 | - private void afterWriteSuccessFwSwUpdate(Registration registration, SimpleDownlinkRequest request) { | |
569 | - LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); | |
570 | - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && lwM2MClient.getFwUpdate() != null) { | |
571 | - lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name()); | |
572 | - lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); | |
573 | - } | |
574 | - if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) { | |
575 | - lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name()); | |
576 | - lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); | |
577 | - } | |
578 | - } | |
579 | - | |
580 | - /** | |
581 | - * After finish operation FwSwUpdate Write (error): fw_state = FAILED | |
582 | - */ | |
583 | - private void afterWriteFwSWUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) { | |
584 | - LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); | |
585 | - if (request.getPath().toString().equals(FW_PACKAGE_5_ID) && lwM2MClient.getFwUpdate() != null) { | |
586 | - lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name()); | |
587 | - lwM2MClient.getFwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); | |
588 | - } | |
589 | - if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) { | |
590 | - lwM2MClient.getSwUpdate().setStateUpdate(FAILED.name()); | |
591 | - lwM2MClient.getSwUpdate().sendLogs(this.handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); | |
592 | - } | |
593 | - } | |
594 | - | |
595 | - private void afterExecuteFwSwUpdateError(Registration registration, SimpleDownlinkRequest request, String msgError) { | |
596 | - LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId()); | |
597 | - if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) { | |
598 | - lwM2MClient.getFwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); | |
599 | - } | |
600 | - if (request.getPath().toString().equals(SW_INSTALL_ID) && lwM2MClient.getSwUpdate() != null) { | |
601 | - lwM2MClient.getSwUpdate().sendLogs(this.handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError); | |
602 | - } | |
603 | - } | |
604 | - | |
605 | - private void afterObserveCancel(LwM2mClient lwM2mClient, int observeCancelCnt, String observeCancelMsg, LwM2mClientRpcRequest rpcRequest) { | |
606 | - handler.sendLogsToThingsboard(lwM2mClient, observeCancelMsg); | |
607 | - log.warn("[{}]", observeCancelMsg); | |
608 | - if (rpcRequest != null) { | |
609 | - rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt)); | |
610 | - handler.sentRpcResponse(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO); | |
611 | - } | |
612 | - } | |
613 | -} |
... | ... | @@ -37,6 +37,8 @@ import org.eclipse.leshan.core.model.DefaultDDFFileValidator; |
37 | 37 | import org.eclipse.leshan.core.model.InvalidDDFFileException; |
38 | 38 | import org.eclipse.leshan.core.model.ObjectModel; |
39 | 39 | import org.eclipse.leshan.core.model.ResourceModel; |
40 | +import org.eclipse.leshan.core.node.LwM2mPath; | |
41 | +import org.eclipse.leshan.core.node.LwM2mResource; | |
40 | 42 | import org.eclipse.leshan.core.node.codec.CodecException; |
41 | 43 | import org.eclipse.leshan.core.request.ContentFormat; |
42 | 44 | import org.springframework.stereotype.Component; |
... | ... | @@ -48,6 +50,8 @@ import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg; |
48 | 50 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
49 | 51 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
50 | 52 | import org.thingsboard.server.transport.lwm2m.server.adaptors.LwM2MJsonAdaptor; |
53 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
54 | +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; | |
51 | 55 | |
52 | 56 | import java.io.ByteArrayInputStream; |
53 | 57 | import java.io.IOException; |
... | ... | @@ -57,6 +61,7 @@ import java.util.concurrent.TimeUnit; |
57 | 61 | import java.util.concurrent.atomic.AtomicInteger; |
58 | 62 | |
59 | 63 | import static org.thingsboard.server.gen.transport.TransportProtos.KeyValueType.BOOLEAN_V; |
64 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; | |
60 | 65 | |
61 | 66 | @Slf4j |
62 | 67 | @Component |
... | ... | @@ -67,37 +72,15 @@ public class LwM2mTransportServerHelper { |
67 | 72 | private final LwM2mTransportContext context; |
68 | 73 | private final AtomicInteger atomicTs = new AtomicInteger(0); |
69 | 74 | |
70 | - | |
71 | 75 | public long getTS() { |
72 | 76 | return TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + (atomicTs.getAndIncrement() % 1000); |
73 | 77 | } |
74 | 78 | |
75 | - /** | |
76 | - * send to Thingsboard Attribute || Telemetry | |
77 | - * | |
78 | - * @param msg - JsonObject: [{name: value}] | |
79 | - * @return - dummyWriteReplace {\"targetIdVer\":\"/19_1.0/0/0\",\"value\":0082} | |
80 | - */ | |
81 | - private <T> TransportServiceCallback<Void> getPubAckCallbackSendAttrTelemetry(final T msg) { | |
82 | - return new TransportServiceCallback<>() { | |
83 | - @Override | |
84 | - public void onSuccess(Void dummy) { | |
85 | - log.trace("Success to publish msg: {}, dummy: {}", msg, dummy); | |
86 | - } | |
87 | - | |
88 | - @Override | |
89 | - public void onError(Throwable e) { | |
90 | - log.trace("[{}] Failed to publish msg: {}", msg, e); | |
91 | - } | |
92 | - }; | |
93 | - } | |
94 | - | |
95 | 79 | public void sendParametersOnThingsboardAttribute(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) { |
96 | 80 | PostAttributeMsg.Builder request = PostAttributeMsg.newBuilder(); |
97 | 81 | request.addAllKv(result); |
98 | 82 | PostAttributeMsg postAttributeMsg = request.build(); |
99 | - TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postAttributeMsg); | |
100 | - context.getTransportService().process(sessionInfo, postAttributeMsg, this.getPubAckCallbackSendAttrTelemetry(call)); | |
83 | + context.getTransportService().process(sessionInfo, postAttributeMsg, TransportServiceCallback.EMPTY); | |
101 | 84 | } |
102 | 85 | |
103 | 86 | public void sendParametersOnThingsboardTelemetry(List<TransportProtos.KeyValueProto> result, SessionInfoProto sessionInfo) { |
... | ... | @@ -107,8 +90,7 @@ public class LwM2mTransportServerHelper { |
107 | 90 | builder.addAllKv(result); |
108 | 91 | request.addTsKvList(builder.build()); |
109 | 92 | PostTelemetryMsg postTelemetryMsg = request.build(); |
110 | - TransportServiceCallback call = this.getPubAckCallbackSendAttrTelemetry(postTelemetryMsg); | |
111 | - context.getTransportService().process(sessionInfo, postTelemetryMsg, this.getPubAckCallbackSendAttrTelemetry(call)); | |
93 | + context.getTransportService().process(sessionInfo, postTelemetryMsg, TransportServiceCallback.EMPTY); | |
112 | 94 | } |
113 | 95 | |
114 | 96 | /** |
... | ... | @@ -210,28 +192,6 @@ public class LwM2mTransportServerHelper { |
210 | 192 | throw new CodecException("Invalid ResourceModel_Type for resource %s, got %s", resourcePath, currentType); |
211 | 193 | } |
212 | 194 | |
213 | - public static ContentFormat convertResourceModelTypeToContentFormat(ResourceModel.Type type) { | |
214 | - switch (type) { | |
215 | - case BOOLEAN: | |
216 | - case STRING: | |
217 | - case TIME: | |
218 | - case INTEGER: | |
219 | - case FLOAT: | |
220 | - return ContentFormat.TLV; | |
221 | - case OPAQUE: | |
222 | - return ContentFormat.OPAQUE; | |
223 | - case OBJLNK: | |
224 | - return ContentFormat.LINK; | |
225 | - default: | |
226 | - } | |
227 | - throw new CodecException("Invalid ResourceModel_Type for %s ContentFormat.", type); | |
228 | - } | |
229 | - | |
230 | - public static ContentFormat getContentFormatByResourceModelType(ResourceModel resourceModel, ContentFormat contentFormat) { | |
231 | - return contentFormat.equals(ContentFormat.TLV) ? convertResourceModelTypeToContentFormat(resourceModel.type) : | |
232 | - contentFormat; | |
233 | - } | |
234 | - | |
235 | 195 | public static Object getValueFromKvProto(TransportProtos.KeyValueProto kv) { |
236 | 196 | switch (kv.getType()) { |
237 | 197 | case BOOLEAN_V: |
... | ... | @@ -247,4 +207,5 @@ public class LwM2mTransportServerHelper { |
247 | 207 | } |
248 | 208 | return null; |
249 | 209 | } |
210 | + | |
250 | 211 | } | ... | ... |
... | ... | @@ -16,13 +16,9 @@ |
16 | 16 | package org.thingsboard.server.transport.lwm2m.server; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; |
19 | -import com.google.common.collect.Sets; | |
20 | -import com.google.gson.Gson; | |
21 | -import com.google.gson.JsonArray; | |
22 | 19 | import com.google.gson.JsonObject; |
23 | 20 | import com.google.gson.JsonParser; |
24 | 21 | import com.google.gson.JsonSyntaxException; |
25 | -import com.google.gson.reflect.TypeToken; | |
26 | 22 | import lombok.extern.slf4j.Slf4j; |
27 | 23 | import org.apache.commons.lang3.StringUtils; |
28 | 24 | import org.eclipse.leshan.core.attributes.Attribute; |
... | ... | @@ -34,6 +30,7 @@ import org.eclipse.leshan.core.node.LwM2mNode; |
34 | 30 | import org.eclipse.leshan.core.node.LwM2mObject; |
35 | 31 | import org.eclipse.leshan.core.node.LwM2mObjectInstance; |
36 | 32 | import org.eclipse.leshan.core.node.LwM2mPath; |
33 | +import org.eclipse.leshan.core.node.LwM2mResource; | |
37 | 34 | import org.eclipse.leshan.core.node.LwM2mSingleResource; |
38 | 35 | import org.eclipse.leshan.core.node.codec.CodecException; |
39 | 36 | import org.eclipse.leshan.core.request.SimpleDownlinkRequest; |
... | ... | @@ -42,17 +39,19 @@ import org.eclipse.leshan.core.util.Hex; |
42 | 39 | import org.eclipse.leshan.server.registration.Registration; |
43 | 40 | import org.nustaq.serialization.FSTConfiguration; |
44 | 41 | import org.thingsboard.server.common.data.DeviceProfile; |
42 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
43 | +import org.thingsboard.server.common.data.device.data.lwm2m.BootstrapConfiguration; | |
44 | +import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportConfiguration; | |
45 | 45 | import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; |
46 | -import org.thingsboard.server.common.data.id.TenantId; | |
47 | 46 | import org.thingsboard.server.common.data.ota.OtaPackageKey; |
48 | 47 | import org.thingsboard.server.common.data.ota.OtaPackageType; |
49 | 48 | import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; |
50 | 49 | import org.thingsboard.server.common.data.ota.OtaPackageUtil; |
51 | 50 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
52 | 51 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
53 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; | |
52 | +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; | |
53 | +import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2MUplinkMsgHandler; | |
54 | 54 | |
55 | -import java.io.IOException; | |
56 | 55 | import java.util.ArrayList; |
57 | 56 | import java.util.Arrays; |
58 | 57 | import java.util.Date; |
... | ... | @@ -60,7 +59,6 @@ import java.util.LinkedList; |
60 | 59 | import java.util.List; |
61 | 60 | import java.util.Map; |
62 | 61 | import java.util.Optional; |
63 | -import java.util.Set; | |
64 | 62 | import java.util.concurrent.ConcurrentHashMap; |
65 | 63 | |
66 | 64 | import static org.eclipse.leshan.core.attributes.Attribute.DIMENSION; |
... | ... | @@ -115,33 +113,17 @@ public class LwM2mTransportUtil { |
115 | 113 | |
116 | 114 | public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms |
117 | 115 | |
118 | - public static final String LOG_LW2M_TELEMETRY = "logLwm2m"; | |
119 | - public static final String LOG_LW2M_INFO = "info"; | |
120 | - public static final String LOG_LW2M_ERROR = "error"; | |
121 | - public static final String LOG_LW2M_WARN = "warn"; | |
122 | - public static final String LOG_LW2M_VALUE = "value"; | |
116 | + public static final String LOG_LWM2M_TELEMETRY = "logLwm2m"; | |
117 | + public static final String LOG_LWM2M_INFO = "info"; | |
118 | + public static final String LOG_LWM2M_ERROR = "error"; | |
119 | + public static final String LOG_LWM2M_WARN = "warn"; | |
120 | + public static final String LOG_LWM2M_VALUE = "value"; | |
123 | 121 | |
124 | 122 | public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; |
125 | 123 | public static final String LWM2M_VERSION_DEFAULT = "1.0"; |
126 | 124 | |
127 | - // RPC | |
128 | - public static final String TYPE_OPER_KEY = "typeOper"; | |
129 | - public static final String TARGET_ID_VER_KEY = "targetIdVer"; | |
130 | - public static final String KEY_NAME_KEY = "key"; | |
131 | - public static final String VALUE_KEY = "value"; | |
132 | - public static final String PARAMS_KEY = "params"; | |
133 | - public static final String SEPARATOR_KEY = ":"; | |
134 | - public static final String FINISH_VALUE_KEY = ","; | |
135 | - public static final String START_JSON_KEY = "{"; | |
136 | - public static final String FINISH_JSON_KEY = "}"; | |
137 | - public static final String INFO_KEY = "info"; | |
138 | - public static final String RESULT_KEY = "result"; | |
139 | - public static final String ERROR_KEY = "error"; | |
140 | - public static final String METHOD_KEY = "methodName"; | |
141 | - | |
142 | - | |
143 | 125 | // Firmware |
144 | - public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "firmwareUpdateCoapRecourse"; | |
126 | + public static final String FIRMWARE_UPDATE_COAP_RECOURSE = "tbfw"; | |
145 | 127 | public static final String FW_UPDATE = "Firmware update"; |
146 | 128 | public static final Integer FW_5_ID = 5; |
147 | 129 | public static final Integer FW_19_ID = 19; |
... | ... | @@ -155,10 +137,14 @@ public class LwM2mTransportUtil { |
155 | 137 | public static final String FW_STATE_ID = "/5/0/3"; |
156 | 138 | // Update Result R |
157 | 139 | public static final String FW_RESULT_ID = "/5/0/5"; |
140 | + | |
141 | + public static final String FW_DELIVERY_METHOD = "/5/0/9"; | |
142 | + | |
158 | 143 | // PkgName R |
159 | 144 | public static final String FW_NAME_ID = "/5/0/6"; |
160 | 145 | // PkgVersion R |
161 | 146 | public static final String FW_5_VER_ID = "/5/0/7"; |
147 | + | |
162 | 148 | /** |
163 | 149 | * Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8 |
164 | 150 | * BC68JAR01A10 |
... | ... | @@ -215,192 +201,12 @@ public class LwM2mTransportUtil { |
215 | 201 | } |
216 | 202 | } |
217 | 203 | |
218 | - /** | |
219 | - * Define the behavior of a write request. | |
220 | - */ | |
221 | - public enum LwM2mTypeOper { | |
222 | - /** | |
223 | - * GET | |
224 | - */ | |
225 | - READ(0, "Read"), | |
226 | - DISCOVER(1, "Discover"), | |
227 | - DISCOVER_ALL(2, "DiscoverAll"), | |
228 | - OBSERVE_READ_ALL(3, "ObserveReadAll"), | |
229 | - /** | |
230 | - * POST | |
231 | - */ | |
232 | - OBSERVE(4, "Observe"), | |
233 | - OBSERVE_CANCEL(5, "ObserveCancel"), | |
234 | - OBSERVE_CANCEL_ALL(6, "ObserveCancelAll"), | |
235 | - EXECUTE(7, "Execute"), | |
236 | - /** | |
237 | - * Replaces the Object Instance or the Resource(s) with the new value provided in the “Write” operation. (see | |
238 | - * section 5.3.3 of the LW M2M spec). | |
239 | - * if all resources are to be replaced | |
240 | - */ | |
241 | - WRITE_REPLACE(8, "WriteReplace"), | |
242 | - /* | |
243 | - PUT | |
244 | - */ | |
245 | - /** | |
246 | - * Adds or updates Resources provided in the new value and leaves other existing Resources unchanged. (see section | |
247 | - * 5.3.3 of the LW M2M spec). | |
248 | - * if this is a partial update request | |
249 | - */ | |
250 | - WRITE_UPDATE(9, "WriteUpdate"), | |
251 | - WRITE_ATTRIBUTES(10, "WriteAttributes"), | |
252 | - DELETE(11, "Delete"), | |
253 | - | |
254 | - // only for RPC | |
255 | - FW_UPDATE(12, "FirmwareUpdate"); | |
256 | -// FW_READ_INFO(12, "FirmwareReadInfo"), | |
257 | - | |
258 | -// SW_READ_INFO(15, "SoftwareReadInfo"), | |
259 | -// SW_UPDATE(16, "SoftwareUpdate"), | |
260 | -// SW_UNINSTALL(18, "SoftwareUninstall"); | |
261 | - | |
262 | - public int code; | |
263 | - public String type; | |
264 | - | |
265 | - LwM2mTypeOper(int code, String type) { | |
266 | - this.code = code; | |
267 | - this.type = type; | |
268 | - } | |
269 | - | |
270 | - public static LwM2mTypeOper fromLwLwM2mTypeOper(String type) { | |
271 | - for (LwM2mTypeOper to : LwM2mTypeOper.values()) { | |
272 | - if (to.type.equals(type)) { | |
273 | - return to; | |
274 | - } | |
275 | - } | |
276 | - throw new IllegalArgumentException(String.format("Unsupported typeOper type : %s", type)); | |
277 | - } | |
278 | - } | |
279 | - | |
280 | - /** | |
281 | - * /** State R | |
282 | - * 0: Idle (before downloading or after successful updating) | |
283 | - * 1: Downloading (The data sequence is on the way) | |
284 | - * 2: Downloaded | |
285 | - * 3: Updating | |
286 | - */ | |
287 | - public enum StateFw { | |
288 | - IDLE(0, "Idle"), | |
289 | - DOWNLOADING(1, "Downloading"), | |
290 | - DOWNLOADED(2, "Downloaded"), | |
291 | - UPDATING(3, "Updating"); | |
292 | - | |
293 | - public int code; | |
294 | - public String type; | |
295 | - | |
296 | - StateFw(int code, String type) { | |
297 | - this.code = code; | |
298 | - this.type = type; | |
299 | - } | |
300 | - | |
301 | - public static StateFw fromStateFwByType(String type) { | |
302 | - for (StateFw to : StateFw.values()) { | |
303 | - if (to.type.equals(type)) { | |
304 | - return to; | |
305 | - } | |
306 | - } | |
307 | - throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); | |
308 | - } | |
309 | - | |
310 | - public static StateFw fromStateFwByCode(int code) { | |
311 | - for (StateFw to : StateFw.values()) { | |
312 | - if (to.code == code) { | |
313 | - return to; | |
314 | - } | |
315 | - } | |
316 | - throw new IllegalArgumentException(String.format("Unsupported FW State code : %s", code)); | |
317 | - } | |
318 | - } | |
319 | - | |
320 | - /** | |
321 | - * FW Update Result | |
322 | - * 0: Initial value. Once the updating process is initiated (Download /Update), this Resource MUST be reset to Initial value. | |
323 | - * 1: Firmware updated successfully. | |
324 | - * 2: Not enough flash memory for the new firmware package. | |
325 | - * 3: Out of RAM during downloading process. | |
326 | - * 4: Connection lost during downloading process. | |
327 | - * 5: Integrity check failure for new downloaded package. | |
328 | - * 6: Unsupported package type. | |
329 | - * 7: Invalid URI. | |
330 | - * 8: Firmware update failed. | |
331 | - * 9: Unsupported protocol. | |
332 | - */ | |
333 | - public enum UpdateResultFw { | |
334 | - INITIAL(0, "Initial value", false), | |
335 | - UPDATE_SUCCESSFULLY(1, "Firmware updated successfully", false), | |
336 | - NOT_ENOUGH(2, "Not enough flash memory for the new firmware package", false), | |
337 | - OUT_OFF_MEMORY(3, "Out of RAM during downloading process", false), | |
338 | - CONNECTION_LOST(4, "Connection lost during downloading process", true), | |
339 | - INTEGRITY_CHECK_FAILURE(5, "Integrity check failure for new downloaded package", true), | |
340 | - UNSUPPORTED_TYPE(6, "Unsupported package type", false), | |
341 | - INVALID_URI(7, "Invalid URI", false), | |
342 | - UPDATE_FAILED(8, "Firmware update failed", false), | |
343 | - UNSUPPORTED_PROTOCOL(9, "Unsupported protocol", false); | |
344 | - | |
345 | - public int code; | |
346 | - public String type; | |
347 | - public boolean isAgain; | |
348 | - | |
349 | - UpdateResultFw(int code, String type, boolean isAgain) { | |
350 | - this.code = code; | |
351 | - this.type = type; | |
352 | - this.isAgain = isAgain; | |
353 | - } | |
354 | - | |
355 | - public static UpdateResultFw fromUpdateResultFwByType(String type) { | |
356 | - for (UpdateResultFw to : UpdateResultFw.values()) { | |
357 | - if (to.type.equals(type)) { | |
358 | - return to; | |
359 | - } | |
360 | - } | |
361 | - throw new IllegalArgumentException(String.format("Unsupported FW Update Result type : %s", type)); | |
362 | - } | |
363 | - | |
364 | - public static UpdateResultFw fromUpdateResultFwByCode(int code) { | |
365 | - for (UpdateResultFw to : UpdateResultFw.values()) { | |
366 | - if (to.code == code) { | |
367 | - return to; | |
368 | - } | |
369 | - } | |
370 | - throw new IllegalArgumentException(String.format("Unsupported FW Update Result code : %s", code)); | |
371 | - } | |
372 | - } | |
373 | - | |
374 | - /** | |
375 | - * FirmwareUpdateStatus { | |
376 | - * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED | |
377 | - */ | |
378 | - public static OtaPackageUpdateStatus equalsFwSateFwResultToFirmwareUpdateStatus(StateFw stateFw, UpdateResultFw updateResultFw) { | |
379 | - switch (updateResultFw) { | |
380 | - case INITIAL: | |
381 | - return equalsFwSateToFirmwareUpdateStatus(stateFw); | |
382 | - case UPDATE_SUCCESSFULLY: | |
383 | - return UPDATED; | |
384 | - case NOT_ENOUGH: | |
385 | - case OUT_OFF_MEMORY: | |
386 | - case CONNECTION_LOST: | |
387 | - case INTEGRITY_CHECK_FAILURE: | |
388 | - case UNSUPPORTED_TYPE: | |
389 | - case INVALID_URI: | |
390 | - case UPDATE_FAILED: | |
391 | - case UNSUPPORTED_PROTOCOL: | |
392 | - return FAILED; | |
393 | - default: | |
394 | - throw new CodecException("Invalid value stateFw %s %s for FirmwareUpdateStatus.", stateFw.name(), updateResultFw.name()); | |
395 | - } | |
396 | - } | |
397 | - | |
398 | - public static OtaPackageUpdateStatus equalsFwResultToFirmwareUpdateStatus(UpdateResultFw updateResultFw) { | |
204 | + public static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(UpdateResultFw updateResultFw) { | |
399 | 205 | switch (updateResultFw) { |
400 | 206 | case INITIAL: |
401 | - return VERIFIED; | |
402 | - case UPDATE_SUCCESSFULLY: | |
403 | - return UPDATED; | |
207 | + return Optional.empty(); | |
208 | + case UPDATE_SUCCESSFULLY: | |
209 | + return Optional.of(UPDATED); | |
404 | 210 | case NOT_ENOUGH: |
405 | 211 | case OUT_OFF_MEMORY: |
406 | 212 | case CONNECTION_LOST: |
... | ... | @@ -409,24 +215,24 @@ public class LwM2mTransportUtil { |
409 | 215 | case INVALID_URI: |
410 | 216 | case UPDATE_FAILED: |
411 | 217 | case UNSUPPORTED_PROTOCOL: |
412 | - return FAILED; | |
218 | + return Optional.of(FAILED); | |
413 | 219 | default: |
414 | 220 | throw new CodecException("Invalid value stateFw %s for FirmwareUpdateStatus.", updateResultFw.name()); |
415 | 221 | } |
416 | 222 | } |
417 | 223 | |
418 | - public static OtaPackageUpdateStatus equalsFwSateToFirmwareUpdateStatus(StateFw stateFw) { | |
419 | - switch (stateFw) { | |
224 | + public static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(UpdateStateFw updateStateFw) { | |
225 | + switch (updateStateFw) { | |
420 | 226 | case IDLE: |
421 | - return VERIFIED; | |
227 | + return Optional.empty(); | |
422 | 228 | case DOWNLOADING: |
423 | - return DOWNLOADING; | |
229 | + return Optional.of(DOWNLOADING); | |
424 | 230 | case DOWNLOADED: |
425 | - return DOWNLOADED; | |
231 | + return Optional.of(DOWNLOADED); | |
426 | 232 | case UPDATING: |
427 | - return UPDATING; | |
233 | + return Optional.of(UPDATING); | |
428 | 234 | default: |
429 | - throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", stateFw); | |
235 | + throw new CodecException("Invalid value stateFw %d for FirmwareUpdateStatus.", updateStateFw); | |
430 | 236 | } |
431 | 237 | } |
432 | 238 | |
... | ... | @@ -574,70 +380,6 @@ public class LwM2mTransportUtil { |
574 | 380 | } |
575 | 381 | } |
576 | 382 | |
577 | - public enum LwM2MFirmwareUpdateStrategy { | |
578 | - OBJ_5_BINARY(1, "ObjectId 5, Binary"), | |
579 | - OBJ_5_TEMP_URL(2, "ObjectId 5, URI"), | |
580 | - OBJ_19_BINARY(3, "ObjectId 19, Binary"); | |
581 | - | |
582 | - public int code; | |
583 | - public String type; | |
584 | - | |
585 | - LwM2MFirmwareUpdateStrategy(int code, String type) { | |
586 | - this.code = code; | |
587 | - this.type = type; | |
588 | - } | |
589 | - | |
590 | - public static LwM2MFirmwareUpdateStrategy fromStrategyFwByType(String type) { | |
591 | - for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { | |
592 | - if (to.type.equals(type)) { | |
593 | - return to; | |
594 | - } | |
595 | - } | |
596 | - throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); | |
597 | - } | |
598 | - | |
599 | - public static LwM2MFirmwareUpdateStrategy fromStrategyFwByCode(int code) { | |
600 | - for (LwM2MFirmwareUpdateStrategy to : LwM2MFirmwareUpdateStrategy.values()) { | |
601 | - if (to.code == code) { | |
602 | - return to; | |
603 | - } | |
604 | - } | |
605 | - throw new IllegalArgumentException(String.format("Unsupported FW Strategy code : %s", code)); | |
606 | - } | |
607 | - } | |
608 | - | |
609 | - public enum LwM2MSoftwareUpdateStrategy { | |
610 | - BINARY(1, "ObjectId 9, Binary"), | |
611 | - TEMP_URL(2, "ObjectId 9, URI"); | |
612 | - | |
613 | - public int code; | |
614 | - public String type; | |
615 | - | |
616 | - LwM2MSoftwareUpdateStrategy(int code, String type) { | |
617 | - this.code = code; | |
618 | - this.type = type; | |
619 | - } | |
620 | - | |
621 | - public static LwM2MSoftwareUpdateStrategy fromStrategySwByType(String type) { | |
622 | - for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { | |
623 | - if (to.type.equals(type)) { | |
624 | - return to; | |
625 | - } | |
626 | - } | |
627 | - throw new IllegalArgumentException(String.format("Unsupported SW Strategy type : %s", type)); | |
628 | - } | |
629 | - | |
630 | - public static LwM2MSoftwareUpdateStrategy fromStrategySwByCode(int code) { | |
631 | - for (LwM2MSoftwareUpdateStrategy to : LwM2MSoftwareUpdateStrategy.values()) { | |
632 | - if (to.code == code) { | |
633 | - return to; | |
634 | - } | |
635 | - } | |
636 | - throw new IllegalArgumentException(String.format("Unsupported SW Strategy code : %s", code)); | |
637 | - } | |
638 | - | |
639 | - } | |
640 | - | |
641 | 383 | /** |
642 | 384 | * FirmwareUpdateStatus { |
643 | 385 | * DOWNLOADING, DOWNLOADED, VERIFIED, UPDATING, UPDATED, FAILED |
... | ... | @@ -695,26 +437,23 @@ public class LwM2mTransportUtil { |
695 | 437 | } |
696 | 438 | } |
697 | 439 | |
698 | - public static LwM2mOtaConvert convertOtaUpdateValueToString (String pathIdVer, Object value, ResourceModel.Type currentType) { | |
699 | - String path = convertPathFromIdVerToObjectId(pathIdVer); | |
440 | + public static LwM2mOtaConvert convertOtaUpdateValueToString(String pathIdVer, Object value, ResourceModel.Type currentType) { | |
441 | + String path = fromVersionedIdToObjectId(pathIdVer); | |
700 | 442 | LwM2mOtaConvert lwM2mOtaConvert = new LwM2mOtaConvert(); |
701 | 443 | if (path != null) { |
702 | 444 | if (FW_STATE_ID.equals(path)) { |
703 | 445 | lwM2mOtaConvert.setCurrentType(STRING); |
704 | - lwM2mOtaConvert.setValue(StateFw.fromStateFwByCode(((Long) value).intValue()).type); | |
446 | + lwM2mOtaConvert.setValue(UpdateStateFw.fromStateFwByCode(((Long) value).intValue()).type); | |
705 | 447 | return lwM2mOtaConvert; |
706 | - } | |
707 | - else if (FW_RESULT_ID.equals(path)) { | |
448 | + } else if (FW_RESULT_ID.equals(path)) { | |
708 | 449 | lwM2mOtaConvert.setCurrentType(STRING); |
709 | - lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).type); | |
450 | + lwM2mOtaConvert.setValue(UpdateResultFw.fromUpdateResultFwByCode(((Long) value).intValue()).getType()); | |
710 | 451 | return lwM2mOtaConvert; |
711 | - } | |
712 | - else if (SW_UPDATE_STATE_ID.equals(path)) { | |
452 | + } else if (SW_UPDATE_STATE_ID.equals(path)) { | |
713 | 453 | lwM2mOtaConvert.setCurrentType(STRING); |
714 | 454 | lwM2mOtaConvert.setValue(UpdateStateSw.fromUpdateStateSwByCode(((Long) value).intValue()).type); |
715 | 455 | return lwM2mOtaConvert; |
716 | - } | |
717 | - else if (SW_RESULT_ID.equals(path)) { | |
456 | + } else if (SW_RESULT_ID.equals(path)) { | |
718 | 457 | lwM2mOtaConvert.setCurrentType(STRING); |
719 | 458 | lwM2mOtaConvert.setValue(UpdateResultSw.fromUpdateResultSwByCode(((Long) value).intValue()).type); |
720 | 459 | return lwM2mOtaConvert; |
... | ... | @@ -738,112 +477,32 @@ public class LwM2mTransportUtil { |
738 | 477 | return null; |
739 | 478 | } |
740 | 479 | |
741 | - | |
742 | - public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) { | |
743 | - LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile(); | |
744 | - lwM2MClientProfile.setTenantId(tenantId); | |
745 | - lwM2MClientProfile.setPostClientLwM2mSettings(profilesConfigData.get(CLIENT_LWM2M_SETTINGS).getAsJsonObject()); | |
746 | - lwM2MClientProfile.setPostKeyNameProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).getAsJsonObject()); | |
747 | - lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray()); | |
748 | - lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray()); | |
749 | - lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).getAsJsonArray()); | |
750 | - lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject()); | |
751 | - return lwM2MClientProfile; | |
752 | - } | |
753 | - | |
754 | - /** | |
755 | - * @return deviceProfileBody with Observe&Attribute&Telemetry From Thingsboard | |
756 | - * Example: | |
757 | - * property: {"clientLwM2mSettings": { | |
758 | - * clientUpdateValueAfterConnect: false; | |
759 | - * } | |
760 | - * property: "observeAttr" | |
761 | - * {"keyName": { | |
762 | - * "/3/0/1": "modelNumber", | |
763 | - * "/3/0/0": "manufacturer", | |
764 | - * "/3/0/2": "serialNumber" | |
765 | - * }, | |
766 | - * "attribute":["/2/0/1","/3/0/9"], | |
767 | - * "telemetry":["/1/0/1","/2/0/1","/6/0/1"], | |
768 | - * "observe":["/2/0","/2/0/0","/4/0/2"]} | |
769 | - * "attributeLwm2m": {"/3_1.0": {"ver": "currentTimeTest11"}, | |
770 | - * "/3_1.0/0": {"gt": 17}, | |
771 | - * "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}} | |
772 | - */ | |
773 | - public static LwM2mClientProfile toLwM2MClientProfile(DeviceProfile deviceProfile) { | |
774 | - if (((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { | |
775 | - Object profile = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); | |
776 | - try { | |
777 | - ObjectMapper mapper = new ObjectMapper(); | |
778 | - String profileStr = mapper.writeValueAsString(profile); | |
779 | - JsonObject profileJson = (profileStr != null) ? validateJson(profileStr) : null; | |
780 | - return getValidateCredentialsBodyFromThingsboard(profileJson) ? LwM2mTransportUtil.getNewProfileParameters(profileJson, deviceProfile.getTenantId()) : null; | |
781 | - } catch (IOException e) { | |
782 | - log.error("", e); | |
783 | - } | |
784 | - } | |
785 | - return null; | |
786 | - } | |
787 | - | |
788 | - public static JsonObject getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { | |
789 | - if (deviceProfile != null && ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties().size() > 0) { | |
790 | - Object bootstrap = ((Lwm2mDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()).getProperties(); | |
791 | - try { | |
792 | - ObjectMapper mapper = new ObjectMapper(); | |
793 | - String bootstrapStr = mapper.writeValueAsString(bootstrap); | |
794 | - JsonObject objectMsg = (bootstrapStr != null) ? validateJson(bootstrapStr) : null; | |
795 | - return (getValidateBootstrapProfileFromThingsboard(objectMsg)) ? objectMsg.get(BOOTSTRAP).getAsJsonObject() : null; | |
796 | - } catch (IOException e) { | |
797 | - log.error("", e); | |
798 | - } | |
480 | +// public static LwM2mClientProfile getNewProfileParameters(JsonObject profilesConfigData, TenantId tenantId) { | |
481 | +// LwM2mClientProfile lwM2MClientProfile = new LwM2mClientProfile(); | |
482 | +// lwM2MClientProfile.setTenantId(tenantId); | |
483 | +// lwM2MClientProfile.setPostClientLwM2mSettings(profilesConfigData.get(CLIENT_LWM2M_SETTINGS).getAsJsonObject()); | |
484 | +// lwM2MClientProfile.setPostKeyNameProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).getAsJsonObject()); | |
485 | +// lwM2MClientProfile.setPostAttributeProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).getAsJsonArray()); | |
486 | +// lwM2MClientProfile.setPostTelemetryProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).getAsJsonArray()); | |
487 | +// lwM2MClientProfile.setPostObserveProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).getAsJsonArray()); | |
488 | +// lwM2MClientProfile.setPostAttributeLwm2mProfile(profilesConfigData.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).getAsJsonObject()); | |
489 | +// return lwM2MClientProfile; | |
490 | +// } | |
491 | + | |
492 | + public static Lwm2mDeviceProfileTransportConfiguration toLwM2MClientProfile(DeviceProfile deviceProfile) { | |
493 | + DeviceProfileTransportConfiguration transportConfiguration = deviceProfile.getProfileData().getTransportConfiguration(); | |
494 | + if (transportConfiguration.getType().equals(DeviceTransportType.LWM2M)) { | |
495 | + return (Lwm2mDeviceProfileTransportConfiguration) transportConfiguration; | |
496 | + } else { | |
497 | + log.warn("[{}] Received profile with invalid transport configuration: {}", deviceProfile.getId(), deviceProfile.getProfileData().getTransportConfiguration()); | |
498 | + throw new IllegalArgumentException("Received profile with invalid transport configuration: " + transportConfiguration.getType()); | |
799 | 499 | } |
800 | - return null; | |
801 | - } | |
802 | - | |
803 | - private static boolean getValidateCredentialsBodyFromThingsboard(JsonObject objectMsg) { | |
804 | - return (objectMsg != null && | |
805 | - !objectMsg.isJsonNull() && | |
806 | - objectMsg.has(CLIENT_LWM2M_SETTINGS) && | |
807 | - !objectMsg.get(CLIENT_LWM2M_SETTINGS).isJsonNull() && | |
808 | - objectMsg.get(CLIENT_LWM2M_SETTINGS).isJsonObject() && | |
809 | - objectMsg.has(OBSERVE_ATTRIBUTE_TELEMETRY) && | |
810 | - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).isJsonNull() && | |
811 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).isJsonObject() && | |
812 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(KEY_NAME) && | |
813 | - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).isJsonNull() && | |
814 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(KEY_NAME).isJsonObject() && | |
815 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(ATTRIBUTE) && | |
816 | - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).isJsonNull() && | |
817 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE).isJsonArray() && | |
818 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(TELEMETRY) && | |
819 | - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).isJsonNull() && | |
820 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(TELEMETRY).isJsonArray() && | |
821 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(OBSERVE_LWM2M) && | |
822 | - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).isJsonNull() && | |
823 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(OBSERVE_LWM2M).isJsonArray() && | |
824 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().has(ATTRIBUTE_LWM2M) && | |
825 | - !objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).isJsonNull() && | |
826 | - objectMsg.get(OBSERVE_ATTRIBUTE_TELEMETRY).getAsJsonObject().get(ATTRIBUTE_LWM2M).isJsonObject()); | |
827 | 500 | } |
828 | 501 | |
829 | - private static boolean getValidateBootstrapProfileFromThingsboard(JsonObject objectMsg) { | |
830 | - return (objectMsg != null && | |
831 | - !objectMsg.isJsonNull() && | |
832 | - objectMsg.has(BOOTSTRAP) && | |
833 | - objectMsg.get(BOOTSTRAP).isJsonObject() && | |
834 | - !objectMsg.get(BOOTSTRAP).isJsonNull() && | |
835 | - objectMsg.get(BOOTSTRAP).getAsJsonObject().has(SERVERS) && | |
836 | - !objectMsg.get(BOOTSTRAP).getAsJsonObject().get(SERVERS).isJsonNull() && | |
837 | - objectMsg.get(BOOTSTRAP).getAsJsonObject().get(SERVERS).isJsonObject() && | |
838 | - objectMsg.get(BOOTSTRAP).getAsJsonObject().has(BOOTSTRAP_SERVER) && | |
839 | - !objectMsg.get(BOOTSTRAP).getAsJsonObject().get(BOOTSTRAP_SERVER).isJsonNull() && | |
840 | - objectMsg.get(BOOTSTRAP).getAsJsonObject().get(BOOTSTRAP_SERVER).isJsonObject() && | |
841 | - objectMsg.get(BOOTSTRAP).getAsJsonObject().has(LWM2M_SERVER) && | |
842 | - !objectMsg.get(BOOTSTRAP).getAsJsonObject().get(LWM2M_SERVER).isJsonNull() && | |
843 | - objectMsg.get(BOOTSTRAP).getAsJsonObject().get(LWM2M_SERVER).isJsonObject()); | |
502 | + public static BootstrapConfiguration getBootstrapParametersFromThingsboard(DeviceProfile deviceProfile) { | |
503 | + return toLwM2MClientProfile(deviceProfile).getBootstrap(); | |
844 | 504 | } |
845 | 505 | |
846 | - | |
847 | 506 | public static JsonObject validateJson(String jsonStr) { |
848 | 507 | JsonObject object = null; |
849 | 508 | if (jsonStr != null && !jsonStr.isEmpty()) { |
... | ... | @@ -900,8 +559,11 @@ public class LwM2mTransportUtil { |
900 | 559 | }; |
901 | 560 | } |
902 | 561 | |
903 | - public static String convertPathFromIdVerToObjectId(String pathIdVer) { | |
562 | + public static String fromVersionedIdToObjectId(String pathIdVer) { | |
904 | 563 | try { |
564 | + if (pathIdVer == null) { | |
565 | + return null; | |
566 | + } | |
905 | 567 | String[] keyArray = pathIdVer.split(LWM2M_SEPARATOR_PATH); |
906 | 568 | if (keyArray.length > 1 && keyArray[1].split(LWM2M_SEPARATOR_KEY).length == 2) { |
907 | 569 | keyArray[1] = keyArray[1].split(LWM2M_SEPARATOR_KEY)[0]; |
... | ... | @@ -910,7 +572,8 @@ public class LwM2mTransportUtil { |
910 | 572 | return pathIdVer; |
911 | 573 | } |
912 | 574 | } catch (Exception e) { |
913 | - return null; | |
575 | + log.warn("Issue converting path with version [{}] to path without version: ", pathIdVer, e); | |
576 | + throw new RuntimeException(e); | |
914 | 577 | } |
915 | 578 | } |
916 | 579 | |
... | ... | @@ -941,12 +604,12 @@ public class LwM2mTransportUtil { |
941 | 604 | return pathIdVer; |
942 | 605 | } else { |
943 | 606 | LwM2mPath pathObjId = new LwM2mPath(pathIdVer); |
944 | - return convertPathFromObjectIdToIdVer(pathIdVer, registration); | |
607 | + return convertObjectIdToVersionedId(pathIdVer, registration); | |
945 | 608 | } |
946 | 609 | } |
947 | 610 | } |
948 | 611 | |
949 | - public static String convertPathFromObjectIdToIdVer(String path, Registration registration) { | |
612 | + public static String convertObjectIdToVersionedId(String path, Registration registration) { | |
950 | 613 | String ver = registration.getSupportedObject().get(new LwM2mPath(path).getObjectId()); |
951 | 614 | ver = ver != null ? ver : LWM2M_VERSION_DEFAULT; |
952 | 615 | try { |
... | ... | @@ -1001,12 +664,12 @@ public class LwM2mTransportUtil { |
1001 | 664 | * Attribute pmax = new Attribute(MAXIMUM_PERIOD, "60"); |
1002 | 665 | * Attribute [] attrs = {gt, st}; |
1003 | 666 | */ |
1004 | - public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MTransportMsgHandler serviceImpl) { | |
667 | + public static SimpleDownlinkRequest createWriteAttributeRequest(String target, Object params, DefaultLwM2MUplinkMsgHandler serviceImpl) { | |
1005 | 668 | AttributeSet attrSet = new AttributeSet(createWriteAttributes(params, serviceImpl, target)); |
1006 | 669 | return attrSet.getAttributes().size() > 0 ? new WriteAttributesRequest(target, attrSet) : null; |
1007 | 670 | } |
1008 | 671 | |
1009 | - private static Attribute[] createWriteAttributes(Object params, DefaultLwM2MTransportMsgHandler serviceImpl, String target) { | |
672 | + private static Attribute[] createWriteAttributes(Object params, DefaultLwM2MUplinkMsgHandler serviceImpl, String target) { | |
1010 | 673 | List<Attribute> attributeLists = new ArrayList<>(); |
1011 | 674 | ObjectMapper oMapper = new ObjectMapper(); |
1012 | 675 | Map<String, Object> map = oMapper.convertValue(params, ConcurrentHashMap.class); |
... | ... | @@ -1024,12 +687,6 @@ public class LwM2mTransportUtil { |
1024 | 687 | return attributeLists.toArray(Attribute[]::new); |
1025 | 688 | } |
1026 | 689 | |
1027 | - public static Set<String> convertJsonArrayToSet(JsonArray jsonArray) { | |
1028 | - List<String> attributeListOld = new Gson().fromJson(jsonArray, new TypeToken<List<String>>() { | |
1029 | - }.getType()); | |
1030 | - return Sets.newConcurrentHashSet(attributeListOld); | |
1031 | - } | |
1032 | - | |
1033 | 690 | public static ResourceModel.Type equalsResourceTypeGetSimpleName(Object value) { |
1034 | 691 | switch (value.getClass().getSimpleName()) { |
1035 | 692 | case "Double": |
... | ... | @@ -1051,15 +708,7 @@ public class LwM2mTransportUtil { |
1051 | 708 | } |
1052 | 709 | } |
1053 | 710 | |
1054 | - public static LwM2mTypeOper setValidTypeOper(String typeOper) { | |
1055 | - try { | |
1056 | - return LwM2mTransportUtil.LwM2mTypeOper.fromLwLwM2mTypeOper(typeOper); | |
1057 | - } catch (Exception e) { | |
1058 | - return null; | |
1059 | - } | |
1060 | - } | |
1061 | - | |
1062 | - public static Object convertWriteAttributes(String type, Object value, DefaultLwM2MTransportMsgHandler serviceImpl, String target) { | |
711 | + public static Object convertWriteAttributes(String type, Object value, DefaultLwM2MUplinkMsgHandler serviceImpl, String target) { | |
1063 | 712 | switch (type) { |
1064 | 713 | /** Integer [0:255]; */ |
1065 | 714 | case DIMENSION: |
... | ... | @@ -1106,4 +755,20 @@ public class LwM2mTransportUtil { |
1106 | 755 | || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.CHECKSUM_ALGORITHM).equals(pathName) |
1107 | 756 | || OtaPackageUtil.getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.SIZE).equals(pathName); |
1108 | 757 | } |
758 | + | |
759 | + /** | |
760 | + * @param lwM2MClient - | |
761 | + * @param path - | |
762 | + * @return - return value of Resource by idPath | |
763 | + */ | |
764 | + public static LwM2mResource getResourceValueFromLwM2MClient(LwM2mClient lwM2MClient, String path) { | |
765 | + LwM2mResource lwm2mResourceValue = null; | |
766 | + ResourceValue resourceValue = lwM2MClient.getResources().get(path); | |
767 | + if (resourceValue != null) { | |
768 | + if (new LwM2mPath(fromVersionedIdToObjectId(path)).isResource()) { | |
769 | + lwm2mResourceValue = lwM2MClient.getResources().get(path).getLwM2mResource(); | |
770 | + } | |
771 | + } | |
772 | + return lwm2mResourceValue; | |
773 | + } | |
1109 | 774 | } | ... | ... |
... | ... | @@ -72,7 +72,7 @@ public class LwM2mVersionedModelProvider implements LwM2mModelProvider { |
72 | 72 | |
73 | 73 | public DynamicModel(Registration registration) { |
74 | 74 | this.registration = registration; |
75 | - this.tenantId = lwM2mClientContext.getProfile(registration).getTenantId(); | |
75 | + this.tenantId = lwM2mClientContext.getClientByEndpoint(registration.getEndpoint()).getTenantId(); | |
76 | 76 | } |
77 | 77 | |
78 | 78 | @Override | ... | ... |
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; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | + | |
20 | +/** | |
21 | + * FW Update Result | |
22 | + * 0: Initial value. Once the updating process is initiated (Download /Update), this Resource MUST be reset to Initial value. | |
23 | + * 1: Firmware updated successfully. | |
24 | + * 2: Not enough flash memory for the new firmware package. | |
25 | + * 3: Out of RAM during downloading process. | |
26 | + * 4: Connection lost during downloading process. | |
27 | + * 5: Integrity check failure for new downloaded package. | |
28 | + * 6: Unsupported package type. | |
29 | + * 7: Invalid URI. | |
30 | + * 8: Firmware update failed. | |
31 | + * 9: Unsupported protocol. | |
32 | + */ | |
33 | +public enum UpdateResultFw { | |
34 | + INITIAL(0, "Initial value", false), | |
35 | + UPDATE_SUCCESSFULLY(1, "Firmware updated successfully", false), | |
36 | + NOT_ENOUGH(2, "Not enough flash memory for the new firmware package", false), | |
37 | + OUT_OFF_MEMORY(3, "Out of RAM during downloading process", false), | |
38 | + CONNECTION_LOST(4, "Connection lost during downloading process", true), | |
39 | + INTEGRITY_CHECK_FAILURE(5, "Integrity check failure for new downloaded package", true), | |
40 | + UNSUPPORTED_TYPE(6, "Unsupported package type", false), | |
41 | + INVALID_URI(7, "Invalid URI", false), | |
42 | + UPDATE_FAILED(8, "Firmware update failed", false), | |
43 | + UNSUPPORTED_PROTOCOL(9, "Unsupported protocol", false); | |
44 | + | |
45 | + @Getter | |
46 | + private int code; | |
47 | + @Getter | |
48 | + private String type; | |
49 | + @Getter | |
50 | + private boolean again; | |
51 | + | |
52 | + UpdateResultFw(int code, String type, boolean isAgain) { | |
53 | + this.code = code; | |
54 | + this.type = type; | |
55 | + this.again = isAgain; | |
56 | + } | |
57 | + | |
58 | + public static UpdateResultFw fromUpdateResultFwByType(String type) { | |
59 | + for (UpdateResultFw to : UpdateResultFw.values()) { | |
60 | + if (to.type.equals(type)) { | |
61 | + return to; | |
62 | + } | |
63 | + } | |
64 | + throw new IllegalArgumentException(String.format("Unsupported FW Update Result type : %s", type)); | |
65 | + } | |
66 | + | |
67 | + public static UpdateResultFw fromUpdateResultFwByCode(int code) { | |
68 | + for (UpdateResultFw to : UpdateResultFw.values()) { | |
69 | + if (to.code == code) { | |
70 | + return to; | |
71 | + } | |
72 | + } | |
73 | + throw new IllegalArgumentException(String.format("Unsupported FW Update Result code : %s", code)); | |
74 | + } | |
75 | +} | ... | ... |
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; | |
17 | + | |
18 | +/** | |
19 | + * /** State R | |
20 | + * 0: Idle (before downloading or after successful updating) | |
21 | + * 1: Downloading (The data sequence is on the way) | |
22 | + * 2: Downloaded | |
23 | + * 3: Updating | |
24 | + */ | |
25 | +public enum UpdateStateFw { | |
26 | + IDLE(0, "Idle"), | |
27 | + DOWNLOADING(1, "Downloading"), | |
28 | + DOWNLOADED(2, "Downloaded"), | |
29 | + UPDATING(3, "Updating"); | |
30 | + | |
31 | + public int code; | |
32 | + public String type; | |
33 | + | |
34 | + UpdateStateFw(int code, String type) { | |
35 | + this.code = code; | |
36 | + this.type = type; | |
37 | + } | |
38 | + | |
39 | + public static UpdateStateFw fromStateFwByType(String type) { | |
40 | + for (UpdateStateFw to : UpdateStateFw.values()) { | |
41 | + if (to.type.equals(type)) { | |
42 | + return to; | |
43 | + } | |
44 | + } | |
45 | + throw new IllegalArgumentException(String.format("Unsupported FW State type : %s", type)); | |
46 | + } | |
47 | + | |
48 | + public static UpdateStateFw fromStateFwByCode(int code) { | |
49 | + for (UpdateStateFw to : UpdateStateFw.values()) { | |
50 | + if (to.code == code) { | |
51 | + return to; | |
52 | + } | |
53 | + } | |
54 | + throw new IllegalArgumentException(String.format("Unsupported FW State code : %s", code)); | |
55 | + } | |
56 | +} | ... | ... |
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.attributes; | |
17 | + | |
18 | +import com.google.common.util.concurrent.ListenableFuture; | |
19 | +import com.google.common.util.concurrent.SettableFuture; | |
20 | +import lombok.RequiredArgsConstructor; | |
21 | +import lombok.extern.slf4j.Slf4j; | |
22 | +import org.eclipse.leshan.core.model.ResourceModel; | |
23 | +import org.eclipse.leshan.core.node.LwM2mPath; | |
24 | +import org.eclipse.leshan.core.node.LwM2mResource; | |
25 | +import org.springframework.stereotype.Service; | |
26 | +import org.thingsboard.server.common.data.ota.OtaPackageKey; | |
27 | +import org.thingsboard.server.common.data.ota.OtaPackageType; | |
28 | +import org.thingsboard.server.common.data.ota.OtaPackageUtil; | |
29 | +import org.thingsboard.server.common.transport.TransportService; | |
30 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | |
31 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
32 | +import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg; | |
33 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | |
34 | +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | |
35 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; | |
36 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; | |
37 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
38 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | |
39 | +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; | |
40 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; | |
41 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; | |
42 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
43 | +import org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService; | |
44 | +import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; | |
45 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
46 | +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; | |
47 | + | |
48 | +import java.util.ArrayList; | |
49 | +import java.util.Collection; | |
50 | +import java.util.List; | |
51 | +import java.util.Map; | |
52 | +import java.util.Optional; | |
53 | +import java.util.concurrent.atomic.AtomicInteger; | |
54 | + | |
55 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper.getValueFromKvProto; | |
56 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; | |
57 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; | |
58 | + | |
59 | +@Slf4j | |
60 | +@Service | |
61 | +@TbLwM2mTransportComponent | |
62 | +@RequiredArgsConstructor | |
63 | +public class DefaultLwM2MAttributesService implements LwM2MAttributesService { | |
64 | + | |
65 | + //TODO: add timeout logic | |
66 | + private final AtomicInteger reqIdSeq = new AtomicInteger(); | |
67 | + private final Map<Integer, SettableFuture<List<TransportProtos.TsKvProto>>> futures; | |
68 | + | |
69 | + private final TransportService transportService; | |
70 | + private final LwM2mTransportServerHelper helper; | |
71 | + private final LwM2mClientContext clientContext; | |
72 | + private final LwM2MTransportServerConfig config; | |
73 | + private final LwM2mUplinkMsgHandler uplinkHandler; | |
74 | + private final LwM2mDownlinkMsgHandler downlinkHandler; | |
75 | + private final LwM2MTelemetryLogService logService; | |
76 | + private final LwM2MOtaUpdateService otaUpdateService; | |
77 | + | |
78 | + @Override | |
79 | + public ListenableFuture<List<TransportProtos.TsKvProto>> getSharedAttributes(LwM2mClient client, Collection<String> keys) { | |
80 | + SettableFuture<List<TransportProtos.TsKvProto>> future = SettableFuture.create(); | |
81 | + int requestId = reqIdSeq.incrementAndGet(); | |
82 | + futures.put(requestId, future); | |
83 | + transportService.process(client.getSession(), TransportProtos.GetAttributeRequestMsg.newBuilder().setRequestId(requestId). | |
84 | + addAllSharedAttributeNames(keys).build(), new TransportServiceCallback<Void>() { | |
85 | + @Override | |
86 | + public void onSuccess(Void msg) { | |
87 | + | |
88 | + } | |
89 | + | |
90 | + @Override | |
91 | + public void onError(Throwable e) { | |
92 | + SettableFuture<List<TransportProtos.TsKvProto>> callback = futures.remove(requestId); | |
93 | + if (callback != null) { | |
94 | + callback.setException(e); | |
95 | + } | |
96 | + } | |
97 | + }); | |
98 | + return future; | |
99 | + } | |
100 | + | |
101 | + @Override | |
102 | + public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse, TransportProtos.SessionInfoProto sessionInfo) { | |
103 | + var callback = futures.remove(getAttributesResponse.getRequestId()); | |
104 | + if (callback != null) { | |
105 | + callback.set(getAttributesResponse.getSharedAttributeListList()); | |
106 | + } | |
107 | + } | |
108 | + | |
109 | + /** | |
110 | + * Update - send request in change value resources in Client | |
111 | + * 1. FirmwareUpdate: | |
112 | + * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 | |
113 | + * 2. Shared Other AttributeUpdate | |
114 | + * -- Path to resources from profile equal keyName or from ModelObject equal name | |
115 | + * -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) | |
116 | + * 3. Delete - nothing | |
117 | + * | |
118 | + * @param msg - | |
119 | + */ | |
120 | + @Override | |
121 | + public void onAttributesUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { | |
122 | + LwM2mClient lwM2MClient = clientContext.getClientBySessionInfo(sessionInfo); | |
123 | + if (msg.getSharedUpdatedCount() > 0 && lwM2MClient != null) { | |
124 | + String newFirmwareTitle = null; | |
125 | + String newFirmwareVersion = null; | |
126 | + String newFirmwareUrl = null; | |
127 | + String newSoftwareTitle = null; | |
128 | + String newSoftwareVersion = null; | |
129 | + List<TransportProtos.TsKvProto> otherAttributes = new ArrayList<>(); | |
130 | + for (TransportProtos.TsKvProto tsKvProto : msg.getSharedUpdatedList()) { | |
131 | + String attrName = tsKvProto.getKv().getKey(); | |
132 | + if (DefaultLwM2MOtaUpdateService.FIRMWARE_TITLE.equals(attrName)) { | |
133 | + newFirmwareTitle = getStrValue(tsKvProto); | |
134 | + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_VERSION.equals(attrName)) { | |
135 | + newFirmwareVersion = getStrValue(tsKvProto); | |
136 | + } else if (DefaultLwM2MOtaUpdateService.FIRMWARE_URL.equals(attrName)) { | |
137 | + newFirmwareUrl = getStrValue(tsKvProto); | |
138 | + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_TITLE.equals(attrName)) { | |
139 | + newSoftwareTitle = getStrValue(tsKvProto); | |
140 | + } else if (DefaultLwM2MOtaUpdateService.SOFTWARE_VERSION.equals(attrName)) { | |
141 | + newSoftwareVersion = getStrValue(tsKvProto); | |
142 | + } else { | |
143 | + otherAttributes.add(tsKvProto); | |
144 | + } | |
145 | + } | |
146 | + if (newFirmwareTitle != null || newFirmwareVersion != null) { | |
147 | + otaUpdateService.onTargetFirmwareUpdate(lwM2MClient, newFirmwareTitle, newFirmwareVersion, Optional.ofNullable(newFirmwareUrl)); | |
148 | + } | |
149 | + if (newSoftwareTitle != null || newSoftwareVersion != null) { | |
150 | + otaUpdateService.onTargetSoftwareUpdate(lwM2MClient, newSoftwareTitle, newSoftwareVersion); | |
151 | + } | |
152 | + if (!otherAttributes.isEmpty()) { | |
153 | + onAttributesUpdate(lwM2MClient, otherAttributes); | |
154 | + } | |
155 | + } else if (lwM2MClient == null) { | |
156 | + log.error("OnAttributeUpdate, lwM2MClient is null"); | |
157 | + } | |
158 | + } | |
159 | + | |
160 | + /** | |
161 | + * #1.1 If two names have equal path => last time attribute | |
162 | + * #2.1 if there is a difference in values between the current resource values and the shared attribute values | |
163 | + * => send to client Request Update of value (new value from shared attribute) | |
164 | + * and LwM2MClient.delayedRequests.add(path) | |
165 | + * #2.1 if there is not a difference in values between the current resource values and the shared attribute values | |
166 | + * | |
167 | + */ | |
168 | + @Override | |
169 | + public void onAttributesUpdate(LwM2mClient lwM2MClient, List<TransportProtos.TsKvProto> tsKvProtos) { | |
170 | + log.trace("[{}] onAttributesUpdate [{}]", lwM2MClient.getEndpoint(), tsKvProtos); | |
171 | + tsKvProtos.forEach(tsKvProto -> { | |
172 | + String pathIdVer = clientContext.getObjectIdByKeyNameFromProfile(lwM2MClient, tsKvProto.getKv().getKey()); | |
173 | + if (pathIdVer != null) { | |
174 | + // #1.1 | |
175 | + if (lwM2MClient.getSharedAttributes().containsKey(pathIdVer)) { | |
176 | + if (tsKvProto.getTs() > lwM2MClient.getSharedAttributes().get(pathIdVer).getTs()) { | |
177 | + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); | |
178 | + } | |
179 | + } else { | |
180 | + lwM2MClient.getSharedAttributes().put(pathIdVer, tsKvProto); | |
181 | + } | |
182 | + } | |
183 | + }); | |
184 | + // #2.1 | |
185 | + lwM2MClient.getSharedAttributes().forEach((pathIdVer, tsKvProto) -> { | |
186 | + this.pushUpdateToClientIfNeeded(lwM2MClient, this.getResourceValueFormatKv(lwM2MClient, pathIdVer), | |
187 | + getValueFromKvProto(tsKvProto.getKv()), pathIdVer); | |
188 | + }); | |
189 | + } | |
190 | + | |
191 | + private void pushUpdateToClientIfNeeded(LwM2mClient lwM2MClient, Object valueOld, Object newValue, String versionedId) { | |
192 | + if (newValue != null && (valueOld == null || !newValue.toString().equals(valueOld.toString()))) { | |
193 | + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId).value(newValue).timeout(this.config.getTimeout()).build(); | |
194 | + downlinkHandler.sendWriteReplaceRequest(lwM2MClient, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, lwM2MClient, versionedId)); | |
195 | + } else { | |
196 | + log.error("Failed update resource [{}] [{}]", versionedId, newValue); | |
197 | + String logMsg = String.format("%s: Failed update resource versionedId - %s value - %s. Value is not changed or bad", | |
198 | + LOG_LWM2M_ERROR, versionedId, newValue); | |
199 | + logService.log(lwM2MClient, logMsg); | |
200 | + log.info("Failed update resource [{}] [{}]", versionedId, newValue); | |
201 | + } | |
202 | + } | |
203 | + | |
204 | + /** | |
205 | + * @param pathIdVer - path resource | |
206 | + * @return - value of Resource into format KvProto or null | |
207 | + */ | |
208 | + private Object getResourceValueFormatKv(LwM2mClient lwM2MClient, String pathIdVer) { | |
209 | + LwM2mResource resourceValue = LwM2mTransportUtil.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); | |
210 | + if (resourceValue != null) { | |
211 | + ResourceModel.Type currentType = resourceValue.getType(); | |
212 | + ResourceModel.Type expectedType = helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); | |
213 | + return LwM2mValueConverterImpl.getInstance().convertValue(resourceValue.getValue(), currentType, expectedType, | |
214 | + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); | |
215 | + } else { | |
216 | + return null; | |
217 | + } | |
218 | + } | |
219 | + | |
220 | + private String getStrValue(TransportProtos.TsKvProto tsKvProto) { | |
221 | + return tsKvProto.getKv().getStringV(); | |
222 | + } | |
223 | +} | ... | ... |
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.attributes; | |
17 | + | |
18 | +import com.google.common.util.concurrent.ListenableFuture; | |
19 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
21 | + | |
22 | +import java.util.Collection; | |
23 | +import java.util.List; | |
24 | + | |
25 | +public interface LwM2MAttributesService { | |
26 | + | |
27 | + ListenableFuture<List<TransportProtos.TsKvProto>> getSharedAttributes(LwM2mClient client, Collection<String> keys); | |
28 | + | |
29 | + void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg getAttributesResponse, TransportProtos.SessionInfoProto sessionInfo); | |
30 | + | |
31 | + void onAttributesUpdate(TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification, TransportProtos.SessionInfoProto sessionInfo); | |
32 | + | |
33 | + void onAttributesUpdate(LwM2mClient lwM2MClient, List<TransportProtos.TsKvProto> tsKvProtos); | |
34 | +} | ... | ... |
... | ... | @@ -20,28 +20,27 @@ import lombok.Setter; |
20 | 20 | import lombok.extern.slf4j.Slf4j; |
21 | 21 | import org.eclipse.leshan.core.model.ObjectModel; |
22 | 22 | import org.eclipse.leshan.core.model.ResourceModel; |
23 | -import org.eclipse.leshan.core.node.LwM2mMultipleResource; | |
24 | 23 | import org.eclipse.leshan.core.node.LwM2mObject; |
25 | 24 | import org.eclipse.leshan.core.node.LwM2mObjectInstance; |
26 | 25 | import org.eclipse.leshan.core.node.LwM2mPath; |
27 | 26 | import org.eclipse.leshan.core.node.LwM2mResource; |
28 | 27 | import org.eclipse.leshan.core.node.LwM2mSingleResource; |
28 | +import org.eclipse.leshan.core.node.codec.LwM2mValueConverter; | |
29 | 29 | import org.eclipse.leshan.core.request.ContentFormat; |
30 | 30 | import org.eclipse.leshan.server.model.LwM2mModelProvider; |
31 | 31 | import org.eclipse.leshan.server.registration.Registration; |
32 | 32 | import org.eclipse.leshan.server.security.SecurityInfo; |
33 | 33 | import org.thingsboard.server.common.data.Device; |
34 | 34 | import org.thingsboard.server.common.data.DeviceProfile; |
35 | +import org.thingsboard.server.common.data.id.TenantId; | |
35 | 36 | import org.thingsboard.server.common.data.ota.OtaPackageType; |
36 | 37 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
37 | 38 | import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; |
38 | 39 | import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto; |
39 | -import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; | |
40 | 40 | import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; |
41 | -import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; | |
41 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
42 | 42 | |
43 | 43 | import java.util.Collection; |
44 | -import java.util.List; | |
45 | 44 | import java.util.Map; |
46 | 45 | import java.util.Optional; |
47 | 46 | import java.util.Queue; |
... | ... | @@ -49,18 +48,15 @@ import java.util.Set; |
49 | 48 | import java.util.UUID; |
50 | 49 | import java.util.concurrent.ConcurrentHashMap; |
51 | 50 | import java.util.concurrent.ConcurrentLinkedQueue; |
52 | -import java.util.concurrent.CopyOnWriteArrayList; | |
53 | 51 | import java.util.concurrent.locks.Lock; |
54 | 52 | import java.util.concurrent.locks.ReentrantLock; |
55 | 53 | import java.util.stream.Collectors; |
56 | 54 | |
57 | -import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE; | |
58 | -import static org.eclipse.leshan.core.model.ResourceModel.Type.STRING; | |
59 | 55 | import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; |
60 | 56 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TRANSPORT_DEFAULT_LWM2M_VERSION; |
61 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; | |
62 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; | |
57 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; | |
63 | 58 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsResourceTypeGetSimpleName; |
59 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; | |
64 | 60 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.getVerFromPathIdVerOrId; |
65 | 61 | |
66 | 62 | @Slf4j |
... | ... | @@ -76,9 +72,7 @@ public class LwM2mClient implements Cloneable { |
76 | 72 | @Getter |
77 | 73 | private final Map<String, ResourceValue> resources; |
78 | 74 | @Getter |
79 | - private final Map<String, TsKvProto> delayedRequests; | |
80 | - @Getter | |
81 | - private final List<String> pendingReadRequests; | |
75 | + private final Map<String, TsKvProto> sharedAttributes; | |
82 | 76 | @Getter |
83 | 77 | private final Queue<LwM2mQueuedRequest> queuedRequests; |
84 | 78 | |
... | ... | @@ -92,6 +86,8 @@ public class LwM2mClient implements Cloneable { |
92 | 86 | @Getter |
93 | 87 | private SecurityInfo securityInfo; |
94 | 88 | @Getter |
89 | + private TenantId tenantId; | |
90 | + @Getter | |
95 | 91 | private UUID deviceId; |
96 | 92 | @Getter |
97 | 93 | private SessionInfoProto session; |
... | ... | @@ -99,19 +95,10 @@ public class LwM2mClient implements Cloneable { |
99 | 95 | private UUID profileId; |
100 | 96 | @Getter |
101 | 97 | @Setter |
102 | - private volatile LwM2mFwSwUpdate fwUpdate; | |
103 | - @Getter | |
104 | - @Setter | |
105 | - private volatile LwM2mFwSwUpdate swUpdate; | |
106 | - @Getter | |
107 | - @Setter | |
108 | 98 | private Registration registration; |
109 | 99 | |
110 | 100 | private ValidateDeviceCredentialsResponse credentials; |
111 | 101 | |
112 | - @Getter | |
113 | - private boolean init; | |
114 | - | |
115 | 102 | public Object clone() throws CloneNotSupportedException { |
116 | 103 | return super.clone(); |
117 | 104 | } |
... | ... | @@ -120,26 +107,22 @@ public class LwM2mClient implements Cloneable { |
120 | 107 | this.nodeId = nodeId; |
121 | 108 | this.endpoint = endpoint; |
122 | 109 | this.lock = new ReentrantLock(); |
123 | - this.delayedRequests = new ConcurrentHashMap<>(); | |
124 | - this.pendingReadRequests = new CopyOnWriteArrayList<>(); | |
110 | + this.sharedAttributes = new ConcurrentHashMap<>(); | |
125 | 111 | this.resources = new ConcurrentHashMap<>(); |
126 | 112 | this.queuedRequests = new ConcurrentLinkedQueue<>(); |
127 | 113 | this.state = LwM2MClientState.CREATED; |
128 | 114 | } |
129 | 115 | |
130 | - public void init(String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) { | |
116 | + public void init(String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID sessionId) { | |
131 | 117 | this.identity = identity; |
132 | 118 | this.securityInfo = securityInfo; |
133 | 119 | this.credentials = credentials; |
134 | - this.profileId = profileId; | |
135 | - this.init = false; | |
136 | - if (this.credentials != null && this.credentials.hasDeviceInfo()) { | |
137 | - this.session = createSession(nodeId, sessionId, credentials); | |
138 | - this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); | |
139 | - this.profileId = new UUID(session.getDeviceProfileIdMSB(), session.getDeviceProfileIdLSB()); | |
140 | - this.deviceName = session.getDeviceName(); | |
141 | - this.deviceProfileName = session.getDeviceType(); | |
142 | - } | |
120 | + this.session = createSession(nodeId, sessionId, credentials); | |
121 | + this.tenantId = new TenantId(new UUID(session.getTenantIdMSB(), session.getTenantIdLSB())); | |
122 | + this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB()); | |
123 | + this.profileId = new UUID(session.getDeviceProfileIdMSB(), session.getDeviceProfileIdLSB()); | |
124 | + this.deviceName = session.getDeviceName(); | |
125 | + this.deviceProfileName = session.getDeviceType(); | |
143 | 126 | } |
144 | 127 | |
145 | 128 | public void lock() { |
... | ... | @@ -198,7 +181,7 @@ public class LwM2mClient implements Cloneable { |
198 | 181 | this.resources.get(pathRezIdVer).setLwM2mResource(rez); |
199 | 182 | return true; |
200 | 183 | } else { |
201 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); | |
184 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); | |
202 | 185 | ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); |
203 | 186 | if (resourceModel != null) { |
204 | 187 | this.resources.put(pathRezIdVer, new ResourceValue(rez, resourceModel)); |
... | ... | @@ -210,7 +193,7 @@ public class LwM2mClient implements Cloneable { |
210 | 193 | } |
211 | 194 | |
212 | 195 | public Object getResourceValue(String pathRezIdVer, String pathRezId) { |
213 | - String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; | |
196 | + String pathRez = pathRezIdVer == null ? convertObjectIdToVersionedId(pathRezId, this.registration) : pathRezIdVer; | |
214 | 197 | if (this.resources.get(pathRez) != null) { |
215 | 198 | return this.resources.get(pathRez).getLwM2mResource().getValue(); |
216 | 199 | } |
... | ... | @@ -218,7 +201,7 @@ public class LwM2mClient implements Cloneable { |
218 | 201 | } |
219 | 202 | |
220 | 203 | public Object getResourceNameByRezId(String pathRezIdVer, String pathRezId) { |
221 | - String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer; | |
204 | + String pathRez = pathRezIdVer == null ? convertObjectIdToVersionedId(pathRezId, this.registration) : pathRezIdVer; | |
222 | 205 | if (this.resources.get(pathRez) != null) { |
223 | 206 | return this.resources.get(pathRez).getResourceModel().name; |
224 | 207 | } |
... | ... | @@ -226,7 +209,7 @@ public class LwM2mClient implements Cloneable { |
226 | 209 | } |
227 | 210 | |
228 | 211 | public String getRezIdByResourceNameAndObjectInstanceId(String resourceName, String pathObjectInstanceIdVer, LwM2mModelProvider modelProvider) { |
229 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathObjectInstanceIdVer)); | |
212 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathObjectInstanceIdVer)); | |
230 | 213 | if (pathIds.isObjectInstance()) { |
231 | 214 | Set<Integer> rezIds = modelProvider.getObjectModel(registration) |
232 | 215 | .getObjectModel(pathIds.getObjectId()).resources.entrySet() |
... | ... | @@ -240,7 +223,7 @@ public class LwM2mClient implements Cloneable { |
240 | 223 | } |
241 | 224 | |
242 | 225 | public ResourceModel getResourceModel(String pathIdVer, LwM2mModelProvider modelProvider) { |
243 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)); | |
226 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); | |
244 | 227 | String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); |
245 | 228 | String verRez = getVerFromPathIdVerOrId(pathIdVer); |
246 | 229 | return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) |
... | ... | @@ -248,14 +231,14 @@ public class LwM2mClient implements Cloneable { |
248 | 231 | } |
249 | 232 | |
250 | 233 | public ObjectModel getObjectModel(String pathIdVer, LwM2mModelProvider modelProvider) { |
251 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer)); | |
234 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); | |
252 | 235 | String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); |
253 | 236 | String verRez = getVerFromPathIdVerOrId(pathIdVer); |
254 | 237 | return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) |
255 | 238 | .getObjectModel(pathIds.getObjectId()) : null; |
256 | 239 | } |
257 | 240 | |
258 | - public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverterImpl converter, String pathIdVer) { | |
241 | + public String objectToString(LwM2mObject lwM2mObject, LwM2mValueConverter converter, String pathIdVer) { | |
259 | 242 | StringBuilder builder = new StringBuilder(); |
260 | 243 | builder.append("LwM2mObject [id=").append(lwM2mObject.getId()).append(", instances={"); |
261 | 244 | lwM2mObject.getInstances().forEach((instId, inst) -> { |
... | ... | @@ -269,7 +252,7 @@ public class LwM2mClient implements Cloneable { |
269 | 252 | return builder.toString(); |
270 | 253 | } |
271 | 254 | |
272 | - public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverterImpl converter, String pathIdVer) { | |
255 | + public String instanceToString(LwM2mObjectInstance objectInstance, LwM2mValueConverter converter, String pathIdVer) { | |
273 | 256 | StringBuilder builder = new StringBuilder(); |
274 | 257 | builder.append("LwM2mObjectInstance [id=").append(objectInstance.getId()).append(", resources={"); |
275 | 258 | objectInstance.getResources().forEach((resId, res) -> { |
... | ... | @@ -283,25 +266,18 @@ public class LwM2mClient implements Cloneable { |
283 | 266 | return builder.toString(); |
284 | 267 | } |
285 | 268 | |
286 | - public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverterImpl converter, String pathIdVer) { | |
287 | - if (!OPAQUE.equals(lwM2mResource.getType())) { | |
288 | - return lwM2mResource.isMultiInstances() ? ((LwM2mMultipleResource) lwM2mResource).toString() : | |
289 | - ((LwM2mSingleResource) lwM2mResource).toString(); | |
290 | - } else { | |
291 | - return String.format("LwM2mSingleResource [id=%s, value=%s, type=%s]", lwM2mResource.getId(), | |
292 | - converter.convertValue(lwM2mResource.getValue(), | |
293 | - OPAQUE, STRING, new LwM2mPath(convertPathFromIdVerToObjectId(pathIdVer))), lwM2mResource.getType().name()); | |
294 | - } | |
269 | + public String resourceToString(LwM2mResource lwM2mResource, LwM2mValueConverter converter, String pathIdVer) { | |
270 | + return lwM2mResource.getValue().toString(); | |
295 | 271 | } |
296 | 272 | |
297 | 273 | public Collection<LwM2mResource> getNewResourceForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, |
298 | - LwM2mValueConverterImpl converter) { | |
299 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); | |
274 | + LwM2mValueConverter converter) { | |
275 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); | |
300 | 276 | Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet(); |
301 | 277 | Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration) |
302 | 278 | .getObjectModel(pathIds.getObjectId()).resources; |
303 | 279 | resourceModels.forEach((resId, resourceModel) -> { |
304 | - if (resId == pathIds.getResourceId()) { | |
280 | + if (resId.equals(pathIds.getResourceId())) { | |
305 | 281 | resources.add(LwM2mSingleResource.newResource(resId, converter.convertValue(params, |
306 | 282 | equalsResourceTypeGetSimpleName(params), resourceModel.type, pathIds), resourceModel.type)); |
307 | 283 | |
... | ... | @@ -311,14 +287,14 @@ public class LwM2mClient implements Cloneable { |
311 | 287 | } |
312 | 288 | |
313 | 289 | public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, Object params, LwM2mModelProvider modelProvider, |
314 | - LwM2mValueConverterImpl converter) { | |
315 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); | |
290 | + LwM2mValueConverter converter) { | |
291 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRezIdVer)); | |
316 | 292 | Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet(); |
317 | 293 | Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration) |
318 | 294 | .getObjectModel(pathIds.getObjectId()).resources; |
319 | 295 | resourceModels.forEach((resId, resourceModel) -> { |
320 | - if (((ConcurrentHashMap) params).containsKey(String.valueOf(resId))) { | |
321 | - Object value = ((ConcurrentHashMap) params).get((String.valueOf(resId))); | |
296 | + if (((Map) params).containsKey(String.valueOf(resId))) { | |
297 | + Object value = ((Map) params).get((String.valueOf(resId))); | |
322 | 298 | resources.add(LwM2mSingleResource.newResource(resId, |
323 | 299 | converter.convertValue(value, equalsResourceTypeGetSimpleName(value), resourceModel.type, pathIds), resourceModel.type)); |
324 | 300 | |
... | ... | @@ -328,7 +304,7 @@ public class LwM2mClient implements Cloneable { |
328 | 304 | } |
329 | 305 | |
330 | 306 | public boolean isValidObjectVersion(String path) { |
331 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(path)); | |
307 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(path)); | |
332 | 308 | String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); |
333 | 309 | String verRez = getVerFromPathIdVerOrId(path); |
334 | 310 | return verRez == null ? TRANSPORT_DEFAULT_LWM2M_VERSION.equals(verSupportedObject) : verRez.equals(verSupportedObject); |
... | ... | @@ -341,7 +317,7 @@ public class LwM2mClient implements Cloneable { |
341 | 317 | public void deleteResources(String pathIdVer, LwM2mModelProvider modelProvider) { |
342 | 318 | Set<String> key = getKeysEqualsIdVer(pathIdVer); |
343 | 319 | key.forEach(pathRez -> { |
344 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); | |
320 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRez)); | |
345 | 321 | ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); |
346 | 322 | if (resourceModel != null) { |
347 | 323 | this.resources.get(pathRez).setResourceModel(resourceModel); |
... | ... | @@ -361,7 +337,7 @@ public class LwM2mClient implements Cloneable { |
361 | 337 | } |
362 | 338 | |
363 | 339 | private void saveResourceModel(String pathRez, LwM2mModelProvider modelProvider) { |
364 | - LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); | |
340 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathRez)); | |
365 | 341 | ResourceModel resourceModel = modelProvider.getObjectModel(registration).getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()); |
366 | 342 | this.resources.get(pathRez).setResourceModel(resourceModel); |
367 | 343 | } |
... | ... | @@ -373,16 +349,6 @@ public class LwM2mClient implements Cloneable { |
373 | 349 | .collect(Collectors.toSet()); |
374 | 350 | } |
375 | 351 | |
376 | - public void initReadValue(DefaultLwM2MTransportMsgHandler serviceImpl, String path) { | |
377 | - if (path != null) { | |
378 | - this.pendingReadRequests.remove(path); | |
379 | - } | |
380 | - if (this.pendingReadRequests.size() == 0) { | |
381 | - this.init = true; | |
382 | - serviceImpl.putDelayedUpdateResourcesThingsboard(this); | |
383 | - } | |
384 | - } | |
385 | - | |
386 | 352 | public ContentFormat getDefaultContentFormat() { |
387 | 353 | if (registration == null) { |
388 | 354 | return ContentFormat.DEFAULT; |
... | ... | @@ -393,21 +359,5 @@ public class LwM2mClient implements Cloneable { |
393 | 359 | } |
394 | 360 | } |
395 | 361 | |
396 | - public LwM2mFwSwUpdate getFwUpdate (LwM2mClientContext clientContext) { | |
397 | - if (this.fwUpdate == null) { | |
398 | - LwM2mClientProfile lwM2mClientProfile = clientContext.getProfile(this.getProfileId()); | |
399 | - this.fwUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.FIRMWARE, lwM2mClientProfile.getFwUpdateStrategy()); | |
400 | - } | |
401 | - return this.fwUpdate; | |
402 | - } | |
403 | - | |
404 | - public LwM2mFwSwUpdate getSwUpdate (LwM2mClientContext clientContext) { | |
405 | - if (this.swUpdate == null) { | |
406 | - LwM2mClientProfile lwM2mClientProfile = clientContext.getProfile(this.getProfileId()); | |
407 | - this.swUpdate = new LwM2mFwSwUpdate(this, OtaPackageType.SOFTWARE, lwM2mClientProfile.getSwUpdateStrategy()); | |
408 | - } | |
409 | - return this.fwUpdate; | |
410 | - } | |
411 | - | |
412 | 362 | } |
413 | 363 | ... | ... |
... | ... | @@ -17,11 +17,12 @@ package org.thingsboard.server.transport.lwm2m.server.client; |
17 | 17 | |
18 | 18 | import org.eclipse.leshan.server.registration.Registration; |
19 | 19 | import org.thingsboard.server.common.data.DeviceProfile; |
20 | +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; | |
20 | 21 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
21 | 22 | import org.thingsboard.server.gen.transport.TransportProtos; |
22 | 23 | |
23 | 24 | import java.util.Collection; |
24 | -import java.util.Map; | |
25 | +import java.util.Optional; | |
25 | 26 | import java.util.Set; |
26 | 27 | import java.util.UUID; |
27 | 28 | |
... | ... | @@ -33,7 +34,7 @@ public interface LwM2mClientContext { |
33 | 34 | |
34 | 35 | LwM2mClient getClientBySessionInfo(TransportProtos.SessionInfoProto sessionInfo); |
35 | 36 | |
36 | - void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException; | |
37 | + Optional<TransportProtos.SessionInfoProto> register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException; | |
37 | 38 | |
38 | 39 | void updateRegistration(LwM2mClient client, Registration registration) throws LwM2MClientStateException; |
39 | 40 | |
... | ... | @@ -41,21 +42,21 @@ public interface LwM2mClientContext { |
41 | 42 | |
42 | 43 | Collection<LwM2mClient> getLwM2mClients(); |
43 | 44 | |
44 | - Map<UUID, LwM2mClientProfile> getProfiles(); | |
45 | + //TODO: replace UUID with DeviceProfileId | |
46 | + Lwm2mDeviceProfileTransportConfiguration getProfile(UUID profileUuId); | |
45 | 47 | |
46 | - LwM2mClientProfile getProfile(UUID profileUuId); | |
48 | + Lwm2mDeviceProfileTransportConfiguration getProfile(Registration registration); | |
47 | 49 | |
48 | - LwM2mClientProfile getProfile(Registration registration); | |
49 | - | |
50 | - Map<UUID, LwM2mClientProfile> setProfiles(Map<UUID, LwM2mClientProfile> profiles); | |
51 | - | |
52 | - LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile); | |
50 | + Lwm2mDeviceProfileTransportConfiguration profileUpdate(DeviceProfile deviceProfile); | |
53 | 51 | |
54 | 52 | Set<String> getSupportedIdVerInClient(LwM2mClient registration); |
55 | 53 | |
56 | 54 | LwM2mClient getClientByDeviceId(UUID deviceId); |
57 | 55 | |
58 | - void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); | |
56 | + String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName); | |
59 | 57 | |
58 | + String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName); | |
59 | + | |
60 | + void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials); | |
60 | 61 | |
61 | 62 | } | ... | ... |
... | ... | @@ -17,13 +17,16 @@ package org.thingsboard.server.transport.lwm2m.server.client; |
17 | 17 | |
18 | 18 | import lombok.RequiredArgsConstructor; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.eclipse.leshan.core.model.ResourceModel; | |
20 | 21 | import org.eclipse.leshan.core.node.LwM2mPath; |
21 | 22 | import org.eclipse.leshan.server.registration.Registration; |
22 | 23 | import org.springframework.stereotype.Service; |
23 | 24 | import org.thingsboard.server.common.data.DeviceProfile; |
25 | +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; | |
24 | 26 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
25 | 27 | import org.thingsboard.server.gen.transport.TransportProtos; |
26 | 28 | import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; |
29 | +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | |
27 | 30 | import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MSecurityInfo; |
28 | 31 | import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; |
29 | 32 | import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; |
... | ... | @@ -39,7 +42,9 @@ import java.util.concurrent.ConcurrentHashMap; |
39 | 42 | import java.util.function.Predicate; |
40 | 43 | |
41 | 44 | import static org.eclipse.leshan.core.SecurityMode.NO_SEC; |
42 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; | |
45 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; | |
46 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; | |
47 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validateObjectVerFromKey; | |
43 | 48 | |
44 | 49 | @Slf4j |
45 | 50 | @Service |
... | ... | @@ -48,10 +53,11 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.c |
48 | 53 | public class LwM2mClientContextImpl implements LwM2mClientContext { |
49 | 54 | |
50 | 55 | private final LwM2mTransportContext context; |
56 | + private final LwM2MTransportServerConfig config; | |
51 | 57 | private final TbEditableSecurityStore securityStore; |
52 | 58 | private final Map<String, LwM2mClient> lwM2mClientsByEndpoint = new ConcurrentHashMap<>(); |
53 | 59 | private final Map<String, LwM2mClient> lwM2mClientsByRegistrationId = new ConcurrentHashMap<>(); |
54 | - private Map<UUID, LwM2mClientProfile> profiles = new ConcurrentHashMap<>(); | |
60 | + private final Map<UUID, Lwm2mDeviceProfileTransportConfiguration> profiles = new ConcurrentHashMap<>(); | |
55 | 61 | |
56 | 62 | @Override |
57 | 63 | public LwM2mClient getClientByEndpoint(String endpoint) { |
... | ... | @@ -59,20 +65,22 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
59 | 65 | } |
60 | 66 | |
61 | 67 | @Override |
62 | - public void register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException { | |
68 | + public Optional<TransportProtos.SessionInfoProto> register(LwM2mClient lwM2MClient, Registration registration) throws LwM2MClientStateException { | |
69 | + TransportProtos.SessionInfoProto oldSession = null; | |
63 | 70 | lwM2MClient.lock(); |
64 | 71 | try { |
65 | 72 | if (LwM2MClientState.UNREGISTERED.equals(lwM2MClient.getState())) { |
66 | 73 | throw new LwM2MClientStateException(lwM2MClient.getState(), "Client is in invalid state."); |
67 | 74 | } |
75 | + oldSession = lwM2MClient.getSession(); | |
68 | 76 | TbLwM2MSecurityInfo securityInfo = securityStore.getTbLwM2MSecurityInfoByEndpoint(lwM2MClient.getEndpoint()); |
69 | 77 | if (securityInfo.getSecurityMode() != null) { |
70 | 78 | if (securityInfo.getDeviceProfile() != null) { |
71 | - UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile()) != null ? securityInfo.getDeviceProfile().getUuidId() : null; | |
79 | + profileUpdate(securityInfo.getDeviceProfile()); | |
72 | 80 | if (securityInfo.getSecurityInfo() != null) { |
73 | - lwM2MClient.init(securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(), securityInfo.getMsg(), profileUuid, UUID.randomUUID()); | |
81 | + lwM2MClient.init(securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(), securityInfo.getMsg(), UUID.randomUUID()); | |
74 | 82 | } else if (NO_SEC.equals(securityInfo.getSecurityMode())) { |
75 | - lwM2MClient.init(null, null, securityInfo.getMsg(), profileUuid, UUID.randomUUID()); | |
83 | + lwM2MClient.init(null, null, securityInfo.getMsg(), UUID.randomUUID()); | |
76 | 84 | } else { |
77 | 85 | throw new RuntimeException(String.format("Registration failed: device %s not found.", lwM2MClient.getEndpoint())); |
78 | 86 | } |
... | ... | @@ -88,6 +96,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
88 | 96 | } finally { |
89 | 97 | lwM2MClient.unlock(); |
90 | 98 | } |
99 | + return Optional.ofNullable(oldSession); | |
91 | 100 | } |
92 | 101 | |
93 | 102 | @Override |
... | ... | @@ -156,6 +165,28 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
156 | 165 | return lwM2mClient; |
157 | 166 | } |
158 | 167 | |
168 | + /** | |
169 | + * Get path to resource from profile equal keyName | |
170 | + * | |
171 | + * @param sessionInfo - | |
172 | + * @param keyName - | |
173 | + * @return - | |
174 | + */ | |
175 | + @Override | |
176 | + public String getObjectIdByKeyNameFromProfile(TransportProtos.SessionInfoProto sessionInfo, String keyName) { | |
177 | + return getObjectIdByKeyNameFromProfile(getClientBySessionInfo(sessionInfo), keyName); | |
178 | + } | |
179 | + | |
180 | + @Override | |
181 | + public String getObjectIdByKeyNameFromProfile(LwM2mClient lwM2mClient, String keyName) { | |
182 | + Lwm2mDeviceProfileTransportConfiguration profile = getProfile(lwM2mClient.getProfileId()); | |
183 | + | |
184 | + return profile.getObserveAttr().getKeyName().entrySet().stream() | |
185 | + .filter(e -> e.getValue().equals(keyName) && validateResourceInModel(lwM2mClient, e.getKey(), false)).findFirst().orElseThrow( | |
186 | + () -> new IllegalArgumentException(keyName + " is not configured in the device profile!") | |
187 | + ).getKey(); | |
188 | + } | |
189 | + | |
159 | 190 | public Registration getRegistration(String registrationId) { |
160 | 191 | return this.lwM2mClientsByRegistrationId.get(registrationId).getRegistration(); |
161 | 192 | } |
... | ... | @@ -163,7 +194,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
163 | 194 | @Override |
164 | 195 | public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) { |
165 | 196 | LwM2mClient client = getClientByEndpoint(registration.getEndpoint()); |
166 | - client.init(null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID()); | |
197 | + client.init(null, null, credentials, UUID.randomUUID()); | |
167 | 198 | lwM2mClientsByRegistrationId.put(registration.getId(), client); |
168 | 199 | profileUpdate(credentials.getDeviceProfile()); |
169 | 200 | } |
... | ... | @@ -174,35 +205,20 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
174 | 205 | } |
175 | 206 | |
176 | 207 | @Override |
177 | - public Map<UUID, LwM2mClientProfile> getProfiles() { | |
178 | - return profiles; | |
179 | - } | |
180 | - | |
181 | - @Override | |
182 | - public LwM2mClientProfile getProfile(UUID profileId) { | |
208 | + public Lwm2mDeviceProfileTransportConfiguration getProfile(UUID profileId) { | |
183 | 209 | return profiles.get(profileId); |
184 | 210 | } |
185 | 211 | |
186 | 212 | @Override |
187 | - public LwM2mClientProfile getProfile(Registration registration) { | |
188 | - return this.getProfiles().get(getClientByEndpoint(registration.getEndpoint()).getProfileId()); | |
189 | - } | |
190 | - | |
191 | - @Override | |
192 | - public Map<UUID, LwM2mClientProfile> setProfiles(Map<UUID, LwM2mClientProfile> profiles) { | |
193 | - return this.profiles = profiles; | |
213 | + public Lwm2mDeviceProfileTransportConfiguration getProfile(Registration registration) { | |
214 | + return profiles.get(getClientByEndpoint(registration.getEndpoint()).getProfileId()); | |
194 | 215 | } |
195 | 216 | |
196 | 217 | @Override |
197 | - public LwM2mClientProfile profileUpdate(DeviceProfile deviceProfile) { | |
198 | - LwM2mClientProfile lwM2MClientProfile = deviceProfile != null ? | |
199 | - LwM2mTransportUtil.toLwM2MClientProfile(deviceProfile) : null; | |
200 | - if (lwM2MClientProfile != null) { | |
201 | - profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); | |
202 | - return lwM2MClientProfile; | |
203 | - } else { | |
204 | - return null; | |
205 | - } | |
218 | + public Lwm2mDeviceProfileTransportConfiguration profileUpdate(DeviceProfile deviceProfile) { | |
219 | + Lwm2mDeviceProfileTransportConfiguration lwM2MClientProfile = LwM2mTransportUtil.toLwM2MClientProfile(deviceProfile); | |
220 | + profiles.put(deviceProfile.getUuidId(), lwM2MClientProfile); | |
221 | + return lwM2MClientProfile; | |
206 | 222 | } |
207 | 223 | |
208 | 224 | @Override |
... | ... | @@ -211,7 +227,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
211 | 227 | Arrays.stream(client.getRegistration().getObjectLinks()).forEach(link -> { |
212 | 228 | LwM2mPath pathIds = new LwM2mPath(link.getUrl()); |
213 | 229 | if (!pathIds.isRoot()) { |
214 | - clientObjects.add(convertPathFromObjectIdToIdVer(link.getUrl(), client.getRegistration())); | |
230 | + clientObjects.add(convertObjectIdToVersionedId(link.getUrl(), client.getRegistration())); | |
215 | 231 | } |
216 | 232 | }); |
217 | 233 | return (clientObjects.size() > 0) ? clientObjects : null; |
... | ... | @@ -222,4 +238,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
222 | 238 | return lwM2mClientsByRegistrationId.values().stream().filter(e -> deviceId.equals(e.getDeviceId())).findFirst().orElse(null); |
223 | 239 | } |
224 | 240 | |
241 | + private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { | |
242 | + ResourceModel resourceModel = lwM2mClient.getResourceModel(pathIdVer, this.config | |
243 | + .getModelProvider()); | |
244 | + Integer objectId = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)).getObjectId(); | |
245 | + String objectVer = validateObjectVerFromKey(pathIdVer); | |
246 | + return resourceModel != null && (isWritableNotOptional ? | |
247 | + objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId)) && resourceModel.operations.isWritable() : | |
248 | + objectId != null && objectVer != null && objectVer.equals(lwM2mClient.getRegistration().getSupportedVersion(objectId))); | |
249 | + } | |
250 | + | |
225 | 251 | } | ... | ... |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientProfile.java
deleted
100644 → 0
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.client; | |
17 | - | |
18 | -import com.google.gson.Gson; | |
19 | -import com.google.gson.JsonArray; | |
20 | -import com.google.gson.JsonObject; | |
21 | -import lombok.Data; | |
22 | -import org.thingsboard.server.common.data.id.TenantId; | |
23 | -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MClientStrategy; | |
24 | -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy; | |
25 | -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MSoftwareUpdateStrategy; | |
26 | - | |
27 | -@Data | |
28 | -public class LwM2mClientProfile { | |
29 | - private final String clientStrategyStr = "clientStrategy"; | |
30 | - private final String fwUpdateStrategyStr = "fwUpdateStrategy"; | |
31 | - private final String swUpdateStrategyStr = "swUpdateStrategy"; | |
32 | - | |
33 | - private TenantId tenantId; | |
34 | - /** | |
35 | - * "clientLwM2mSettings": { | |
36 | - * "fwUpdateStrategy": "1", | |
37 | - * "swUpdateStrategy": "1", | |
38 | - * "clientStrategy": "1" | |
39 | - * } | |
40 | - **/ | |
41 | - private JsonObject postClientLwM2mSettings; | |
42 | - | |
43 | - /** | |
44 | - * {"keyName": { | |
45 | - * "/3_1.0/0/1": "modelNumber", | |
46 | - * "/3_1.0/0/0": "manufacturer", | |
47 | - * "/3_1.0/0/2": "serialNumber" | |
48 | - * } | |
49 | - **/ | |
50 | - private JsonObject postKeyNameProfile; | |
51 | - | |
52 | - /** | |
53 | - * [ "/3_1.0/0/0", "/3_1.0/0/1"] | |
54 | - */ | |
55 | - private JsonArray postAttributeProfile; | |
56 | - | |
57 | - /** | |
58 | - * [ "/3_1.0/0/0", "/3_1.0/0/2"] | |
59 | - */ | |
60 | - private JsonArray postTelemetryProfile; | |
61 | - | |
62 | - /** | |
63 | - * [ "/3_1.0/0", "/3_1.0/0/1, "/3_1.0/0/2"] | |
64 | - */ | |
65 | - private JsonArray postObserveProfile; | |
66 | - | |
67 | - /** | |
68 | - * "attributeLwm2m": {"/3_1.0": {"ver": "currentTimeTest11"}, | |
69 | - * "/3_1.0/0": {"gt": 17}, | |
70 | - * "/3_1.0/0/9": {"pmax": 45}, "/3_1.2": {ver": "3_1.2"}} | |
71 | - */ | |
72 | - private JsonObject postAttributeLwm2mProfile; | |
73 | - | |
74 | - public LwM2mClientProfile clone() { | |
75 | - LwM2mClientProfile lwM2mClientProfile = new LwM2mClientProfile(); | |
76 | - lwM2mClientProfile.postClientLwM2mSettings = this.deepCopy(this.postClientLwM2mSettings, JsonObject.class); | |
77 | - lwM2mClientProfile.postKeyNameProfile = this.deepCopy(this.postKeyNameProfile, JsonObject.class); | |
78 | - lwM2mClientProfile.postAttributeProfile = this.deepCopy(this.postAttributeProfile, JsonArray.class); | |
79 | - lwM2mClientProfile.postTelemetryProfile = this.deepCopy(this.postTelemetryProfile, JsonArray.class); | |
80 | - lwM2mClientProfile.postObserveProfile = this.deepCopy(this.postObserveProfile, JsonArray.class); | |
81 | - lwM2mClientProfile.postAttributeLwm2mProfile = this.deepCopy(this.postAttributeLwm2mProfile, JsonObject.class); | |
82 | - return lwM2mClientProfile; | |
83 | - } | |
84 | - | |
85 | - | |
86 | - private <T> T deepCopy(T elements, Class<T> type) { | |
87 | - try { | |
88 | - Gson gson = new Gson(); | |
89 | - return gson.fromJson(gson.toJson(elements), type); | |
90 | - } catch (Exception e) { | |
91 | - e.printStackTrace(); | |
92 | - return null; | |
93 | - } | |
94 | - } | |
95 | - | |
96 | - public int getClientStrategy() { | |
97 | - return this.postClientLwM2mSettings.getAsJsonObject().has(this.clientStrategyStr) ? | |
98 | - Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.clientStrategyStr).getAsString()) : | |
99 | - LwM2MClientStrategy.CLIENT_STRATEGY_1.code; | |
100 | - } | |
101 | - | |
102 | - public int getFwUpdateStrategy() { | |
103 | - return this.postClientLwM2mSettings.getAsJsonObject().has(this.fwUpdateStrategyStr) ? | |
104 | - Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.fwUpdateStrategyStr).getAsString()) : | |
105 | - LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code; | |
106 | - } | |
107 | - | |
108 | - public int getSwUpdateStrategy() { | |
109 | - return this.postClientLwM2mSettings.getAsJsonObject().has(this.swUpdateStrategyStr) ? | |
110 | - Integer.parseInt(this.postClientLwM2mSettings.getAsJsonObject().get(this.swUpdateStrategyStr).getAsString()) : | |
111 | - LwM2MSoftwareUpdateStrategy.BINARY.code; | |
112 | - } | |
113 | -} |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientRpcRequest.java
deleted
100644 → 0
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.client; | |
17 | - | |
18 | -import com.google.gson.Gson; | |
19 | -import com.google.gson.JsonObject; | |
20 | -import com.google.gson.reflect.TypeToken; | |
21 | -import lombok.Data; | |
22 | -import lombok.extern.slf4j.Slf4j; | |
23 | -import org.apache.commons.lang3.StringUtils; | |
24 | -import org.eclipse.leshan.core.node.LwM2mPath; | |
25 | -import org.eclipse.leshan.server.registration.Registration; | |
26 | -import org.thingsboard.server.gen.transport.TransportProtos; | |
27 | -import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; | |
28 | - | |
29 | -import java.util.Map; | |
30 | -import java.util.Objects; | |
31 | -import java.util.concurrent.ConcurrentHashMap; | |
32 | -import java.util.concurrent.TimeoutException; | |
33 | - | |
34 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.ERROR_KEY; | |
35 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_JSON_KEY; | |
36 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FINISH_VALUE_KEY; | |
37 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.INFO_KEY; | |
38 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.KEY_NAME_KEY; | |
39 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper; | |
40 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.DISCOVER_ALL; | |
41 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; | |
42 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE; | |
43 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_CANCEL; | |
44 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE_READ_ALL; | |
45 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_ATTRIBUTES; | |
46 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; | |
47 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_UPDATE; | |
48 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.METHOD_KEY; | |
49 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.PARAMS_KEY; | |
50 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.RESULT_KEY; | |
51 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SEPARATOR_KEY; | |
52 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.START_JSON_KEY; | |
53 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.TARGET_ID_VER_KEY; | |
54 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.VALUE_KEY; | |
55 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromIdVerToObjectId; | |
56 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.validPathIdVer; | |
57 | - | |
58 | -@Slf4j | |
59 | -@Data | |
60 | -public class LwM2mClientRpcRequest { | |
61 | - | |
62 | - private Registration registration; | |
63 | - private TransportProtos.SessionInfoProto sessionInfo; | |
64 | - private String bodyParams; | |
65 | - private int requestId; | |
66 | - | |
67 | - private LwM2mTypeOper typeOper; | |
68 | - private String key; | |
69 | - private String targetIdVer; | |
70 | - private Object value; | |
71 | - private Map<String, Object> params; | |
72 | - | |
73 | - private String errorMsg; | |
74 | - private String valueMsg; | |
75 | - private String infoMsg; | |
76 | - private String responseCode; | |
77 | - | |
78 | - public LwM2mClientRpcRequest() { | |
79 | - } | |
80 | - | |
81 | - public LwM2mClientRpcRequest(LwM2mTypeOper lwM2mTypeOper, String bodyParams, int requestId, | |
82 | - TransportProtos.SessionInfoProto sessionInfo, Registration registration, DefaultLwM2MTransportMsgHandler handler) { | |
83 | - this.registration = registration; | |
84 | - this.sessionInfo = sessionInfo; | |
85 | - this.requestId = requestId; | |
86 | - if (lwM2mTypeOper != null) { | |
87 | - this.typeOper = lwM2mTypeOper; | |
88 | - } else { | |
89 | - this.errorMsg = METHOD_KEY + " - " + typeOper + " is not valid."; | |
90 | - } | |
91 | - if (this.errorMsg == null && !bodyParams.equals("null")) { | |
92 | - this.bodyParams = bodyParams; | |
93 | - this.init(handler); | |
94 | - } | |
95 | - } | |
96 | - | |
97 | - public TransportProtos.ToDeviceRpcResponseMsg getDeviceRpcResponseResultMsg() { | |
98 | - JsonObject payloadResp = new JsonObject(); | |
99 | - payloadResp.addProperty(RESULT_KEY, this.responseCode); | |
100 | - if (this.errorMsg != null) { | |
101 | - payloadResp.addProperty(ERROR_KEY, this.errorMsg); | |
102 | - } else if (this.valueMsg != null) { | |
103 | - payloadResp.addProperty(VALUE_KEY, this.valueMsg); | |
104 | - } else if (this.infoMsg != null) { | |
105 | - payloadResp.addProperty(INFO_KEY, this.infoMsg); | |
106 | - } | |
107 | - return TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | |
108 | - .setPayload(payloadResp.getAsJsonObject().toString()) | |
109 | - .setRequestId(this.requestId) | |
110 | - .build(); | |
111 | - } | |
112 | - | |
113 | - private void init(DefaultLwM2MTransportMsgHandler handler) { | |
114 | - try { | |
115 | - // #1 | |
116 | - if (this.bodyParams.contains(KEY_NAME_KEY)) { | |
117 | - String targetIdVerStr = this.getValueKeyFromBody(KEY_NAME_KEY); | |
118 | - if (targetIdVerStr != null) { | |
119 | - String targetIdVer = handler.getPresentPathIntoProfile(sessionInfo, targetIdVerStr); | |
120 | - if (targetIdVer != null) { | |
121 | - this.targetIdVer = targetIdVer; | |
122 | - this.setInfoMsg(String.format("Changed by: key - %s, pathIdVer - %s", | |
123 | - targetIdVerStr, targetIdVer)); | |
124 | - } | |
125 | - } | |
126 | - } | |
127 | - if (this.getTargetIdVer() == null && this.bodyParams.contains(TARGET_ID_VER_KEY)) { | |
128 | - this.setValidTargetIdVerKey(); | |
129 | - } | |
130 | - if (this.bodyParams.contains(VALUE_KEY)) { | |
131 | - this.value = this.getValueKeyFromBody(VALUE_KEY); | |
132 | - } | |
133 | - try { | |
134 | - if (this.bodyParams.contains(PARAMS_KEY)) { | |
135 | - this.setValidParamsKey(handler); | |
136 | - } | |
137 | - } catch (Exception e) { | |
138 | - this.setErrorMsg(String.format("Params of request is bad Json format. %s", e.getMessage())); | |
139 | - } | |
140 | - | |
141 | - if (this.getTargetIdVer() == null | |
142 | - && !(OBSERVE_READ_ALL == this.getTypeOper() | |
143 | - || DISCOVER_ALL == this.getTypeOper() | |
144 | - || OBSERVE_CANCEL == this.getTypeOper() | |
145 | - || FW_UPDATE == this.getTypeOper())) { | |
146 | - this.setErrorMsg(TARGET_ID_VER_KEY + " and " + | |
147 | - KEY_NAME_KEY + " is null or bad format"); | |
148 | - } | |
149 | - /** | |
150 | - * EXECUTE && WRITE_REPLACE - only for Resource or ResourceInstance | |
151 | - */ | |
152 | - else if (this.getTargetIdVer() != null | |
153 | - && (EXECUTE == this.getTypeOper() | |
154 | - || WRITE_REPLACE == this.getTypeOper()) | |
155 | - && !(new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResource() | |
156 | - || new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.getTargetIdVer()))).isResourceInstance())) { | |
157 | - this.setErrorMsg("Invalid parameter " + TARGET_ID_VER_KEY | |
158 | - + ". Only Resource or ResourceInstance can be this operation"); | |
159 | - } | |
160 | - } catch (Exception e) { | |
161 | - this.setErrorMsg(String.format("Bad format request. %s", e.getMessage())); | |
162 | - } | |
163 | - | |
164 | - } | |
165 | - | |
166 | - private void setValidTargetIdVerKey() { | |
167 | - String targetIdVerStr = this.getValueKeyFromBody(TARGET_ID_VER_KEY); | |
168 | - // targetIdVer without ver - ok | |
169 | - try { | |
170 | - // targetIdVer with/without ver - ok | |
171 | - this.targetIdVer = validPathIdVer(targetIdVerStr, this.registration); | |
172 | - if (this.targetIdVer != null) { | |
173 | - this.infoMsg = String.format("Changed by: pathIdVer - %s", this.targetIdVer); | |
174 | - } | |
175 | - } catch (Exception e) { | |
176 | - if (this.targetIdVer == null) { | |
177 | - this.errorMsg = TARGET_ID_VER_KEY + " - " + targetIdVerStr + " is not valid."; | |
178 | - } | |
179 | - } | |
180 | - } | |
181 | - | |
182 | - private void setValidParamsKey(DefaultLwM2MTransportMsgHandler handler) { | |
183 | - String paramsStr = this.getValueKeyFromBody(PARAMS_KEY); | |
184 | - if (paramsStr != null) { | |
185 | - String params2Json = | |
186 | - START_JSON_KEY | |
187 | - + "\"" | |
188 | - + paramsStr | |
189 | - .replaceAll(SEPARATOR_KEY, "\"" + SEPARATOR_KEY + "\"") | |
190 | - .replaceAll(FINISH_VALUE_KEY, "\"" + FINISH_VALUE_KEY + "\"") | |
191 | - + "\"" | |
192 | - + FINISH_JSON_KEY; | |
193 | - // jsonObject | |
194 | - Map<String, Object> params = new Gson().fromJson(params2Json, new TypeToken<ConcurrentHashMap<String, Object>>() { | |
195 | - }.getType()); | |
196 | - if (WRITE_UPDATE == this.getTypeOper()) { | |
197 | - if (this.targetIdVer != null) { | |
198 | - Map<String, Object> paramsResourceId = this.convertParamsToResourceId((ConcurrentHashMap<String, Object>) params, handler); | |
199 | - if (paramsResourceId.size() > 0) { | |
200 | - this.setParams(paramsResourceId); | |
201 | - } | |
202 | - } | |
203 | - } else if (WRITE_ATTRIBUTES == this.getTypeOper()) { | |
204 | - this.setParams(params); | |
205 | - } | |
206 | - } | |
207 | - } | |
208 | - | |
209 | - private String getValueKeyFromBody(String key) { | |
210 | - String valueKey = null; | |
211 | - int startInd = -1; | |
212 | - int finishInd = -1; | |
213 | - try { | |
214 | - switch (key) { | |
215 | - case KEY_NAME_KEY: | |
216 | - case TARGET_ID_VER_KEY: | |
217 | - case VALUE_KEY: | |
218 | - startInd = this.bodyParams.indexOf(SEPARATOR_KEY, this.bodyParams.indexOf(key)); | |
219 | - finishInd = this.bodyParams.indexOf(FINISH_VALUE_KEY, this.bodyParams.indexOf(key)); | |
220 | - if (startInd >= 0 && finishInd < 0) { | |
221 | - finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key)); | |
222 | - } | |
223 | - break; | |
224 | - case PARAMS_KEY: | |
225 | - startInd = this.bodyParams.indexOf(START_JSON_KEY, this.bodyParams.indexOf(key)); | |
226 | - finishInd = this.bodyParams.indexOf(FINISH_JSON_KEY, this.bodyParams.indexOf(key)); | |
227 | - } | |
228 | - if (startInd >= 0 && finishInd > 0) { | |
229 | - valueKey = this.bodyParams.substring(startInd + 1, finishInd); | |
230 | - } | |
231 | - } catch (Exception e) { | |
232 | - log.error("", new TimeoutException()); | |
233 | - } | |
234 | - /** | |
235 | - * ReplaceAll "\"" | |
236 | - */ | |
237 | - if (StringUtils.trimToNull(valueKey) != null) { | |
238 | - char[] chars = valueKey.toCharArray(); | |
239 | - for (int i = 0; i < chars.length; i++) { | |
240 | - if (chars[i] == 92 || chars[i] == 34) chars[i] = 32; | |
241 | - } | |
242 | - return key.equals(PARAMS_KEY) ? String.valueOf(chars) : String.valueOf(chars).replaceAll(" ", ""); | |
243 | - } | |
244 | - return null; | |
245 | - } | |
246 | - | |
247 | - private ConcurrentHashMap<String, Object> convertParamsToResourceId(ConcurrentHashMap<String, Object> params, | |
248 | - DefaultLwM2MTransportMsgHandler serviceImpl) { | |
249 | - Map<String, Object> paramsIdVer = new ConcurrentHashMap<>(); | |
250 | - LwM2mPath targetId = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(this.targetIdVer))); | |
251 | - if (targetId.isObjectInstance()) { | |
252 | - params.forEach((k, v) -> { | |
253 | - try { | |
254 | - int id = Integer.parseInt(k); | |
255 | - paramsIdVer.put(String.valueOf(id), v); | |
256 | - } catch (NumberFormatException e) { | |
257 | - String targetIdVer = serviceImpl.getPresentPathIntoProfile(sessionInfo, k); | |
258 | - if (targetIdVer != null) { | |
259 | - LwM2mPath lwM2mPath = new LwM2mPath(Objects.requireNonNull(convertPathFromIdVerToObjectId(targetIdVer))); | |
260 | - paramsIdVer.put(String.valueOf(lwM2mPath.getResourceId()), v); | |
261 | - } | |
262 | - /** WRITE_UPDATE*/ | |
263 | - else { | |
264 | - String rezId = this.getRezIdByResourceNameAndObjectInstanceId(k, serviceImpl); | |
265 | - if (rezId != null) { | |
266 | - paramsIdVer.put(rezId, v); | |
267 | - } | |
268 | - } | |
269 | - } | |
270 | - }); | |
271 | - } | |
272 | - return (ConcurrentHashMap<String, Object>) paramsIdVer; | |
273 | - } | |
274 | - | |
275 | - private String getRezIdByResourceNameAndObjectInstanceId(String resourceName, DefaultLwM2MTransportMsgHandler handler) { | |
276 | - LwM2mClient lwM2mClient = handler.clientContext.getClientBySessionInfo(this.sessionInfo); | |
277 | - return lwM2mClient != null ? | |
278 | - lwM2mClient.getRezIdByResourceNameAndObjectInstanceId(resourceName, this.targetIdVer, handler.config.getModelProvider()) : | |
279 | - null; | |
280 | - } | |
281 | -} |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mFwSwUpdate.java
deleted
100644 → 0
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.client; | |
17 | - | |
18 | -import lombok.Getter; | |
19 | -import lombok.Setter; | |
20 | -import lombok.extern.slf4j.Slf4j; | |
21 | -import org.apache.commons.lang3.StringUtils; | |
22 | -import org.eclipse.leshan.core.request.ContentFormat; | |
23 | -import org.eclipse.leshan.server.registration.Registration; | |
24 | -import org.thingsboard.server.common.data.ota.OtaPackageType; | |
25 | -import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; | |
26 | -import org.thingsboard.server.gen.transport.TransportProtos; | |
27 | -import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler; | |
28 | -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportRequest; | |
29 | -import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; | |
30 | - | |
31 | -import java.util.ArrayList; | |
32 | -import java.util.List; | |
33 | -import java.util.UUID; | |
34 | -import java.util.concurrent.CopyOnWriteArrayList; | |
35 | - | |
36 | -import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; | |
37 | -import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; | |
38 | -import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; | |
39 | -import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; | |
40 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; | |
41 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.FAILED; | |
42 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INITIATED; | |
43 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; | |
44 | -import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; | |
45 | -import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; | |
46 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; | |
47 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; | |
48 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; | |
49 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; | |
50 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_19_ID; | |
51 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_5_ID; | |
52 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_PACKAGE_URI_ID; | |
53 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; | |
54 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; | |
55 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE; | |
56 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_UPDATE_ID; | |
57 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_ERROR; | |
58 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LW2M_INFO; | |
59 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY; | |
60 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; | |
61 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL; | |
62 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.EXECUTE; | |
63 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.OBSERVE; | |
64 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LwM2mTypeOper.WRITE_REPLACE; | |
65 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_INSTALL_ID; | |
66 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_NAME_ID; | |
67 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_PACKAGE_ID; | |
68 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_RESULT_ID; | |
69 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UN_INSTALL_ID; | |
70 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE; | |
71 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_UPDATE_STATE_ID; | |
72 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.SW_VER_ID; | |
73 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer; | |
74 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.equalsFwSateToFirmwareUpdateStatus; | |
75 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.splitCamelCaseString; | |
76 | - | |
77 | -@Slf4j | |
78 | -public class LwM2mFwSwUpdate { | |
79 | - // 5/0/6 PkgName | |
80 | - // 9/0/0 PkgName | |
81 | - @Getter | |
82 | - @Setter | |
83 | - private volatile String currentTitle; | |
84 | - // 5/0/7 PkgVersion | |
85 | - // 9/0/1 PkgVersion | |
86 | - @Getter | |
87 | - @Setter | |
88 | - private volatile String currentVersion; | |
89 | - @Getter | |
90 | - @Setter | |
91 | - private volatile UUID currentId; | |
92 | - @Getter | |
93 | - @Setter | |
94 | - private volatile String stateUpdate; | |
95 | - @Getter | |
96 | - private String pathPackageId; | |
97 | - @Getter | |
98 | - private String pathStateId; | |
99 | - @Getter | |
100 | - private String pathResultId; | |
101 | - @Getter | |
102 | - private String pathNameId; | |
103 | - @Getter | |
104 | - private String pathVerId; | |
105 | - @Getter | |
106 | - private String pathInstallId; | |
107 | - @Getter | |
108 | - private String pathUnInstallId; | |
109 | - @Getter | |
110 | - private String wUpdate; | |
111 | - @Getter | |
112 | - @Setter | |
113 | - private volatile boolean infoFwSwUpdate = false; | |
114 | - private final OtaPackageType type; | |
115 | - @Getter | |
116 | - LwM2mClient lwM2MClient; | |
117 | - @Getter | |
118 | - @Setter | |
119 | - private final List<String> pendingInfoRequestsStart; | |
120 | - @Getter | |
121 | - @Setter | |
122 | - private volatile LwM2mClientRpcRequest rpcRequest; | |
123 | - @Getter | |
124 | - @Setter | |
125 | - private volatile int updateStrategy; | |
126 | - | |
127 | - public LwM2mFwSwUpdate(LwM2mClient lwM2MClient, OtaPackageType type, int updateStrategy) { | |
128 | - this.lwM2MClient = lwM2MClient; | |
129 | - this.pendingInfoRequestsStart = new CopyOnWriteArrayList<>(); | |
130 | - this.type = type; | |
131 | - this.stateUpdate = null; | |
132 | - this.updateStrategy = updateStrategy; | |
133 | - this.initPathId(); | |
134 | - } | |
135 | - | |
136 | - private void initPathId() { | |
137 | - if (FIRMWARE.equals(this.type)) { | |
138 | - this.pathPackageId = LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy ? | |
139 | - FW_PACKAGE_5_ID : LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy ? | |
140 | - FW_PACKAGE_URI_ID : FW_PACKAGE_19_ID; | |
141 | - this.pathStateId = FW_STATE_ID; | |
142 | - this.pathResultId = FW_RESULT_ID; | |
143 | - this.pathNameId = FW_NAME_ID; | |
144 | - this.pathVerId = FW_5_VER_ID; | |
145 | - this.pathInstallId = FW_UPDATE_ID; | |
146 | - this.wUpdate = FW_UPDATE; | |
147 | - } else if (SOFTWARE.equals(this.type)) { | |
148 | - this.pathPackageId = SW_PACKAGE_ID; | |
149 | - this.pathStateId = SW_UPDATE_STATE_ID; | |
150 | - this.pathResultId = SW_RESULT_ID; | |
151 | - this.pathNameId = SW_NAME_ID; | |
152 | - this.pathVerId = SW_VER_ID; | |
153 | - this.pathInstallId = SW_INSTALL_ID; | |
154 | - this.pathUnInstallId = SW_UN_INSTALL_ID; | |
155 | - this.wUpdate = SW_UPDATE; | |
156 | - } | |
157 | - } | |
158 | - | |
159 | - public void initReadValue(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request, String pathIdVer) { | |
160 | - if (pathIdVer != null) { | |
161 | - this.pendingInfoRequestsStart.remove(pathIdVer); | |
162 | - } | |
163 | - if (this.pendingInfoRequestsStart.size() == 0) { | |
164 | - this.infoFwSwUpdate = false; | |
165 | -// if (!FAILED.name().equals(this.stateUpdate)) { | |
166 | - boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart(handler) : | |
167 | - this.conditionalSwUpdateStart(handler); | |
168 | - if (conditionalStart) { | |
169 | - this.writeFwSwWare(handler, request); | |
170 | - } | |
171 | -// } | |
172 | - } | |
173 | - } | |
174 | - | |
175 | - /** | |
176 | - * Send FsSw to Lwm2mClient: | |
177 | - * before operation Write: fw_state = DOWNLOADING | |
178 | - */ | |
179 | - public void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { | |
180 | - if (this.currentId != null) { | |
181 | - this.stateUpdate = OtaPackageUpdateStatus.INITIATED.name(); | |
182 | - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null); | |
183 | - String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration()); | |
184 | - String fwMsg = String.format("%s: Start type operation %s paths: %s", LOG_LW2M_INFO, | |
185 | - LwM2mTransportUtil.LwM2mTypeOper.FW_UPDATE.name(), this.pathPackageId); | |
186 | - handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); | |
187 | - log.warn("8) Start firmware Update. Send save to: [{}] ver: [{}] path: [{}]", this.lwM2MClient.getDeviceName(), this.currentVersion, targetIdVer); | |
188 | - if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy) { | |
189 | - int chunkSize = 0; | |
190 | - int chunk = 0; | |
191 | - byte[] firmwareChunk = handler.otaPackageDataCache.get(this.currentId.toString(), chunkSize, chunk); | |
192 | - request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE, | |
193 | - firmwareChunk, handler.config.getTimeout(), this.rpcRequest); | |
194 | - } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL.code == this.updateStrategy) { | |
195 | - String apiFont = "coap://176.36.143.9:5685"; | |
196 | - String uri = apiFont + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + this.currentId.toString(); | |
197 | - log.warn("89) coapUri: [{}]", uri); | |
198 | - request.sendAllRequest(this.lwM2MClient, targetIdVer, WRITE_REPLACE, null, | |
199 | - uri, handler.config.getTimeout(), this.rpcRequest); | |
200 | - } else if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy) { | |
201 | - | |
202 | - } | |
203 | - } else { | |
204 | - String msgError = "FirmWareId is null."; | |
205 | - log.warn("6) [{}]", msgError); | |
206 | - if (this.rpcRequest != null) { | |
207 | - handler.sentRpcResponse(this.rpcRequest, CONTENT.name(), msgError, LOG_LW2M_ERROR); | |
208 | - } | |
209 | - log.error(msgError); | |
210 | - this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError); | |
211 | - } | |
212 | - } | |
213 | - | |
214 | - public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) { | |
215 | -// this.sendSateOnThingsBoard(handler); | |
216 | - String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.", | |
217 | - typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate); | |
218 | - if (LOG_LW2M_ERROR.equals(typeInfo)) { | |
219 | - msg = String.format("%s Error: %s", msg, msgError); | |
220 | - } | |
221 | - handler.sendLogsToThingsboard(lwM2MClient, msg); | |
222 | - } | |
223 | - | |
224 | - | |
225 | - /** | |
226 | - * After inspection Update Result | |
227 | - * fw_state/sw_state = UPDATING | |
228 | - * send execute | |
229 | - */ | |
230 | - public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { | |
231 | - this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); | |
232 | - request.sendAllRequest(this.lwM2MClient, this.pathInstallId, EXECUTE, null, 0, this.rpcRequest); | |
233 | - } | |
234 | - | |
235 | - /** | |
236 | - * Firmware start: Check if the version has changed and launch a new update. | |
237 | - * -ObjectId 5, Binary or ObjectId 5, URI | |
238 | - * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. | |
239 | - * - We launch the update regardless of the state of the firmware and its version. | |
240 | - * -- If the result of the update - errors (more than 1) - This means that the previous. the update failed. | |
241 | - * * ObjectId 5, Binary | |
242 | - * -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed. | |
243 | - * Compare current versions by equals. | |
244 | - * * ObjectId 5, URI | |
245 | - * -- If the result of the update is not errors (equal to 1 or 0) and ver in Object 5 is not empty - it means that the previous update has passed. | |
246 | - * Compare current versions by contains. | |
247 | - */ | |
248 | - private boolean conditionalFwUpdateStart(DefaultLwM2MTransportMsgHandler handler) { | |
249 | - Long updateResultFw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
250 | - String ver5 = (String) this.lwM2MClient.getResourceValue(null, this.pathVerId); | |
251 | - String pathName = (String) this.lwM2MClient.getResourceValue(null, this.pathNameId); | |
252 | - String ver3 = (String) this.lwM2MClient.getResourceValue(null, FW_3_VER_ID); | |
253 | - // #1/#2 | |
254 | - String fwMsg = null; | |
255 | - if ((this.currentVersion != null && ( | |
256 | - ver5 != null && ver5.equals(this.currentVersion) || | |
257 | - ver3 != null && ver3.contains(this.currentVersion) | |
258 | - )) || | |
259 | - (this.currentTitle != null && pathName != null && this.currentTitle.equals(pathName))) { | |
260 | - fwMsg = String.format("%s: The update was interrupted. The device has the same version: %s.", LOG_LW2M_ERROR, | |
261 | - this.currentVersion); | |
262 | - } | |
263 | - else if (updateResultFw != null && updateResultFw > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { | |
264 | - fwMsg = String.format("%s: The update was interrupted. The device has the status UpdateResult: error (%d).", LOG_LW2M_ERROR, | |
265 | - updateResultFw); | |
266 | - } | |
267 | - if (fwMsg != null) { | |
268 | - handler.sendLogsToThingsboard(fwMsg, lwM2MClient.getRegistration().getId()); | |
269 | - return false; | |
270 | - } | |
271 | - else { | |
272 | - return true; | |
273 | - } | |
274 | - } | |
275 | - | |
276 | - | |
277 | - /** | |
278 | - * Before operation Execute inspection Update Result : | |
279 | - * 0 - Initial value | |
280 | - */ | |
281 | - public boolean conditionalFwExecuteStart() { | |
282 | - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
283 | - return LwM2mTransportUtil.UpdateResultFw.INITIAL.code == updateResult; | |
284 | - } | |
285 | - | |
286 | - /** | |
287 | - * After operation Execute success inspection Update Result : | |
288 | - * 1 - "Firmware updated successfully" | |
289 | - */ | |
290 | - public boolean conditionalFwExecuteAfterSuccess() { | |
291 | - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
292 | - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult; | |
293 | - } | |
294 | - | |
295 | - /** | |
296 | - * After operation Execute success inspection Update Result : | |
297 | - * > 1 error: "Firmware updated successfully" | |
298 | - */ | |
299 | - public boolean conditionalFwExecuteAfterError() { | |
300 | - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
301 | - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult; | |
302 | - } | |
303 | - | |
304 | - /** | |
305 | - * Software start | |
306 | - * - If Update Result -errors (equal or more than 50) - This means that the previous. the update failed. | |
307 | - * * - We launch the update regardless of the state of the firmware and its version. | |
308 | - * - If Update Result is not errors (less than 50) and ver is not empty - This means that before. the update has passed. | |
309 | - * - If Update Result is not errors and ver is empty - This means that there was no update yet or before. UnInstall update | |
310 | - * - If Update Result is not errors and ver is not empty - This means that before unInstall update | |
311 | - * * - Check if the version has changed and launch a new update. | |
312 | - */ | |
313 | - private boolean conditionalSwUpdateStart(DefaultLwM2MTransportMsgHandler handler) { | |
314 | - Long updateResultSw = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
315 | - // #1/#2 | |
316 | - return updateResultSw >= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code || | |
317 | - ( | |
318 | - (updateResultSw <= LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code | |
319 | - ) && | |
320 | - ( | |
321 | - (this.currentVersion != null && !this.currentVersion.equals(this.lwM2MClient.getResourceValue(null, this.pathVerId))) || | |
322 | - (this.currentTitle != null && !this.currentTitle.equals(this.lwM2MClient.getResourceValue(null, this.pathNameId))) | |
323 | - ) | |
324 | - ); | |
325 | - } | |
326 | - | |
327 | - /** | |
328 | - * Before operation Execute inspection Update Result : | |
329 | - * 3 - Successfully Downloaded and package integrity verified | |
330 | - */ | |
331 | - public boolean conditionalSwUpdateExecute() { | |
332 | - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
333 | - return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_DOWNLOADED_VERIFIED.code == updateResult; | |
334 | - } | |
335 | - | |
336 | - /** | |
337 | - * After finish operation Execute (success): | |
338 | - * -- inspection Update Result: | |
339 | - * ---- FW если Update Result == 1 ("Firmware updated successfully") или SW если Update Result == 2 ("Software successfully installed.") | |
340 | - * -- fw_state/sw_state = UPDATED | |
341 | - * <p> | |
342 | - * After finish operation Execute (error): | |
343 | - * -- inspection updateResult and send to thingsboard info about error | |
344 | - * --- send to telemetry ( key - this is name Update Result in model) ( | |
345 | - * -- fw_state/sw_state = FAILED | |
346 | - */ | |
347 | - public void finishFwSwUpdate(DefaultLwM2MTransportMsgHandler handler, boolean success) { | |
348 | - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
349 | - String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type : | |
350 | - LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type; | |
351 | - String key = splitCamelCaseString((String) this.lwM2MClient.getResourceNameByRezId(null, this.pathResultId)); | |
352 | - if (success) { | |
353 | - this.stateUpdate = OtaPackageUpdateStatus.UPDATED.name(); | |
354 | - this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null); | |
355 | - } else { | |
356 | - this.stateUpdate = OtaPackageUpdateStatus.FAILED.name(); | |
357 | - this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, value); | |
358 | - } | |
359 | - handler.helper.sendParametersOnThingsboardTelemetry( | |
360 | - handler.helper.getKvStringtoThingsboard(key, value), this.lwM2MClient.getSession()); | |
361 | - } | |
362 | - | |
363 | - /** | |
364 | - * After operation Execute success inspection Update Result : | |
365 | - * 2 - "Software successfully installed." | |
366 | - */ | |
367 | - public boolean conditionalSwExecuteAfterSuccess() { | |
368 | - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
369 | - return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_INSTALLED.code == updateResult; | |
370 | - } | |
371 | - | |
372 | - /** | |
373 | - * After operation Execute success inspection Update Result : | |
374 | - * >= 50 - error "NOT_ENOUGH_STORAGE" | |
375 | - */ | |
376 | - public boolean conditionalSwExecuteAfterError() { | |
377 | - Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId); | |
378 | - return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult; | |
379 | - } | |
380 | - | |
381 | - private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) { | |
382 | - request.sendAllRequest(lwM2MClient, | |
383 | - convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE, | |
384 | - null, null, 0, null); | |
385 | - request.sendAllRequest(lwM2MClient, | |
386 | - convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE, | |
387 | - null, null, 0, null); | |
388 | - } | |
389 | - | |
390 | - public void sendSateOnThingsBoard(DefaultLwM2MTransportMsgHandler handler) { | |
391 | - if (StringUtils.trimToNull(this.stateUpdate) != null) { | |
392 | - List<TransportProtos.KeyValueProto> result = new ArrayList<>(); | |
393 | - TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(this.type, STATE)); | |
394 | - kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(stateUpdate); | |
395 | - result.add(kvProto.build()); | |
396 | - handler.helper.sendParametersOnThingsboardTelemetry(result, | |
397 | - handler.getSessionInfoOrCloseSession(this.lwM2MClient.getRegistration())); | |
398 | - } | |
399 | - } | |
400 | - | |
401 | - public void sendReadObserveInfo(LwM2mTransportRequest request) { | |
402 | - this.infoFwSwUpdate = true; | |
403 | - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( | |
404 | - this.pathStateId, this.lwM2MClient.getRegistration())); | |
405 | - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( | |
406 | - this.pathResultId, this.lwM2MClient.getRegistration())); | |
407 | - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( | |
408 | - FW_3_VER_ID, this.lwM2MClient.getRegistration())); | |
409 | - if (LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY.code == this.updateStrategy || | |
410 | - LwM2mTransportUtil.LwM2MFirmwareUpdateStrategy.OBJ_19_BINARY.code == this.updateStrategy || | |
411 | - SOFTWARE.equals(this.type)) { | |
412 | - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( | |
413 | - this.pathVerId, this.lwM2MClient.getRegistration())); | |
414 | - this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer( | |
415 | - this.pathNameId, this.lwM2MClient.getRegistration())); | |
416 | - } | |
417 | - this.pendingInfoRequestsStart.forEach(pathIdVer -> { | |
418 | - request.sendAllRequest(this.lwM2MClient, pathIdVer, OBSERVE, null, 0, this.rpcRequest); | |
419 | - }); | |
420 | - | |
421 | - } | |
422 | - | |
423 | - /** | |
424 | - * Before operation Execute (FwUpdate) inspection Update Result : | |
425 | - * - after finished operation Write result: success (FwUpdate): fw_state = DOWNLOADED | |
426 | - * - before start operation Execute (FwUpdate) Update Result = 0 - Initial value | |
427 | - * - start Execute (FwUpdate) | |
428 | - * After finished operation Execute (FwUpdate) inspection Update Result : | |
429 | - * - after start operation Execute (FwUpdate): fw_state = UPDATING | |
430 | - * - after success finished operation Execute (FwUpdate) Update Result == 1 ("Firmware updated successfully") | |
431 | - * - finished operation Execute (FwUpdate) | |
432 | - */ | |
433 | - public void updateStateOta(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request, | |
434 | - Registration registration, String path, int value) { | |
435 | - if (OBJ_5_BINARY.code == this.getUpdateStrategy()) { | |
436 | - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { | |
437 | - if (DOWNLOADED.name().equals(this.getStateUpdate()) | |
438 | - && this.conditionalFwExecuteStart()) { | |
439 | - this.executeFwSwWare(handler, request); | |
440 | - } else if (UPDATING.name().equals(this.getStateUpdate()) | |
441 | - && this.conditionalFwExecuteAfterSuccess()) { | |
442 | - this.finishFwSwUpdate(handler, true); | |
443 | - } else if (UPDATING.name().equals(this.getStateUpdate()) | |
444 | - && this.conditionalFwExecuteAfterError()) { | |
445 | - this.finishFwSwUpdate(handler, false); | |
446 | - } | |
447 | - } | |
448 | - } else if (OBJ_5_TEMP_URL.code == this.getUpdateStrategy()) { | |
449 | - if (this.currentId != null && (convertPathFromObjectIdToIdVer(FW_STATE_ID, registration).equals(path))) { | |
450 | - String state = equalsFwSateToFirmwareUpdateStatus(LwM2mTransportUtil.StateFw.fromStateFwByCode(value)).name(); | |
451 | - if (StringUtils.isNotEmpty(state) && !FAILED.name().equals(this.stateUpdate) && !state.equals(this.stateUpdate)) { | |
452 | - this.stateUpdate = state; | |
453 | - this.sendSateOnThingsBoard(handler); | |
454 | - } | |
455 | - if (value == LwM2mTransportUtil.StateFw.DOWNLOADED.code) { | |
456 | - this.executeFwSwWare(handler, request); | |
457 | - } | |
458 | - handler.firmwareUpdateState.put(lwM2MClient.getEndpoint(), value); | |
459 | - } | |
460 | - if ((convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) { | |
461 | - if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.INITIAL.code) { | |
462 | - this.setStateUpdate(INITIATED.name()); | |
463 | - } else if (this.currentId != null && value == LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { | |
464 | - this.setStateUpdate(UPDATED.name()); | |
465 | - } else if (value > LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code) { | |
466 | - this.setStateUpdate(FAILED.name()); | |
467 | - } | |
468 | - this.sendSateOnThingsBoard(handler); | |
469 | - } | |
470 | - } else if (OBJ_19_BINARY.code == this.getUpdateStrategy()) { | |
471 | - | |
472 | - } | |
473 | - } | |
474 | -} |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ParametersAnalyzeResult.java
renamed from
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/client/ResultsAnalyzerParameters.java
... | ... | @@ -21,11 +21,11 @@ import java.util.Set; |
21 | 21 | import java.util.concurrent.ConcurrentHashMap; |
22 | 22 | |
23 | 23 | @Data |
24 | -public class ResultsAnalyzerParameters { | |
24 | +public class ParametersAnalyzeResult { | |
25 | 25 | Set<String> pathPostParametersAdd; |
26 | 26 | Set<String> pathPostParametersDel; |
27 | 27 | |
28 | - public ResultsAnalyzerParameters() { | |
28 | + public ParametersAnalyzeResult() { | |
29 | 29 | this.pathPostParametersAdd = ConcurrentHashMap.newKeySet(); |
30 | 30 | this.pathPostParametersDel = ConcurrentHashMap.newKeySet(); |
31 | 31 | } | ... | ... |
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.common; | |
17 | + | |
18 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
19 | + | |
20 | +import javax.annotation.PreDestroy; | |
21 | +import java.util.concurrent.ExecutorService; | |
22 | + | |
23 | +public abstract class LwM2MExecutorAwareService { | |
24 | + | |
25 | + protected ExecutorService executor; | |
26 | + | |
27 | + protected abstract int getExecutorSize(); | |
28 | + | |
29 | + protected abstract String getExecutorName(); | |
30 | + | |
31 | + protected void init() { | |
32 | + this.executor = ThingsBoardExecutors.newWorkStealingPool(getExecutorSize(), getExecutorName()); | |
33 | + } | |
34 | + | |
35 | + public void destroy() { | |
36 | + if (executor != null) { | |
37 | + executor.shutdownNow(); | |
38 | + } | |
39 | + } | |
40 | + | |
41 | +} | ... | ... |
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 lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
21 | + | |
22 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; | |
23 | + | |
24 | +@Slf4j | |
25 | +public abstract class AbstractTbLwM2MRequestCallback<R, T> implements DownlinkRequestCallback<R, T> { | |
26 | + | |
27 | + protected final LwM2MTelemetryLogService logService; | |
28 | + protected final LwM2mClient client; | |
29 | + | |
30 | + protected AbstractTbLwM2MRequestCallback(LwM2MTelemetryLogService logService, LwM2mClient client) { | |
31 | + this.logService = logService; | |
32 | + this.client = client; | |
33 | + } | |
34 | + | |
35 | + @Override | |
36 | + public void onValidationError(String params, String msg) { | |
37 | + log.trace("[{}] Request [{}] validation failed. Reason: {}", client.getEndpoint(), params, msg); | |
38 | + logService.log(client, String.format("[%s]: Request [%s] validation failed. Reason: %s", LOG_LWM2M_WARN, params, msg)); | |
39 | + } | |
40 | + | |
41 | + @Override | |
42 | + public void onError(String params, Exception e) { | |
43 | + log.trace("[{}] Request [{}] processing failed", client.getEndpoint(), params, e); | |
44 | + logService.log(client, String.format("[%s]: Request [%s] processing failed. Reason: %s", LOG_LWM2M_WARN, params, e)); | |
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; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | + | |
20 | +public abstract class AbstractTbLwM2MTargetedDownlinkRequest<T> implements TbLwM2MDownlinkRequest<T>, HasVersionedId { | |
21 | + | |
22 | + @Getter | |
23 | + private final String versionedId; | |
24 | + @Getter | |
25 | + private final long timeout; | |
26 | + | |
27 | + public AbstractTbLwM2MTargetedDownlinkRequest(String versionedId, long timeout) { | |
28 | + this.versionedId = versionedId; | |
29 | + this.timeout = timeout; | |
30 | + } | |
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.server.downlink; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.eclipse.leshan.core.Link; | |
21 | +import org.eclipse.leshan.core.attributes.Attribute; | |
22 | +import org.eclipse.leshan.core.attributes.AttributeSet; | |
23 | +import org.eclipse.leshan.core.model.ResourceModel; | |
24 | +import org.eclipse.leshan.core.node.LwM2mPath; | |
25 | +import org.eclipse.leshan.core.node.LwM2mResource; | |
26 | +import org.eclipse.leshan.core.node.ObjectLink; | |
27 | +import org.eclipse.leshan.core.node.codec.CodecException; | |
28 | +import org.eclipse.leshan.core.observation.Observation; | |
29 | +import org.eclipse.leshan.core.request.ContentFormat; | |
30 | +import org.eclipse.leshan.core.request.DeleteRequest; | |
31 | +import org.eclipse.leshan.core.request.DiscoverRequest; | |
32 | +import org.eclipse.leshan.core.request.ExecuteRequest; | |
33 | +import org.eclipse.leshan.core.request.ObserveRequest; | |
34 | +import org.eclipse.leshan.core.request.ReadRequest; | |
35 | +import org.eclipse.leshan.core.request.SimpleDownlinkRequest; | |
36 | +import org.eclipse.leshan.core.request.WriteAttributesRequest; | |
37 | +import org.eclipse.leshan.core.request.WriteRequest; | |
38 | +import org.eclipse.leshan.core.response.DeleteResponse; | |
39 | +import org.eclipse.leshan.core.response.DiscoverResponse; | |
40 | +import org.eclipse.leshan.core.response.ExecuteResponse; | |
41 | +import org.eclipse.leshan.core.response.LwM2mResponse; | |
42 | +import org.eclipse.leshan.core.response.ObserveResponse; | |
43 | +import org.eclipse.leshan.core.response.ReadResponse; | |
44 | +import org.eclipse.leshan.core.response.WriteAttributesResponse; | |
45 | +import org.eclipse.leshan.core.response.WriteResponse; | |
46 | +import org.eclipse.leshan.core.util.Hex; | |
47 | +import org.eclipse.leshan.server.registration.Registration; | |
48 | +import org.springframework.stereotype.Service; | |
49 | +import org.thingsboard.common.util.JacksonUtil; | |
50 | +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; | |
51 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | |
52 | +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | |
53 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; | |
54 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
55 | +import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; | |
56 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
57 | +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; | |
58 | + | |
59 | +import javax.annotation.PostConstruct; | |
60 | +import javax.annotation.PreDestroy; | |
61 | +import java.util.Arrays; | |
62 | +import java.util.Collection; | |
63 | +import java.util.Date; | |
64 | +import java.util.LinkedList; | |
65 | +import java.util.List; | |
66 | +import java.util.Set; | |
67 | +import java.util.function.Function; | |
68 | +import java.util.function.Predicate; | |
69 | +import java.util.stream.Collectors; | |
70 | + | |
71 | +import static org.eclipse.leshan.core.attributes.Attribute.GREATER_THAN; | |
72 | +import static org.eclipse.leshan.core.attributes.Attribute.LESSER_THAN; | |
73 | +import static org.eclipse.leshan.core.attributes.Attribute.MAXIMUM_PERIOD; | |
74 | +import static org.eclipse.leshan.core.attributes.Attribute.MINIMUM_PERIOD; | |
75 | +import static org.eclipse.leshan.core.attributes.Attribute.STEP; | |
76 | + | |
77 | +@Slf4j | |
78 | +@Service | |
79 | +@TbLwM2mTransportComponent | |
80 | +@RequiredArgsConstructor | |
81 | +public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService implements LwM2mDownlinkMsgHandler { | |
82 | + | |
83 | + public LwM2mValueConverterImpl converter; | |
84 | + | |
85 | + private final LwM2mTransportContext context; | |
86 | + private final LwM2MTransportServerConfig config; | |
87 | + private final LwM2MTelemetryLogService logService; | |
88 | + | |
89 | + @PostConstruct | |
90 | + public void init() { | |
91 | + super.init(); | |
92 | + this.converter = LwM2mValueConverterImpl.getInstance(); | |
93 | + } | |
94 | + | |
95 | + @PreDestroy | |
96 | + public void destroy() { | |
97 | + super.destroy(); | |
98 | + } | |
99 | + | |
100 | + @Override | |
101 | + protected int getExecutorSize() { | |
102 | + return config.getDownlinkPoolSize(); | |
103 | + } | |
104 | + | |
105 | + @Override | |
106 | + protected String getExecutorName() { | |
107 | + return "LwM2M Downlink"; | |
108 | + } | |
109 | + | |
110 | + @Override | |
111 | + public void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback<ReadRequest, ReadResponse> callback) { | |
112 | + validateVersionedId(client, request); | |
113 | + ReadRequest downlink = new ReadRequest(getContentFormat(client, request), request.getObjectId()); | |
114 | + sendRequest(client, downlink, request.getTimeout(), callback); | |
115 | + } | |
116 | + | |
117 | + @Override | |
118 | + public void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback<ObserveRequest, ObserveResponse> callback) { | |
119 | + validateVersionedId(client, request); | |
120 | + LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); | |
121 | + Set<Observation> observations = context.getServer().getObservationService().getObservations(client.getRegistration()); | |
122 | + if (observations.stream().noneMatch(observation -> observation.getPath().equals(resultIds))) { | |
123 | + ObserveRequest downlink; | |
124 | + ContentFormat contentFormat = getContentFormat(client, request); | |
125 | + if (resultIds.isResource()) { | |
126 | + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); | |
127 | + } else if (resultIds.isObjectInstance()) { | |
128 | + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); | |
129 | + } else { | |
130 | + downlink = new ObserveRequest(contentFormat, resultIds.getObjectId()); | |
131 | + } | |
132 | + log.info("[{}] Send observation: {}.", client.getEndpoint(), request.getVersionedId()); | |
133 | + sendRequest(client, downlink, request.getTimeout(), callback); | |
134 | + } else { | |
135 | + throw new IllegalArgumentException("Observation is already registered!"); | |
136 | + } | |
137 | + } | |
138 | + | |
139 | + @Override | |
140 | + public void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback<TbLwM2MObserveAllRequest, Set<String>> callback) { | |
141 | + Set<Observation> observations = context.getServer().getObservationService().getObservations(client.getRegistration()); | |
142 | + Set<String> paths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); | |
143 | + callback.onSuccess(request, paths); | |
144 | + } | |
145 | + | |
146 | + @Override | |
147 | + public void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback<TbLwM2MDiscoverAllRequest, List<Link>> callback) { | |
148 | + callback.onSuccess(request, Arrays.asList(client.getRegistration().getSortedObjectLinks())); | |
149 | + } | |
150 | + | |
151 | + @Override | |
152 | + public void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback<ExecuteRequest, ExecuteResponse> callback) { | |
153 | + ResourceModel resourceModelExecute = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); | |
154 | + if (resourceModelExecute != null) { | |
155 | + ExecuteRequest downlink; | |
156 | + if (request.getParams() != null && !resourceModelExecute.multiple) { | |
157 | + downlink = new ExecuteRequest(request.getVersionedId(), (String) this.converter.convertValue(request.getParams(), resourceModelExecute.type, ResourceModel.Type.STRING, new LwM2mPath(request.getObjectId()))); | |
158 | + } else { | |
159 | + downlink = new ExecuteRequest(request.getVersionedId()); | |
160 | + } | |
161 | + sendRequest(client, downlink, request.getTimeout(), callback); | |
162 | + } | |
163 | + } | |
164 | + | |
165 | + @Override | |
166 | + public void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback<DeleteRequest, DeleteResponse> callback) { | |
167 | + sendRequest(client, new DeleteRequest(request.getObjectId()), request.getTimeout(), callback); | |
168 | + } | |
169 | + | |
170 | + @Override | |
171 | + public void sendCancelObserveRequest(LwM2mClient client, TbLwM2MCancelObserveRequest request, DownlinkRequestCallback<TbLwM2MCancelObserveRequest, Integer> callback) { | |
172 | + int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration(), request.getObjectId()); | |
173 | + callback.onSuccess(request, observeCancelCnt); | |
174 | + } | |
175 | + | |
176 | + @Override | |
177 | + public void sendCancelAllRequest(LwM2mClient client, TbLwM2MCancelAllRequest request, DownlinkRequestCallback<TbLwM2MCancelAllRequest, Integer> callback) { | |
178 | + int observeCancelCnt = context.getServer().getObservationService().cancelObservations(client.getRegistration()); | |
179 | + callback.onSuccess(request, observeCancelCnt); | |
180 | + } | |
181 | + | |
182 | + @Override | |
183 | + public void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback<DiscoverRequest, DiscoverResponse> callback) { | |
184 | + validateVersionedId(client, request); | |
185 | + sendRequest(client, new DiscoverRequest(request.getObjectId()), request.getTimeout(), callback); | |
186 | + } | |
187 | + | |
188 | + @Override | |
189 | + public void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback<WriteAttributesRequest, WriteAttributesResponse> callback) { | |
190 | + validateVersionedId(client, request); | |
191 | + if (request.getAttributes() == null) { | |
192 | + throw new IllegalArgumentException("Attributes to write are not specified!"); | |
193 | + } | |
194 | + ObjectAttributes params = request.getAttributes(); | |
195 | + List<Attribute> attributes = new LinkedList<>(); | |
196 | +// Dimension and Object version are read only attributes. | |
197 | +// addAttribute(attributes, DIMENSION, params.getDim(), dim -> dim >= 0 && dim <= 255); | |
198 | +// addAttribute(attributes, OBJECT_VERSION, params.getVer(), StringUtils::isNotEmpty, Function.identity()); | |
199 | + addAttribute(attributes, MAXIMUM_PERIOD, params.getPmax()); | |
200 | + addAttribute(attributes, MINIMUM_PERIOD, params.getPmin()); | |
201 | + addAttribute(attributes, GREATER_THAN, params.getGt()); | |
202 | + addAttribute(attributes, LESSER_THAN, params.getLt()); | |
203 | + addAttribute(attributes, STEP, params.getSt()); | |
204 | + AttributeSet attributeSet = new AttributeSet(attributes); | |
205 | + sendRequest(client, new WriteAttributesRequest(request.getObjectId(), attributeSet), request.getTimeout(), callback); | |
206 | + } | |
207 | + | |
208 | + @Override | |
209 | + public void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback) { | |
210 | + ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); | |
211 | + if (resourceModelWrite != null) { | |
212 | + ContentFormat contentFormat = convertResourceModelTypeToContentFormat(client, resourceModelWrite.type); | |
213 | + try { | |
214 | + LwM2mPath path = new LwM2mPath(request.getObjectId()); | |
215 | + WriteRequest downlink = this.getWriteRequestSingleResource(resourceModelWrite.type, contentFormat, | |
216 | + path.getObjectId(), path.getObjectInstanceId(), path.getResourceId(), request.getValue()); | |
217 | + sendRequest(client, downlink, request.getTimeout(), callback); | |
218 | + } catch (Exception e) { | |
219 | + callback.onError(JacksonUtil.toString(request), e); | |
220 | + } | |
221 | + } else { | |
222 | + callback.onValidationError(JacksonUtil.toString(request), "Resource " + request.getVersionedId() + " is not configured in the device profile!"); | |
223 | + } | |
224 | + } | |
225 | + | |
226 | + @Override | |
227 | + public void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback) { | |
228 | + LwM2mPath resultIds = new LwM2mPath(request.getObjectId()); | |
229 | + if (resultIds.isResource()) { | |
230 | + /* | |
231 | + * send request: path = '/3/0' node == wM2mObjectInstance | |
232 | + * with params == "\"resources\": {15: resource:{id:15. value:'+01'...}} | |
233 | + **/ | |
234 | + Collection<LwM2mResource> resources = client.getNewResourceForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); | |
235 | + ResourceModel resourceModelWrite = client.getResourceModel(request.getVersionedId(), this.config.getModelProvider()); | |
236 | + ContentFormat contentFormat = request.getObjectContentFormat() != null ? request.getObjectContentFormat() : convertResourceModelTypeToContentFormat(client, resourceModelWrite.type); | |
237 | + WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), | |
238 | + resultIds.getObjectInstanceId(), resources); | |
239 | + sendRequest(client, downlink, request.getTimeout(), callback); | |
240 | + } else if (resultIds.isObjectInstance()) { | |
241 | + /* | |
242 | + * params = "{\"id\":0,\"resources\":[{\"id\":14,\"value\":\"+5\"},{\"id\":15,\"value\":\"+9\"}]}" | |
243 | + * int rscId = resultIds.getObjectInstanceId(); | |
244 | + * contentFormat – Format of the payload (TLV or JSON). | |
245 | + */ | |
246 | + Collection<LwM2mResource> resources = client.getNewResourcesForInstance(request.getVersionedId(), request.getValue(), this.config.getModelProvider(), this.converter); | |
247 | + if (resources.size() > 0) { | |
248 | + ContentFormat contentFormat = request.getObjectContentFormat() != null ? request.getObjectContentFormat() : client.getDefaultContentFormat(); | |
249 | + WriteRequest downlink = new WriteRequest(WriteRequest.Mode.UPDATE, contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resources); | |
250 | + sendRequest(client, downlink, request.getTimeout(), callback); | |
251 | + } else { | |
252 | + callback.onValidationError(JacksonUtil.toString(request), "No resources to update!"); | |
253 | + } | |
254 | + } else { | |
255 | + callback.onValidationError(JacksonUtil.toString(request), "Update of the root level object is not supported yet!"); | |
256 | + } | |
257 | + } | |
258 | + | |
259 | + private <R extends SimpleDownlinkRequest<T>, T extends LwM2mResponse> void sendRequest(LwM2mClient client, R request, long timeoutInMs, DownlinkRequestCallback<R, T> callback) { | |
260 | + Registration registration = client.getRegistration(); | |
261 | + try { | |
262 | + logService.log(client, String.format("[%s][%s] Sending request: %s to %s", registration.getId(), registration.getSocketAddress(), request.getClass().getSimpleName(), request.getPath())); | |
263 | + context.getServer().send(registration, request, timeoutInMs, response -> { | |
264 | + executor.submit(() -> { | |
265 | + try { | |
266 | + callback.onSuccess(request, response); | |
267 | + } catch (Exception e) { | |
268 | + log.error("[{}] failed to process successful response [{}] ", registration.getEndpoint(), response, e); | |
269 | + } | |
270 | + }); | |
271 | + }, e -> { | |
272 | + executor.submit(() -> { | |
273 | + callback.onError(JacksonUtil.toString(request), e); | |
274 | + }); | |
275 | + }); | |
276 | + } catch (Exception e) { | |
277 | + callback.onError(JacksonUtil.toString(request), e); | |
278 | + } | |
279 | + } | |
280 | + | |
281 | + private WriteRequest getWriteRequestSingleResource(ResourceModel.Type type, ContentFormat contentFormat, int objectId, int instanceId, int resourceId, Object value) { | |
282 | + switch (type) { | |
283 | + case STRING: // String | |
284 | + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, value.toString()); | |
285 | + case INTEGER: // Long | |
286 | + final long valueInt = Integer.toUnsignedLong(Integer.parseInt(value.toString())); | |
287 | + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueInt); | |
288 | + case OBJLNK: // ObjectLink | |
289 | + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, ObjectLink.fromPath(value.toString())); | |
290 | + case BOOLEAN: // Boolean | |
291 | + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, Boolean.parseBoolean(value.toString())); | |
292 | + case FLOAT: // Double | |
293 | + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, Double.parseDouble(value.toString())); | |
294 | + case TIME: // Date | |
295 | + Date date = new Date(Long.decode(value.toString())); | |
296 | + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); | |
297 | + case OPAQUE: // byte[] value, base64 | |
298 | + byte[] valueRequest; | |
299 | + if (value instanceof byte[]) { | |
300 | + valueRequest = (byte[]) value; | |
301 | + } else { | |
302 | + valueRequest = Hex.decodeHex(value.toString().toCharArray()); | |
303 | + } | |
304 | + return new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest); | |
305 | + default: | |
306 | + throw new IllegalArgumentException("Not supported type:" + type.name()); | |
307 | + } | |
308 | + } | |
309 | + | |
310 | + 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 | + } | |
314 | + if (request.getObjectId() == null) { | |
315 | + throw new IllegalArgumentException("Specified object id is null!"); | |
316 | + } | |
317 | + } | |
318 | + | |
319 | + private static <T> void addAttribute(List<Attribute> attributes, String attributeName, T value) { | |
320 | + addAttribute(attributes, attributeName, value, null, null); | |
321 | + } | |
322 | + | |
323 | + private static <T> void addAttribute(List<Attribute> attributes, String attributeName, T value, Function<T, ?> converter) { | |
324 | + addAttribute(attributes, attributeName, value, null, converter); | |
325 | + } | |
326 | + | |
327 | + private static <T> void addAttribute(List<Attribute> attributes, String attributeName, T value, Predicate<T> filter, Function<T, ?> converter) { | |
328 | + if (value != null && ((filter == null) || filter.test(value))) { | |
329 | + attributes.add(new Attribute(attributeName, converter != null ? converter.apply(value) : value)); | |
330 | + } | |
331 | + } | |
332 | + | |
333 | + private static ContentFormat convertResourceModelTypeToContentFormat(LwM2mClient client, ResourceModel.Type type) { | |
334 | + switch (type) { | |
335 | + case BOOLEAN: | |
336 | + case STRING: | |
337 | + case TIME: | |
338 | + case INTEGER: | |
339 | + case FLOAT: | |
340 | + return client.getDefaultContentFormat(); | |
341 | + case OPAQUE: | |
342 | + return ContentFormat.OPAQUE; | |
343 | + case OBJLNK: | |
344 | + return ContentFormat.LINK; | |
345 | + default: | |
346 | + } | |
347 | + throw new CodecException("Invalid ResourceModel_Type for %s ContentFormat.", type); | |
348 | + } | |
349 | + | |
350 | + private static ContentFormat getContentFormat(LwM2mClient client, HasContentFormat request) { | |
351 | + return request.getContentFormat() != null ? request.getContentFormat() : client.getDefaultContentFormat(); | |
352 | + } | |
353 | +} | ... | ... |
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 | +public interface DownlinkRequestCallback<R, T> { | |
19 | + | |
20 | + void onSuccess(R request, T response); | |
21 | + | |
22 | + void onValidationError(String params, String msg); | |
23 | + | |
24 | + void onError(String params, Exception e); | |
25 | + | |
26 | + static <R, T> DownlinkRequestCallback<R, T> doNothing() { | |
27 | + return new DownlinkRequestCallback<>() { | |
28 | + | |
29 | + @Override | |
30 | + public void onSuccess(R request, T response) { | |
31 | + | |
32 | + } | |
33 | + | |
34 | + @Override | |
35 | + public void onValidationError(String params, String msg) { | |
36 | + | |
37 | + } | |
38 | + | |
39 | + @Override | |
40 | + public void onError(String params, Exception e) { | |
41 | + | |
42 | + } | |
43 | + }; | |
44 | + } | |
45 | +} | ... | ... |
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.eclipse.leshan.core.request.ContentFormat; | |
19 | + | |
20 | +public interface HasContentFormat { | |
21 | + | |
22 | + ContentFormat getContentFormat(); | |
23 | +} | ... | ... |
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 | +public interface HasVersionedId { | |
21 | + | |
22 | + String getVersionedId(); | |
23 | + | |
24 | + default String getObjectId(){ | |
25 | + return LwM2mTransportUtil.fromVersionedIdToObjectId(getVersionedId()); | |
26 | + } | |
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.downlink; | |
17 | + | |
18 | +import org.eclipse.leshan.core.Link; | |
19 | +import org.eclipse.leshan.core.request.DeleteRequest; | |
20 | +import org.eclipse.leshan.core.request.DiscoverRequest; | |
21 | +import org.eclipse.leshan.core.request.ExecuteRequest; | |
22 | +import org.eclipse.leshan.core.request.ObserveRequest; | |
23 | +import org.eclipse.leshan.core.request.ReadRequest; | |
24 | +import org.eclipse.leshan.core.request.WriteAttributesRequest; | |
25 | +import org.eclipse.leshan.core.request.WriteRequest; | |
26 | +import org.eclipse.leshan.core.response.DeleteResponse; | |
27 | +import org.eclipse.leshan.core.response.DiscoverResponse; | |
28 | +import org.eclipse.leshan.core.response.ExecuteResponse; | |
29 | +import org.eclipse.leshan.core.response.ObserveResponse; | |
30 | +import org.eclipse.leshan.core.response.ReadResponse; | |
31 | +import org.eclipse.leshan.core.response.WriteAttributesResponse; | |
32 | +import org.eclipse.leshan.core.response.WriteResponse; | |
33 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
34 | + | |
35 | +import java.util.List; | |
36 | +import java.util.Set; | |
37 | + | |
38 | +public interface LwM2mDownlinkMsgHandler { | |
39 | + | |
40 | + void sendReadRequest(LwM2mClient client, TbLwM2MReadRequest request, DownlinkRequestCallback<ReadRequest, ReadResponse> callback); | |
41 | + | |
42 | + void sendObserveRequest(LwM2mClient client, TbLwM2MObserveRequest request, DownlinkRequestCallback<ObserveRequest, ObserveResponse> callback); | |
43 | + | |
44 | + void sendObserveAllRequest(LwM2mClient client, TbLwM2MObserveAllRequest request, DownlinkRequestCallback<TbLwM2MObserveAllRequest, Set<String>> callback); | |
45 | + | |
46 | + void sendExecuteRequest(LwM2mClient client, TbLwM2MExecuteRequest request, DownlinkRequestCallback<ExecuteRequest, ExecuteResponse> callback); | |
47 | + | |
48 | + void sendDeleteRequest(LwM2mClient client, TbLwM2MDeleteRequest request, DownlinkRequestCallback<DeleteRequest, DeleteResponse> callback); | |
49 | + | |
50 | + void sendCancelObserveRequest(LwM2mClient client, TbLwM2MCancelObserveRequest request, DownlinkRequestCallback<TbLwM2MCancelObserveRequest, Integer> callback); | |
51 | + | |
52 | + void sendCancelAllRequest(LwM2mClient client, TbLwM2MCancelAllRequest request, DownlinkRequestCallback<TbLwM2MCancelAllRequest, Integer> callback); | |
53 | + | |
54 | + void sendDiscoverRequest(LwM2mClient client, TbLwM2MDiscoverRequest request, DownlinkRequestCallback<DiscoverRequest, DiscoverResponse> callback); | |
55 | + | |
56 | + void sendDiscoverAllRequest(LwM2mClient client, TbLwM2MDiscoverAllRequest request, DownlinkRequestCallback<TbLwM2MDiscoverAllRequest, List<Link>> callback); | |
57 | + | |
58 | + void sendWriteAttributesRequest(LwM2mClient client, TbLwM2MWriteAttributesRequest request, DownlinkRequestCallback<WriteAttributesRequest, WriteAttributesResponse> callback); | |
59 | + | |
60 | + void sendWriteReplaceRequest(LwM2mClient client, TbLwM2MWriteReplaceRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback); | |
61 | + | |
62 | + void sendWriteUpdateRequest(LwM2mClient client, TbLwM2MWriteUpdateRequest request, DownlinkRequestCallback<WriteRequest, WriteResponse> callback); | |
63 | + | |
64 | + | |
65 | +} | ... | ... |
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 lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
21 | + | |
22 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; | |
23 | + | |
24 | +@Slf4j | |
25 | +public class TbLwM2MCancelAllObserveCallback extends AbstractTbLwM2MRequestCallback<TbLwM2MCancelAllRequest, Integer> { | |
26 | + | |
27 | + public TbLwM2MCancelAllObserveCallback(LwM2MTelemetryLogService logService, LwM2mClient client) { | |
28 | + super(logService, client); | |
29 | + } | |
30 | + | |
31 | + @Override | |
32 | + public void onSuccess(TbLwM2MCancelAllRequest request, Integer canceledSubscriptionsCount) { | |
33 | + log.trace("[{}] Cancel of all observations was successful: {}", client.getEndpoint(), canceledSubscriptionsCount); | |
34 | + logService.log(client, String.format("[%s]: Cancel of all observations was successful. Result: [%s]", LOG_LWM2M_INFO, canceledSubscriptionsCount)); | |
35 | + } | |
36 | + | |
37 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
21 | + | |
22 | +public class TbLwM2MCancelAllRequest implements TbLwM2MDownlinkRequest<Integer> { | |
23 | + | |
24 | + @Getter | |
25 | + private final long timeout; | |
26 | + | |
27 | + @Builder | |
28 | + private TbLwM2MCancelAllRequest(long timeout) { | |
29 | + this.timeout = timeout; | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + public LwM2mOperationType getType() { | |
34 | + return LwM2mOperationType.OBSERVE_CANCEL_ALL; | |
35 | + } | |
36 | + | |
37 | +} | ... | ... |
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 lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
22 | + | |
23 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; | |
24 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType.OBSERVE_CANCEL; | |
25 | + | |
26 | +@Slf4j | |
27 | +public class TbLwM2MCancelObserveCallback extends AbstractTbLwM2MRequestCallback<TbLwM2MCancelObserveRequest, Integer> { | |
28 | + | |
29 | + private final String versionedId; | |
30 | + | |
31 | + public TbLwM2MCancelObserveCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) { | |
32 | + super(logService, client); | |
33 | + this.versionedId = versionedId; | |
34 | + } | |
35 | + | |
36 | + @Override | |
37 | + public void onSuccess(TbLwM2MCancelObserveRequest request, Integer canceledSubscriptionsCount) { | |
38 | + log.trace("[{}] Cancel observation of [{}] successful: {}", client.getEndpoint(), versionedId, canceledSubscriptionsCount); | |
39 | + logService.log(client, String.format("[%s]: Cancel Observe for [%s] successful. Result: [%s]", LOG_LWM2M_INFO, versionedId, canceledSubscriptionsCount)); | |
40 | + } | |
41 | + | |
42 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
20 | + | |
21 | +public class TbLwM2MCancelObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest<Integer> { | |
22 | + | |
23 | + @Builder | |
24 | + private TbLwM2MCancelObserveRequest(String versionedId, long timeout) { | |
25 | + super(versionedId, timeout); | |
26 | + } | |
27 | + | |
28 | + @Override | |
29 | + public LwM2mOperationType getType() { | |
30 | + return LwM2mOperationType.OBSERVE_CANCEL; | |
31 | + } | |
32 | + | |
33 | + | |
34 | + | |
35 | +} | ... | ... |
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.eclipse.leshan.core.request.DeleteRequest; | |
19 | +import org.eclipse.leshan.core.response.DeleteResponse; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
23 | + | |
24 | +public class TbLwM2MDeleteCallback extends TbLwM2MTargetedCallback<DeleteRequest, DeleteResponse> { | |
25 | + | |
26 | + public TbLwM2MDeleteCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { | |
27 | + super(logService, client, targetId); | |
28 | + } | |
29 | + | |
30 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import org.eclipse.leshan.core.response.ReadResponse; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
21 | + | |
22 | +public class TbLwM2MDeleteRequest extends AbstractTbLwM2MTargetedDownlinkRequest<ReadResponse> { | |
23 | + | |
24 | + @Builder | |
25 | + private TbLwM2MDeleteRequest(String versionedId, long timeout) { | |
26 | + super(versionedId, timeout); | |
27 | + } | |
28 | + | |
29 | + @Override | |
30 | + public LwM2mOperationType getType() { | |
31 | + return LwM2mOperationType.DELETE; | |
32 | + } | |
33 | + | |
34 | + | |
35 | + | |
36 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
21 | + | |
22 | +public class TbLwM2MDiscoverAllRequest implements TbLwM2MDownlinkRequest<String> { | |
23 | + | |
24 | + @Getter | |
25 | + private final long timeout; | |
26 | + | |
27 | + @Builder | |
28 | + private TbLwM2MDiscoverAllRequest(long timeout) { | |
29 | + this.timeout = timeout; | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + public LwM2mOperationType getType() { | |
34 | + return LwM2mOperationType.DISCOVER_ALL; | |
35 | + } | |
36 | + | |
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; | |
17 | + | |
18 | +import org.eclipse.leshan.core.request.DiscoverRequest; | |
19 | +import org.eclipse.leshan.core.response.DiscoverResponse; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
23 | + | |
24 | +public class TbLwM2MDiscoverCallback extends TbLwM2MTargetedCallback<DiscoverRequest, DiscoverResponse> { | |
25 | + | |
26 | + public TbLwM2MDiscoverCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { | |
27 | + super(logService, client, targetId); | |
28 | + } | |
29 | + | |
30 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import org.eclipse.leshan.core.response.DiscoverResponse; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
21 | + | |
22 | +public class TbLwM2MDiscoverRequest extends AbstractTbLwM2MTargetedDownlinkRequest<DiscoverResponse> { | |
23 | + | |
24 | + @Builder | |
25 | + private TbLwM2MDiscoverRequest(String versionedId, long timeout) { | |
26 | + super(versionedId, timeout); | |
27 | + } | |
28 | + | |
29 | + @Override | |
30 | + public LwM2mOperationType getType() { | |
31 | + return LwM2mOperationType.DISCOVER; | |
32 | + } | |
33 | + | |
34 | + | |
35 | + | |
36 | +} | ... | ... |
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.LwM2mOperationType; | |
19 | + | |
20 | +public interface TbLwM2MDownlinkRequest<T> { | |
21 | + | |
22 | + LwM2mOperationType getType(); | |
23 | + | |
24 | + long getTimeout(); | |
25 | + | |
26 | +} | ... | ... |
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.eclipse.leshan.core.request.ExecuteRequest; | |
19 | +import org.eclipse.leshan.core.response.ExecuteResponse; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
23 | + | |
24 | +public class TbLwM2MExecuteCallback extends TbLwM2MTargetedCallback<ExecuteRequest, ExecuteResponse> { | |
25 | + | |
26 | + public TbLwM2MExecuteCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { | |
27 | + super(logService, client, targetId); | |
28 | + } | |
29 | + | |
30 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.eclipse.leshan.core.response.ReadResponse; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
22 | + | |
23 | +public class TbLwM2MExecuteRequest extends AbstractTbLwM2MTargetedDownlinkRequest<ReadResponse> { | |
24 | + | |
25 | + @Getter | |
26 | + private final Object params; | |
27 | + | |
28 | + @Builder | |
29 | + private TbLwM2MExecuteRequest(String versionedId, long timeout, Object params) { | |
30 | + super(versionedId, timeout); | |
31 | + this.params = params; | |
32 | + } | |
33 | + | |
34 | + @Override | |
35 | + public LwM2mOperationType getType() { | |
36 | + return LwM2mOperationType.EXECUTE; | |
37 | + } | |
38 | + | |
39 | + | |
40 | + | |
41 | +} | ... | ... |
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 lombok.RequiredArgsConstructor; | |
19 | + | |
20 | +import java.util.concurrent.CountDownLatch; | |
21 | + | |
22 | +@RequiredArgsConstructor | |
23 | +public class TbLwM2MLatchCallback<R, T> implements DownlinkRequestCallback<R, T> { | |
24 | + | |
25 | + private final CountDownLatch countDownLatch; | |
26 | + private final DownlinkRequestCallback<R, T> callback; | |
27 | + | |
28 | + @Override | |
29 | + public void onSuccess(R request, T response) { | |
30 | + callback.onSuccess(request, response); | |
31 | + countDownLatch.countDown(); | |
32 | + } | |
33 | + | |
34 | + @Override | |
35 | + public void onValidationError(String params, String msg) { | |
36 | + callback.onValidationError(params, msg); | |
37 | + countDownLatch.countDown(); | |
38 | + } | |
39 | + | |
40 | + @Override | |
41 | + public void onError(String params, Exception e) { | |
42 | + callback.onError(params, e); | |
43 | + countDownLatch.countDown(); | |
44 | + } | |
45 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
21 | + | |
22 | +import java.util.Set; | |
23 | + | |
24 | +public class TbLwM2MObserveAllRequest implements TbLwM2MDownlinkRequest<Set<String>> { | |
25 | + | |
26 | + @Getter | |
27 | + private final long timeout; | |
28 | + | |
29 | + @Builder | |
30 | + private TbLwM2MObserveAllRequest(long timeout) { | |
31 | + this.timeout = timeout; | |
32 | + } | |
33 | + | |
34 | + @Override | |
35 | + public LwM2mOperationType getType() { | |
36 | + return LwM2mOperationType.OBSERVE_READ_ALL; | |
37 | + } | |
38 | + | |
39 | + | |
40 | + | |
41 | +} | ... | ... |
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 lombok.extern.slf4j.Slf4j; | |
19 | +import org.eclipse.leshan.core.request.ObserveRequest; | |
20 | +import org.eclipse.leshan.core.response.ObserveResponse; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
23 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
24 | + | |
25 | +@Slf4j | |
26 | +public class TbLwM2MObserveCallback extends TbLwM2MUplinkTargetedCallback<ObserveRequest, ObserveResponse> { | |
27 | + | |
28 | + public TbLwM2MObserveCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { | |
29 | + super(handler, logService, client, targetId); | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + public void onSuccess(ObserveRequest request, ObserveResponse response) { | |
34 | + super.onSuccess(request, response); | |
35 | + handler.onUpdateValueAfterReadResponse(client.getRegistration(), versionedId, response); | |
36 | + } | |
37 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.eclipse.leshan.core.request.ContentFormat; | |
21 | +import org.eclipse.leshan.core.response.ObserveResponse; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
23 | + | |
24 | +public class TbLwM2MObserveRequest extends AbstractTbLwM2MTargetedDownlinkRequest<ObserveResponse> implements HasContentFormat { | |
25 | + | |
26 | + @Getter | |
27 | + private final ContentFormat contentFormat; | |
28 | + | |
29 | + @Builder | |
30 | + private TbLwM2MObserveRequest(String versionedId, long timeout, ContentFormat contentFormat) { | |
31 | + super(versionedId, timeout); | |
32 | + this.contentFormat = contentFormat; | |
33 | + } | |
34 | + | |
35 | + @Override | |
36 | + public LwM2mOperationType getType() { | |
37 | + return LwM2mOperationType.OBSERVE; | |
38 | + } | |
39 | + | |
40 | + | |
41 | + | |
42 | +} | ... | ... |
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 lombok.extern.slf4j.Slf4j; | |
19 | +import org.eclipse.leshan.core.request.ReadRequest; | |
20 | +import org.eclipse.leshan.core.response.ReadResponse; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
23 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
24 | + | |
25 | +@Slf4j | |
26 | +public class TbLwM2MReadCallback extends TbLwM2MUplinkTargetedCallback<ReadRequest, ReadResponse> { | |
27 | + | |
28 | + public TbLwM2MReadCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { | |
29 | + super(handler, logService, client, targetId); | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + public void onSuccess(ReadRequest request, ReadResponse response) { | |
34 | + super.onSuccess(request, response); | |
35 | + handler.onUpdateValueAfterReadResponse(client.getRegistration(), versionedId, response); | |
36 | + } | |
37 | + | |
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; | |
17 | + | |
18 | +import lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.eclipse.leshan.core.request.ContentFormat; | |
21 | +import org.eclipse.leshan.core.response.ReadResponse; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
23 | + | |
24 | +public class TbLwM2MReadRequest extends AbstractTbLwM2MTargetedDownlinkRequest<ReadResponse> implements HasContentFormat { | |
25 | + | |
26 | + @Getter | |
27 | + private final ContentFormat contentFormat; | |
28 | + | |
29 | + @Builder | |
30 | + private TbLwM2MReadRequest(String versionedId, long timeout, ContentFormat contentFormat) { | |
31 | + super(versionedId, timeout); | |
32 | + this.contentFormat = contentFormat; | |
33 | + } | |
34 | + | |
35 | + @Override | |
36 | + public LwM2mOperationType getType() { | |
37 | + return LwM2mOperationType.READ; | |
38 | + } | |
39 | + | |
40 | + | |
41 | + | |
42 | +} | ... | ... |
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 lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
22 | + | |
23 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; | |
24 | + | |
25 | +@Slf4j | |
26 | +public abstract class TbLwM2MTargetedCallback<R, T> extends AbstractTbLwM2MRequestCallback<R, T> { | |
27 | + | |
28 | + protected final String versionedId; | |
29 | + | |
30 | + public TbLwM2MTargetedCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) { | |
31 | + super(logService, client); | |
32 | + this.versionedId = versionedId; | |
33 | + } | |
34 | + | |
35 | + @Override | |
36 | + public void onSuccess(R request, T response) { | |
37 | + //TODO convert camelCase to "camel case" using .split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])") | |
38 | + 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)); | |
41 | + } | |
42 | + | |
43 | +} | ... | ... |
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 lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
22 | + | |
23 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; | |
24 | + | |
25 | +@Slf4j | |
26 | +public abstract class TbLwM2MUplinkTargetedCallback<R, T> extends TbLwM2MTargetedCallback<R, T> { | |
27 | + | |
28 | + protected LwM2mUplinkMsgHandler handler; | |
29 | + | |
30 | + public TbLwM2MUplinkTargetedCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String versionedId) { | |
31 | + super(logService, client, versionedId); | |
32 | + this.handler = handler; | |
33 | + } | |
34 | + | |
35 | +} | ... | ... |
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.eclipse.leshan.core.request.WriteAttributesRequest; | |
19 | +import org.eclipse.leshan.core.response.WriteAttributesResponse; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
23 | + | |
24 | +public class TbLwM2MWriteAttributesCallback extends TbLwM2MTargetedCallback<WriteAttributesRequest, WriteAttributesResponse> { | |
25 | + | |
26 | + public TbLwM2MWriteAttributesCallback(LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { | |
27 | + super(logService, client, targetId); | |
28 | + } | |
29 | + | |
30 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.eclipse.leshan.core.response.WriteAttributesResponse; | |
21 | +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
23 | + | |
24 | +public class TbLwM2MWriteAttributesRequest extends AbstractTbLwM2MTargetedDownlinkRequest<WriteAttributesResponse> { | |
25 | + | |
26 | + @Getter | |
27 | + private final ObjectAttributes attributes; | |
28 | + | |
29 | + @Builder | |
30 | + private TbLwM2MWriteAttributesRequest(String versionedId, long timeout, ObjectAttributes attributes) { | |
31 | + super(versionedId, timeout); | |
32 | + this.attributes = attributes; | |
33 | + } | |
34 | + | |
35 | + @Override | |
36 | + public LwM2mOperationType getType() { | |
37 | + return LwM2mOperationType.WRITE_ATTRIBUTES; | |
38 | + } | |
39 | + | |
40 | + | |
41 | + | |
42 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.eclipse.leshan.core.request.ContentFormat; | |
21 | +import org.eclipse.leshan.core.response.WriteResponse; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
23 | + | |
24 | +public class TbLwM2MWriteReplaceRequest extends AbstractTbLwM2MTargetedDownlinkRequest<WriteResponse> { | |
25 | + | |
26 | + @Getter | |
27 | + private final ContentFormat contentFormat; | |
28 | + @Getter | |
29 | + private final Object value; | |
30 | + | |
31 | + @Builder | |
32 | + private TbLwM2MWriteReplaceRequest(String versionedId, long timeout, ContentFormat contentFormat, Object value) { | |
33 | + super(versionedId, timeout); | |
34 | + this.contentFormat = contentFormat; | |
35 | + this.value = value; | |
36 | + } | |
37 | + | |
38 | + @Override | |
39 | + public LwM2mOperationType getType() { | |
40 | + return LwM2mOperationType.WRITE_REPLACE; | |
41 | + } | |
42 | + | |
43 | + | |
44 | + | |
45 | +} | ... | ... |
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.eclipse.leshan.core.request.WriteRequest; | |
19 | +import org.eclipse.leshan.core.response.WriteResponse; | |
20 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
23 | + | |
24 | +public class TbLwM2MWriteResponseCallback extends TbLwM2MUplinkTargetedCallback<WriteRequest, WriteResponse> { | |
25 | + | |
26 | + public TbLwM2MWriteResponseCallback(LwM2mUplinkMsgHandler handler, LwM2MTelemetryLogService logService, LwM2mClient client, String targetId) { | |
27 | + super(handler, logService, client, targetId); | |
28 | + } | |
29 | + | |
30 | + @Override | |
31 | + public void onSuccess(WriteRequest request, WriteResponse response) { | |
32 | + super.onSuccess(request, response); | |
33 | + handler.onWriteResponseOk(client, versionedId, request); | |
34 | + } | |
35 | + | |
36 | +} | ... | ... |
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 lombok.Builder; | |
19 | +import lombok.Getter; | |
20 | +import org.eclipse.leshan.core.request.ContentFormat; | |
21 | +import org.eclipse.leshan.core.response.WriteResponse; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
23 | + | |
24 | +public class TbLwM2MWriteUpdateRequest extends AbstractTbLwM2MTargetedDownlinkRequest<WriteResponse> { | |
25 | + | |
26 | + @Getter | |
27 | + private final Object value; | |
28 | + @Getter | |
29 | + private final ContentFormat objectContentFormat; | |
30 | + | |
31 | + @Builder | |
32 | + private TbLwM2MWriteUpdateRequest(String versionedId, long timeout, Object value, ContentFormat objectContentFormat) { | |
33 | + super(versionedId, timeout); | |
34 | + this.value = value; | |
35 | + this.objectContentFormat = objectContentFormat; | |
36 | + } | |
37 | + | |
38 | + @Override | |
39 | + public LwM2mOperationType getType() { | |
40 | + return LwM2mOperationType.WRITE_UPDATE; | |
41 | + } | |
42 | + | |
43 | + | |
44 | + | |
45 | +} | ... | ... |
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.log; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.springframework.stereotype.Service; | |
21 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; | |
23 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
24 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | |
25 | + | |
26 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; | |
27 | + | |
28 | +@Slf4j | |
29 | +@Service | |
30 | +@TbLwM2mTransportComponent | |
31 | +@RequiredArgsConstructor | |
32 | +public class DefaultLwM2MTelemetryLogService implements LwM2MTelemetryLogService { | |
33 | + | |
34 | + private final LwM2mClientContext clientContext; | |
35 | + private final LwM2mTransportServerHelper helper; | |
36 | + | |
37 | + /** | |
38 | + * @param logMsg - text msg | |
39 | + * @param registrationId - Id of Registration LwM2M Client | |
40 | + */ | |
41 | + @Override | |
42 | + public void log(String registrationId, String logMsg) { | |
43 | + log(clientContext.getClientByRegistrationId(registrationId), logMsg); | |
44 | + } | |
45 | + | |
46 | + @Override | |
47 | + public void log(LwM2mClient client, String logMsg) { | |
48 | + if (logMsg != null && client != null && client.getSession() != null) { | |
49 | + if (logMsg.length() > 1024) { | |
50 | + logMsg = logMsg.substring(0, 1024); | |
51 | + } | |
52 | + this.helper.sendParametersOnThingsboardTelemetry(this.helper.getKvStringtoThingsboard(LOG_LWM2M_TELEMETRY, logMsg), client.getSession()); | |
53 | + } | |
54 | + } | |
55 | + | |
56 | +} | ... | ... |
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.log; | |
17 | + | |
18 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
19 | + | |
20 | +public interface LwM2MTelemetryLogService { | |
21 | + | |
22 | + void log(LwM2mClient client, String msg); | |
23 | + | |
24 | + void log(String registrationId, String msg); | |
25 | + | |
26 | +} | ... | ... |
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.ota; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.eclipse.leshan.core.request.ContentFormat; | |
21 | +import org.springframework.beans.factory.annotation.Autowired; | |
22 | +import org.springframework.context.annotation.Lazy; | |
23 | +import org.springframework.stereotype.Service; | |
24 | +import org.thingsboard.common.util.DonAsynchron; | |
25 | +import org.thingsboard.server.cache.ota.OtaPackageDataCache; | |
26 | +import org.thingsboard.server.common.data.StringUtils; | |
27 | +import org.thingsboard.server.common.data.ota.OtaPackageKey; | |
28 | +import org.thingsboard.server.common.data.ota.OtaPackageType; | |
29 | +import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; | |
30 | +import org.thingsboard.server.common.transport.TransportService; | |
31 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | |
32 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
33 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | |
34 | +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | |
35 | +import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; | |
36 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; | |
37 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; | |
38 | +import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; | |
39 | +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; | |
40 | +import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; | |
41 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
42 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | |
43 | +import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; | |
44 | +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; | |
45 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; | |
46 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; | |
47 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; | |
48 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; | |
49 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
50 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
51 | + | |
52 | +import javax.annotation.PostConstruct; | |
53 | +import javax.annotation.PreDestroy; | |
54 | +import java.util.ArrayList; | |
55 | +import java.util.List; | |
56 | +import java.util.Map; | |
57 | +import java.util.Optional; | |
58 | +import java.util.UUID; | |
59 | +import java.util.concurrent.ConcurrentHashMap; | |
60 | + | |
61 | +import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE; | |
62 | +import static org.thingsboard.server.common.data.ota.OtaPackageUtil.getAttributeKey; | |
63 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FIRMWARE_UPDATE_COAP_RECOURSE; | |
64 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_TELEMETRY; | |
65 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; | |
66 | + | |
67 | +@Slf4j | |
68 | +@Service | |
69 | +@TbLwM2mTransportComponent | |
70 | +@RequiredArgsConstructor | |
71 | +public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService implements LwM2MOtaUpdateService { | |
72 | + | |
73 | + public static final String FIRMWARE_VERSION = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.VERSION); | |
74 | + public static final String FIRMWARE_TITLE = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.TITLE); | |
75 | + public static final String FIRMWARE_URL = getAttributeKey(OtaPackageType.FIRMWARE, OtaPackageKey.URL); | |
76 | + public static final String SOFTWARE_VERSION = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.VERSION); | |
77 | + public static final String SOFTWARE_TITLE = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.TITLE); | |
78 | + public static final String SOFTWARE_URL = getAttributeKey(OtaPackageType.SOFTWARE, OtaPackageKey.URL); | |
79 | + | |
80 | + private static final String FW_PACKAGE_5_ID = "/5/0/0"; | |
81 | + private static final String FW_URL_ID = "/5/0/1"; | |
82 | + private static final String FW_EXECUTE_ID = "/5/0/2"; | |
83 | + private static final String FW_NAME_ID = "/5/0/6"; | |
84 | + private static final String FW_VER_ID = "/5/0/7"; | |
85 | + private static final String SW_NAME_ID = "/9/0/0"; | |
86 | + private static final String SW_VER_ID = "/9/0/1"; | |
87 | + | |
88 | + private final Map<String, LwM2MClientOtaInfo> fwStates = new ConcurrentHashMap<>(); | |
89 | + private final Map<String, LwM2MClientOtaInfo> swStates = new ConcurrentHashMap<>(); | |
90 | + | |
91 | + private final TransportService transportService; | |
92 | + private final LwM2mClientContext clientContext; | |
93 | + private final LwM2MTransportServerConfig config; | |
94 | + private final LwM2mUplinkMsgHandler uplinkHandler; | |
95 | + private final LwM2mDownlinkMsgHandler downlinkHandler; | |
96 | + private final OtaPackageDataCache otaPackageDataCache; | |
97 | + private final LwM2MTelemetryLogService logService; | |
98 | + private final LwM2mTransportServerHelper helper; | |
99 | + | |
100 | + @Autowired | |
101 | + @Lazy | |
102 | + private LwM2MAttributesService attributesService; | |
103 | + | |
104 | + @PostConstruct | |
105 | + public void init() { | |
106 | + super.init(); | |
107 | + } | |
108 | + | |
109 | + @PreDestroy | |
110 | + public void destroy() { | |
111 | + super.destroy(); | |
112 | + } | |
113 | + | |
114 | + @Override | |
115 | + protected int getExecutorSize() { | |
116 | + return config.getOtaPoolSize(); | |
117 | + } | |
118 | + | |
119 | + @Override | |
120 | + protected String getExecutorName() { | |
121 | + return "LwM2M OTA"; | |
122 | + } | |
123 | + | |
124 | + @Override | |
125 | + public void init(LwM2mClient client) { | |
126 | + //TODO: add locks by client fwInfo. | |
127 | + //TODO: check that the client supports FW and SW by checking the supported objects in the model. | |
128 | + List<String> attributesToFetch = new ArrayList<>(); | |
129 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
130 | + if (fwInfo.isSupported()) { | |
131 | + attributesToFetch.add(FIRMWARE_TITLE); | |
132 | + attributesToFetch.add(FIRMWARE_VERSION); | |
133 | + attributesToFetch.add(FIRMWARE_URL); | |
134 | + } | |
135 | + | |
136 | + if (!attributesToFetch.isEmpty()) { | |
137 | + var future = attributesService.getSharedAttributes(client, attributesToFetch); | |
138 | + DonAsynchron.withCallback(future, attrs -> { | |
139 | + if (fwInfo.isSupported()) { | |
140 | + Optional<String> newFirmwareTitle = getAttributeValue(attrs, FIRMWARE_TITLE); | |
141 | + Optional<String> newFirmwareVersion = getAttributeValue(attrs, FIRMWARE_VERSION); | |
142 | + Optional<String> newFirmwareUrl = getAttributeValue(attrs, FIRMWARE_URL); | |
143 | + if (newFirmwareTitle.isPresent() && newFirmwareVersion.isPresent()) { | |
144 | + onTargetFirmwareUpdate(client, newFirmwareTitle.get(), newFirmwareVersion.get(), newFirmwareUrl); | |
145 | + } | |
146 | + } | |
147 | + }, throwable -> { | |
148 | + if (fwInfo.isSupported()) { | |
149 | + fwInfo.setTargetFetchFailure(true); | |
150 | + } | |
151 | + }, executor); | |
152 | + } | |
153 | + } | |
154 | + | |
155 | + @Override | |
156 | + public void forceFirmwareUpdate(LwM2mClient client) { | |
157 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
158 | + fwInfo.setRetryAttempts(0); | |
159 | + fwInfo.setFailedPackageId(null); | |
160 | + startFirmwareUpdateIfNeeded(client, fwInfo); | |
161 | + } | |
162 | + | |
163 | + @Override | |
164 | + public void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional<String> newFirmwareUrl) { | |
165 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
166 | + fwInfo.updateTarget(newFirmwareTitle, newFirmwareVersion, newFirmwareUrl); | |
167 | + startFirmwareUpdateIfNeeded(client, fwInfo); | |
168 | + } | |
169 | + | |
170 | + @Override | |
171 | + public void onCurrentFirmwareNameUpdate(LwM2mClient client, String name) { | |
172 | + log.debug("[{}] Current fw name: {}", client.getEndpoint(), name); | |
173 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
174 | + fwInfo.setCurrentName(name); | |
175 | + } | |
176 | + | |
177 | + @Override | |
178 | + public void onCurrentFirmwareVersion3Update(LwM2mClient client, String version) { | |
179 | + log.debug("[{}] Current fw version: {}", client.getEndpoint(), version); | |
180 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
181 | + fwInfo.setCurrentVersion3(version); | |
182 | + } | |
183 | + | |
184 | + @Override | |
185 | + public void onCurrentFirmwareVersion5Update(LwM2mClient client, String version) { | |
186 | + log.debug("[{}] Current fw version: {}", client.getEndpoint(), version); | |
187 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
188 | + fwInfo.setCurrentVersion5(version); | |
189 | + } | |
190 | + | |
191 | + @Override | |
192 | + public void onCurrentFirmwareStateUpdate(LwM2mClient client, Long stateCode) { | |
193 | + log.debug("[{}] Current fw state: {}", client.getEndpoint(), stateCode); | |
194 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
195 | + UpdateStateFw state = UpdateStateFw.fromStateFwByCode(stateCode.intValue()); | |
196 | + if (UpdateStateFw.DOWNLOADED.equals(state)) { | |
197 | + executeFwUpdate(client); | |
198 | + } | |
199 | + fwInfo.setUpdateState(state); | |
200 | + Optional<OtaPackageUpdateStatus> status = LwM2mTransportUtil.toOtaPackageUpdateStatus(state); | |
201 | + status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, | |
202 | + otaStatus, "Firmware Update State: " + state.name())); | |
203 | + } | |
204 | + | |
205 | + @Override | |
206 | + public void onCurrentFirmwareResultUpdate(LwM2mClient client, Long code) { | |
207 | + log.debug("[{}] Current fw result: {}", client.getEndpoint(), code); | |
208 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
209 | + UpdateResultFw result = UpdateResultFw.fromUpdateResultFwByCode(code.intValue()); | |
210 | + Optional<OtaPackageUpdateStatus> status = LwM2mTransportUtil.toOtaPackageUpdateStatus(result); | |
211 | + status.ifPresent(otaStatus -> sendStateUpdateToTelemetry(client, fwInfo, | |
212 | + otaStatus, "Firmware Update Result: " + result.name())); | |
213 | + if (result.isAgain() && fwInfo.getRetryAttempts() <= 2) { | |
214 | + fwInfo.setRetryAttempts(fwInfo.getRetryAttempts() + 1); | |
215 | + startFirmwareUpdateIfNeeded(client, fwInfo); | |
216 | + } else { | |
217 | + fwInfo.setUpdateResult(result); | |
218 | + } | |
219 | + } | |
220 | + | |
221 | + @Override | |
222 | + public void onCurrentFirmwareDeliveryMethodUpdate(LwM2mClient client, Long value) { | |
223 | + log.debug("[{}] Current fw delivery method: {}", client.getEndpoint(), value); | |
224 | + LwM2MClientOtaInfo fwInfo = getOrInitFwInfo(client); | |
225 | + fwInfo.setDeliveryMethod(value.intValue()); | |
226 | + } | |
227 | + | |
228 | + @Override | |
229 | + public void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion) { | |
230 | + | |
231 | + } | |
232 | + | |
233 | + private void startFirmwareUpdateIfNeeded(LwM2mClient client, LwM2MClientOtaInfo fwInfo) { | |
234 | + try { | |
235 | + if (!fwInfo.isSupported()) { | |
236 | + log.debug("[{}] Fw update is not supported: {}", client.getEndpoint(), fwInfo); | |
237 | + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Client does not support firmware update or profile misconfiguration!"); | |
238 | + } else if (fwInfo.isUpdateRequired()) { | |
239 | + if (StringUtils.isNotEmpty(fwInfo.getTargetUrl())) { | |
240 | + log.debug("[{}] Starting update to [{}{}] using URL: {}", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion(), fwInfo.getTargetUrl()); | |
241 | + startFirmwareUpdateUsingUrl(client, fwInfo.getTargetUrl()); | |
242 | + } else { | |
243 | + log.debug("[{}] Starting update to [{}{}] using binary", client.getEndpoint(), fwInfo.getTargetName(), fwInfo.getTargetVersion()); | |
244 | + startFirmwareUpdateUsingBinary(client, fwInfo); | |
245 | + } | |
246 | + } | |
247 | + } catch (Exception e) { | |
248 | + log.warn("[{}] failed to update client: {}", client.getEndpoint(), fwInfo, e); | |
249 | + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Internal server error: " + e.getMessage()); | |
250 | + } | |
251 | + } | |
252 | + | |
253 | + private void startFirmwareUpdateUsingUrl(LwM2mClient client, String url) { | |
254 | + String targetIdVer = convertObjectIdToVersionedId(FW_URL_ID, client.getRegistration()); | |
255 | + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(targetIdVer).value(url).timeout(config.getTimeout()).build(); | |
256 | + downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, targetIdVer)); | |
257 | + } | |
258 | + | |
259 | + public void startFirmwareUpdateUsingBinary(LwM2mClient client, LwM2MClientOtaInfo fwInfo) { | |
260 | + String versionedId = convertObjectIdToVersionedId(FW_PACKAGE_5_ID, client.getRegistration()); | |
261 | + this.transportService.process(client.getSession(), createOtaPackageRequestMsg(client.getSession(), OtaPackageType.FIRMWARE.name()), | |
262 | + new TransportServiceCallback<>() { | |
263 | + @Override | |
264 | + public void onSuccess(TransportProtos.GetOtaPackageResponseMsg response) { | |
265 | + executor.submit(() -> doUpdateFirmwareUsingBinary(response, fwInfo, versionedId, client)); | |
266 | + } | |
267 | + | |
268 | + @Override | |
269 | + public void onError(Throwable e) { | |
270 | + logService.log(client, "Failed to process firmware update: " + e.getMessage()); | |
271 | + } | |
272 | + }); | |
273 | + } | |
274 | + | |
275 | + private void doUpdateFirmwareUsingBinary(TransportProtos.GetOtaPackageResponseMsg response, LwM2MClientOtaInfo fwInfo, String versionedId, LwM2mClient client) { | |
276 | + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { | |
277 | + UUID otaPackageId = new UUID(response.getOtaPackageIdMSB(), response.getOtaPackageIdLSB()); | |
278 | + LwM2MFirmwareUpdateStrategy strategy; | |
279 | + if (fwInfo.getDeliveryMethod() == null || fwInfo.getDeliveryMethod() == 2) { | |
280 | + strategy = fwInfo.getStrategy(); | |
281 | + } else { | |
282 | + strategy = fwInfo.getDeliveryMethod() == 0 ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY; | |
283 | + } | |
284 | + switch (strategy) { | |
285 | + case OBJ_5_BINARY: | |
286 | + byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0); | |
287 | + TbLwM2MWriteReplaceRequest writeRequest = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) | |
288 | + .value(firmwareChunk).contentFormat(ContentFormat.OPAQUE) | |
289 | + .timeout(config.getTimeout()).build(); | |
290 | + downlinkHandler.sendWriteReplaceRequest(client, writeRequest, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId)); | |
291 | + break; | |
292 | + case OBJ_5_TEMP_URL: | |
293 | + startFirmwareUpdateUsingUrl(client, fwInfo.getBaseUrl() + "/" + FIRMWARE_UPDATE_COAP_RECOURSE + "/" + otaPackageId.toString()); | |
294 | + break; | |
295 | + default: | |
296 | + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Unsupported strategy: " + strategy.name()); | |
297 | + } | |
298 | + } else { | |
299 | + sendStateUpdateToTelemetry(client, fwInfo, OtaPackageUpdateStatus.FAILED, "Failed to fetch OTA package: " + response.getResponseStatus()); | |
300 | + } | |
301 | + } | |
302 | + | |
303 | + private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(TransportProtos.SessionInfoProto sessionInfo, String nameFwSW) { | |
304 | + return TransportProtos.GetOtaPackageRequestMsg.newBuilder() | |
305 | + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
306 | + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) | |
307 | + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
308 | + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
309 | + .setType(nameFwSW) | |
310 | + .build(); | |
311 | + } | |
312 | + | |
313 | + private void executeFwUpdate(LwM2mClient client) { | |
314 | + TbLwM2MExecuteRequest request = TbLwM2MExecuteRequest.builder().versionedId(FW_EXECUTE_ID).timeout(config.getTimeout()).build(); | |
315 | + downlinkHandler.sendExecuteRequest(client, request, new TbLwM2MExecuteCallback(logService, client, FW_EXECUTE_ID)); | |
316 | + } | |
317 | + | |
318 | + private Optional<String> getAttributeValue(List<TransportProtos.TsKvProto> attrs, String keyName) { | |
319 | + for (TransportProtos.TsKvProto attr : attrs) { | |
320 | + if (keyName.equals(attr.getKv().getKey())) { | |
321 | + if (attr.getKv().getType().equals(TransportProtos.KeyValueType.STRING_V)) { | |
322 | + return Optional.of(attr.getKv().getStringV()); | |
323 | + } else { | |
324 | + return Optional.empty(); | |
325 | + } | |
326 | + } | |
327 | + } | |
328 | + return Optional.empty(); | |
329 | + } | |
330 | + | |
331 | + private LwM2MClientOtaInfo getOrInitFwInfo(LwM2mClient client) { | |
332 | + //TODO: fetch state from the cache or DB. | |
333 | + return fwStates.computeIfAbsent(client.getEndpoint(), endpoint -> { | |
334 | + var profile = clientContext.getProfile(client.getProfileId()); | |
335 | + return new LwM2MClientOtaInfo(endpoint, OtaPackageType.FIRMWARE, profile.getClientLwM2mSettings().getFwUpdateStrategy(), | |
336 | + profile.getClientLwM2mSettings().getFwUpdateRecourse()); | |
337 | + }); | |
338 | + } | |
339 | + | |
340 | + private LwM2MClientOtaInfo getOrInitSwInfo(LwM2mClient client) { | |
341 | + //TODO: fetch state from the cache or DB. | |
342 | + return swStates.computeIfAbsent(client.getEndpoint(), endpoint -> { | |
343 | + var profile = clientContext.getProfile(client.getProfileId()); | |
344 | + return new LwM2MClientOtaInfo(endpoint, OtaPackageType.SOFTWARE, profile.getClientLwM2mSettings().getSwUpdateStrategy(), profile.getClientLwM2mSettings().getSwUpdateRecourse()); | |
345 | + }); | |
346 | + | |
347 | + } | |
348 | + | |
349 | + private void sendStateUpdateToTelemetry(LwM2mClient client, LwM2MClientOtaInfo fwInfo, OtaPackageUpdateStatus status, String log) { | |
350 | + List<TransportProtos.KeyValueProto> result = new ArrayList<>(); | |
351 | + TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(fwInfo.getType(), STATE)); | |
352 | + kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(status.name()); | |
353 | + result.add(kvProto.build()); | |
354 | + kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(LOG_LWM2M_TELEMETRY); | |
355 | + kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(log); | |
356 | + result.add(kvProto.build()); | |
357 | + helper.sendParametersOnThingsboardTelemetry(result, client.getSession()); | |
358 | + } | |
359 | + | |
360 | +} | ... | ... |
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.ota; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import org.thingsboard.server.common.data.StringUtils; | |
20 | +import org.thingsboard.server.common.data.ota.OtaPackageType; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.LwM2MFirmwareUpdateStrategy; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.UpdateStateFw; | |
23 | +import org.thingsboard.server.transport.lwm2m.server.UpdateResultFw; | |
24 | + | |
25 | +import java.util.Optional; | |
26 | + | |
27 | +@Data | |
28 | +public class LwM2MClientOtaInfo { | |
29 | + | |
30 | + private final String endpoint; | |
31 | + private final OtaPackageType type; | |
32 | + | |
33 | + private String baseUrl; | |
34 | + | |
35 | + private boolean targetFetchFailure; | |
36 | + private String targetName; | |
37 | + private String targetVersion; | |
38 | + private String targetUrl; | |
39 | + | |
40 | + private boolean currentFetchFailure; | |
41 | + private String currentName; | |
42 | + private String currentVersion3; | |
43 | + private String currentVersion5; | |
44 | + private Integer deliveryMethod; | |
45 | + | |
46 | + //TODO: use value from device if applicable; | |
47 | + private LwM2MFirmwareUpdateStrategy strategy; | |
48 | + private UpdateStateFw updateState; | |
49 | + private UpdateResultFw updateResult; | |
50 | + | |
51 | + private String failedPackageId; | |
52 | + private int retryAttempts; | |
53 | + | |
54 | + public LwM2MClientOtaInfo(String endpoint, OtaPackageType type, Integer strategyCode, String baseUrl) { | |
55 | + this.endpoint = endpoint; | |
56 | + this.type = type; | |
57 | + this.strategy = LwM2MFirmwareUpdateStrategy.fromStrategyFwByCode(strategyCode); | |
58 | + this.baseUrl = baseUrl; | |
59 | + } | |
60 | + | |
61 | + public void updateTarget(String targetName, String targetVersion, Optional<String> newFirmwareUrl) { | |
62 | + this.targetName = targetName; | |
63 | + this.targetVersion = targetVersion; | |
64 | + this.targetUrl = newFirmwareUrl.orElse(null); | |
65 | + } | |
66 | + | |
67 | + public boolean isUpdateRequired() { | |
68 | + if (StringUtils.isEmpty(targetName) || StringUtils.isEmpty(targetVersion) || !isSupported()) { | |
69 | + return false; | |
70 | + } else { | |
71 | + String targetPackageId = getPackageId(targetName, targetVersion); | |
72 | + String currentPackageIdUsingObject5 = getPackageId(currentName, currentVersion5); | |
73 | + if (StringUtils.isNotEmpty(failedPackageId) && failedPackageId.equals(targetPackageId)) { | |
74 | + return false; | |
75 | + } else { | |
76 | + if (targetPackageId.equals(currentPackageIdUsingObject5)) { | |
77 | + return false; | |
78 | + } else if (StringUtils.isNotEmpty(currentVersion3)) { | |
79 | + return !currentVersion3.contains(targetPackageId); | |
80 | + } else { | |
81 | + return true; | |
82 | + } | |
83 | + } | |
84 | + } | |
85 | + } | |
86 | + | |
87 | + public boolean isSupported() { | |
88 | + return StringUtils.isNotEmpty(currentName) || StringUtils.isNotEmpty(currentVersion5) || StringUtils.isNotEmpty(currentVersion3); | |
89 | + } | |
90 | + | |
91 | + public void setUpdateResult(UpdateResultFw updateResult) { | |
92 | + this.updateResult = updateResult; | |
93 | + switch (updateResult) { | |
94 | + case INITIAL: | |
95 | + break; | |
96 | + case UPDATE_SUCCESSFULLY: | |
97 | + retryAttempts = 0; | |
98 | + break; | |
99 | + default: | |
100 | + failedPackageId = getPackageId(targetName, targetVersion); | |
101 | + break; | |
102 | + } | |
103 | + } | |
104 | + | |
105 | + private static String getPackageId(String name, String version) { | |
106 | + return (StringUtils.isNotEmpty(name) ? name : "") + (StringUtils.isNotEmpty(version) ? version : ""); | |
107 | + } | |
108 | + | |
109 | +} | ... | ... |
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.ota; | |
17 | + | |
18 | +public enum LwM2MClientOtaState { | |
19 | + | |
20 | + IDLE, IN_PROGRESS, SUCCESS, FAILED | |
21 | + | |
22 | +} | ... | ... |
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.ota; | |
17 | + | |
18 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
19 | + | |
20 | +import java.util.Optional; | |
21 | + | |
22 | +public interface LwM2MOtaUpdateService { | |
23 | + | |
24 | + void init(LwM2mClient client); | |
25 | + | |
26 | + void forceFirmwareUpdate(LwM2mClient client); | |
27 | + | |
28 | + void onTargetFirmwareUpdate(LwM2mClient client, String newFirmwareTitle, String newFirmwareVersion, Optional<String> newFirmwareUrl); | |
29 | + | |
30 | + void onTargetSoftwareUpdate(LwM2mClient client, String newSoftwareTitle, String newSoftwareVersion); | |
31 | + | |
32 | + void onCurrentFirmwareNameUpdate(LwM2mClient client, String name); | |
33 | + | |
34 | + void onCurrentFirmwareVersion3Update(LwM2mClient client, String version); | |
35 | + | |
36 | + void onCurrentFirmwareVersion5Update(LwM2mClient client, String version); | |
37 | + | |
38 | + void onCurrentFirmwareStateUpdate(LwM2mClient client, Long state); | |
39 | + | |
40 | + void onCurrentFirmwareResultUpdate(LwM2mClient client, Long result); | |
41 | + | |
42 | + void onCurrentFirmwareDeliveryMethodUpdate(LwM2mClient lwM2MClient, Long value); | |
43 | +} | ... | ... |
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; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.eclipse.leshan.core.ResponseCode; | |
21 | +import org.springframework.stereotype.Service; | |
22 | +import org.thingsboard.common.util.JacksonUtil; | |
23 | +import org.thingsboard.server.common.data.StringUtils; | |
24 | +import org.thingsboard.server.common.transport.TransportService; | |
25 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
26 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | |
27 | +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | |
28 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOperationType; | |
29 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
30 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | |
31 | +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; | |
32 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllObserveCallback; | |
33 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; | |
34 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; | |
35 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; | |
36 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteCallback; | |
37 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDeleteRequest; | |
38 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverAllRequest; | |
39 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; | |
40 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; | |
41 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback; | |
42 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest; | |
43 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveAllRequest; | |
44 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; | |
45 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; | |
46 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; | |
47 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; | |
48 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; | |
49 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; | |
50 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest; | |
51 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback; | |
52 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteUpdateRequest; | |
53 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
54 | +import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; | |
55 | + | |
56 | +import java.util.Map; | |
57 | +import java.util.Set; | |
58 | +import java.util.UUID; | |
59 | +import java.util.concurrent.ConcurrentHashMap; | |
60 | +import java.util.stream.Collectors; | |
61 | + | |
62 | +@Slf4j | |
63 | +@Service | |
64 | +@TbLwM2mTransportComponent | |
65 | +@RequiredArgsConstructor | |
66 | +public class DefaultLwM2MRpcRequestHandler implements LwM2MRpcRequestHandler { | |
67 | + | |
68 | + private final TransportService transportService; | |
69 | + private final LwM2mClientContext clientContext; | |
70 | + private final LwM2MTransportServerConfig config; | |
71 | + private final LwM2mUplinkMsgHandler uplinkHandler; | |
72 | + private final LwM2mDownlinkMsgHandler downlinkHandler; | |
73 | + private final LwM2MTelemetryLogService logService; | |
74 | + private final Map<UUID, Long> rpcSubscriptions = new ConcurrentHashMap<>(); | |
75 | + | |
76 | + @Override | |
77 | + public void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg rpcRequst, TransportProtos.SessionInfoProto sessionInfo) { | |
78 | + this.cleanupOldSessions(); | |
79 | + UUID requestUUID = new UUID(rpcRequst.getRequestIdMSB(), rpcRequst.getRequestIdLSB()); | |
80 | + log.warn("Received params: {}", rpcRequst.getParams()); | |
81 | + // We use this map to protect from browser issue that the same command is sent twice. | |
82 | + // TODO: This is probably not the best place and should be moved to DeviceActor | |
83 | + if (!this.rpcSubscriptions.containsKey(requestUUID)) { | |
84 | + LwM2mOperationType operationType = LwM2mOperationType.fromType(rpcRequst.getMethodName()); | |
85 | + if (operationType == null) { | |
86 | + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.METHOD_NOT_ALLOWED.getName(), "Unsupported operation type: " + rpcRequst.getMethodName()); | |
87 | + return; | |
88 | + } | |
89 | + LwM2mClient client = clientContext.getClientBySessionInfo(sessionInfo); | |
90 | + if (client.getRegistration() == null) { | |
91 | + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.INTERNAL_SERVER_ERROR.getName(), "Registration is empty"); | |
92 | + return; | |
93 | + } | |
94 | + try { | |
95 | + if (operationType.isHasObjectId()) { | |
96 | + String objectId = getIdFromParameters(client, rpcRequst); | |
97 | + switch (operationType) { | |
98 | + case READ: | |
99 | + sendReadRequest(client, rpcRequst, objectId); | |
100 | + break; | |
101 | + case OBSERVE: | |
102 | + sendObserveRequest(client, rpcRequst, objectId); | |
103 | + break; | |
104 | + case DISCOVER: | |
105 | + sendDiscoverRequest(client, rpcRequst, objectId); | |
106 | + break; | |
107 | + case EXECUTE: | |
108 | + sendExecuteRequest(client, rpcRequst, objectId); | |
109 | + break; | |
110 | + case WRITE_ATTRIBUTES: | |
111 | + sendWriteAttributesRequest(client, rpcRequst, objectId); | |
112 | + break; | |
113 | + case OBSERVE_CANCEL: | |
114 | + sendCancelObserveRequest(client, rpcRequst, objectId); | |
115 | + break; | |
116 | + case DELETE: | |
117 | + sendDeleteRequest(client, rpcRequst, objectId); | |
118 | + break; | |
119 | + case WRITE_UPDATE: | |
120 | + sendWriteUpdateRequest(client, rpcRequst, objectId); | |
121 | + break; | |
122 | + case WRITE_REPLACE: | |
123 | + sendWriteReplaceRequest(client, rpcRequst, objectId); | |
124 | + break; | |
125 | + default: | |
126 | + throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); | |
127 | + } | |
128 | + } else { | |
129 | + switch (operationType) { | |
130 | + case OBSERVE_CANCEL_ALL: | |
131 | + sendCancelAllObserveRequest(client, rpcRequst); | |
132 | + break; | |
133 | + case OBSERVE_READ_ALL: | |
134 | + sendObserveAllRequest(client, rpcRequst); | |
135 | + break; | |
136 | + case DISCOVER_ALL: | |
137 | + sendDiscoverAllRequest(client, rpcRequst); | |
138 | + break; | |
139 | + case FW_UPDATE: | |
140 | + //TODO: implement and add break statement | |
141 | + default: | |
142 | + throw new IllegalArgumentException("Unsupported operation: " + operationType.name()); | |
143 | + } | |
144 | + } | |
145 | + } catch (IllegalArgumentException e) { | |
146 | + this.sendErrorRpcResponse(sessionInfo, rpcRequst.getRequestId(), ResponseCode.BAD_REQUEST.getName(), e.getMessage()); | |
147 | + } | |
148 | + } | |
149 | + } | |
150 | + | |
151 | + private void sendReadRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
152 | + TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
153 | + var mainCallback = new TbLwM2MReadCallback(uplinkHandler, logService, client, versionedId); | |
154 | + var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback); | |
155 | + downlinkHandler.sendReadRequest(client, request, rpcCallback); | |
156 | + } | |
157 | + | |
158 | + private void sendObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
159 | + TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
160 | + var mainCallback = new TbLwM2MObserveCallback(uplinkHandler, logService, client, versionedId); | |
161 | + var rpcCallback = new RpcReadResponseCallback<>(transportService, client, requestMsg, versionedId, mainCallback); | |
162 | + downlinkHandler.sendObserveRequest(client, request, rpcCallback); | |
163 | + } | |
164 | + | |
165 | + private void sendObserveAllRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { | |
166 | + TbLwM2MObserveAllRequest request = TbLwM2MObserveAllRequest.builder().timeout(this.config.getTimeout()).build(); | |
167 | + downlinkHandler.sendObserveAllRequest(client, request, new RpcLinkSetCallback<>(transportService, client, requestMsg, null)); | |
168 | + } | |
169 | + | |
170 | + private void sendDiscoverAllRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { | |
171 | + TbLwM2MDiscoverAllRequest request = TbLwM2MDiscoverAllRequest.builder().timeout(this.config.getTimeout()).build(); | |
172 | + downlinkHandler.sendDiscoverAllRequest(client, request, new RpcLinkSetCallback<>(transportService, client, requestMsg, null)); | |
173 | + } | |
174 | + | |
175 | + private void sendDiscoverRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
176 | + TbLwM2MDiscoverRequest request = TbLwM2MDiscoverRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
177 | + var mainCallback = new TbLwM2MDiscoverCallback(logService, client, versionedId); | |
178 | + var rpcCallback = new RpcDiscoverCallback(transportService, client, requestMsg, mainCallback); | |
179 | + downlinkHandler.sendDiscoverRequest(client, request, rpcCallback); | |
180 | + } | |
181 | + | |
182 | + private void sendExecuteRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
183 | + TbLwM2MExecuteRequest downlink = TbLwM2MExecuteRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
184 | + var mainCallback = new TbLwM2MExecuteCallback(logService, client, versionedId); | |
185 | + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); | |
186 | + downlinkHandler.sendExecuteRequest(client, downlink, rpcCallback); | |
187 | + } | |
188 | + | |
189 | + private void sendWriteAttributesRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
190 | + RpcWriteAttributesRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteAttributesRequest.class); | |
191 | + TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(versionedId) | |
192 | + .attributes(requestBody.getAttributes()) | |
193 | + .timeout(this.config.getTimeout()).build(); | |
194 | + var mainCallback = new TbLwM2MWriteAttributesCallback(logService, client, versionedId); | |
195 | + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); | |
196 | + downlinkHandler.sendWriteAttributesRequest(client, request, rpcCallback); | |
197 | + } | |
198 | + | |
199 | + private void sendWriteUpdateRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
200 | + RpcWriteUpdateRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteUpdateRequest.class); | |
201 | + TbLwM2MWriteUpdateRequest.TbLwM2MWriteUpdateRequestBuilder builder = TbLwM2MWriteUpdateRequest.builder().versionedId(versionedId); | |
202 | + builder.value(requestBody.getValue()).timeout(this.config.getTimeout()); | |
203 | + var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId); | |
204 | + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); | |
205 | + downlinkHandler.sendWriteUpdateRequest(client, builder.build(), rpcCallback); | |
206 | + } | |
207 | + | |
208 | + private void sendWriteReplaceRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
209 | + RpcWriteUpdateRequest requestBody = JacksonUtil.fromString(requestMsg.getParams(), RpcWriteUpdateRequest.class); | |
210 | + TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionedId) | |
211 | + .value(requestBody.getValue()) | |
212 | + .timeout(this.config.getTimeout()).build(); | |
213 | + var mainCallback = new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionedId); | |
214 | + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); | |
215 | + downlinkHandler.sendWriteReplaceRequest(client, request, rpcCallback); | |
216 | + } | |
217 | + | |
218 | + private void sendCancelObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
219 | + TbLwM2MCancelObserveRequest downlink = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
220 | + var mainCallback = new TbLwM2MCancelObserveCallback(logService, client, versionedId); | |
221 | + var rpcCallback = new RpcCancelObserveCallback(transportService, client, requestMsg, mainCallback); | |
222 | + downlinkHandler.sendCancelObserveRequest(client, downlink, rpcCallback); | |
223 | + } | |
224 | + | |
225 | + private void sendDeleteRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId) { | |
226 | + TbLwM2MDeleteRequest downlink = TbLwM2MDeleteRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
227 | + var mainCallback = new TbLwM2MDeleteCallback(logService, client, versionedId); | |
228 | + var rpcCallback = new RpcEmptyResponseCallback<>(transportService, client, requestMsg, mainCallback); | |
229 | + downlinkHandler.sendDeleteRequest(client, downlink, rpcCallback); | |
230 | + } | |
231 | + | |
232 | + private void sendCancelAllObserveRequest(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg) { | |
233 | + TbLwM2MCancelAllRequest downlink = TbLwM2MCancelAllRequest.builder().timeout(this.config.getTimeout()).build(); | |
234 | + var mainCallback = new TbLwM2MCancelAllObserveCallback(logService, client); | |
235 | + var rpcCallback = new RpcCancelAllObserveCallback(transportService, client, requestMsg, mainCallback); | |
236 | + downlinkHandler.sendCancelAllRequest(client, downlink, rpcCallback); | |
237 | + } | |
238 | + | |
239 | + private String getIdFromParameters(LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg rpcRequst) { | |
240 | + IdOrKeyRequest requestParams = JacksonUtil.fromString(rpcRequst.getParams(), IdOrKeyRequest.class); | |
241 | + String targetId; | |
242 | + if (StringUtils.isNotEmpty(requestParams.getKey())) { | |
243 | + targetId = clientContext.getObjectIdByKeyNameFromProfile(client, requestParams.getKey()); | |
244 | + } else if (StringUtils.isNotEmpty(requestParams.getId())) { | |
245 | + targetId = requestParams.getId(); | |
246 | + } else { | |
247 | + throw new IllegalArgumentException("Can't find 'key' or 'id' in the requestParams parameters!"); | |
248 | + } | |
249 | + return targetId; | |
250 | + } | |
251 | + | |
252 | + private void sendErrorRpcResponse(TransportProtos.SessionInfoProto sessionInfo, int requestId, String result, String error) { | |
253 | + String payload = JacksonUtil.toString(JacksonUtil.newObjectNode().put("result", result).put("error", error)); | |
254 | + TransportProtos.ToDeviceRpcResponseMsg msg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder().setRequestId(requestId).setPayload(payload).build(); | |
255 | + transportService.process(sessionInfo, msg, null); | |
256 | + } | |
257 | + | |
258 | + private void cleanupOldSessions() { | |
259 | + log.warn("4.1) before rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); | |
260 | + if (rpcSubscriptions.size() > 0) { | |
261 | + long currentTime = System.currentTimeMillis(); | |
262 | + Set<UUID> rpcSubscriptionsToRemove = rpcSubscriptions.entrySet().stream().filter(kv -> currentTime > kv.getValue()).map(Map.Entry::getKey).collect(Collectors.toSet()); | |
263 | + log.warn("4.2) System.currentTimeMillis(): [{}]", System.currentTimeMillis()); | |
264 | + log.warn("4.3) rpcSubscriptionsToRemove: [{}]", rpcSubscriptionsToRemove); | |
265 | + rpcSubscriptionsToRemove.forEach(rpcSubscriptions::remove); | |
266 | + } | |
267 | + log.warn("4.4) after rpcSubscriptions.size(): [{}]", rpcSubscriptions.size()); | |
268 | + } | |
269 | + | |
270 | + @Override | |
271 | + public void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceResponse, TransportProtos.SessionInfoProto sessionInfo) { | |
272 | + log.warn("5) onToDeviceRpcResponse: [{}], sessionUUID: [{}]", toDeviceResponse, new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); | |
273 | + transportService.process(sessionInfo, toDeviceResponse, null); | |
274 | + } | |
275 | + | |
276 | + public void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse) { | |
277 | + log.info("[{}] toServerRpcResponse", toServerResponse); | |
278 | + } | |
279 | +} | ... | ... |
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; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +@Data | |
21 | +public class IdOrKeyRequest { | |
22 | + | |
23 | + private String key; | |
24 | + private String id; | |
25 | + | |
26 | +} | ... | ... |
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; | |
17 | + | |
18 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
19 | + | |
20 | +public interface LwM2MRpcRequestHandler { | |
21 | + | |
22 | + void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest, TransportProtos.SessionInfoProto sessionInfo); | |
23 | + | |
24 | + void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponse, TransportProtos.SessionInfoProto sessionInfo); | |
25 | + | |
26 | + void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse); | |
27 | + | |
28 | + | |
29 | +} | ... | ... |
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; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonInclude; | |
19 | +import lombok.Builder; | |
20 | +import lombok.Data; | |
21 | + | |
22 | +@Data | |
23 | +@Builder | |
24 | +@JsonInclude(JsonInclude.Include.NON_NULL) | |
25 | +public class LwM2MRpcResponseBody { | |
26 | + | |
27 | + private String result; | |
28 | + private String value; | |
29 | + private String error; | |
30 | + private String info; | |
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.server.rpc; | |
17 | + | |
18 | +import org.eclipse.leshan.core.ResponseCode; | |
19 | +import org.thingsboard.server.common.transport.TransportService; | |
20 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
21 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; | |
23 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelAllRequest; | |
24 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; | |
25 | + | |
26 | +public class RpcCancelAllObserveCallback extends RpcDownlinkRequestCallbackProxy<TbLwM2MCancelAllRequest, Integer> { | |
27 | + | |
28 | + public RpcCancelAllObserveCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<TbLwM2MCancelAllRequest, Integer> callback) { | |
29 | + super(transportService, client, requestMsg, callback); | |
30 | + } | |
31 | + | |
32 | + @Override | |
33 | + protected void sendRpcReplyOnSuccess(Integer response) { | |
34 | + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.CONTENT.getName()).value(response.toString()).build()); | |
35 | + } | |
36 | +} | ... | ... |
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; | |
17 | + | |
18 | +import org.eclipse.leshan.core.ResponseCode; | |
19 | +import org.eclipse.leshan.core.node.LwM2mObject; | |
20 | +import org.eclipse.leshan.core.node.LwM2mObjectInstance; | |
21 | +import org.eclipse.leshan.core.node.LwM2mResource; | |
22 | +import org.eclipse.leshan.core.response.ReadResponse; | |
23 | +import org.thingsboard.server.common.data.StringUtils; | |
24 | +import org.thingsboard.server.common.transport.TransportService; | |
25 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
26 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
27 | +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; | |
28 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; | |
29 | + | |
30 | +import java.util.Optional; | |
31 | + | |
32 | +public class RpcCancelObserveCallback extends RpcDownlinkRequestCallbackProxy<TbLwM2MCancelObserveRequest, Integer> { | |
33 | + | |
34 | + public RpcCancelObserveCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<TbLwM2MCancelObserveRequest, Integer> callback) { | |
35 | + super(transportService, client, requestMsg, callback); | |
36 | + } | |
37 | + | |
38 | + @Override | |
39 | + protected void sendRpcReplyOnSuccess(Integer response) { | |
40 | + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.CONTENT.getName()).value(response.toString()).build()); | |
41 | + } | |
42 | +} | ... | ... |
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; | |
17 | + | |
18 | +import org.eclipse.leshan.core.Link; | |
19 | +import org.eclipse.leshan.core.node.LwM2mObject; | |
20 | +import org.eclipse.leshan.core.node.LwM2mObjectInstance; | |
21 | +import org.eclipse.leshan.core.node.LwM2mResource; | |
22 | +import org.eclipse.leshan.core.request.DiscoverRequest; | |
23 | +import org.eclipse.leshan.core.response.DiscoverResponse; | |
24 | +import org.eclipse.leshan.core.response.ReadResponse; | |
25 | +import org.jetbrains.annotations.NotNull; | |
26 | +import org.thingsboard.server.common.data.StringUtils; | |
27 | +import org.thingsboard.server.common.transport.TransportService; | |
28 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
29 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
30 | +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; | |
31 | + | |
32 | +import java.util.Optional; | |
33 | + | |
34 | +public class RpcDiscoverCallback extends RpcLwM2MDownlinkCallback<DiscoverRequest, DiscoverResponse> { | |
35 | + | |
36 | + public RpcDiscoverCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<DiscoverRequest, DiscoverResponse> callback) { | |
37 | + super(transportService, client, requestMsg, callback); | |
38 | + } | |
39 | + | |
40 | + protected Optional<String> serializeSuccessfulResponse(DiscoverResponse response) { | |
41 | + return Optional.of(Link.serialize(response.getObjectLinks())); | |
42 | + } | |
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.rpc; | |
17 | + | |
18 | +import org.eclipse.leshan.core.ResponseCode; | |
19 | +import org.eclipse.leshan.core.node.codec.LwM2mValueConverter; | |
20 | +import org.thingsboard.common.util.JacksonUtil; | |
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.utils.LwM2mValueConverterImpl; | |
26 | + | |
27 | +public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkRequestCallback<R, T> { | |
28 | + | |
29 | + private final TransportService transportService; | |
30 | + private final TransportProtos.ToDeviceRpcRequestMsg request; | |
31 | + private final DownlinkRequestCallback<R, T> callback; | |
32 | + | |
33 | + protected final LwM2mClient client; | |
34 | + protected final LwM2mValueConverter converter; | |
35 | + | |
36 | + public RpcDownlinkRequestCallbackProxy(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<R, T> callback) { | |
37 | + this.transportService = transportService; | |
38 | + this.client = client; | |
39 | + this.request = requestMsg; | |
40 | + this.callback = callback; | |
41 | + this.converter = LwM2mValueConverterImpl.getInstance(); | |
42 | + } | |
43 | + | |
44 | + @Override | |
45 | + public void onSuccess(R request, T response) { | |
46 | + sendRpcReplyOnSuccess(response); | |
47 | + if (callback != null) { | |
48 | + callback.onSuccess(request, response); | |
49 | + } | |
50 | + } | |
51 | + | |
52 | + @Override | |
53 | + public void onValidationError(String params, String msg) { | |
54 | + sendRpcReplyOnValidationError(msg); | |
55 | + if (callback != null) { | |
56 | + callback.onValidationError(params, msg); | |
57 | + } | |
58 | + } | |
59 | + | |
60 | + @Override | |
61 | + public void onError(String params, Exception e) { | |
62 | + sendRpcReplyOnError(e); | |
63 | + if (callback != null) { | |
64 | + callback.onError(params, e); | |
65 | + } | |
66 | + } | |
67 | + | |
68 | + protected void reply(LwM2MRpcResponseBody response) { | |
69 | + TransportProtos.ToDeviceRpcResponseMsg msg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() | |
70 | + .setPayload(JacksonUtil.toString(response)) | |
71 | + .setRequestId(request.getRequestId()) | |
72 | + .build(); | |
73 | + transportService.process(client.getSession(), msg, null); | |
74 | + } | |
75 | + | |
76 | + abstract protected void sendRpcReplyOnSuccess(T response); | |
77 | + | |
78 | + protected void sendRpcReplyOnValidationError(String msg) { | |
79 | + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.BAD_REQUEST.getName()).error(msg).build()); | |
80 | + } | |
81 | + | |
82 | + protected void sendRpcReplyOnError(Exception e) { | |
83 | + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.INTERNAL_SERVER_ERROR.getName()).error(e.getMessage()).build()); | |
84 | + } | |
85 | + | |
86 | +} | ... | ... |
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; | |
17 | + | |
18 | +import org.eclipse.leshan.core.request.LwM2mRequest; | |
19 | +import org.eclipse.leshan.core.response.LwM2mResponse; | |
20 | +import org.thingsboard.server.common.transport.TransportService; | |
21 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
22 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
23 | +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; | |
24 | + | |
25 | +import java.util.Optional; | |
26 | + | |
27 | +public class RpcEmptyResponseCallback<R extends LwM2mRequest<T>, T extends LwM2mResponse> extends RpcLwM2MDownlinkCallback<R, T> { | |
28 | + | |
29 | + public RpcEmptyResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<R, T> callback) { | |
30 | + super(transportService, client, requestMsg, callback); | |
31 | + } | |
32 | + | |
33 | + protected Optional<String> serializeSuccessfulResponse(T response) { | |
34 | + return Optional.empty(); | |
35 | + } | |
36 | + | |
37 | +} | ... | ... |
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; | |
17 | + | |
18 | +import org.eclipse.leshan.core.Link; | |
19 | +import org.eclipse.leshan.core.ResponseCode; | |
20 | +import org.eclipse.leshan.core.response.DiscoverResponse; | |
21 | +import org.thingsboard.common.util.JacksonUtil; | |
22 | +import org.thingsboard.server.common.transport.TransportService; | |
23 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
24 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
25 | +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; | |
26 | + | |
27 | +import java.util.Optional; | |
28 | +import java.util.Set; | |
29 | + | |
30 | +public class RpcLinkSetCallback<R, T> extends RpcDownlinkRequestCallbackProxy<R, T> { | |
31 | + | |
32 | + public RpcLinkSetCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<R, T> callback) { | |
33 | + super(transportService, client, requestMsg, callback); | |
34 | + } | |
35 | + | |
36 | + @Override | |
37 | + protected void sendRpcReplyOnSuccess(T response) { | |
38 | + reply(LwM2MRpcResponseBody.builder().result(ResponseCode.CONTENT.getName()).value(JacksonUtil.toString(response)).build()); | |
39 | + } | |
40 | + | |
41 | +} | ... | ... |
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; | |
17 | + | |
18 | +import org.eclipse.leshan.core.request.LwM2mRequest; | |
19 | +import org.eclipse.leshan.core.response.LwM2mResponse; | |
20 | +import org.thingsboard.server.common.data.StringUtils; | |
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 | + | |
26 | +import java.util.Optional; | |
27 | + | |
28 | +public abstract class RpcLwM2MDownlinkCallback<R extends LwM2mRequest<T>, T extends LwM2mResponse> extends RpcDownlinkRequestCallbackProxy<R, T> { | |
29 | + | |
30 | + public RpcLwM2MDownlinkCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, DownlinkRequestCallback<R, T> callback) { | |
31 | + super(transportService, client, requestMsg, callback); | |
32 | + } | |
33 | + | |
34 | + @Override | |
35 | + protected void sendRpcReplyOnSuccess(T response) { | |
36 | + LwM2MRpcResponseBody.LwM2MRpcResponseBodyBuilder builder = LwM2MRpcResponseBody.builder().result(response.getCode().getName()); | |
37 | + if (response.isSuccess()) { | |
38 | + Optional<String> responseValue = serializeSuccessfulResponse(response); | |
39 | + if (responseValue.isPresent() && StringUtils.isNotEmpty(responseValue.get())) { | |
40 | + builder.value(responseValue.get()); | |
41 | + } | |
42 | + } else { | |
43 | + if (StringUtils.isNotEmpty(response.getErrorMessage())) { | |
44 | + builder.error(response.getErrorMessage()); | |
45 | + } | |
46 | + } | |
47 | + reply(builder.build()); | |
48 | + } | |
49 | + | |
50 | + protected abstract Optional<String> serializeSuccessfulResponse(T response); | |
51 | +} | ... | ... |
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; | |
17 | + | |
18 | +import org.eclipse.leshan.core.node.LwM2mObject; | |
19 | +import org.eclipse.leshan.core.node.LwM2mObjectInstance; | |
20 | +import org.eclipse.leshan.core.node.LwM2mResource; | |
21 | +import org.eclipse.leshan.core.request.LwM2mRequest; | |
22 | +import org.eclipse.leshan.core.response.ReadResponse; | |
23 | +import org.thingsboard.server.common.transport.TransportService; | |
24 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
25 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
26 | +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; | |
27 | + | |
28 | +import java.util.Optional; | |
29 | + | |
30 | +public class RpcReadResponseCallback<R extends LwM2mRequest<T>, T extends ReadResponse> extends RpcLwM2MDownlinkCallback<R, T> { | |
31 | + | |
32 | + private final String versionedId; | |
33 | + | |
34 | + public RpcReadResponseCallback(TransportService transportService, LwM2mClient client, TransportProtos.ToDeviceRpcRequestMsg requestMsg, String versionedId, DownlinkRequestCallback<R, T> callback) { | |
35 | + super(transportService, client, requestMsg, callback); | |
36 | + this.versionedId = versionedId; | |
37 | + } | |
38 | + | |
39 | + @Override | |
40 | + protected Optional<String> serializeSuccessfulResponse(T response) { | |
41 | + Object value = null; | |
42 | + if (response.getContent() instanceof LwM2mObject) { | |
43 | + value = client.objectToString((LwM2mObject) response.getContent(), this.converter, versionedId); | |
44 | + } else if (response.getContent() instanceof LwM2mObjectInstance) { | |
45 | + value = client.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, versionedId); | |
46 | + } else if (response.getContent() instanceof LwM2mResource) { | |
47 | + value = client.resourceToString((LwM2mResource) response.getContent(), this.converter, versionedId); | |
48 | + } | |
49 | + return Optional.of(String.format("%s", value)); | |
50 | + } | |
51 | +} | ... | ... |
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; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; | |
21 | + | |
22 | +@Data | |
23 | +@EqualsAndHashCode(callSuper = true) | |
24 | +public class RpcWriteAttributesRequest extends IdOrKeyRequest { | |
25 | + | |
26 | + private ObjectAttributes attributes; | |
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; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | + | |
21 | +@Data | |
22 | +@EqualsAndHashCode(callSuper = true) | |
23 | +public class RpcWriteReplaceRequest extends IdOrKeyRequest { | |
24 | + | |
25 | + private Object value; | |
26 | + | |
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.rpc; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; | |
21 | + | |
22 | +@Data | |
23 | +@EqualsAndHashCode(callSuper = true) | |
24 | +public class RpcWriteUpdateRequest extends IdOrKeyRequest { | |
25 | + | |
26 | + private Object value; | |
27 | + private String contentFormat; | |
28 | + | |
29 | +} | ... | ... |
... | ... | @@ -85,6 +85,7 @@ public class TbLwM2mSecurityStore implements TbEditableSecurityStore { |
85 | 85 | |
86 | 86 | @Override |
87 | 87 | public void remove(String endpoint) { |
88 | - securityStore.remove(endpoint); | |
88 | + //TODO: Make sure we delay removal of security store from endpoint due to reg/unreg race condition. | |
89 | +// securityStore.remove(endpoint); | |
89 | 90 | } |
90 | 91 | } | ... | ... |
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.uplink; | |
17 | + | |
18 | +import com.google.gson.Gson; | |
19 | +import com.google.gson.GsonBuilder; | |
20 | +import com.google.gson.JsonElement; | |
21 | +import com.google.gson.JsonObject; | |
22 | +import com.google.gson.reflect.TypeToken; | |
23 | +import lombok.extern.slf4j.Slf4j; | |
24 | +import org.eclipse.leshan.core.model.ObjectModel; | |
25 | +import org.eclipse.leshan.core.model.ResourceModel; | |
26 | +import org.eclipse.leshan.core.node.LwM2mObject; | |
27 | +import org.eclipse.leshan.core.node.LwM2mObjectInstance; | |
28 | +import org.eclipse.leshan.core.node.LwM2mPath; | |
29 | +import org.eclipse.leshan.core.node.LwM2mResource; | |
30 | +import org.eclipse.leshan.core.observation.Observation; | |
31 | +import org.eclipse.leshan.core.request.ObserveRequest; | |
32 | +import org.eclipse.leshan.core.request.ReadRequest; | |
33 | +import org.eclipse.leshan.core.request.WriteRequest; | |
34 | +import org.eclipse.leshan.core.response.ObserveResponse; | |
35 | +import org.eclipse.leshan.core.response.ReadResponse; | |
36 | +import org.eclipse.leshan.server.registration.Registration; | |
37 | +import org.springframework.context.annotation.Lazy; | |
38 | +import org.springframework.stereotype.Service; | |
39 | +import org.thingsboard.common.util.DonAsynchron; | |
40 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
41 | +import org.thingsboard.server.cache.ota.OtaPackageDataCache; | |
42 | +import org.thingsboard.server.common.data.Device; | |
43 | +import org.thingsboard.server.common.data.DeviceProfile; | |
44 | +import org.thingsboard.server.common.data.device.data.lwm2m.ObjectAttributes; | |
45 | +import org.thingsboard.server.common.data.device.data.lwm2m.TelemetryMappingConfiguration; | |
46 | +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; | |
47 | +import org.thingsboard.server.common.data.ota.OtaPackageUtil; | |
48 | +import org.thingsboard.server.common.transport.TransportService; | |
49 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | |
50 | +import org.thingsboard.server.common.transport.service.DefaultTransportService; | |
51 | +import org.thingsboard.server.gen.transport.TransportProtos; | |
52 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; | |
53 | +import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto; | |
54 | +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent; | |
55 | +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; | |
56 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mOtaConvert; | |
57 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest; | |
58 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener; | |
59 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; | |
60 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; | |
61 | +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil; | |
62 | +import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService; | |
63 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; | |
64 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientStateException; | |
65 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; | |
66 | +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; | |
67 | +import org.thingsboard.server.transport.lwm2m.server.client.ParametersAnalyzeResult; | |
68 | +import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto; | |
69 | +import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService; | |
70 | +import org.thingsboard.server.transport.lwm2m.server.downlink.DownlinkRequestCallback; | |
71 | +import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler; | |
72 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveCallback; | |
73 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MCancelObserveRequest; | |
74 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverCallback; | |
75 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MDiscoverRequest; | |
76 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MLatchCallback; | |
77 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveCallback; | |
78 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MObserveRequest; | |
79 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadCallback; | |
80 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MReadRequest; | |
81 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesCallback; | |
82 | +import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteAttributesRequest; | |
83 | +import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService; | |
84 | +import org.thingsboard.server.transport.lwm2m.server.ota.LwM2MOtaUpdateService; | |
85 | +import org.thingsboard.server.transport.lwm2m.server.rpc.LwM2MRpcRequestHandler; | |
86 | +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore; | |
87 | +import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl; | |
88 | + | |
89 | +import javax.annotation.PostConstruct; | |
90 | +import javax.annotation.PreDestroy; | |
91 | +import java.util.ArrayList; | |
92 | +import java.util.Collection; | |
93 | +import java.util.Collections; | |
94 | +import java.util.HashSet; | |
95 | +import java.util.List; | |
96 | +import java.util.Map; | |
97 | +import java.util.Optional; | |
98 | +import java.util.Random; | |
99 | +import java.util.Set; | |
100 | +import java.util.UUID; | |
101 | +import java.util.concurrent.ConcurrentHashMap; | |
102 | +import java.util.concurrent.CountDownLatch; | |
103 | +import java.util.concurrent.ExecutorService; | |
104 | +import java.util.concurrent.TimeUnit; | |
105 | +import java.util.stream.Collectors; | |
106 | + | |
107 | +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; | |
108 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_3_VER_ID; | |
109 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_5_VER_ID; | |
110 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_DELIVERY_METHOD; | |
111 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_NAME_ID; | |
112 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_RESULT_ID; | |
113 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.FW_STATE_ID; | |
114 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_ERROR; | |
115 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_INFO; | |
116 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.LOG_LWM2M_WARN; | |
117 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertObjectIdToVersionedId; | |
118 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertOtaUpdateValueToString; | |
119 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.fromVersionedIdToObjectId; | |
120 | + | |
121 | + | |
122 | +@Slf4j | |
123 | +@Service | |
124 | +@TbLwM2mTransportComponent | |
125 | +public class DefaultLwM2MUplinkMsgHandler extends LwM2MExecutorAwareService implements LwM2mUplinkMsgHandler { | |
126 | + | |
127 | + public LwM2mValueConverterImpl converter; | |
128 | + | |
129 | + private final TransportService transportService; | |
130 | + private final LwM2mTransportContext context; | |
131 | + private final LwM2MAttributesService attributesService; | |
132 | + private final LwM2MOtaUpdateService otaService; | |
133 | + public final LwM2MTransportServerConfig config; | |
134 | + private final LwM2MTelemetryLogService logService; | |
135 | + public final OtaPackageDataCache otaPackageDataCache; | |
136 | + public final LwM2mTransportServerHelper helper; | |
137 | + private final TbLwM2MDtlsSessionStore sessionStore; | |
138 | + public final LwM2mClientContext clientContext; | |
139 | + private final LwM2MRpcRequestHandler rpcHandler; | |
140 | + public final LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler; | |
141 | + | |
142 | + public final Map<String, Integer> firmwareUpdateState; | |
143 | + | |
144 | + public DefaultLwM2MUplinkMsgHandler(TransportService transportService, | |
145 | + LwM2MTransportServerConfig config, | |
146 | + LwM2mTransportServerHelper helper, | |
147 | + LwM2mClientContext clientContext, | |
148 | + LwM2MTelemetryLogService logService, | |
149 | + @Lazy LwM2MOtaUpdateService otaService, | |
150 | + @Lazy LwM2MAttributesService attributesService, | |
151 | + @Lazy LwM2MRpcRequestHandler rpcHandler, | |
152 | + @Lazy LwM2mDownlinkMsgHandler defaultLwM2MDownlinkMsgHandler, | |
153 | + OtaPackageDataCache otaPackageDataCache, | |
154 | + LwM2mTransportContext context, TbLwM2MDtlsSessionStore sessionStore) { | |
155 | + this.transportService = transportService; | |
156 | + this.attributesService = attributesService; | |
157 | + this.otaService = otaService; | |
158 | + this.config = config; | |
159 | + this.helper = helper; | |
160 | + this.clientContext = clientContext; | |
161 | + this.logService = logService; | |
162 | + this.rpcHandler = rpcHandler; | |
163 | + this.defaultLwM2MDownlinkMsgHandler = defaultLwM2MDownlinkMsgHandler; | |
164 | + this.otaPackageDataCache = otaPackageDataCache; | |
165 | + this.context = context; | |
166 | + this.firmwareUpdateState = new ConcurrentHashMap<>(); | |
167 | + this.sessionStore = sessionStore; | |
168 | + } | |
169 | + | |
170 | + @PostConstruct | |
171 | + public void init() { | |
172 | + super.init(); | |
173 | + this.context.getScheduler().scheduleAtFixedRate(this::reportActivity, new Random().nextInt((int) config.getSessionReportTimeout()), config.getSessionReportTimeout(), TimeUnit.MILLISECONDS); | |
174 | + this.converter = LwM2mValueConverterImpl.getInstance(); | |
175 | + } | |
176 | + | |
177 | + @PreDestroy | |
178 | + public void destroy() { | |
179 | + super.destroy(); | |
180 | + } | |
181 | + | |
182 | + @Override | |
183 | + protected String getExecutorName() { | |
184 | + return "LwM2M uplink"; | |
185 | + } | |
186 | + | |
187 | + @Override | |
188 | + protected int getExecutorSize() { | |
189 | + return config.getUplinkPoolSize(); | |
190 | + } | |
191 | + | |
192 | + /** | |
193 | + * Start registration device | |
194 | + * Create session: Map<String <registrationId >, LwM2MClient> | |
195 | + * 1. replaceNewRegistration -> (solving the problem of incorrect termination of the previous session with this endpoint) | |
196 | + * 1.1 When we initialize the registration, we register the session by endpoint. | |
197 | + * 1.2 If the server has incomplete requests (canceling the registration of the previous session), | |
198 | + * delete the previous session only by the previous registration.getId | |
199 | + * 1.2 Add Model (Entity) for client (from registration & observe) by registration.getId | |
200 | + * 1.2 Remove from sessions Model by enpPoint | |
201 | + * Next -> Create new LwM2MClient for current session -> setModelClient... | |
202 | + * | |
203 | + * @param registration - Registration LwM2M Client | |
204 | + * @param previousObservations - may be null | |
205 | + */ | |
206 | + public void onRegistered(Registration registration, Collection<Observation> previousObservations) { | |
207 | + executor.submit(() -> { | |
208 | + LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); | |
209 | + try { | |
210 | + log.warn("[{}] [{{}] Client: create after Registration", registration.getEndpoint(), registration.getId()); | |
211 | + if (lwM2MClient != null) { | |
212 | + Optional<SessionInfoProto> oldSessionInfo = this.clientContext.register(lwM2MClient, registration); | |
213 | + if (oldSessionInfo.isPresent()) { | |
214 | + log.info("[{}] Closing old session: {}", registration.getEndpoint(), new UUID(oldSessionInfo.get().getSessionIdMSB(), oldSessionInfo.get().getSessionIdLSB())); | |
215 | + closeSession(oldSessionInfo.get()); | |
216 | + } | |
217 | + logService.log(lwM2MClient, LOG_LWM2M_INFO + ": Client registered with registration id: " + registration.getId()); | |
218 | + SessionInfoProto sessionInfo = lwM2MClient.getSession(); | |
219 | + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo)); | |
220 | + log.warn("40) sessionId [{}] Registering rpc subscription after Registration client", new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); | |
221 | + TransportProtos.TransportToDeviceActorMsg msg = TransportProtos.TransportToDeviceActorMsg.newBuilder() | |
222 | + .setSessionInfo(sessionInfo) | |
223 | + .setSessionEvent(DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN)) | |
224 | + .setSubscribeToAttributes(TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build()) | |
225 | + .setSubscribeToRPC(TransportProtos.SubscribeToRPCMsg.newBuilder().setSessionType(TransportProtos.SessionType.ASYNC).build()) | |
226 | + .build(); | |
227 | + transportService.process(msg, null); | |
228 | + this.initClientTelemetry(lwM2MClient); | |
229 | + this.initAttributes(lwM2MClient); | |
230 | + otaService.init(lwM2MClient); | |
231 | + } else { | |
232 | + log.error("Client: [{}] onRegistered [{}] name [{}] lwM2MClient ", registration.getId(), registration.getEndpoint(), null); | |
233 | + } | |
234 | + } catch (LwM2MClientStateException stateException) { | |
235 | + if (LwM2MClientState.UNREGISTERED.equals(stateException.getState())) { | |
236 | + log.info("[{}] retry registration due to race condition: [{}].", registration.getEndpoint(), stateException.getState()); | |
237 | + // Race condition detected and the client was in progress of unregistration while new registration arrived. Let's try again. | |
238 | + onRegistered(registration, previousObservations); | |
239 | + } else { | |
240 | + logService.log(lwM2MClient, LOG_LWM2M_WARN + ": Client registration failed due to invalid state: " + stateException.getState()); | |
241 | + } | |
242 | + } catch (Throwable t) { | |
243 | + log.error("[{}] endpoint [{}] error Unable registration.", registration.getEndpoint(), t); | |
244 | + logService.log(lwM2MClient, LOG_LWM2M_WARN + ": Client registration failed due to: " + t.getMessage()); | |
245 | + } | |
246 | + }); | |
247 | + } | |
248 | + | |
249 | + /** | |
250 | + * if sessionInfo removed from sessions, then new registerAsyncSession | |
251 | + * | |
252 | + * @param registration - Registration LwM2M Client | |
253 | + */ | |
254 | + public void updatedReg(Registration registration) { | |
255 | + executor.submit(() -> { | |
256 | + LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
257 | + try { | |
258 | + log.warn("[{}] [{{}] Client: update after Registration", registration.getEndpoint(), registration.getId()); | |
259 | + clientContext.updateRegistration(lwM2MClient, registration); | |
260 | + TransportProtos.SessionInfoProto sessionInfo = lwM2MClient.getSession(); | |
261 | + this.reportActivityAndRegister(sessionInfo); | |
262 | + if (registration.usesQueueMode()) { | |
263 | + LwM2mQueuedRequest request; | |
264 | + while ((request = lwM2MClient.getQueuedRequests().poll()) != null) { | |
265 | + request.send(); | |
266 | + } | |
267 | + } | |
268 | + } catch (LwM2MClientStateException stateException) { | |
269 | + if (LwM2MClientState.REGISTERED.equals(stateException.getState())) { | |
270 | + log.info("[{}] update registration failed because client has different registration id: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage()); | |
271 | + } else { | |
272 | + onRegistered(registration, Collections.emptyList()); | |
273 | + } | |
274 | + } catch (Throwable t) { | |
275 | + log.error("[{}] endpoint [{}] error Unable update registration.", registration.getEndpoint(), t); | |
276 | + logService.log(lwM2MClient, LOG_LWM2M_ERROR + String.format(": Client update Registration, %s", t.getMessage())); | |
277 | + } | |
278 | + }); | |
279 | + } | |
280 | + | |
281 | + /** | |
282 | + * @param registration - Registration LwM2M Client | |
283 | + * @param observations - !!! Warn: if have not finishing unReg, then this operation will be finished on next Client`s connect | |
284 | + */ | |
285 | + public void unReg(Registration registration, Collection<Observation> observations) { | |
286 | + executor.submit(() -> { | |
287 | + LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
288 | + try { | |
289 | + logService.log(client, LOG_LWM2M_INFO + ": Client unRegistration"); | |
290 | + clientContext.unregister(client, registration); | |
291 | + SessionInfoProto sessionInfo = client.getSession(); | |
292 | + if (sessionInfo != null) { | |
293 | + closeSession(sessionInfo); | |
294 | + sessionStore.remove(registration.getEndpoint()); | |
295 | + log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType()); | |
296 | + } else { | |
297 | + log.error("Client close session: [{}] unReg [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); | |
298 | + } | |
299 | + } catch (LwM2MClientStateException stateException) { | |
300 | + log.info("[{}] delete registration: [{}] {}.", registration.getEndpoint(), stateException.getState(), stateException.getMessage()); | |
301 | + } catch (Throwable t) { | |
302 | + log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); | |
303 | + logService.log(client, LOG_LWM2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage())); | |
304 | + } | |
305 | + }); | |
306 | + } | |
307 | + | |
308 | + public void closeSession(SessionInfoProto sessionInfo) { | |
309 | + transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.CLOSED), null); | |
310 | + transportService.deregisterSession(sessionInfo); | |
311 | + } | |
312 | + | |
313 | + @Override | |
314 | + public void onSleepingDev(Registration registration) { | |
315 | + log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); | |
316 | + logService.log(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is sleeping!"); | |
317 | + //TODO: associate endpointId with device information. | |
318 | + } | |
319 | + | |
320 | +// /** | |
321 | +// * Cancel observation for All objects for this registration | |
322 | +// */ | |
323 | +// @Override | |
324 | +// public void setCancelObservationsAll(Registration registration) { | |
325 | +// if (registration != null) { | |
326 | +// LwM2mClient client = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
327 | +// if (client != null && client.getRegistration() != null && client.getRegistration().getId().equals(registration.getId())) { | |
328 | +// defaultLwM2MDownlinkMsgHandler.sendCancelAllRequest(client, TbLwM2MCancelAllRequest.builder().build(), new TbLwM2MCancelAllObserveRequestCallback(this, client)); | |
329 | +// } | |
330 | +// } | |
331 | +// } | |
332 | + | |
333 | + /** | |
334 | + * Sending observe value to thingsboard from ObservationListener.onResponse: object, instance, SingleResource or MultipleResource | |
335 | + * | |
336 | + * @param registration - Registration LwM2M Client | |
337 | + * @param path - observe | |
338 | + * @param response - observe | |
339 | + */ | |
340 | + @Override | |
341 | + public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response) { | |
342 | + if (response.getContent() != null) { | |
343 | + LwM2mClient lwM2MClient = clientContext.getClientByEndpoint(registration.getEndpoint()); | |
344 | + ObjectModel objectModelVersion = lwM2MClient.getObjectModel(path, this.config.getModelProvider()); | |
345 | + if (objectModelVersion != null) { | |
346 | + if (response.getContent() instanceof LwM2mObject) { | |
347 | + LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); | |
348 | + this.updateObjectResourceValue(lwM2MClient, lwM2mObject, path); | |
349 | + } else if (response.getContent() instanceof LwM2mObjectInstance) { | |
350 | + LwM2mObjectInstance lwM2mObjectInstance = (LwM2mObjectInstance) response.getContent(); | |
351 | + this.updateObjectInstanceResourceValue(lwM2MClient, lwM2mObjectInstance, path); | |
352 | + } else if (response.getContent() instanceof LwM2mResource) { | |
353 | + LwM2mResource lwM2mResource = (LwM2mResource) response.getContent(); | |
354 | + this.updateResourcesValue(lwM2MClient, lwM2mResource, path); | |
355 | + } | |
356 | + } | |
357 | + } | |
358 | + } | |
359 | + | |
360 | + /** | |
361 | + * @param sessionInfo - | |
362 | + * @param deviceProfile - | |
363 | + */ | |
364 | + @Override | |
365 | + public void onDeviceProfileUpdate(SessionInfoProto sessionInfo, DeviceProfile deviceProfile) { | |
366 | + List<LwM2mClient> clients = clientContext.getLwM2mClients() | |
367 | + .stream().filter(e -> e.getProfileId().equals(deviceProfile.getUuidId())).collect(Collectors.toList()); | |
368 | + clients.forEach(client -> client.onDeviceProfileUpdate(deviceProfile)); | |
369 | + if (clients.size() > 0) { | |
370 | + this.onDeviceProfileUpdate(clients, deviceProfile); | |
371 | + } | |
372 | + } | |
373 | + | |
374 | + @Override | |
375 | + public void onDeviceUpdate(SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) { | |
376 | + //TODO: check, maybe device has multiple sessions/registrations? Is this possible according to the standard. | |
377 | + LwM2mClient client = clientContext.getClientByDeviceId(device.getUuidId()); | |
378 | + if (client != null) { | |
379 | + this.onDeviceUpdate(client, device, deviceProfileOpt); | |
380 | + } | |
381 | + } | |
382 | + | |
383 | + @Override | |
384 | + public void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt) { | |
385 | + String idVer = resourceUpdateMsgOpt.get().getResourceKey(); | |
386 | + clientContext.getLwM2mClients().forEach(e -> e.updateResourceModel(idVer, this.config.getModelProvider())); | |
387 | + } | |
388 | + | |
389 | + @Override | |
390 | + public void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt) { | |
391 | + String pathIdVer = resourceDeleteMsgOpt.get().getResourceKey(); | |
392 | + clientContext.getLwM2mClients().forEach(e -> e.deleteResources(pathIdVer, this.config.getModelProvider())); | |
393 | + } | |
394 | + | |
395 | + /** | |
396 | + * Deregister session in transport | |
397 | + * | |
398 | + * @param sessionInfo - lwm2m client | |
399 | + */ | |
400 | + @Override | |
401 | + public void doDisconnect(SessionInfoProto sessionInfo) { | |
402 | + closeSession(sessionInfo); | |
403 | + } | |
404 | + | |
405 | + /** | |
406 | + * Those methods are called by the protocol stage thread pool, this means that execution MUST be done in a short delay, | |
407 | + * * if you need to do long time processing use a dedicated thread pool. | |
408 | + * | |
409 | + * @param registration - | |
410 | + */ | |
411 | + @Override | |
412 | + public void onAwakeDev(Registration registration) { | |
413 | + log.trace("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); | |
414 | + logService.log(clientContext.getClientByEndpoint(registration.getEndpoint()), LOG_LWM2M_INFO + ": Client is awake!"); | |
415 | + //TODO: associate endpointId with device information. | |
416 | + } | |
417 | + | |
418 | + /** | |
419 | + * #1 clientOnlyObserveAfterConnect == true | |
420 | + * - Only Observe Request to the client marked as observe from the profile configuration. | |
421 | + * #2. clientOnlyObserveAfterConnect == false | |
422 | + * После регистрации отправляю запрос на read всех ресурсов, которые после регистрации есть у клиента, | |
423 | + * а затем запрос на observe (edited) | |
424 | + * - Read Request to the client after registration to read all resource values for all objects | |
425 | + * - then Observe Request to the client marked as observe from the profile configuration. | |
426 | + * | |
427 | + * @param lwM2MClient - object with All parameters off client | |
428 | + */ | |
429 | + private void initClientTelemetry(LwM2mClient lwM2MClient) { | |
430 | + Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2MClient.getProfileId()); | |
431 | + Set<String> supportedObjects = clientContext.getSupportedIdVerInClient(lwM2MClient); | |
432 | + if (supportedObjects != null && supportedObjects.size() > 0) { | |
433 | + // #1 | |
434 | + this.sendReadRequests(lwM2MClient, profile, supportedObjects); | |
435 | + this.sendObserveRequests(lwM2MClient, profile, supportedObjects); | |
436 | + this.sendWriteAttributeRequests(lwM2MClient, profile, supportedObjects); | |
437 | +// Removed. Used only for debug. | |
438 | +// this.sendDiscoverRequests(lwM2MClient, profile, supportedObjects); | |
439 | + } | |
440 | + } | |
441 | + | |
442 | + private void sendReadRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set<String> supportedObjects) { | |
443 | + Set<String> targetIds = new HashSet<>(profile.getObserveAttr().getAttribute()); | |
444 | + targetIds.addAll(profile.getObserveAttr().getTelemetry()); | |
445 | + targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); | |
446 | + | |
447 | + CountDownLatch latch = new CountDownLatch(targetIds.size()); | |
448 | + targetIds.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId, | |
449 | + new TbLwM2MLatchCallback<>(latch, new TbLwM2MReadCallback(this, logService, lwM2MClient, versionedId)))); | |
450 | + try { | |
451 | + latch.await(); | |
452 | + } catch (InterruptedException e) { | |
453 | + log.error("[{}] Failed to await Read requests!", lwM2MClient.getEndpoint()); | |
454 | + } | |
455 | + } | |
456 | + | |
457 | + private void sendObserveRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set<String> supportedObjects) { | |
458 | + Set<String> targetIds = profile.getObserveAttr().getObserve(); | |
459 | + targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); | |
460 | + | |
461 | + CountDownLatch latch = new CountDownLatch(targetIds.size()); | |
462 | + targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId, | |
463 | + new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, logService, lwM2MClient, targetId)))); | |
464 | + try { | |
465 | + latch.await(); | |
466 | + } catch (InterruptedException e) { | |
467 | + log.error("[{}] Failed to await Observe requests!", lwM2MClient.getEndpoint()); | |
468 | + } | |
469 | + } | |
470 | + | |
471 | + private void sendWriteAttributeRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set<String> supportedObjects) { | |
472 | + Map<String, ObjectAttributes> attributesMap = profile.getObserveAttr().getAttributeLwm2m(); | |
473 | + attributesMap = attributesMap.entrySet().stream().filter(target -> isSupportedTargetId(supportedObjects, target.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); | |
474 | +// TODO: why do we need to put observe into pending read requests? | |
475 | +// lwM2MClient.getPendingReadRequests().addAll(targetIds); | |
476 | + attributesMap.forEach((targetId, params) -> sendWriteAttributesRequest(lwM2MClient, targetId, params)); | |
477 | + } | |
478 | + | |
479 | + private void sendDiscoverRequests(LwM2mClient lwM2MClient, Lwm2mDeviceProfileTransportConfiguration profile, Set<String> supportedObjects) { | |
480 | + Set<String> targetIds = profile.getObserveAttr().getAttributeLwm2m().keySet(); | |
481 | + targetIds = targetIds.stream().filter(target -> isSupportedTargetId(supportedObjects, target)).collect(Collectors.toSet()); | |
482 | +// TODO: why do we need to put observe into pending read requests? | |
483 | +// lwM2MClient.getPendingReadRequests().addAll(targetIds); | |
484 | + targetIds.forEach(targetId -> sendDiscoverRequest(lwM2MClient, targetId)); | |
485 | + } | |
486 | + | |
487 | + private void sendDiscoverRequest(LwM2mClient lwM2MClient, String targetId) { | |
488 | + TbLwM2MDiscoverRequest request = TbLwM2MDiscoverRequest.builder().versionedId(targetId).timeout(this.config.getTimeout()).build(); | |
489 | + defaultLwM2MDownlinkMsgHandler.sendDiscoverRequest(lwM2MClient, request, new TbLwM2MDiscoverCallback(logService, lwM2MClient, targetId)); | |
490 | + } | |
491 | + | |
492 | + private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId) { | |
493 | + sendReadRequest(lwM2MClient, versionedId, new TbLwM2MReadCallback(this, logService, lwM2MClient, versionedId)); | |
494 | + } | |
495 | + | |
496 | + private void sendReadRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback<ReadRequest, ReadResponse> callback) { | |
497 | + TbLwM2MReadRequest request = TbLwM2MReadRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
498 | + defaultLwM2MDownlinkMsgHandler.sendReadRequest(lwM2MClient, request, callback); | |
499 | + } | |
500 | + | |
501 | + private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId) { | |
502 | + sendObserveRequest(lwM2MClient, versionedId, new TbLwM2MObserveCallback(this, logService, lwM2MClient, versionedId)); | |
503 | + } | |
504 | + | |
505 | + private void sendObserveRequest(LwM2mClient lwM2MClient, String versionedId, DownlinkRequestCallback<ObserveRequest, ObserveResponse> callback) { | |
506 | + TbLwM2MObserveRequest request = TbLwM2MObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
507 | + defaultLwM2MDownlinkMsgHandler.sendObserveRequest(lwM2MClient, request, callback); | |
508 | + } | |
509 | + | |
510 | + private void sendWriteAttributesRequest(LwM2mClient lwM2MClient, String targetId, ObjectAttributes params) { | |
511 | + TbLwM2MWriteAttributesRequest request = TbLwM2MWriteAttributesRequest.builder().versionedId(targetId).attributes(params).timeout(this.config.getTimeout()).build(); | |
512 | + defaultLwM2MDownlinkMsgHandler.sendWriteAttributesRequest(lwM2MClient, request, new TbLwM2MWriteAttributesCallback(logService, lwM2MClient, targetId)); | |
513 | + } | |
514 | + | |
515 | + private void sendCancelObserveRequest(String versionedId, LwM2mClient client) { | |
516 | + TbLwM2MCancelObserveRequest request = TbLwM2MCancelObserveRequest.builder().versionedId(versionedId).timeout(this.config.getTimeout()).build(); | |
517 | + defaultLwM2MDownlinkMsgHandler.sendCancelObserveRequest(client, request, new TbLwM2MCancelObserveCallback(logService, client, versionedId)); | |
518 | + } | |
519 | + | |
520 | + private void updateObjectResourceValue(LwM2mClient client, LwM2mObject lwM2mObject, String pathIdVer) { | |
521 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); | |
522 | + lwM2mObject.getInstances().forEach((instanceId, instance) -> { | |
523 | + String pathInstance = pathIds.toString() + "/" + instanceId; | |
524 | + this.updateObjectInstanceResourceValue(client, instance, pathInstance); | |
525 | + }); | |
526 | + } | |
527 | + | |
528 | + private void updateObjectInstanceResourceValue(LwM2mClient client, LwM2mObjectInstance lwM2mObjectInstance, String pathIdVer) { | |
529 | + LwM2mPath pathIds = new LwM2mPath(fromVersionedIdToObjectId(pathIdVer)); | |
530 | + lwM2mObjectInstance.getResources().forEach((resourceId, resource) -> { | |
531 | + String pathRez = pathIds.toString() + "/" + resourceId; | |
532 | + this.updateResourcesValue(client, resource, pathRez); | |
533 | + }); | |
534 | + } | |
535 | + | |
536 | + /** | |
537 | + * Sending observe value of resources to thingsboard | |
538 | + * #1 Return old Value Resource from LwM2MClient | |
539 | + * #2 Update new Resources (replace old Resource Value on new Resource Value) | |
540 | + * #3 If fr_update -> UpdateFirmware | |
541 | + * #4 updateAttrTelemetry | |
542 | + * | |
543 | + * @param lwM2MClient - Registration LwM2M Client | |
544 | + * @param lwM2mResource - LwM2mSingleResource response.getContent() | |
545 | + * @param path - resource | |
546 | + */ | |
547 | + private void updateResourcesValue(LwM2mClient lwM2MClient, LwM2mResource lwM2mResource, String path) { | |
548 | + Registration registration = lwM2MClient.getRegistration(); | |
549 | + if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) { | |
550 | + if (path.equals(convertObjectIdToVersionedId(FW_NAME_ID, registration))) { | |
551 | + otaService.onCurrentFirmwareNameUpdate(lwM2MClient, (String) lwM2mResource.getValue()); | |
552 | + } else if (path.equals(convertObjectIdToVersionedId(FW_3_VER_ID, registration))) { | |
553 | + otaService.onCurrentFirmwareVersion3Update(lwM2MClient, (String) lwM2mResource.getValue()); | |
554 | + } else if (path.equals(convertObjectIdToVersionedId(FW_5_VER_ID, registration))) { | |
555 | + otaService.onCurrentFirmwareVersion5Update(lwM2MClient, (String) lwM2mResource.getValue()); | |
556 | + } else if (path.equals(convertObjectIdToVersionedId(FW_STATE_ID, registration))) { | |
557 | + otaService.onCurrentFirmwareStateUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); | |
558 | + } else if (path.equals(convertObjectIdToVersionedId(FW_RESULT_ID, registration))) { | |
559 | + otaService.onCurrentFirmwareResultUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); | |
560 | + } else if (path.equals(convertObjectIdToVersionedId(FW_DELIVERY_METHOD, registration))) { | |
561 | + otaService.onCurrentFirmwareDeliveryMethodUpdate(lwM2MClient, (Long) lwM2mResource.getValue()); | |
562 | + } | |
563 | + this.updateAttrTelemetry(registration, Collections.singleton(path)); | |
564 | + } else { | |
565 | + log.error("Fail update Resource [{}]", lwM2mResource); | |
566 | + } | |
567 | + } | |
568 | + | |
569 | + | |
570 | + /** | |
571 | + * send Attribute and Telemetry to Thingsboard | |
572 | + * #1 - get AttrName/TelemetryName with value from LwM2MClient: | |
573 | + * -- resourceId == path from LwM2MClientProfile.postAttributeProfile/postTelemetryProfile/postObserveProfile | |
574 | + * -- AttrName/TelemetryName == resourceName from ModelObject.objectModel, value from ModelObject.instance.resource(resourceId) | |
575 | + * #2 - set Attribute/Telemetry | |
576 | + * | |
577 | + * @param registration - Registration LwM2M Client | |
578 | + */ | |
579 | + private void updateAttrTelemetry(Registration registration, Set<String> paths) { | |
580 | + try { | |
581 | + ResultsAddKeyValueProto results = this.getParametersFromProfile(registration, paths); | |
582 | + SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration); | |
583 | + if (results != null && sessionInfo != null) { | |
584 | + if (results.getResultAttributes().size() > 0) { | |
585 | + this.helper.sendParametersOnThingsboardAttribute(results.getResultAttributes(), sessionInfo); | |
586 | + } | |
587 | + if (results.getResultTelemetries().size() > 0) { | |
588 | + this.helper.sendParametersOnThingsboardTelemetry(results.getResultTelemetries(), sessionInfo); | |
589 | + } | |
590 | + } | |
591 | + } catch (Exception e) { | |
592 | + log.error("UpdateAttrTelemetry", e); | |
593 | + } | |
594 | + } | |
595 | + | |
596 | + private boolean isSupportedTargetId(Set<String> supportedIds, String targetId) { | |
597 | + String[] targetIdParts = targetId.split(LWM2M_SEPARATOR_PATH); | |
598 | + if (targetIdParts.length <= 1) { | |
599 | + return false; | |
600 | + } | |
601 | + String targetIdSearch = targetIdParts[0]; | |
602 | + for (int i = 1; i < targetIdParts.length; i++) { | |
603 | + targetIdSearch += "/" + targetIdParts[i]; | |
604 | + if (supportedIds.contains(targetIdSearch)) { | |
605 | + return true; | |
606 | + } | |
607 | + } | |
608 | + return false; | |
609 | + } | |
610 | + | |
611 | + private ConcurrentHashMap<String, Object> getPathForWriteAttributes(JsonObject objectJson) { | |
612 | + ConcurrentHashMap<String, Object> pathAttributes = new Gson().fromJson(objectJson.toString(), | |
613 | + new TypeToken<ConcurrentHashMap<String, Object>>() { | |
614 | + }.getType()); | |
615 | + return pathAttributes; | |
616 | + } | |
617 | + | |
618 | + private void onDeviceUpdate(LwM2mClient lwM2MClient, Device device, Optional<DeviceProfile> deviceProfileOpt) { | |
619 | + deviceProfileOpt.ifPresent(deviceProfile -> this.onDeviceProfileUpdate(Collections.singletonList(lwM2MClient), deviceProfile)); | |
620 | + lwM2MClient.onDeviceUpdate(device, deviceProfileOpt); | |
621 | + } | |
622 | + | |
623 | + /** | |
624 | + * // * @param attributes - new JsonObject | |
625 | + * // * @param telemetry - new JsonObject | |
626 | + * | |
627 | + * @param registration - Registration LwM2M Client | |
628 | + * @param path - | |
629 | + */ | |
630 | + private ResultsAddKeyValueProto getParametersFromProfile(Registration registration, Set<String> path) { | |
631 | + if (path != null && path.size() > 0) { | |
632 | + ResultsAddKeyValueProto results = new ResultsAddKeyValueProto(); | |
633 | + var profile = clientContext.getProfile(registration); | |
634 | + List<TransportProtos.KeyValueProto> resultAttributes = new ArrayList<>(); | |
635 | + profile.getObserveAttr().getAttribute().forEach(pathIdVer -> { | |
636 | + if (path.contains(pathIdVer)) { | |
637 | + TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer, registration); | |
638 | + if (kvAttr != null) { | |
639 | + resultAttributes.add(kvAttr); | |
640 | + } | |
641 | + } | |
642 | + }); | |
643 | + List<TransportProtos.KeyValueProto> resultTelemetries = new ArrayList<>(); | |
644 | + profile.getObserveAttr().getTelemetry().forEach(pathIdVer -> { | |
645 | + if (path.contains(pathIdVer)) { | |
646 | + TransportProtos.KeyValueProto kvAttr = this.getKvToThingsboard(pathIdVer, registration); | |
647 | + if (kvAttr != null) { | |
648 | + resultTelemetries.add(kvAttr); | |
649 | + } | |
650 | + } | |
651 | + }); | |
652 | + if (resultAttributes.size() > 0) { | |
653 | + results.setResultAttributes(resultAttributes); | |
654 | + } | |
655 | + if (resultTelemetries.size() > 0) { | |
656 | + results.setResultTelemetries(resultTelemetries); | |
657 | + } | |
658 | + return results; | |
659 | + } | |
660 | + return null; | |
661 | + } | |
662 | + | |
663 | + private TransportProtos.KeyValueProto getKvToThingsboard(String pathIdVer, Registration registration) { | |
664 | + LwM2mClient lwM2MClient = this.clientContext.getClientByEndpoint(registration.getEndpoint()); | |
665 | + Map<String, String> names = clientContext.getProfile(lwM2MClient.getProfileId()).getObserveAttr().getKeyName(); | |
666 | + if (names != null && names.containsKey(pathIdVer)) { | |
667 | + String resourceName = names.get(pathIdVer); | |
668 | + if (resourceName != null && !resourceName.isEmpty()) { | |
669 | + try { | |
670 | + LwM2mResource resourceValue = LwM2mTransportUtil.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer); | |
671 | + if (resourceValue != null) { | |
672 | + ResourceModel.Type currentType = resourceValue.getType(); | |
673 | + ResourceModel.Type expectedType = this.helper.getResourceModelTypeEqualsKvProtoValueType(currentType, pathIdVer); | |
674 | + Object valueKvProto = null; | |
675 | + if (resourceValue.isMultiInstances()) { | |
676 | + valueKvProto = new JsonObject(); | |
677 | + Object finalvalueKvProto = valueKvProto; | |
678 | + Gson gson = new GsonBuilder().create(); | |
679 | + ResourceModel.Type finalCurrentType = currentType; | |
680 | + resourceValue.getInstances().forEach((k, v) -> { | |
681 | + Object val = this.converter.convertValue(v, finalCurrentType, expectedType, | |
682 | + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); | |
683 | + JsonElement element = gson.toJsonTree(val, val.getClass()); | |
684 | + ((JsonObject) finalvalueKvProto).add(String.valueOf(k), element); | |
685 | + }); | |
686 | + valueKvProto = gson.toJson(valueKvProto); | |
687 | + } else { | |
688 | + valueKvProto = this.converter.convertValue(resourceValue.getValue(), currentType, expectedType, | |
689 | + new LwM2mPath(fromVersionedIdToObjectId(pathIdVer))); | |
690 | + } | |
691 | + LwM2mOtaConvert lwM2mOtaConvert = convertOtaUpdateValueToString(pathIdVer, valueKvProto, currentType); | |
692 | + valueKvProto = lwM2mOtaConvert.getValue(); | |
693 | + currentType = lwM2mOtaConvert.getCurrentType(); | |
694 | + return valueKvProto != null ? this.helper.getKvAttrTelemetryToThingsboard(currentType, resourceName, valueKvProto, resourceValue.isMultiInstances()) : null; | |
695 | + } | |
696 | + } catch (Exception e) { | |
697 | + log.error("Failed to add parameters.", e); | |
698 | + } | |
699 | + } | |
700 | + } else { | |
701 | + log.error("Failed to add parameters. path: [{}], names: [{}]", pathIdVer, names); | |
702 | + } | |
703 | + return null; | |
704 | + } | |
705 | + | |
706 | + @Override | |
707 | + public void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request) { | |
708 | + if (request.getNode() instanceof LwM2mResource) { | |
709 | + this.updateResourcesValue(client, ((LwM2mResource) request.getNode()), path); | |
710 | + } else if (request.getNode() instanceof LwM2mObjectInstance) { | |
711 | + ((LwM2mObjectInstance) request.getNode()).getResources().forEach((resId, resource) -> { | |
712 | + this.updateResourcesValue(client, resource, path + "/" + resId); | |
713 | + }); | |
714 | + } | |
715 | + | |
716 | + } | |
717 | + | |
718 | + //TODO: review and optimize the logic to minimize number of the requests to device. | |
719 | + private void onDeviceProfileUpdate(List<LwM2mClient> clients, DeviceProfile deviceProfile) { | |
720 | + var oldProfile = clientContext.getProfile(deviceProfile.getUuidId()); | |
721 | + if (clientContext.profileUpdate(deviceProfile) != null) { | |
722 | + // #1 | |
723 | + TelemetryMappingConfiguration oldTelemetryParams = oldProfile.getObserveAttr(); | |
724 | + Set<String> attributeSetOld = oldTelemetryParams.getAttribute(); | |
725 | + Set<String> telemetrySetOld = oldTelemetryParams.getTelemetry(); | |
726 | + Set<String> observeOld = oldTelemetryParams.getObserve(); | |
727 | + Map<String, String> keyNameOld = oldTelemetryParams.getKeyName(); | |
728 | + Map<String, ObjectAttributes> attributeLwm2mOld = oldTelemetryParams.getAttributeLwm2m(); | |
729 | + | |
730 | + var newProfile = clientContext.getProfile(deviceProfile.getUuidId()); | |
731 | + TelemetryMappingConfiguration newTelemetryParams = newProfile.getObserveAttr(); | |
732 | + Set<String> attributeSetNew = newTelemetryParams.getAttribute(); | |
733 | + Set<String> telemetrySetNew = newTelemetryParams.getTelemetry(); | |
734 | + Set<String> observeNew = newTelemetryParams.getObserve(); | |
735 | + Map<String, String> keyNameNew = newTelemetryParams.getKeyName(); | |
736 | + Map<String, ObjectAttributes> attributeLwm2mNew = newTelemetryParams.getAttributeLwm2m(); | |
737 | + | |
738 | + Set<String> observeToAdd = diffSets(observeOld, observeNew); | |
739 | + Set<String> observeToRemove = diffSets(observeNew, observeOld); | |
740 | + | |
741 | + Set<String> newObjectsToRead = new HashSet<>(); | |
742 | + | |
743 | + // #3.1 | |
744 | + if (!attributeSetOld.equals(attributeSetNew)) { | |
745 | + newObjectsToRead.addAll(diffSets(attributeSetOld, attributeSetNew)); | |
746 | + } | |
747 | + // #3.2 | |
748 | + if (!telemetrySetOld.equals(telemetrySetNew)) { | |
749 | + newObjectsToRead.addAll(diffSets(telemetrySetOld, telemetrySetNew)); | |
750 | + } | |
751 | + // #3.3 | |
752 | + if (!keyNameOld.equals(keyNameNew)) { | |
753 | + ParametersAnalyzeResult keyNameChange = this.getAnalyzerKeyName(keyNameOld, keyNameNew); | |
754 | + newObjectsToRead.addAll(keyNameChange.getPathPostParametersAdd()); | |
755 | + } | |
756 | + | |
757 | + // #3.4, #6 | |
758 | + if (!attributeLwm2mOld.equals(attributeLwm2mNew)) { | |
759 | + this.compareAndSendWriteAttributes(clients, attributeLwm2mOld, attributeLwm2mNew); | |
760 | + } | |
761 | + | |
762 | + // #4.1 add | |
763 | + if (!newObjectsToRead.isEmpty()) { | |
764 | + Set<String> newObjectsToReadButNotNewInObserve = diffSets(observeToAdd, newObjectsToRead); | |
765 | + // update value in Resources | |
766 | + for (String versionedId : newObjectsToReadButNotNewInObserve) { | |
767 | + clients.forEach(client -> sendReadRequest(client, versionedId)); | |
768 | + } | |
769 | + } | |
770 | + | |
771 | + // Calculating difference between old and new flags. | |
772 | + if (!observeToAdd.isEmpty()) { | |
773 | + for (String targetId : observeToAdd) { | |
774 | + clients.forEach(client -> sendObserveRequest(client, targetId)); | |
775 | + } | |
776 | + } | |
777 | + if (!observeToRemove.isEmpty()) { | |
778 | + for (String targetId : observeToRemove) { | |
779 | + clients.forEach(client -> sendCancelObserveRequest(targetId, client)); | |
780 | + } | |
781 | + } | |
782 | + } | |
783 | + } | |
784 | + | |
785 | + /** | |
786 | + * Returns new set with elements that are present in set B(new) but absent in set A(old). | |
787 | + */ | |
788 | + private static <T> Set<T> diffSets(Set<T> a, Set<T> b) { | |
789 | + return b.stream().filter(p -> !a.contains(p)).collect(Collectors.toSet()); | |
790 | + } | |
791 | + | |
792 | + private ParametersAnalyzeResult getAnalyzerKeyName(Map<String, String> keyNameOld, Map<String, String> keyNameNew) { | |
793 | + ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); | |
794 | + Set<String> paths = keyNameNew.entrySet() | |
795 | + .stream() | |
796 | + .filter(e -> !e.getValue().equals(keyNameOld.get(e.getKey()))) | |
797 | + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)).keySet(); | |
798 | + analyzerParameters.setPathPostParametersAdd(paths); | |
799 | + return analyzerParameters; | |
800 | + } | |
801 | + | |
802 | + /** | |
803 | + * #6.1 - send update WriteAttribute | |
804 | + * #6.2 - send empty WriteAttribute | |
805 | + */ | |
806 | + private void compareAndSendWriteAttributes(List<LwM2mClient> clients, Map<String, ObjectAttributes> lwm2mAttributesOld, Map<String, ObjectAttributes> lwm2mAttributesNew) { | |
807 | + ParametersAnalyzeResult analyzerParameters = new ParametersAnalyzeResult(); | |
808 | + Set<String> pathOld = lwm2mAttributesOld.keySet(); | |
809 | + Set<String> pathNew = lwm2mAttributesNew.keySet(); | |
810 | + analyzerParameters.setPathPostParametersAdd(pathNew | |
811 | + .stream().filter(p -> !pathOld.contains(p)).collect(Collectors.toSet())); | |
812 | + analyzerParameters.setPathPostParametersDel(pathOld | |
813 | + .stream().filter(p -> !pathNew.contains(p)).collect(Collectors.toSet())); | |
814 | + Set<String> pathCommon = pathNew | |
815 | + .stream().filter(pathOld::contains).collect(Collectors.toSet()); | |
816 | + Set<String> pathCommonChange = pathCommon | |
817 | + .stream().filter(p -> !lwm2mAttributesOld.get(p).equals(lwm2mAttributesNew.get(p))).collect(Collectors.toSet()); | |
818 | + analyzerParameters.getPathPostParametersAdd().addAll(pathCommonChange); | |
819 | + // #6 | |
820 | + // #6.2 | |
821 | + if (analyzerParameters.getPathPostParametersAdd().size() > 0) { | |
822 | + clients.forEach(client -> { | |
823 | + Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client); | |
824 | + Set<String> pathSend = analyzerParameters.getPathPostParametersAdd().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) | |
825 | + .collect(Collectors.toUnmodifiableSet()); | |
826 | + if (!pathSend.isEmpty()) { | |
827 | + pathSend.forEach(target -> sendWriteAttributesRequest(client, target, lwm2mAttributesNew.get(target))); | |
828 | + } | |
829 | + }); | |
830 | + } | |
831 | + // #6.2 | |
832 | + if (analyzerParameters.getPathPostParametersDel().size() > 0) { | |
833 | + clients.forEach(client -> { | |
834 | + Set<String> clientObjects = clientContext.getSupportedIdVerInClient(client); | |
835 | + Set<String> pathSend = analyzerParameters.getPathPostParametersDel().stream().filter(target -> clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1])) | |
836 | + .collect(Collectors.toUnmodifiableSet()); | |
837 | + if (!pathSend.isEmpty()) { | |
838 | + pathSend.forEach(target -> sendWriteAttributesRequest(client, target, new ObjectAttributes())); | |
839 | + } | |
840 | + }); | |
841 | + } | |
842 | + } | |
843 | + | |
844 | + /** | |
845 | + * @param updateCredentials - Credentials include config only security Client (without config attr/telemetry...) | |
846 | + * config attr/telemetry... in profile | |
847 | + */ | |
848 | + @Override | |
849 | + public void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials) { | |
850 | + log.info("[{}] idList [{}] valueList updateCredentials", updateCredentials.getCredentialsIdList(), updateCredentials.getCredentialsValueList()); | |
851 | + } | |
852 | + | |
853 | + /** | |
854 | + * @param lwM2MClient - | |
855 | + * @return SessionInfoProto - | |
856 | + */ | |
857 | + private SessionInfoProto getSessionInfo(LwM2mClient lwM2MClient) { | |
858 | + if (lwM2MClient != null && lwM2MClient.getSession() != null) { | |
859 | + return lwM2MClient.getSession(); | |
860 | + } | |
861 | + return null; | |
862 | + } | |
863 | + | |
864 | + /** | |
865 | + * @param registration - Registration LwM2M Client | |
866 | + * @return - sessionInfo after access connect client | |
867 | + */ | |
868 | + public SessionInfoProto getSessionInfoOrCloseSession(Registration registration) { | |
869 | + return getSessionInfo(clientContext.getClientByEndpoint(registration.getEndpoint())); | |
870 | + } | |
871 | + | |
872 | + /** | |
873 | + * if sessionInfo removed from sessions, then new registerAsyncSession | |
874 | + * | |
875 | + * @param sessionInfo - | |
876 | + */ | |
877 | + private void reportActivityAndRegister(SessionInfoProto sessionInfo) { | |
878 | + if (sessionInfo != null && transportService.reportActivity(sessionInfo) == null) { | |
879 | + transportService.registerAsyncSession(sessionInfo, new LwM2mSessionMsgListener(this, attributesService, rpcHandler, sessionInfo)); | |
880 | + this.reportActivitySubscription(sessionInfo); | |
881 | + } | |
882 | + } | |
883 | + | |
884 | + private void reportActivity() { | |
885 | + clientContext.getLwM2mClients().forEach(client -> reportActivityAndRegister(client.getSession())); | |
886 | + } | |
887 | + | |
888 | + /** | |
889 | + * #1. !!! sharedAttr === profileAttr !!! | |
890 | + * - If there is a difference in values between the current resource values and the shared attribute values | |
891 | + * - when the client connects to the server | |
892 | + * #1.1 get attributes name from profile include name resources in ModelObject if resource isWritable | |
893 | + * #1.2 #1 size > 0 => send Request getAttributes to thingsboard | |
894 | + * #2. FirmwareAttribute subscribe: | |
895 | + * | |
896 | + * @param lwM2MClient - LwM2M Client | |
897 | + */ | |
898 | + public void initAttributes(LwM2mClient lwM2MClient) { | |
899 | + Map<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); | |
900 | + if (!keyNamesMap.isEmpty()) { | |
901 | + Set<String> keysToFetch = new HashSet<>(keyNamesMap.values()); | |
902 | + keysToFetch.removeAll(OtaPackageUtil.ALL_FW_ATTRIBUTE_KEYS); | |
903 | + keysToFetch.removeAll(OtaPackageUtil.ALL_SW_ATTRIBUTE_KEYS); | |
904 | + DonAsynchron.withCallback(attributesService.getSharedAttributes(lwM2MClient, keysToFetch), | |
905 | + v -> attributesService.onAttributesUpdate(lwM2MClient, v), | |
906 | + t -> log.error("[{}] Failed to get attributes", lwM2MClient.getEndpoint(), t), | |
907 | + executor); | |
908 | + } | |
909 | + } | |
910 | + | |
911 | + private TransportProtos.GetOtaPackageRequestMsg createOtaPackageRequestMsg(SessionInfoProto sessionInfo, String nameFwSW) { | |
912 | + return TransportProtos.GetOtaPackageRequestMsg.newBuilder() | |
913 | + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
914 | + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) | |
915 | + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
916 | + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
917 | + .setType(nameFwSW) | |
918 | + .build(); | |
919 | + } | |
920 | + | |
921 | + private Map<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) { | |
922 | + Lwm2mDeviceProfileTransportConfiguration profile = clientContext.getProfile(lwM2MClient.getProfileId()); | |
923 | + return profile.getObserveAttr().getKeyName(); | |
924 | + } | |
925 | + | |
926 | + public LwM2MTransportServerConfig getConfig() { | |
927 | + return this.config; | |
928 | + } | |
929 | + | |
930 | + private void reportActivitySubscription(TransportProtos.SessionInfoProto sessionInfo) { | |
931 | + transportService.process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder() | |
932 | + .setAttributeSubscription(true) | |
933 | + .setRpcSubscription(true) | |
934 | + .setLastActivityTime(System.currentTimeMillis()) | |
935 | + .build(), TransportServiceCallback.EMPTY); | |
936 | + } | |
937 | +} | ... | ... |
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/LwM2mUplinkMsgHandler.java
renamed from
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportMsgHandler.java
... | ... | @@ -13,9 +13,10 @@ |
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.server; | |
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.WriteRequest; | |
19 | 20 | import org.eclipse.leshan.core.response.ReadResponse; |
20 | 21 | import org.eclipse.leshan.server.registration.Registration; |
21 | 22 | import org.thingsboard.server.common.data.Device; |
... | ... | @@ -23,12 +24,11 @@ import org.thingsboard.server.common.data.DeviceProfile; |
23 | 24 | import org.thingsboard.server.gen.transport.TransportProtos; |
24 | 25 | import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig; |
25 | 26 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
26 | -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientRpcRequest; | |
27 | 27 | |
28 | 28 | import java.util.Collection; |
29 | 29 | import java.util.Optional; |
30 | 30 | |
31 | -public interface LwM2mTransportMsgHandler { | |
31 | +public interface LwM2mUplinkMsgHandler { | |
32 | 32 | |
33 | 33 | void onRegistered(Registration registration, Collection<Observation> previousObsersations); |
34 | 34 | |
... | ... | @@ -38,33 +38,23 @@ public interface LwM2mTransportMsgHandler { |
38 | 38 | |
39 | 39 | void onSleepingDev(Registration registration); |
40 | 40 | |
41 | - void setCancelObservationsAll(Registration registration); | |
42 | - | |
43 | - void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, LwM2mClientRpcRequest rpcRequest); | |
44 | - | |
45 | - void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); | |
41 | + void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response); | |
46 | 42 | |
47 | 43 | void onDeviceProfileUpdate(TransportProtos.SessionInfoProto sessionInfo, DeviceProfile deviceProfile); |
48 | 44 | |
49 | 45 | void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt); |
50 | 46 | |
51 | - void onResourceUpdate (Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt); | |
47 | + void onResourceUpdate(Optional<TransportProtos.ResourceUpdateMsg> resourceUpdateMsgOpt); | |
52 | 48 | |
53 | 49 | void onResourceDelete(Optional<TransportProtos.ResourceDeleteMsg> resourceDeleteMsgOpt); |
54 | 50 | |
55 | - void onToDeviceRpcRequest(TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest, TransportProtos.SessionInfoProto sessionInfo); | |
56 | - | |
57 | - void onToDeviceRpcResponse(TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponse, TransportProtos.SessionInfoProto sessionInfo); | |
58 | - | |
59 | - void onToServerRpcResponse(TransportProtos.ToServerRpcResponseMsg toServerResponse); | |
60 | - | |
61 | 51 | void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); |
62 | 52 | |
63 | 53 | void onAwakeDev(Registration registration); |
64 | 54 | |
65 | - void sendLogsToThingsboard(LwM2mClient client, String msg); | |
55 | + void onWriteResponseOk(LwM2mClient client, String path, WriteRequest request); | |
66 | 56 | |
67 | - void sendLogsToThingsboard(String registrationId, String msg); | |
57 | + void onToTransportUpdateCredentials(TransportProtos.ToTransportUpdateCredentialsProto updateCredentials); | |
68 | 58 | |
69 | 59 | LwM2MTransportServerConfig getConfig(); |
70 | 60 | } | ... | ... |
... | ... | @@ -117,7 +117,7 @@ public class LwM2mValueConverterImpl implements LwM2mValueConverter { |
117 | 117 | switch (currentType) { |
118 | 118 | case INTEGER: |
119 | 119 | log.debug("Trying to convert long value {} to date", value); |
120 | - /** let's assume we received the millisecond since 1970/1/1 */ | |
120 | + /* let's assume we received the millisecond since 1970/1/1 */ | |
121 | 121 | return new Date(((Number) value).longValue() * 1000L); |
122 | 122 | case STRING: |
123 | 123 | log.debug("Trying to convert string value {} to date", value); | ... | ... |
... | ... | @@ -142,12 +142,10 @@ transport: |
142 | 142 | timeout: "${LWM2M_TIMEOUT:120000}" |
143 | 143 | recommended_ciphers: "${LWM2M_RECOMMENDED_CIPHERS:false}" |
144 | 144 | recommended_supported_groups: "${LWM2M_RECOMMENDED_SUPPORTED_GROUPS:true}" |
145 | - response_pool_size: "${LWM2M_RESPONSE_POOL_SIZE:100}" | |
146 | - registered_pool_size: "${LWM2M_REGISTERED_POOL_SIZE:10}" | |
147 | - registration_store_pool_size: "${LWM2M_REGISTRATION_STORE_POOL_SIZE:100}" | |
145 | + uplink_pool_size: "${LWM2M_UPLINK_POOL_SIZE:10}" | |
146 | + downlink_pool_size: "${LWM2M_DOWNLINK_POOL_SIZE:10}" | |
147 | + ota_pool_size: "${LWM2M_OTA_POOL_SIZE:10}" | |
148 | 148 | clean_period_in_sec: "${LWM2M_CLEAN_PERIOD_IN_SEC:2}" |
149 | - update_registered_pool_size: "${LWM2M_UPDATE_REGISTERED_POOL_SIZE:10}" | |
150 | - un_registered_pool_size: "${LWM2M_UN_REGISTERED_POOL_SIZE:10}" | |
151 | 149 | log_max_length: "${LWM2M_LOG_MAX_LENGTH:100}" |
152 | 150 | # Use redis for Security and Registration stores |
153 | 151 | redis.enabled: "${LWM2M_REDIS_ENABLED:false}" | ... | ... |
... | ... | @@ -89,6 +89,13 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V |
89 | 89 | objectsList: [this.objectsList], |
90 | 90 | objectLwm2m: [''] |
91 | 91 | }); |
92 | + this.lwm2mListFormGroup.valueChanges.subscribe((value) => { | |
93 | + let formValue = null; | |
94 | + if (this.lwm2mListFormGroup.valid) { | |
95 | + formValue = value; | |
96 | + } | |
97 | + this.propagateChange(formValue); | |
98 | + }); | |
92 | 99 | } |
93 | 100 | |
94 | 101 | private updateValidators = (): void => { |
... | ... | @@ -142,7 +149,7 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V |
142 | 149 | this.objectsList = []; |
143 | 150 | this.modelValue = []; |
144 | 151 | } |
145 | - this.lwm2mListFormGroup.get('objectsList').setValue(this.objectsList, {emitEvents: false}); | |
152 | + this.lwm2mListFormGroup.patchValue({objectsList: this.objectsList}, {emitEvent: false}); | |
146 | 153 | this.dirty = false; |
147 | 154 | } |
148 | 155 | } |
... | ... | @@ -195,9 +202,9 @@ export class Lwm2mObjectListComponent implements ControlValueAccessor, OnInit, V |
195 | 202 | } |
196 | 203 | } |
197 | 204 | |
198 | - private clear = (): void => { | |
205 | + private clear() { | |
199 | 206 | this.searchText = ''; |
200 | - this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null); | |
207 | + this.lwm2mListFormGroup.get('objectLwm2m').patchValue(null, {emitEvent: false}); | |
201 | 208 | setTimeout(() => { |
202 | 209 | this.objectInput.nativeElement.blur(); |
203 | 210 | this.objectInput.nativeElement.focus(); | ... | ... |