diff --git a/application/src/main/data/upgrade/3.2.2/schema_update.sql b/application/src/main/data/upgrade/3.2.2/schema_update.sql index 77a5a9dbbe..af3fd418a8 100644 --- a/application/src/main/data/upgrade/3.2.2/schema_update.sql +++ b/application/src/main/data/upgrade/3.2.2/schema_update.sql @@ -63,6 +63,8 @@ CREATE TABLE IF NOT EXISTS firmware ( id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, + device_profile_id uuid, + type varchar(32) NOT NULL, title varchar(255) NOT NULL, version varchar(255) NOT NULL, file_name varchar(255), @@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS firmware ( ); ALTER TABLE device_profile - ADD COLUMN IF NOT EXISTS firmware_id uuid; + ADD COLUMN IF NOT EXISTS firmware_id uuid, + ADD COLUMN IF NOT EXISTS software_id uuid; ALTER TABLE device - ADD COLUMN IF NOT EXISTS firmware_id uuid; + ADD COLUMN IF NOT EXISTS firmware_id uuid, + ADD COLUMN IF NOT EXISTS software_id uuid; DO $$ BEGIN @@ -90,11 +94,23 @@ DO $$ FOREIGN KEY (firmware_id) REFERENCES firmware(id); END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN + ALTER TABLE device_profile + ADD CONSTRAINT fk_software_device_profile + FOREIGN KEY (firmware_id) REFERENCES firmware(id); + END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN ALTER TABLE device ADD CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id); END IF; + + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN + ALTER TABLE device + ADD CONSTRAINT fk_software_device + FOREIGN KEY (firmware_id) REFERENCES firmware(id); + END IF; END; $$; diff --git a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java index 225c79b79a..849bcb51b9 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java @@ -146,12 +146,16 @@ public class DeviceProfileController extends BaseController { checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); boolean isFirmwareChanged = false; + boolean isSoftwareChanged = false; if (!created) { DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) { isFirmwareChanged = true; } + if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) { + isSoftwareChanged = true; + } } DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); @@ -164,9 +168,8 @@ public class DeviceProfileController extends BaseController { null, created ? ActionType.ADDED : ActionType.UPDATED, null); - if (isFirmwareChanged) { - firmwareStateService.update(savedDeviceProfile); - } + firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); + sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); return savedDeviceProfile; diff --git a/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java b/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java index 9c08967c8e..9b3caf7a79 100644 --- a/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java +++ b/application/src/main/java/org/thingsboard/server/controller/FirmwareController.java @@ -35,6 +35,8 @@ import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -133,6 +135,8 @@ public class FirmwareController extends BaseController { Firmware firmware = new Firmware(firmwareId); firmware.setCreatedTime(info.getCreatedTime()); firmware.setTenantId(getTenantId()); + firmware.setDeviceProfileId(info.getDeviceProfileId()); + firmware.setType(info.getType()); firmware.setTitle(info.getTitle()); firmware.setVersion(info.getVersion()); firmware.setAdditionalInfo(info.getAdditionalInfo()); @@ -175,17 +179,22 @@ public class FirmwareController extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") - @RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET) + @RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) @ResponseBody - public PageData getFirmwares(@PathVariable("hasData") boolean hasData, + public PageData getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId, + @PathVariable("type") String strType, + @PathVariable("hasData") boolean hasData, @RequestParam int pageSize, @RequestParam int page, @RequestParam(required = false) String textSearch, @RequestParam(required = false) String sortProperty, @RequestParam(required = false) String sortOrder) throws ThingsboardException { + checkParameter("deviceProfileId", strDeviceProfileId); + checkParameter("type", strType); try { PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); - return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndHasData(getTenantId(), hasData, pageLink)); + return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), + new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink)); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java b/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java index 6d358e0ff9..ca369b3698 100644 --- a/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java @@ -19,13 +19,17 @@ import com.google.common.util.concurrent.FutureCallback; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.firmware.FirmwareUtil; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKey; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; @@ -43,37 +47,49 @@ import org.thingsboard.server.queue.TbQueueProducer; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.provider.TbCoreQueueFactory; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.queue.TbClusterService; import javax.annotation.Nullable; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.function.Consumer; - -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM; -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM; -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE; -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE; -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; +import java.util.function.Function; + +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM; +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM; +import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE; +import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE; +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE; +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS; +import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION; +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey; +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey; +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey; +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; @Slf4j @Service @TbCoreComponent public class DefaultFirmwareStateService implements FirmwareStateService { + private final TbClusterService tbClusterService; private final FirmwareService firmwareService; private final DeviceService deviceService; private final DeviceProfileService deviceProfileService; private final RuleEngineTelemetryService telemetryService; private final TbQueueProducer> fwStateMsgProducer; - public DefaultFirmwareStateService(FirmwareService firmwareService, + public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService, DeviceService deviceService, DeviceProfileService deviceProfileService, RuleEngineTelemetryService telemetryService, TbCoreQueueFactory coreQueueFactory) { + this.tbClusterService = tbClusterService; this.firmwareService = firmwareService; this.deviceService = deviceService; this.deviceProfileService = deviceProfileService; @@ -83,6 +99,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService { @Override public void update(Device device, Device oldDevice) { + updateFirmware(device, oldDevice); + updateSoftware(device, oldDevice); + } + + private void updateFirmware(Device device, Device oldDevice) { FirmwareId newFirmwareId = device.getFirmwareId(); if (newFirmwareId == null) { DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); @@ -97,35 +118,84 @@ public class DefaultFirmwareStateService implements FirmwareStateService { } if (!newFirmwareId.equals(oldFirmwareId)) { // Device was updated and new firmware is different from previous firmware. - send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis()); + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE); } } else { // Device was updated and new firmware is not set. - remove(device); + remove(device, FIRMWARE); } } else if (newFirmwareId != null) { // Device was created and firmware is defined. - send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis()); + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE); + } + } + + private void updateSoftware(Device device, Device oldDevice) { + FirmwareId newSoftwareId = device.getSoftwareId(); + if (newSoftwareId == null) { + DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); + newSoftwareId = newDeviceProfile.getSoftwareId(); + } + if (oldDevice != null) { + if (newSoftwareId != null) { + FirmwareId oldSoftwareId = oldDevice.getSoftwareId(); + if (oldSoftwareId == null) { + DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId()); + oldSoftwareId = oldDeviceProfile.getSoftwareId(); + } + if (!newSoftwareId.equals(oldSoftwareId)) { + // Device was updated and new firmware is different from previous firmware. + send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE); + } + } else { + // Device was updated and new firmware is not set. + remove(device, SOFTWARE); + } + } else if (newSoftwareId != null) { + // Device was created and firmware is defined. + send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE); } } @Override - public void update(DeviceProfile deviceProfile) { + public void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged) { TenantId tenantId = deviceProfile.getTenantId(); + if (isFirmwareChanged) { + update(tenantId, deviceProfile, FIRMWARE); + } + if (isSoftwareChanged) { + update(tenantId, deviceProfile, SOFTWARE); + } + } + + private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) { + Function> getDevicesFunction; Consumer updateConsumer; + + switch (firmwareType) { + case FIRMWARE: + getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl); + break; + case SOFTWARE: + getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl); + break; + default: + log.warn("Unsupported firmware type: [{}]", firmwareType); + return; + } + if (deviceProfile.getFirmwareId() != null) { long ts = System.currentTimeMillis(); - updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts); + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType); } else { - updateConsumer = this::remove; + updateConsumer = d -> remove(d, firmwareType); } PageLink pageLink = new PageLink(100); PageData pageData; do { - pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink); - + pageData = getDevicesFunction.apply(pageLink); pageData.getData().forEach(updateConsumer); if (pageData.hasNext()) { @@ -140,16 +210,17 @@ public class DefaultFirmwareStateService implements FirmwareStateService { FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB())); DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); + FirmwareType firmwareType = FirmwareType.valueOf(msg.getType()); long ts = msg.getTs(); Device device = deviceService.findDeviceById(tenantId, deviceId); if (device == null) { log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId); } else { - FirmwareId currentFirmwareId = device.getFirmwareId(); - + FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); if (currentFirmwareId == null) { - currentFirmwareId = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()).getFirmwareId(); + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()); + currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); } if (targetFirmwareId.equals(currentFirmwareId)) { @@ -162,7 +233,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { return isSuccess; } - private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts) { + private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) { ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder() .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) @@ -170,6 +241,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()) .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()) + .setType(firmwareType.name()) .setTs(ts) .build(); @@ -183,10 +255,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService { fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); List telemetry = new ArrayList<>(); - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle()))); - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion()))); - telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts))); - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name()))); + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle()))); + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion()))); + telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name()))); telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { @Override @@ -206,7 +278,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { TenantId tenantId = device.getTenantId(); DeviceId deviceId = device.getId(); - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.INITIATED.name())); + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name())); telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { @Override @@ -221,13 +293,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService { }); List attributes = new ArrayList<>(); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle()))); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion()))); + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize()))); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm()))); + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum()))); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle()))); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion()))); - - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize()))); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm()))); - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum()))); telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { @@ -241,13 +312,15 @@ public class DefaultFirmwareStateService implements FirmwareStateService { }); } - private void remove(Device device) { - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, - Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM), + private void remove(Device device, FirmwareType firmwareType) { + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType), new FutureCallback<>() { @Override public void onSuccess(@Nullable Void tmp) { log.trace("[{}] Success remove target firmware attributes!", device.getId()); + Set keysToNotify = new HashSet<>(); + FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); + tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); } @Override diff --git a/application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java b/application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java index ac51b0d95e..8562f096c9 100644 --- a/application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java +++ b/application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java @@ -23,7 +23,7 @@ public interface FirmwareStateService { void update(Device device, Device oldDevice); - void update(DeviceProfile deviceProfile); + void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged); boolean process(ToFirmwareStateServiceMsg msg); diff --git a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java index 38bd40bdd6..bd2df29617 100644 --- a/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java +++ b/application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java @@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.TenantProfile; import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.firmware.FirmwareUtil; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -512,16 +514,17 @@ public class DefaultTransportApiService implements TransportApiService { private ListenableFuture handle(TransportProtos.GetFirmwareRequestMsg requestMsg) { TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); + FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType()); Device device = deviceService.findDeviceById(tenantId, deviceId); if (device == null) { return getEmptyTransportApiResponseFuture(); } - FirmwareId firmwareId = device.getFirmwareId(); - + FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); if (firmwareId == null) { - firmwareId = deviceProfileCache.find(device.getDeviceProfileId()).getFirmwareId(); + DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId()); + firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); } TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder(); @@ -537,6 +540,7 @@ public class DefaultTransportApiService implements TransportApiService { builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()); builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()); + builder.setType(firmwareInfo.getType().name()); builder.setTitle(firmwareInfo.getTitle()); builder.setVersion(firmwareInfo.getVersion()); builder.setFileName(firmwareInfo.getFileName()); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java index 425a21e1a2..ec36be13a5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java @@ -24,10 +24,13 @@ 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.DeviceProfile; import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; @@ -38,6 +41,7 @@ import java.util.Collections; import java.util.List; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; public abstract class BaseFirmwareControllerTest extends AbstractControllerTest { @@ -53,6 +57,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest private Tenant savedTenant; private User tenantAdmin; + private DeviceProfileId deviceProfileId; @Before public void beforeTest() throws Exception { @@ -71,6 +76,11 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest tenantAdmin.setLastName("Downs"); tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); + Assert.assertNotNull(savedDeviceProfile); + deviceProfileId = savedDeviceProfile.getId(); } @After @@ -84,6 +94,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testSaveFirmware() throws Exception { FirmwareInfo firmwareInfo = new FirmwareInfo(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); @@ -107,6 +119,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testSaveFirmwareData() throws Exception { FirmwareInfo firmwareInfo = new FirmwareInfo(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); @@ -137,6 +151,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testUpdateFirmwareFromDifferentTenant() throws Exception { FirmwareInfo firmwareInfo = new FirmwareInfo(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); @@ -150,6 +166,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testFindFirmwareInfoById() throws Exception { FirmwareInfo firmwareInfo = new FirmwareInfo(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); @@ -163,6 +181,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testFindFirmwareById() throws Exception { FirmwareInfo firmwareInfo = new FirmwareInfo(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); @@ -180,6 +200,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest @Test public void testDeleteFirmware() throws Exception { FirmwareInfo firmwareInfo = new FirmwareInfo(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); @@ -197,6 +219,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest List firmwares = new ArrayList<>(); for (int i = 0; i < 165; i++) { FirmwareInfo firmwareInfo = new FirmwareInfo(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION + i); @@ -238,6 +262,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest for (int i = 0; i < 165; i++) { FirmwareInfo firmwareInfo = new FirmwareInfo(); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION + i); @@ -257,7 +283,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest PageLink pageLink = new PageLink(24); PageData pageData; do { - pageData = doGetTypedWithPageLink("/api/firmwares/true?", + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?", new TypeReference<>() { }, pageLink); loadedFirmwaresWithData.addAll(pageData.getData()); @@ -269,7 +295,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest List loadedFirmwaresWithoutData = new ArrayList<>(); pageLink = new PageLink(24); do { - pageData = doGetTypedWithPageLink("/api/firmwares/false?", + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?", new TypeReference<>() { }, pageLink); loadedFirmwaresWithoutData.addAll(pageData.getData()); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java index 8e111fa33f..b4692ec5bf 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java @@ -65,6 +65,8 @@ public interface DeviceService { PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink); + PageData findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink); + PageData findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); PageData findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java index 907638eabb..980f8303f2 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java @@ -18,6 +18,8 @@ package org.thingsboard.server.dao.firmware; import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -37,7 +39,7 @@ public interface FirmwareService { PageData findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink); - PageData findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); + PageData findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink); void deleteFirmware(TenantId tenantId, FirmwareId firmwareId); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java index 094069dafb..f4b5fef1f9 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java @@ -93,22 +93,26 @@ public class DataConstants { public static final String USERNAME = "username"; public static final String PASSWORD = "password"; - //firmware - //telemetry - public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title"; - public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version"; - public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; - public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; - public static final String TARGET_FIRMWARE_TS = "target_fw_ts"; - public static final String FIRMWARE_STATE = "fw_state"; - - //attributes - //telemetry - public static final String FIRMWARE_TITLE = "fw_title"; - public static final String FIRMWARE_VERSION = "fw_version"; - public static final String FIRMWARE_SIZE = "fw_size"; - public static final String FIRMWARE_CHECKSUM = "fw_checksum"; - public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm"; +//<<<<<<< HEAD +//======= +// //firmware +// //telemetry +// public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title"; +// public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version"; +// public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; +// public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; +// public static final String TARGET_FIRMWARE_TS = "target_fw_ts"; +// public static final String FIRMWARE_STATE = "fw_state"; +// +// //attributes +// //telemetry +// public static final String FIRMWARE_TITLE = "fw_title"; +// public static final String FIRMWARE_VERSION = "fw_version"; +// public static final String FIRMWARE_SIZE = "fw_size"; +// public static final String FIRMWARE_CHECKSUM = "fw_checksum"; +// public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm"; +//>>>>>>> origin/master public static final String EDGE_MSG_SOURCE = "edge"; public static final String MSG_SOURCE_KEY = "source"; + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java index 5282435c0a..bce3ba703f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/Device.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/Device.java @@ -32,7 +32,7 @@ import java.io.IOException; @EqualsAndHashCode(callSuper = true) @Slf4j -public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId { +public class Device extends SearchTextBasedWithAdditionalInfo implements HasName, HasTenantId, HasCustomerId, HasFirmware { private static final long serialVersionUID = 2807343040519543363L; @@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen private byte[] deviceDataBytes; private FirmwareId firmwareId; + private FirmwareId softwareId; public Device() { super(); @@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.deviceProfileId = device.getDeviceProfileId(); this.setDeviceData(device.getDeviceData()); this.firmwareId = device.getFirmwareId(); + this.softwareId = device.getSoftwareId(); } public Device updateDevice(Device device) { @@ -79,6 +81,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.label = device.getLabel(); this.deviceProfileId = device.getDeviceProfileId(); this.setDeviceData(device.getDeviceData()); + this.setFirmwareId(device.getFirmwareId()); + this.setSoftwareId(device.getSoftwareId()); return this; } @@ -171,6 +175,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo implemen this.firmwareId = firmwareId; } + public FirmwareId getSoftwareId() { + return softwareId; + } + + public void setSoftwareId(FirmwareId softwareId) { + this.softwareId = softwareId; + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java index 660d9dec93..daf7d77af0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java @@ -36,7 +36,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn @Data @EqualsAndHashCode(callSuper = true) @Slf4j -public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId { +public class DeviceProfile extends SearchTextBased implements HasName, HasTenantId, HasFirmware { private TenantId tenantId; @NoXss @@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased implements H private FirmwareId firmwareId; + private FirmwareId softwareId; + public DeviceProfile() { super(); } @@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased implements H this.defaultQueueName = deviceProfile.getDefaultQueueName(); this.setProfileData(deviceProfile.getProfileData()); this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); + this.firmwareId = deviceProfile.getFirmwareId(); + this.softwareId = deviceProfile.getSoftwareId(); } @Override diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java index c9941e29bd..9b00e9b256 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java @@ -19,6 +19,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.TenantId; @@ -30,6 +32,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo private static final long serialVersionUID = 3168391583570815419L; private TenantId tenantId; + private DeviceProfileId deviceProfileId; + private FirmwareType type; private String title; private String version; private boolean hasData; @@ -51,6 +55,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo public FirmwareInfo(FirmwareInfo firmwareInfo) { super(firmwareInfo); this.tenantId = firmwareInfo.getTenantId(); + this.deviceProfileId = firmwareInfo.getDeviceProfileId(); + this.type = firmwareInfo.getType(); this.title = firmwareInfo.getTitle(); this.version = firmwareInfo.getVersion(); this.hasData = firmwareInfo.isHasData(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java b/common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java new file mode 100644 index 0000000000..ae05829092 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java @@ -0,0 +1,25 @@ +/** + * Copyright © 2016-2021 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.common.data; + +import org.thingsboard.server.common.data.id.FirmwareId; + +public interface HasFirmware { + + FirmwareId getFirmwareId(); + + FirmwareId getSoftwareId(); +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTopics.java b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTopics.java index 419f9c7f43..6373d41803 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTopics.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTopics.java @@ -31,6 +31,7 @@ public class MqttTopics { private static final String SUB_TOPIC = "+"; private static final String PROVISION = "/provision"; private static final String FIRMWARE = "/fw"; + private static final String SOFTWARE = "/sw"; private static final String CHUNK = "/chunk/"; private static final String ERROR = "/error"; @@ -75,9 +76,17 @@ public class MqttTopics { // v2 topics public static final String BASE_DEVICE_API_TOPIC_V2 = "v2"; - public static final String DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/"; - public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + SUB_TOPIC + CHUNK + SUB_TOPIC; + public static final String REQUEST_ID_PATTERN = "(?\\d+)"; + public static final String CHUNK_PATTERN = "(?\\d+)"; + + public static final String DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN; + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC; public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR; + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT = BASE_DEVICE_API_TOPIC_V2 + "%s" + RESPONSE + "/"+ "%s" + CHUNK + "%d"; + + public static final String DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN; + public static final String DEVICE_SOFTWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC; + public static final String DEVICE_SOFTWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + ERROR; private MqttTopics() { } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java b/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java new file mode 100644 index 0000000000..cb38b6724c --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2021 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.common.data.firmware; + +import lombok.Getter; + +public enum FirmwareKey { + + TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm"); + + @Getter + private final String value; + + FirmwareKey(String value) { + this.value = value; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java b/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java new file mode 100644 index 0000000000..5f6aa0d925 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java @@ -0,0 +1,30 @@ +/** + * Copyright © 2016-2021 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.common.data.firmware; + +import lombok.Getter; + +public enum FirmwareType { + + FIRMWARE("fw"), SOFTWARE("sw"); + + @Getter + private final String keyPrefix; + + FirmwareType(String keyPrefix) { + this.keyPrefix = keyPrefix; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java b/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java new file mode 100644 index 0000000000..646ad24173 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java @@ -0,0 +1,91 @@ +/** + * Copyright © 2016-2021 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.common.data.firmware; + +import lombok.extern.slf4j.Slf4j; +import org.thingsboard.server.common.data.HasFirmware; +import org.thingsboard.server.common.data.id.FirmwareId; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; + +@Slf4j +public class FirmwareUtil { + + public static final List ALL_FW_ATTRIBUTE_KEYS; + + public static final List ALL_SW_ATTRIBUTE_KEYS; + + static { + ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>(); + for (FirmwareKey key : FirmwareKey.values()) { + ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key)); + + } + + ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>(); + for (FirmwareKey key : FirmwareKey.values()) { + ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key)); + + } + } + + public static List getAttributeKeys(FirmwareType firmwareType) { + switch (firmwareType) { + case FIRMWARE: + return ALL_FW_ATTRIBUTE_KEYS; + case SOFTWARE: + return ALL_SW_ATTRIBUTE_KEYS; + } + return Collections.emptyList(); + } + + public static String getAttributeKey(FirmwareType type, FirmwareKey key) { + return type.getKeyPrefix() + "_" + key.getValue(); + } + + public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) { + return getTelemetryKey("target_", type, key); + } + + public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) { + return getTelemetryKey("current_", type, key); + } + + private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) { + return prefix + type.getKeyPrefix() + "_" + key.getValue(); + } + + public static String getTelemetryKey(FirmwareType type, FirmwareKey key) { + return type.getKeyPrefix() + "_" + key.getValue(); + } + + public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) { + switch (firmwareType) { + case FIRMWARE: + return entity.getFirmwareId(); + case SOFTWARE: + return entity.getSoftwareId(); + default: + log.warn("Unsupported firmware type: [{}]", firmwareType); + return null; + } + } +} diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java index 2756276dc4..ae965e327a 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java @@ -16,5 +16,5 @@ package org.thingsboard.server.common.msg.session; public enum FeatureType { - ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE + ATTRIBUTES, TELEMETRY, RPC, CLAIM, PROVISION, FIRMWARE, SOFTWARE } diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java index a6f05018f8..939197af80 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java +++ b/common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java @@ -32,7 +32,8 @@ public enum SessionMsgType { CLAIM_REQUEST(), - GET_FIRMWARE_REQUEST; + GET_FIRMWARE_REQUEST, + GET_SOFTWARE_REQUEST; private final boolean requiresRulesProcessing; diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto index 340f9c1b05..6161ae5403 100644 --- a/common/queue/src/main/proto/queue.proto +++ b/common/queue/src/main/proto/queue.proto @@ -388,16 +388,18 @@ message GetFirmwareRequestMsg { int64 deviceIdLSB = 2; int64 tenantIdMSB = 3; int64 tenantIdLSB = 4; + string type = 5; } message GetFirmwareResponseMsg { ResponseStatus responseStatus = 1; int64 firmwareIdMSB = 2; int64 firmwareIdLSB = 3; - string title = 4; - string version = 5; - string contentType = 6; - string fileName = 7; + string type = 4; + string title = 5; + string version = 6; + string contentType = 7; + string fileName = 8; } //Used to report session state to tb-Service and persist this state in the cache on the tb-Service level. @@ -711,4 +713,5 @@ message ToFirmwareStateServiceMsg { int64 deviceIdLSB = 5; int64 firmwareIdMSB = 6; int64 firmwareIdLSB = 7; + string type = 8; } diff --git a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java index 468bc244c6..e287a28aa0 100644 --- a/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java +++ b/common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java @@ -43,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.DeviceProfileTransportC import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.security.DeviceTokenCredentials; import org.thingsboard.server.common.msg.session.FeatureType; import org.thingsboard.server.common.msg.session.SessionMsgType; @@ -122,6 +123,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); } else if (featureType.get() == FeatureType.FIRMWARE) { processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST); + } else if (featureType.get() == FeatureType.SOFTWARE) { + processRequest(exchange, SessionMsgType.GET_SOFTWARE_REQUEST); } else { log.trace("Invalid feature type parameter"); exchange.respond(CoAP.ResponseCode.BAD_REQUEST); @@ -326,12 +329,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource { new CoapNoOpCallback(exchange)); break; case GET_FIRMWARE_REQUEST: - TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() - .setTenantIdMSB(sessionInfo.getTenantIdMSB()) - .setTenantIdLSB(sessionInfo.getTenantIdLSB()) - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build(); - transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); + getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE); + break; + case GET_SOFTWARE_REQUEST: + getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE); break; } } catch (AdaptorException e) { @@ -340,6 +341,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource { } } + private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) { + TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) + .setType(firmwareType.name()).build(); + transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); + } + private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { tokenToNotificationCounterMap.remove(token); return tokenToSessionIdMap.remove(token); diff --git a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java index 5489b76b03..bb9499ae29 100644 --- a/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java +++ b/common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.thingsboard.server.common.data.DeviceTransportType; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.TbTransportService; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.transport.SessionMsgListener; @@ -210,8 +211,29 @@ public class DeviceApiController implements TbTransportService { public DeferredResult getFirmware(@PathVariable("deviceToken") String deviceToken, @RequestParam(value = "title") String title, @RequestParam(value = "version") String version, - @RequestParam(value = "chunkSize", required = false, defaultValue = "0") int chunkSize, + @RequestParam(value = "size", required = false, defaultValue = "0") int size, @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { + return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE); + } + + @RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET) + public DeferredResult getSoftware(@PathVariable("deviceToken") String deviceToken, + @RequestParam(value = "title") String title, + @RequestParam(value = "version") String version, + @RequestParam(value = "size", required = false, defaultValue = "0") int size, + @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { + return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE); + } + + @RequestMapping(value = "/provision", method = RequestMethod.POST) + public DeferredResult provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) { + DeferredResult responseWriter = new DeferredResult<>(); + transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json), + new DeviceProvisionCallback(responseWriter)); + return responseWriter; + } + + private DeferredResult getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) { DeferredResult responseWriter = new DeferredResult<>(); transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { @@ -219,20 +241,13 @@ public class DeviceApiController implements TbTransportService { .setTenantIdMSB(sessionInfo.getTenantIdMSB()) .setTenantIdLSB(sessionInfo.getTenantIdLSB()) .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build(); - transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, chunkSize, chunk)); + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) + .setType(firmwareType.name()).build(); + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk)); })); return responseWriter; } - @RequestMapping(value = "/provision", method = RequestMethod.POST) - public DeferredResult provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) { - DeferredResult responseWriter = new DeferredResult<>(); - transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json), - new DeviceProvisionCallback(responseWriter)); - return responseWriter; - } - private static class DeviceAuthCallback implements TransportServiceCallback { private final TransportContext transportContext; private final DeferredResult responseWriter; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java index f52919ea3c..ae9055da9e 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java @@ -41,6 +41,9 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.cache.firmware.FirmwareDataCache; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; +import org.thingsboard.server.common.data.firmware.FirmwareKey; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.firmware.FirmwareUtil; import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.transport.TransportService; import org.thingsboard.server.common.transport.TransportServiceCallback; @@ -79,7 +82,6 @@ import java.util.stream.Collectors; import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; @@ -332,7 +334,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { String pathName = tsKvProto.getKv().getKey(); String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); - if (FIRMWARE_VERSION.equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) { + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) { this.getInfoFirmwareUpdate(lwM2MClient); } if (pathIdVer != null) { @@ -358,7 +360,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { msg.getSharedUpdatedList().forEach(tsKvProto -> { String pathName = tsKvProto.getKv().getKey(); Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); - if (FIRMWARE_VERSION.equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) { + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) { lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew); } }); diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java index 5e4d8a75b9..bb1311429c 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.TransportPayloadType; import org.thingsboard.server.common.data.device.profile.MqttTopics; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.msg.EncryptionUtil; import org.thingsboard.server.common.msg.tools.TbRateLimitsException; @@ -59,6 +60,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; import org.thingsboard.server.common.transport.service.DefaultTransportService; import org.thingsboard.server.common.transport.service.SessionMetaData; +import org.thingsboard.server.common.transport.util.SslUtil; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; @@ -68,7 +70,6 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler; import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher; -import org.thingsboard.server.common.transport.util.SslUtil; import javax.net.ssl.SSLPeerUnverifiedException; import java.io.IOException; @@ -97,6 +98,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN; +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN; /** * @author Andrew Shvayka @@ -104,7 +107,9 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; @Slf4j public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener>, SessionMsgListener { - private static final Pattern FW_PATTERN = Pattern.compile("v2/fw/request/(?\\d+)/chunk/(?\\d+)"); + private static final Pattern FW_REQUEST_PATTERN = Pattern.compile(DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN); + private static final Pattern SW_REQUEST_PATTERN = Pattern.compile(DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN); + private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE"; @@ -314,38 +319,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); - } else if ((fwMatcher = FW_PATTERN.matcher(topicName)).find()) { - String payload = mqttMsg.content().toString(UTF8); - int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0; - String requestId = fwMatcher.group("requestId"); - int chunk = Integer.parseInt(fwMatcher.group("chunk")); - - if (chunkSize > 0) { - this.fwChunkSizes.put(requestId, chunkSize); - } else { - chunkSize = fwChunkSizes.getOrDefault(requestId, 0); - } - - if (chunkSize > context.getMaxPayloadSize()) { - sendFirmwareError(ctx, PAYLOAD_TOO_LARGE); - return; - } - - String firmwareId = fwSessions.get(requestId); - - if (firmwareId != null) { - sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk); - } else { - TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo(); - TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) - .setTenantIdMSB(sessionInfo.getTenantIdMSB()) - .setTenantIdLSB(sessionInfo.getTenantIdLSB()) - .build(); - transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg, - new FirmwareCallback(ctx, mqttMsg.variableHeader().packetId(), getFirmwareRequestMsg, requestId, chunkSize, chunk)); - } + } else if ((fwMatcher = FW_REQUEST_PATTERN.matcher(topicName)).find()) { + getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.FIRMWARE); + } else if ((fwMatcher = SW_REQUEST_PATTERN.matcher(topicName)).find()) { + getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.SOFTWARE); } else { transportService.reportActivity(deviceSessionCtx.getSessionInfo()); ack(ctx, msgId); @@ -357,6 +334,41 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } + private void getFirmwareCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, FirmwareType type) { + String payload = mqttMsg.content().toString(UTF8); + int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0; + String requestId = fwMatcher.group("requestId"); + int chunk = Integer.parseInt(fwMatcher.group("chunk")); + + if (chunkSize > 0) { + this.fwChunkSizes.put(requestId, chunkSize); + } else { + chunkSize = fwChunkSizes.getOrDefault(requestId, 0); + } + + if (chunkSize > context.getMaxPayloadSize()) { + sendFirmwareError(ctx, PAYLOAD_TOO_LARGE); + return; + } + + String firmwareId = fwSessions.get(requestId); + + if (firmwareId != null) { + sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk, type); + } else { + TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo(); + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) + .setType(type.name()) + .build(); + transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg, + new FirmwareCallback(ctx, msgId, getFirmwareRequestMsg, requestId, chunkSize, chunk)); + } + } + private void ack(ChannelHandlerContext ctx, int msgId) { if (msgId > 0) { ctx.writeAndFlush(createMqttPubAckMsg(msgId)); @@ -435,7 +447,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())); fwSessions.put(requestId, firmwareId.toString()); - sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk); + sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, FirmwareType.valueOf(response.getType())); } else { sendFirmwareError(ctx, response.getResponseStatus().toString()); } @@ -448,13 +460,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement } } - private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk) { + private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, FirmwareType type) { log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); ack(ctx, msgId); try { byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk); deviceSessionCtx.getPayloadAdaptor() - .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk) + .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type) .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); if (firmwareChunk != null && chunkSize != firmwareChunk.length) { scheduler.schedule(() -> processDisconnect(ctx), 60, TimeUnit.SECONDS); @@ -504,6 +516,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC: case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC: case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC: + case MqttTopics.DEVICE_SOFTWARE_RESPONSES_TOPIC: + case MqttTopics.DEVICE_SOFTWARE_ERROR_TOPIC: registerSubQoS(topic, grantedQoSList, reqQoS); break; default: diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java index 8d0bf312a0..dbda48d15d 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java @@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.gen.transport.TransportProtos; @@ -43,6 +44,9 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT; + + /** * @author Andrew Shvayka */ @@ -151,8 +155,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) { - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/chunk/" + chunk, firmwareChunk)); + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) { + return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk)); } public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java index 2bd35df8d9..d0c1f30524 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java @@ -23,6 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; import io.netty.handler.codec.mqtt.MqttMessageType; import io.netty.handler.codec.mqtt.MqttPublishMessage; import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; @@ -77,7 +78,7 @@ public interface MqttTransportAdaptor { Optional convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; - Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException; + Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException; default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) { MqttFixedHeader mqttFixedHeader = diff --git a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java index d42e7d85e8..29df08e9c3 100644 --- a/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java +++ b/common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.device.profile.MqttTopics; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.transport.adaptor.AdaptorException; import org.thingsboard.server.common.transport.adaptor.JsonConverter; import org.thingsboard.server.common.transport.adaptor.ProtoConverter; @@ -38,6 +39,8 @@ import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionConte import java.util.Optional; +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT; + @Component @Slf4j public class ProtoMqttAdaptor implements MqttTransportAdaptor { @@ -165,8 +168,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { } @Override - public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException { - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/" + chunk, firmwareChunk)); + public Optional convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException { + return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk)); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java index a714d5ec53..34982b92b6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java @@ -83,6 +83,8 @@ public interface DeviceDao extends Dao, TenantEntityDao { PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink); + PageData findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink); + /** * Find device infos by tenantId, type and page link. * diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java index ec5f8c1360..0cc7242a4f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java @@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -406,9 +407,31 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D if (firmware == null) { throw new DataValidationException("Can't assign non-existent firmware!"); } + if (!firmware.getType().equals(FirmwareType.FIRMWARE)) { + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); + } if (firmware.getData() == null) { throw new DataValidationException("Can't assign firmware with empty data!"); } + if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) { + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + } + } + + if (deviceProfile.getSoftwareId() != null) { + Firmware software = firmwareService.findFirmwareById(tenantId, deviceProfile.getSoftwareId()); + if (software == null) { + throw new DataValidationException("Can't assign non-existent software!"); + } + if (!software.getType().equals(FirmwareType.SOFTWARE)) { + throw new DataValidationException("Can't assign software with type: " + software.getType()); + } + if (software.getData() == null) { + throw new DataValidationException("Can't assign software with empty data!"); + } + if (!software.getDeviceProfileId().equals(deviceProfile.getId())) { + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + } } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java index 02d8bd2b15..937efed0b5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration; import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; import org.thingsboard.server.common.data.edge.Edge; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -361,13 +362,22 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe @Override public PageData findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) { - log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyFirmware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateString(type, "Incorrect type " + type); validatePageLink(pageLink); return deviceDao.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId.getId(), type, pageLink); } + @Override + public PageData findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink) { + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptySoftware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateString(type, "Incorrect type " + type); + validatePageLink(pageLink); + return deviceDao.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId.getId(), type, pageLink); + } + @Override public PageData findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); @@ -696,9 +706,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe if (firmware == null) { throw new DataValidationException("Can't assign non-existent firmware!"); } + if (!firmware.getType().equals(FirmwareType.FIRMWARE)) { + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); + } if (firmware.getData() == null) { throw new DataValidationException("Can't assign firmware with empty data!"); } + if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) { + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + } + } + + if (device.getSoftwareId() != null) { + Firmware software = firmwareService.findFirmwareById(tenantId, device.getSoftwareId()); + if (software == null) { + throw new DataValidationException("Can't assign non-existent software!"); + } + if (!software.getType().equals(FirmwareType.SOFTWARE)) { + throw new DataValidationException("Can't assign software with type: " + software.getType()); + } + if (software.getData() == null) { + throw new DataValidationException("Can't assign software with empty data!"); + } + if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) { + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); + } } } }; diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java b/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java index 5876947440..6ac3df8292 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java @@ -27,13 +27,17 @@ import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import org.thingsboard.server.cache.firmware.FirmwareDataCache; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.FirmwareId; 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.device.DeviceProfileDao; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -56,6 +60,7 @@ public class BaseFirmwareService implements FirmwareService { public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; private final TenantDao tenantDao; + private final DeviceProfileDao deviceProfileDao; private final FirmwareDao firmwareDao; private final FirmwareInfoDao firmwareInfoDao; private final CacheManager cacheManager; @@ -124,7 +129,8 @@ public class BaseFirmwareService implements FirmwareService { public ListenableFuture findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) { log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId); validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); - return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); } + return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); + } @Override public PageData findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) { @@ -135,11 +141,11 @@ public class BaseFirmwareService implements FirmwareService { } @Override - public PageData findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) { + public PageData findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) { log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validatePageLink(pageLink); - return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink); + return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink); } @Override @@ -157,6 +163,10 @@ public class BaseFirmwareService implements FirmwareService { throw new DataValidationException("The firmware referenced by the devices cannot be deleted!"); } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!"); + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) { + throw new DataValidationException("The software referenced by the devices cannot be deleted!"); + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) { + throw new DataValidationException("The software referenced by the device profile cannot be deleted!"); } else { throw t; } @@ -173,29 +183,15 @@ public class BaseFirmwareService implements FirmwareService { private DataValidator firmwareInfoValidator = new DataValidator<>() { @Override - protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmware) { - if (firmware.getTenantId() == null) { - throw new DataValidationException("Firmware should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Firmware is referencing to non-existent tenant!"); - } - } - - if (StringUtils.isEmpty(firmware.getTitle())) { - throw new DataValidationException("Firmware title should be specified!"); - } - - if (StringUtils.isEmpty(firmware.getVersion())) { - throw new DataValidationException("Firmware version should be specified!"); - } + protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmwareInfo) { + validateImpl(firmwareInfo); } @Override protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) { FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId()); + validateUpdateDeviceProfile(firmware, firmwareOld); BaseFirmwareService.validateUpdate(firmware, firmwareOld); } }; @@ -204,22 +200,7 @@ public class BaseFirmwareService implements FirmwareService { @Override protected void validateDataImpl(TenantId tenantId, Firmware firmware) { - if (firmware.getTenantId() == null) { - throw new DataValidationException("Firmware should be assigned to tenant!"); - } else { - Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId()); - if (tenant == null) { - throw new DataValidationException("Firmware is referencing to non-existent tenant!"); - } - } - - if (StringUtils.isEmpty(firmware.getTitle())) { - throw new DataValidationException("Firmware title should be specified!"); - } - - if (StringUtils.isEmpty(firmware.getVersion())) { - throw new DataValidationException("Firmware version should be specified!"); - } + validateImpl(firmware); if (StringUtils.isEmpty(firmware.getFileName())) { throw new DataValidationException("Firmware file name should be specified!"); @@ -267,6 +248,7 @@ public class BaseFirmwareService implements FirmwareService { protected void validateUpdate(TenantId tenantId, Firmware firmware) { Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId()); + validateUpdateDeviceProfile(firmware, firmwareOld); BaseFirmwareService.validateUpdate(firmware, firmwareOld); if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) { @@ -275,7 +257,19 @@ public class BaseFirmwareService implements FirmwareService { } }; + private void validateUpdateDeviceProfile(FirmwareInfo firmware, FirmwareInfo firmwareOld) { + if (firmwareOld.getDeviceProfileId() != null && !firmwareOld.getDeviceProfileId().equals(firmware.getDeviceProfileId())) { + if (firmwareInfoDao.isFirmwareUsed(firmwareOld.getId(), firmware.getType(), firmwareOld.getDeviceProfileId())) { + throw new DataValidationException("Can`t update deviceProfileId because firmware is already in use!"); + } + } + } + private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) { + if (!firmwareOld.getType().equals(firmware.getType())) { + throw new DataValidationException("Updating type is prohibited!"); + } + if (!firmwareOld.getTitle().equals(firmware.getTitle())) { throw new DataValidationException("Updating firmware title is prohibited!"); } @@ -305,6 +299,36 @@ public class BaseFirmwareService implements FirmwareService { } } + private void validateImpl(FirmwareInfo firmwareInfo) { + if (firmwareInfo.getTenantId() == null) { + throw new DataValidationException("Firmware should be assigned to tenant!"); + } else { + Tenant tenant = tenantDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getTenantId().getId()); + if (tenant == null) { + throw new DataValidationException("Firmware is referencing to non-existent tenant!"); + } + } + + if (firmwareInfo.getDeviceProfileId() != null) { + DeviceProfile deviceProfile = deviceProfileDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getDeviceProfileId().getId()); + if (deviceProfile == null) { + throw new DataValidationException("Firmware is referencing to non-existent device profile!"); + } + } + + if (firmwareInfo.getType() == null) { + throw new DataValidationException("Type should be specified!"); + } + + if (StringUtils.isEmpty(firmwareInfo.getTitle())) { + throw new DataValidationException("Firmware title should be specified!"); + } + + if (StringUtils.isEmpty(firmwareInfo.getVersion())) { + throw new DataValidationException("Firmware version should be specified!"); + } + } + private PaginatedRemover tenantFirmwareRemover = new PaginatedRemover<>() { diff --git a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java index a2af06ad9a..7cb6c3a57f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java @@ -16,6 +16,9 @@ package org.thingsboard.server.dao.firmware; import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -27,6 +30,8 @@ public interface FirmwareInfoDao extends Dao { PageData findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink); - PageData findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); + PageData findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink); + + boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index ae3b974054..bc0afe630d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -154,6 +154,7 @@ public class ModelConstants { public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id"; public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data"; public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id"; + public static final String DEVICE_SOFTWARE_ID_PROPERTY = "software_id"; public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; public static final String DEVICE_BY_TENANT_BY_TYPE_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_by_type_and_search_text"; @@ -178,6 +179,7 @@ public class ModelConstants { public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name"; public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key"; public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id"; + public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id"; /** * Cassandra entityView constants. @@ -476,6 +478,8 @@ public class ModelConstants { */ public static final String FIRMWARE_TABLE_NAME = "firmware"; public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; + public static final String FIRMWARE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id"; + public static final String FIRMWARE_TYPE_COLUMN = "type"; public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY; public static final String FIRMWARE_VERSION_COLUMN = "version"; public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java index 8dee576611..ed6a871fea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java @@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.hibernate.annotations.TypeDefs; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.device.data.DeviceData; import org.thingsboard.server.common.data.id.CustomerId; @@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.SearchTextEntity; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.dao.util.mapping.JsonBinaryType; import org.thingsboard.server.dao.util.mapping.JsonStringType; @@ -43,8 +43,8 @@ import java.util.UUID; @Data @EqualsAndHashCode(callSuper = true) @TypeDefs({ - @TypeDef(name = "json", typeClass = JsonStringType.class), - @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) + @TypeDef(name = "json", typeClass = JsonStringType.class), + @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) }) @MappedSuperclass public abstract class AbstractDeviceEntity extends BaseSqlEntity implements SearchTextEntity { @@ -77,6 +77,9 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti @Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid") private UUID firmwareId; + @Column(name = ModelConstants.DEVICE_SOFTWARE_ID_PROPERTY, columnDefinition = "uuid") + private UUID softwareId; + @Type(type = "jsonb") @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb") private JsonNode deviceData; @@ -102,6 +105,9 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti if (device.getFirmwareId() != null) { this.firmwareId = device.getFirmwareId().getId(); } + if (device.getSoftwareId() != null) { + this.softwareId = device.getSoftwareId().getId(); + } this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class); this.name = device.getName(); this.type = device.getType(); @@ -122,6 +128,7 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti this.searchText = deviceEntity.getSearchText(); this.additionalInfo = deviceEntity.getAdditionalInfo(); this.firmwareId = deviceEntity.getFirmwareId(); + this.softwareId = deviceEntity.getSoftwareId(); } @Override @@ -149,6 +156,9 @@ public abstract class AbstractDeviceEntity extends BaseSqlEnti if (firmwareId != null) { device.setFirmwareId(new FirmwareId(firmwareId)); } + if (softwareId != null) { + device.setSoftwareId(new FirmwareId(softwareId)); + } device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class)); device.setName(name); device.setType(type); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java index e437c87fdd..e4c32a44f0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java @@ -21,9 +21,10 @@ import lombok.Data; import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; +import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.DeviceProfile; -import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceProfileProvisionType; +import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.device.profile.DeviceProfileData; import org.thingsboard.server.common.data.id.DeviceProfileId; @@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.SearchTextEntity; -import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.dao.util.mapping.JsonBinaryType; import javax.persistence.Column; @@ -87,12 +87,15 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb") private JsonNode profileData; - @Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY) + @Column(name = ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY) private String provisionDeviceKey; - @Column(name=ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY) + @Column(name = ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY) private UUID firmwareId; + @Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY) + private UUID softwareId; + public DeviceProfileEntity() { super(); } @@ -120,6 +123,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl if (deviceProfile.getFirmwareId() != null) { this.firmwareId = deviceProfile.getFirmwareId().getId(); } + if (deviceProfile.getSoftwareId() != null) { + this.firmwareId = deviceProfile.getSoftwareId().getId(); + } } @Override @@ -160,6 +166,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity impl deviceProfile.setFirmwareId(new FirmwareId(firmwareId)); } + if (softwareId != null) { + deviceProfile.setSoftwareId(new FirmwareId(softwareId)); + } + return deviceProfile; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java index 690bb4e6d9..4f3aded716 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java @@ -21,6 +21,8 @@ import lombok.EqualsAndHashCode; import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.Firmware; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -30,6 +32,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.Lob; import javax.persistence.Table; import java.nio.ByteBuffer; @@ -40,10 +44,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @@ -57,6 +63,13 @@ public class FirmwareEntity extends BaseSqlEntity implements SearchTex @Column(name = FIRMWARE_TENANT_ID_COLUMN) private UUID tenantId; + @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN) + private UUID deviceProfileId; + + @Enumerated(EnumType.STRING) + @Column(name = FIRMWARE_TYPE_COLUMN) + private FirmwareType type; + @Column(name = FIRMWARE_TITLE_COLUMN) private String title; @@ -97,6 +110,10 @@ public class FirmwareEntity extends BaseSqlEntity implements SearchTex this.createdTime = firmware.getCreatedTime(); this.setUuid(firmware.getUuidId()); this.tenantId = firmware.getTenantId().getId(); + if (firmware.getDeviceProfileId() != null) { + this.deviceProfileId = firmware.getDeviceProfileId().getId(); + } + this.type = firmware.getType(); this.title = firmware.getTitle(); this.version = firmware.getVersion(); this.fileName = firmware.getFileName(); @@ -123,6 +140,10 @@ public class FirmwareEntity extends BaseSqlEntity implements SearchTex Firmware firmware = new Firmware(new FirmwareId(id)); firmware.setCreatedTime(createdTime); firmware.setTenantId(new TenantId(tenantId)); + if (deviceProfileId != null) { + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); + } + firmware.setType(type); firmware.setTitle(title); firmware.setVersion(version); firmware.setFileName(fileName); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java index 3549291e4b..bb62db5ea4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java @@ -22,6 +22,8 @@ import org.hibernate.annotations.Type; import org.hibernate.annotations.TypeDef; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -31,6 +33,8 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.Table; import javax.persistence.Transient; import java.util.UUID; @@ -38,13 +42,13 @@ import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; @@ -58,6 +62,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S @Column(name = FIRMWARE_TENANT_ID_COLUMN) private UUID tenantId; + @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN) + private UUID deviceProfileId; + + @Enumerated(EnumType.STRING) + @Column(name = FIRMWARE_TYPE_COLUMN) + private FirmwareType type; + @Column(name = FIRMWARE_TITLE_COLUMN) private String title; @@ -97,6 +108,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S this.createdTime = firmware.getCreatedTime(); this.setUuid(firmware.getUuidId()); this.tenantId = firmware.getTenantId().getId(); + this.type = firmware.getType(); + if (firmware.getDeviceProfileId() != null) { + this.deviceProfileId = firmware.getDeviceProfileId().getId(); + } this.title = firmware.getTitle(); this.version = firmware.getVersion(); this.fileName = firmware.getFileName(); @@ -107,12 +122,14 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S this.additionalInfo = firmware.getAdditionalInfo(); } - public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version, + public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, FirmwareType type, String title, String version, String fileName, String contentType, String checksumAlgorithm, String checksum, Long dataSize, Object additionalInfo, boolean hasData) { this.id = id; this.createdTime = createdTime; this.tenantId = tenantId; + this.deviceProfileId = deviceProfileId; + this.type = type; this.title = title; this.version = version; this.fileName = fileName; @@ -139,6 +156,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity implements S FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id)); firmware.setCreatedTime(createdTime); firmware.setTenantId(new TenantId(tenantId)); + if (deviceProfileId != null) { + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); + } + firmware.setType(type); firmware.setTitle(title); firmware.setVersion(version); firmware.setFileName(fileName); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java index b6d723f812..3bbe18f2ab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java @@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, + @Param("type") String type, + @Param("textSearch") String textSearch, + Pageable pageable); + @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + "FROM DeviceEntity d " + "LEFT JOIN CustomerEntity c on c.id = d.customerId " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java index 1291dc09e4..344286d7ac 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java @@ -164,6 +164,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao DaoUtil.toPageable(pageLink))); } + @Override + public PageData findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) { + return DaoUtil.toPageData( + deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull( + tenantId, + type, + Objects.toString(pageLink.getTextSearch(), ""), + DaoUtil.toPageable(pageLink))); + } + @Override public PageData findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) { return DaoUtil.toPageData( diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java index 5601019c13..dab6ce3304 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java @@ -20,27 +20,41 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.query.Param; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; import java.util.UUID; public interface FirmwareInfoRepository extends CrudRepository { - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + "f.tenantId = :tenantId " + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") Page findAllByTenantId(@Param("tenantId") UUID tenantId, @Param("searchText") String searchText, Pageable pageable); - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE " + "f.tenantId = :tenantId " + + "AND f.deviceProfileId = :deviceProfileId " + + "AND f.type = :type " + "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") - Page findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId, - @Param("hasData") boolean hasData, - @Param("searchText") String searchText, - Pageable pageable); + Page findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, + @Param("deviceProfileId") UUID deviceProfileId, + @Param("type") FirmwareType type, + @Param("hasData") boolean hasData, + @Param("searchText") String searchText, + Pageable pageable); - @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id") + @Query("SELECT new FirmwareInfoEntity(f.id, f.createdTime, f.tenantId, f.deviceProfileId, f.type, f.title, f.version, f.fileName, f.contentType, f.checksumAlgorithm, f.checksum, f.dataSize, f.additionalInfo, f.data IS NOT NULL) FROM FirmwareEntity f WHERE f.id = :id") FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id); + + @Query(value = "SELECT exists(SELECT * " + + "FROM device_profile AS dp " + + "LEFT JOIN device AS d ON dp.id = d.device_profile_id " + + "WHERE dp.id = :deviceProfileId AND " + + "(('FIRMWARE' = :type AND (dp.firmware_id = :firmwareId OR d.firmware_id = :firmwareId)) " + + "OR ('SOFTWARE' = :type AND (dp.software_id = :firmwareId or d.software_id = :firmwareId))))", nativeQuery = true) + boolean isFirmwareUsed(@Param("firmwareId") UUID firmwareId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java index 3b80c1c7e5..8cae33a057 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java @@ -20,6 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.FirmwareInfo; +import org.thingsboard.server.common.data.firmware.FirmwareType; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.FirmwareId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -73,12 +76,19 @@ public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) { + public PageData findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) { return DaoUtil.toPageData(firmwareInfoRepository - .findAllByTenantIdAndHasData( + .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData( tenantId.getId(), + deviceProfileId.getId(), + firmwareType, hasData, Objects.toString(pageLink.getTextSearch(), ""), DaoUtil.toPageable(pageLink))); } + + @Override + public boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId) { + return firmwareInfoRepository.isFirmwareUsed(firmwareId.getId(), deviceProfileId.getId(), type.name()); + } } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 6dffac26b0..17017d2619 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -162,6 +162,8 @@ CREATE TABLE IF NOT EXISTS firmware ( id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, + device_profile_id uuid , + type varchar(32) NOT NULL, title varchar(255) NOT NULL, version varchar(255) NOT NULL, file_name varchar(255), @@ -188,13 +190,15 @@ CREATE TABLE IF NOT EXISTS device_profile ( is_default boolean, tenant_id uuid, firmware_id uuid, + software_id uuid, default_rule_chain_id uuid, default_queue_name varchar(255), provision_device_key varchar, CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id), + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id) ); CREATE TABLE IF NOT EXISTS device ( @@ -210,9 +214,11 @@ CREATE TABLE IF NOT EXISTS device ( search_text varchar(255), tenant_id uuid, firmware_id uuid, + software_id uuid, CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id), + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id) ); CREATE TABLE IF NOT EXISTS device_credentials ( diff --git a/dao/src/main/resources/sql/schema-entities-idx.sql b/dao/src/main/resources/sql/schema-entities-idx.sql index 91ec86a8c4..28bbe4311e 100644 --- a/dao/src/main/resources/sql/schema-entities-idx.sql +++ b/dao/src/main/resources/sql/schema-entities-idx.sql @@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type); CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc); CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time); + diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index f71401ed57..5b11e1a269 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware ( id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, created_time bigint NOT NULL, tenant_id uuid NOT NULL, + device_profile_id uuid , + type varchar(32) NOT NULL, title varchar(255) NOT NULL, version varchar(255) NOT NULL, file_name varchar(255), @@ -191,6 +193,7 @@ CREATE TABLE IF NOT EXISTS firmware ( additional_info varchar, search_text varchar(255), CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) +-- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS device_profile ( @@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile ( is_default boolean, tenant_id uuid, firmware_id uuid, + software_id uuid, default_rule_chain_id uuid, default_queue_name varchar(255), provision_device_key varchar, CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id), + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id) ); +-- We will use one-to-many relation in the first release and extend this feature in case of user requests +-- CREATE TABLE IF NOT EXISTS device_profile_firmware ( +-- device_profile_id uuid NOT NULL, +-- firmware_id uuid NOT NULL, +-- CONSTRAINT device_profile_firmware_unq_key UNIQUE (device_profile_id, firmware_id), +-- CONSTRAINT fk_device_profile_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE, +-- CONSTRAINT fk_device_profile_firmware_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id) ON DELETE CASCADE, +-- ); + CREATE TABLE IF NOT EXISTS device ( id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, created_time bigint NOT NULL, @@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device ( search_text varchar(255), tenant_id uuid, firmware_id uuid, + software_id uuid, CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id), + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id) ); CREATE TABLE IF NOT EXISTS device_credentials ( diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java index e377baa2cb..618135fdd3 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; import org.thingsboard.server.common.data.DeviceTransportType; import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.firmware.FirmwareType; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -43,6 +44,8 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.stream.Collectors; +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; + public class BaseDeviceProfileServiceTest extends AbstractServiceTest { private IdComparator idComparator = new IdComparator<>(); @@ -97,6 +100,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(savedDeviceProfile.getId()); + firmware.setType(FIRMWARE); firmware.setTitle("my firmware"); firmware.setVersion("v1.0"); firmware.setFileName("test.txt"); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java index 6d7b695ef1..4fcdd93bce 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java @@ -20,10 +20,13 @@ import org.apache.commons.lang3.RandomStringUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceInfo; +import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.EntitySubtype; import org.thingsboard.server.common.data.Firmware; import org.thingsboard.server.common.data.Tenant; @@ -42,6 +45,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; public abstract class BaseDeviceServiceTest extends AbstractServiceTest { @@ -66,6 +70,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { tenantProfileService.deleteTenantProfiles(anotherTenantId); } + @Rule + public ExpectedException thrown = ExpectedException.none(); + @Test public void testSaveDevicesWithoutMaxDeviceLimit() { Device device = this.saveDevice(tenantId, "My device"); @@ -183,6 +190,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(device.getDeviceProfileId()); + firmware.setType(FIRMWARE); firmware.setTitle("my firmware"); firmware.setVersion("v1.0"); firmware.setFileName("test.txt"); @@ -198,6 +207,40 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId()); Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); } + + @Test + public void testAssignFirmwareToDeviceWithDifferentDeviceProfile() { + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("My device"); + device.setType("default"); + Device savedDevice = deviceService.saveDevice(device); + + Assert.assertNotNull(savedDevice); + + DeviceProfile deviceProfile = createDeviceProfile(tenantId, "New device Profile"); + DeviceProfile savedProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + Assert.assertNotNull(savedProfile); + + Firmware firmware = new Firmware(); + firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(savedProfile.getId()); + firmware.setType(FIRMWARE); + firmware.setTitle("my firmware"); + firmware.setVersion("v1.0"); + firmware.setFileName("test.txt"); + firmware.setContentType("text/plain"); + firmware.setChecksumAlgorithm("sha256"); + firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); + firmware.setData(ByteBuffer.wrap(new byte[]{1})); + Firmware savedFirmware = firmwareService.saveFirmware(firmware); + + savedDevice.setFirmwareId(savedFirmware.getId()); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Can't assign firmware with different deviceProfile!"); + deviceService.saveDevice(savedDevice); + } @Test(expected = DataValidationException.class) public void testSaveDeviceWithEmptyName() { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java index bf96d09922..06a2b7084f 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java @@ -19,13 +19,16 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; 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.Firmware; import org.thingsboard.server.common.data.FirmwareInfo; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -36,6 +39,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; + public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { public static final String TITLE = "My firmware"; @@ -50,6 +55,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { private TenantId tenantId; + private DeviceProfileId deviceProfileId; + @Before public void before() { Tenant tenant = new Tenant(); @@ -57,8 +64,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Tenant savedTenant = tenantService.saveTenant(tenant); Assert.assertNotNull(savedTenant); tenantId = savedTenant.getId(); + + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + Assert.assertNotNull(savedDeviceProfile); + deviceProfileId = savedDeviceProfile.getId(); } + @Rule + public ExpectedException thrown = ExpectedException.none(); + @After public void after() { tenantService.deleteTenant(tenantId); @@ -68,6 +83,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { public void testSaveFirmware() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -99,6 +116,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { public void testSaveFirmwareInfoAndUpdateWithData() { FirmwareInfo firmwareInfo = new FirmwareInfo(); firmwareInfo.setTenantId(tenantId); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); @@ -112,6 +131,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Firmware firmware = new Firmware(savedFirmwareInfo.getId()); firmware.setCreatedTime(firmwareInfo.getCreatedTime()); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -135,9 +156,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId()); } - @Test(expected = DataValidationException.class) + @Test public void testSaveFirmwareWithEmptyTenant() { Firmware firmware = new Firmware(); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -145,65 +168,108 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware should be assigned to tenant!"); firmwareService.saveFirmware(firmware); } - @Test(expected = DataValidationException.class) + @Test + public void testSaveFirmwareWithEmptyType() { + Firmware firmware = new Firmware(); + firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setTitle(TITLE); + firmware.setVersion(VERSION); + firmware.setFileName(FILE_NAME); + firmware.setContentType(CONTENT_TYPE); + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); + firmware.setChecksum(CHECKSUM); + firmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Type should be specified!"); + firmwareService.saveFirmware(firmware); + } + + @Test public void testSaveFirmwareWithEmptyTitle() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); firmware.setContentType(CONTENT_TYPE); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware title should be specified!"); firmwareService.saveFirmware(firmware); } - @Test(expected = DataValidationException.class) + @Test public void testSaveFirmwareWithEmptyFileName() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setContentType(CONTENT_TYPE); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware file name should be specified!"); firmwareService.saveFirmware(firmware); } - @Test(expected = DataValidationException.class) + @Test public void testSaveFirmwareWithEmptyContentType() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware content type should be specified!"); firmwareService.saveFirmware(firmware); } - @Test(expected = DataValidationException.class) + @Test public void testSaveFirmwareWithEmptyData() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); firmware.setContentType(CONTENT_TYPE); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware data should be specified!"); firmwareService.saveFirmware(firmware); } - @Test(expected = DataValidationException.class) + @Test public void testSaveFirmwareWithInvalidTenant() { Firmware firmware = new Firmware(); firmware.setTenantId(new TenantId(Uuids.timeBased())); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -211,41 +277,77 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setChecksum(CHECKSUM); firmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware is referencing to non-existent tenant!"); firmwareService.saveFirmware(firmware); } - @Test(expected = DataValidationException.class) + @Test + public void testSaveFirmwareWithInvalidDeviceProfileId() { + Firmware firmware = new Firmware(); + firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(new DeviceProfileId(Uuids.timeBased())); + firmware.setType(FIRMWARE); + firmware.setTitle(TITLE); + firmware.setVersion(VERSION); + firmware.setFileName(FILE_NAME); + firmware.setContentType(CONTENT_TYPE); + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); + firmware.setChecksum(CHECKSUM); + firmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware is referencing to non-existent device profile!"); + firmwareService.saveFirmware(firmware); + } + + @Test public void testSaveFirmwareWithEmptyChecksum() { Firmware firmware = new Firmware(); - firmware.setTenantId(new TenantId(Uuids.timeBased())); + firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); firmware.setContentType(CONTENT_TYPE); firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); firmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware checksum should be specified!"); firmwareService.saveFirmware(firmware); } - @Test(expected = DataValidationException.class) + @Test public void testSaveFirmwareInfoWithExistingTitleAndVersion() { FirmwareInfo firmwareInfo = new FirmwareInfo(); firmwareInfo.setTenantId(tenantId); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION); firmwareService.saveFirmwareInfo(firmwareInfo); FirmwareInfo newFirmwareInfo = new FirmwareInfo(); newFirmwareInfo.setTenantId(tenantId); + newFirmwareInfo.setDeviceProfileId(deviceProfileId); + newFirmwareInfo.setType(FIRMWARE); newFirmwareInfo.setTitle(TITLE); newFirmwareInfo.setVersion(VERSION); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware with such title and version already exists!"); firmwareService.saveFirmwareInfo(newFirmwareInfo); } - @Test(expected = DataValidationException.class) + @Test public void testSaveFirmwareWithExistingTitleAndVersion() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -257,18 +359,27 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Firmware newFirmware = new Firmware(); newFirmware.setTenantId(tenantId); + newFirmware.setDeviceProfileId(deviceProfileId); + newFirmware.setType(FIRMWARE); newFirmware.setTitle(TITLE); newFirmware.setVersion(VERSION); newFirmware.setFileName(FILE_NAME); newFirmware.setContentType(CONTENT_TYPE); + newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); + newFirmware.setChecksum(CHECKSUM); newFirmware.setData(DATA); + + thrown.expect(DataValidationException.class); + thrown.expectMessage("Firmware with such title and version already exists!"); firmwareService.saveFirmware(newFirmware); } - @Test(expected = DataValidationException.class) + @Test public void testDeleteFirmwareWithReferenceByDevice() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -281,11 +392,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Device device = new Device(); device.setTenantId(tenantId); device.setName("My device"); - device.setType("default"); + device.setDeviceProfileId(deviceProfileId); device.setFirmwareId(savedFirmware.getId()); Device savedDevice = deviceService.saveDevice(device); try { + thrown.expect(DataValidationException.class); + thrown.expectMessage("The firmware referenced by the devices cannot be deleted!"); firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); } finally { deviceService.deleteDevice(tenantId, savedDevice.getId()); @@ -293,10 +406,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { } } - @Test(expected = DataValidationException.class) - public void testDeleteFirmwareWithReferenceByDeviceProfile() { + @Test + public void testUpdateDeviceProfileIdWithReferenceByDevice() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -306,12 +421,81 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { firmware.setData(DATA); Firmware savedFirmware = firmwareService.saveFirmware(firmware); - DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); - deviceProfile.setFirmwareId(savedFirmware.getId()); + Device device = new Device(); + device.setTenantId(tenantId); + device.setName("My device"); + device.setDeviceProfileId(deviceProfileId); + device.setFirmwareId(savedFirmware.getId()); + Device savedDevice = deviceService.saveDevice(device); + + try { + thrown.expect(DataValidationException.class); + thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!"); + savedFirmware.setDeviceProfileId(null); + firmwareService.saveFirmware(savedFirmware); + } finally { + deviceService.deleteDevice(tenantId, savedDevice.getId()); + firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + } + } + + @Test + public void testDeleteFirmwareWithReferenceByDeviceProfile() { + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + Firmware firmware = new Firmware(); + firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(savedDeviceProfile.getId()); + firmware.setType(FIRMWARE); + firmware.setTitle(TITLE); + firmware.setVersion(VERSION); + firmware.setFileName(FILE_NAME); + firmware.setContentType(CONTENT_TYPE); + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); + firmware.setChecksum(CHECKSUM); + firmware.setData(DATA); + Firmware savedFirmware = firmwareService.saveFirmware(firmware); + + savedDeviceProfile.setFirmwareId(savedFirmware.getId()); + deviceProfileService.saveDeviceProfile(savedDeviceProfile); + try { + thrown.expect(DataValidationException.class); + thrown.expectMessage("The firmware referenced by the device profile cannot be deleted!"); + firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + } finally { + deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); + } + } + + @Test + public void testUpdateDeviceProfileIdWithReferenceByDeviceProfile() { + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); + + Firmware firmware = new Firmware(); + firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(savedDeviceProfile.getId()); + firmware.setType(FIRMWARE); + firmware.setTitle(TITLE); + firmware.setVersion(VERSION); + firmware.setFileName(FILE_NAME); + firmware.setContentType(CONTENT_TYPE); + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); + firmware.setChecksum(CHECKSUM); + firmware.setData(DATA); + Firmware savedFirmware = firmwareService.saveFirmware(firmware); + + savedDeviceProfile.setFirmwareId(savedFirmware.getId()); + deviceProfileService.saveDeviceProfile(savedDeviceProfile); + + try { + thrown.expect(DataValidationException.class); + thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!"); + savedFirmware.setDeviceProfileId(null); + firmwareService.saveFirmware(savedFirmware); } finally { deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); @@ -322,6 +506,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { public void testFindFirmwareById() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -341,6 +527,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { public void testFindFirmwareInfoById() { FirmwareInfo firmware = new FirmwareInfo(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware); @@ -355,6 +543,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { public void testDeleteFirmware() { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION); firmware.setFileName(FILE_NAME); @@ -377,6 +567,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { for (int i = 0; i < 165; i++) { Firmware firmware = new Firmware(); firmware.setTenantId(tenantId); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(TITLE); firmware.setVersion(VERSION + i); firmware.setFileName(FILE_NAME); @@ -420,6 +612,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { for (int i = 0; i < 165; i++) { FirmwareInfo firmwareInfo = new FirmwareInfo(); firmwareInfo.setTenantId(tenantId); + firmwareInfo.setDeviceProfileId(deviceProfileId); + firmwareInfo.setType(FIRMWARE); firmwareInfo.setTitle(TITLE); firmwareInfo.setVersion(VERSION + i); firmwareInfo.setFileName(FILE_NAME); @@ -434,7 +628,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { PageLink pageLink = new PageLink(16); PageData pageData; do { - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, false, pageLink); + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -450,6 +644,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { Firmware firmware = new Firmware(f.getId()); firmware.setCreatedTime(f.getCreatedTime()); firmware.setTenantId(f.getTenantId()); + firmware.setDeviceProfileId(deviceProfileId); + firmware.setType(FIRMWARE); firmware.setTitle(f.getTitle()); firmware.setVersion(f.getVersion()); firmware.setFileName(FILE_NAME); @@ -465,7 +661,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { loadedFirmwares = new ArrayList<>(); pageLink = new PageLink(16); do { - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, true, pageLink); + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink); loadedFirmwares.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); diff --git a/ui-ngx/src/app/core/http/entity.service.ts b/ui-ngx/src/app/core/http/entity.service.ts index 04d2db610d..c5ef4b8db4 100644 --- a/ui-ngx/src/app/core/http/entity.service.ts +++ b/ui-ngx/src/app/core/http/entity.service.ts @@ -361,7 +361,7 @@ export class EntityService { break; case EntityType.FIRMWARE: pageLink.sortOrder.property = 'title'; - entitiesObservable = this.firmwareService.getFirmwares(pageLink, true, config); + entitiesObservable = this.firmwareService.getFirmwares(pageLink, config); break; } return entitiesObservable; diff --git a/ui-ngx/src/app/core/http/firmware.service.ts b/ui-ngx/src/app/core/http/firmware.service.ts index 7bf42fb49e..8c3249aee6 100644 --- a/ui-ngx/src/app/core/http/firmware.service.ts +++ b/ui-ngx/src/app/core/http/firmware.service.ts @@ -20,7 +20,7 @@ import { PageLink } from '@shared/models/page/page-link'; import { defaultHttpOptionsFromConfig, defaultHttpUploadOptions, RequestConfig } from '@core/http/http-utils'; import { Observable } from 'rxjs'; import { PageData } from '@shared/models/page/page-data'; -import { Firmware, FirmwareInfo } from '@shared/models/firmware.models'; +import { Firmware, FirmwareInfo, FirmwareType } from '@shared/models/firmware.models'; import { catchError, map, mergeMap } from 'rxjs/operators'; import { deepClone, isDefinedAndNotNull } from '@core/utils'; @@ -34,12 +34,13 @@ export class FirmwareService { } - public getFirmwares(pageLink: PageLink, hasData?: boolean, config?: RequestConfig): Observable> { - let url = `/api/firmwares`; - if (isDefinedAndNotNull(hasData)) { - url += `/${hasData}`; - } - url += `${pageLink.toQuery()}`; + public getFirmwares(pageLink: PageLink, config?: RequestConfig): Observable> { + return this.http.get>(`/api/firmwares${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config)); + } + + public getFirmwaresInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: FirmwareType, + hasData = true, config?: RequestConfig): Observable> { + const url = `/api/firmwares/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`; return this.http.get>(url, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html index a4f9fc3482..23219f1074 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html @@ -60,10 +60,6 @@ {{ 'device-profile.type-required' | translate }} - - device-profile.description diff --git a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts index 83f248db92..9ad7993857 100644 --- a/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts @@ -108,7 +108,6 @@ export class AddDeviceProfileDialogComponent extends type: [DeviceProfileType.DEFAULT, [Validators.required]], defaultRuleChainId: [null, []], defaultQueueName: ['', []], - firmwareId: [null], description: ['', []] } ); @@ -187,7 +186,6 @@ export class AddDeviceProfileDialogComponent extends transportType: this.transportConfigFormGroup.get('transportType').value, provisionType: deviceProvisionConfiguration.type, provisionDeviceKey, - firmwareId: this.deviceProfileDetailsFormGroup.get('firmwareId').value, description: this.deviceProfileDetailsFormGroup.get('description').value, profileData: { configuration: createDeviceProfileConfiguration(DeviceProfileType.DEFAULT), diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html index 5f7593a03b..5c21d65adc 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.html @@ -65,8 +65,16 @@ + + device-profile.type diff --git a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts index 23f1bd511c..d8505a0a3b 100644 --- a/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts @@ -40,6 +40,7 @@ import { EntityType } from '@shared/models/entity-type.models'; import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { ServiceType } from '@shared/models/queue.models'; import { EntityId } from '@shared/models/id/entity-id'; +import { FirmwareType } from '@shared/models/firmware.models'; @Component({ selector: 'tb-device-profile', @@ -69,6 +70,8 @@ export class DeviceProfileComponent extends EntityComponent { deviceProfileId: EntityId; + firmwareTypes = FirmwareType; + constructor(protected store: Store, protected translate: TranslateService, @Optional() @Inject('entity') protected entityValue: DeviceProfile, @@ -110,6 +113,7 @@ export class DeviceProfileComponent extends EntityComponent { defaultRuleChainId: [entity && entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null, []], defaultQueueName: [entity ? entity.defaultQueueName : '', []], firmwareId: [entity ? entity.firmwareId : null], + softwareId: [entity ? entity.softwareId : null], description: [entity ? entity.description : '', []], } ); @@ -186,6 +190,7 @@ export class DeviceProfileComponent extends EntityComponent { this.entityForm.patchValue({defaultRuleChainId: entity.defaultRuleChainId ? entity.defaultRuleChainId.id : null}, {emitEvent: false}); this.entityForm.patchValue({defaultQueueName: entity.defaultQueueName}, {emitEvent: false}); this.entityForm.patchValue({firmwareId: entity.firmwareId}, {emitEvent: false}); + this.entityForm.patchValue({softwareId: entity.softwareId}, {emitEvent: false}); this.entityForm.patchValue({description: entity.description}, {emitEvent: false}); } diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html index 416b006499..245cb6503b 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html @@ -48,10 +48,6 @@ device.label - - device-profile.transport-type diff --git a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts index df925e4e21..4e2b5b2ff8 100644 --- a/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts @@ -107,7 +107,6 @@ export class DeviceWizardDialogComponent extends this.deviceWizardFormGroup = this.fb.group({ name: ['', Validators.required], label: [''], - firmwareId: [null], gateway: [false], overwriteActivityTime: [false], transportType: [DeviceTransportType.DEFAULT, Validators.required], @@ -313,7 +312,6 @@ export class DeviceWizardDialogComponent extends const device = { name: this.deviceWizardFormGroup.get('name').value, label: this.deviceWizardFormGroup.get('label').value, - firmwareId: this.deviceWizardFormGroup.get('firmwareId').value, deviceProfileId: profileId, additionalInfo: { gateway: this.deviceWizardFormGroup.get('gateway').value, diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.html b/ui-ngx/src/app/modules/home/pages/device/device.component.html index be2ce1367d..f09437662a 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.html +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.html @@ -103,8 +103,16 @@ + + diff --git a/ui-ngx/src/app/modules/home/pages/device/device.component.ts b/ui-ngx/src/app/modules/home/pages/device/device.component.ts index 34335b5e2f..d7f21fb2fc 100644 --- a/ui-ngx/src/app/modules/home/pages/device/device.component.ts +++ b/ui-ngx/src/app/modules/home/pages/device/device.component.ts @@ -34,6 +34,7 @@ import { ActionNotificationShow } from '@core/notification/notification.actions' import { TranslateService } from '@ngx-translate/core'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { Subject } from 'rxjs'; +import { FirmwareType } from '@shared/models/firmware.models'; @Component({ selector: 'tb-device', @@ -48,6 +49,8 @@ export class DeviceComponent extends EntityComponent { deviceScope: 'tenant' | 'customer' | 'customer_user' | 'edge'; + firmwareTypes = FirmwareType; + constructor(protected store: Store, protected translate: TranslateService, @Inject('entity') protected entityValue: DeviceInfo, @@ -80,6 +83,7 @@ export class DeviceComponent extends EntityComponent { name: [entity ? entity.name : '', [Validators.required]], deviceProfileId: [entity ? entity.deviceProfileId : null, [Validators.required]], firmwareId: [entity ? entity.firmwareId : null], + softwareId: [entity ? entity.softwareId : null], label: [entity ? entity.label : ''], deviceData: [entity ? entity.deviceData : null, [Validators.required]], additionalInfo: this.fb.group( @@ -94,19 +98,19 @@ export class DeviceComponent extends EntityComponent { } updateForm(entity: DeviceInfo) { - this.entityForm.patchValue({name: entity.name}); - this.entityForm.patchValue({deviceProfileId: entity.deviceProfileId}); - this.entityForm.patchValue({firmwareId: entity.firmwareId}); - this.entityForm.patchValue({label: entity.label}); - this.entityForm.patchValue({deviceData: entity.deviceData}); this.entityForm.patchValue({ - additionalInfo: - { - gateway: entity.additionalInfo ? entity.additionalInfo.gateway : false, - overwriteActivityTime: entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false - } + name: entity.name, + deviceProfileId: entity.deviceProfileId, + firmwareId: entity.firmwareId, + softwareId: entity.softwareId, + label: entity.label, + deviceData: entity.deviceData, + additionalInfo: { + gateway: entity.additionalInfo ? entity.additionalInfo.gateway : false, + overwriteActivityTime: entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false, + description: entity.additionalInfo ? entity.additionalInfo.description : '' + } }); - this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); } @@ -152,6 +156,10 @@ export class DeviceComponent extends EntityComponent { this.entityForm.markAsDirty(); } } + this.entityForm.patchValue({ + firmwareId: null, + softwareId: null + }); } } } diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts b/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts index b809f6f403..23efe2117b 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts +++ b/ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts @@ -21,7 +21,12 @@ import { EntityTableColumn, EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { Firmware, FirmwareInfo } from '@shared/models/firmware.models'; +import { + ChecksumAlgorithmTranslationMap, + Firmware, + FirmwareInfo, + FirmwareTypeTranslationMap +} from '@shared/models/firmware.models'; import { EntityType, entityTypeResources, entityTypeTranslations } from '@shared/models/entity-type.models'; import { TranslateService } from '@ngx-translate/core'; import { DatePipe } from '@angular/common'; @@ -49,14 +54,17 @@ export class FirmwareTableConfigResolve implements Resolve('createdTime', 'common.created-time', this.datePipe, '150px'), - new EntityTableColumn('title', 'firmware.title', '33%'), - new EntityTableColumn('version', 'firmware.version', '33%'), - new EntityTableColumn('fileName', 'firmware.file-name', '33%'), + new EntityTableColumn('title', 'firmware.title', '25%'), + new EntityTableColumn('version', 'firmware.version', '25%'), + new EntityTableColumn('type', 'firmware.type', '25%', entity => { + return this.translate.instant(FirmwareTypeTranslationMap.get(entity.type)); + }), + new EntityTableColumn('fileName', 'firmware.file-name', '25%'), new EntityTableColumn('dataSize', 'firmware.file-size', '70px', entity => { return this.fileSize.transform(entity.dataSize || 0); }), new EntityTableColumn('checksum', 'firmware.checksum', '540px', entity => { - return `${entity.checksumAlgorithm}: ${entity.checksum}`; + return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`; }, () => ({}), false) ); diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html b/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html index 2911802a75..cba515f1d8 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html +++ b/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html @@ -67,10 +67,27 @@ +
+ + firmware.type + + + + {{ firmwareTypeTranslationMap.get(firmwareType) | translate }} + + + + + +
firmware.checksum-algorithm - + diff --git a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts b/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts index 8ffef239b4..adfd2050c7 100644 --- a/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts +++ b/ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts @@ -22,7 +22,13 @@ import { TranslateService } from '@ngx-translate/core'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { EntityComponent } from '@home/components/entity/entity.component'; -import { ChecksumAlgorithm, ChecksumAlgorithmTranslationMap, Firmware } from '@shared/models/firmware.models'; +import { + ChecksumAlgorithm, + ChecksumAlgorithmTranslationMap, + Firmware, + FirmwareType, + FirmwareTypeTranslationMap +} from '@shared/models/firmware.models'; import { distinctUntilChanged, map, takeUntil } from 'rxjs/operators'; import { ActionNotificationShow } from '@core/notification/notification.actions'; @@ -36,6 +42,8 @@ export class FirmwaresComponent extends EntityComponent implements OnI checksumAlgorithms = Object.values(ChecksumAlgorithm); checksumAlgorithmTranslationMap = ChecksumAlgorithmTranslationMap; + firmwareTypes = Object.values(FirmwareType); + firmwareTypeTranslationMap = FirmwareTypeTranslationMap; constructor(protected store: Store, protected translate: TranslateService, @@ -83,6 +91,8 @@ export class FirmwaresComponent extends EntityComponent implements OnI const form = this.fb.group({ title: [entity ? entity.title : '', [Validators.required, Validators.maxLength(255)]], version: [entity ? entity.version : '', [Validators.required, Validators.maxLength(255)]], + type: [entity?.type ? entity.type : FirmwareType.FIRMWARE, [Validators.required]], + deviceProfileId: [entity ? entity.deviceProfileId : null], checksumAlgorithm: [entity ? entity.checksumAlgorithm : null], checksum: [entity ? entity.checksum : '', Validators.maxLength(1020)], additionalInfo: this.fb.group( @@ -105,6 +115,8 @@ export class FirmwaresComponent extends EntityComponent implements OnI this.entityForm.patchValue({ title: entity.title, version: entity.version, + type: entity.type, + deviceProfileId: entity.deviceProfileId, checksumAlgorithm: entity.checksumAlgorithm, checksum: entity.checksum, fileName: entity.fileName, diff --git a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html b/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html index 4e733b3132..7bb434e7d1 100644 --- a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html +++ b/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html @@ -37,11 +37,11 @@
- firmware.no-firmware-text + {{ notFoundFirmware | translate }}
- {{ translate.get('firmware.no-firmware-matching', + {{ translate.get(notMatchingFirmware, {entity: truncate.transform(searchText, true, 6, '...')}) | async }} diff --git a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts b/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts index 79dc170c7b..751415bcce 100644 --- a/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts +++ b/ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts @@ -28,7 +28,7 @@ import { BaseData } from '@shared/models/base-data'; import { EntityService } from '@core/http/entity.service'; import { TruncatePipe } from '@shared/pipe/truncate.pipe'; import { MatAutocompleteTrigger } from '@angular/material/autocomplete'; -import { FirmwareInfo } from '@shared/models/firmware.models'; +import { FirmwareInfo, FirmwareType } from '@shared/models/firmware.models'; import { FirmwareService } from '@core/http/firmware.service'; import { PageLink } from '@shared/models/page/page-link'; import { Direction } from '@shared/models/page/sort-order'; @@ -49,6 +49,12 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn modelValue: string | null; + @Input() + type = FirmwareType.FIRMWARE; + + @Input() + deviceProfileId: string; + @Input() labelText: string; @@ -81,6 +87,23 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn private dirty = false; + private firmwareTypeTranslation = new Map( + [ + [FirmwareType.FIRMWARE, { + label: 'firmware.firmware', + required: 'firmware.firmware-required', + noFound: 'firmware.no-firmware-text', + noMatching: 'firmware.no-firmware-matching' + }], + [FirmwareType.SOFTWARE, { + label: 'firmware.software', + required: 'firmware.software-required', + noFound: 'firmware.no-software-text', + noMatching: 'firmware.no-software-matching' + }] + ] + ); + private propagateChange = (v: any) => { }; constructor(private store: Store, @@ -160,7 +183,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn this.entityService.getEntity(EntityType.FIRMWARE, firmwareId, {ignoreLoading: true, ignoreErrors: true}).subscribe( (entity) => { this.modelValue = entity.id.id; - this.firmwareFormGroup.get('firmwareId').patchValue(entity, {emitEvent: false}); + this.firmwareFormGroup.get('firmwareId').patchValue(entity); }, () => { this.modelValue = null; @@ -173,6 +196,7 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } else { this.modelValue = null; this.firmwareFormGroup.get('firmwareId').patchValue('', {emitEvent: false}); + this.propagateChange(null); } } else { this.modelValue = null; @@ -209,7 +233,8 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn property: 'title', direction: Direction.ASC }); - return this.firmwareService.getFirmwares(pageLink, true, {ignoreLoading: true}).pipe( + return this.firmwareService.getFirmwaresInfoByDeviceProfileId(pageLink, this.deviceProfileId, this.type, + true, {ignoreLoading: true}).pipe( map((data) => data && data.data.length ? data.data : null) ); } @@ -223,11 +248,19 @@ export class FirmwareAutocompleteComponent implements ControlValueAccessor, OnIn } get placeholderText(): string { - return this.labelText || 'firmware.firmware'; + return this.labelText || this.firmwareTypeTranslation.get(this.type).label; } get requiredErrorText(): string { - return this.requiredText || 'firmware.firmware-required'; + return this.requiredText || this.firmwareTypeTranslation.get(this.type).required; + } + + get notFoundFirmware(): string { + return this.firmwareTypeTranslation.get(this.type).noFound; + } + + get notMatchingFirmware(): string { + return this.firmwareTypeTranslation.get(this.type).noMatching; } firmwareTitleText(firmware: FirmwareInfo): string { diff --git a/ui-ngx/src/app/shared/models/device.models.ts b/ui-ngx/src/app/shared/models/device.models.ts index 8489e894bd..683d9ddcc1 100644 --- a/ui-ngx/src/app/shared/models/device.models.ts +++ b/ui-ngx/src/app/shared/models/device.models.ts @@ -498,6 +498,7 @@ export interface DeviceProfile extends BaseData { defaultRuleChainId?: RuleChainId; defaultQueueName?: string; firmwareId?: FirmwareId; + softwareId?: FirmwareId; profileData: DeviceProfileData; } @@ -558,6 +559,7 @@ export interface Device extends BaseData { type: string; label: string; firmwareId?: FirmwareId; + softwareId?: FirmwareId; deviceProfileId?: DeviceProfileId; deviceData?: DeviceData; additionalInfo?: any; diff --git a/ui-ngx/src/app/shared/models/firmware.models.ts b/ui-ngx/src/app/shared/models/firmware.models.ts index 65dd1761bf..57c95c3c9a 100644 --- a/ui-ngx/src/app/shared/models/firmware.models.ts +++ b/ui-ngx/src/app/shared/models/firmware.models.ts @@ -17,6 +17,7 @@ import { BaseData } from '@shared/models/base-data'; import { TenantId } from '@shared/models/id/tenant-id'; import { FirmwareId } from '@shared/models/id/firmware-id'; +import { DeviceProfileId } from '@shared/models/id/device-profile-id'; export enum ChecksumAlgorithm { MD5 = 'md5', @@ -32,14 +33,28 @@ export const ChecksumAlgorithmTranslationMap = new Map( + [ + [FirmwareType.FIRMWARE, 'firmware.types.firmware'], + [FirmwareType.SOFTWARE, 'firmware.types.software'] + ] +); + export interface FirmwareInfo extends BaseData { tenantId?: TenantId; + type: FirmwareType; + deviceProfileId?: DeviceProfileId; title?: string; version?: string; hasData?: boolean; fileName: string; - checksum?: ChecksumAlgorithm; - checksumAlgorithm?: string; + checksum?: string; + checksumAlgorithm?: ChecksumAlgorithm; contentType: string; dataSize?: number; additionalInfo?: any; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 1f8e5e05d8..57727fa40f 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -1948,6 +1948,8 @@ "idCopiedMessage": "Firmware Id has been copied to clipboard", "no-firmware-matching": "No firmware matching '{{entity}}' were found.", "no-firmware-text": "No firmwares found", + "no-software-matching": "No sowtware matching '{{entity}}' were found.", + "no-software-text": "No software found", "file-name": "File name", "file-size": "File size", "file-size-bytes": "File size in bytes", @@ -1956,8 +1958,15 @@ "firmware-required": "Firmware is required.", "search": "Search firmwares", "selected-firmware": "{ count, plural, 1 {1 firmware} other {# firmwares} } selected", + "software": "Software", + "software-required": "Software is required.", "title": "Title", "title-required": "Title is required.", + "type": "Firmware type", + "types": { + "firmware": "Firmware", + "software": "Software" + }, "version": "Version", "version-required": "Version is required.", "warning-after-save-no-edit": "Once the firmware is saved, it will not be possible to change the title and version fields."