lwm2m: add test FW with objectId = 19

This commit is contained in:
nickAS21 2025-04-12 14:39:29 +03:00
parent f722618aa0
commit ed25c79655
5 changed files with 166 additions and 8 deletions

View File

@ -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<LwM2MBootstrapServerCredential> 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<LwM2MBootstrapServerCredential> getBootstrapServerCredentialsNoSec(LwM2MProfileBootstrapConfigType bootstrapConfigType) {
List<LwM2MBootstrapServerCredential> bootstrap = new ArrayList<>();

View File

@ -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

View File

@ -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<OtaPackageUpdateStatus> 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<TsKvEntry> getFwSwParamsObject19TelemetryFromAPI(UUID deviceId) throws Exception {
String type_state = "dataFotaAttr";
final List<TsKvEntry> 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<TsKvEntry> ts) {
List<OtaPackageUpdateStatus> 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());
}
}

View File

@ -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<TsKvEntry> 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);
}
}

View File

@ -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";
/**