Showing
8 changed files
with
286 additions
and
26 deletions
... | ... | @@ -119,6 +119,7 @@ import org.thingsboard.server.queue.discovery.PartitionService; |
119 | 119 | import org.thingsboard.server.queue.provider.TbQueueProducerProvider; |
120 | 120 | import org.thingsboard.server.queue.util.TbCoreComponent; |
121 | 121 | import org.thingsboard.server.service.component.ComponentDiscoveryService; |
122 | +import org.thingsboard.server.service.firmware.FirmwareStateService; | |
122 | 123 | import org.thingsboard.server.service.lwm2m.LwM2MModelsRepository; |
123 | 124 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
124 | 125 | import org.thingsboard.server.service.queue.TbClusterService; |
... | ... | @@ -241,6 +242,9 @@ public abstract class BaseController { |
241 | 242 | protected FirmwareService firmwareService; |
242 | 243 | |
243 | 244 | @Autowired |
245 | + protected FirmwareStateService firmwareStateService; | |
246 | + | |
247 | + @Autowired | |
244 | 248 | protected TbQueueProducerProvider producerProvider; |
245 | 249 | |
246 | 250 | @Autowired | ... | ... |
... | ... | @@ -70,6 +70,7 @@ import javax.annotation.Nullable; |
70 | 70 | import java.io.IOException; |
71 | 71 | import java.util.ArrayList; |
72 | 72 | import java.util.List; |
73 | +import java.util.Objects; | |
73 | 74 | import java.util.stream.Collectors; |
74 | 75 | |
75 | 76 | @RestController |
... | ... | @@ -117,13 +118,25 @@ public class DeviceController extends BaseController { |
117 | 118 | |
118 | 119 | checkEntity(device.getId(), device, Resource.DEVICE); |
119 | 120 | |
121 | + boolean created = device.getId() == null; | |
122 | + | |
123 | + boolean isFirmwareChanged = false; | |
124 | + | |
125 | + if (created) { | |
126 | + isFirmwareChanged = true; | |
127 | + } else { | |
128 | + Device oldDevice = deviceService.findDeviceById(getTenantId(), device.getId()); | |
129 | + if (!Objects.equals(device.getFirmwareId(), oldDevice.getFirmwareId()) || !oldDevice.getDeviceProfileId().equals(device.getDeviceProfileId())) { | |
130 | + isFirmwareChanged = true; | |
131 | + } | |
132 | + } | |
133 | + | |
120 | 134 | Device savedDevice = checkNotNull(deviceService.saveDeviceWithAccessToken(device, accessToken)); |
121 | 135 | |
122 | 136 | tbClusterService.onDeviceChange(savedDevice, null); |
123 | 137 | tbClusterService.pushMsgToCore(new DeviceNameOrTypeUpdateMsg(savedDevice.getTenantId(), |
124 | 138 | savedDevice.getId(), savedDevice.getName(), savedDevice.getType()), null); |
125 | - tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), | |
126 | - device.getId() == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); | |
139 | + tbClusterService.onEntityStateChange(savedDevice.getTenantId(), savedDevice.getId(), created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); | |
127 | 140 | |
128 | 141 | logEntityAction(savedDevice.getId(), savedDevice, |
129 | 142 | savedDevice.getCustomerId(), |
... | ... | @@ -134,12 +147,19 @@ public class DeviceController extends BaseController { |
134 | 147 | } else { |
135 | 148 | deviceStateService.onDeviceUpdated(savedDevice); |
136 | 149 | } |
150 | + | |
151 | + if (isFirmwareChanged) { | |
152 | + firmwareStateService.update(savedDevice, created); | |
153 | + } | |
154 | + | |
137 | 155 | return savedDevice; |
138 | - } catch (Exception e) { | |
156 | + } catch ( | |
157 | + Exception e) { | |
139 | 158 | logEntityAction(emptyId(EntityType.DEVICE), device, |
140 | 159 | null, device.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); |
141 | 160 | throw handleException(e); |
142 | 161 | } |
162 | + | |
143 | 163 | } |
144 | 164 | |
145 | 165 | @PreAuthorize("hasAuthority('TENANT_ADMIN')") | ... | ... |
... | ... | @@ -43,6 +43,7 @@ import org.thingsboard.server.service.security.permission.Operation; |
43 | 43 | import org.thingsboard.server.service.security.permission.Resource; |
44 | 44 | |
45 | 45 | import java.util.List; |
46 | +import java.util.Objects; | |
46 | 47 | import java.util.UUID; |
47 | 48 | |
48 | 49 | @RestController |
... | ... | @@ -143,6 +144,15 @@ public class DeviceProfileController extends BaseController { |
143 | 144 | |
144 | 145 | checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); |
145 | 146 | |
147 | + boolean isFirmwareChanged = false; | |
148 | + | |
149 | + if (!created) { | |
150 | + DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); | |
151 | + if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) { | |
152 | + isFirmwareChanged = true; | |
153 | + } | |
154 | + } | |
155 | + | |
146 | 156 | DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); |
147 | 157 | |
148 | 158 | tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); |
... | ... | @@ -153,6 +163,10 @@ public class DeviceProfileController extends BaseController { |
153 | 163 | null, |
154 | 164 | created ? ActionType.ADDED : ActionType.UPDATED, null); |
155 | 165 | |
166 | + if (isFirmwareChanged) { | |
167 | + firmwareStateService.update(savedDeviceProfile); | |
168 | + } | |
169 | + | |
156 | 170 | return savedDeviceProfile; |
157 | 171 | } catch (Exception e) { |
158 | 172 | logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, | ... | ... |
... | ... | @@ -15,7 +15,9 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.controller; |
17 | 17 | |
18 | +import com.google.common.hash.Hashing; | |
18 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.apache.commons.lang3.StringUtils; | |
19 | 21 | import org.springframework.core.io.ByteArrayResource; |
20 | 22 | import org.springframework.http.HttpHeaders; |
21 | 23 | import org.springframework.http.ResponseEntity; |
... | ... | @@ -127,6 +129,11 @@ public class FirmwareController extends BaseController { |
127 | 129 | firmware.setVersion(info.getVersion()); |
128 | 130 | firmware.setAdditionalInfo(info.getAdditionalInfo()); |
129 | 131 | |
132 | + if (StringUtils.isEmpty(checksumAlgorithm)) { | |
133 | + checksumAlgorithm = "sha256"; | |
134 | + checksum = Hashing.sha256().hashBytes(file.getBytes()).toString(); | |
135 | + } | |
136 | + | |
130 | 137 | firmware.setChecksumAlgorithm(checksumAlgorithm); |
131 | 138 | firmware.setChecksum(checksum); |
132 | 139 | firmware.setFileName(file.getOriginalFilename()); | ... | ... |
application/src/main/java/org/thingsboard/server/service/firmware/DefaultFirmwareStateService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.firmware; | |
17 | + | |
18 | +import com.google.common.util.concurrent.FutureCallback; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.springframework.stereotype.Service; | |
21 | +import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; | |
22 | +import org.thingsboard.server.common.data.DataConstants; | |
23 | +import org.thingsboard.server.common.data.Device; | |
24 | +import org.thingsboard.server.common.data.DeviceProfile; | |
25 | +import org.thingsboard.server.common.data.Firmware; | |
26 | +import org.thingsboard.server.common.data.id.DeviceId; | |
27 | +import org.thingsboard.server.common.data.id.FirmwareId; | |
28 | +import org.thingsboard.server.common.data.id.TenantId; | |
29 | +import org.thingsboard.server.common.data.kv.AttributeKvEntry; | |
30 | +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; | |
31 | +import org.thingsboard.server.common.data.kv.BasicTsKvEntry; | |
32 | +import org.thingsboard.server.common.data.kv.LongDataEntry; | |
33 | +import org.thingsboard.server.common.data.kv.StringDataEntry; | |
34 | +import org.thingsboard.server.common.data.kv.TsKvEntry; | |
35 | +import org.thingsboard.server.common.data.page.PageData; | |
36 | +import org.thingsboard.server.common.data.page.PageLink; | |
37 | +import org.thingsboard.server.dao.device.DeviceProfileService; | |
38 | +import org.thingsboard.server.dao.device.DeviceService; | |
39 | +import org.thingsboard.server.dao.firmware.FirmwareService; | |
40 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
41 | + | |
42 | +import javax.annotation.Nullable; | |
43 | +import java.util.ArrayList; | |
44 | +import java.util.Arrays; | |
45 | +import java.util.List; | |
46 | +import java.util.function.Consumer; | |
47 | + | |
48 | +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM; | |
49 | +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM; | |
50 | +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE; | |
51 | +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE; | |
52 | +import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; | |
53 | + | |
54 | +@Slf4j | |
55 | +@Service | |
56 | +@TbCoreComponent | |
57 | +public class DefaultFirmwareStateService implements FirmwareStateService { | |
58 | + | |
59 | + private final FirmwareService firmwareService; | |
60 | + private final DeviceService deviceService; | |
61 | + private final DeviceProfileService deviceProfileService; | |
62 | + private final RuleEngineTelemetryService telemetryService; | |
63 | + | |
64 | + public DefaultFirmwareStateService(FirmwareService firmwareService, DeviceService deviceService, DeviceProfileService deviceProfileService, RuleEngineTelemetryService telemetryService) { | |
65 | + this.firmwareService = firmwareService; | |
66 | + this.deviceService = deviceService; | |
67 | + this.deviceProfileService = deviceProfileService; | |
68 | + this.telemetryService = telemetryService; | |
69 | + } | |
70 | + | |
71 | + @Override | |
72 | + public void update(Device device, boolean created) { | |
73 | + FirmwareId firmwareId = device.getFirmwareId(); | |
74 | + if (firmwareId == null) { | |
75 | + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); | |
76 | + firmwareId = deviceProfile.getFirmwareId(); | |
77 | + } | |
78 | + | |
79 | + if (firmwareId == null) { | |
80 | + if (!created) { | |
81 | + remove(device); | |
82 | + } | |
83 | + } else { | |
84 | + update(device, firmwareService.findFirmwareById(device.getTenantId(), firmwareId), System.currentTimeMillis()); | |
85 | + } | |
86 | + } | |
87 | + | |
88 | + @Override | |
89 | + public void update(DeviceProfile deviceProfile) { | |
90 | + TenantId tenantId = deviceProfile.getTenantId(); | |
91 | + | |
92 | + Consumer<Device> updateConsumer; | |
93 | + if (deviceProfile.getFirmwareId() != null) { | |
94 | + Firmware firmware = firmwareService.findFirmwareById(tenantId, deviceProfile.getFirmwareId()); | |
95 | + long ts = System.currentTimeMillis(); | |
96 | + updateConsumer = d -> update(d, firmware, ts); | |
97 | + } else { | |
98 | + updateConsumer = this::remove; | |
99 | + } | |
100 | + | |
101 | + PageLink pageLink = new PageLink(100); | |
102 | + PageData<Device> pageData; | |
103 | + do { | |
104 | + //TODO: create a query which will return devices without firmware | |
105 | + pageData = deviceService.findDevicesByTenantIdAndType(tenantId, deviceProfile.getName(), pageLink); | |
106 | + | |
107 | + pageData.getData().stream().filter(d -> d.getFirmwareId() == null).forEach(updateConsumer); | |
108 | + | |
109 | + if (pageData.hasNext()) { | |
110 | + pageLink = pageLink.nextPageLink(); | |
111 | + } | |
112 | + } while (pageData.hasNext()); | |
113 | + } | |
114 | + | |
115 | + private void update(Device device, Firmware firmware, long ts) { | |
116 | + TenantId tenantId = device.getTenantId(); | |
117 | + DeviceId deviceId = device.getId(); | |
118 | + | |
119 | + List<TsKvEntry> telemetry = new ArrayList<>(); | |
120 | + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle()))); | |
121 | + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion()))); | |
122 | + | |
123 | + telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { | |
124 | + @Override | |
125 | + public void onSuccess(@Nullable Void tmp) { | |
126 | + log.trace("[{}] Success save telemetry with target firmware for device!", deviceId); | |
127 | + } | |
128 | + | |
129 | + @Override | |
130 | + public void onFailure(Throwable t) { | |
131 | + log.error("[{}] Failed to save telemetry with target firmware for device!", deviceId, t); | |
132 | + } | |
133 | + }); | |
134 | + | |
135 | + List<AttributeKvEntry> attributes = new ArrayList<>(); | |
136 | + | |
137 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle()))); | |
138 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion()))); | |
139 | + | |
140 | + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, (long) firmware.getData().array().length))); | |
141 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm()))); | |
142 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum()))); | |
143 | + telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { | |
144 | + @Override | |
145 | + public void onSuccess(@Nullable Void tmp) { | |
146 | + log.trace("[{}] Success save attributes with target firmware!", deviceId); | |
147 | + } | |
148 | + | |
149 | + @Override | |
150 | + public void onFailure(Throwable t) { | |
151 | + log.error("[{}] Failed to save attributes with target firmware!", deviceId, t); | |
152 | + } | |
153 | + }); | |
154 | + } | |
155 | + | |
156 | + private void remove(Device device) { | |
157 | + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, | |
158 | + Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM), | |
159 | + new FutureCallback<>() { | |
160 | + @Override | |
161 | + public void onSuccess(@Nullable Void tmp) { | |
162 | + log.trace("[{}] Success remove target firmware attributes!", device.getId()); | |
163 | + } | |
164 | + | |
165 | + @Override | |
166 | + public void onFailure(Throwable t) { | |
167 | + log.error("[{}] Failed to remove target firmware attributes!", device.getId(), t); | |
168 | + } | |
169 | + }); | |
170 | + } | |
171 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/firmware/FirmwareStateService.java
0 → 100644
1 | +/** | |
2 | + * Copyright © 2016-2021 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.service.firmware; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.Device; | |
19 | +import org.thingsboard.server.common.data.DeviceProfile; | |
20 | + | |
21 | +public interface FirmwareStateService { | |
22 | + | |
23 | + void update(Device device, boolean created); | |
24 | + | |
25 | + void update(DeviceProfile deviceProfile); | |
26 | + | |
27 | +} | ... | ... |
... | ... | @@ -91,4 +91,19 @@ public class DataConstants { |
91 | 91 | public static final String USERNAME = "username"; |
92 | 92 | public static final String PASSWORD = "password"; |
93 | 93 | |
94 | + //firmware | |
95 | + //telemetry | |
96 | + public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title"; | |
97 | + public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version"; | |
98 | + public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; | |
99 | + public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; | |
100 | + public static final String CURRENT_FIRMWARE_STATE = "cur_fw_state"; | |
101 | + | |
102 | + //attributes | |
103 | + //telemetry | |
104 | + public static final String FIRMWARE_TITLE = "fw_title"; | |
105 | + public static final String FIRMWARE_VERSION = "fw_version"; | |
106 | + public static final String FIRMWARE_SIZE = "fw_size"; | |
107 | + public static final String FIRMWARE_CHECKSUM = "fw_checksum"; | |
108 | + public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm"; | |
94 | 109 | } | ... | ... |
... | ... | @@ -114,7 +114,8 @@ public class BaseFirmwareService implements FirmwareService { |
114 | 114 | log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); |
115 | 115 | validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
116 | 116 | validatePageLink(pageLink); |
117 | - return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink); } | |
117 | + return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink); | |
118 | + } | |
118 | 119 | |
119 | 120 | @Override |
120 | 121 | public void deleteFirmware(TenantId tenantId, FirmwareId firmwareId) { |
... | ... | @@ -211,31 +212,32 @@ public class BaseFirmwareService implements FirmwareService { |
211 | 212 | throw new DataValidationException("Firmware data should be specified!"); |
212 | 213 | } |
213 | 214 | |
214 | - if (firmware.getChecksumAlgorithm() != null) { | |
215 | - if (StringUtils.isEmpty(firmware.getChecksum())) { | |
216 | - throw new DataValidationException("Firmware checksum should be specified!"); | |
217 | - } | |
215 | + if (StringUtils.isEmpty(firmware.getChecksumAlgorithm())) { | |
216 | + throw new DataValidationException("Firmware checksum algorithm should be specified!"); | |
217 | + } | |
218 | + if (StringUtils.isEmpty(firmware.getChecksum())) { | |
219 | + throw new DataValidationException("Firmware checksum should be specified!"); | |
220 | + } | |
218 | 221 | |
219 | - HashFunction hashFunction; | |
220 | - switch (firmware.getChecksumAlgorithm()) { | |
221 | - case "sha256": | |
222 | - hashFunction = Hashing.sha256(); | |
223 | - break; | |
224 | - case "md5": | |
225 | - hashFunction = Hashing.md5(); | |
226 | - break; | |
227 | - case "crc32": | |
228 | - hashFunction = Hashing.crc32(); | |
229 | - break; | |
230 | - default: | |
231 | - throw new DataValidationException("Unknown checksum algorithm!"); | |
232 | - } | |
222 | + HashFunction hashFunction; | |
223 | + switch (firmware.getChecksumAlgorithm()) { | |
224 | + case "sha256": | |
225 | + hashFunction = Hashing.sha256(); | |
226 | + break; | |
227 | + case "md5": | |
228 | + hashFunction = Hashing.md5(); | |
229 | + break; | |
230 | + case "crc32": | |
231 | + hashFunction = Hashing.crc32(); | |
232 | + break; | |
233 | + default: | |
234 | + throw new DataValidationException("Unknown checksum algorithm!"); | |
235 | + } | |
233 | 236 | |
234 | - String currentChecksum = hashFunction.hashBytes(data.array()).toString(); | |
237 | + String currentChecksum = hashFunction.hashBytes(data.array()).toString(); | |
235 | 238 | |
236 | - if (!currentChecksum.equals(firmware.getChecksum())) { | |
237 | - throw new DataValidationException("Wrong firmware file!"); | |
238 | - } | |
239 | + if (!currentChecksum.equals(firmware.getChecksum())) { | |
240 | + throw new DataValidationException("Wrong firmware file!"); | |
239 | 241 | } |
240 | 242 | } |
241 | 243 | ... | ... |