Browse Source

Merge pull request #4524 from thingsboard/feature/sota

FOTA/SOTA
pull/4525/head
Andrew Shvayka 5 years ago
committed by GitHub
parent
commit
5ca0e05ab5
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      application/src/main/data/upgrade/3.2.2/schema_update.sql
  2. 9
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  3. 15
      application/src/main/java/org/thingsboard/server/controller/FirmwareController.java
  4. 141
      application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java
  5. 2
      application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java
  6. 10
      application/src/main/java/org/thingsboard/server/service/transport/DefaultTransportApiService.java
  7. 30
      application/src/test/java/org/thingsboard/server/controller/BaseFirmwareControllerTest.java
  8. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java
  9. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/firmware/FirmwareService.java
  10. 36
      common/data/src/main/java/org/thingsboard/server/common/data/DataConstants.java
  11. 14
      common/data/src/main/java/org/thingsboard/server/common/data/Device.java
  12. 6
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceProfile.java
  13. 6
      common/data/src/main/java/org/thingsboard/server/common/data/FirmwareInfo.java
  14. 25
      common/data/src/main/java/org/thingsboard/server/common/data/HasFirmware.java
  15. 13
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/MqttTopics.java
  16. 30
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareKey.java
  17. 30
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareType.java
  18. 91
      common/data/src/main/java/org/thingsboard/server/common/data/firmware/FirmwareUtil.java
  19. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/session/FeatureType.java
  20. 3
      common/message/src/main/java/org/thingsboard/server/common/msg/session/SessionMsgType.java
  21. 11
      common/queue/src/main/proto/queue.proto
  22. 23
      common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
  23. 37
      common/transport/http/src/main/java/org/thingsboard/server/transport/http/DeviceApiController.java
  24. 8
      common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/LwM2mTransportServiceImpl.java
  25. 88
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  26. 8
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/JsonMqttAdaptor.java
  27. 3
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/MqttTransportAdaptor.java
  28. 7
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/adaptors/ProtoMqttAdaptor.java
  29. 2
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java
  30. 23
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  31. 34
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  32. 96
      dao/src/main/java/org/thingsboard/server/dao/firmware/BaseFirmwareService.java
  33. 7
      dao/src/main/java/org/thingsboard/server/dao/firmware/FirmwareInfoDao.java
  34. 4
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  35. 16
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractDeviceEntity.java
  36. 18
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DeviceProfileEntity.java
  37. 21
      dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareEntity.java
  38. 27
      dao/src/main/java/org/thingsboard/server/dao/model/sql/FirmwareInfoEntity.java
  39. 9
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java
  40. 10
      dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java
  41. 28
      dao/src/main/java/org/thingsboard/server/dao/sql/firmware/FirmwareInfoRepository.java
  42. 14
      dao/src/main/java/org/thingsboard/server/dao/sql/firmware/JpaFirmwareInfoDao.java
  43. 10
      dao/src/main/resources/sql/schema-entities-hsql.sql
  44. 1
      dao/src/main/resources/sql/schema-entities-idx.sql
  45. 20
      dao/src/main/resources/sql/schema-entities.sql
  46. 5
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceProfileServiceTest.java
  47. 43
      dao/src/test/java/org/thingsboard/server/dao/service/BaseDeviceServiceTest.java
  48. 232
      dao/src/test/java/org/thingsboard/server/dao/service/BaseFirmwareServiceTest.java
  49. 2
      ui-ngx/src/app/core/http/entity.service.ts
  50. 15
      ui-ngx/src/app/core/http/firmware.service.ts
  51. 4
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html
  52. 2
      ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.ts
  53. 8
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.html
  54. 5
      ui-ngx/src/app/modules/home/components/profile/device-profile.component.ts
  55. 4
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html
  56. 2
      ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.ts
  57. 8
      ui-ngx/src/app/modules/home/pages/device/device.component.html
  58. 30
      ui-ngx/src/app/modules/home/pages/device/device.component.ts
  59. 18
      ui-ngx/src/app/modules/home/pages/firmware/firmware-table-config.resolve.ts
  60. 19
      ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html
  61. 14
      ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.ts
  62. 4
      ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html
  63. 43
      ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.ts
  64. 2
      ui-ngx/src/app/shared/models/device.models.ts
  65. 19
      ui-ngx/src/app/shared/models/firmware.models.ts
  66. 9
      ui-ngx/src/assets/locale/locale.constant-en_US.json

20
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;
$$;

9
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;

15
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<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData,
public PageData<FirmwareInfo> 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);
}

141
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<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> 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<PageLink, PageData<Device>> getDevicesFunction;
Consumer<Device> 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<Device> 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<TsKvEntry> 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<AttributeKvEntry> 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<AttributeKey> 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

2
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);

10
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<TransportApiResponseMsg> 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());

30
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<FirmwareInfo> 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<FirmwareInfo> 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<FirmwareInfo> 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());

2
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceService.java

@ -65,6 +65,8 @@ public interface DeviceService {
PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink);
PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink);
PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink);

4
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<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink);
PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);
PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
void deleteFirmware(TenantId tenantId, FirmwareId firmwareId);

36
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";
}

14
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<DeviceId> implements HasName, HasTenantId, HasCustomerId {
public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId, HasFirmware {
private static final long serialVersionUID = 2807343040519543363L;
@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen
private byte[] deviceDataBytes;
private FirmwareId firmwareId;
private FirmwareId softwareId;
public Device() {
super();
@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> 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<DeviceId> 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<DeviceId> 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();

6
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<DeviceProfileId> implements HasName, HasTenantId {
public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasFirmware {
private TenantId tenantId;
@NoXss
@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
private FirmwareId firmwareId;
private FirmwareId softwareId;
public DeviceProfile() {
super();
}
@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H
this.defaultQueueName = deviceProfile.getDefaultQueueName();
this.setProfileData(deviceProfile.getProfileData());
this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey();
this.firmwareId = deviceProfile.getFirmwareId();
this.softwareId = deviceProfile.getSoftwareId();
}
@Override

6
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<FirmwareId>
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<FirmwareId>
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();

25
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();
}

13
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 = "(?<requestId>\\d+)";
public static final String CHUNK_PATTERN = "(?<chunk>\\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() {
}

30
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;
}
}

30
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;
}
}

91
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<String> ALL_FW_ATTRIBUTE_KEYS;
public static final List<String> 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<String> 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;
}
}
}

2
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
}

3
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;

11
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;
}

23
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);

37
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<ResponseEntity> 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<ResponseEntity> 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<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
new DeviceProvisionCallback(responseWriter));
return responseWriter;
}
private DeferredResult<ResponseEntity> getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) {
DeferredResult<ResponseEntity> 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<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) {
DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>();
transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json),
new DeviceProvisionCallback(responseWriter));
return responseWriter;
}
private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
private final TransportContext transportContext;
private final DeferredResult<ResponseEntity> responseWriter;

8
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);
}
});

88
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<Future<? super Void>>, SessionMsgListener {
private static final Pattern FW_PATTERN = Pattern.compile("v2/fw/request/(?<requestId>\\d+)/chunk/(?<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:

8
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<MqttMessage> 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<MqttMessage> 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 {

3
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<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException;
Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException;
Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException;
default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) {
MqttFixedHeader mqttFixedHeader =

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

2
dao/src/main/java/org/thingsboard/server/dao/device/DeviceDao.java

@ -83,6 +83,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao {
PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink);
PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink);
/**
* Find device infos by tenantId, type and page link.
*

23
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!");
}
}
}

34
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<Device> 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<Device> 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<DeviceInfo> 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!");
}
}
}
};

96
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<FirmwareInfo> 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<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) {
@ -135,11 +141,11 @@ public class BaseFirmwareService implements FirmwareService {
}
@Override
public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) {
public PageData<FirmwareInfo> 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<FirmwareInfo> 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<TenantId, FirmwareInfo> tenantFirmwareRemover =
new PaginatedRemover<>() {

7
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<FirmwareInfo> {
PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink);
PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink);
PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink);
boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId);
}

4
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";

16
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<T extends Device> extends BaseSqlEntity<T> implements SearchTextEntity<T> {
@ -77,6 +77,9 @@ public abstract class AbstractDeviceEntity<T extends Device> 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<T extends Device> 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<T extends Device> 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<T extends Device> 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);

18
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<DeviceProfile> 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<DeviceProfile> 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<DeviceProfile> impl
deviceProfile.setFirmwareId(new FirmwareId(firmwareId));
}
if (softwareId != null) {
deviceProfile.setSoftwareId(new FirmwareId(softwareId));
}
return deviceProfile;
}
}

21
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<Firmware> 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<Firmware> 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<Firmware> 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);

27
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<FirmwareInfo> 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<FirmwareInfo> 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<FirmwareInfo> 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<FirmwareInfo> 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);

9
dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceRepository.java

@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " +
"AND d.type = :type " +
"AND d.softwareId = null " +
"AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
Page<DeviceEntity> 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 " +

10
dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceDao.java

@ -164,6 +164,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device>
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) {
return DaoUtil.toPageData(
deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull(
tenantId,
type,
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
@Override
public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) {
return DaoUtil.toPageData(

28
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<FirmwareInfoEntity, UUID> {
@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<FirmwareInfoEntity> 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<FirmwareInfoEntity> findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId,
@Param("hasData") boolean hasData,
@Param("searchText") String searchText,
Pageable pageable);
Page<FirmwareInfoEntity> 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);
}

14
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<FirmwareInfoEnt
}
@Override
public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) {
public PageData<FirmwareInfo> 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());
}
}

10
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 (

1
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);

20
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 (

5
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<DeviceProfile> 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");

43
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() {

232
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<FirmwareInfo> 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();

2
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;

15
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<PageData<FirmwareInfo>> {
let url = `/api/firmwares`;
if (isDefinedAndNotNull(hasData)) {
url += `/${hasData}`;
}
url += `${pageLink.toQuery()}`;
public getFirmwares(pageLink: PageLink, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
return this.http.get<PageData<FirmwareInfo>>(`/api/firmwares${pageLink.toQuery()}`, defaultHttpOptionsFromConfig(config));
}
public getFirmwaresInfoByDeviceProfileId(pageLink: PageLink, deviceProfileId: string, type: FirmwareType,
hasData = true, config?: RequestConfig): Observable<PageData<FirmwareInfo>> {
const url = `/api/firmwares/${deviceProfileId}/${type}/${hasData}${pageLink.toQuery()}`;
return this.http.get<PageData<FirmwareInfo>>(url, defaultHttpOptionsFromConfig(config));
}

4
ui-ngx/src/app/modules/home/components/profile/add-device-profile-dialog.component.html

@ -60,10 +60,6 @@
{{ 'device-profile.type-required' | translate }}
</mat-error>
</mat-form-field>
<tb-firmware-autocomplete
[useFullEntityId]="true"
formControlName="firmwareId">
</tb-firmware-autocomplete>
<mat-form-field class="mat-block">
<mat-label translate>device-profile.description</mat-label>
<textarea matInput formControlName="description" rows="2"></textarea>

2
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),

8
ui-ngx/src/app/modules/home/components/profile/device-profile.component.html

@ -65,8 +65,16 @@
</tb-queue-type-list>
<tb-firmware-autocomplete
[useFullEntityId]="true"
[type]="firmwareTypes.FIRMWARE"
[deviceProfileId]="deviceProfileId?.id"
formControlName="firmwareId">
</tb-firmware-autocomplete>
<tb-firmware-autocomplete
[useFullEntityId]="true"
[type]="firmwareTypes.SOFTWARE"
[deviceProfileId]="deviceProfileId?.id"
formControlName="softwareId">
</tb-firmware-autocomplete>
<mat-form-field fxHide class="mat-block">
<mat-label translate>device-profile.type</mat-label>
<mat-select formControlName="type" required>

5
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<DeviceProfile> {
deviceProfileId: EntityId;
firmwareTypes = FirmwareType;
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
@Optional() @Inject('entity') protected entityValue: DeviceProfile,
@ -110,6 +113,7 @@ export class DeviceProfileComponent extends EntityComponent<DeviceProfile> {
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<DeviceProfile> {
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});
}

4
ui-ngx/src/app/modules/home/components/wizard/device-wizard-dialog.component.html

@ -48,10 +48,6 @@
<mat-label translate>device.label</mat-label>
<input matInput formControlName="label">
</mat-form-field>
<tb-firmware-autocomplete
[useFullEntityId]="true"
formControlName="firmwareId">
</tb-firmware-autocomplete>
<mat-form-field class="mat-block" style="padding-bottom: 14px;">
<mat-label translate>device-profile.transport-type</mat-label>
<mat-select formControlName="transportType" required>

2
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,

8
ui-ngx/src/app/modules/home/pages/device/device.component.html

@ -103,8 +103,16 @@
</mat-form-field>
<tb-firmware-autocomplete
[useFullEntityId]="true"
[type]="firmwareTypes.FIRMWARE"
[deviceProfileId]="entityForm.get('deviceProfileId').value?.id"
formControlName="firmwareId">
</tb-firmware-autocomplete>
<tb-firmware-autocomplete
[useFullEntityId]="true"
[type]="firmwareTypes.SOFTWARE"
[deviceProfileId]="entityForm.get('deviceProfileId').value?.id"
formControlName="softwareId">
</tb-firmware-autocomplete>
<tb-device-data
formControlName="deviceData"
required>

30
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<DeviceInfo> {
deviceScope: 'tenant' | 'customer' | 'customer_user' | 'edge';
firmwareTypes = FirmwareType;
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
@Inject('entity') protected entityValue: DeviceInfo,
@ -80,6 +83,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
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<DeviceInfo> {
}
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<DeviceInfo> {
this.entityForm.markAsDirty();
}
}
this.entityForm.patchValue({
firmwareId: null,
softwareId: null
});
}
}
}

18
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<EntityTableConfig<Fir
this.config.columns.push(
new DateEntityTableColumn<FirmwareInfo>('createdTime', 'common.created-time', this.datePipe, '150px'),
new EntityTableColumn<FirmwareInfo>('title', 'firmware.title', '33%'),
new EntityTableColumn<FirmwareInfo>('version', 'firmware.version', '33%'),
new EntityTableColumn<FirmwareInfo>('fileName', 'firmware.file-name', '33%'),
new EntityTableColumn<FirmwareInfo>('title', 'firmware.title', '25%'),
new EntityTableColumn<FirmwareInfo>('version', 'firmware.version', '25%'),
new EntityTableColumn<FirmwareInfo>('type', 'firmware.type', '25%', entity => {
return this.translate.instant(FirmwareTypeTranslationMap.get(entity.type));
}),
new EntityTableColumn<FirmwareInfo>('fileName', 'firmware.file-name', '25%'),
new EntityTableColumn<FirmwareInfo>('dataSize', 'firmware.file-size', '70px', entity => {
return this.fileSize.transform(entity.dataSize || 0);
}),
new EntityTableColumn<FirmwareInfo>('checksum', 'firmware.checksum', '540px', entity => {
return `${entity.checksumAlgorithm}: ${entity.checksum}`;
return `${ChecksumAlgorithmTranslationMap.get(entity.checksumAlgorithm)}: ${entity.checksum}`;
}, () => ({}), false)
);

19
ui-ngx/src/app/modules/home/pages/firmware/firmwares.component.html

@ -67,10 +67,27 @@
</mat-error>
</mat-form-field>
</div>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayout.xs="column">
<mat-form-field fxFlex="45">
<mat-label translate>firmware.type</mat-label>
<input *ngIf="!isAdd" matInput type="text" [readonly]="isEdit" [disabled]="!isEdit"
value="{{ firmwareTypeTranslationMap.get(entityForm.get('type').value) | translate }}">
<mat-select formControlName="type" required *ngIf="isAdd">
<mat-option *ngFor="let firmwareType of firmwareTypes" [value]="firmwareType">
{{ firmwareTypeTranslationMap.get(firmwareType) | translate }}
</mat-option>
</mat-select>
</mat-form-field>
<tb-device-profile-autocomplete
formControlName="deviceProfileId" fxFlex
[editProfileEnabled]="false">
</tb-device-profile-autocomplete>
</div>
<div fxLayout="row" fxLayoutGap.gt-xs="8px" fxLayoutGap.sm="8px" fxLayout.xs="column" fxLayout.md="column">
<mat-form-field class="mat-block" fxFlex="33">
<mat-label translate>firmware.checksum-algorithm</mat-label>
<input *ngIf="!isAdd" matInput formControlName="checksumAlgorithm" type="text" [readonly]="isEdit">
<input *ngIf="!isAdd" matInput type="text" [readonly]="isEdit" [disabled]="!isEdit"
value="{{ checksumAlgorithmTranslationMap.get(entityForm.get('checksumAlgorithm').value) | translate }}">
<mat-select formControlName="checksumAlgorithm" *ngIf="isAdd">
<mat-option [value]=null></mat-option>
<mat-option *ngFor="let checksumAlgorithm of checksumAlgorithms" [value]="checksumAlgorithm">

14
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<Firmware> implements OnI
checksumAlgorithms = Object.values(ChecksumAlgorithm);
checksumAlgorithmTranslationMap = ChecksumAlgorithmTranslationMap;
firmwareTypes = Object.values(FirmwareType);
firmwareTypeTranslationMap = FirmwareTypeTranslationMap;
constructor(protected store: Store<AppState>,
protected translate: TranslateService,
@ -83,6 +91,8 @@ export class FirmwaresComponent extends EntityComponent<Firmware> 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<Firmware> 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,

4
ui-ngx/src/app/shared/components/firmware/firmware-autocomplete.component.html

@ -37,11 +37,11 @@
<mat-option *ngIf="!(filteredFirmwares | async)?.length" [value]="null" class="tb-not-found">
<div class="tb-not-found-content" (click)="$event.stopPropagation()">
<div *ngIf="!textIsNotEmpty(searchText); else searchNotEmpty">
<span translate>firmware.no-firmware-text</span>
<span>{{ notFoundFirmware | translate }}</span>
</div>
<ng-template #searchNotEmpty>
<span>
{{ translate.get('firmware.no-firmware-matching',
{{ translate.get(notMatchingFirmware,
{entity: truncate.transform(searchText, true, 6, &apos;...&apos;)}) | async }}
</span>
</ng-template>

43
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, any>(
[
[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<AppState>,
@ -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 {

2
ui-ngx/src/app/shared/models/device.models.ts

@ -498,6 +498,7 @@ export interface DeviceProfile extends BaseData<DeviceProfileId> {
defaultRuleChainId?: RuleChainId;
defaultQueueName?: string;
firmwareId?: FirmwareId;
softwareId?: FirmwareId;
profileData: DeviceProfileData;
}
@ -558,6 +559,7 @@ export interface Device extends BaseData<DeviceId> {
type: string;
label: string;
firmwareId?: FirmwareId;
softwareId?: FirmwareId;
deviceProfileId?: DeviceProfileId;
deviceData?: DeviceData;
additionalInfo?: any;

19
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<ChecksumAlgorithm, string
]
);
export enum FirmwareType {
FIRMWARE = 'FIRMWARE',
SOFTWARE = 'SOFTWARE'
}
export const FirmwareTypeTranslationMap = new Map<FirmwareType, string>(
[
[FirmwareType.FIRMWARE, 'firmware.types.firmware'],
[FirmwareType.SOFTWARE, 'firmware.types.software']
]
);
export interface FirmwareInfo extends BaseData<FirmwareId> {
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;

9
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."

Loading…
Cancel
Save