created FirmwareStateService

This commit is contained in:
YevhenBondarenko 2021-04-14 16:36:34 +03:00
parent 81b203efeb
commit d6f451ccca
8 changed files with 286 additions and 26 deletions

View File

@ -119,6 +119,7 @@ import org.thingsboard.server.queue.discovery.PartitionService;
import org.thingsboard.server.queue.provider.TbQueueProducerProvider; import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.component.ComponentDiscoveryService; import org.thingsboard.server.service.component.ComponentDiscoveryService;
import org.thingsboard.server.service.firmware.FirmwareStateService;
import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository; import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository;
import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.service.queue.TbClusterService; import org.thingsboard.server.service.queue.TbClusterService;
@ -240,6 +241,9 @@ public abstract class BaseController {
@Autowired @Autowired
protected FirmwareService firmwareService; protected FirmwareService firmwareService;
@Autowired
protected FirmwareStateService firmwareStateService;
@Autowired @Autowired
protected TbQueueProducerProvider producerProvider; protected TbQueueProducerProvider producerProvider;

View File

@ -70,6 +70,7 @@ import javax.annotation.Nullable;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@RestController @RestController
@ -117,13 +118,25 @@ public class DeviceController extends BaseController {
checkEntity(device.getId(), device, Resource.DEVICE); checkEntity(device.getId(), device, Resource.DEVICE);
boolean created = device.getId() == null;
boolean isFirmwareChanged = false;
if (created) {
isFirmwareChanged = true;
} else {
Device oldDevice = deviceService.findDeviceById(getTenantId(), device.getId());
if (!Objects.equals(device.getFirmwareId(), oldDevice.getFirmwareId()) || !oldDevice.getDeviceProfileId().equals(device.getDeviceProfileId())) {
isFirmwareChanged = true;
}
}
Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken));
tbClusterService.onDeviceChange(savedDevice, null); tbClusterService.onDeviceChange(savedDevice, null);
tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(),
savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null);
tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED);
logEntityAction(savedDevice.getId(), savedDevice, logEntityAction(savedDevice.getId(), savedDevice,
savedDevice.getCustomerId(), savedDevice.getCustomerId(),
@ -134,12 +147,19 @@ public class DeviceController extends BaseController {
} else { } else {
deviceStateService.onDeviceUpdated(savedDevice); deviceStateService.onDeviceUpdated(savedDevice);
} }
if (isFirmwareChanged) {
firmwareStateService.update(savedDevice, created);
}
return savedDevice; return savedDevice;
} catch (Exception e) { } catch (
Exception e) {
logEntityAction(emptyId(EntityType.DEVICE), device, logEntityAction(emptyId(EntityType.DEVICE), device,
null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e);
throw handleException(e); throw handleException(e);
} }
} }
@PreAuthorize("hasAuthority('TENANT_ADMIN')") @PreAuthorize("hasAuthority('TENANT_ADMIN')")

View File

@ -43,6 +43,7 @@ import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.UUID; import java.util.UUID;
@RestController @RestController
@ -143,6 +144,15 @@ public class DeviceProfileController extends BaseController {
checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE);
boolean isFirmwareChanged = false;
if (!created) {
DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId());
if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) {
isFirmwareChanged = true;
}
}
DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile));
tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); tbClusterService.onDeviceProfileChange(savedDeviceProfile, null);
@ -153,6 +163,10 @@ public class DeviceProfileController extends BaseController {
null, null,
created ? ActionType.ADDED : ActionType.UPDATED, null); created ? ActionType.ADDED : ActionType.UPDATED, null);
if (isFirmwareChanged) {
firmwareStateService.update(savedDeviceProfile);
}
return savedDeviceProfile; return savedDeviceProfile;
} catch (Exception e) { } catch (Exception e) {
logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile,

View File

@ -15,7 +15,9 @@
*/ */
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import com.google.common.hash.Hashing;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
@ -127,6 +129,11 @@ public class FirmwareController extends BaseController {
firmware.setVersion(info.getVersion()); firmware.setVersion(info.getVersion());
firmware.setAdditionalInfo(info.getAdditionalInfo()); firmware.setAdditionalInfo(info.getAdditionalInfo());
if (StringUtils.isEmpty(checksumAlgorithm)) {
checksumAlgorithm = "sha256";
checksum = Hashing.sha256().hashBytes(file.getBytes()).toString();
}
firmware.setChecksumAlgorithm(checksumAlgorithm); firmware.setChecksumAlgorithm(checksumAlgorithm);
firmware.setChecksum(checksum); firmware.setChecksum(checksum);
firmware.setFileName(file.getOriginalFilename()); firmware.setFileName(file.getOriginalFilename());

View File

@ -0,0 +1,171 @@
/**
* 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.service.firmware;
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.server.common.data.DataConstants;
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.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.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.firmware.FirmwareService;
import org.thingsboard.server.queue.util.TbCoreComponent;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
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;
@Slf4j
@Service
@TbCoreComponent
public class DefaultFirmwareStateService implements FirmwareStateService {
private final FirmwareService firmwareService;
private final DeviceService deviceService;
private final DeviceProfileService deviceProfileService;
private final RuleEngineTelemetryService telemetryService;
public DefaultFirmwareStateService(FirmwareService firmwareService, DeviceService deviceService, DeviceProfileService deviceProfileService, RuleEngineTelemetryService telemetryService) {
this.firmwareService = firmwareService;
this.deviceService = deviceService;
this.deviceProfileService = deviceProfileService;
this.telemetryService = telemetryService;
}
@Override
public void update(Device device, boolean created) {
FirmwareId firmwareId = device.getFirmwareId();
if (firmwareId == null) {
DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId());
firmwareId = deviceProfile.getFirmwareId();
}
if (firmwareId == null) {
if (!created) {
remove(device);
}
} else {
update(device, firmwareService.findFirmwareById(device.getTenantId(), firmwareId), System.currentTimeMillis());
}
}
@Override
public void update(DeviceProfile deviceProfile) {
TenantId tenantId = deviceProfile.getTenantId();
Consumer<Device> updateConsumer;
if (deviceProfile.getFirmwareId() != null) {
Firmware firmware = firmwareService.findFirmwareById(tenantId, deviceProfile.getFirmwareId());
long ts = System.currentTimeMillis();
updateConsumer = d -> update(d, firmware, ts);
} else {
updateConsumer = this::remove;
}
PageLink pageLink = new PageLink(100);
PageData<Device> pageData;
do {
//TODO: create a query which will return devices without firmware
pageData = deviceService.findDevicesByTenantIdAndType(tenantId, deviceProfile.getName(), pageLink);
pageData.getData().stream().filter(d -> d.getFirmwareId() == null).forEach(updateConsumer);
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
}
private void update(Device device, Firmware firmware, long ts) {
TenantId tenantId = device.getTenantId();
DeviceId deviceId = device.getId();
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())));
telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void tmp) {
log.trace("[{}] Success save telemetry with target firmware for device!", deviceId);
}
@Override
public void onFailure(Throwable t) {
log.error("[{}] Failed to save telemetry with target firmware for device!", deviceId, t);
}
});
List<AttributeKvEntry> attributes = new ArrayList<>();
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, (long) firmware.getData().array().length)));
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) {
log.trace("[{}] Success save attributes with target firmware!", deviceId);
}
@Override
public void onFailure(Throwable t) {
log.error("[{}] Failed to save attributes with target firmware!", deviceId, t);
}
});
}
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),
new FutureCallback<>() {
@Override
public void onSuccess(@Nullable Void tmp) {
log.trace("[{}] Success remove target firmware attributes!", device.getId());
}
@Override
public void onFailure(Throwable t) {
log.error("[{}] Failed to remove target firmware attributes!", device.getId(), t);
}
});
}
}

View File

@ -0,0 +1,27 @@
/**
* 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.service.firmware;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
public interface FirmwareStateService {
void update(Device device, boolean created);
void update(DeviceProfile deviceProfile);
}

View File

@ -91,4 +91,19 @@ public class DataConstants {
public static final String USERNAME = "username"; public static final String USERNAME = "username";
public static final String PASSWORD = "password"; public static final String PASSWORD = "password";
//firmware
//telemetry
public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title";
public static final String CURRENT_FIRMWARE_VERSION = "cur_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 CURRENT_FIRMWARE_STATE = "cur_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";
} }

View File

@ -114,7 +114,8 @@ public class BaseFirmwareService implements FirmwareService {
log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
validatePageLink(pageLink); validatePageLink(pageLink);
return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink); } return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink);
}
@Override @Override
public void deleteFirmware(TenantId tenantId, FirmwareId firmwareId) { public void deleteFirmware(TenantId tenantId, FirmwareId firmwareId) {
@ -211,31 +212,32 @@ public class BaseFirmwareService implements FirmwareService {
throw new DataValidationException("Firmware data should be specified!"); throw new DataValidationException("Firmware data should be specified!");
} }
if (firmware.getChecksumAlgorithm() != null) { if (StringUtils.isEmpty(firmware.getChecksumAlgorithm())) {
if (StringUtils.isEmpty(firmware.getChecksum())) { throw new DataValidationException("Firmware checksum algorithm should be specified!");
throw new DataValidationException("Firmware checksum should be specified!"); }
} if (StringUtils.isEmpty(firmware.getChecksum())) {
throw new DataValidationException("Firmware checksum should be specified!");
}
HashFunction hashFunction; HashFunction hashFunction;
switch (firmware.getChecksumAlgorithm()) { switch (firmware.getChecksumAlgorithm()) {
case "sha256": case "sha256":
hashFunction = Hashing.sha256(); hashFunction = Hashing.sha256();
break; break;
case "md5": case "md5":
hashFunction = Hashing.md5(); hashFunction = Hashing.md5();
break; break;
case "crc32": case "crc32":
hashFunction = Hashing.crc32(); hashFunction = Hashing.crc32();
break; break;
default: default:
throw new DataValidationException("Unknown checksum algorithm!"); throw new DataValidationException("Unknown checksum algorithm!");
} }
String currentChecksum = hashFunction.hashBytes(data.array()).toString(); String currentChecksum = hashFunction.hashBytes(data.array()).toString();
if (!currentChecksum.equals(firmware.getChecksum())) { if (!currentChecksum.equals(firmware.getChecksum())) {
throw new DataValidationException("Wrong firmware file!"); throw new DataValidationException("Wrong firmware file!");
}
} }
} }