diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 9bfb067bbd..0950d66024 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -102,6 +102,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClient import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState.ON_UPDATE_SUCCESS; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; +import static org.thingsboard.server.transport.lwm2m.ota.AbstractOtaLwM2MIntegrationTest.CLIENT_LWM2M_SETTINGS_19; @TestPropertySource(properties = { "transport.lwm2m.enabled=true", @@ -382,6 +383,16 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte transportConfiguration.setBootstrap(bootstrapServerCredentials); return transportConfiguration; } + protected Lwm2mDeviceProfileTransportConfiguration getTransportConfiguration19(String observeAttr, List bootstrapServerCredentials) { + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = new Lwm2mDeviceProfileTransportConfiguration(); + TelemetryMappingConfiguration observeAttrConfiguration = JacksonUtil.fromString(observeAttr, TelemetryMappingConfiguration.class); + OtherConfiguration clientLwM2mSettings = JacksonUtil.fromString(CLIENT_LWM2M_SETTINGS_19, OtherConfiguration.class); + transportConfiguration.setBootstrapServerUpdateEnable(true); + transportConfiguration.setObserveAttr(observeAttrConfiguration); + transportConfiguration.setClientLwM2mSettings(clientLwM2mSettings); + transportConfiguration.setBootstrap(bootstrapServerCredentials); + return transportConfiguration; + } protected List getBootstrapServerCredentialsNoSec(LwM2MProfileBootstrapConfigType bootstrapConfigType) { List bootstrap = new ArrayList<>(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java index 41a2259790..4f12c6d819 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mBinaryAppDataContainer.java @@ -84,8 +84,8 @@ public class LwM2mBinaryAppDataContainer extends BaseInstanceEnabler implements try { if (id != null) this.setId(id); executorService.scheduleWithFixedDelay(() -> { - fireResourceChange(0); - fireResourceChange(2); +// fireResourceChange(0); +// fireResourceChange(2); } , 1, 1, TimeUnit.SECONDS); // 1 sec // , 1800000, 1800000, TimeUnit.MILLISECONDS); // 30 MIN diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java index 4d56e891cc..52b3946072 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/AbstractOtaLwM2MIntegrationTest.java @@ -16,10 +16,14 @@ package org.thingsboard.server.transport.lwm2m.ota; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.binary.Hex; import lombok.extern.slf4j.Slf4j; +import org.eclipse.leshan.core.ResponseCode; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -31,25 +35,29 @@ import org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries; import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE; import static org.thingsboard.server.common.data.ota.OtaPackageType.SOFTWARE; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.RESOURCE_ID_11; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.*; @Slf4j @DaoSqlTest public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest { - private final String[] RESOURCES_OTA = new String[]{"3.xml", "5.xml", "9.xml"}; + private final String[] RESOURCES_OTA = new String[]{"3.xml", "5.xml", "9.xml", "19.xml"}; protected static final String CLIENT_ENDPOINT_WITHOUT_FW_INFO = "WithoutFirmwareInfoDevice"; protected static final String CLIENT_ENDPOINT_OTA5 = "Ota5_Device"; protected static final String CLIENT_ENDPOINT_OTA9 = "Ota9_Device"; protected List expectedStatuses; - protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA5 = " {\n" + @@ -78,6 +86,54 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg " \"attributeLwm2m\": {}\n" + " }"; + protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA5_19 = + + " {\n" + + " \"keyName\": {\n" + + " \"/5_1.2/0/3\": \"state\",\n" + + " \"/5_1.2/0/5\": \"updateResult\",\n" + + " \"/5_1.2/0/6\": \"pkgname\",\n" + + " \"/5_1.2/0/7\": \"pkgversion\",\n" + + " \"/5_1.2/0/9\": \"firmwareUpdateDeliveryMethod\",\n" + + " \"/19_1.1/0/0\": \"dataRead\",\n" + + " \"/19_1.1/65534/0\": \"dataFotaAttr\"\n" + + " },\n" + + " \"observe\": [\n" + + " \"/5_1.2/0/3\",\n" + + " \"/5_1.2/0/5\",\n" + + " \"/5_1.2/0/6\",\n" + + " \"/5_1.2/0/7\",\n" + + " \"/5_1.2/0/9\",\n" + + " \"/19_1.1/0/0\",\n" + + " \"/19_1.1/65534/0\"\n" + + " ],\n" + + " \"attribute\": [],\n" + + " \"telemetry\": [\n" + + " \"/5_1.2/0/3\",\n" + + " \"/5_1.2/0/5\",\n" + + " \"/5_1.2/0/6\",\n" + + " \"/5_1.2/0/7\",\n" + + " \"/5_1.2/0/9\",\n" + + " \"/19_1.1/0/0\",\n" + + " \"/19_1.1/65534/0\"\n" + + " ],\n" + + " \"attributeLwm2m\": {}\n" + + " }"; + + public static final String CLIENT_LWM2M_SETTINGS_19 = + " {\n" + + " \"useObject19ForOta\": true,\n" + + " \"edrxCycle\": null,\n" + + " \"powerMode\": \"DRX\",\n" + + " \"fwUpdateResource\": null,\n" + + " \"fwUpdateStrategy\": 1,\n" + + " \"psmActivityTimer\": null,\n" + + " \"swUpdateResource\": null,\n" + + " \"swUpdateStrategy\": 1,\n" + + " \"pagingTransmissionWindow\": null,\n" + + " \"clientOnlyObserveAfterConnect\": 1\n" + + " }"; + protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA9 = " {\n" + @@ -159,6 +215,13 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg log.warn("Fetched telemetry by API for deviceId {}, list size {}, tsKvEntries {}", deviceId, tsKvEntries.size(), tsKvEntries); return tsKvEntries; } + protected List getFwSwParamsObject19TelemetryFromAPI(UUID deviceId) throws Exception { + String type_state = "dataFotaAttr"; + final List tsKvEntries = toTimeseries(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?orderBy=ASC&keys=" + type_state + "&startTs=0&endTs=" + System.currentTimeMillis(), new TypeReference<>() { + })); + log.warn("Fetched telemetry by API for deviceId {}, list size {}, tsKvEntries {}", deviceId, tsKvEntries.size(), tsKvEntries); + return tsKvEntries; + } protected boolean predicateForStatuses(List ts) { List statuses = ts.stream() @@ -169,4 +232,27 @@ public abstract class AbstractOtaLwM2MIntegrationTest extends AbstractLwM2MInteg log.warn("{}", statuses); return statuses.containsAll(expectedStatuses); } + + protected void resultReadOtaParams_19(String resourceIdVer, OtaPackageInfo otaPackageInfo) throws Exception { + String actualResult = sendRPCById(resourceIdVer); + ObjectNode rpcActualResult = JacksonUtil.fromString(actualResult, ObjectNode.class); + assertEquals(ResponseCode.CONTENT.getName(), rpcActualResult.get("result").asText()); + String valStr = rpcActualResult.get("value").asText(); + String start = "{ id=0 value="; + String valHexDec = valStr.substring(valStr.indexOf(start) + start.length(), (valStr.indexOf("}"))); + String valNode = new String(Hex.decodeHex((valHexDec).toCharArray())); + ObjectNode actualResultVal = JacksonUtil.fromString(valNode, ObjectNode.class); + assert actualResultVal != null; + assertEquals(otaPackageInfo.getTitle(), actualResultVal.get(OTA_INFO_19_TITLE).asText()); + assertEquals(otaPackageInfo.getVersion(), actualResultVal.get(OTA_INFO_19_VERSION).asText()); + assertEquals(otaPackageInfo.getChecksum(), actualResultVal.get(OTA_INFO_19_FILE_CHECKSUM256).asText()); + assertEquals(otaPackageInfo.getFileName(), actualResultVal.get(OTA_INFO_19_FILE_NAME).asText()); + assertEquals(Optional.of(otaPackageInfo.getDataSize()), Optional.of((long) actualResultVal.get(OTA_INFO_19_FILE_SIZE).asInt())); + } + + private String sendRPCById(String path) throws Exception { + String setRpcRequest = "{\"method\": \"Read\", \"params\": {\"id\": \"" + path + "\"}}"; + return doPostAsync("/api/plugins/rpc/twoway/" + lwM2MTestClient.getDeviceIdStr(), setRpcRequest, String.class, status().isOk()); + } + } diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java index 587eba6aa3..c5d0720042 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/Ota5LwM2MIntegrationTest.java @@ -16,17 +16,25 @@ package org.thingsboard.server.transport.lwm2m.ota.sql; import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.github.dockerjava.zerodep.shaded.org.apache.commons.codec.binary.Hex; +import jakarta.validation.constraints.AssertTrue; import lombok.extern.slf4j.Slf4j; +import org.assertj.core.api.Assertions; +import org.eclipse.leshan.core.ResponseCode; import org.junit.Assert; import org.junit.Test; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.OtaPackageInfo; import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MDeviceCredentials; import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus; import org.thingsboard.server.transport.lwm2m.ota.AbstractOtaLwM2MIntegrationTest; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; import java.util.Arrays; import java.util.Collections; @@ -36,6 +44,9 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.thingsboard.rest.client.utils.RestJsonConverter.toTimeseries; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.DOWNLOADING; @@ -44,7 +55,9 @@ import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.INIT import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.QUEUED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATED; import static org.thingsboard.server.common.data.ota.OtaPackageUpdateStatus.UPDATING; +import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.*; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfileBootstrapConfigType.NONE; +import static org.thingsboard.server.transport.lwm2m.server.ota.DefaultLwM2MOtaUpdateService.FW_INSTANCE_ID; @Slf4j public class Ota5LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { @@ -107,4 +120,50 @@ public class Ota5LwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest { } + /** + * ObjectId = 19 + * { + * "title" : "BC68J01", + * "version" : "10", + * "fileChecksumSHA256" : "f2a08d4963e981c78f2a99f62d8439af4437a72ea7267a8c01d013c072c01ded", + * "fileSize" : 59832, + * "fileName" : "FW_BC68JAR01A10_TO_BC68JAR01A09_09.bin", + * "packageType" : "FIRMWARE" + * } + * to base64 + * /5/0/5 -> Update Result (Res); 5/0/3 -> State; + * => ((Res>=0 && Res<=9) && State=0) + * => Write to Package/Write to Package URI -> DOWNLOADING ((Res>=0 && Res<=9) && State=1) + * => Download Finished -> DOWNLOADED ((Res==0 || Res=8) && State=2) + * => Executable resource Update is triggered / Initiate Firmware Update -> UPDATING (Res=0 && State=3) + * => Update Successful [Res==1] + * => Start / Res=0 -> "IDLE" .... + * @throws Exception + */ + @Test + public void testFirmwareUpdateByObject5WithObject19_Ok() throws Exception { + Lwm2mDeviceProfileTransportConfiguration transportConfiguration = getTransportConfiguration19(OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA5_19, getBootstrapServerCredentialsNoSec(NONE)); + DeviceProfile deviceProfile = createLwm2mDeviceProfile("profileFor" + this.CLIENT_ENDPOINT_OTA5, transportConfiguration); + LwM2MDeviceCredentials deviceCredentials = getDeviceCredentialsNoSec(createNoSecClientCredentials(this.CLIENT_ENDPOINT_OTA5)); + final Device device = createLwm2mDevice(deviceCredentials, this.CLIENT_ENDPOINT_OTA5, deviceProfile.getId()); + createNewClient(SECURITY_NO_SEC, null, false, this.CLIENT_ENDPOINT_OTA5, device.getId().getId().toString()); + awaitObserveReadAll(6, device.getId().getId().toString()); + + OtaPackageInfo otaPackageInfo = createFirmware("fw.v.1.5.0-update", deviceProfile.getId()); + device.setFirmwareId(otaPackageInfo.getId()); + final Device savedDevice = doPost("/api/device", device, Device.class); + + assertThat(savedDevice).as("saved device").isNotNull(); + assertThat(getDeviceFromAPI(device.getId().getId())).as("fetched device").isEqualTo(savedDevice); + + expectedStatuses = Arrays.asList(QUEUED, INITIATED, DOWNLOADING, DOWNLOADED, UPDATING, UPDATED); + List ts = await("await on timeseries for FW") + .atMost(TIMEOUT, TimeUnit.SECONDS) + .until(() -> getFwSwStateTelemetryFromAPI(device.getId().getId(), "fw_state"), this::predicateForStatuses); + + String ver_Id_19 = lwM2MTestClient.getLeshanClient().getObjectTree().getModel().getObjectModel(BINARY_APP_DATA_CONTAINER).version; + String resourceIdVer = "/" + BINARY_APP_DATA_CONTAINER + "_" + ver_Id_19 + "/" + FW_INSTANCE_ID + "/" + RESOURCE_ID_0; + resultReadOtaParams_19(resourceIdVer, otaPackageInfo); + log.warn("Object5: Got the ts: {}", ts); + } } diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java index 34b5e7c09e..802d79dd68 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/ota/DefaultLwM2MOtaUpdateService.java @@ -108,12 +108,14 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl public static final String FW_RESULT_ID = "/5/0/5"; public static final String FW_NAME_ID = "/5/0/6"; public static final String FW_VER_ID = "/5/0/7"; - public static final String FW_INFO_19_INSTANCE_ID = "/19/65534"; - public static final String SW_INFO_19_INSTANCE_ID = "/19/65535"; + public static final int FW_INSTANCE_ID = 65534; + public static final String FW_INFO_19_INSTANCE_ID = "/19/" + FW_INSTANCE_ID; + public static final int SW_INSTANCE_ID = 65535; + public static final String SW_INFO_19_INSTANCE_ID = "/19/" + SW_INSTANCE_ID; public static final String OTA_INFO_19_TITLE = "title"; public static final String OTA_INFO_19_VERSION = "version"; - public static final String OTA_INFO_19_FILE_CHECKSUM256 = "fileChecksumSHA256"; - public static final String OTA_INFO_19_FILE_SIZE = "fileSize"; + public static final String OTA_INFO_19_FILE_CHECKSUM256 = "checksum"; + public static final String OTA_INFO_19_FILE_SIZE = "dataSize"; public static final String OTA_INFO_19_FILE_NAME = "fileName"; /**