Merge pull request #6781 from volodymyr-babak/feature/edge-add-ota-updates
[3.4] Edge OTA support
This commit is contained in:
commit
4c6f817e05
@ -137,6 +137,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService {
|
||||
case ENTITY_VIEW:
|
||||
case DASHBOARD:
|
||||
case RULE_CHAIN:
|
||||
case OTA_PACKAGE:
|
||||
future = entityProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
|
||||
break;
|
||||
case CUSTOMER:
|
||||
|
||||
@ -26,6 +26,7 @@ import org.thingsboard.server.dao.dashboard.DashboardService;
|
||||
import org.thingsboard.server.dao.device.DeviceProfileService;
|
||||
import org.thingsboard.server.dao.edge.EdgeEventService;
|
||||
import org.thingsboard.server.dao.edge.EdgeService;
|
||||
import org.thingsboard.server.dao.ota.OtaPackageService;
|
||||
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||
import org.thingsboard.server.dao.user.UserService;
|
||||
@ -41,6 +42,7 @@ import org.thingsboard.server.service.edge.rpc.processor.DeviceEdgeProcessor;
|
||||
import org.thingsboard.server.service.edge.rpc.processor.DeviceProfileEdgeProcessor;
|
||||
import org.thingsboard.server.service.edge.rpc.processor.EntityEdgeProcessor;
|
||||
import org.thingsboard.server.service.edge.rpc.processor.EntityViewEdgeProcessor;
|
||||
import org.thingsboard.server.service.edge.rpc.processor.OtaPackageEdgeProcessor;
|
||||
import org.thingsboard.server.service.edge.rpc.processor.RelationEdgeProcessor;
|
||||
import org.thingsboard.server.service.edge.rpc.processor.RuleChainEdgeProcessor;
|
||||
import org.thingsboard.server.service.edge.rpc.processor.TelemetryEdgeProcessor;
|
||||
@ -93,6 +95,9 @@ public class EdgeContextComponent {
|
||||
@Autowired
|
||||
private EdgeRequestsService edgeRequestsService;
|
||||
|
||||
@Autowired
|
||||
private OtaPackageService otaPackageService;
|
||||
|
||||
@Autowired
|
||||
private AlarmEdgeProcessor alarmProcessor;
|
||||
|
||||
@ -138,6 +143,9 @@ public class EdgeContextComponent {
|
||||
@Autowired
|
||||
private AdminSettingsEdgeProcessor adminSettingsProcessor;
|
||||
|
||||
@Autowired
|
||||
private OtaPackageEdgeProcessor otaPackageEdgeProcessor;
|
||||
|
||||
@Autowired
|
||||
private EdgeEventStorageSettings edgeEventStorageSettings;
|
||||
|
||||
|
||||
@ -536,6 +536,8 @@ public final class EdgeGrpcSession implements Closeable {
|
||||
return ctx.getWidgetTypeProcessor().processWidgetTypeToEdge(edgeEvent, msgType, action);
|
||||
case ADMIN_SETTINGS:
|
||||
return ctx.getAdminSettingsProcessor().processAdminSettingsToEdge(edgeEvent);
|
||||
case OTA_PACKAGE:
|
||||
return ctx.getOtaPackageEdgeProcessor().processOtaPackageToEdge(edgeEvent, msgType, action);
|
||||
default:
|
||||
log.warn("Unsupported edge event type [{}]", edgeEvent);
|
||||
return null;
|
||||
|
||||
@ -25,6 +25,7 @@ import org.thingsboard.server.service.edge.rpc.fetch.CustomerUsersEdgeEventFetch
|
||||
import org.thingsboard.server.service.edge.rpc.fetch.DashboardsEdgeEventFetcher;
|
||||
import org.thingsboard.server.service.edge.rpc.fetch.DeviceProfilesEdgeEventFetcher;
|
||||
import org.thingsboard.server.service.edge.rpc.fetch.EdgeEventFetcher;
|
||||
import org.thingsboard.server.service.edge.rpc.fetch.OtaPackagesEdgeEventFetcher;
|
||||
import org.thingsboard.server.service.edge.rpc.fetch.RuleChainsEdgeEventFetcher;
|
||||
import org.thingsboard.server.service.edge.rpc.fetch.SystemWidgetsBundlesEdgeEventFetcher;
|
||||
import org.thingsboard.server.service.edge.rpc.fetch.TenantAdminUsersEdgeEventFetcher;
|
||||
@ -53,6 +54,7 @@ public class EdgeSyncCursor {
|
||||
fetchers.add(new SystemWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
|
||||
fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
|
||||
fetchers.add(new DashboardsEdgeEventFetcher(ctx.getDashboardService()));
|
||||
fetchers.add(new OtaPackagesEdgeEventFetcher(ctx.getOtaPackageService()));
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright © 2016-2022 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.edge.rpc.constructor;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.OtaPackage;
|
||||
import org.thingsboard.server.common.data.id.OtaPackageId;
|
||||
import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
|
||||
@Component
|
||||
@TbCoreComponent
|
||||
public class OtaPackageMsgConstructor {
|
||||
|
||||
public OtaPackageUpdateMsg constructOtaPackageUpdatedMsg(UpdateMsgType msgType, OtaPackage otaPackage) {
|
||||
OtaPackageUpdateMsg.Builder builder = OtaPackageUpdateMsg.newBuilder()
|
||||
.setMsgType(msgType)
|
||||
.setIdMSB(otaPackage.getId().getId().getMostSignificantBits())
|
||||
.setIdLSB(otaPackage.getId().getId().getLeastSignificantBits())
|
||||
.setDeviceProfileIdMSB(otaPackage.getDeviceProfileId().getId().getMostSignificantBits())
|
||||
.setDeviceProfileIdLSB(otaPackage.getDeviceProfileId().getId().getLeastSignificantBits())
|
||||
.setType(otaPackage.getType().name())
|
||||
.setTitle(otaPackage.getTitle())
|
||||
.setVersion(otaPackage.getVersion())
|
||||
.setTag(otaPackage.getTag());
|
||||
|
||||
if (otaPackage.getUrl() != null) {
|
||||
builder.setUrl(otaPackage.getUrl());
|
||||
}
|
||||
if (otaPackage.getAdditionalInfo() != null) {
|
||||
builder.setAdditionalInfo(JacksonUtil.toString(otaPackage.getAdditionalInfo()));
|
||||
}
|
||||
if (otaPackage.getFileName() != null) {
|
||||
builder.setFileName(otaPackage.getFileName());
|
||||
}
|
||||
if (otaPackage.getContentType() != null) {
|
||||
builder.setContentType(otaPackage.getContentType());
|
||||
}
|
||||
if (otaPackage.getChecksumAlgorithm() != null) {
|
||||
builder.setChecksumAlgorithm(otaPackage.getChecksumAlgorithm().name());
|
||||
}
|
||||
if (otaPackage.getChecksum() != null) {
|
||||
builder.setChecksum(otaPackage.getChecksum());
|
||||
}
|
||||
if (otaPackage.getDataSize() != null) {
|
||||
builder.setDataSize(otaPackage.getDataSize());
|
||||
}
|
||||
if (otaPackage.getData() != null) {
|
||||
builder.setData(ByteString.copyFrom(otaPackage.getData().array()));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public OtaPackageUpdateMsg constructOtaPackageDeleteMsg(OtaPackageId otaPackageId) {
|
||||
return OtaPackageUpdateMsg.newBuilder()
|
||||
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE)
|
||||
.setIdMSB(otaPackageId.getId().getMostSignificantBits())
|
||||
.setIdLSB(otaPackageId.getId().getLeastSignificantBits()).build();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Copyright © 2016-2022 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.edge.rpc.fetch;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.server.common.data.EdgeUtils;
|
||||
import org.thingsboard.server.common.data.OtaPackageInfo;
|
||||
import org.thingsboard.server.common.data.edge.Edge;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventType;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.dao.ota.OtaPackageService;
|
||||
|
||||
@AllArgsConstructor
|
||||
@Slf4j
|
||||
public class OtaPackagesEdgeEventFetcher extends BasePageableEdgeEventFetcher<OtaPackageInfo> {
|
||||
|
||||
private final OtaPackageService otaPackageService;
|
||||
|
||||
@Override
|
||||
PageData<OtaPackageInfo> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) {
|
||||
return otaPackageService.findTenantOtaPackagesByTenantId(tenantId, pageLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, OtaPackageInfo otaPackageInfo) {
|
||||
return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OTA_PACKAGE,
|
||||
EdgeEventActionType.ADDED, otaPackageInfo.getId(), null);
|
||||
}
|
||||
}
|
||||
@ -46,6 +46,7 @@ import org.thingsboard.server.dao.device.DeviceService;
|
||||
import org.thingsboard.server.dao.edge.EdgeEventService;
|
||||
import org.thingsboard.server.dao.edge.EdgeService;
|
||||
import org.thingsboard.server.dao.entityview.EntityViewService;
|
||||
import org.thingsboard.server.dao.ota.OtaPackageService;
|
||||
import org.thingsboard.server.dao.relation.RelationService;
|
||||
import org.thingsboard.server.dao.rule.RuleChainService;
|
||||
import org.thingsboard.server.dao.service.DataValidator;
|
||||
@ -61,6 +62,7 @@ import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor;
|
||||
import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor;
|
||||
import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor;
|
||||
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor;
|
||||
import org.thingsboard.server.service.edge.rpc.constructor.OtaPackageMsgConstructor;
|
||||
import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor;
|
||||
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor;
|
||||
import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor;
|
||||
@ -137,6 +139,9 @@ public abstract class BaseEdgeProcessor {
|
||||
@Autowired
|
||||
protected WidgetTypeService widgetTypeService;
|
||||
|
||||
@Autowired
|
||||
protected OtaPackageService otaPackageService;
|
||||
|
||||
@Autowired
|
||||
protected DataValidator<Device> deviceValidator;
|
||||
|
||||
@ -182,6 +187,9 @@ public abstract class BaseEdgeProcessor {
|
||||
@Autowired
|
||||
protected AdminSettingsMsgConstructor adminSettingsMsgConstructor;
|
||||
|
||||
@Autowired
|
||||
protected OtaPackageMsgConstructor otaPackageMsgConstructor;
|
||||
|
||||
@Autowired
|
||||
protected DbCallbackExecutorService dbCallbackExecutorService;
|
||||
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Copyright © 2016-2022 The Thingsboard Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.service.edge.rpc.processor;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.thingsboard.server.common.data.EdgeUtils;
|
||||
import org.thingsboard.server.common.data.OtaPackage;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
|
||||
import org.thingsboard.server.common.data.edge.EdgeEventActionType;
|
||||
import org.thingsboard.server.common.data.id.OtaPackageId;
|
||||
import org.thingsboard.server.gen.edge.v1.DownlinkMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.UpdateMsgType;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@TbCoreComponent
|
||||
public class OtaPackageEdgeProcessor extends BaseEdgeProcessor {
|
||||
|
||||
public DownlinkMsg processOtaPackageToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) {
|
||||
OtaPackageId otaPackageId = new OtaPackageId(edgeEvent.getEntityId());
|
||||
DownlinkMsg downlinkMsg = null;
|
||||
switch (action) {
|
||||
case ADDED:
|
||||
case UPDATED:
|
||||
OtaPackage otaPackage = otaPackageService.findOtaPackageById(edgeEvent.getTenantId(), otaPackageId);
|
||||
if (otaPackage != null) {
|
||||
OtaPackageUpdateMsg otaPackageUpdateMsg =
|
||||
otaPackageMsgConstructor.constructOtaPackageUpdatedMsg(msgType, otaPackage);
|
||||
downlinkMsg = DownlinkMsg.newBuilder()
|
||||
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
|
||||
.addOtaPackageUpdateMsg(otaPackageUpdateMsg)
|
||||
.build();
|
||||
}
|
||||
break;
|
||||
case DELETED:
|
||||
OtaPackageUpdateMsg otaPackageUpdateMsg =
|
||||
otaPackageMsgConstructor.constructOtaPackageDeleteMsg(otaPackageId);
|
||||
downlinkMsg = DownlinkMsg.newBuilder()
|
||||
.setDownlinkMsgId(EdgeUtils.nextPositiveInt())
|
||||
.addOtaPackageUpdateMsg(otaPackageUpdateMsg)
|
||||
.build();
|
||||
break;
|
||||
}
|
||||
return downlinkMsg;
|
||||
}
|
||||
|
||||
}
|
||||
@ -45,8 +45,10 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen
|
||||
ActionType actionType = saveOtaPackageInfoRequest.getId() == null ? ActionType.ADDED : ActionType.UPDATED;
|
||||
try {
|
||||
OtaPackageInfo savedOtaPackageInfo = otaPackageService.saveOtaPackageInfo(new OtaPackageInfo(saveOtaPackageInfoRequest), saveOtaPackageInfoRequest.isUsesUrl());
|
||||
notificationEntityService.notifyEntity(tenantId, savedOtaPackageInfo.getId(), savedOtaPackageInfo, null,
|
||||
actionType, user, null);
|
||||
|
||||
boolean sendToEdge = savedOtaPackageInfo.hasUrl() || savedOtaPackageInfo.isHasData();
|
||||
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedOtaPackageInfo.getId(), savedOtaPackageInfo, user, actionType, sendToEdge, null);
|
||||
|
||||
return savedOtaPackageInfo;
|
||||
} catch (Exception e) {
|
||||
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), saveOtaPackageInfoRequest, null,
|
||||
@ -61,8 +63,11 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen
|
||||
OtaPackageId otaPackageId = otaPackageInfo.getId();
|
||||
try {
|
||||
otaPackageService.deleteOtaPackage(tenantId, otaPackageId);
|
||||
notificationEntityService.notifyEntity(tenantId, otaPackageId, otaPackageInfo, null,
|
||||
ActionType.DELETED, user, null, otaPackageInfo.getId().toString());
|
||||
// notificationEntityService.notifyEntity(tenantId, otaPackageId, otaPackageInfo, null,
|
||||
// ActionType.DELETED, user, null, otaPackageInfo.getId().toString());
|
||||
|
||||
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, otaPackageId, otaPackageInfo,
|
||||
user, ActionType.DELETED, true, null, otaPackageInfo.getId().toString());
|
||||
} catch (Exception e) {
|
||||
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), null, null,
|
||||
ActionType.DELETED, user, e, otaPackageInfo.getId().toString());
|
||||
@ -95,8 +100,8 @@ public class DefaultTbOtaPackageService extends AbstractTbEntityService implemen
|
||||
otaPackage.setData(ByteBuffer.wrap(data));
|
||||
otaPackage.setDataSize((long) data.length);
|
||||
OtaPackageInfo savedOtaPackage = otaPackageService.saveOtaPackage(otaPackage);
|
||||
notificationEntityService.notifyEntity(tenantId, savedOtaPackage.getId(), savedOtaPackage, null,
|
||||
ActionType.UPDATED, user, null);
|
||||
|
||||
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedOtaPackage.getId(), savedOtaPackage, user, ActionType.UPDATED, true, null);
|
||||
return savedOtaPackage;
|
||||
} catch (Exception e) {
|
||||
notificationEntityService.notifyEntity(tenantId, emptyId(EntityType.OTA_PACKAGE), null, null,
|
||||
|
||||
@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.protobuf.AbstractMessage;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.MessageLite;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
@ -32,7 +33,10 @@ import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
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.cluster.TbClusterService;
|
||||
import org.thingsboard.server.common.data.Customer;
|
||||
@ -42,6 +46,8 @@ import org.thingsboard.server.common.data.Device;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.EntityType;
|
||||
import org.thingsboard.server.common.data.EntityView;
|
||||
import org.thingsboard.server.common.data.OtaPackageInfo;
|
||||
import org.thingsboard.server.common.data.SaveOtaPackageInfoRequest;
|
||||
import org.thingsboard.server.common.data.Tenant;
|
||||
import org.thingsboard.server.common.data.TenantProfile;
|
||||
import org.thingsboard.server.common.data.User;
|
||||
@ -70,6 +76,8 @@ import org.thingsboard.server.common.data.id.EntityId;
|
||||
import org.thingsboard.server.common.data.id.EntityIdFactory;
|
||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
|
||||
import org.thingsboard.server.common.data.ota.OtaPackageType;
|
||||
import org.thingsboard.server.common.data.page.PageData;
|
||||
import org.thingsboard.server.common.data.page.PageLink;
|
||||
import org.thingsboard.server.common.data.query.EntityKeyValueType;
|
||||
@ -107,6 +115,7 @@ import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
|
||||
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
|
||||
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.EntityViewsRequestMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.RelationRequestMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.RpcResponseMsg;
|
||||
@ -123,6 +132,7 @@ import org.thingsboard.server.gen.edge.v1.WidgetTypeUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -133,18 +143,21 @@ import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
import static org.thingsboard.server.common.data.ota.OtaPackageType.FIRMWARE;
|
||||
|
||||
@TestPropertySource(properties = {
|
||||
"edges.enabled=true",
|
||||
})
|
||||
abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
|
||||
private static final String CUSTOM_DEVICE_PROFILE_NAME = "Thermostat";
|
||||
private static final String THERMOSTAT_DEVICE_PROFILE_NAME = "Thermostat";
|
||||
|
||||
private Tenant savedTenant;
|
||||
private TenantId tenantId;
|
||||
private User tenantAdmin;
|
||||
|
||||
private DeviceProfile thermostatDeviceProfile;
|
||||
|
||||
private EdgeImitator edgeImitator;
|
||||
private Edge edge;
|
||||
|
||||
@ -204,12 +217,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration();
|
||||
transportConfiguration.setTransportPayloadTypeConfiguration(new JsonTransportPayloadConfiguration());
|
||||
|
||||
DeviceProfile deviceProfile = this.createDeviceProfile(CUSTOM_DEVICE_PROFILE_NAME, transportConfiguration);
|
||||
thermostatDeviceProfile = this.createDeviceProfile(THERMOSTAT_DEVICE_PROFILE_NAME, transportConfiguration);
|
||||
|
||||
extendDeviceProfileData(deviceProfile);
|
||||
doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
|
||||
extendDeviceProfileData(thermostatDeviceProfile);
|
||||
thermostatDeviceProfile = doPost("/api/deviceProfile", thermostatDeviceProfile, DeviceProfile.class);
|
||||
|
||||
Device savedDevice = saveDevice("Edge Device 1", CUSTOM_DEVICE_PROFILE_NAME);
|
||||
Device savedDevice = saveDevice("Edge Device 1", THERMOSTAT_DEVICE_PROFILE_NAME);
|
||||
doPost("/api/edge/" + edge.getUuidId()
|
||||
+ "/device/" + savedDevice.getUuidId(), Device.class);
|
||||
|
||||
@ -269,7 +282,7 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
List<DeviceProfileUpdateMsg> deviceProfileUpdateMsgList = edgeImitator.findAllMessagesByType(DeviceProfileUpdateMsg.class);
|
||||
Assert.assertEquals(3, deviceProfileUpdateMsgList.size());
|
||||
Optional<DeviceProfileUpdateMsg> deviceProfileUpdateMsgOpt =
|
||||
deviceProfileUpdateMsgList.stream().filter(dfum -> CUSTOM_DEVICE_PROFILE_NAME.equals(dfum.getName())).findAny();
|
||||
deviceProfileUpdateMsgList.stream().filter(dfum -> THERMOSTAT_DEVICE_PROFILE_NAME.equals(dfum.getName())).findAny();
|
||||
Assert.assertTrue(deviceProfileUpdateMsgOpt.isPresent());
|
||||
DeviceProfileUpdateMsg deviceProfileUpdateMsg = deviceProfileUpdateMsgOpt.get();
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, deviceProfileUpdateMsg.getMsgType());
|
||||
@ -1643,6 +1656,110 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
Assert.assertTrue("Expected key and value must be found", found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtaPackages_usesUrl() throws Exception {
|
||||
// 1
|
||||
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
|
||||
firmwareInfo.setDeviceProfileId(thermostatDeviceProfile.getId());
|
||||
firmwareInfo.setType(FIRMWARE);
|
||||
firmwareInfo.setTitle("My firmware #1");
|
||||
firmwareInfo.setVersion("v1.0");
|
||||
firmwareInfo.setTag("My firmware #1 v1.0");
|
||||
firmwareInfo.setUsesUrl(true);
|
||||
firmwareInfo.setUrl("http://localhost:8080/v1/package");
|
||||
firmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode());
|
||||
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
|
||||
Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg);
|
||||
OtaPackageUpdateMsg otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage;
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(savedFirmwareInfo.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getIdMSB());
|
||||
Assert.assertEquals(savedFirmwareInfo.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getIdLSB());
|
||||
Assert.assertEquals(thermostatDeviceProfile.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdMSB());
|
||||
Assert.assertEquals(thermostatDeviceProfile.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdLSB());
|
||||
Assert.assertEquals(FIRMWARE, OtaPackageType.valueOf(otaPackageUpdateMsg.getType()));
|
||||
Assert.assertEquals("My firmware #1", otaPackageUpdateMsg.getTitle());
|
||||
Assert.assertEquals("v1.0", otaPackageUpdateMsg.getVersion());
|
||||
Assert.assertEquals("My firmware #1 v1.0", otaPackageUpdateMsg.getTag());
|
||||
Assert.assertEquals("http://localhost:8080/v1/package", otaPackageUpdateMsg.getUrl());
|
||||
Assert.assertFalse(otaPackageUpdateMsg.hasData());
|
||||
Assert.assertFalse(otaPackageUpdateMsg.hasFileName());
|
||||
Assert.assertFalse(otaPackageUpdateMsg.hasContentType());
|
||||
Assert.assertFalse(otaPackageUpdateMsg.hasChecksumAlgorithm());
|
||||
Assert.assertFalse(otaPackageUpdateMsg.hasChecksum());
|
||||
Assert.assertFalse(otaPackageUpdateMsg.hasDataSize());
|
||||
|
||||
// 2
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
doDelete("/api/otaPackage/" + savedFirmwareInfo.getUuidId())
|
||||
.andExpect(status().isOk());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
latestMessage = edgeImitator.getLatestMessage();
|
||||
Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg);
|
||||
otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage;
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(otaPackageUpdateMsg.getIdMSB(), savedFirmwareInfo.getUuidId().getMostSignificantBits());
|
||||
Assert.assertEquals(otaPackageUpdateMsg.getIdLSB(), savedFirmwareInfo.getUuidId().getLeastSignificantBits());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOtaPackages_hasData() throws Exception {
|
||||
// 1
|
||||
SaveOtaPackageInfoRequest firmwareInfo = new SaveOtaPackageInfoRequest();
|
||||
firmwareInfo.setDeviceProfileId(thermostatDeviceProfile.getId());
|
||||
firmwareInfo.setType(FIRMWARE);
|
||||
firmwareInfo.setTitle("My firmware #2");
|
||||
firmwareInfo.setVersion("v2.0");
|
||||
firmwareInfo.setTag("My firmware #2 v2.0");
|
||||
firmwareInfo.setUsesUrl(false);
|
||||
firmwareInfo.setHasData(false);
|
||||
firmwareInfo.setAdditionalInfo(JacksonUtil.newObjectNode());
|
||||
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
|
||||
OtaPackageInfo savedFirmwareInfo = doPost("/api/otaPackage", firmwareInfo, OtaPackageInfo.class);
|
||||
MockMultipartFile testData = new MockMultipartFile("file", "firmware.bin", "image/png", ByteBuffer.wrap(new byte[]{1, 3, 5}).array());
|
||||
savedFirmwareInfo = saveData("/api/otaPackage/" + savedFirmwareInfo.getId().getId().toString() + "?checksumAlgorithm={checksumAlgorithm}", testData, ChecksumAlgorithm.SHA256.name());
|
||||
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
|
||||
AbstractMessage latestMessage = edgeImitator.getLatestMessage();
|
||||
Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg);
|
||||
OtaPackageUpdateMsg otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage;
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_UPDATED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(savedFirmwareInfo.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getIdMSB());
|
||||
Assert.assertEquals(savedFirmwareInfo.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getIdLSB());
|
||||
Assert.assertEquals(thermostatDeviceProfile.getUuidId().getMostSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdMSB());
|
||||
Assert.assertEquals(thermostatDeviceProfile.getUuidId().getLeastSignificantBits(), otaPackageUpdateMsg.getDeviceProfileIdLSB());
|
||||
Assert.assertEquals(FIRMWARE, OtaPackageType.valueOf(otaPackageUpdateMsg.getType()));
|
||||
Assert.assertEquals("My firmware #2", otaPackageUpdateMsg.getTitle());
|
||||
Assert.assertEquals("v2.0", otaPackageUpdateMsg.getVersion());
|
||||
Assert.assertEquals("My firmware #2 v2.0", otaPackageUpdateMsg.getTag());
|
||||
Assert.assertFalse(otaPackageUpdateMsg.hasUrl());
|
||||
Assert.assertEquals("firmware.bin", otaPackageUpdateMsg.getFileName());
|
||||
Assert.assertEquals("image/png", otaPackageUpdateMsg.getContentType());
|
||||
Assert.assertEquals(ChecksumAlgorithm.SHA256.name(), otaPackageUpdateMsg.getChecksumAlgorithm());
|
||||
Assert.assertEquals("62467691cf583d4fa78b18fafaf9801f505e0ef03baf0603fd4b0cd004cd1e75", otaPackageUpdateMsg.getChecksum());
|
||||
Assert.assertEquals(3L, otaPackageUpdateMsg.getDataSize());
|
||||
Assert.assertEquals(ByteString.copyFrom(new byte[]{1, 3, 5}), otaPackageUpdateMsg.getData());
|
||||
|
||||
// 2
|
||||
edgeImitator.expectMessageAmount(1);
|
||||
doDelete("/api/otaPackage/" + savedFirmwareInfo.getUuidId())
|
||||
.andExpect(status().isOk());
|
||||
Assert.assertTrue(edgeImitator.waitForMessages());
|
||||
latestMessage = edgeImitator.getLatestMessage();
|
||||
Assert.assertTrue(latestMessage instanceof OtaPackageUpdateMsg);
|
||||
otaPackageUpdateMsg = (OtaPackageUpdateMsg) latestMessage;
|
||||
Assert.assertEquals(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE, otaPackageUpdateMsg.getMsgType());
|
||||
Assert.assertEquals(otaPackageUpdateMsg.getIdMSB(), savedFirmwareInfo.getUuidId().getMostSignificantBits());
|
||||
Assert.assertEquals(otaPackageUpdateMsg.getIdLSB(), savedFirmwareInfo.getUuidId().getLeastSignificantBits());
|
||||
}
|
||||
|
||||
// Utility methods
|
||||
|
||||
private Device saveDeviceOnCloudAndVerifyDeliveryToEdge() throws Exception {
|
||||
@ -1723,4 +1840,12 @@ abstract public class BaseEdgeTest extends AbstractControllerTest {
|
||||
Assert.assertEquals(source, target);
|
||||
Assert.assertEquals(source.hashCode(), target.hashCode());
|
||||
}
|
||||
|
||||
private OtaPackageInfo saveData(String urlTemplate, MockMultipartFile content, String... params) throws Exception {
|
||||
MockMultipartHttpServletRequestBuilder postRequest = MockMvcRequestBuilders.multipart(urlTemplate, params);
|
||||
postRequest.file(content);
|
||||
setJwtToken(postRequest);
|
||||
return readResponse(mockMvc.perform(postRequest).andExpect(status().isOk()), OtaPackageInfo.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -41,6 +41,7 @@ import org.thingsboard.server.gen.edge.v1.DownlinkResponseMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.EdgeConfiguration;
|
||||
import org.thingsboard.server.gen.edge.v1.EntityDataProto;
|
||||
import org.thingsboard.server.gen.edge.v1.EntityViewUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.OtaPackageUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.RelationUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.RuleChainMetadataUpdateMsg;
|
||||
import org.thingsboard.server.gen.edge.v1.RuleChainUpdateMsg;
|
||||
@ -277,6 +278,11 @@ public class EdgeImitator {
|
||||
result.add(saveDownlinkMsg(deviceCredentialsRequestMsg));
|
||||
}
|
||||
}
|
||||
if (downlinkMsg.getOtaPackageUpdateMsgCount() > 0) {
|
||||
for (OtaPackageUpdateMsg otaPackageUpdateMsg : downlinkMsg.getOtaPackageUpdateMsgList()) {
|
||||
result.add(saveDownlinkMsg(otaPackageUpdateMsg));
|
||||
}
|
||||
}
|
||||
return Futures.allAsList(result);
|
||||
}
|
||||
|
||||
|
||||
@ -60,6 +60,8 @@ public final class EdgeUtils {
|
||||
return EdgeEventType.WIDGETS_BUNDLE;
|
||||
case WIDGET_TYPE:
|
||||
return EdgeEventType.WIDGET_TYPE;
|
||||
case OTA_PACKAGE:
|
||||
return EdgeEventType.OTA_PACKAGE;
|
||||
default:
|
||||
log.warn("Unsupported entity type [{}]", entityType);
|
||||
return null;
|
||||
|
||||
@ -31,5 +31,6 @@ public enum EdgeEventType {
|
||||
TENANT,
|
||||
WIDGETS_BUNDLE,
|
||||
WIDGET_TYPE,
|
||||
ADMIN_SETTINGS
|
||||
ADMIN_SETTINGS,
|
||||
OTA_PACKAGE
|
||||
}
|
||||
|
||||
@ -107,6 +107,8 @@ public class EntityIdFactory {
|
||||
return new WidgetsBundleId(uuid);
|
||||
case WIDGET_TYPE:
|
||||
return new WidgetTypeId(uuid);
|
||||
case OTA_PACKAGE:
|
||||
return new OtaPackageId(uuid);
|
||||
case EDGE:
|
||||
return new EdgeId(uuid);
|
||||
}
|
||||
|
||||
@ -421,6 +421,26 @@ enum EdgeEntityType {
|
||||
ASSET = 1;
|
||||
}
|
||||
|
||||
message OtaPackageUpdateMsg {
|
||||
UpdateMsgType msgType = 1;
|
||||
int64 idMSB = 2;
|
||||
int64 idLSB = 3;
|
||||
int64 deviceProfileIdMSB = 4;
|
||||
int64 deviceProfileIdLSB = 5;
|
||||
string type = 6;
|
||||
string title = 7;
|
||||
string version = 8;
|
||||
string tag = 9;
|
||||
optional string url = 10;
|
||||
optional string fileName = 11;
|
||||
optional string contentType = 12;
|
||||
optional string checksumAlgorithm = 13;
|
||||
optional string checksum = 14;
|
||||
optional int64 dataSize = 15;
|
||||
optional bytes data = 16;
|
||||
optional string additionalInfo = 17;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main Messages;
|
||||
*/
|
||||
@ -477,5 +497,6 @@ message DownlinkMsg {
|
||||
repeated WidgetTypeUpdateMsg widgetTypeUpdateMsg = 19;
|
||||
repeated AdminSettingsUpdateMsg adminSettingsUpdateMsg = 20;
|
||||
repeated DeviceRpcCallMsg deviceRpcCallMsg = 21;
|
||||
repeated OtaPackageUpdateMsg otaPackageUpdateMsg = 22;
|
||||
}
|
||||
|
||||
|
||||
@ -397,40 +397,35 @@ public class EdgeServiceImpl extends AbstractCachedEntityService<EdgeCacheKey, E
|
||||
@Override
|
||||
public PageData<EdgeId> findRelatedEdgeIdsByEntityId(TenantId tenantId, EntityId entityId, PageLink pageLink) {
|
||||
log.trace("[{}] Executing findRelatedEdgeIdsByEntityId [{}] [{}]", tenantId, entityId, pageLink);
|
||||
if (EntityType.TENANT.equals(entityId.getEntityType()) ||
|
||||
EntityType.CUSTOMER.equals(entityId.getEntityType()) ||
|
||||
EntityType.DEVICE_PROFILE.equals(entityId.getEntityType())) {
|
||||
if (EntityType.TENANT.equals(entityId.getEntityType()) ||
|
||||
EntityType.DEVICE_PROFILE.equals(entityId.getEntityType())) {
|
||||
switch (entityId.getEntityType()) {
|
||||
case TENANT:
|
||||
case DEVICE_PROFILE:
|
||||
case OTA_PACKAGE:
|
||||
return convertToEdgeIds(findEdgesByTenantId(tenantId, pageLink));
|
||||
} else {
|
||||
case CUSTOMER:
|
||||
return convertToEdgeIds(findEdgesByTenantIdAndCustomerId(tenantId, new CustomerId(entityId.getId()), pageLink));
|
||||
}
|
||||
} else {
|
||||
switch (entityId.getEntityType()) {
|
||||
case EDGE:
|
||||
List<EdgeId> edgeIds = Collections.singletonList(new EdgeId(entityId.getId()));
|
||||
return new PageData<>(edgeIds, 1, 1, false);
|
||||
case DEVICE:
|
||||
case ASSET:
|
||||
case ENTITY_VIEW:
|
||||
case DASHBOARD:
|
||||
case RULE_CHAIN:
|
||||
return convertToEdgeIds(findEdgesByTenantIdAndEntityId(tenantId, entityId, pageLink));
|
||||
case USER:
|
||||
User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));
|
||||
if (userById == null) {
|
||||
return createEmptyEdgeIdPageData();
|
||||
}
|
||||
if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
|
||||
return convertToEdgeIds(findEdgesByTenantId(tenantId, pageLink));
|
||||
} else {
|
||||
return convertToEdgeIds(findEdgesByTenantIdAndCustomerId(tenantId, userById.getCustomerId(), pageLink));
|
||||
}
|
||||
default:
|
||||
log.warn("[{}] Unsupported entity type {}", tenantId, entityId.getEntityType());
|
||||
case EDGE:
|
||||
List<EdgeId> edgeIds = Collections.singletonList(new EdgeId(entityId.getId()));
|
||||
return new PageData<>(edgeIds, 1, 1, false);
|
||||
case DEVICE:
|
||||
case ASSET:
|
||||
case ENTITY_VIEW:
|
||||
case DASHBOARD:
|
||||
case RULE_CHAIN:
|
||||
return convertToEdgeIds(findEdgesByTenantIdAndEntityId(tenantId, entityId, pageLink));
|
||||
case USER:
|
||||
User userById = userService.findUserById(tenantId, new UserId(entityId.getId()));
|
||||
if (userById == null) {
|
||||
return createEmptyEdgeIdPageData();
|
||||
}
|
||||
}
|
||||
if (userById.getCustomerId() == null || userById.getCustomerId().isNullUid()) {
|
||||
return convertToEdgeIds(findEdgesByTenantId(tenantId, pageLink));
|
||||
} else {
|
||||
return convertToEdgeIds(findEdgesByTenantIdAndCustomerId(tenantId, userById.getCustomerId(), pageLink));
|
||||
}
|
||||
default:
|
||||
log.warn("[{}] Unsupported entity type {}", tenantId, entityId.getEntityType());
|
||||
return createEmptyEdgeIdPageData();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user