lwm2m: add params FOTA to object 19
This commit is contained in:
parent
8ef931f754
commit
5f2bb68bc8
@ -113,6 +113,7 @@ import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.SHOR
|
|||||||
import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.STEP;
|
import static org.eclipse.leshan.core.link.lwm2m.attributes.LwM2mAttributes.STEP;
|
||||||
import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK;
|
import static org.eclipse.leshan.core.model.ResourceModel.Type.OBJLNK;
|
||||||
import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
|
import static org.eclipse.leshan.core.model.ResourceModel.Type.OPAQUE;
|
||||||
|
import static org.thingsboard.server.common.transport.util.JsonUtils.isBase64;
|
||||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody;
|
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.convertMultiResourceValuesFromRpcBody;
|
||||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.createModelsDefault;
|
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.createModelsDefault;
|
||||||
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fromVersionedIdToObjectId;
|
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.fromVersionedIdToObjectId;
|
||||||
@ -399,7 +400,8 @@ public class DefaultLwM2mDownlinkMsgHandler extends LwM2MExecutorAwareService im
|
|||||||
try {
|
try {
|
||||||
Object valueForMultiResource = request.getValue();
|
Object valueForMultiResource = request.getValue();
|
||||||
if (resultIds.isResourceInstance()) {
|
if (resultIds.isResourceInstance()) {
|
||||||
String resourceInstance = "{" + resultIds.getResourceInstanceId() + "=" + request.getValue() + "}";
|
String valueStr = isBase64(request.getValue().toString()) ? "\"" + request.getValue() + "\"" : request.getValue().toString();
|
||||||
|
String resourceInstance = "{" + resultIds.getResourceInstanceId() + "=" + valueStr + "}";
|
||||||
valueForMultiResource = JsonParser.parseString(resourceInstance);
|
valueForMultiResource = JsonParser.parseString(resourceInstance);
|
||||||
}
|
}
|
||||||
Map<Integer, Object> value = convertMultiResourceValuesFromRpcBody(valueForMultiResource, resourceModelWrite.type, request.getObjectId());
|
Map<Integer, Object> value = convertMultiResourceValuesFromRpcBody(valueForMultiResource, resourceModelWrite.type, request.getObjectId());
|
||||||
|
|||||||
@ -15,16 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.transport.lwm2m.server.ota;
|
package org.thingsboard.server.transport.lwm2m.server.ota;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import com.google.common.hash.Hashing;
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.annotation.PreDestroy;
|
import jakarta.annotation.PreDestroy;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.eclipse.leshan.core.model.ObjectModel;
|
||||||
|
import org.eclipse.leshan.core.node.LwM2mPath;
|
||||||
import org.eclipse.leshan.core.node.codec.CodecException;
|
import org.eclipse.leshan.core.node.codec.CodecException;
|
||||||
import org.eclipse.leshan.core.request.ContentFormat;
|
import org.eclipse.leshan.core.request.ContentFormat;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.common.util.DonAsynchron;
|
import org.thingsboard.common.util.DonAsynchron;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.server.cache.ota.OtaPackageDataCache;
|
import org.thingsboard.server.cache.ota.OtaPackageDataCache;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguration;
|
import org.thingsboard.server.common.data.device.profile.lwm2m.OtherConfiguration;
|
||||||
@ -37,15 +42,12 @@ import org.thingsboard.server.gen.transport.TransportProtos;
|
|||||||
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
|
import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
|
||||||
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
|
import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
|
import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper;
|
||||||
|
import org.thingsboard.server.transport.lwm2m.server.LwM2mVersionedModelProvider;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService;
|
import org.thingsboard.server.transport.lwm2m.server.attributes.LwM2MAttributesService;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
|
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
|
import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService;
|
import org.thingsboard.server.transport.lwm2m.server.common.LwM2MExecutorAwareService;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.downlink.LwM2mDownlinkMsgHandler;
|
import org.thingsboard.server.transport.lwm2m.server.downlink.*;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteCallback;
|
|
||||||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MExecuteRequest;
|
|
||||||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteReplaceRequest;
|
|
||||||
import org.thingsboard.server.transport.lwm2m.server.downlink.TbLwM2MWriteResponseCallback;
|
|
||||||
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
|
import org.thingsboard.server.transport.lwm2m.server.log.LwM2MTelemetryLogService;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareDeliveryMethod;
|
import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareDeliveryMethod;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult;
|
import org.thingsboard.server.transport.lwm2m.server.ota.firmware.FirmwareUpdateResult;
|
||||||
@ -56,6 +58,7 @@ import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MClientSwO
|
|||||||
import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy;
|
import org.thingsboard.server.transport.lwm2m.server.ota.software.LwM2MSoftwareUpdateStrategy;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult;
|
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateResult;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState;
|
import org.thingsboard.server.transport.lwm2m.server.ota.software.SoftwareUpdateState;
|
||||||
|
import org.thingsboard.server.transport.lwm2m.server.rpc.RpcCreateRequest;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MClientOtaInfoStore;
|
import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MClientOtaInfoStore;
|
||||||
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
|
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
|
||||||
|
|
||||||
@ -64,6 +67,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
|
import static org.thingsboard.server.common.data.ota.OtaPackageKey.STATE;
|
||||||
@ -104,6 +108,14 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
|
|||||||
public static final String FW_RESULT_ID = "/5/0/5";
|
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_NAME_ID = "/5/0/6";
|
||||||
public static final String FW_VER_ID = "/5/0/7";
|
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 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_NAME = "fileName";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8
|
* Quectel@Hi15RM1-HLB_V1.0@BC68JAR01A10,V150R100C20B300SP7,V150R100C20B300SP7@8
|
||||||
* Revision:BC68JAR01A10
|
* Revision:BC68JAR01A10
|
||||||
@ -134,6 +146,7 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
|
|||||||
private final LwM2MTelemetryLogService logService;
|
private final LwM2MTelemetryLogService logService;
|
||||||
private final LwM2mTransportServerHelper helper;
|
private final LwM2mTransportServerHelper helper;
|
||||||
private final TbLwM2MClientOtaInfoStore otaInfoStore;
|
private final TbLwM2MClientOtaInfoStore otaInfoStore;
|
||||||
|
private final LwM2mVersionedModelProvider modelProvider;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Lazy
|
@Lazy
|
||||||
@ -510,6 +523,9 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
|
|||||||
} else {
|
} else {
|
||||||
strategy = info.getDeliveryMethod() == FirmwareDeliveryMethod.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY;
|
strategy = info.getDeliveryMethod() == FirmwareDeliveryMethod.PULL.code ? LwM2MFirmwareUpdateStrategy.OBJ_5_TEMP_URL : LwM2MFirmwareUpdateStrategy.OBJ_5_BINARY;
|
||||||
}
|
}
|
||||||
|
if (clientContext.getProfile(client.getProfileId()).getClientLwM2mSettings().getUseObject19ForOta()){
|
||||||
|
sendInfoFwToObject19ForOta(client, convertObjectIdToVersionedId(FW_INFO_19_INSTANCE_ID, client), response, otaPackageId);
|
||||||
|
}
|
||||||
switch (strategy) {
|
switch (strategy) {
|
||||||
case OBJ_5_BINARY:
|
case OBJ_5_BINARY:
|
||||||
startUpdateUsingBinary(client, convertObjectIdToVersionedId(FW_PACKAGE_5_ID, client), otaPackageId);
|
startUpdateUsingBinary(client, convertObjectIdToVersionedId(FW_PACKAGE_5_ID, client), otaPackageId);
|
||||||
@ -641,6 +657,61 @@ public class DefaultLwM2MOtaUpdateService extends LwM2MExecutorAwareService impl
|
|||||||
helper.sendParametersOnThingsboardTelemetry(result, client.getSession(), client.getKeyTsLatestMap());
|
helper.sendParametersOnThingsboardTelemetry(result, client.getSession(), client.getKeyTsLatestMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* send to client: versionedId="/19/65534/0/0, value = FwOtaInfo in bas64 -> format json:
|
||||||
|
* {"title":"BC68JAR01",
|
||||||
|
* "version":"A10",
|
||||||
|
* "fileChecksumSHA256":"f2a08d4963e981c78f2a99f62d8439af4437a72ea7267a8c01d013c072c01ded",
|
||||||
|
* "fileSize":59832}
|
||||||
|
* @param client
|
||||||
|
* @param targetIdVer
|
||||||
|
* @param response
|
||||||
|
* @param otaPackageId
|
||||||
|
*/
|
||||||
|
private void sendInfoFwToObject19ForOta(LwM2mClient client, String targetIdVer, TransportProtos.GetOtaPackageResponseMsg response, UUID otaPackageId) {
|
||||||
|
log.trace("[{}] Current info fw toObject19ForOta", client.getEndpoint());
|
||||||
|
ObjectModel objectModel = client.getObjectModel(targetIdVer, modelProvider);
|
||||||
|
if (objectModel != null) {
|
||||||
|
try {
|
||||||
|
if (client.getRegistration().getSupportedObject().get(19) != null) {
|
||||||
|
ObjectNode objectNodeInfoFw = JacksonUtil.newObjectNode();
|
||||||
|
byte[] firmwareChunk = otaPackageDataCache.get(otaPackageId.toString(), 0, 0);
|
||||||
|
String fileChecksumSHA256 = Hashing.sha256().hashBytes(firmwareChunk).toString();
|
||||||
|
objectNodeInfoFw.put(OTA_INFO_19_TITLE, response.getTitle());
|
||||||
|
objectNodeInfoFw.put(OTA_INFO_19_VERSION, response.getVersion());
|
||||||
|
objectNodeInfoFw.put(OTA_INFO_19_FILE_CHECKSUM256, fileChecksumSHA256);
|
||||||
|
objectNodeInfoFw.put(OTA_INFO_19_FILE_SIZE, firmwareChunk.length);
|
||||||
|
objectNodeInfoFw.put(OTA_INFO_19_FILE_NAME, response.getFileName());
|
||||||
|
String objectNodeInfoFwStr = JacksonUtil.toString(objectNodeInfoFw);
|
||||||
|
assert objectNodeInfoFwStr != null;
|
||||||
|
String objectNodeInfoFwBase64 = Base64.getEncoder().encodeToString(objectNodeInfoFwStr.getBytes());
|
||||||
|
|
||||||
|
|
||||||
|
LwM2mPath pathFwInstance = new LwM2mPath(FW_INFO_19_INSTANCE_ID);
|
||||||
|
if (client.getRegistration().getAvailableInstances().contains(pathFwInstance)) {
|
||||||
|
String versionId = targetIdVer + "/0/0";
|
||||||
|
TbLwM2MWriteReplaceRequest request = TbLwM2MWriteReplaceRequest.builder().versionedId(versionId).value(objectNodeInfoFwBase64).timeout(clientContext.getRequestTimeout(client)).build();
|
||||||
|
downlinkHandler.sendWriteReplaceRequest(client, request, new TbLwM2MWriteResponseCallback(uplinkHandler, logService, client, versionId));
|
||||||
|
} else {
|
||||||
|
String valueResourcesStr = "{\"" + 0 + "\":{\"0\":\"" + objectNodeInfoFwBase64 + "\"}}";
|
||||||
|
String valueStr = "{\"id\":\"" + targetIdVer + "\",\"value\":" + valueResourcesStr + "}";
|
||||||
|
RpcCreateRequest requestBody = JacksonUtil.fromString(valueStr, RpcCreateRequest.class);
|
||||||
|
assert requestBody != null;
|
||||||
|
TbLwM2MCreateRequest.TbLwM2MCreateRequestBuilder builder = TbLwM2MCreateRequest.builder().versionedId(targetIdVer);
|
||||||
|
builder.value(requestBody.getValue()).nodes(requestBody.getNodes()).timeout(clientContext.getRequestTimeout(client));
|
||||||
|
downlinkHandler.sendCreateRequest(client, builder.build(), new TbLwM2MCreateResponseCallback(uplinkHandler, logService, client, targetIdVer));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String errorMsg = "Failed to send Info Fw to object 19. The client does not have object 19.";
|
||||||
|
log.trace("[{}] {}", client.getEndpoint(), errorMsg);
|
||||||
|
logService.log(client, errorMsg);
|
||||||
|
}
|
||||||
|
} catch (Exception e){
|
||||||
|
log.error("", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(FirmwareUpdateResult fwUpdateResult) {
|
private static Optional<OtaPackageUpdateStatus> toOtaPackageUpdateStatus(FirmwareUpdateResult fwUpdateResult) {
|
||||||
switch (fwUpdateResult) {
|
switch (fwUpdateResult) {
|
||||||
case INITIAL:
|
case INITIAL:
|
||||||
|
|||||||
@ -74,18 +74,16 @@ public abstract class LwM2MClientOtaInfo<Strategy, State, Result> {
|
|||||||
} else {
|
} else {
|
||||||
if (targetPackageId.equals(currentPackageId)) {
|
if (targetPackageId.equals(currentPackageId)) {
|
||||||
return false;
|
return false;
|
||||||
} else if (StringUtils.isNotEmpty(targetTag) && targetTag.equals(currentPackageId)) {
|
} else{
|
||||||
return false;
|
if (targetTag.equals(currentPackageId)) {
|
||||||
} else if (StringUtils.isNotEmpty(currentVersion3)) {
|
|
||||||
if (StringUtils.isNotEmpty(targetTag) && currentVersion3.contains(targetTag)) {
|
|
||||||
return false;
|
return false;
|
||||||
|
} else if (StringUtils.isNotEmpty(currentVersion3)) {
|
||||||
|
return !(currentVersion3.contains(targetTag) || targetTag.contains(currentVersion3));
|
||||||
}
|
}
|
||||||
return !currentVersion3.contains(targetPackageId);
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
|
|||||||
@ -23,9 +23,13 @@ import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class JsonUtils {
|
public class JsonUtils {
|
||||||
|
|
||||||
|
private static final Pattern BASE64_PATTERN =
|
||||||
|
Pattern.compile("^[A-Za-z0-9+/]+={0,2}$");
|
||||||
|
|
||||||
public static JsonObject getJsonObject(List<KeyValueProto> tsKv) {
|
public static JsonObject getJsonObject(List<KeyValueProto> tsKv) {
|
||||||
JsonObject json = new JsonObject();
|
JsonObject json = new JsonObject();
|
||||||
for (KeyValueProto kv : tsKv) {
|
for (KeyValueProto kv : tsKv) {
|
||||||
@ -56,7 +60,14 @@ public class JsonUtils {
|
|||||||
} else if (value instanceof Long) {
|
} else if (value instanceof Long) {
|
||||||
return new JsonPrimitive((Long) value);
|
return new JsonPrimitive((Long) value);
|
||||||
} else if (value instanceof String) {
|
} else if (value instanceof String) {
|
||||||
return JsonParser.parseString((String) value);
|
try {
|
||||||
|
return JsonParser.parseString((String) value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (isBase64(value.toString())) {
|
||||||
|
value = "\"" + value + "\"";
|
||||||
|
}
|
||||||
|
return JsonParser.parseString((String) value);
|
||||||
|
}
|
||||||
} else if (value instanceof Boolean) {
|
} else if (value instanceof Boolean) {
|
||||||
return new JsonPrimitive((Boolean) value);
|
return new JsonPrimitive((Boolean) value);
|
||||||
} else if (value instanceof Double) {
|
} else if (value instanceof Double) {
|
||||||
@ -77,4 +88,7 @@ public class JsonUtils {
|
|||||||
return jsonObject;
|
return jsonObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isBase64(String value) {
|
||||||
|
return value.length() % 4 == 0 && BASE64_PATTERN.matcher(value).matches();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user