Commit 4843baf7b96cbf79ed512de4aa521ad9c0f85609
Merge branch 'master' of https://github.com/thingsboard/thingsboard into feature…
…/firmware-checksum-autogenerating
Showing
84 changed files
with
1742 additions
and
535 deletions
Too many changes to show.
To preserve performance only 84 of 164 files are displayed.
... | ... | @@ -63,6 +63,8 @@ CREATE TABLE IF NOT EXISTS firmware ( |
63 | 63 | id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, |
64 | 64 | created_time bigint NOT NULL, |
65 | 65 | tenant_id uuid NOT NULL, |
66 | + device_profile_id uuid, | |
67 | + type varchar(32) NOT NULL, | |
66 | 68 | title varchar(255) NOT NULL, |
67 | 69 | version varchar(255) NOT NULL, |
68 | 70 | file_name varchar(255), |
... | ... | @@ -77,10 +79,12 @@ CREATE TABLE IF NOT EXISTS firmware ( |
77 | 79 | ); |
78 | 80 | |
79 | 81 | ALTER TABLE device_profile |
80 | - ADD COLUMN IF NOT EXISTS firmware_id uuid; | |
82 | + ADD COLUMN IF NOT EXISTS firmware_id uuid, | |
83 | + ADD COLUMN IF NOT EXISTS software_id uuid; | |
81 | 84 | |
82 | 85 | ALTER TABLE device |
83 | - ADD COLUMN IF NOT EXISTS firmware_id uuid; | |
86 | + ADD COLUMN IF NOT EXISTS firmware_id uuid, | |
87 | + ADD COLUMN IF NOT EXISTS software_id uuid; | |
84 | 88 | |
85 | 89 | DO $$ |
86 | 90 | BEGIN |
... | ... | @@ -90,11 +94,23 @@ DO $$ |
90 | 94 | FOREIGN KEY (firmware_id) REFERENCES firmware(id); |
91 | 95 | END IF; |
92 | 96 | |
97 | + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device_profile') THEN | |
98 | + ALTER TABLE device_profile | |
99 | + ADD CONSTRAINT fk_software_device_profile | |
100 | + FOREIGN KEY (firmware_id) REFERENCES firmware(id); | |
101 | + END IF; | |
102 | + | |
93 | 103 | IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_firmware_device') THEN |
94 | 104 | ALTER TABLE device |
95 | 105 | ADD CONSTRAINT fk_firmware_device |
96 | 106 | FOREIGN KEY (firmware_id) REFERENCES firmware(id); |
97 | 107 | END IF; |
108 | + | |
109 | + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_software_device') THEN | |
110 | + ALTER TABLE device | |
111 | + ADD CONSTRAINT fk_software_device | |
112 | + FOREIGN KEY (firmware_id) REFERENCES firmware(id); | |
113 | + END IF; | |
98 | 114 | END; |
99 | 115 | $$; |
100 | 116 | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; |
22 | 22 | import org.springframework.context.event.EventListener; |
23 | 23 | import org.springframework.core.annotation.Order; |
24 | 24 | import org.springframework.stereotype.Service; |
25 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
25 | 26 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
26 | 27 | import org.thingsboard.server.actors.ActorSystemContext; |
27 | 28 | import org.thingsboard.server.actors.DefaultTbActorSystem; |
... | ... | @@ -108,7 +109,7 @@ public class DefaultActorService extends TbApplicationEventListener<PartitionCha |
108 | 109 | if (poolSize == 1) { |
109 | 110 | return Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(dispatcherName)); |
110 | 111 | } else { |
111 | - return Executors.newWorkStealingPool(poolSize); | |
112 | + return ThingsBoardExecutors.newWorkStealingPool(poolSize, dispatcherName); | |
112 | 113 | } |
113 | 114 | } |
114 | 115 | ... | ... |
... | ... | @@ -146,12 +146,16 @@ public class DeviceProfileController extends BaseController { |
146 | 146 | checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); |
147 | 147 | |
148 | 148 | boolean isFirmwareChanged = false; |
149 | + boolean isSoftwareChanged = false; | |
149 | 150 | |
150 | 151 | if (!created) { |
151 | 152 | DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfile.getId()); |
152 | 153 | if (!Objects.equals(deviceProfile.getFirmwareId(), oldDeviceProfile.getFirmwareId())) { |
153 | 154 | isFirmwareChanged = true; |
154 | 155 | } |
156 | + if (!Objects.equals(deviceProfile.getSoftwareId(), oldDeviceProfile.getSoftwareId())) { | |
157 | + isSoftwareChanged = true; | |
158 | + } | |
155 | 159 | } |
156 | 160 | |
157 | 161 | DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); |
... | ... | @@ -164,9 +168,8 @@ public class DeviceProfileController extends BaseController { |
164 | 168 | null, |
165 | 169 | created ? ActionType.ADDED : ActionType.UPDATED, null); |
166 | 170 | |
167 | - if (isFirmwareChanged) { | |
168 | - firmwareStateService.update(savedDeviceProfile); | |
169 | - } | |
171 | + firmwareStateService.update(savedDeviceProfile, isFirmwareChanged, isSoftwareChanged); | |
172 | + | |
170 | 173 | sendEntityNotificationMsg(getTenantId(), savedDeviceProfile.getId(), |
171 | 174 | deviceProfile.getId() == null ? EdgeEventActionType.ADDED : EdgeEventActionType.UPDATED); |
172 | 175 | return savedDeviceProfile; | ... | ... |
... | ... | @@ -35,6 +35,8 @@ import org.thingsboard.server.common.data.FirmwareInfo; |
35 | 35 | import org.thingsboard.server.common.data.audit.ActionType; |
36 | 36 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
37 | 37 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
38 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
39 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
38 | 40 | import org.thingsboard.server.common.data.id.FirmwareId; |
39 | 41 | import org.thingsboard.server.common.data.page.PageData; |
40 | 42 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -135,6 +137,8 @@ public class FirmwareController extends BaseController { |
135 | 137 | Firmware firmware = new Firmware(firmwareId); |
136 | 138 | firmware.setCreatedTime(info.getCreatedTime()); |
137 | 139 | firmware.setTenantId(getTenantId()); |
140 | + firmware.setDeviceProfileId(info.getDeviceProfileId()); | |
141 | + firmware.setType(info.getType()); | |
138 | 142 | firmware.setTitle(info.getTitle()); |
139 | 143 | firmware.setVersion(info.getVersion()); |
140 | 144 | firmware.setAdditionalInfo(info.getAdditionalInfo()); |
... | ... | @@ -178,17 +182,22 @@ public class FirmwareController extends BaseController { |
178 | 182 | } |
179 | 183 | |
180 | 184 | @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
181 | - @RequestMapping(value = "/firmwares/{hasData}", method = RequestMethod.GET) | |
185 | + @RequestMapping(value = "/firmwares/{deviceProfileId}/{type}/{hasData}", method = RequestMethod.GET) | |
182 | 186 | @ResponseBody |
183 | - public PageData<FirmwareInfo> getFirmwares(@PathVariable("hasData") boolean hasData, | |
187 | + public PageData<FirmwareInfo> getFirmwares(@PathVariable("deviceProfileId") String strDeviceProfileId, | |
188 | + @PathVariable("type") String strType, | |
189 | + @PathVariable("hasData") boolean hasData, | |
184 | 190 | @RequestParam int pageSize, |
185 | 191 | @RequestParam int page, |
186 | 192 | @RequestParam(required = false) String textSearch, |
187 | 193 | @RequestParam(required = false) String sortProperty, |
188 | 194 | @RequestParam(required = false) String sortOrder) throws ThingsboardException { |
195 | + checkParameter("deviceProfileId", strDeviceProfileId); | |
196 | + checkParameter("type", strType); | |
189 | 197 | try { |
190 | 198 | PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
191 | - return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndHasData(getTenantId(), hasData, pageLink)); | |
199 | + return checkNotNull(firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(getTenantId(), | |
200 | + new DeviceProfileId(toUUID(strDeviceProfileId)), FirmwareType.valueOf(strType), hasData, pageLink)); | |
192 | 201 | } catch (Exception e) { |
193 | 202 | throw handleException(e); |
194 | 203 | } | ... | ... |
... | ... | @@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestParam; |
32 | 32 | import org.springframework.web.bind.annotation.ResponseBody; |
33 | 33 | import org.springframework.web.bind.annotation.ResponseStatus; |
34 | 34 | import org.springframework.web.bind.annotation.RestController; |
35 | +import org.thingsboard.common.util.JacksonUtil; | |
35 | 36 | import org.thingsboard.rule.engine.api.MailService; |
36 | 37 | import org.thingsboard.server.common.data.EntityType; |
37 | 38 | import org.thingsboard.server.common.data.User; |
... | ... | @@ -89,13 +90,14 @@ public class UserController extends BaseController { |
89 | 90 | try { |
90 | 91 | UserId userId = new UserId(toUUID(strUserId)); |
91 | 92 | User user = checkUserId(userId, Operation.READ); |
92 | - if(!user.getAdditionalInfo().isNull()) { | |
93 | - processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), DEFAULT_DASHBOARD); | |
94 | - processDashboardIdFromAdditionalInfo((ObjectNode) user.getAdditionalInfo(), HOME_DASHBOARD); | |
95 | - } | |
96 | - UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); | |
97 | - if(userCredentials.isEnabled()) { | |
98 | - addUserCredentialsEnabled((ObjectNode) user.getAdditionalInfo()); | |
93 | + if(user.getAdditionalInfo().isObject()) { | |
94 | + ObjectNode additionalInfo = (ObjectNode) user.getAdditionalInfo(); | |
95 | + processDashboardIdFromAdditionalInfo(additionalInfo, DEFAULT_DASHBOARD); | |
96 | + processDashboardIdFromAdditionalInfo(additionalInfo, HOME_DASHBOARD); | |
97 | + UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); | |
98 | + if(userCredentials.isEnabled() && !additionalInfo.has("userCredentialsEnabled")) { | |
99 | + additionalInfo.put("userCredentialsEnabled", true); | |
100 | + } | |
99 | 101 | } |
100 | 102 | return user; |
101 | 103 | } catch (Exception e) { |
... | ... | @@ -103,14 +105,6 @@ public class UserController extends BaseController { |
103 | 105 | } |
104 | 106 | } |
105 | 107 | |
106 | - private void addUserCredentialsEnabled(ObjectNode additionalInfo) { | |
107 | - if(!additionalInfo.isNull()) { | |
108 | - if(!additionalInfo.has("userCredentialsEnabled")) { | |
109 | - additionalInfo.put("userCredentialsEnabled", true); | |
110 | - } | |
111 | - } | |
112 | - } | |
113 | - | |
114 | 108 | @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
115 | 109 | @RequestMapping(value = "/user/tokenAccessEnabled", method = RequestMethod.GET) |
116 | 110 | @ResponseBody | ... | ... |
application/src/main/java/org/thingsboard/server/service/apiusage/DefaultTbApiUsageStateService.java
... | ... | @@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired; |
23 | 23 | import org.springframework.beans.factory.annotation.Value; |
24 | 24 | import org.springframework.context.annotation.Lazy; |
25 | 25 | import org.springframework.stereotype.Service; |
26 | +import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
26 | 27 | import org.thingsboard.rule.engine.api.MailService; |
27 | 28 | import org.thingsboard.server.common.data.ApiFeature; |
28 | 29 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
... | ... | @@ -146,7 +147,7 @@ public class DefaultTbApiUsageStateService extends TbApplicationEventListener<Pa |
146 | 147 | this.scheduler = scheduler; |
147 | 148 | this.tenantProfileCache = tenantProfileCache; |
148 | 149 | this.mailService = mailService; |
149 | - this.mailExecutor = Executors.newSingleThreadExecutor(); | |
150 | + this.mailExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("api-usage-svc-mail")); | |
150 | 151 | } |
151 | 152 | |
152 | 153 | @PostConstruct | ... | ... |
... | ... | @@ -19,13 +19,17 @@ import com.google.common.util.concurrent.FutureCallback; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.springframework.stereotype.Service; |
21 | 21 | import org.thingsboard.rule.engine.api.RuleEngineTelemetryService; |
22 | +import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; | |
22 | 23 | import org.thingsboard.server.common.data.DataConstants; |
23 | 24 | import org.thingsboard.server.common.data.Device; |
24 | 25 | import org.thingsboard.server.common.data.DeviceProfile; |
25 | 26 | import org.thingsboard.server.common.data.FirmwareInfo; |
27 | +import org.thingsboard.server.common.data.firmware.FirmwareUtil; | |
28 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
26 | 29 | import org.thingsboard.server.common.data.id.DeviceId; |
27 | 30 | import org.thingsboard.server.common.data.id.FirmwareId; |
28 | 31 | import org.thingsboard.server.common.data.id.TenantId; |
32 | +import org.thingsboard.server.common.data.kv.AttributeKey; | |
29 | 33 | import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
30 | 34 | import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; |
31 | 35 | import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
... | ... | @@ -43,37 +47,49 @@ import org.thingsboard.server.queue.TbQueueProducer; |
43 | 47 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
44 | 48 | import org.thingsboard.server.queue.provider.TbCoreQueueFactory; |
45 | 49 | import org.thingsboard.server.queue.util.TbCoreComponent; |
50 | +import org.thingsboard.server.service.queue.TbClusterService; | |
46 | 51 | |
47 | 52 | import javax.annotation.Nullable; |
48 | 53 | import java.util.ArrayList; |
49 | -import java.util.Arrays; | |
50 | 54 | import java.util.Collections; |
55 | +import java.util.HashSet; | |
51 | 56 | import java.util.List; |
57 | +import java.util.Set; | |
52 | 58 | import java.util.UUID; |
53 | 59 | import java.util.function.Consumer; |
54 | - | |
55 | -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM; | |
56 | -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_CHECKSUM_ALGORITHM; | |
57 | -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_SIZE; | |
58 | -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_TITLE; | |
59 | -import static org.thingsboard.server.common.data.DataConstants.FIRMWARE_VERSION; | |
60 | +import java.util.function.Function; | |
61 | + | |
62 | +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM; | |
63 | +import static org.thingsboard.server.common.data.firmware.FirmwareKey.CHECKSUM_ALGORITHM; | |
64 | +import static org.thingsboard.server.common.data.firmware.FirmwareKey.SIZE; | |
65 | +import static org.thingsboard.server.common.data.firmware.FirmwareKey.STATE; | |
66 | +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TITLE; | |
67 | +import static org.thingsboard.server.common.data.firmware.FirmwareKey.TS; | |
68 | +import static org.thingsboard.server.common.data.firmware.FirmwareKey.VERSION; | |
69 | +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getAttributeKey; | |
70 | +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTargetTelemetryKey; | |
71 | +import static org.thingsboard.server.common.data.firmware.FirmwareUtil.getTelemetryKey; | |
72 | +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; | |
73 | +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; | |
60 | 74 | |
61 | 75 | @Slf4j |
62 | 76 | @Service |
63 | 77 | @TbCoreComponent |
64 | 78 | public class DefaultFirmwareStateService implements FirmwareStateService { |
65 | 79 | |
80 | + private final TbClusterService tbClusterService; | |
66 | 81 | private final FirmwareService firmwareService; |
67 | 82 | private final DeviceService deviceService; |
68 | 83 | private final DeviceProfileService deviceProfileService; |
69 | 84 | private final RuleEngineTelemetryService telemetryService; |
70 | 85 | private final TbQueueProducer<TbProtoQueueMsg<ToFirmwareStateServiceMsg>> fwStateMsgProducer; |
71 | 86 | |
72 | - public DefaultFirmwareStateService(FirmwareService firmwareService, | |
87 | + public DefaultFirmwareStateService(TbClusterService tbClusterService, FirmwareService firmwareService, | |
73 | 88 | DeviceService deviceService, |
74 | 89 | DeviceProfileService deviceProfileService, |
75 | 90 | RuleEngineTelemetryService telemetryService, |
76 | 91 | TbCoreQueueFactory coreQueueFactory) { |
92 | + this.tbClusterService = tbClusterService; | |
77 | 93 | this.firmwareService = firmwareService; |
78 | 94 | this.deviceService = deviceService; |
79 | 95 | this.deviceProfileService = deviceProfileService; |
... | ... | @@ -83,6 +99,11 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
83 | 99 | |
84 | 100 | @Override |
85 | 101 | public void update(Device device, Device oldDevice) { |
102 | + updateFirmware(device, oldDevice); | |
103 | + updateSoftware(device, oldDevice); | |
104 | + } | |
105 | + | |
106 | + private void updateFirmware(Device device, Device oldDevice) { | |
86 | 107 | FirmwareId newFirmwareId = device.getFirmwareId(); |
87 | 108 | if (newFirmwareId == null) { |
88 | 109 | DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); |
... | ... | @@ -97,35 +118,84 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
97 | 118 | } |
98 | 119 | if (!newFirmwareId.equals(oldFirmwareId)) { |
99 | 120 | // Device was updated and new firmware is different from previous firmware. |
100 | - send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis()); | |
121 | + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE); | |
101 | 122 | } |
102 | 123 | } else { |
103 | 124 | // Device was updated and new firmware is not set. |
104 | - remove(device); | |
125 | + remove(device, FIRMWARE); | |
105 | 126 | } |
106 | 127 | } else if (newFirmwareId != null) { |
107 | 128 | // Device was created and firmware is defined. |
108 | - send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis()); | |
129 | + send(device.getTenantId(), device.getId(), newFirmwareId, System.currentTimeMillis(), FIRMWARE); | |
130 | + } | |
131 | + } | |
132 | + | |
133 | + private void updateSoftware(Device device, Device oldDevice) { | |
134 | + FirmwareId newSoftwareId = device.getSoftwareId(); | |
135 | + if (newSoftwareId == null) { | |
136 | + DeviceProfile newDeviceProfile = deviceProfileService.findDeviceProfileById(device.getTenantId(), device.getDeviceProfileId()); | |
137 | + newSoftwareId = newDeviceProfile.getSoftwareId(); | |
138 | + } | |
139 | + if (oldDevice != null) { | |
140 | + if (newSoftwareId != null) { | |
141 | + FirmwareId oldSoftwareId = oldDevice.getSoftwareId(); | |
142 | + if (oldSoftwareId == null) { | |
143 | + DeviceProfile oldDeviceProfile = deviceProfileService.findDeviceProfileById(oldDevice.getTenantId(), oldDevice.getDeviceProfileId()); | |
144 | + oldSoftwareId = oldDeviceProfile.getSoftwareId(); | |
145 | + } | |
146 | + if (!newSoftwareId.equals(oldSoftwareId)) { | |
147 | + // Device was updated and new firmware is different from previous firmware. | |
148 | + send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE); | |
149 | + } | |
150 | + } else { | |
151 | + // Device was updated and new firmware is not set. | |
152 | + remove(device, SOFTWARE); | |
153 | + } | |
154 | + } else if (newSoftwareId != null) { | |
155 | + // Device was created and firmware is defined. | |
156 | + send(device.getTenantId(), device.getId(), newSoftwareId, System.currentTimeMillis(), SOFTWARE); | |
109 | 157 | } |
110 | 158 | } |
111 | 159 | |
112 | 160 | @Override |
113 | - public void update(DeviceProfile deviceProfile) { | |
161 | + public void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged) { | |
114 | 162 | TenantId tenantId = deviceProfile.getTenantId(); |
115 | 163 | |
164 | + if (isFirmwareChanged) { | |
165 | + update(tenantId, deviceProfile, FIRMWARE); | |
166 | + } | |
167 | + if (isSoftwareChanged) { | |
168 | + update(tenantId, deviceProfile, SOFTWARE); | |
169 | + } | |
170 | + } | |
171 | + | |
172 | + private void update(TenantId tenantId, DeviceProfile deviceProfile, FirmwareType firmwareType) { | |
173 | + Function<PageLink, PageData<Device>> getDevicesFunction; | |
116 | 174 | Consumer<Device> updateConsumer; |
175 | + | |
176 | + switch (firmwareType) { | |
177 | + case FIRMWARE: | |
178 | + getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pl); | |
179 | + break; | |
180 | + case SOFTWARE: | |
181 | + getDevicesFunction = pl -> deviceService.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId, deviceProfile.getName(), pl); | |
182 | + break; | |
183 | + default: | |
184 | + log.warn("Unsupported firmware type: [{}]", firmwareType); | |
185 | + return; | |
186 | + } | |
187 | + | |
117 | 188 | if (deviceProfile.getFirmwareId() != null) { |
118 | 189 | long ts = System.currentTimeMillis(); |
119 | - updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts); | |
190 | + updateConsumer = d -> send(d.getTenantId(), d.getId(), deviceProfile.getFirmwareId(), ts, firmwareType); | |
120 | 191 | } else { |
121 | - updateConsumer = this::remove; | |
192 | + updateConsumer = d -> remove(d, firmwareType); | |
122 | 193 | } |
123 | 194 | |
124 | 195 | PageLink pageLink = new PageLink(100); |
125 | 196 | PageData<Device> pageData; |
126 | 197 | do { |
127 | - pageData = deviceService.findDevicesByTenantIdAndTypeAndEmptyFirmware(tenantId, deviceProfile.getName(), pageLink); | |
128 | - | |
198 | + pageData = getDevicesFunction.apply(pageLink); | |
129 | 199 | pageData.getData().forEach(updateConsumer); |
130 | 200 | |
131 | 201 | if (pageData.hasNext()) { |
... | ... | @@ -140,16 +210,17 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
140 | 210 | FirmwareId targetFirmwareId = new FirmwareId(new UUID(msg.getFirmwareIdMSB(), msg.getFirmwareIdLSB())); |
141 | 211 | DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB())); |
142 | 212 | TenantId tenantId = new TenantId(new UUID(msg.getTenantIdMSB(), msg.getTenantIdLSB())); |
213 | + FirmwareType firmwareType = FirmwareType.valueOf(msg.getType()); | |
143 | 214 | long ts = msg.getTs(); |
144 | 215 | |
145 | 216 | Device device = deviceService.findDeviceById(tenantId, deviceId); |
146 | 217 | if (device == null) { |
147 | 218 | log.warn("[{}] [{}] Device was removed during firmware update msg was queued!", tenantId, deviceId); |
148 | 219 | } else { |
149 | - FirmwareId currentFirmwareId = device.getFirmwareId(); | |
150 | - | |
220 | + FirmwareId currentFirmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); | |
151 | 221 | if (currentFirmwareId == null) { |
152 | - currentFirmwareId = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()).getFirmwareId(); | |
222 | + DeviceProfile deviceProfile = deviceProfileService.findDeviceProfileById(tenantId, device.getDeviceProfileId()); | |
223 | + currentFirmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); | |
153 | 224 | } |
154 | 225 | |
155 | 226 | if (targetFirmwareId.equals(currentFirmwareId)) { |
... | ... | @@ -162,7 +233,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
162 | 233 | return isSuccess; |
163 | 234 | } |
164 | 235 | |
165 | - private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts) { | |
236 | + private void send(TenantId tenantId, DeviceId deviceId, FirmwareId firmwareId, long ts, FirmwareType firmwareType) { | |
166 | 237 | ToFirmwareStateServiceMsg msg = ToFirmwareStateServiceMsg.newBuilder() |
167 | 238 | .setTenantIdMSB(tenantId.getId().getMostSignificantBits()) |
168 | 239 | .setTenantIdLSB(tenantId.getId().getLeastSignificantBits()) |
... | ... | @@ -170,6 +241,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
170 | 241 | .setDeviceIdLSB(deviceId.getId().getLeastSignificantBits()) |
171 | 242 | .setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()) |
172 | 243 | .setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()) |
244 | + .setType(firmwareType.name()) | |
173 | 245 | .setTs(ts) |
174 | 246 | .build(); |
175 | 247 | |
... | ... | @@ -183,10 +255,10 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
183 | 255 | fwStateMsgProducer.send(tpi, new TbProtoQueueMsg<>(UUID.randomUUID(), msg), null); |
184 | 256 | |
185 | 257 | List<TsKvEntry> telemetry = new ArrayList<>(); |
186 | - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_TITLE, firmware.getTitle()))); | |
187 | - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.TARGET_FIRMWARE_VERSION, firmware.getVersion()))); | |
188 | - telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(DataConstants.TARGET_FIRMWARE_TS, ts))); | |
189 | - telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.QUEUED.name()))); | |
258 | + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), TITLE), firmware.getTitle()))); | |
259 | + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTargetTelemetryKey(firmware.getType(), VERSION), firmware.getVersion()))); | |
260 | + telemetry.add(new BasicTsKvEntry(ts, new LongDataEntry(getTargetTelemetryKey(firmware.getType(), TS), ts))); | |
261 | + telemetry.add(new BasicTsKvEntry(ts, new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.QUEUED.name()))); | |
190 | 262 | |
191 | 263 | telemetryService.saveAndNotify(tenantId, deviceId, telemetry, new FutureCallback<>() { |
192 | 264 | @Override |
... | ... | @@ -206,7 +278,7 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
206 | 278 | TenantId tenantId = device.getTenantId(); |
207 | 279 | DeviceId deviceId = device.getId(); |
208 | 280 | |
209 | - BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(DataConstants.FIRMWARE_STATE, FirmwareUpdateStatus.INITIATED.name())); | |
281 | + BasicTsKvEntry status = new BasicTsKvEntry(System.currentTimeMillis(), new StringDataEntry(getTelemetryKey(firmware.getType(), STATE), FirmwareUpdateStatus.INITIATED.name())); | |
210 | 282 | |
211 | 283 | telemetryService.saveAndNotify(tenantId, deviceId, Collections.singletonList(status), new FutureCallback<>() { |
212 | 284 | @Override |
... | ... | @@ -221,13 +293,12 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
221 | 293 | }); |
222 | 294 | |
223 | 295 | List<AttributeKvEntry> attributes = new ArrayList<>(); |
296 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), TITLE), firmware.getTitle()))); | |
297 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), VERSION), firmware.getVersion()))); | |
298 | + attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(getAttributeKey(firmware.getType(), SIZE), firmware.getDataSize()))); | |
299 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM_ALGORITHM), firmware.getChecksumAlgorithm().name()))); | |
300 | + attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(getAttributeKey(firmware.getType(), CHECKSUM), firmware.getChecksum()))); | |
224 | 301 | |
225 | - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_TITLE, firmware.getTitle()))); | |
226 | - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_VERSION, firmware.getVersion()))); | |
227 | - | |
228 | - attributes.add(new BaseAttributeKvEntry(ts, new LongDataEntry(FIRMWARE_SIZE, firmware.getDataSize()))); | |
229 | - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM_ALGORITHM, firmware.getChecksumAlgorithm().name()))); | |
230 | - attributes.add(new BaseAttributeKvEntry(ts, new StringDataEntry(DataConstants.FIRMWARE_CHECKSUM, firmware.getChecksum()))); | |
231 | 302 | telemetryService.saveAndNotify(tenantId, deviceId, DataConstants.SHARED_SCOPE, attributes, new FutureCallback<>() { |
232 | 303 | @Override |
233 | 304 | public void onSuccess(@Nullable Void tmp) { |
... | ... | @@ -241,13 +312,15 @@ public class DefaultFirmwareStateService implements FirmwareStateService { |
241 | 312 | }); |
242 | 313 | } |
243 | 314 | |
244 | - private void remove(Device device) { | |
245 | - telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, | |
246 | - Arrays.asList(FIRMWARE_TITLE, FIRMWARE_VERSION, FIRMWARE_SIZE, FIRMWARE_CHECKSUM_ALGORITHM, FIRMWARE_CHECKSUM), | |
315 | + private void remove(Device device, FirmwareType firmwareType) { | |
316 | + telemetryService.deleteAndNotify(device.getTenantId(), device.getId(), DataConstants.SHARED_SCOPE, FirmwareUtil.getAttributeKeys(firmwareType), | |
247 | 317 | new FutureCallback<>() { |
248 | 318 | @Override |
249 | 319 | public void onSuccess(@Nullable Void tmp) { |
250 | 320 | log.trace("[{}] Success remove target firmware attributes!", device.getId()); |
321 | + Set<AttributeKey> keysToNotify = new HashSet<>(); | |
322 | + FirmwareUtil.ALL_FW_ATTRIBUTE_KEYS.forEach(key -> keysToNotify.add(new AttributeKey(DataConstants.SHARED_SCOPE, key))); | |
323 | + tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete(device.getTenantId(), device.getId(), keysToNotify), null); | |
251 | 324 | } |
252 | 325 | |
253 | 326 | @Override | ... | ... |
... | ... | @@ -23,7 +23,7 @@ public interface FirmwareStateService { |
23 | 23 | |
24 | 24 | void update(Device device, Device oldDevice); |
25 | 25 | |
26 | - void update(DeviceProfile deviceProfile); | |
26 | + void update(DeviceProfile deviceProfile, boolean isFirmwareChanged, boolean isSoftwareChanged); | |
27 | 27 | |
28 | 28 | boolean process(ToFirmwareStateServiceMsg msg); |
29 | 29 | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j; |
20 | 20 | import org.springframework.beans.factory.annotation.Value; |
21 | 21 | import org.springframework.scheduling.annotation.Scheduled; |
22 | 22 | import org.springframework.stereotype.Service; |
23 | +import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
23 | 24 | import org.thingsboard.rule.engine.api.RpcError; |
24 | 25 | import org.thingsboard.server.actors.ActorSystemContext; |
25 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
... | ... | @@ -127,7 +128,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< |
127 | 128 | consumers.computeIfAbsent(configuration.getName(), queueName -> tbRuleEngineQueueFactory.createToRuleEngineMsgConsumer(configuration)); |
128 | 129 | consumerStats.put(configuration.getName(), new TbRuleEngineConsumerStats(configuration.getName(), statsFactory)); |
129 | 130 | } |
130 | - submitExecutor = Executors.newSingleThreadExecutor(); | |
131 | + submitExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-rule-engine-consumer-service-submit-executor")); | |
131 | 132 | } |
132 | 133 | |
133 | 134 | @PreDestroy |
... | ... | @@ -160,6 +161,7 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< |
160 | 161 | |
161 | 162 | private void launchConsumer(TbQueueConsumer<TbProtoQueueMsg<ToRuleEngineMsg>> consumer, TbRuleEngineQueueConfiguration configuration, TbRuleEngineConsumerStats stats) { |
162 | 163 | consumersExecutor.execute(() -> { |
164 | + Thread.currentThread().setName("" + Thread.currentThread().getName() + "-" + configuration.getName()); | |
163 | 165 | while (!stopped) { |
164 | 166 | try { |
165 | 167 | List<TbProtoQueueMsg<ToRuleEngineMsg>> msgs = consumer.poll(pollDuration); | ... | ... |
... | ... | @@ -185,7 +185,7 @@ public class DefaultTbResourceService implements TbResourceService { |
185 | 185 | instance.setId(0); |
186 | 186 | List<LwM2mResourceObserve> resources = new ArrayList<>(); |
187 | 187 | obj.resources.forEach((k, v) -> { |
188 | - if (!v.operations.isExecutable()) { | |
188 | + if (v.operations.isReadable()) { | |
189 | 189 | LwM2mResourceObserve lwM2MResourceObserve = new LwM2mResourceObserve(k, v.name, false, false, false); |
190 | 190 | resources.add(lwM2MResourceObserve); |
191 | 191 | } | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import lombok.Getter; |
25 | 25 | import lombok.extern.slf4j.Slf4j; |
26 | 26 | import org.springframework.beans.factory.annotation.Value; |
27 | 27 | import org.springframework.scheduling.annotation.Scheduled; |
28 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
28 | 29 | import org.thingsboard.server.queue.usagestats.TbApiUsageClient; |
29 | 30 | import org.thingsboard.server.service.apiusage.TbApiUsageStateService; |
30 | 31 | |
... | ... | @@ -93,7 +94,7 @@ public abstract class AbstractNashornJsInvokeService extends AbstractJsInvokeSer |
93 | 94 | super.init(maxRequestsTimeout); |
94 | 95 | if (useJsSandbox()) { |
95 | 96 | sandbox = NashornSandboxes.create(); |
96 | - monitorExecutorService = Executors.newWorkStealingPool(getMonitorThreadPoolSize()); | |
97 | + monitorExecutorService = ThingsBoardExecutors.newWorkStealingPool(getMonitorThreadPoolSize(), "nashorn-js-monitor"); | |
97 | 98 | sandbox.setExecutor(monitorExecutorService); |
98 | 99 | sandbox.setMaxCPUTime(getMaxCpuTime()); |
99 | 100 | sandbox.allowNoBraces(false); | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; |
20 | 20 | import org.springframework.context.annotation.Lazy; |
21 | 21 | import org.springframework.context.event.EventListener; |
22 | 22 | import org.springframework.stereotype.Service; |
23 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
23 | 24 | import org.thingsboard.server.gen.transport.TransportProtos; |
24 | 25 | import org.thingsboard.server.queue.discovery.event.ClusterTopologyChangeEvent; |
25 | 26 | import org.thingsboard.server.queue.discovery.event.PartitionChangeEvent; |
... | ... | @@ -62,7 +63,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer |
62 | 63 | private SubscriptionManagerService subscriptionManagerService; |
63 | 64 | |
64 | 65 | private ExecutorService subscriptionUpdateExecutor; |
65 | - | |
66 | + | |
66 | 67 | private TbApplicationEventListener<PartitionChangeEvent> partitionChangeListener = new TbApplicationEventListener<>() { |
67 | 68 | @Override |
68 | 69 | protected void onTbApplicationEvent(PartitionChangeEvent event) { |
... | ... | @@ -93,7 +94,7 @@ public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionSer |
93 | 94 | |
94 | 95 | @PostConstruct |
95 | 96 | public void initExecutor() { |
96 | - subscriptionUpdateExecutor = Executors.newWorkStealingPool(20); | |
97 | + subscriptionUpdateExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass()); | |
97 | 98 | } |
98 | 99 | |
99 | 100 | @PreDestroy | ... | ... |
... | ... | @@ -28,6 +28,8 @@ import org.springframework.beans.factory.annotation.Value; |
28 | 28 | import org.springframework.stereotype.Service; |
29 | 29 | import org.springframework.util.StringUtils; |
30 | 30 | import org.springframework.web.socket.CloseStatus; |
31 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
32 | +import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
31 | 33 | import org.thingsboard.server.common.data.DataConstants; |
32 | 34 | import org.thingsboard.server.common.data.id.CustomerId; |
33 | 35 | import org.thingsboard.server.common.data.id.EntityId; |
... | ... | @@ -157,9 +159,9 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi |
157 | 159 | @PostConstruct |
158 | 160 | public void initExecutor() { |
159 | 161 | serviceId = serviceInfoProvider.getServiceId(); |
160 | - executor = Executors.newWorkStealingPool(50); | |
162 | + executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass()); | |
161 | 163 | |
162 | - pingExecutor = Executors.newSingleThreadScheduledExecutor(); | |
164 | + pingExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("telemetry-web-socket-ping")); | |
163 | 165 | pingExecutor.scheduleWithFixedDelay(this::sendPing, 10000, 10000, TimeUnit.MILLISECONDS); |
164 | 166 | } |
165 | 167 | ... | ... |
... | ... | @@ -41,6 +41,8 @@ import org.thingsboard.server.common.data.TenantProfile; |
41 | 41 | import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials; |
42 | 42 | import org.thingsboard.server.common.data.device.credentials.ProvisionDeviceCredentialsData; |
43 | 43 | import org.thingsboard.server.common.data.device.profile.ProvisionDeviceProfileCredentials; |
44 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
45 | +import org.thingsboard.server.common.data.firmware.FirmwareUtil; | |
44 | 46 | import org.thingsboard.server.common.data.id.CustomerId; |
45 | 47 | import org.thingsboard.server.common.data.id.DeviceId; |
46 | 48 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
... | ... | @@ -512,16 +514,17 @@ public class DefaultTransportApiService implements TransportApiService { |
512 | 514 | private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.GetFirmwareRequestMsg requestMsg) { |
513 | 515 | TenantId tenantId = new TenantId(new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB())); |
514 | 516 | DeviceId deviceId = new DeviceId(new UUID(requestMsg.getDeviceIdMSB(), requestMsg.getDeviceIdLSB())); |
517 | + FirmwareType firmwareType = FirmwareType.valueOf(requestMsg.getType()); | |
515 | 518 | Device device = deviceService.findDeviceById(tenantId, deviceId); |
516 | 519 | |
517 | 520 | if (device == null) { |
518 | 521 | return getEmptyTransportApiResponseFuture(); |
519 | 522 | } |
520 | 523 | |
521 | - FirmwareId firmwareId = device.getFirmwareId(); | |
522 | - | |
524 | + FirmwareId firmwareId = FirmwareUtil.getFirmwareId(device, firmwareType); | |
523 | 525 | if (firmwareId == null) { |
524 | - firmwareId = deviceProfileCache.find(device.getDeviceProfileId()).getFirmwareId(); | |
526 | + DeviceProfile deviceProfile = deviceProfileCache.find(device.getDeviceProfileId()); | |
527 | + firmwareId = FirmwareUtil.getFirmwareId(deviceProfile, firmwareType); | |
525 | 528 | } |
526 | 529 | |
527 | 530 | TransportProtos.GetFirmwareResponseMsg.Builder builder = TransportProtos.GetFirmwareResponseMsg.newBuilder(); |
... | ... | @@ -537,6 +540,7 @@ public class DefaultTransportApiService implements TransportApiService { |
537 | 540 | builder.setResponseStatus(TransportProtos.ResponseStatus.SUCCESS); |
538 | 541 | builder.setFirmwareIdMSB(firmwareId.getId().getMostSignificantBits()); |
539 | 542 | builder.setFirmwareIdLSB(firmwareId.getId().getLeastSignificantBits()); |
543 | + builder.setType(firmwareInfo.getType().name()); | |
540 | 544 | builder.setTitle(firmwareInfo.getTitle()); |
541 | 545 | builder.setVersion(firmwareInfo.getVersion()); |
542 | 546 | builder.setFileName(firmwareInfo.getFileName()); | ... | ... |
... | ... | @@ -21,6 +21,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; |
21 | 21 | import org.springframework.context.event.EventListener; |
22 | 22 | import org.springframework.core.annotation.Order; |
23 | 23 | import org.springframework.stereotype.Service; |
24 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
24 | 25 | import org.thingsboard.server.common.stats.MessagesStats; |
25 | 26 | import org.thingsboard.server.common.stats.StatsFactory; |
26 | 27 | import org.thingsboard.server.common.stats.StatsType; |
... | ... | @@ -70,7 +71,7 @@ public class TbCoreTransportApiService { |
70 | 71 | |
71 | 72 | @PostConstruct |
72 | 73 | public void init() { |
73 | - this.transportCallbackExecutor = Executors.newWorkStealingPool(maxCallbackThreads); | |
74 | + this.transportCallbackExecutor = ThingsBoardExecutors.newWorkStealingPool(maxCallbackThreads, getClass()); | |
74 | 75 | TbQueueProducer<TbProtoQueueMsg<TransportApiResponseMsg>> producer = tbCoreQueueFactory.createTransportApiResponseProducer(); |
75 | 76 | TbQueueConsumer<TbProtoQueueMsg<TransportApiRequestMsg>> consumer = tbCoreQueueFactory.createTransportApiRequestConsumer(); |
76 | 77 | ... | ... |
... | ... | @@ -24,10 +24,13 @@ import org.springframework.mock.web.MockMultipartFile; |
24 | 24 | import org.springframework.test.web.servlet.request.MockMultipartHttpServletRequestBuilder; |
25 | 25 | import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; |
26 | 26 | import org.thingsboard.common.util.JacksonUtil; |
27 | +import org.thingsboard.server.common.data.DeviceProfile; | |
27 | 28 | import org.thingsboard.server.common.data.Firmware; |
28 | 29 | import org.thingsboard.server.common.data.FirmwareInfo; |
29 | 30 | import org.thingsboard.server.common.data.Tenant; |
30 | 31 | import org.thingsboard.server.common.data.User; |
32 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
33 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
31 | 34 | import org.thingsboard.server.common.data.page.PageData; |
32 | 35 | import org.thingsboard.server.common.data.page.PageLink; |
33 | 36 | import org.thingsboard.server.common.data.security.Authority; |
... | ... | @@ -38,6 +41,7 @@ import java.util.Collections; |
38 | 41 | import java.util.List; |
39 | 42 | |
40 | 43 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
44 | +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; | |
41 | 45 | |
42 | 46 | public abstract class BaseFirmwareControllerTest extends AbstractControllerTest { |
43 | 47 | |
... | ... | @@ -53,6 +57,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
53 | 57 | |
54 | 58 | private Tenant savedTenant; |
55 | 59 | private User tenantAdmin; |
60 | + private DeviceProfileId deviceProfileId; | |
56 | 61 | |
57 | 62 | @Before |
58 | 63 | public void beforeTest() throws Exception { |
... | ... | @@ -71,6 +76,11 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
71 | 76 | tenantAdmin.setLastName("Downs"); |
72 | 77 | |
73 | 78 | tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); |
79 | + | |
80 | + DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", null); | |
81 | + DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); | |
82 | + Assert.assertNotNull(savedDeviceProfile); | |
83 | + deviceProfileId = savedDeviceProfile.getId(); | |
74 | 84 | } |
75 | 85 | |
76 | 86 | @After |
... | ... | @@ -84,6 +94,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
84 | 94 | @Test |
85 | 95 | public void testSaveFirmware() throws Exception { |
86 | 96 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
97 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
98 | + firmwareInfo.setType(FIRMWARE); | |
87 | 99 | firmwareInfo.setTitle(TITLE); |
88 | 100 | firmwareInfo.setVersion(VERSION); |
89 | 101 | |
... | ... | @@ -107,6 +119,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
107 | 119 | @Test |
108 | 120 | public void testSaveFirmwareData() throws Exception { |
109 | 121 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
122 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
123 | + firmwareInfo.setType(FIRMWARE); | |
110 | 124 | firmwareInfo.setTitle(TITLE); |
111 | 125 | firmwareInfo.setVersion(VERSION); |
112 | 126 | |
... | ... | @@ -137,6 +151,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
137 | 151 | @Test |
138 | 152 | public void testUpdateFirmwareFromDifferentTenant() throws Exception { |
139 | 153 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
154 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
155 | + firmwareInfo.setType(FIRMWARE); | |
140 | 156 | firmwareInfo.setTitle(TITLE); |
141 | 157 | firmwareInfo.setVersion(VERSION); |
142 | 158 | |
... | ... | @@ -150,6 +166,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
150 | 166 | @Test |
151 | 167 | public void testFindFirmwareInfoById() throws Exception { |
152 | 168 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
169 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
170 | + firmwareInfo.setType(FIRMWARE); | |
153 | 171 | firmwareInfo.setTitle(TITLE); |
154 | 172 | firmwareInfo.setVersion(VERSION); |
155 | 173 | |
... | ... | @@ -163,6 +181,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
163 | 181 | @Test |
164 | 182 | public void testFindFirmwareById() throws Exception { |
165 | 183 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
184 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
185 | + firmwareInfo.setType(FIRMWARE); | |
166 | 186 | firmwareInfo.setTitle(TITLE); |
167 | 187 | firmwareInfo.setVersion(VERSION); |
168 | 188 | |
... | ... | @@ -180,6 +200,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
180 | 200 | @Test |
181 | 201 | public void testDeleteFirmware() throws Exception { |
182 | 202 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
203 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
204 | + firmwareInfo.setType(FIRMWARE); | |
183 | 205 | firmwareInfo.setTitle(TITLE); |
184 | 206 | firmwareInfo.setVersion(VERSION); |
185 | 207 | |
... | ... | @@ -197,6 +219,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
197 | 219 | List<FirmwareInfo> firmwares = new ArrayList<>(); |
198 | 220 | for (int i = 0; i < 165; i++) { |
199 | 221 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
222 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
223 | + firmwareInfo.setType(FIRMWARE); | |
200 | 224 | firmwareInfo.setTitle(TITLE); |
201 | 225 | firmwareInfo.setVersion(VERSION + i); |
202 | 226 | |
... | ... | @@ -238,6 +262,8 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
238 | 262 | |
239 | 263 | for (int i = 0; i < 165; i++) { |
240 | 264 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
265 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
266 | + firmwareInfo.setType(FIRMWARE); | |
241 | 267 | firmwareInfo.setTitle(TITLE); |
242 | 268 | firmwareInfo.setVersion(VERSION + i); |
243 | 269 | |
... | ... | @@ -257,7 +283,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
257 | 283 | PageLink pageLink = new PageLink(24); |
258 | 284 | PageData<FirmwareInfo> pageData; |
259 | 285 | do { |
260 | - pageData = doGetTypedWithPageLink("/api/firmwares/true?", | |
286 | + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/true?", | |
261 | 287 | new TypeReference<>() { |
262 | 288 | }, pageLink); |
263 | 289 | loadedFirmwaresWithData.addAll(pageData.getData()); |
... | ... | @@ -269,7 +295,7 @@ public abstract class BaseFirmwareControllerTest extends AbstractControllerTest |
269 | 295 | List<FirmwareInfo> loadedFirmwaresWithoutData = new ArrayList<>(); |
270 | 296 | pageLink = new PageLink(24); |
271 | 297 | do { |
272 | - pageData = doGetTypedWithPageLink("/api/firmwares/false?", | |
298 | + pageData = doGetTypedWithPageLink("/api/firmwares/" + deviceProfileId.toString() + "/FIRMWARE/false?", | |
273 | 299 | new TypeReference<>() { |
274 | 300 | }, pageLink); |
275 | 301 | loadedFirmwaresWithoutData.addAll(pageData.getData()); | ... | ... |
... | ... | @@ -16,10 +16,10 @@ |
16 | 16 | package org.thingsboard.server.service.queue; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | +import org.junit.After; | |
19 | 20 | import org.junit.Assert; |
20 | 21 | import org.junit.Test; |
21 | 22 | import org.junit.runner.RunWith; |
22 | -import org.mockito.Mockito; | |
23 | 23 | import org.mockito.junit.MockitoJUnitRunner; |
24 | 24 | import org.thingsboard.server.gen.transport.TransportProtos; |
25 | 25 | import org.thingsboard.server.queue.common.TbProtoQueueMsg; |
... | ... | @@ -28,39 +28,74 @@ import org.thingsboard.server.service.queue.processing.TbRuleEngineSubmitStrateg |
28 | 28 | import java.util.UUID; |
29 | 29 | import java.util.concurrent.ConcurrentHashMap; |
30 | 30 | import java.util.concurrent.ConcurrentMap; |
31 | +import java.util.concurrent.CountDownLatch; | |
31 | 32 | import java.util.concurrent.ExecutorService; |
32 | 33 | import java.util.concurrent.Executors; |
33 | 34 | import java.util.concurrent.TimeUnit; |
34 | 35 | |
36 | +import static org.junit.Assert.assertTrue; | |
37 | +import static org.mockito.ArgumentMatchers.any; | |
35 | 38 | import static org.mockito.Mockito.mock; |
39 | +import static org.mockito.Mockito.times; | |
40 | +import static org.mockito.Mockito.verify; | |
36 | 41 | import static org.mockito.Mockito.when; |
37 | 42 | |
38 | 43 | @Slf4j |
39 | 44 | @RunWith(MockitoJUnitRunner.class) |
40 | 45 | public class TbMsgPackProcessingContextTest { |
41 | 46 | |
47 | + public static final int TIMEOUT = 10; | |
48 | + ExecutorService executorService; | |
49 | + | |
50 | + @After | |
51 | + public void tearDown() { | |
52 | + if (executorService != null) { | |
53 | + executorService.shutdownNow(); | |
54 | + } | |
55 | + } | |
56 | + | |
42 | 57 | @Test |
43 | 58 | public void testHighConcurrencyCase() throws InterruptedException { |
44 | - TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class); | |
59 | + //log.warn("preparing the test..."); | |
45 | 60 | int msgCount = 1000; |
46 | 61 | int parallelCount = 5; |
47 | - ExecutorService executorService = Executors.newFixedThreadPool(parallelCount); | |
48 | - try { | |
49 | - ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> messages = new ConcurrentHashMap<>(); | |
50 | - for (int i = 0; i < msgCount; i++) { | |
51 | - messages.put(UUID.randomUUID(), new TbProtoQueueMsg<>(UUID.randomUUID(), null)); | |
52 | - } | |
53 | - when(strategyMock.getPendingMap()).thenReturn(messages); | |
54 | - TbMsgPackProcessingContext context = new TbMsgPackProcessingContext("Main", strategyMock); | |
55 | - for (UUID uuid : messages.keySet()) { | |
56 | - for (int i = 0; i < parallelCount; i++) { | |
57 | - executorService.submit(() -> context.onSuccess(uuid)); | |
58 | - } | |
62 | + executorService = Executors.newFixedThreadPool(parallelCount); | |
63 | + | |
64 | + ConcurrentMap<UUID, TbProtoQueueMsg<TransportProtos.ToRuleEngineMsg>> messages = new ConcurrentHashMap<>(msgCount); | |
65 | + for (int i = 0; i < msgCount; i++) { | |
66 | + messages.put(UUID.randomUUID(), new TbProtoQueueMsg<>(UUID.randomUUID(), null)); | |
67 | + } | |
68 | + TbRuleEngineSubmitStrategy strategyMock = mock(TbRuleEngineSubmitStrategy.class); | |
69 | + when(strategyMock.getPendingMap()).thenReturn(messages); | |
70 | + | |
71 | + TbMsgPackProcessingContext context = new TbMsgPackProcessingContext("Main", strategyMock); | |
72 | + for (UUID uuid : messages.keySet()) { | |
73 | + final CountDownLatch readyLatch = new CountDownLatch(parallelCount); | |
74 | + final CountDownLatch startLatch = new CountDownLatch(1); | |
75 | + final CountDownLatch finishLatch = new CountDownLatch(parallelCount); | |
76 | + for (int i = 0; i < parallelCount; i++) { | |
77 | + //final String taskName = "" + uuid + " " + i; | |
78 | + executorService.submit(() -> { | |
79 | + //log.warn("ready {}", taskName); | |
80 | + readyLatch.countDown(); | |
81 | + try { | |
82 | + startLatch.await(); | |
83 | + } catch (InterruptedException e) { | |
84 | + Assert.fail("failed to await"); | |
85 | + } | |
86 | + //log.warn("go {}", taskName); | |
87 | + | |
88 | + context.onSuccess(uuid); | |
89 | + | |
90 | + finishLatch.countDown(); | |
91 | + }); | |
59 | 92 | } |
60 | - Assert.assertTrue(context.await(10, TimeUnit.SECONDS)); | |
61 | - Mockito.verify(strategyMock, Mockito.times(msgCount)).onSuccess(Mockito.any(UUID.class)); | |
62 | - } finally { | |
63 | - executorService.shutdownNow(); | |
93 | + assertTrue(readyLatch.await(TIMEOUT, TimeUnit.SECONDS)); | |
94 | + Thread.yield(); | |
95 | + startLatch.countDown(); //run all-at-once submitted tasks | |
96 | + assertTrue(finishLatch.await(TIMEOUT, TimeUnit.SECONDS)); | |
64 | 97 | } |
98 | + assertTrue(context.await(TIMEOUT, TimeUnit.SECONDS)); | |
99 | + verify(strategyMock, times(msgCount)).onSuccess(any(UUID.class)); | |
65 | 100 | } |
66 | 101 | } | ... | ... |
... | ... | @@ -65,6 +65,8 @@ public interface DeviceService { |
65 | 65 | |
66 | 66 | PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink); |
67 | 67 | |
68 | + PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink); | |
69 | + | |
68 | 70 | PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink); |
69 | 71 | |
70 | 72 | PageData<DeviceInfo> findDeviceInfosByTenantIdAndDeviceProfileId(TenantId tenantId, DeviceProfileId deviceProfileId, PageLink pageLink); | ... | ... |
... | ... | @@ -18,8 +18,9 @@ package org.thingsboard.server.dao.firmware; |
18 | 18 | import com.google.common.util.concurrent.ListenableFuture; |
19 | 19 | import org.thingsboard.server.common.data.Firmware; |
20 | 20 | import org.thingsboard.server.common.data.FirmwareInfo; |
21 | -import org.thingsboard.server.common.data.exception.ThingsboardException; | |
22 | 21 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
22 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
23 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
23 | 24 | import org.thingsboard.server.common.data.id.FirmwareId; |
24 | 25 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 26 | import org.thingsboard.server.common.data.page.PageData; |
... | ... | @@ -33,7 +34,7 @@ public interface FirmwareService { |
33 | 34 | |
34 | 35 | Firmware saveFirmware(Firmware firmware); |
35 | 36 | |
36 | - String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) throws ThingsboardException; | |
37 | + String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data); | |
37 | 38 | |
38 | 39 | Firmware findFirmwareById(TenantId tenantId, FirmwareId firmwareId); |
39 | 40 | |
... | ... | @@ -43,7 +44,7 @@ public interface FirmwareService { |
43 | 44 | |
44 | 45 | PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink); |
45 | 46 | |
46 | - PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); | |
47 | + PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink); | |
47 | 48 | |
48 | 49 | void deleteFirmware(TenantId tenantId, FirmwareId firmwareId); |
49 | 50 | ... | ... |
... | ... | @@ -93,22 +93,26 @@ public class DataConstants { |
93 | 93 | public static final String USERNAME = "username"; |
94 | 94 | public static final String PASSWORD = "password"; |
95 | 95 | |
96 | - //firmware | |
97 | - //telemetry | |
98 | - public static final String CURRENT_FIRMWARE_TITLE = "cur_fw_title"; | |
99 | - public static final String CURRENT_FIRMWARE_VERSION = "cur_fw_version"; | |
100 | - public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; | |
101 | - public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; | |
102 | - public static final String TARGET_FIRMWARE_TS = "target_fw_ts"; | |
103 | - public static final String FIRMWARE_STATE = "fw_state"; | |
104 | - | |
105 | - //attributes | |
106 | - //telemetry | |
107 | - public static final String FIRMWARE_TITLE = "fw_title"; | |
108 | - public static final String FIRMWARE_VERSION = "fw_version"; | |
109 | - public static final String FIRMWARE_SIZE = "fw_size"; | |
110 | - public static final String FIRMWARE_CHECKSUM = "fw_checksum"; | |
111 | - public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm"; | |
96 | +//<<<<<<< HEAD | |
97 | +//======= | |
98 | +// //firmware | |
99 | +// //telemetry | |
100 | +// public static final String CURRENT_FIRMWARE_TITLE = "current_fw_title"; | |
101 | +// public static final String CURRENT_FIRMWARE_VERSION = "current_fw_version"; | |
102 | +// public static final String TARGET_FIRMWARE_TITLE = "target_fw_title"; | |
103 | +// public static final String TARGET_FIRMWARE_VERSION = "target_fw_version"; | |
104 | +// public static final String TARGET_FIRMWARE_TS = "target_fw_ts"; | |
105 | +// public static final String FIRMWARE_STATE = "fw_state"; | |
106 | +// | |
107 | +// //attributes | |
108 | +// //telemetry | |
109 | +// public static final String FIRMWARE_TITLE = "fw_title"; | |
110 | +// public static final String FIRMWARE_VERSION = "fw_version"; | |
111 | +// public static final String FIRMWARE_SIZE = "fw_size"; | |
112 | +// public static final String FIRMWARE_CHECKSUM = "fw_checksum"; | |
113 | +// public static final String FIRMWARE_CHECKSUM_ALGORITHM = "fw_checksum_algorithm"; | |
114 | +//>>>>>>> origin/master | |
112 | 115 | public static final String EDGE_MSG_SOURCE = "edge"; |
113 | 116 | public static final String MSG_SOURCE_KEY = "source"; |
117 | + | |
114 | 118 | } | ... | ... |
... | ... | @@ -32,7 +32,7 @@ import java.io.IOException; |
32 | 32 | |
33 | 33 | @EqualsAndHashCode(callSuper = true) |
34 | 34 | @Slf4j |
35 | -public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId { | |
35 | +public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implements HasName, HasTenantId, HasCustomerId, HasFirmware { | |
36 | 36 | |
37 | 37 | private static final long serialVersionUID = 2807343040519543363L; |
38 | 38 | |
... | ... | @@ -50,6 +50,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
50 | 50 | private byte[] deviceDataBytes; |
51 | 51 | |
52 | 52 | private FirmwareId firmwareId; |
53 | + private FirmwareId softwareId; | |
53 | 54 | |
54 | 55 | public Device() { |
55 | 56 | super(); |
... | ... | @@ -69,6 +70,7 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
69 | 70 | this.deviceProfileId = device.getDeviceProfileId(); |
70 | 71 | this.setDeviceData(device.getDeviceData()); |
71 | 72 | this.firmwareId = device.getFirmwareId(); |
73 | + this.softwareId = device.getSoftwareId(); | |
72 | 74 | } |
73 | 75 | |
74 | 76 | public Device updateDevice(Device device) { |
... | ... | @@ -79,6 +81,8 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
79 | 81 | this.label = device.getLabel(); |
80 | 82 | this.deviceProfileId = device.getDeviceProfileId(); |
81 | 83 | this.setDeviceData(device.getDeviceData()); |
84 | + this.setFirmwareId(device.getFirmwareId()); | |
85 | + this.setSoftwareId(device.getSoftwareId()); | |
82 | 86 | return this; |
83 | 87 | } |
84 | 88 | |
... | ... | @@ -171,6 +175,14 @@ public class Device extends SearchTextBasedWithAdditionalInfo<DeviceId> implemen |
171 | 175 | this.firmwareId = firmwareId; |
172 | 176 | } |
173 | 177 | |
178 | + public FirmwareId getSoftwareId() { | |
179 | + return softwareId; | |
180 | + } | |
181 | + | |
182 | + public void setSoftwareId(FirmwareId softwareId) { | |
183 | + this.softwareId = softwareId; | |
184 | + } | |
185 | + | |
174 | 186 | @Override |
175 | 187 | public String toString() { |
176 | 188 | StringBuilder builder = new StringBuilder(); | ... | ... |
... | ... | @@ -36,7 +36,7 @@ import static org.thingsboard.server.common.data.SearchTextBasedWithAdditionalIn |
36 | 36 | @Data |
37 | 37 | @EqualsAndHashCode(callSuper = true) |
38 | 38 | @Slf4j |
39 | -public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId { | |
39 | +public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements HasName, HasTenantId, HasFirmware { | |
40 | 40 | |
41 | 41 | private TenantId tenantId; |
42 | 42 | @NoXss |
... | ... | @@ -59,6 +59,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H |
59 | 59 | |
60 | 60 | private FirmwareId firmwareId; |
61 | 61 | |
62 | + private FirmwareId softwareId; | |
63 | + | |
62 | 64 | public DeviceProfile() { |
63 | 65 | super(); |
64 | 66 | } |
... | ... | @@ -77,6 +79,8 @@ public class DeviceProfile extends SearchTextBased<DeviceProfileId> implements H |
77 | 79 | this.defaultQueueName = deviceProfile.getDefaultQueueName(); |
78 | 80 | this.setProfileData(deviceProfile.getProfileData()); |
79 | 81 | this.provisionDeviceKey = deviceProfile.getProvisionDeviceKey(); |
82 | + this.firmwareId = deviceProfile.getFirmwareId(); | |
83 | + this.softwareId = deviceProfile.getSoftwareId(); | |
80 | 84 | } |
81 | 85 | |
82 | 86 | @Override | ... | ... |
... | ... | @@ -20,6 +20,8 @@ import lombok.Data; |
20 | 20 | import lombok.EqualsAndHashCode; |
21 | 21 | import lombok.extern.slf4j.Slf4j; |
22 | 22 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
23 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
24 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
23 | 25 | import org.thingsboard.server.common.data.id.FirmwareId; |
24 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
25 | 27 | |
... | ... | @@ -31,6 +33,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> |
31 | 33 | private static final long serialVersionUID = 3168391583570815419L; |
32 | 34 | |
33 | 35 | private TenantId tenantId; |
36 | + private DeviceProfileId deviceProfileId; | |
37 | + private FirmwareType type; | |
34 | 38 | private String title; |
35 | 39 | private String version; |
36 | 40 | private boolean hasData; |
... | ... | @@ -52,6 +56,8 @@ public class FirmwareInfo extends SearchTextBasedWithAdditionalInfo<FirmwareId> |
52 | 56 | public FirmwareInfo(FirmwareInfo firmwareInfo) { |
53 | 57 | super(firmwareInfo); |
54 | 58 | this.tenantId = firmwareInfo.getTenantId(); |
59 | + this.deviceProfileId = firmwareInfo.getDeviceProfileId(); | |
60 | + this.type = firmwareInfo.getType(); | |
55 | 61 | this.title = firmwareInfo.getTitle(); |
56 | 62 | this.version = firmwareInfo.getVersion(); |
57 | 63 | this.hasData = firmwareInfo.isHasData(); | ... | ... |
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.common.data; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.id.FirmwareId; | |
19 | + | |
20 | +public interface HasFirmware { | |
21 | + | |
22 | + FirmwareId getFirmwareId(); | |
23 | + | |
24 | + FirmwareId getSoftwareId(); | |
25 | +} | ... | ... |
... | ... | @@ -31,6 +31,7 @@ public class MqttTopics { |
31 | 31 | private static final String SUB_TOPIC = "+"; |
32 | 32 | private static final String PROVISION = "/provision"; |
33 | 33 | private static final String FIRMWARE = "/fw"; |
34 | + private static final String SOFTWARE = "/sw"; | |
34 | 35 | private static final String CHUNK = "/chunk/"; |
35 | 36 | private static final String ERROR = "/error"; |
36 | 37 | |
... | ... | @@ -75,9 +76,17 @@ public class MqttTopics { |
75 | 76 | // v2 topics |
76 | 77 | public static final String BASE_DEVICE_API_TOPIC_V2 = "v2"; |
77 | 78 | |
78 | - public static final String DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/"; | |
79 | - public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + SUB_TOPIC + CHUNK + SUB_TOPIC; | |
79 | + public static final String REQUEST_ID_PATTERN = "(?<requestId>\\d+)"; | |
80 | + public static final String CHUNK_PATTERN = "(?<chunk>\\d+)"; | |
81 | + | |
82 | + public static final String DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN; | |
83 | + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC; | |
80 | 84 | public static final String DEVICE_FIRMWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + FIRMWARE + ERROR; |
85 | + public static final String DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT = BASE_DEVICE_API_TOPIC_V2 + "%s" + RESPONSE + "/"+ "%s" + CHUNK + "%d"; | |
86 | + | |
87 | + public static final String DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + REQUEST + "/" + REQUEST_ID_PATTERN + CHUNK + CHUNK_PATTERN; | |
88 | + public static final String DEVICE_SOFTWARE_RESPONSES_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + RESPONSE + "/" + SUB_TOPIC + CHUNK + SUB_TOPIC; | |
89 | + public static final String DEVICE_SOFTWARE_ERROR_TOPIC = BASE_DEVICE_API_TOPIC_V2 + SOFTWARE + ERROR; | |
81 | 90 | |
82 | 91 | private MqttTopics() { |
83 | 92 | } | ... | ... |
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.common.data.firmware; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | + | |
20 | +public enum FirmwareKey { | |
21 | + | |
22 | + TITLE("title"), VERSION("version"), TS("ts"), STATE("state"), SIZE("size"), CHECKSUM("checksum"), CHECKSUM_ALGORITHM("checksum_algorithm"); | |
23 | + | |
24 | + @Getter | |
25 | + private final String value; | |
26 | + | |
27 | + FirmwareKey(String value) { | |
28 | + this.value = value; | |
29 | + } | |
30 | +} | ... | ... |
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.common.data.firmware; | |
17 | + | |
18 | +import lombok.Getter; | |
19 | + | |
20 | +public enum FirmwareType { | |
21 | + | |
22 | + FIRMWARE("fw"), SOFTWARE("sw"); | |
23 | + | |
24 | + @Getter | |
25 | + private final String keyPrefix; | |
26 | + | |
27 | + FirmwareType(String keyPrefix) { | |
28 | + this.keyPrefix = keyPrefix; | |
29 | + } | |
30 | +} | ... | ... |
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.common.data.firmware; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.thingsboard.server.common.data.HasFirmware; | |
20 | +import org.thingsboard.server.common.data.id.FirmwareId; | |
21 | + | |
22 | +import java.util.ArrayList; | |
23 | +import java.util.Collections; | |
24 | +import java.util.List; | |
25 | + | |
26 | +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; | |
27 | +import static org.thingsboard.server.common.data.firmware.FirmwareType.SOFTWARE; | |
28 | + | |
29 | +@Slf4j | |
30 | +public class FirmwareUtil { | |
31 | + | |
32 | + public static final List<String> ALL_FW_ATTRIBUTE_KEYS; | |
33 | + | |
34 | + public static final List<String> ALL_SW_ATTRIBUTE_KEYS; | |
35 | + | |
36 | + static { | |
37 | + ALL_FW_ATTRIBUTE_KEYS = new ArrayList<>(); | |
38 | + for (FirmwareKey key : FirmwareKey.values()) { | |
39 | + ALL_FW_ATTRIBUTE_KEYS.add(getAttributeKey(FIRMWARE, key)); | |
40 | + | |
41 | + } | |
42 | + | |
43 | + ALL_SW_ATTRIBUTE_KEYS = new ArrayList<>(); | |
44 | + for (FirmwareKey key : FirmwareKey.values()) { | |
45 | + ALL_SW_ATTRIBUTE_KEYS.add(getAttributeKey(SOFTWARE, key)); | |
46 | + | |
47 | + } | |
48 | + } | |
49 | + | |
50 | + public static List<String> getAttributeKeys(FirmwareType firmwareType) { | |
51 | + switch (firmwareType) { | |
52 | + case FIRMWARE: | |
53 | + return ALL_FW_ATTRIBUTE_KEYS; | |
54 | + case SOFTWARE: | |
55 | + return ALL_SW_ATTRIBUTE_KEYS; | |
56 | + } | |
57 | + return Collections.emptyList(); | |
58 | + } | |
59 | + | |
60 | + public static String getAttributeKey(FirmwareType type, FirmwareKey key) { | |
61 | + return type.getKeyPrefix() + "_" + key.getValue(); | |
62 | + } | |
63 | + | |
64 | + public static String getTargetTelemetryKey(FirmwareType type, FirmwareKey key) { | |
65 | + return getTelemetryKey("target_", type, key); | |
66 | + } | |
67 | + | |
68 | + public static String getCurrentTelemetryKey(FirmwareType type, FirmwareKey key) { | |
69 | + return getTelemetryKey("current_", type, key); | |
70 | + } | |
71 | + | |
72 | + private static String getTelemetryKey(String prefix, FirmwareType type, FirmwareKey key) { | |
73 | + return prefix + type.getKeyPrefix() + "_" + key.getValue(); | |
74 | + } | |
75 | + | |
76 | + public static String getTelemetryKey(FirmwareType type, FirmwareKey key) { | |
77 | + return type.getKeyPrefix() + "_" + key.getValue(); | |
78 | + } | |
79 | + | |
80 | + public static FirmwareId getFirmwareId(HasFirmware entity, FirmwareType firmwareType) { | |
81 | + switch (firmwareType) { | |
82 | + case FIRMWARE: | |
83 | + return entity.getFirmwareId(); | |
84 | + case SOFTWARE: | |
85 | + return entity.getSoftwareId(); | |
86 | + default: | |
87 | + log.warn("Unsupported firmware type: [{}]", firmwareType); | |
88 | + return null; | |
89 | + } | |
90 | + } | |
91 | +} | ... | ... |
... | ... | @@ -38,6 +38,7 @@ import static java.util.Collections.emptyList; |
38 | 38 | @Slf4j |
39 | 39 | public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> implements TbQueueConsumer<T> { |
40 | 40 | |
41 | + public static final long ONE_MILLISECOND_IN_NANOS = TimeUnit.MILLISECONDS.toNanos(1); | |
41 | 42 | private volatile boolean subscribed; |
42 | 43 | protected volatile boolean stopped = false; |
43 | 44 | protected volatile Set<TopicPartitionInfo> partitions; |
... | ... | @@ -83,7 +84,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i |
83 | 84 | } |
84 | 85 | |
85 | 86 | if (consumerLock.isLocked()) { |
86 | - log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock", new RuntimeException("stacktrace")); | |
87 | + log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace")); | |
87 | 88 | } |
88 | 89 | |
89 | 90 | consumerLock.lock(); |
... | ... | @@ -131,9 +132,12 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i |
131 | 132 | List<T> sleepAndReturnEmpty(final long startNanos, final long durationInMillis) { |
132 | 133 | long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis); |
133 | 134 | long spentNanos = System.nanoTime() - startNanos; |
134 | - if (spentNanos < durationNanos) { | |
135 | + long nanosLeft = durationNanos - spentNanos; | |
136 | + if (nanosLeft >= ONE_MILLISECOND_IN_NANOS) { | |
135 | 137 | try { |
136 | - Thread.sleep(Math.max(TimeUnit.NANOSECONDS.toMillis(durationNanos - spentNanos), 1)); | |
138 | + long sleepMs = TimeUnit.NANOSECONDS.toMillis(nanosLeft); | |
139 | + log.trace("Going to sleep after poll: topic {} for {}ms", topic, sleepMs); | |
140 | + Thread.sleep(sleepMs); | |
137 | 141 | } catch (InterruptedException e) { |
138 | 142 | if (!stopped) { |
139 | 143 | log.error("Failed to wait", e); |
... | ... | @@ -146,7 +150,7 @@ public abstract class AbstractTbQueueConsumerTemplate<R, T extends TbQueueMsg> i |
146 | 150 | @Override |
147 | 151 | public void commit() { |
148 | 152 | if (consumerLock.isLocked()) { |
149 | - log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock", new RuntimeException("stacktrace")); | |
153 | + log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace")); | |
150 | 154 | } |
151 | 155 | consumerLock.lock(); |
152 | 156 | try { | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import com.google.common.util.concurrent.ListenableFuture; |
20 | 20 | import com.google.common.util.concurrent.SettableFuture; |
21 | 21 | import lombok.Builder; |
22 | 22 | import lombok.extern.slf4j.Slf4j; |
23 | +import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
23 | 24 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
24 | 25 | import org.thingsboard.server.queue.TbQueueAdmin; |
25 | 26 | import org.thingsboard.server.queue.TbQueueCallback; |
... | ... | @@ -77,7 +78,7 @@ public class DefaultTbQueueRequestTemplate<Request extends TbQueueMsg, Response |
77 | 78 | this.executor = executor; |
78 | 79 | } else { |
79 | 80 | internalExecutor = true; |
80 | - this.executor = Executors.newSingleThreadExecutor(); | |
81 | + this.executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-queue-request-template-" + responseTemplate.getTopic())); | |
81 | 82 | } |
82 | 83 | } |
83 | 84 | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.queue.common; |
17 | 17 | |
18 | 18 | import lombok.Builder; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | +import org.thingsboard.common.util.ThingsBoardThreadFactory; | |
20 | 21 | import org.thingsboard.server.common.msg.queue.TopicPartitionInfo; |
21 | 22 | import org.thingsboard.server.queue.TbQueueConsumer; |
22 | 23 | import org.thingsboard.server.queue.TbQueueHandler; |
... | ... | @@ -70,8 +71,8 @@ public class DefaultTbQueueResponseTemplate<Request extends TbQueueMsg, Response |
70 | 71 | this.requestTimeout = requestTimeout; |
71 | 72 | this.callbackExecutor = executor; |
72 | 73 | this.stats = stats; |
73 | - this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(); | |
74 | - this.loopExecutor = Executors.newSingleThreadExecutor(); | |
74 | + this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("tb-queue-response-template-timeout-" + requestTemplate.getTopic())); | |
75 | + this.loopExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("tb-queue-response-template-loop-" + requestTemplate.getTopic())); | |
75 | 76 | } |
76 | 77 | |
77 | 78 | @Override | ... | ... |
... | ... | @@ -388,16 +388,18 @@ message GetFirmwareRequestMsg { |
388 | 388 | int64 deviceIdLSB = 2; |
389 | 389 | int64 tenantIdMSB = 3; |
390 | 390 | int64 tenantIdLSB = 4; |
391 | + string type = 5; | |
391 | 392 | } |
392 | 393 | |
393 | 394 | message GetFirmwareResponseMsg { |
394 | 395 | ResponseStatus responseStatus = 1; |
395 | 396 | int64 firmwareIdMSB = 2; |
396 | 397 | int64 firmwareIdLSB = 3; |
397 | - string title = 4; | |
398 | - string version = 5; | |
399 | - string contentType = 6; | |
400 | - string fileName = 7; | |
398 | + string type = 4; | |
399 | + string title = 5; | |
400 | + string version = 6; | |
401 | + string contentType = 7; | |
402 | + string fileName = 8; | |
401 | 403 | } |
402 | 404 | |
403 | 405 | //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 { |
711 | 713 | int64 deviceIdLSB = 5; |
712 | 714 | int64 firmwareIdMSB = 6; |
713 | 715 | int64 firmwareIdLSB = 7; |
716 | + string type = 8; | |
714 | 717 | } | ... | ... |
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 |
43 | 43 | import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadConfiguration; |
44 | 44 | import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; |
45 | 45 | import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; |
46 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
46 | 47 | import org.thingsboard.server.common.data.security.DeviceTokenCredentials; |
47 | 48 | import org.thingsboard.server.common.msg.session.FeatureType; |
48 | 49 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
... | ... | @@ -122,6 +123,8 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
122 | 123 | processRequest(exchange, SessionMsgType.GET_ATTRIBUTES_REQUEST); |
123 | 124 | } else if (featureType.get() == FeatureType.FIRMWARE) { |
124 | 125 | processRequest(exchange, SessionMsgType.GET_FIRMWARE_REQUEST); |
126 | + } else if (featureType.get() == FeatureType.SOFTWARE) { | |
127 | + processRequest(exchange, SessionMsgType.GET_SOFTWARE_REQUEST); | |
125 | 128 | } else { |
126 | 129 | log.trace("Invalid feature type parameter"); |
127 | 130 | exchange.respond(CoAP.ResponseCode.BAD_REQUEST); |
... | ... | @@ -326,12 +329,10 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
326 | 329 | new CoapNoOpCallback(exchange)); |
327 | 330 | break; |
328 | 331 | case GET_FIRMWARE_REQUEST: |
329 | - TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() | |
330 | - .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
331 | - .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
332 | - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
333 | - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build(); | |
334 | - transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); | |
332 | + getFirmwareCallback(sessionInfo, exchange, FirmwareType.FIRMWARE); | |
333 | + break; | |
334 | + case GET_SOFTWARE_REQUEST: | |
335 | + getFirmwareCallback(sessionInfo, exchange, FirmwareType.SOFTWARE); | |
335 | 336 | break; |
336 | 337 | } |
337 | 338 | } catch (AdaptorException e) { |
... | ... | @@ -340,6 +341,16 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
340 | 341 | } |
341 | 342 | } |
342 | 343 | |
344 | + private void getFirmwareCallback(TransportProtos.SessionInfoProto sessionInfo, CoapExchange exchange, FirmwareType firmwareType) { | |
345 | + TransportProtos.GetFirmwareRequestMsg requestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() | |
346 | + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
347 | + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
348 | + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
349 | + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) | |
350 | + .setType(firmwareType.name()).build(); | |
351 | + transportContext.getTransportService().process(sessionInfo, requestMsg, new FirmwareCallback(exchange)); | |
352 | + } | |
353 | + | |
343 | 354 | private TransportProtos.SessionInfoProto lookupAsyncSessionInfo(String token) { |
344 | 355 | tokenToNotificationCounterMap.remove(token); |
345 | 356 | return tokenToSessionIdMap.remove(token); | ... | ... |
... | ... | @@ -34,6 +34,7 @@ import org.springframework.web.bind.annotation.RequestParam; |
34 | 34 | import org.springframework.web.bind.annotation.RestController; |
35 | 35 | import org.springframework.web.context.request.async.DeferredResult; |
36 | 36 | import org.thingsboard.server.common.data.DeviceTransportType; |
37 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
37 | 38 | import org.thingsboard.server.common.data.TbTransportService; |
38 | 39 | import org.thingsboard.server.common.data.id.DeviceId; |
39 | 40 | import org.thingsboard.server.common.transport.SessionMsgListener; |
... | ... | @@ -210,8 +211,29 @@ public class DeviceApiController implements TbTransportService { |
210 | 211 | public DeferredResult<ResponseEntity> getFirmware(@PathVariable("deviceToken") String deviceToken, |
211 | 212 | @RequestParam(value = "title") String title, |
212 | 213 | @RequestParam(value = "version") String version, |
213 | - @RequestParam(value = "chunkSize", required = false, defaultValue = "0") int chunkSize, | |
214 | + @RequestParam(value = "size", required = false, defaultValue = "0") int size, | |
214 | 215 | @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { |
216 | + return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.FIRMWARE); | |
217 | + } | |
218 | + | |
219 | + @RequestMapping(value = "/{deviceToken}/software", method = RequestMethod.GET) | |
220 | + public DeferredResult<ResponseEntity> getSoftware(@PathVariable("deviceToken") String deviceToken, | |
221 | + @RequestParam(value = "title") String title, | |
222 | + @RequestParam(value = "version") String version, | |
223 | + @RequestParam(value = "size", required = false, defaultValue = "0") int size, | |
224 | + @RequestParam(value = "chunk", required = false, defaultValue = "0") int chunk) { | |
225 | + return getFirmwareCallback(deviceToken, title, version, size, chunk, FirmwareType.SOFTWARE); | |
226 | + } | |
227 | + | |
228 | + @RequestMapping(value = "/provision", method = RequestMethod.POST) | |
229 | + public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) { | |
230 | + DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); | |
231 | + transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json), | |
232 | + new DeviceProvisionCallback(responseWriter)); | |
233 | + return responseWriter; | |
234 | + } | |
235 | + | |
236 | + private DeferredResult<ResponseEntity> getFirmwareCallback(String deviceToken, String title, String version, int size, int chunk, FirmwareType firmwareType) { | |
215 | 237 | DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); |
216 | 238 | transportContext.getTransportService().process(DeviceTransportType.DEFAULT, ValidateDeviceTokenRequestMsg.newBuilder().setToken(deviceToken).build(), |
217 | 239 | new DeviceAuthCallback(transportContext, responseWriter, sessionInfo -> { |
... | ... | @@ -219,20 +241,13 @@ public class DeviceApiController implements TbTransportService { |
219 | 241 | .setTenantIdMSB(sessionInfo.getTenantIdMSB()) |
220 | 242 | .setTenantIdLSB(sessionInfo.getTenantIdLSB()) |
221 | 243 | .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) |
222 | - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()).build(); | |
223 | - transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, chunkSize, chunk)); | |
244 | + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) | |
245 | + .setType(firmwareType.name()).build(); | |
246 | + transportContext.getTransportService().process(sessionInfo, requestMsg, new GetFirmwareCallback(responseWriter, title, version, size, chunk)); | |
224 | 247 | })); |
225 | 248 | return responseWriter; |
226 | 249 | } |
227 | 250 | |
228 | - @RequestMapping(value = "/provision", method = RequestMethod.POST) | |
229 | - public DeferredResult<ResponseEntity> provisionDevice(@RequestBody String json, HttpServletRequest httpRequest) { | |
230 | - DeferredResult<ResponseEntity> responseWriter = new DeferredResult<>(); | |
231 | - transportContext.getTransportService().process(JsonConverter.convertToProvisionRequestMsg(json), | |
232 | - new DeviceProvisionCallback(responseWriter)); | |
233 | - return responseWriter; | |
234 | - } | |
235 | - | |
236 | 251 | private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> { |
237 | 252 | private final TransportContext transportContext; |
238 | 253 | private final DeferredResult<ResponseEntity> responseWriter; | ... | ... |
... | ... | @@ -55,7 +55,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE |
55 | 55 | import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; |
56 | 56 | import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; |
57 | 57 | import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; |
58 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; | |
58 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; | |
59 | 59 | |
60 | 60 | @Slf4j |
61 | 61 | @Component |
... | ... | @@ -93,7 +93,6 @@ public class LwM2MTransportBootstrapServerConfiguration { |
93 | 93 | builder.setCoapConfig(getCoapConfig(bootstrapPortNoSec, bootstrapSecurePort)); |
94 | 94 | |
95 | 95 | /** Define model provider (Create Models )*/ |
96 | -// builder.setModel(new StaticModel(contextS.getLwM2MTransportConfigServer().getModelsValueCommon())); | |
97 | 96 | |
98 | 97 | /** Create credentials */ |
99 | 98 | this.setServerWithCredentials(builder); | ... | ... |
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.transport.lwm2m.server; | |
17 | + | |
18 | +import org.eclipse.californium.core.network.config.NetworkConfig; | |
19 | + | |
20 | +public class LwM2mNetworkConfig { | |
21 | + | |
22 | + public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) { | |
23 | + NetworkConfig coapConfig = new NetworkConfig(); | |
24 | + coapConfig.setInt(NetworkConfig.Keys.COAP_PORT,serverPortNoSec); | |
25 | + coapConfig.setInt(NetworkConfig.Keys.COAP_SECURE_PORT,serverSecurePort); | |
26 | + /** | |
27 | + * Example:Property for large packet: | |
28 | + * #NetworkConfig config = new NetworkConfig(); | |
29 | + * #config.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE,32); | |
30 | + * #config.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE,32); | |
31 | + * #config.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE,2048); | |
32 | + * #config.setInt(NetworkConfig.Keys.MAX_RETRANSMIT,3); | |
33 | + * #config.setInt(NetworkConfig.Keys.MAX_TRANSMIT_WAIT,120000); | |
34 | + */ | |
35 | + | |
36 | + /** | |
37 | + * Property to indicate if the response should always include the Block2 option \ | |
38 | + * when client request early blockwise negociation but the response can be sent on one packet. | |
39 | + * - value of false indicate that the server will respond without block2 option if no further blocks are required. | |
40 | + * - value of true indicate that the server will response with block2 option event if no further blocks are required. | |
41 | + * CoAP client will try to use block mode | |
42 | + * or adapt the block size when receiving a 4.13 Entity too large response code | |
43 | + */ | |
44 | + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_STRICT_BLOCK2_OPTION, true); | |
45 | + /*** | |
46 | + * Property to indicate if the response should always include the Block2 option \ | |
47 | + * when client request early blockwise negociation but the response can be sent on one packet. | |
48 | + * - value of false indicate that the server will respond without block2 option if no further blocks are required. | |
49 | + * - value of true indicate that the server will response with block2 option event if no further blocks are required. | |
50 | + */ | |
51 | + coapConfig.setBoolean(NetworkConfig.Keys.BLOCKWISE_ENTITY_TOO_LARGE_AUTO_FAILOVER, true); | |
52 | + | |
53 | + coapConfig.setInt(NetworkConfig.Keys.BLOCKWISE_STATUS_LIFETIME, 300000); | |
54 | + /** | |
55 | + * !!! REQUEST_ENTITY_TOO_LARGE CODE=4.13 | |
56 | + * The maximum size of a resource body (in bytes) that will be accepted | |
57 | + * as the payload of a POST/PUT or the response to a GET request in a | |
58 | + * transparent> blockwise transfer. | |
59 | + * This option serves as a safeguard against excessive memory | |
60 | + * consumption when many resources contain large bodies that cannot be | |
61 | + * transferred in a single CoAP message. This option has no impact on | |
62 | + * *manually* managed blockwise transfers in which the blocks are handled individually. | |
63 | + * Note that this option does not prevent local clients or resource | |
64 | + * implementations from sending large bodies as part of a request or response to a peer. | |
65 | + * The default value of this property is DEFAULT_MAX_RESOURCE_BODY_SIZE = 8192 | |
66 | + * A value of {@code 0} turns off transparent handling of blockwise transfers altogether. | |
67 | + */ | |
68 | +// coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 8192); | |
69 | + coapConfig.setInt(NetworkConfig.Keys.MAX_RESOURCE_BODY_SIZE, 16384); | |
70 | + /** | |
71 | + * The default DTLS response matcher. | |
72 | + * Supported values are STRICT, RELAXED, or PRINCIPAL. | |
73 | + * The default value is STRICT. | |
74 | + * Create new instance of udp endpoint context matcher. | |
75 | + * Params: | |
76 | + * checkAddress | |
77 | + * – true with address check, (STRICT, UDP) | |
78 | + * - false, without | |
79 | + */ | |
80 | + coapConfig.setString(NetworkConfig.Keys.RESPONSE_MATCHING, "STRICT"); | |
81 | + /** | |
82 | + * https://tools.ietf.org/html/rfc7959#section-2.9.3 | |
83 | + * The block size (number of bytes) to use when doing a blockwise transfer. \ | |
84 | + * This value serves as the upper limit for block size in blockwise transfers | |
85 | + */ | |
86 | + coapConfig.setInt(NetworkConfig.Keys.PREFERRED_BLOCK_SIZE, 512); | |
87 | + /** | |
88 | + * The maximum payload size (in bytes) that can be transferred in a | |
89 | + * single message, i.e. without requiring a blockwise transfer. | |
90 | + * NB: this value MUST be adapted to the maximum message size supported by the transport layer. | |
91 | + * In particular, this value cannot exceed the network's MTU if UDP is used as the transport protocol | |
92 | + * DEFAULT_VALUE = 1024 | |
93 | + */ | |
94 | + coapConfig.setInt(NetworkConfig.Keys.MAX_MESSAGE_SIZE, 512); | |
95 | + | |
96 | + coapConfig.setInt(NetworkConfig.Keys.MAX_RETRANSMIT, 4); | |
97 | + | |
98 | + return coapConfig; | |
99 | + } | |
100 | +} | ... | ... |
... | ... | @@ -26,6 +26,7 @@ import org.eclipse.leshan.server.registration.RegistrationUpdate; |
26 | 26 | |
27 | 27 | import java.util.Collection; |
28 | 28 | |
29 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; | |
29 | 30 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.convertPathFromObjectIdToIdVer; |
30 | 31 | |
31 | 32 | @Slf4j |
... | ... | @@ -85,17 +86,19 @@ public class LwM2mServerListener { |
85 | 86 | |
86 | 87 | @Override |
87 | 88 | public void cancelled(Observation observation) { |
88 | - log.info("Received notification cancelled from [{}] ", observation.getPath()); | |
89 | + String msg = String.format("%s: Cancel Observation %s.", LOG_LW2M_INFO, observation.getPath()); | |
90 | + service.sendLogsToThingsboard(msg, observation.getRegistrationId()); | |
91 | + log.trace(msg); | |
89 | 92 | } |
90 | 93 | |
91 | 94 | @Override |
92 | 95 | public void onResponse(Observation observation, Registration registration, ObserveResponse response) { |
93 | 96 | if (registration != null) { |
94 | 97 | try { |
95 | - service.onObservationResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), | |
98 | + service.onUpdateValueAfterReadResponse(registration, convertPathFromObjectIdToIdVer(observation.getPath().toString(), | |
96 | 99 | registration), response, null); |
97 | 100 | } catch (Exception e) { |
98 | - log.error("[{}] onResponse", e.toString()); | |
101 | + log.error("Observation/Read onResponse", e); | |
99 | 102 | |
100 | 103 | } |
101 | 104 | } |
... | ... | @@ -108,7 +111,10 @@ public class LwM2mServerListener { |
108 | 111 | |
109 | 112 | @Override |
110 | 113 | public void newObservation(Observation observation, Registration registration) { |
111 | - log.info("Received newObservation from [{}] endpoint [{}] ", observation.getPath(), registration.getEndpoint()); | |
114 | + String msg = String.format("%s: Successful start newObservation %s.", LOG_LW2M_INFO, | |
115 | + observation.getPath()); | |
116 | + service.sendLogsToThingsboard(msg, registration.getId()); | |
117 | + log.trace(msg); | |
112 | 118 | } |
113 | 119 | }; |
114 | 120 | ... | ... |
... | ... | @@ -25,7 +25,6 @@ import com.google.gson.JsonSyntaxException; |
25 | 25 | import com.google.gson.reflect.TypeToken; |
26 | 26 | import lombok.extern.slf4j.Slf4j; |
27 | 27 | import org.apache.commons.lang3.StringUtils; |
28 | -import org.eclipse.californium.core.network.config.NetworkConfig; | |
29 | 28 | import org.eclipse.leshan.core.attributes.Attribute; |
30 | 29 | import org.eclipse.leshan.core.attributes.AttributeSet; |
31 | 30 | import org.eclipse.leshan.core.model.ObjectModel; |
... | ... | @@ -40,7 +39,6 @@ import org.eclipse.leshan.core.node.codec.CodecException; |
40 | 39 | import org.eclipse.leshan.core.request.DownlinkRequest; |
41 | 40 | import org.eclipse.leshan.core.request.WriteAttributesRequest; |
42 | 41 | import org.eclipse.leshan.core.util.Hex; |
43 | -import org.eclipse.leshan.server.californium.LeshanServerBuilder; | |
44 | 42 | import org.eclipse.leshan.server.registration.Registration; |
45 | 43 | import org.nustaq.serialization.FSTConfiguration; |
46 | 44 | import org.thingsboard.server.common.data.DeviceProfile; |
... | ... | @@ -50,7 +48,6 @@ import org.thingsboard.server.common.transport.TransportServiceCallback; |
50 | 48 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; |
51 | 49 | import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile; |
52 | 50 | |
53 | -import java.io.File; | |
54 | 51 | import java.io.IOException; |
55 | 52 | import java.util.ArrayList; |
56 | 53 | import java.util.Arrays; |
... | ... | @@ -72,7 +69,6 @@ import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPA |
72 | 69 | @Slf4j |
73 | 70 | public class LwM2mTransportHandler { |
74 | 71 | |
75 | - // public static final String BASE_DEVICE_API_TOPIC = "v1/devices/me"; | |
76 | 72 | public static final String TRANSPORT_DEFAULT_LWM2M_VERSION = "1.0"; |
77 | 73 | public static final String CLIENT_LWM2M_SETTINGS = "clientLwM2mSettings"; |
78 | 74 | public static final String BOOTSTRAP = "bootstrap"; |
... | ... | @@ -85,19 +81,12 @@ public class LwM2mTransportHandler { |
85 | 81 | public static final String KEY_NAME = "keyName"; |
86 | 82 | public static final String OBSERVE_LWM2M = "observe"; |
87 | 83 | public static final String ATTRIBUTE_LWM2M = "attributeLwm2m"; |
88 | -// public static final String RESOURCE_VALUE = "resValue"; | |
89 | -// public static final String RESOURCE_TYPE = "resType"; | |
90 | 84 | |
91 | 85 | private static final String REQUEST = "/request"; |
92 | - // private static final String RESPONSE = "/response"; | |
93 | 86 | private static final String ATTRIBUTES = "/" + ATTRIBUTE; |
94 | 87 | public static final String TELEMETRIES = "/" + TELEMETRY; |
95 | - // public static final String ATTRIBUTES_RESPONSE = ATTRIBUTES + RESPONSE; | |
96 | 88 | public static final String ATTRIBUTES_REQUEST = ATTRIBUTES + REQUEST; |
97 | - // public static final String DEVICE_ATTRIBUTES_RESPONSE = ATTRIBUTES_RESPONSE + "/"; | |
98 | 89 | public static final String DEVICE_ATTRIBUTES_REQUEST = ATTRIBUTES_REQUEST + "/"; |
99 | -// public static final String DEVICE_ATTRIBUTES_TOPIC = BASE_DEVICE_API_TOPIC + ATTRIBUTES; | |
100 | -// public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + TELEMETRIES; | |
101 | 90 | |
102 | 91 | public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms |
103 | 92 | |
... | ... | @@ -112,6 +101,11 @@ public class LwM2mTransportHandler { |
112 | 101 | |
113 | 102 | public static final String CLIENT_NOT_AUTHORIZED = "Client not authorized"; |
114 | 103 | |
104 | + public static final Integer FR_OBJECT_ID = 5; | |
105 | + public static final Integer FR_RESOURCE_VER_ID = 7; | |
106 | + public static final String FR_PATH_RESOURCE_VER_ID = LWM2M_SEPARATOR_PATH + FR_OBJECT_ID + LWM2M_SEPARATOR_PATH | |
107 | + + "0" + LWM2M_SEPARATOR_PATH + FR_RESOURCE_VER_ID; | |
108 | + | |
115 | 109 | public enum LwM2mTypeServer { |
116 | 110 | BOOTSTRAP(0, "bootstrap"), |
117 | 111 | CLIENT(1, "client"); |
... | ... | @@ -168,6 +162,8 @@ public class LwM2mTransportHandler { |
168 | 162 | WRITE_ATTRIBUTES(8, "WriteAttributes"), |
169 | 163 | DELETE(9, "Delete"); |
170 | 164 | |
165 | +// READ_INFO_FW(10, "ReadInfoFirmware"); | |
166 | + | |
171 | 167 | public int code; |
172 | 168 | public String type; |
173 | 169 | |
... | ... | @@ -190,21 +186,6 @@ public class LwM2mTransportHandler { |
190 | 186 | public static final String SERVICE_CHANNEL = "SERVICE"; |
191 | 187 | public static final String RESPONSE_CHANNEL = "RESP"; |
192 | 188 | |
193 | - public static NetworkConfig getCoapConfig(Integer serverPortNoSec, Integer serverSecurePort) { | |
194 | - NetworkConfig coapConfig; | |
195 | - File configFile = new File(NetworkConfig.DEFAULT_FILE_NAME); | |
196 | - if (configFile.isFile()) { | |
197 | - coapConfig = new NetworkConfig(); | |
198 | - coapConfig.load(configFile); | |
199 | - } else { | |
200 | - coapConfig = LeshanServerBuilder.createDefaultNetworkConfig(); | |
201 | - coapConfig.store(configFile); | |
202 | - } | |
203 | - coapConfig.setString("COAP_PORT", Integer.toString(serverPortNoSec)); | |
204 | - coapConfig.setString("COAP_SECURE_PORT", Integer.toString(serverSecurePort)); | |
205 | - return coapConfig; | |
206 | - } | |
207 | - | |
208 | 189 | public static boolean equalsResourceValue(Object valueOld, Object valueNew, ResourceModel.Type type, LwM2mPath resourcePath) throws CodecException { |
209 | 190 | switch (type) { |
210 | 191 | case BOOLEAN: | ... | ... |
... | ... | @@ -67,6 +67,7 @@ import static org.eclipse.californium.core.coap.CoAP.ResponseCode.CONTENT; |
67 | 67 | import static org.eclipse.leshan.core.ResponseCode.BAD_REQUEST; |
68 | 68 | import static org.eclipse.leshan.core.ResponseCode.NOT_FOUND; |
69 | 69 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEFAULT_TIMEOUT; |
70 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID; | |
70 | 71 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; |
71 | 72 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; |
72 | 73 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; |
... | ... | @@ -125,7 +126,6 @@ public class LwM2mTransportRequest { |
125 | 126 | public void sendAllRequest(Registration registration, String targetIdVer, LwM2mTypeOper typeOper, |
126 | 127 | String contentFormatName, Object params, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { |
127 | 128 | try { |
128 | - | |
129 | 129 | String target = convertPathFromIdVerToObjectId(targetIdVer); |
130 | 130 | DownlinkRequest request = null; |
131 | 131 | ContentFormat contentFormat = contentFormatName != null ? ContentFormat.fromName(contentFormatName.toUpperCase()) : ContentFormat.DEFAULT; |
... | ... | @@ -145,11 +145,11 @@ public class LwM2mTransportRequest { |
145 | 145 | break; |
146 | 146 | case OBSERVE: |
147 | 147 | if (resultIds.isResource()) { |
148 | - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); | |
148 | + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId(), resultIds.getResourceId()); | |
149 | 149 | } else if (resultIds.isObjectInstance()) { |
150 | - request = new ObserveRequest(resultIds.getObjectId(), resultIds.getObjectInstanceId()); | |
150 | + request = new ObserveRequest(contentFormat, resultIds.getObjectId(), resultIds.getObjectInstanceId()); | |
151 | 151 | } else if (resultIds.getObjectId() >= 0) { |
152 | - request = new ObserveRequest(resultIds.getObjectId()); | |
152 | + request = new ObserveRequest(contentFormat, resultIds.getObjectId()); | |
153 | 153 | } |
154 | 154 | break; |
155 | 155 | case OBSERVE_CANCEL: |
... | ... | @@ -171,8 +171,6 @@ public class LwM2mTransportRequest { |
171 | 171 | break; |
172 | 172 | case WRITE_REPLACE: |
173 | 173 | // Request to write a <b>String Single-Instance Resource</b> using the TLV content format. |
174 | -// resource = lwM2MClient.getResourceModel(targetIdVer); | |
175 | -// if (contentFormat.equals(ContentFormat.TLV) && !resource.multiple) { | |
176 | 174 | resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() |
177 | 175 | .getModelProvider()); |
178 | 176 | if (contentFormat.equals(ContentFormat.TLV)) { |
... | ... | @@ -181,7 +179,6 @@ public class LwM2mTransportRequest { |
181 | 179 | registration, rpcRequest); |
182 | 180 | } |
183 | 181 | // Mode.REPLACE && Request to write a <b>String Single-Instance Resource</b> using the given content format (TEXT, TLV, JSON) |
184 | -// else if (!contentFormat.equals(ContentFormat.TLV) && !resource.multiple) { | |
185 | 182 | else if (!contentFormat.equals(ContentFormat.TLV)) { |
186 | 183 | request = this.getWriteRequestSingleResource(contentFormat, resultIds.getObjectId(), |
187 | 184 | resultIds.getObjectInstanceId(), resultIds.getResourceId(), params, resourceModel.type, |
... | ... | @@ -215,13 +212,16 @@ public class LwM2mTransportRequest { |
215 | 212 | long finalTimeoutInMs = timeoutInMs; |
216 | 213 | lwM2MClient.getQueuedRequests().add(() -> sendRequest(registration, lwM2MClient, finalRequest, finalTimeoutInMs, rpcRequest)); |
217 | 214 | } catch (Exception e) { |
218 | - log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper, e); | |
215 | + log.error("[{}] [{}] [{}] Failed to send downlink.", registration.getEndpoint(), targetIdVer, typeOper.name(), e); | |
216 | + } | |
217 | + } else if (OBSERVE_CANCEL == typeOper) { | |
218 | + log.trace("[{}], [{}] - [{}] SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); | |
219 | + if (rpcRequest != null) { | |
220 | + rpcRequest.setInfoMsg(null); | |
221 | + serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null); | |
219 | 222 | } |
220 | - } else if (OBSERVE_CANCEL == typeOper && rpcRequest != null) { | |
221 | - rpcRequest.setInfoMsg(null); | |
222 | - serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, null); | |
223 | 223 | } else { |
224 | - log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper, targetIdVer); | |
224 | + log.error("[{}], [{}] - [{}] error SendRequest", registration.getEndpoint(), typeOper.name(), targetIdVer); | |
225 | 225 | if (rpcRequest != null) { |
226 | 226 | String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null"; |
227 | 227 | serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR); |
... | ... | @@ -236,8 +236,8 @@ public class LwM2mTransportRequest { |
236 | 236 | Set<String> observationPaths = observations.stream().map(observation -> observation.getPath().toString()).collect(Collectors.toUnmodifiableSet()); |
237 | 237 | String msg = String.format("%s: type operation %s observation paths - %s", LOG_LW2M_INFO, |
238 | 238 | OBSERVE_READ_ALL.type, observationPaths); |
239 | - serviceImpl.sendLogsToThingsboard(msg, registration); | |
240 | - log.info("[{}], [{}]", registration.getEndpoint(), msg); | |
239 | + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); | |
240 | + log.trace("[{}] [{}], [{}]", typeOper.name(), registration.getEndpoint(), msg); | |
241 | 241 | if (rpcRequest != null) { |
242 | 242 | String valueMsg = String.format("Observation paths - %s", observationPaths); |
243 | 243 | serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE); |
... | ... | @@ -246,7 +246,7 @@ public class LwM2mTransportRequest { |
246 | 246 | } catch (Exception e) { |
247 | 247 | String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR, |
248 | 248 | typeOper.name(), e.getMessage()); |
249 | - serviceImpl.sendLogsToThingsboard(msg, registration); | |
249 | + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); | |
250 | 250 | throw new Exception(e); |
251 | 251 | } |
252 | 252 | } |
... | ... | @@ -261,45 +261,53 @@ public class LwM2mTransportRequest { |
261 | 261 | private void sendRequest(Registration registration, LwM2mClient lwM2MClient, DownlinkRequest request, long timeoutInMs, Lwm2mClientRpcRequest rpcRequest) { |
262 | 262 | leshanServer.send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> { |
263 | 263 | if (!lwM2MClient.isInit()) { |
264 | - lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); | |
264 | + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); | |
265 | 265 | } |
266 | 266 | if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) { |
267 | 267 | this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest); |
268 | - if (request instanceof WriteRequest && ((WriteRequest) request).isReplaceRequest()) { | |
269 | - LwM2mNode node = ((WriteRequest) request).getNode(); | |
270 | - Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(), | |
271 | - ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath()); | |
272 | - String msg = String.format("%s: sendRequest Replace: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s SendRequest to Client", | |
273 | - LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), | |
274 | - response.getCode().getName(), request.getPath().toString(), value); | |
275 | - serviceImpl.sendLogsToThingsboard(msg, registration); | |
276 | - log.info("[{}] [{}] - [{}] [{}] Update SendRequest[{}]", registration.getEndpoint(), | |
277 | - ((Response) response.getCoapResponse()).getCode(), response.getCode(), | |
278 | - request.getPath().toString(), value); | |
279 | - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO); | |
280 | - } | |
281 | 268 | } else { |
282 | - String msg = String.format("%s: sendRequest: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s SendRequest to Client", LOG_LW2M_ERROR, | |
269 | + String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(), | |
283 | 270 | ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString()); |
284 | - serviceImpl.sendLogsToThingsboard(msg, registration); | |
285 | - log.error("[{}], [{}] - [{}] [{}] error SendRequest", registration.getEndpoint(), ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); | |
271 | + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); | |
272 | + log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(), | |
273 | + ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString()); | |
274 | + if (!lwM2MClient.isInit()) { | |
275 | + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); | |
276 | + } | |
286 | 277 | if (rpcRequest != null) { |
287 | 278 | serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR); |
288 | 279 | } |
280 | + /** Not Found | |
281 | + * set setClient_fw_version = empty | |
282 | + **/ | |
283 | + if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) { | |
284 | + lwM2MClient.setUpdateFw(false); | |
285 | + lwM2MClient.getFrUpdate().setClientFwVersion(""); | |
286 | + log.warn("updateFirmwareClient1"); | |
287 | + serviceImpl.updateFirmwareClient(lwM2MClient); | |
288 | + } | |
289 | 289 | } |
290 | 290 | }, e -> { |
291 | + /** version == null | |
292 | + * set setClient_fw_version = empty | |
293 | + **/ | |
294 | + if (FR_PATH_RESOURCE_VER_ID.equals(request.getPath().toString()) && lwM2MClient.isUpdateFw()) { | |
295 | + lwM2MClient.setUpdateFw(false); | |
296 | + lwM2MClient.getFrUpdate().setClientFwVersion(""); | |
297 | + log.warn("updateFirmwareClient2"); | |
298 | + serviceImpl.updateFirmwareClient(lwM2MClient); | |
299 | + } | |
291 | 300 | if (!lwM2MClient.isInit()) { |
292 | - lwM2MClient.initValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); | |
301 | + lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration)); | |
293 | 302 | } |
294 | - String msg = String.format("%s: sendRequest: Resource path - %s msg error - %s SendRequest to Client", | |
295 | - LOG_LW2M_ERROR, request.getPath().toString(), e.getMessage()); | |
296 | - serviceImpl.sendLogsToThingsboard(msg, registration); | |
297 | - log.error("[{}] - [{}] error SendRequest", request.getPath().toString(), e.toString()); | |
303 | + String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s", | |
304 | + LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage()); | |
305 | + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); | |
306 | + log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString()); | |
298 | 307 | if (rpcRequest != null) { |
299 | 308 | serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR); |
300 | 309 | } |
301 | 310 | }); |
302 | - | |
303 | 311 | } |
304 | 312 | |
305 | 313 | private WriteRequest getWriteRequestSingleResource(ContentFormat contentFormat, Integer objectId, Integer instanceId, |
... | ... | @@ -323,7 +331,9 @@ public class LwM2mTransportRequest { |
323 | 331 | Date date = new Date(Long.decode(value.toString())); |
324 | 332 | return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, date) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, date); |
325 | 333 | case OPAQUE: // byte[] value, base64 |
326 | - return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())) : new WriteRequest(contentFormat, objectId, instanceId, resourceId, Hex.decodeHex(value.toString().toCharArray())); | |
334 | + byte[] valueRequest = value instanceof byte[] ? (byte[]) value : Hex.decodeHex(value.toString().toCharArray()); | |
335 | + return (contentFormat == null) ? new WriteRequest(objectId, instanceId, resourceId, valueRequest) : | |
336 | + new WriteRequest(contentFormat, objectId, instanceId, resourceId, valueRequest); | |
327 | 337 | default: |
328 | 338 | } |
329 | 339 | } |
... | ... | @@ -337,7 +347,7 @@ public class LwM2mTransportRequest { |
337 | 347 | String patn = "/" + objectId + "/" + instanceId + "/" + resourceId; |
338 | 348 | String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client", |
339 | 349 | patn, type, value, e.toString()); |
340 | - serviceImpl.sendLogsToThingsboard(msg, registration); | |
350 | + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); | |
341 | 351 | log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString()); |
342 | 352 | if (rpcRequest != null) { |
343 | 353 | String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value); |
... | ... | @@ -369,7 +379,7 @@ public class LwM2mTransportRequest { |
369 | 379 | DownlinkRequest request, Lwm2mClientRpcRequest rpcRequest) { |
370 | 380 | String pathIdVer = convertPathFromObjectIdToIdVer(path, registration); |
371 | 381 | if (response instanceof ReadResponse) { |
372 | - serviceImpl.onObservationResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); | |
382 | + serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest); | |
373 | 383 | } else if (response instanceof CancelObservationResponse) { |
374 | 384 | log.info("[{}] Path [{}] CancelObservationResponse 3_Send", pathIdVer, response); |
375 | 385 | |
... | ... | @@ -389,14 +399,33 @@ public class LwM2mTransportRequest { |
389 | 399 | } else if (response instanceof WriteAttributesResponse) { |
390 | 400 | log.info("[{}] Path [{}] WriteAttributesResponse 8_Send", pathIdVer, response); |
391 | 401 | } else if (response instanceof WriteResponse) { |
392 | - log.info("[{}] Path [{}] WriteAttributesResponse 9_Send", pathIdVer, response); | |
402 | + log.info("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response); | |
403 | + this.infoWriteResponse(registration, response, request); | |
393 | 404 | serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request); |
394 | 405 | } |
395 | - if (rpcRequest != null && (response instanceof ExecuteResponse | |
396 | - || response instanceof WriteAttributesResponse | |
397 | - || response instanceof DeleteResponse)) { | |
398 | - rpcRequest.setInfoMsg(null); | |
399 | - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null); | |
406 | + if (rpcRequest != null) { | |
407 | + if (response instanceof ExecuteResponse | |
408 | + || response instanceof WriteAttributesResponse | |
409 | + || response instanceof DeleteResponse) { | |
410 | + rpcRequest.setInfoMsg(null); | |
411 | + serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null); | |
412 | + } else if (response instanceof WriteResponse) { | |
413 | + serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO); | |
414 | + } | |
400 | 415 | } |
401 | 416 | } |
417 | + | |
418 | + private void infoWriteResponse(Registration registration, LwM2mResponse response, | |
419 | + DownlinkRequest request) { | |
420 | + LwM2mNode node = ((WriteRequest) request).getNode(); | |
421 | + Object value = this.converter.convertValue(((LwM2mSingleResource) node).getValue(), | |
422 | + ((LwM2mSingleResource) node).getType(), ResourceModel.Type.STRING, request.getPath()); | |
423 | + String msg = String.format("%s: Update finished successfully: CoapCde - %s Lwm2m code - %d name - %s Resource path - %s value - %s", | |
424 | + LOG_LW2M_INFO, ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), | |
425 | + response.getCode().getName(), request.getPath().toString(), value); | |
426 | + serviceImpl.sendLogsToThingsboard(msg, registration.getId()); | |
427 | + log.warn("[{}] [{}] [{}] - [{}] [{}] Update finished successfully: [{}]", request.getClass().getName().toString(), registration.getEndpoint(), | |
428 | + ((Response) response.getCoapResponse()).getCode(), response.getCode(), | |
429 | + request.getPath().toString(), value); | |
430 | + } | |
402 | 431 | } | ... | ... |
... | ... | @@ -16,6 +16,8 @@ |
16 | 16 | package org.thingsboard.server.transport.lwm2m.server; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | +import org.eclipse.californium.core.network.config.NetworkConfig; | |
20 | +import org.eclipse.californium.core.network.stack.BlockwiseLayer; | |
19 | 21 | import org.eclipse.californium.scandium.config.DtlsConnectorConfig; |
20 | 22 | import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder; |
21 | 23 | import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder; |
... | ... | @@ -57,7 +59,7 @@ import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE |
57 | 59 | import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8; |
58 | 60 | import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256; |
59 | 61 | import static org.eclipse.californium.scandium.dtls.cipher.CipherSuite.TLS_PSK_WITH_AES_128_CCM_8; |
60 | -import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.getCoapConfig; | |
62 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.getCoapConfig; | |
61 | 63 | |
62 | 64 | @Slf4j |
63 | 65 | @Component |
... | ... | @@ -92,7 +94,10 @@ public class LwM2mTransportServerConfiguration { |
92 | 94 | /** Use a magic converter to support bad type send by the UI. */ |
93 | 95 | builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance())); |
94 | 96 | |
97 | + | |
95 | 98 | /** Create CoAP Config */ |
99 | + NetworkConfig networkConfig = getCoapConfig(serverPortNoSec, serverSecurePort); | |
100 | + BlockwiseLayer blockwiseLayer = new BlockwiseLayer(networkConfig); | |
96 | 101 | builder.setCoapConfig(getCoapConfig(serverPortNoSec, serverSecurePort)); |
97 | 102 | |
98 | 103 | /** Define model provider (Create Models )*/ |
... | ... | @@ -110,6 +115,7 @@ public class LwM2mTransportServerConfiguration { |
110 | 115 | |
111 | 116 | /** Create DTLS Config */ |
112 | 117 | DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder(); |
118 | + dtlsConfig.setServerOnly(true); | |
113 | 119 | dtlsConfig.setRecommendedSupportedGroupsOnly(this.context.getLwM2MTransportConfigServer().isRecommendedSupportedGroups()); |
114 | 120 | dtlsConfig.setRecommendedCipherSuitesOnly(this.context.getLwM2MTransportConfigServer().isRecommendedCiphers()); |
115 | 121 | if (this.pskMode) { | ... | ... |
... | ... | @@ -39,7 +39,7 @@ public interface LwM2mTransportService extends TbTransportService { |
39 | 39 | |
40 | 40 | void setCancelObservations(Registration registration); |
41 | 41 | |
42 | - void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest); | |
42 | + void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest); | |
43 | 43 | |
44 | 44 | void onAttributeUpdate(TransportProtos.AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo); |
45 | 45 | |
... | ... | @@ -60,6 +60,4 @@ public interface LwM2mTransportService extends TbTransportService { |
60 | 60 | void doTrigger(Registration registration, String path); |
61 | 61 | |
62 | 62 | void doDisconnect(TransportProtos.SessionInfoProto sessionInfo); |
63 | - | |
64 | - | |
65 | 63 | } | ... | ... |
... | ... | @@ -38,10 +38,15 @@ import org.eclipse.leshan.server.registration.Registration; |
38 | 38 | import org.springframework.context.annotation.Lazy; |
39 | 39 | import org.springframework.stereotype.Service; |
40 | 40 | import org.thingsboard.common.util.JacksonUtil; |
41 | +import org.thingsboard.server.cache.firmware.FirmwareDataCache; | |
41 | 42 | import org.thingsboard.server.common.data.Device; |
42 | 43 | import org.thingsboard.server.common.data.DeviceProfile; |
43 | -import org.thingsboard.server.common.data.DeviceTransportType; | |
44 | +import org.thingsboard.server.common.data.firmware.FirmwareKey; | |
45 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
46 | +import org.thingsboard.server.common.data.firmware.FirmwareUtil; | |
47 | +import org.thingsboard.server.common.data.id.FirmwareId; | |
44 | 48 | import org.thingsboard.server.common.transport.TransportService; |
49 | +import org.thingsboard.server.common.transport.TransportServiceCallback; | |
45 | 50 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
46 | 51 | import org.thingsboard.server.common.transport.service.DefaultTransportService; |
47 | 52 | import org.thingsboard.server.gen.transport.TransportProtos; |
... | ... | @@ -77,9 +82,11 @@ import java.util.stream.Collectors; |
77 | 82 | |
78 | 83 | import static org.eclipse.californium.core.coap.CoAP.ResponseCode.BAD_REQUEST; |
79 | 84 | import static org.eclipse.leshan.core.attributes.Attribute.OBJECT_VERSION; |
85 | +import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_KEY; | |
80 | 86 | import static org.thingsboard.server.common.data.lwm2m.LwM2mConstants.LWM2M_SEPARATOR_PATH; |
81 | 87 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.CLIENT_NOT_AUTHORIZED; |
82 | 88 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.DEVICE_ATTRIBUTES_REQUEST; |
89 | +import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.FR_PATH_RESOURCE_VER_ID; | |
83 | 90 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_ERROR; |
84 | 91 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_INFO; |
85 | 92 | import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportHandler.LOG_LW2M_VALUE; |
... | ... | @@ -110,6 +117,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
110 | 117 | private ExecutorService executorUpdateRegistered; |
111 | 118 | private ExecutorService executorUnRegistered; |
112 | 119 | private LwM2mValueConverterImpl converter; |
120 | + private FirmwareDataCache firmwareDataCache; | |
121 | + | |
113 | 122 | |
114 | 123 | private final TransportService transportService; |
115 | 124 | |
... | ... | @@ -121,12 +130,15 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
121 | 130 | |
122 | 131 | private final LwM2mTransportRequest lwM2mTransportRequest; |
123 | 132 | |
124 | - public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, @Lazy LwM2mTransportRequest lwM2mTransportRequest) { | |
133 | + public LwM2mTransportServiceImpl(TransportService transportService, LwM2mTransportContextServer lwM2mTransportContextServer, | |
134 | + LwM2mClientContext lwM2mClientContext, LeshanServer leshanServer, | |
135 | + @Lazy LwM2mTransportRequest lwM2mTransportRequest, FirmwareDataCache firmwareDataCache) { | |
125 | 136 | this.transportService = transportService; |
126 | 137 | this.lwM2mTransportContextServer = lwM2mTransportContextServer; |
127 | 138 | this.lwM2mClientContext = lwM2mClientContext; |
128 | 139 | this.leshanServer = leshanServer; |
129 | 140 | this.lwM2mTransportRequest = lwM2mTransportRequest; |
141 | + this.firmwareDataCache = firmwareDataCache; | |
130 | 142 | } |
131 | 143 | |
132 | 144 | @PostConstruct |
... | ... | @@ -168,8 +180,9 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
168 | 180 | transportService.process(sessionInfo, DefaultTransportService.getSessionEventMsg(SessionEvent.OPEN), null); |
169 | 181 | transportService.process(sessionInfo, TransportProtos.SubscribeToAttributeUpdatesMsg.newBuilder().build(), null); |
170 | 182 | transportService.process(sessionInfo, TransportProtos.SubscribeToRPCMsg.newBuilder().build(), null); |
183 | + this.getInfoFirmwareUpdate(lwM2MClient); | |
171 | 184 | this.initLwM2mFromClientValue(registration, lwM2MClient); |
172 | - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration); | |
185 | + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client create after Registration", registration.getId()); | |
173 | 186 | } else { |
174 | 187 | log.error("Client: [{}] onRegistered [{}] name [{}] sessionInfo ", registration.getId(), registration.getEndpoint(), null); |
175 | 188 | } |
... | ... | @@ -224,7 +237,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
224 | 237 | executorUnRegistered.submit(() -> { |
225 | 238 | try { |
226 | 239 | this.setCancelObservations(registration); |
227 | - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration); | |
240 | + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId()); | |
228 | 241 | this.closeClientSession(registration); |
229 | 242 | } catch (Throwable t) { |
230 | 243 | log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t); |
... | ... | @@ -257,7 +270,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
257 | 270 | @Override |
258 | 271 | public void onSleepingDev(Registration registration) { |
259 | 272 | log.info("[{}] [{}] Received endpoint Sleeping version event", registration.getId(), registration.getEndpoint()); |
260 | - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration); | |
273 | + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is sleeping!", registration.getId()); | |
261 | 274 | |
262 | 275 | //TODO: associate endpointId with device information. |
263 | 276 | } |
... | ... | @@ -280,7 +293,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
280 | 293 | * @param response - observe |
281 | 294 | */ |
282 | 295 | @Override |
283 | - public void onObservationResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { | |
296 | + public void onUpdateValueAfterReadResponse(Registration registration, String path, ReadResponse response, Lwm2mClientRpcRequest rpcRequest) { | |
284 | 297 | if (response.getContent() != null) { |
285 | 298 | if (response.getContent() instanceof LwM2mObject) { |
286 | 299 | LwM2mObject lwM2mObject = (LwM2mObject) response.getContent(); |
... | ... | @@ -304,20 +317,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
304 | 317 | |
305 | 318 | /** |
306 | 319 | * Update - send request in change value resources in Client |
307 | - * Path to resources from profile equal keyName or from ModelObject equal name | |
308 | - * Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) | |
309 | - * Delete - nothing * | |
320 | + * 1. FirmwareUpdate: | |
321 | + * - If msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 | |
322 | + * 2. Shared Other AttributeUpdate | |
323 | + * -- Path to resources from profile equal keyName or from ModelObject equal name | |
324 | + * -- Only for resources: isWritable && isPresent as attribute in profile -> LwM2MClientProfile (format: CamelCase) | |
325 | + * 3. Delete - nothing | |
310 | 326 | * |
311 | 327 | * @param msg - |
312 | 328 | */ |
313 | 329 | @Override |
314 | 330 | public void onAttributeUpdate(AttributeUpdateNotificationMsg msg, TransportProtos.SessionInfoProto sessionInfo) { |
331 | + LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); | |
315 | 332 | if (msg.getSharedUpdatedCount() > 0) { |
316 | 333 | msg.getSharedUpdatedList().forEach(tsKvProto -> { |
317 | 334 | String pathName = tsKvProto.getKv().getKey(); |
318 | 335 | String pathIdVer = this.getPresentPathIntoProfile(sessionInfo, pathName); |
319 | 336 | Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); |
320 | - LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClient(new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB())); | |
337 | + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) { | |
338 | + this.getInfoFirmwareUpdate(lwM2MClient); | |
339 | + } | |
321 | 340 | if (pathIdVer != null) { |
322 | 341 | ResourceModel resourceModel = lwM2MClient.getResourceModel(pathIdVer, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() |
323 | 342 | .getModelProvider()); |
... | ... | @@ -327,19 +346,26 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
327 | 346 | log.error("Resource path - [{}] value - [{}] is not Writable and cannot be updated", pathIdVer, valueNew); |
328 | 347 | String logMsg = String.format("%s: attributeUpdate: Resource path - %s value - %s is not Writable and cannot be updated", |
329 | 348 | LOG_LW2M_ERROR, pathIdVer, valueNew); |
330 | - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); | |
349 | + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); | |
331 | 350 | } |
332 | 351 | } else { |
333 | 352 | log.error("Resource name name - [{}] value - [{}] is not present as attribute/telemetry in profile and cannot be updated", pathName, valueNew); |
334 | 353 | String logMsg = String.format("%s: attributeUpdate: attribute name - %s value - %s is not present as attribute in profile and cannot be updated", |
335 | 354 | LOG_LW2M_ERROR, pathName, valueNew); |
336 | - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); | |
355 | + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); | |
337 | 356 | } |
357 | + | |
338 | 358 | }); |
339 | 359 | } else if (msg.getSharedDeletedCount() > 0) { |
360 | + msg.getSharedUpdatedList().forEach(tsKvProto -> { | |
361 | + String pathName = tsKvProto.getKv().getKey(); | |
362 | + Object valueNew = this.lwM2mTransportContextServer.getValueFromKvProto(tsKvProto.getKv()); | |
363 | + if (FirmwareUtil.getAttributeKey(FirmwareType.FIRMWARE, FirmwareKey.VERSION).equals(pathName) && !valueNew.equals(lwM2MClient.getFrUpdate().getCurrentFwVersion())) { | |
364 | + lwM2MClient.getFrUpdate().setCurrentFwVersion((String) valueNew); | |
365 | + } | |
366 | + }); | |
340 | 367 | log.info("[{}] delete [{}] onAttributeUpdate", msg.getSharedDeletedList(), sessionInfo); |
341 | 368 | } |
342 | - | |
343 | 369 | } |
344 | 370 | |
345 | 371 | /** |
... | ... | @@ -455,23 +481,21 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
455 | 481 | } |
456 | 482 | if (rpcRequest.has(lwm2mClientRpcRequest.paramsKey) && rpcRequest.get(lwm2mClientRpcRequest.paramsKey).isJsonObject()) { |
457 | 483 | lwm2mClientRpcRequest.setParams(new Gson().fromJson(rpcRequest.get(lwm2mClientRpcRequest.paramsKey) |
458 | - .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() { | |
459 | - }.getType())); | |
484 | + .getAsJsonObject().toString(), new TypeToken<ConcurrentHashMap<String, Object>>() { | |
485 | + }.getType())); | |
460 | 486 | } |
461 | 487 | lwm2mClientRpcRequest.setSessionInfo(sessionInfo); |
462 | 488 | if (OBSERVE_READ_ALL != lwm2mClientRpcRequest.getTypeOper() && lwm2mClientRpcRequest.getTargetIdVer() == null) { |
463 | 489 | lwm2mClientRpcRequest.setErrorMsg(lwm2mClientRpcRequest.targetIdVerKey + " and " + |
464 | 490 | lwm2mClientRpcRequest.keyNameKey + " is null or bad format"); |
465 | - } | |
466 | - else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper() | |
491 | + } else if ((EXECUTE == lwm2mClientRpcRequest.getTypeOper() | |
467 | 492 | || WRITE_REPLACE == lwm2mClientRpcRequest.getTypeOper()) |
468 | - && lwm2mClientRpcRequest.getTargetIdVer() !=null | |
493 | + && lwm2mClientRpcRequest.getTargetIdVer() != null | |
469 | 494 | && !(new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResource() |
470 | 495 | || new LwM2mPath(convertPathFromIdVerToObjectId(lwm2mClientRpcRequest.getTargetIdVer())).isResourceInstance())) { |
471 | - lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey | |
496 | + lwm2mClientRpcRequest.setErrorMsg("Invalid parameter " + lwm2mClientRpcRequest.targetIdVerKey | |
472 | 497 | + ". Only Resource or ResourceInstance can be this operation"); |
473 | - } | |
474 | - else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()){ | |
498 | + } else if (WRITE_UPDATE == lwm2mClientRpcRequest.getTypeOper()) { | |
475 | 499 | lwm2mClientRpcRequest.setErrorMsg("Procedures In Development..."); |
476 | 500 | } |
477 | 501 | } else { |
... | ... | @@ -483,23 +507,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
483 | 507 | return lwm2mClientRpcRequest; |
484 | 508 | } |
485 | 509 | |
486 | - public void sentRpcRequest (Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { | |
510 | + public void sentRpcRequest(Lwm2mClientRpcRequest rpcRequest, String requestCode, String msg, String typeMsg) { | |
487 | 511 | rpcRequest.setResponseCode(requestCode); |
488 | - if (LOG_LW2M_ERROR.equals(typeMsg)) { | |
489 | - rpcRequest.setInfoMsg(null); | |
490 | - rpcRequest.setValueMsg(null); | |
491 | - if (rpcRequest.getErrorMsg() == null) { | |
492 | - msg = msg.isEmpty() ? null : msg; | |
493 | - rpcRequest.setErrorMsg(msg); | |
494 | - } | |
495 | - } else if (LOG_LW2M_INFO.equals(typeMsg)) { | |
496 | - if (rpcRequest.getInfoMsg() == null) { | |
497 | - rpcRequest.setInfoMsg(msg); | |
498 | - } | |
499 | - } else if (LOG_LW2M_VALUE.equals(typeMsg)) { | |
500 | - if (rpcRequest.getValueMsg() == null) { | |
501 | - rpcRequest.setValueMsg(msg); | |
502 | - } | |
512 | + if (LOG_LW2M_ERROR.equals(typeMsg)) { | |
513 | + rpcRequest.setInfoMsg(null); | |
514 | + rpcRequest.setValueMsg(null); | |
515 | + if (rpcRequest.getErrorMsg() == null) { | |
516 | + msg = msg.isEmpty() ? null : msg; | |
517 | + rpcRequest.setErrorMsg(msg); | |
518 | + } | |
519 | + } else if (LOG_LW2M_INFO.equals(typeMsg)) { | |
520 | + if (rpcRequest.getInfoMsg() == null) { | |
521 | + rpcRequest.setInfoMsg(msg); | |
522 | + } | |
523 | + } else if (LOG_LW2M_VALUE.equals(typeMsg)) { | |
524 | + if (rpcRequest.getValueMsg() == null) { | |
525 | + rpcRequest.setValueMsg(msg); | |
526 | + } | |
503 | 527 | } |
504 | 528 | this.onToDeviceRpcResponse(rpcRequest.getDeviceRpcResponseResultMsg(), rpcRequest.getSessionInfo()); |
505 | 529 | } |
... | ... | @@ -556,7 +580,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
556 | 580 | */ |
557 | 581 | protected void onAwakeDev(Registration registration) { |
558 | 582 | log.info("[{}] [{}] Received endpoint Awake version event", registration.getId(), registration.getEndpoint()); |
559 | - this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration); | |
583 | + this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client is awake!", registration.getId()); | |
560 | 584 | //TODO: associate endpointId with device information. |
561 | 585 | } |
562 | 586 | |
... | ... | @@ -583,10 +607,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
583 | 607 | |
584 | 608 | /** |
585 | 609 | * @param logMsg - text msg |
586 | - * @param registration - Id of Registration LwM2M Client | |
610 | + * @param registrationId - Id of Registration LwM2M Client | |
587 | 611 | */ |
588 | - public void sendLogsToThingsboard(String logMsg, Registration registration) { | |
589 | - SessionInfoProto sessionInfo = this.getValidateSessionInfo(registration); | |
612 | + public void sendLogsToThingsboard(String logMsg, String registrationId) { | |
613 | + SessionInfoProto sessionInfo = this.getValidateSessionInfo(registrationId); | |
590 | 614 | if (logMsg != null && sessionInfo != null) { |
591 | 615 | this.lwM2mTransportContextServer.sendParametersOnThingsboardTelemetry(this.lwM2mTransportContextServer.getKvLogyToThingsboard(logMsg), sessionInfo); |
592 | 616 | } |
... | ... | @@ -610,7 +634,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
610 | 634 | if (clientObjects != null && clientObjects.size() > 0) { |
611 | 635 | if (LWM2M_STRATEGY_2 == LwM2mTransportHandler.getClientOnlyObserveAfterConnect(lwM2MClientProfile)) { |
612 | 636 | // #2 |
613 | - lwM2MClient.getPendingRequests().addAll(clientObjects); | |
637 | + lwM2MClient.getPendingReadRequests().addAll(clientObjects); | |
614 | 638 | clientObjects.forEach(path -> lwM2mTransportRequest.sendAllRequest(registration, path, READ, ContentFormat.TLV.getName(), |
615 | 639 | null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); |
616 | 640 | } |
... | ... | @@ -652,6 +676,8 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
652 | 676 | * Sending observe value of resources to thingsboard |
653 | 677 | * #1 Return old Value Resource from LwM2MClient |
654 | 678 | * #2 Update new Resources (replace old Resource Value on new Resource Value) |
679 | + * #3 If fr_update -> UpdateFirmware | |
680 | + * #4 updateAttrTelemetry | |
655 | 681 | * |
656 | 682 | * @param registration - Registration LwM2M Client |
657 | 683 | * @param lwM2mResource - LwM2mSingleResource response.getContent() |
... | ... | @@ -661,6 +687,19 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
661 | 687 | LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); |
662 | 688 | if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer() |
663 | 689 | .getModelProvider())) { |
690 | + if (FR_PATH_RESOURCE_VER_ID.equals(convertPathFromIdVerToObjectId(path)) && | |
691 | + lwM2MClient.getFrUpdate().getCurrentFwVersion() != null | |
692 | + && !lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion()) | |
693 | + && lwM2MClient.isUpdateFw()) { | |
694 | + | |
695 | + /** version != null | |
696 | + * set setClient_fw_version = value | |
697 | + **/ | |
698 | + lwM2MClient.setUpdateFw(false); | |
699 | + lwM2MClient.getFrUpdate().setClientFwVersion(lwM2mResource.getValue().toString()); | |
700 | + log.warn("updateFirmwareClient3"); | |
701 | + this.updateFirmwareClient(lwM2MClient); | |
702 | + } | |
664 | 703 | Set<String> paths = new HashSet<>(); |
665 | 704 | paths.add(path); |
666 | 705 | this.updateAttrTelemetry(registration, paths); |
... | ... | @@ -669,6 +708,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
669 | 708 | } |
670 | 709 | } |
671 | 710 | |
711 | + | |
672 | 712 | /** |
673 | 713 | * send Attribute and Telemetry to Thingsboard |
674 | 714 | * #1 - get AttrName/TelemetryName with value from LwM2MClient: |
... | ... | @@ -723,22 +763,23 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
723 | 763 | params = this.getPathForWriteAttributes(lwM2MClientProfile.getPostAttributeLwm2mProfile()); |
724 | 764 | result = params.keySet(); |
725 | 765 | } |
726 | - if (!result.isEmpty()) { | |
766 | + if (result != null && !result.isEmpty()) { | |
727 | 767 | // #1 |
728 | 768 | Set<String> pathSend = result.stream().filter(target -> { |
729 | 769 | return target.split(LWM2M_SEPARATOR_PATH).length < 3 ? |
730 | 770 | clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1]) : |
731 | 771 | clientObjects.contains("/" + target.split(LWM2M_SEPARATOR_PATH)[1] + "/" + target.split(LWM2M_SEPARATOR_PATH)[2]); |
732 | 772 | } |
733 | - ) | |
734 | - .collect(Collectors.toUnmodifiableSet()); | |
773 | + ).collect(Collectors.toUnmodifiableSet()); | |
735 | 774 | if (!pathSend.isEmpty()) { |
736 | - lwM2MClient.getPendingRequests().addAll(pathSend); | |
775 | + lwM2MClient.getPendingReadRequests().addAll(pathSend); | |
737 | 776 | ConcurrentHashMap<String, Object> finalParams = params; |
738 | - pathSend.forEach(target -> lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), | |
739 | - finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null)); | |
777 | + pathSend.forEach(target -> { | |
778 | + lwM2mTransportRequest.sendAllRequest(registration, target, typeOper, ContentFormat.TLV.getName(), | |
779 | + finalParams != null ? finalParams.get(target) : null, this.lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); | |
780 | + }); | |
740 | 781 | if (OBSERVE.equals(typeOper)) { |
741 | - lwM2MClient.initValue(this, null); | |
782 | + lwM2MClient.initReadValue(this, null); | |
742 | 783 | } |
743 | 784 | } |
744 | 785 | } |
... | ... | @@ -969,7 +1010,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
969 | 1010 | // update value in Resources |
970 | 1011 | registrationIds.forEach(registrationId -> { |
971 | 1012 | Registration registration = lwM2mClientContext.getRegistration(registrationId); |
972 | - this.readResourceValueObserve(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ); | |
1013 | + this.readObserveFromProfile(registration, sendAttrToThingsboard.getPathPostParametersAdd(), READ); | |
973 | 1014 | // send attr/telemetry to tingsboard for new path |
974 | 1015 | this.updateAttrTelemetry(registration, sendAttrToThingsboard.getPathPostParametersAdd()); |
975 | 1016 | }); |
... | ... | @@ -998,12 +1039,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
998 | 1039 | registrationIds.forEach(registrationId -> { |
999 | 1040 | Registration registration = lwM2mClientContext.getRegistration(registrationId); |
1000 | 1041 | if (postObserveAnalyzer.getPathPostParametersAdd().size() > 0) { |
1001 | - this.readResourceValueObserve(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); | |
1042 | + this.readObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersAdd(), OBSERVE); | |
1002 | 1043 | } |
1003 | 1044 | // 5.3 del |
1004 | 1045 | // send Request cancel observe to Client |
1005 | 1046 | if (postObserveAnalyzer.getPathPostParametersDel().size() > 0) { |
1006 | - this.cancelObserveIsValue(registration, postObserveAnalyzer.getPathPostParametersDel()); | |
1047 | + this.cancelObserveFromProfile(registration, postObserveAnalyzer.getPathPostParametersDel()); | |
1007 | 1048 | } |
1008 | 1049 | }); |
1009 | 1050 | } |
... | ... | @@ -1043,7 +1084,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
1043 | 1084 | * @param registration - Registration LwM2M Client |
1044 | 1085 | * @param targets - path Resources == [ "/2/0/0", "/2/0/1"] |
1045 | 1086 | */ |
1046 | - private void readResourceValueObserve(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) { | |
1087 | + private void readObserveFromProfile(Registration registration, Set<String> targets, LwM2mTypeOper typeOper) { | |
1047 | 1088 | targets.forEach(target -> { |
1048 | 1089 | LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(target)); |
1049 | 1090 | if (pathIds.isResource()) { |
... | ... | @@ -1133,7 +1174,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
1133 | 1174 | |
1134 | 1175 | } |
1135 | 1176 | |
1136 | - private void cancelObserveIsValue(Registration registration, Set<String> paramAnallyzer) { | |
1177 | + private void cancelObserveFromProfile(Registration registration, Set<String> paramAnallyzer) { | |
1137 | 1178 | LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(registration, null); |
1138 | 1179 | paramAnallyzer.forEach(pathIdVer -> { |
1139 | 1180 | if (this.getResourceValueFromLwM2MClient(lwM2MClient, pathIdVer) != null) { |
... | ... | @@ -1153,7 +1194,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
1153 | 1194 | log.error("Failed update resource [{}] [{}]", path, valueNew); |
1154 | 1195 | String logMsg = String.format("%s: Failed update resource path - %s value - %s. Value is not changed or bad", |
1155 | 1196 | LOG_LW2M_ERROR, path, valueNew); |
1156 | - this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration()); | |
1197 | + this.sendLogsToThingsboard(logMsg, lwM2MClient.getRegistration().getId()); | |
1157 | 1198 | log.info("Failed update resource [{}] [{}]", path, valueNew); |
1158 | 1199 | } |
1159 | 1200 | } |
... | ... | @@ -1182,8 +1223,10 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
1182 | 1223 | } |
1183 | 1224 | |
1184 | 1225 | /** |
1185 | - * Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values | |
1186 | - * #1 Get path resource by result attributesResponse | |
1226 | + * 1. FirmwareUpdate: | |
1227 | + * - msg.getSharedUpdatedList().forEach(tsKvProto -> {tsKvProto.getKv().getKey().indexOf(FIRMWARE_UPDATE_PREFIX, 0) == 0 | |
1228 | + * 2. Update resource value on client: if there is a difference in values between the current resource values and the shared attribute values | |
1229 | + * - Get path resource by result attributesResponse | |
1187 | 1230 | * |
1188 | 1231 | * @param attributesResponse - |
1189 | 1232 | * @param sessionInfo - |
... | ... | @@ -1191,6 +1234,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
1191 | 1234 | public void onGetAttributesResponse(TransportProtos.GetAttributeResponseMsg attributesResponse, TransportProtos.SessionInfoProto sessionInfo) { |
1192 | 1235 | try { |
1193 | 1236 | List<TransportProtos.TsKvProto> tsKvProtos = attributesResponse.getSharedAttributeListList(); |
1237 | + | |
1194 | 1238 | this.updateAttriuteFromThingsboard(tsKvProtos, sessionInfo); |
1195 | 1239 | } catch (Exception e) { |
1196 | 1240 | log.error(String.valueOf(e)); |
... | ... | @@ -1274,7 +1318,7 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
1274 | 1318 | */ |
1275 | 1319 | private SessionInfoProto getValidateSessionInfo(String registrationId) { |
1276 | 1320 | LwM2mClient lwM2MClient = lwM2mClientContext.getLwM2mClientWithReg(null, registrationId); |
1277 | - return getNewSessionInfoProto(lwM2MClient); | |
1321 | + return lwM2MClient != null ? this.getNewSessionInfoProto(lwM2MClient) : null; | |
1278 | 1322 | } |
1279 | 1323 | |
1280 | 1324 | /** |
... | ... | @@ -1293,28 +1337,88 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
1293 | 1337 | } |
1294 | 1338 | |
1295 | 1339 | /** |
1296 | - * !!! sharedAttr === profileAttr !!! | |
1297 | - * If there is a difference in values between the current resource values and the shared attribute values | |
1298 | - * when the client connects to the server | |
1299 | - * #1 get attributes name from profile include name resources in ModelObject if resource isWritable | |
1300 | - * #2.1 #1 size > 0 => send Request getAttributes to thingsboard | |
1340 | + * #1. !!! sharedAttr === profileAttr !!! | |
1341 | + * - If there is a difference in values between the current resource values and the shared attribute values | |
1342 | + * - when the client connects to the server | |
1343 | + * #1.1 get attributes name from profile include name resources in ModelObject if resource isWritable | |
1344 | + * #1.2 #1 size > 0 => send Request getAttributes to thingsboard | |
1345 | + * #2. FirmwareAttribute subscribe: | |
1301 | 1346 | * |
1302 | 1347 | * @param lwM2MClient - LwM2M Client |
1303 | 1348 | */ |
1304 | 1349 | public void putDelayedUpdateResourcesThingsboard(LwM2mClient lwM2MClient) { |
1305 | 1350 | SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); |
1306 | 1351 | if (sessionInfo != null) { |
1307 | - //#1.1 + #1.2 | |
1308 | - List<String> attrSharedNames = this.getNamesAttrFromProfileIsWritable(lwM2MClient); | |
1309 | - if (attrSharedNames.size() > 0) { | |
1310 | - //#2.1 | |
1352 | + //#1.1 | |
1353 | + ConcurrentMap<String, String> keyNamesMap = this.getNamesFromProfileForSharedAttributes(lwM2MClient); | |
1354 | + if (keyNamesMap.values().size() > 0) { | |
1311 | 1355 | try { |
1312 | - TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, attrSharedNames); | |
1356 | + //#1.2 | |
1357 | + TransportProtos.GetAttributeRequestMsg getAttributeMsg = lwM2mTransportContextServer.getAdaptor().convertToGetAttributes(null, keyNamesMap.values()); | |
1313 | 1358 | transportService.process(sessionInfo, getAttributeMsg, getAckCallback(lwM2MClient, getAttributeMsg.getRequestId(), DEVICE_ATTRIBUTES_REQUEST)); |
1314 | 1359 | } catch (AdaptorException e) { |
1315 | 1360 | log.warn("Failed to decode get attributes request", e); |
1316 | 1361 | } |
1317 | 1362 | } |
1363 | + | |
1364 | + } | |
1365 | + } | |
1366 | + | |
1367 | + public void getInfoFirmwareUpdate(LwM2mClient lwM2MClient) { | |
1368 | + SessionInfoProto sessionInfo = this.getValidateSessionInfo(lwM2MClient.getRegistration()); | |
1369 | + if (sessionInfo != null) { | |
1370 | + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() | |
1371 | + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
1372 | + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) | |
1373 | + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
1374 | + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
1375 | + .build(); | |
1376 | + transportService.process(sessionInfo, getFirmwareRequestMsg, | |
1377 | + new TransportServiceCallback<>() { | |
1378 | + @Override | |
1379 | + public void onSuccess(TransportProtos.GetFirmwareResponseMsg response) { | |
1380 | + if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { | |
1381 | + lwM2MClient.getFrUpdate().setCurrentFwVersion(response.getVersion()); | |
1382 | + lwM2MClient.getFrUpdate().setCurrentFwId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId()); | |
1383 | + lwM2MClient.setUpdateFw(true); | |
1384 | + readRequestToClientFirmwareVer(lwM2MClient.getRegistration()); | |
1385 | + } else { | |
1386 | + log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString()); | |
1387 | + } | |
1388 | + } | |
1389 | + | |
1390 | + @Override | |
1391 | + public void onError(Throwable e) { | |
1392 | + log.trace("Failed to process credentials ", e); | |
1393 | + } | |
1394 | + }); | |
1395 | + } | |
1396 | + } | |
1397 | + | |
1398 | + /** | |
1399 | + * @param registration | |
1400 | + */ | |
1401 | + public void readRequestToClientFirmwareVer(Registration registration) { | |
1402 | + String pathIdVer = convertPathFromObjectIdToIdVer(FR_PATH_RESOURCE_VER_ID, registration); | |
1403 | + lwM2mTransportRequest.sendAllRequest(registration, pathIdVer, READ, ContentFormat.TLV.getName(), | |
1404 | + null, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); | |
1405 | + } | |
1406 | + | |
1407 | + /** | |
1408 | + * | |
1409 | + * @param lwM2MClient - | |
1410 | + */ | |
1411 | + public void updateFirmwareClient(LwM2mClient lwM2MClient) { | |
1412 | + if (!lwM2MClient.getFrUpdate().getCurrentFwVersion().equals(lwM2MClient.getFrUpdate().getClientFwVersion())) { | |
1413 | + int chunkSize = 0; | |
1414 | + int chunk = 0; | |
1415 | + byte[] firmwareChunk = firmwareDataCache.get(lwM2MClient.getFrUpdate().getCurrentFwId().toString(), chunkSize, chunk); | |
1416 | + Integer objectId = 5; | |
1417 | + String verSupportedObject = lwM2MClient.getRegistration().getSupportedObject().get(objectId); | |
1418 | + String targetIdVer = LWM2M_SEPARATOR_PATH + objectId + LWM2M_SEPARATOR_KEY + verSupportedObject + LWM2M_SEPARATOR_PATH + 0 + LWM2M_SEPARATOR_PATH + 0; | |
1419 | + lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.TLV.getName(), | |
1420 | + firmwareChunk, lwM2mTransportContextServer.getLwM2MTransportConfigServer().getTimeout(), null); | |
1421 | + log.warn("updateFirmwareClient [{}] [{}]", lwM2MClient.getFrUpdate().getCurrentFwVersion(), lwM2MClient.getFrUpdate().getClientFwVersion()); | |
1318 | 1422 | } |
1319 | 1423 | } |
1320 | 1424 | |
... | ... | @@ -1326,23 +1430,12 @@ public class LwM2mTransportServiceImpl implements LwM2mTransportService { |
1326 | 1430 | * @param lwM2MClient - |
1327 | 1431 | * @return ArrayList keyNames from profile profileAttr && IsWritable |
1328 | 1432 | */ |
1329 | - private List<String> getNamesAttrFromProfileIsWritable(LwM2mClient lwM2MClient) { | |
1433 | + private ConcurrentMap<String, String> getNamesFromProfileForSharedAttributes(LwM2mClient lwM2MClient) { | |
1434 | + | |
1330 | 1435 | LwM2mClientProfile profile = lwM2mClientContext.getProfile(lwM2MClient.getProfileId()); |
1331 | - Set<String> attrSet = new Gson().fromJson(profile.getPostAttributeProfile(), | |
1332 | - new TypeToken<HashSet<String>>() { | |
1333 | - }.getType()); | |
1334 | - ConcurrentMap<String, String> keyNamesMap = new Gson().fromJson(profile.getPostKeyNameProfile().toString(), | |
1436 | + return new Gson().fromJson(profile.getPostKeyNameProfile().toString(), | |
1335 | 1437 | new TypeToken<ConcurrentHashMap<String, String>>() { |
1336 | 1438 | }.getType()); |
1337 | - | |
1338 | - ConcurrentMap<String, String> keyNamesIsWritable = keyNamesMap.entrySet() | |
1339 | - .stream() | |
1340 | - .filter(e -> (attrSet.contains(e.getKey()) && validateResourceInModel(lwM2MClient, e.getKey(), true))) | |
1341 | - .collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue)); | |
1342 | - | |
1343 | - Set<String> namesIsWritable = ConcurrentHashMap.newKeySet(); | |
1344 | - namesIsWritable.addAll(new HashSet<>(keyNamesIsWritable.values())); | |
1345 | - return new ArrayList<>(namesIsWritable); | |
1346 | 1439 | } |
1347 | 1440 | |
1348 | 1441 | private boolean validateResourceInModel(LwM2mClient lwM2mClient, String pathIdVer, boolean isWritableNotOptional) { | ... | ... |
... | ... | @@ -24,11 +24,8 @@ import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
24 | 24 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
25 | 25 | import org.thingsboard.server.gen.transport.TransportProtos; |
26 | 26 | |
27 | -import java.util.Arrays; | |
28 | -import java.util.HashSet; | |
29 | -import java.util.List; | |
27 | +import java.util.Collection; | |
30 | 28 | import java.util.Random; |
31 | -import java.util.Set; | |
32 | 29 | |
33 | 30 | @Slf4j |
34 | 31 | @Component("LwM2MJsonAdaptor") |
... | ... | @@ -54,11 +51,7 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { |
54 | 51 | } |
55 | 52 | |
56 | 53 | @Override |
57 | - public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException { | |
58 | - return processGetAttributeRequestMsg(clientKeys, sharedKeys); | |
59 | - } | |
60 | - | |
61 | - protected TransportProtos.GetAttributeRequestMsg processGetAttributeRequestMsg(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException { | |
54 | + public TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException { | |
62 | 55 | try { |
63 | 56 | TransportProtos.GetAttributeRequestMsg.Builder result = TransportProtos.GetAttributeRequestMsg.newBuilder(); |
64 | 57 | Random random = new Random(); |
... | ... | @@ -75,14 +68,4 @@ public class LwM2MJsonAdaptor implements LwM2MTransportAdaptor { |
75 | 68 | throw new AdaptorException(e); |
76 | 69 | } |
77 | 70 | } |
78 | - | |
79 | - private Set<String> toStringSet(JsonElement requestBody, String name) { | |
80 | - JsonElement element = requestBody.getAsJsonObject().get(name); | |
81 | - if (element != null) { | |
82 | - return new HashSet<>(Arrays.asList(element.getAsString().split(","))); | |
83 | - } else { | |
84 | - return null; | |
85 | - } | |
86 | - } | |
87 | - | |
88 | 71 | } | ... | ... |
... | ... | @@ -19,7 +19,7 @@ import com.google.gson.JsonElement; |
19 | 19 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
20 | 20 | import org.thingsboard.server.gen.transport.TransportProtos; |
21 | 21 | |
22 | -import java.util.List; | |
22 | +import java.util.Collection; | |
23 | 23 | |
24 | 24 | public interface LwM2MTransportAdaptor { |
25 | 25 | |
... | ... | @@ -27,5 +27,5 @@ public interface LwM2MTransportAdaptor { |
27 | 27 | |
28 | 28 | TransportProtos.PostAttributeMsg convertToPostAttributes(JsonElement jsonElement) throws AdaptorException; |
29 | 29 | |
30 | - TransportProtos.GetAttributeRequestMsg convertToGetAttributes(List<String> clientKeys, List<String> sharedKeys) throws AdaptorException; | |
30 | + TransportProtos.GetAttributeRequestMsg convertToGetAttributes(Collection<String> clientKeys, Collection<String> sharedKeys) throws AdaptorException; | |
31 | 31 | } | ... | ... |
... | ... | @@ -57,13 +57,15 @@ public class LwM2mClient implements Cloneable { |
57 | 57 | private UUID deviceId; |
58 | 58 | private UUID sessionId; |
59 | 59 | private UUID profileId; |
60 | + private volatile LwM2mFirmwareUpdate frUpdate; | |
60 | 61 | private Registration registration; |
61 | 62 | private ValidateDeviceCredentialsResponseMsg credentialsResponse; |
62 | 63 | private final Map<String, ResourceValue> resources; |
63 | 64 | private final Map<String, TransportProtos.TsKvProto> delayedRequests; |
64 | - private final List<String> pendingRequests; | |
65 | + private final List<String> pendingReadRequests; | |
65 | 66 | private final Queue<LwM2mQueuedRequest> queuedRequests; |
66 | 67 | private boolean init; |
68 | + private volatile boolean updateFw; | |
67 | 69 | |
68 | 70 | public Object clone() throws CloneNotSupportedException { |
69 | 71 | return super.clone(); |
... | ... | @@ -75,12 +77,14 @@ public class LwM2mClient implements Cloneable { |
75 | 77 | this.securityInfo = securityInfo; |
76 | 78 | this.credentialsResponse = credentialsResponse; |
77 | 79 | this.delayedRequests = new ConcurrentHashMap<>(); |
78 | - this.pendingRequests = new CopyOnWriteArrayList<>(); | |
80 | + this.pendingReadRequests = new CopyOnWriteArrayList<>(); | |
79 | 81 | this.resources = new ConcurrentHashMap<>(); |
80 | 82 | this.profileId = profileId; |
81 | 83 | this.sessionId = sessionId; |
82 | 84 | this.init = false; |
85 | + this.updateFw = false; | |
83 | 86 | this.queuedRequests = new ConcurrentLinkedQueue<>(); |
87 | + this.frUpdate = new LwM2mFirmwareUpdate(); | |
84 | 88 | } |
85 | 89 | |
86 | 90 | public boolean saveResourceValue(String pathRez, LwM2mResource rez, LwM2mModelProvider modelProvider) { |
... | ... | @@ -103,15 +107,13 @@ public class LwM2mClient implements Cloneable { |
103 | 107 | LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRez)); |
104 | 108 | String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); |
105 | 109 | String verRez = getVerFromPathIdVerOrId(pathRez); |
106 | - return (verRez == null || verSupportedObject.equals(verRez)) ? modelProvider.getObjectModel(registration) | |
110 | + return verRez == null || verRez.equals(verSupportedObject) ? modelProvider.getObjectModel(registration) | |
107 | 111 | .getResourceModel(pathIds.getObjectId(), pathIds.getResourceId()) : null; |
108 | 112 | } |
109 | 113 | |
110 | 114 | public Collection<LwM2mResource> getNewResourcesForInstance(String pathRezIdVer, LwM2mModelProvider modelProvider, |
111 | 115 | LwM2mValueConverterImpl converter) { |
112 | 116 | LwM2mPath pathIds = new LwM2mPath(convertPathFromIdVerToObjectId(pathRezIdVer)); |
113 | - String verSupportedObject = registration.getSupportedObject().get(pathIds.getObjectId()); | |
114 | - String verRez = getVerFromPathIdVerOrId(pathRezIdVer); | |
115 | 117 | Collection<LwM2mResource> resources = ConcurrentHashMap.newKeySet(); |
116 | 118 | Map<Integer, ResourceModel> resourceModels = modelProvider.getObjectModel(registration) |
117 | 119 | .getObjectModel(pathIds.getObjectId()).resources; |
... | ... | @@ -170,11 +172,11 @@ public class LwM2mClient implements Cloneable { |
170 | 172 | .collect(Collectors.toSet()); |
171 | 173 | } |
172 | 174 | |
173 | - public void initValue(LwM2mTransportServiceImpl serviceImpl, String path) { | |
175 | + public void initReadValue(LwM2mTransportServiceImpl serviceImpl, String path) { | |
174 | 176 | if (path != null) { |
175 | - this.pendingRequests.remove(path); | |
177 | + this.pendingReadRequests.remove(path); | |
176 | 178 | } |
177 | - if (this.pendingRequests.size() == 0) { | |
179 | + if (this.pendingReadRequests.size() == 0) { | |
178 | 180 | this.init = true; |
179 | 181 | serviceImpl.putDelayedUpdateResourcesThingsboard(this); |
180 | 182 | } | ... | ... |
... | ... | @@ -82,12 +82,12 @@ public class LwM2mClientContextImpl implements LwM2mClientContext { |
82 | 82 | |
83 | 83 | @Override |
84 | 84 | public LwM2mClient getLwM2mClientWithReg(Registration registration, String registrationId) { |
85 | - LwM2mClient client = registrationId != null ? | |
85 | + LwM2mClient client = registrationId != null && this.lwM2mClients.containsKey(registrationId) ? | |
86 | 86 | this.lwM2mClients.get(registrationId) : |
87 | - this.lwM2mClients.containsKey(registration.getId()) ? | |
88 | - this.lwM2mClients.get(registration.getId()) : | |
89 | - this.lwM2mClients.get(registration.getEndpoint()); | |
90 | - return client != null ? client : updateInSessionsLwM2MClient(registration); | |
87 | + registration !=null && this.lwM2mClients.containsKey(registration.getId()) ? | |
88 | + this.lwM2mClients.get(registration.getId()) : registration !=null && this.lwM2mClients.containsKey(registration) ? | |
89 | + this.lwM2mClients.get(registration.getEndpoint()) : null; | |
90 | + return client != null ? client : registration!= null ? updateInSessionsLwM2MClient(registration) : null; | |
91 | 91 | } |
92 | 92 | |
93 | 93 | @Override | ... | ... |
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.transport.lwm2m.server.client; | |
17 | + | |
18 | +import lombok.Data; | |
19 | + | |
20 | +import java.util.UUID; | |
21 | + | |
22 | +@Data | |
23 | +public class LwM2mFirmwareUpdate { | |
24 | + private volatile String clientFwVersion; | |
25 | + private volatile String currentFwVersion; | |
26 | + private volatile UUID currentFwId; | |
27 | +} | ... | ... |
... | ... | @@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.DeviceProfile; |
47 | 47 | import org.thingsboard.server.common.data.DeviceTransportType; |
48 | 48 | import org.thingsboard.server.common.data.TransportPayloadType; |
49 | 49 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
50 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
50 | 51 | import org.thingsboard.server.common.data.id.FirmwareId; |
51 | 52 | import org.thingsboard.server.common.msg.EncryptionUtil; |
52 | 53 | import org.thingsboard.server.common.msg.tools.TbRateLimitsException; |
... | ... | @@ -59,6 +60,7 @@ import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; |
59 | 60 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
60 | 61 | import org.thingsboard.server.common.transport.service.DefaultTransportService; |
61 | 62 | import org.thingsboard.server.common.transport.service.SessionMetaData; |
63 | +import org.thingsboard.server.common.transport.util.SslUtil; | |
62 | 64 | import org.thingsboard.server.gen.transport.TransportProtos; |
63 | 65 | import org.thingsboard.server.gen.transport.TransportProtos.ProvisionDeviceResponseMsg; |
64 | 66 | import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent; |
... | ... | @@ -68,7 +70,6 @@ import org.thingsboard.server.transport.mqtt.adaptors.MqttTransportAdaptor; |
68 | 70 | import org.thingsboard.server.transport.mqtt.session.DeviceSessionCtx; |
69 | 71 | import org.thingsboard.server.transport.mqtt.session.GatewaySessionHandler; |
70 | 72 | import org.thingsboard.server.transport.mqtt.session.MqttTopicMatcher; |
71 | -import org.thingsboard.server.common.transport.util.SslUtil; | |
72 | 73 | |
73 | 74 | import javax.net.ssl.SSLPeerUnverifiedException; |
74 | 75 | import java.io.IOException; |
... | ... | @@ -97,6 +98,8 @@ import static io.netty.handler.codec.mqtt.MqttMessageType.UNSUBACK; |
97 | 98 | import static io.netty.handler.codec.mqtt.MqttQoS.AT_LEAST_ONCE; |
98 | 99 | import static io.netty.handler.codec.mqtt.MqttQoS.AT_MOST_ONCE; |
99 | 100 | import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; |
101 | +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN; | |
102 | +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN; | |
100 | 103 | |
101 | 104 | /** |
102 | 105 | * @author Andrew Shvayka |
... | ... | @@ -104,7 +107,9 @@ import static io.netty.handler.codec.mqtt.MqttQoS.FAILURE; |
104 | 107 | @Slf4j |
105 | 108 | public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener<Future<? super Void>>, SessionMsgListener { |
106 | 109 | |
107 | - private static final Pattern FW_PATTERN = Pattern.compile("v2/fw/request/(?<requestId>\\d+)/chunk/(?<chunk>\\d+)"); | |
110 | + private static final Pattern FW_REQUEST_PATTERN = Pattern.compile(DEVICE_FIRMWARE_REQUEST_TOPIC_PATTERN); | |
111 | + private static final Pattern SW_REQUEST_PATTERN = Pattern.compile(DEVICE_SOFTWARE_REQUEST_TOPIC_PATTERN); | |
112 | + | |
108 | 113 | |
109 | 114 | private static final String PAYLOAD_TOO_LARGE = "PAYLOAD_TOO_LARGE"; |
110 | 115 | |
... | ... | @@ -314,38 +319,10 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
314 | 319 | } else if (topicName.equals(MqttTopics.DEVICE_CLAIM_TOPIC)) { |
315 | 320 | TransportProtos.ClaimDeviceMsg claimDeviceMsg = payloadAdaptor.convertToClaimDevice(deviceSessionCtx, mqttMsg); |
316 | 321 | transportService.process(deviceSessionCtx.getSessionInfo(), claimDeviceMsg, getPubAckCallback(ctx, msgId, claimDeviceMsg)); |
317 | - } else if ((fwMatcher = FW_PATTERN.matcher(topicName)).find()) { | |
318 | - String payload = mqttMsg.content().toString(UTF8); | |
319 | - int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0; | |
320 | - String requestId = fwMatcher.group("requestId"); | |
321 | - int chunk = Integer.parseInt(fwMatcher.group("chunk")); | |
322 | - | |
323 | - if (chunkSize > 0) { | |
324 | - this.fwChunkSizes.put(requestId, chunkSize); | |
325 | - } else { | |
326 | - chunkSize = fwChunkSizes.getOrDefault(requestId, 0); | |
327 | - } | |
328 | - | |
329 | - if (chunkSize > context.getMaxPayloadSize()) { | |
330 | - sendFirmwareError(ctx, PAYLOAD_TOO_LARGE); | |
331 | - return; | |
332 | - } | |
333 | - | |
334 | - String firmwareId = fwSessions.get(requestId); | |
335 | - | |
336 | - if (firmwareId != null) { | |
337 | - sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk); | |
338 | - } else { | |
339 | - TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo(); | |
340 | - TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() | |
341 | - .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
342 | - .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) | |
343 | - .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
344 | - .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
345 | - .build(); | |
346 | - transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg, | |
347 | - new FirmwareCallback(ctx, mqttMsg.variableHeader().packetId(), getFirmwareRequestMsg, requestId, chunkSize, chunk)); | |
348 | - } | |
322 | + } else if ((fwMatcher = FW_REQUEST_PATTERN.matcher(topicName)).find()) { | |
323 | + getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.FIRMWARE); | |
324 | + } else if ((fwMatcher = SW_REQUEST_PATTERN.matcher(topicName)).find()) { | |
325 | + getFirmwareCallback(ctx, mqttMsg, msgId, fwMatcher, FirmwareType.SOFTWARE); | |
349 | 326 | } else { |
350 | 327 | transportService.reportActivity(deviceSessionCtx.getSessionInfo()); |
351 | 328 | ack(ctx, msgId); |
... | ... | @@ -357,6 +334,41 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
357 | 334 | } |
358 | 335 | } |
359 | 336 | |
337 | + private void getFirmwareCallback(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, int msgId, Matcher fwMatcher, FirmwareType type) { | |
338 | + String payload = mqttMsg.content().toString(UTF8); | |
339 | + int chunkSize = StringUtils.isNotEmpty(payload) ? Integer.parseInt(payload) : 0; | |
340 | + String requestId = fwMatcher.group("requestId"); | |
341 | + int chunk = Integer.parseInt(fwMatcher.group("chunk")); | |
342 | + | |
343 | + if (chunkSize > 0) { | |
344 | + this.fwChunkSizes.put(requestId, chunkSize); | |
345 | + } else { | |
346 | + chunkSize = fwChunkSizes.getOrDefault(requestId, 0); | |
347 | + } | |
348 | + | |
349 | + if (chunkSize > context.getMaxPayloadSize()) { | |
350 | + sendFirmwareError(ctx, PAYLOAD_TOO_LARGE); | |
351 | + return; | |
352 | + } | |
353 | + | |
354 | + String firmwareId = fwSessions.get(requestId); | |
355 | + | |
356 | + if (firmwareId != null) { | |
357 | + sendFirmware(ctx, mqttMsg.variableHeader().packetId(), firmwareId, requestId, chunkSize, chunk, type); | |
358 | + } else { | |
359 | + TransportProtos.SessionInfoProto sessionInfo = deviceSessionCtx.getSessionInfo(); | |
360 | + TransportProtos.GetFirmwareRequestMsg getFirmwareRequestMsg = TransportProtos.GetFirmwareRequestMsg.newBuilder() | |
361 | + .setDeviceIdMSB(sessionInfo.getDeviceIdMSB()) | |
362 | + .setDeviceIdLSB(sessionInfo.getDeviceIdLSB()) | |
363 | + .setTenantIdMSB(sessionInfo.getTenantIdMSB()) | |
364 | + .setTenantIdLSB(sessionInfo.getTenantIdLSB()) | |
365 | + .setType(type.name()) | |
366 | + .build(); | |
367 | + transportService.process(deviceSessionCtx.getSessionInfo(), getFirmwareRequestMsg, | |
368 | + new FirmwareCallback(ctx, msgId, getFirmwareRequestMsg, requestId, chunkSize, chunk)); | |
369 | + } | |
370 | + } | |
371 | + | |
360 | 372 | private void ack(ChannelHandlerContext ctx, int msgId) { |
361 | 373 | if (msgId > 0) { |
362 | 374 | ctx.writeAndFlush(createMqttPubAckMsg(msgId)); |
... | ... | @@ -435,7 +447,7 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
435 | 447 | if (TransportProtos.ResponseStatus.SUCCESS.equals(response.getResponseStatus())) { |
436 | 448 | FirmwareId firmwareId = new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())); |
437 | 449 | fwSessions.put(requestId, firmwareId.toString()); |
438 | - sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk); | |
450 | + sendFirmware(ctx, msgId, firmwareId.toString(), requestId, chunkSize, chunk, FirmwareType.valueOf(response.getType())); | |
439 | 451 | } else { |
440 | 452 | sendFirmwareError(ctx, response.getResponseStatus().toString()); |
441 | 453 | } |
... | ... | @@ -448,13 +460,13 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
448 | 460 | } |
449 | 461 | } |
450 | 462 | |
451 | - private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk) { | |
463 | + private void sendFirmware(ChannelHandlerContext ctx, int msgId, String firmwareId, String requestId, int chunkSize, int chunk, FirmwareType type) { | |
452 | 464 | log.trace("[{}] Send firmware [{}] to device!", sessionId, firmwareId); |
453 | 465 | ack(ctx, msgId); |
454 | 466 | try { |
455 | 467 | byte[] firmwareChunk = context.getFirmwareDataCache().get(firmwareId, chunkSize, chunk); |
456 | 468 | deviceSessionCtx.getPayloadAdaptor() |
457 | - .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk) | |
469 | + .convertToPublish(deviceSessionCtx, firmwareChunk, requestId, chunk, type) | |
458 | 470 | .ifPresent(deviceSessionCtx.getChannel()::writeAndFlush); |
459 | 471 | } catch (Exception e) { |
460 | 472 | log.trace("[{}] Failed to send firmware response!", sessionId, e); |
... | ... | @@ -501,6 +513,8 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement |
501 | 513 | case MqttTopics.DEVICE_PROVISION_RESPONSE_TOPIC: |
502 | 514 | case MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC: |
503 | 515 | case MqttTopics.DEVICE_FIRMWARE_ERROR_TOPIC: |
516 | + case MqttTopics.DEVICE_SOFTWARE_RESPONSES_TOPIC: | |
517 | + case MqttTopics.DEVICE_SOFTWARE_ERROR_TOPIC: | |
504 | 518 | registerSubQoS(topic, grantedQoSList, reqQoS); |
505 | 519 | break; |
506 | 520 | default: | ... | ... |
... | ... | @@ -30,6 +30,7 @@ import lombok.extern.slf4j.Slf4j; |
30 | 30 | import org.springframework.stereotype.Component; |
31 | 31 | import org.springframework.util.StringUtils; |
32 | 32 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
33 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
33 | 34 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
34 | 35 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
35 | 36 | import org.thingsboard.server.gen.transport.TransportProtos; |
... | ... | @@ -43,6 +44,9 @@ import java.util.Optional; |
43 | 44 | import java.util.Set; |
44 | 45 | import java.util.UUID; |
45 | 46 | |
47 | +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT; | |
48 | + | |
49 | + | |
46 | 50 | /** |
47 | 51 | * @author Andrew Shvayka |
48 | 52 | */ |
... | ... | @@ -151,8 +155,8 @@ public class JsonMqttAdaptor implements MqttTransportAdaptor { |
151 | 155 | } |
152 | 156 | |
153 | 157 | @Override |
154 | - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) { | |
155 | - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/chunk/" + chunk, firmwareChunk)); | |
158 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) { | |
159 | + return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk)); | |
156 | 160 | } |
157 | 161 | |
158 | 162 | public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { | ... | ... |
... | ... | @@ -23,6 +23,7 @@ import io.netty.handler.codec.mqtt.MqttMessage; |
23 | 23 | import io.netty.handler.codec.mqtt.MqttMessageType; |
24 | 24 | import io.netty.handler.codec.mqtt.MqttPublishMessage; |
25 | 25 | import io.netty.handler.codec.mqtt.MqttPublishVariableHeader; |
26 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
26 | 27 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
27 | 28 | import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg; |
28 | 29 | import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg; |
... | ... | @@ -77,7 +78,7 @@ public interface MqttTransportAdaptor { |
77 | 78 | |
78 | 79 | Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, ProvisionDeviceResponseMsg provisionResponse) throws AdaptorException; |
79 | 80 | |
80 | - Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException; | |
81 | + Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException; | |
81 | 82 | |
82 | 83 | default MqttPublishMessage createMqttPublishMsg(MqttDeviceAwareSessionContext ctx, String topic, byte[] payloadInBytes) { |
83 | 84 | MqttFixedHeader mqttFixedHeader = | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import lombok.extern.slf4j.Slf4j; |
28 | 28 | import org.springframework.stereotype.Component; |
29 | 29 | import org.springframework.util.StringUtils; |
30 | 30 | import org.thingsboard.server.common.data.device.profile.MqttTopics; |
31 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
31 | 32 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
32 | 33 | import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
33 | 34 | import org.thingsboard.server.common.transport.adaptor.ProtoConverter; |
... | ... | @@ -38,6 +39,8 @@ import org.thingsboard.server.transport.mqtt.session.MqttDeviceAwareSessionConte |
38 | 39 | |
39 | 40 | import java.util.Optional; |
40 | 41 | |
42 | +import static org.thingsboard.server.common.data.device.profile.MqttTopics.DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT; | |
43 | + | |
41 | 44 | @Component |
42 | 45 | @Slf4j |
43 | 46 | public class ProtoMqttAdaptor implements MqttTransportAdaptor { |
... | ... | @@ -165,8 +168,8 @@ public class ProtoMqttAdaptor implements MqttTransportAdaptor { |
165 | 168 | } |
166 | 169 | |
167 | 170 | @Override |
168 | - public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk) throws AdaptorException { | |
169 | - return Optional.of(createMqttPublishMsg(ctx, MqttTopics.DEVICE_FIRMWARE_RESPONSE_TOPIC_PREFIX + requestId + "/" + chunk, firmwareChunk)); | |
171 | + public Optional<MqttMessage> convertToPublish(MqttDeviceAwareSessionContext ctx, byte[] firmwareChunk, String requestId, int chunk, FirmwareType firmwareType) throws AdaptorException { | |
172 | + return Optional.of(createMqttPublishMsg(ctx, String.format(DEVICE_FIRMWARE_RESPONSES_TOPIC_FORMAT, firmwareType.getKeyPrefix(), requestId, chunk), firmwareChunk)); | |
170 | 173 | } |
171 | 174 | |
172 | 175 | @Override | ... | ... |
... | ... | @@ -55,7 +55,6 @@ import java.util.Optional; |
55 | 55 | import java.util.UUID; |
56 | 56 | import java.util.concurrent.ConcurrentHashMap; |
57 | 57 | import java.util.concurrent.ConcurrentLinkedDeque; |
58 | -import java.util.stream.Collectors; | |
59 | 58 | |
60 | 59 | @TbSnmpTransportComponent |
61 | 60 | @Component |
... | ... | @@ -72,25 +71,30 @@ public class SnmpTransportContext extends TransportContext { |
72 | 71 | private final SnmpAuthService snmpAuthService; |
73 | 72 | |
74 | 73 | private final Map<DeviceId, DeviceSessionContext> sessions = new ConcurrentHashMap<>(); |
75 | - private Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>(); | |
74 | + private final Collection<DeviceId> allSnmpDevicesIds = new ConcurrentLinkedDeque<>(); | |
76 | 75 | |
77 | 76 | @AfterStartUp(order = 2) |
78 | - public void initDevicesSessions() { | |
77 | + public void fetchDevicesAndEstablishSessions() { | |
79 | 78 | log.info("Initializing SNMP devices sessions"); |
80 | - allSnmpDevicesIds = protoEntityService.getAllSnmpDevicesIds().stream() | |
81 | - .map(DeviceId::new) | |
82 | - .collect(Collectors.toList()); | |
83 | - log.trace("Found all SNMP devices ids: {}", allSnmpDevicesIds); | |
84 | 79 | |
85 | - List<DeviceId> managedDevicesIds = allSnmpDevicesIds.stream() | |
86 | - .filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId())) | |
87 | - .collect(Collectors.toList()); | |
88 | - log.info("SNMP devices managed by current SNMP transport: {}", managedDevicesIds); | |
80 | + int batchIndex = 0; | |
81 | + int batchSize = 512; | |
82 | + boolean nextBatchExists = true; | |
89 | 83 | |
90 | - managedDevicesIds.stream() | |
91 | - .map(protoEntityService::getDeviceById) | |
92 | - .collect(Collectors.toList()) | |
93 | - .forEach(this::establishDeviceSession); | |
84 | + while (nextBatchExists) { | |
85 | + TransportProtos.GetSnmpDevicesResponseMsg snmpDevicesResponse = protoEntityService.getSnmpDevicesIds(batchIndex, batchSize); | |
86 | + snmpDevicesResponse.getIdsList().stream() | |
87 | + .map(id -> new DeviceId(UUID.fromString(id))) | |
88 | + .peek(allSnmpDevicesIds::add) | |
89 | + .filter(deviceId -> balancingService.isManagedByCurrentTransport(deviceId.getId())) | |
90 | + .map(protoEntityService::getDeviceById) | |
91 | + .forEach(device -> getExecutor().execute(() -> establishDeviceSession(device))); | |
92 | + | |
93 | + nextBatchExists = snmpDevicesResponse.getHasNextPage(); | |
94 | + batchIndex++; | |
95 | + } | |
96 | + | |
97 | + log.debug("Found all SNMP devices ids: {}", allSnmpDevicesIds); | |
94 | 98 | } |
95 | 99 | |
96 | 100 | private void establishDeviceSession(Device device) { | ... | ... |
... | ... | @@ -81,26 +81,7 @@ public class ProtoTransportEntityService { |
81 | 81 | .orElseThrow(() -> new IllegalArgumentException("Device credentials not found")); |
82 | 82 | } |
83 | 83 | |
84 | - public List<UUID> getAllSnmpDevicesIds() { | |
85 | - List<UUID> result = new ArrayList<>(); | |
86 | - | |
87 | - int page = 0; | |
88 | - int pageSize = 512; | |
89 | - boolean hasNextPage = true; | |
90 | - | |
91 | - while (hasNextPage) { | |
92 | - TransportProtos.GetSnmpDevicesResponseMsg responseMsg = requestSnmpDevicesIds(page, pageSize); | |
93 | - result.addAll(responseMsg.getIdsList().stream() | |
94 | - .map(UUID::fromString) | |
95 | - .collect(Collectors.toList())); | |
96 | - hasNextPage = responseMsg.getHasNextPage(); | |
97 | - page++; | |
98 | - } | |
99 | - | |
100 | - return result; | |
101 | - } | |
102 | - | |
103 | - private TransportProtos.GetSnmpDevicesResponseMsg requestSnmpDevicesIds(int page, int pageSize) { | |
84 | + public TransportProtos.GetSnmpDevicesResponseMsg getSnmpDevicesIds(int page, int pageSize) { | |
104 | 85 | TransportProtos.GetSnmpDevicesRequestMsg requestMsg = TransportProtos.GetSnmpDevicesRequestMsg.newBuilder() |
105 | 86 | .setPage(page) |
106 | 87 | .setPageSize(pageSize) | ... | ... |
... | ... | @@ -20,6 +20,7 @@ import lombok.Data; |
20 | 20 | import lombok.Getter; |
21 | 21 | import lombok.extern.slf4j.Slf4j; |
22 | 22 | import org.springframework.beans.factory.annotation.Autowired; |
23 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
23 | 24 | import org.thingsboard.server.cache.firmware.FirmwareDataCache; |
24 | 25 | import org.thingsboard.server.queue.discovery.TbServiceInfoProvider; |
25 | 26 | import org.thingsboard.server.queue.scheduler.SchedulerComponent; |
... | ... | @@ -57,7 +58,7 @@ public abstract class TransportContext { |
57 | 58 | |
58 | 59 | @PostConstruct |
59 | 60 | public void init() { |
60 | - executor = Executors.newWorkStealingPool(50); | |
61 | + executor = ThingsBoardExecutors.newWorkStealingPool(50, getClass()); | |
61 | 62 | } |
62 | 63 | |
63 | 64 | @PreDestroy | ... | ... |
... | ... | @@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j; |
25 | 25 | import org.springframework.beans.factory.annotation.Value; |
26 | 26 | import org.springframework.context.ApplicationEventPublisher; |
27 | 27 | import org.springframework.stereotype.Service; |
28 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
28 | 29 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
29 | 30 | import org.thingsboard.server.common.data.ApiUsageRecordKey; |
30 | 31 | import org.thingsboard.server.common.data.ApiUsageState; |
... | ... | @@ -186,7 +187,7 @@ public class DefaultTransportService implements TransportService { |
186 | 187 | this.ruleEngineProducerStats = statsFactory.createMessagesStats(StatsType.RULE_ENGINE.getName() + ".producer"); |
187 | 188 | this.tbCoreProducerStats = statsFactory.createMessagesStats(StatsType.CORE.getName() + ".producer"); |
188 | 189 | this.transportApiStats = statsFactory.createMessagesStats(StatsType.TRANSPORT.getName() + ".producer"); |
189 | - this.transportCallbackExecutor = Executors.newWorkStealingPool(20); | |
190 | + this.transportCallbackExecutor = ThingsBoardExecutors.newWorkStealingPool(20, getClass()); | |
190 | 191 | this.scheduler.scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS); |
191 | 192 | transportApiRequestTemplate = queueProvider.createTransportApiRequestTemplate(); |
192 | 193 | transportApiRequestTemplate.setMessagesStats(transportApiStats); | ... | ... |
... | ... | @@ -34,7 +34,7 @@ public abstract class AbstractListeningExecutor implements ListeningExecutor { |
34 | 34 | |
35 | 35 | @PostConstruct |
36 | 36 | public void init() { |
37 | - this.service = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool(getThreadPollSize())); | |
37 | + this.service = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(getThreadPollSize(), getClass())); | |
38 | 38 | } |
39 | 39 | |
40 | 40 | @PreDestroy | ... | ... |
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.common.util; | |
17 | + | |
18 | +import java.util.concurrent.ExecutorService; | |
19 | +import java.util.concurrent.ForkJoinPool; | |
20 | + | |
21 | +public class ThingsBoardExecutors { | |
22 | + | |
23 | + /** | |
24 | + * Method forked from ExecutorService to provide thread poll name | |
25 | + * | |
26 | + * Creates a thread pool that maintains enough threads to support | |
27 | + * the given parallelism level, and may use multiple queues to | |
28 | + * reduce contention. The parallelism level corresponds to the | |
29 | + * maximum number of threads actively engaged in, or available to | |
30 | + * engage in, task processing. The actual number of threads may | |
31 | + * grow and shrink dynamically. A work-stealing pool makes no | |
32 | + * guarantees about the order in which submitted tasks are | |
33 | + * executed. | |
34 | + * | |
35 | + * @param parallelism the targeted parallelism level | |
36 | + * @param namePrefix used to define thread name | |
37 | + * @return the newly created thread pool | |
38 | + * @throws IllegalArgumentException if {@code parallelism <= 0} | |
39 | + * @since 1.8 | |
40 | + */ | |
41 | + public static ExecutorService newWorkStealingPool(int parallelism, String namePrefix) { | |
42 | + return new ForkJoinPool(parallelism, | |
43 | + new ThingsBoardForkJoinWorkerThreadFactory(namePrefix), | |
44 | + null, true); | |
45 | + } | |
46 | + | |
47 | + public static ExecutorService newWorkStealingPool(int parallelism, Class clazz) { | |
48 | + return newWorkStealingPool(parallelism, clazz.getSimpleName()); | |
49 | + } | |
50 | +} | ... | ... |
common/util/src/main/java/org/thingsboard/common/util/ThingsBoardForkJoinWorkerThreadFactory.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.common.util; | |
17 | + | |
18 | +import lombok.NonNull; | |
19 | +import lombok.ToString; | |
20 | + | |
21 | +import java.util.concurrent.ForkJoinPool; | |
22 | +import java.util.concurrent.ForkJoinWorkerThread; | |
23 | +import java.util.concurrent.atomic.AtomicLong; | |
24 | + | |
25 | +@ToString | |
26 | +public class ThingsBoardForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { | |
27 | + private final String namePrefix; | |
28 | + private final AtomicLong threadNumber = new AtomicLong(1); | |
29 | + | |
30 | + public ThingsBoardForkJoinWorkerThreadFactory(@NonNull String namePrefix) { | |
31 | + this.namePrefix = namePrefix; | |
32 | + } | |
33 | + | |
34 | + @Override | |
35 | + public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { | |
36 | + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); | |
37 | + thread.setName(namePrefix +"-"+thread.getPoolIndex()+"-"+threadNumber.getAndIncrement()); | |
38 | + return thread; | |
39 | + } | |
40 | + | |
41 | +} | ... | ... |
... | ... | @@ -83,6 +83,8 @@ public interface DeviceDao extends Dao<Device>, TenantEntityDao { |
83 | 83 | |
84 | 84 | PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(UUID tenantId, String type, PageLink pageLink); |
85 | 85 | |
86 | + PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink); | |
87 | + | |
86 | 88 | /** |
87 | 89 | * Find device infos by tenantId, type and page link. |
88 | 90 | * | ... | ... |
... | ... | @@ -56,6 +56,7 @@ import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfilePr |
56 | 56 | import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
57 | 57 | import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; |
58 | 58 | import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; |
59 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
59 | 60 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
60 | 61 | import org.thingsboard.server.common.data.id.TenantId; |
61 | 62 | import org.thingsboard.server.common.data.page.PageData; |
... | ... | @@ -406,9 +407,31 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D |
406 | 407 | if (firmware == null) { |
407 | 408 | throw new DataValidationException("Can't assign non-existent firmware!"); |
408 | 409 | } |
410 | + if (!firmware.getType().equals(FirmwareType.FIRMWARE)) { | |
411 | + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); | |
412 | + } | |
409 | 413 | if (firmware.getData() == null) { |
410 | 414 | throw new DataValidationException("Can't assign firmware with empty data!"); |
411 | 415 | } |
416 | + if (!firmware.getDeviceProfileId().equals(deviceProfile.getId())) { | |
417 | + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); | |
418 | + } | |
419 | + } | |
420 | + | |
421 | + if (deviceProfile.getSoftwareId() != null) { | |
422 | + Firmware software = firmwareService.findFirmwareById(tenantId, deviceProfile.getSoftwareId()); | |
423 | + if (software == null) { | |
424 | + throw new DataValidationException("Can't assign non-existent software!"); | |
425 | + } | |
426 | + if (!software.getType().equals(FirmwareType.SOFTWARE)) { | |
427 | + throw new DataValidationException("Can't assign software with type: " + software.getType()); | |
428 | + } | |
429 | + if (software.getData() == null) { | |
430 | + throw new DataValidationException("Can't assign software with empty data!"); | |
431 | + } | |
432 | + if (!software.getDeviceProfileId().equals(deviceProfile.getId())) { | |
433 | + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); | |
434 | + } | |
412 | 435 | } |
413 | 436 | } |
414 | 437 | ... | ... |
... | ... | @@ -53,6 +53,7 @@ import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfig |
53 | 53 | import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration; |
54 | 54 | import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration; |
55 | 55 | import org.thingsboard.server.common.data.edge.Edge; |
56 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
56 | 57 | import org.thingsboard.server.common.data.id.CustomerId; |
57 | 58 | import org.thingsboard.server.common.data.id.DeviceId; |
58 | 59 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
... | ... | @@ -361,7 +362,7 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
361 | 362 | |
362 | 363 | @Override |
363 | 364 | public PageData<Device> findDevicesByTenantIdAndTypeAndEmptyFirmware(TenantId tenantId, String type, PageLink pageLink) { |
364 | - log.trace("Executing findDevicesByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); | |
365 | + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptyFirmware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); | |
365 | 366 | validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
366 | 367 | validateString(type, "Incorrect type " + type); |
367 | 368 | validatePageLink(pageLink); |
... | ... | @@ -369,6 +370,15 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
369 | 370 | } |
370 | 371 | |
371 | 372 | @Override |
373 | + public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(TenantId tenantId, String type, PageLink pageLink) { | |
374 | + log.trace("Executing findDevicesByTenantIdAndTypeAndEmptySoftware, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); | |
375 | + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); | |
376 | + validateString(type, "Incorrect type " + type); | |
377 | + validatePageLink(pageLink); | |
378 | + return deviceDao.findDevicesByTenantIdAndTypeAndEmptySoftware(tenantId.getId(), type, pageLink); | |
379 | + } | |
380 | + | |
381 | + @Override | |
372 | 382 | public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(TenantId tenantId, String type, PageLink pageLink) { |
373 | 383 | log.trace("Executing findDeviceInfosByTenantIdAndType, tenantId [{}], type [{}], pageLink [{}]", tenantId, type, pageLink); |
374 | 384 | validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
... | ... | @@ -696,9 +706,31 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe |
696 | 706 | if (firmware == null) { |
697 | 707 | throw new DataValidationException("Can't assign non-existent firmware!"); |
698 | 708 | } |
709 | + if (!firmware.getType().equals(FirmwareType.FIRMWARE)) { | |
710 | + throw new DataValidationException("Can't assign firmware with type: " + firmware.getType()); | |
711 | + } | |
699 | 712 | if (firmware.getData() == null) { |
700 | 713 | throw new DataValidationException("Can't assign firmware with empty data!"); |
701 | 714 | } |
715 | + if (!firmware.getDeviceProfileId().equals(device.getDeviceProfileId())) { | |
716 | + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); | |
717 | + } | |
718 | + } | |
719 | + | |
720 | + if (device.getSoftwareId() != null) { | |
721 | + Firmware software = firmwareService.findFirmwareById(tenantId, device.getSoftwareId()); | |
722 | + if (software == null) { | |
723 | + throw new DataValidationException("Can't assign non-existent software!"); | |
724 | + } | |
725 | + if (!software.getType().equals(FirmwareType.SOFTWARE)) { | |
726 | + throw new DataValidationException("Can't assign software with type: " + software.getType()); | |
727 | + } | |
728 | + if (software.getData() == null) { | |
729 | + throw new DataValidationException("Can't assign software with empty data!"); | |
730 | + } | |
731 | + if (!software.getDeviceProfileId().equals(device.getDeviceProfileId())) { | |
732 | + throw new DataValidationException("Can't assign firmware with different deviceProfile!"); | |
733 | + } | |
702 | 734 | } |
703 | 735 | } |
704 | 736 | }; | ... | ... |
... | ... | @@ -27,16 +27,20 @@ import org.springframework.cache.CacheManager; |
27 | 27 | import org.springframework.cache.annotation.Cacheable; |
28 | 28 | import org.springframework.stereotype.Service; |
29 | 29 | import org.thingsboard.server.cache.firmware.FirmwareDataCache; |
30 | +import org.thingsboard.server.common.data.DeviceProfile; | |
30 | 31 | import org.thingsboard.server.common.data.Firmware; |
31 | 32 | import org.thingsboard.server.common.data.FirmwareInfo; |
32 | 33 | import org.thingsboard.server.common.data.Tenant; |
33 | 34 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
34 | 35 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
35 | 36 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
37 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
38 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
36 | 39 | import org.thingsboard.server.common.data.id.FirmwareId; |
37 | 40 | import org.thingsboard.server.common.data.id.TenantId; |
38 | 41 | import org.thingsboard.server.common.data.page.PageData; |
39 | 42 | import org.thingsboard.server.common.data.page.PageLink; |
43 | +import org.thingsboard.server.dao.device.DeviceProfileDao; | |
40 | 44 | import org.thingsboard.server.dao.exception.DataValidationException; |
41 | 45 | import org.thingsboard.server.dao.service.DataValidator; |
42 | 46 | import org.thingsboard.server.dao.service.PaginatedRemover; |
... | ... | @@ -63,6 +67,7 @@ public class BaseFirmwareService implements FirmwareService { |
63 | 67 | public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; |
64 | 68 | |
65 | 69 | private final TenantDao tenantDao; |
70 | + private final DeviceProfileDao deviceProfileDao; | |
66 | 71 | private final FirmwareDao firmwareDao; |
67 | 72 | private final FirmwareInfoDao firmwareInfoDao; |
68 | 73 | private final CacheManager cacheManager; |
... | ... | @@ -113,7 +118,7 @@ public class BaseFirmwareService implements FirmwareService { |
113 | 118 | } |
114 | 119 | |
115 | 120 | @Override |
116 | - public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) throws ThingsboardException { | |
121 | + public String generateChecksum(ChecksumAlgorithm checksumAlgorithm, ByteBuffer data) { | |
117 | 122 | |
118 | 123 | if (data == null || !data.hasArray() || data.array().length == 0) { |
119 | 124 | throw new DataValidationException("Firmware data should be specified!"); |
... | ... | @@ -167,7 +172,8 @@ public class BaseFirmwareService implements FirmwareService { |
167 | 172 | public ListenableFuture<FirmwareInfo> findFirmwareInfoByIdAsync(TenantId tenantId, FirmwareId firmwareId) { |
168 | 173 | log.trace("Executing findFirmwareInfoByIdAsync [{}]", firmwareId); |
169 | 174 | validateId(firmwareId, INCORRECT_FIRMWARE_ID + firmwareId); |
170 | - return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); } | |
175 | + return firmwareInfoDao.findByIdAsync(tenantId, firmwareId.getId()); | |
176 | + } | |
171 | 177 | |
172 | 178 | @Override |
173 | 179 | public PageData<FirmwareInfo> findTenantFirmwaresByTenantId(TenantId tenantId, PageLink pageLink) { |
... | ... | @@ -178,12 +184,11 @@ public class BaseFirmwareService implements FirmwareService { |
178 | 184 | } |
179 | 185 | |
180 | 186 | @Override |
181 | - public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndHasData(TenantId tenantId, | |
182 | - boolean hasData, PageLink pageLink) { | |
187 | + public PageData<FirmwareInfo> findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) { | |
183 | 188 | log.trace("Executing findTenantFirmwaresByTenantIdAndHasData, tenantId [{}], hasData [{}] pageLink [{}]", tenantId, hasData, pageLink); |
184 | 189 | validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
185 | 190 | validatePageLink(pageLink); |
186 | - return firmwareInfoDao.findFirmwareInfoByTenantIdAndHasData(tenantId, hasData, pageLink); | |
191 | + return firmwareInfoDao.findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, firmwareType, hasData, pageLink); | |
187 | 192 | } |
188 | 193 | |
189 | 194 | @Override |
... | ... | @@ -201,6 +206,10 @@ public class BaseFirmwareService implements FirmwareService { |
201 | 206 | throw new DataValidationException("The firmware referenced by the devices cannot be deleted!"); |
202 | 207 | } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_firmware_device_profile")) { |
203 | 208 | throw new DataValidationException("The firmware referenced by the device profile cannot be deleted!"); |
209 | + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device")) { | |
210 | + throw new DataValidationException("The software referenced by the devices cannot be deleted!"); | |
211 | + } else if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_software_device_profile")) { | |
212 | + throw new DataValidationException("The software referenced by the device profile cannot be deleted!"); | |
204 | 213 | } else { |
205 | 214 | throw t; |
206 | 215 | } |
... | ... | @@ -217,29 +226,15 @@ public class BaseFirmwareService implements FirmwareService { |
217 | 226 | private DataValidator<FirmwareInfo> firmwareInfoValidator = new DataValidator<>() { |
218 | 227 | |
219 | 228 | @Override |
220 | - protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmware) { | |
221 | - if (firmware.getTenantId() == null) { | |
222 | - throw new DataValidationException("Firmware should be assigned to tenant!"); | |
223 | - } else { | |
224 | - Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId()); | |
225 | - if (tenant == null) { | |
226 | - throw new DataValidationException("Firmware is referencing to non-existent tenant!"); | |
227 | - } | |
228 | - } | |
229 | - | |
230 | - if (StringUtils.isEmpty(firmware.getTitle())) { | |
231 | - throw new DataValidationException("Firmware title should be specified!"); | |
232 | - } | |
233 | - | |
234 | - if (StringUtils.isEmpty(firmware.getVersion())) { | |
235 | - throw new DataValidationException("Firmware version should be specified!"); | |
236 | - } | |
229 | + protected void validateDataImpl(TenantId tenantId, FirmwareInfo firmwareInfo) { | |
230 | + validateImpl(firmwareInfo); | |
237 | 231 | } |
238 | 232 | |
239 | 233 | @Override |
240 | 234 | protected void validateUpdate(TenantId tenantId, FirmwareInfo firmware) { |
241 | 235 | FirmwareInfo firmwareOld = firmwareInfoDao.findById(tenantId, firmware.getUuidId()); |
242 | 236 | |
237 | + validateUpdateDeviceProfile(firmware, firmwareOld); | |
243 | 238 | BaseFirmwareService.validateUpdate(firmware, firmwareOld); |
244 | 239 | } |
245 | 240 | }; |
... | ... | @@ -248,22 +243,7 @@ public class BaseFirmwareService implements FirmwareService { |
248 | 243 | |
249 | 244 | @Override |
250 | 245 | protected void validateDataImpl(TenantId tenantId, Firmware firmware) { |
251 | - if (firmware.getTenantId() == null) { | |
252 | - throw new DataValidationException("Firmware should be assigned to tenant!"); | |
253 | - } else { | |
254 | - Tenant tenant = tenantDao.findById(firmware.getTenantId(), firmware.getTenantId().getId()); | |
255 | - if (tenant == null) { | |
256 | - throw new DataValidationException("Firmware is referencing to non-existent tenant!"); | |
257 | - } | |
258 | - } | |
259 | - | |
260 | - if (StringUtils.isEmpty(firmware.getTitle())) { | |
261 | - throw new DataValidationException("Firmware title should be specified!"); | |
262 | - } | |
263 | - | |
264 | - if (StringUtils.isEmpty(firmware.getVersion())) { | |
265 | - throw new DataValidationException("Firmware version should be specified!"); | |
266 | - } | |
246 | + validateImpl(firmware); | |
267 | 247 | |
268 | 248 | if (StringUtils.isEmpty(firmware.getFileName())) { |
269 | 249 | throw new DataValidationException("Firmware file name should be specified!"); |
... | ... | @@ -282,11 +262,7 @@ public class BaseFirmwareService implements FirmwareService { |
282 | 262 | |
283 | 263 | String currentChecksum; |
284 | 264 | |
285 | - try { | |
286 | - currentChecksum = generateChecksum(firmware.getChecksumAlgorithm(), firmware.getData()); | |
287 | - } catch (ThingsboardException e) { | |
288 | - throw new DataValidationException(e.getMessage()); | |
289 | - } | |
265 | + currentChecksum = generateChecksum(firmware.getChecksumAlgorithm(), firmware.getData()); | |
290 | 266 | |
291 | 267 | if (!currentChecksum.equals(firmware.getChecksum())) { |
292 | 268 | throw new DataValidationException("Wrong firmware file!"); |
... | ... | @@ -297,6 +273,7 @@ public class BaseFirmwareService implements FirmwareService { |
297 | 273 | protected void validateUpdate(TenantId tenantId, Firmware firmware) { |
298 | 274 | Firmware firmwareOld = firmwareDao.findById(tenantId, firmware.getUuidId()); |
299 | 275 | |
276 | + validateUpdateDeviceProfile(firmware, firmwareOld); | |
300 | 277 | BaseFirmwareService.validateUpdate(firmware, firmwareOld); |
301 | 278 | |
302 | 279 | if (firmwareOld.getData() != null && !firmwareOld.getData().equals(firmware.getData())) { |
... | ... | @@ -305,7 +282,19 @@ public class BaseFirmwareService implements FirmwareService { |
305 | 282 | } |
306 | 283 | }; |
307 | 284 | |
285 | + private void validateUpdateDeviceProfile(FirmwareInfo firmware, FirmwareInfo firmwareOld) { | |
286 | + if (firmwareOld.getDeviceProfileId() != null && !firmwareOld.getDeviceProfileId().equals(firmware.getDeviceProfileId())) { | |
287 | + if (firmwareInfoDao.isFirmwareUsed(firmwareOld.getId(), firmware.getType(), firmwareOld.getDeviceProfileId())) { | |
288 | + throw new DataValidationException("Can`t update deviceProfileId because firmware is already in use!"); | |
289 | + } | |
290 | + } | |
291 | + } | |
292 | + | |
308 | 293 | private static void validateUpdate(FirmwareInfo firmware, FirmwareInfo firmwareOld) { |
294 | + if (!firmwareOld.getType().equals(firmware.getType())) { | |
295 | + throw new DataValidationException("Updating type is prohibited!"); | |
296 | + } | |
297 | + | |
309 | 298 | if (!firmwareOld.getTitle().equals(firmware.getTitle())) { |
310 | 299 | throw new DataValidationException("Updating firmware title is prohibited!"); |
311 | 300 | } |
... | ... | @@ -335,6 +324,36 @@ public class BaseFirmwareService implements FirmwareService { |
335 | 324 | } |
336 | 325 | } |
337 | 326 | |
327 | + private void validateImpl(FirmwareInfo firmwareInfo) { | |
328 | + if (firmwareInfo.getTenantId() == null) { | |
329 | + throw new DataValidationException("Firmware should be assigned to tenant!"); | |
330 | + } else { | |
331 | + Tenant tenant = tenantDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getTenantId().getId()); | |
332 | + if (tenant == null) { | |
333 | + throw new DataValidationException("Firmware is referencing to non-existent tenant!"); | |
334 | + } | |
335 | + } | |
336 | + | |
337 | + if (firmwareInfo.getDeviceProfileId() != null) { | |
338 | + DeviceProfile deviceProfile = deviceProfileDao.findById(firmwareInfo.getTenantId(), firmwareInfo.getDeviceProfileId().getId()); | |
339 | + if (deviceProfile == null) { | |
340 | + throw new DataValidationException("Firmware is referencing to non-existent device profile!"); | |
341 | + } | |
342 | + } | |
343 | + | |
344 | + if (firmwareInfo.getType() == null) { | |
345 | + throw new DataValidationException("Type should be specified!"); | |
346 | + } | |
347 | + | |
348 | + if (StringUtils.isEmpty(firmwareInfo.getTitle())) { | |
349 | + throw new DataValidationException("Firmware title should be specified!"); | |
350 | + } | |
351 | + | |
352 | + if (StringUtils.isEmpty(firmwareInfo.getVersion())) { | |
353 | + throw new DataValidationException("Firmware version should be specified!"); | |
354 | + } | |
355 | + } | |
356 | + | |
338 | 357 | private PaginatedRemover<TenantId, FirmwareInfo> tenantFirmwareRemover = |
339 | 358 | new PaginatedRemover<>() { |
340 | 359 | ... | ... |
... | ... | @@ -16,6 +16,9 @@ |
16 | 16 | package org.thingsboard.server.dao.firmware; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.common.data.FirmwareInfo; |
19 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
20 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
21 | +import org.thingsboard.server.common.data.id.FirmwareId; | |
19 | 22 | import org.thingsboard.server.common.data.id.TenantId; |
20 | 23 | import org.thingsboard.server.common.data.page.PageData; |
21 | 24 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -27,6 +30,8 @@ public interface FirmwareInfoDao extends Dao<FirmwareInfo> { |
27 | 30 | |
28 | 31 | PageData<FirmwareInfo> findFirmwareInfoByTenantId(TenantId tenantId, PageLink pageLink); |
29 | 32 | |
30 | - PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink); | |
33 | + PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink); | |
34 | + | |
35 | + boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId); | |
31 | 36 | |
32 | 37 | } | ... | ... |
... | ... | @@ -154,6 +154,7 @@ public class ModelConstants { |
154 | 154 | public static final String DEVICE_DEVICE_PROFILE_ID_PROPERTY = "device_profile_id"; |
155 | 155 | public static final String DEVICE_DEVICE_DATA_PROPERTY = "device_data"; |
156 | 156 | public static final String DEVICE_FIRMWARE_ID_PROPERTY = "firmware_id"; |
157 | + public static final String DEVICE_SOFTWARE_ID_PROPERTY = "software_id"; | |
157 | 158 | |
158 | 159 | public static final String DEVICE_BY_TENANT_AND_SEARCH_TEXT_COLUMN_FAMILY_NAME = "device_by_tenant_and_search_text"; |
159 | 160 | 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 { |
178 | 179 | public static final String DEVICE_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY = "default_queue_name"; |
179 | 180 | public static final String DEVICE_PROFILE_PROVISION_DEVICE_KEY = "provision_device_key"; |
180 | 181 | public static final String DEVICE_PROFILE_FIRMWARE_ID_PROPERTY = "firmware_id"; |
182 | + public static final String DEVICE_PROFILE_SOFTWARE_ID_PROPERTY = "software_id"; | |
181 | 183 | |
182 | 184 | /** |
183 | 185 | * Cassandra entityView constants. |
... | ... | @@ -476,6 +478,8 @@ public class ModelConstants { |
476 | 478 | */ |
477 | 479 | public static final String FIRMWARE_TABLE_NAME = "firmware"; |
478 | 480 | public static final String FIRMWARE_TENANT_ID_COLUMN = TENANT_ID_COLUMN; |
481 | + public static final String FIRMWARE_DEVICE_PROFILE_ID_COLUMN = "device_profile_id"; | |
482 | + public static final String FIRMWARE_TYPE_COLUMN = "type"; | |
479 | 483 | public static final String FIRMWARE_TITLE_COLUMN = TITLE_PROPERTY; |
480 | 484 | public static final String FIRMWARE_VERSION_COLUMN = "version"; |
481 | 485 | public static final String FIRMWARE_FILE_NAME_COLUMN = "file_name"; | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import lombok.EqualsAndHashCode; |
22 | 22 | import org.hibernate.annotations.Type; |
23 | 23 | import org.hibernate.annotations.TypeDef; |
24 | 24 | import org.hibernate.annotations.TypeDefs; |
25 | +import org.thingsboard.common.util.JacksonUtil; | |
25 | 26 | import org.thingsboard.server.common.data.Device; |
26 | 27 | import org.thingsboard.server.common.data.device.data.DeviceData; |
27 | 28 | import org.thingsboard.server.common.data.id.CustomerId; |
... | ... | @@ -32,7 +33,6 @@ import org.thingsboard.server.common.data.id.TenantId; |
32 | 33 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
33 | 34 | import org.thingsboard.server.dao.model.ModelConstants; |
34 | 35 | import org.thingsboard.server.dao.model.SearchTextEntity; |
35 | -import org.thingsboard.common.util.JacksonUtil; | |
36 | 36 | import org.thingsboard.server.dao.util.mapping.JsonBinaryType; |
37 | 37 | import org.thingsboard.server.dao.util.mapping.JsonStringType; |
38 | 38 | |
... | ... | @@ -43,8 +43,8 @@ import java.util.UUID; |
43 | 43 | @Data |
44 | 44 | @EqualsAndHashCode(callSuper = true) |
45 | 45 | @TypeDefs({ |
46 | - @TypeDef(name = "json", typeClass = JsonStringType.class), | |
47 | - @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) | |
46 | + @TypeDef(name = "json", typeClass = JsonStringType.class), | |
47 | + @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) | |
48 | 48 | }) |
49 | 49 | @MappedSuperclass |
50 | 50 | 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 |
77 | 77 | @Column(name = ModelConstants.DEVICE_FIRMWARE_ID_PROPERTY, columnDefinition = "uuid") |
78 | 78 | private UUID firmwareId; |
79 | 79 | |
80 | + @Column(name = ModelConstants.DEVICE_SOFTWARE_ID_PROPERTY, columnDefinition = "uuid") | |
81 | + private UUID softwareId; | |
82 | + | |
80 | 83 | @Type(type = "jsonb") |
81 | 84 | @Column(name = ModelConstants.DEVICE_DEVICE_DATA_PROPERTY, columnDefinition = "jsonb") |
82 | 85 | private JsonNode deviceData; |
... | ... | @@ -102,6 +105,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti |
102 | 105 | if (device.getFirmwareId() != null) { |
103 | 106 | this.firmwareId = device.getFirmwareId().getId(); |
104 | 107 | } |
108 | + if (device.getSoftwareId() != null) { | |
109 | + this.softwareId = device.getSoftwareId().getId(); | |
110 | + } | |
105 | 111 | this.deviceData = JacksonUtil.convertValue(device.getDeviceData(), ObjectNode.class); |
106 | 112 | this.name = device.getName(); |
107 | 113 | this.type = device.getType(); |
... | ... | @@ -122,6 +128,7 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti |
122 | 128 | this.searchText = deviceEntity.getSearchText(); |
123 | 129 | this.additionalInfo = deviceEntity.getAdditionalInfo(); |
124 | 130 | this.firmwareId = deviceEntity.getFirmwareId(); |
131 | + this.softwareId = deviceEntity.getSoftwareId(); | |
125 | 132 | } |
126 | 133 | |
127 | 134 | @Override |
... | ... | @@ -149,6 +156,9 @@ public abstract class AbstractDeviceEntity<T extends Device> extends BaseSqlEnti |
149 | 156 | if (firmwareId != null) { |
150 | 157 | device.setFirmwareId(new FirmwareId(firmwareId)); |
151 | 158 | } |
159 | + if (softwareId != null) { | |
160 | + device.setSoftwareId(new FirmwareId(softwareId)); | |
161 | + } | |
152 | 162 | device.setDeviceData(JacksonUtil.convertValue(deviceData, DeviceData.class)); |
153 | 163 | device.setName(name); |
154 | 164 | device.setType(type); | ... | ... |
... | ... | @@ -21,9 +21,10 @@ import lombok.Data; |
21 | 21 | import lombok.EqualsAndHashCode; |
22 | 22 | import org.hibernate.annotations.Type; |
23 | 23 | import org.hibernate.annotations.TypeDef; |
24 | +import org.thingsboard.common.util.JacksonUtil; | |
24 | 25 | import org.thingsboard.server.common.data.DeviceProfile; |
25 | -import org.thingsboard.server.common.data.DeviceProfileType; | |
26 | 26 | import org.thingsboard.server.common.data.DeviceProfileProvisionType; |
27 | +import org.thingsboard.server.common.data.DeviceProfileType; | |
27 | 28 | import org.thingsboard.server.common.data.DeviceTransportType; |
28 | 29 | import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
29 | 30 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
... | ... | @@ -33,7 +34,6 @@ import org.thingsboard.server.common.data.id.TenantId; |
33 | 34 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
34 | 35 | import org.thingsboard.server.dao.model.ModelConstants; |
35 | 36 | import org.thingsboard.server.dao.model.SearchTextEntity; |
36 | -import org.thingsboard.common.util.JacksonUtil; | |
37 | 37 | import org.thingsboard.server.dao.util.mapping.JsonBinaryType; |
38 | 38 | |
39 | 39 | import javax.persistence.Column; |
... | ... | @@ -87,12 +87,15 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl |
87 | 87 | @Column(name = ModelConstants.DEVICE_PROFILE_PROFILE_DATA_PROPERTY, columnDefinition = "jsonb") |
88 | 88 | private JsonNode profileData; |
89 | 89 | |
90 | - @Column(name=ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY) | |
90 | + @Column(name = ModelConstants.DEVICE_PROFILE_PROVISION_DEVICE_KEY) | |
91 | 91 | private String provisionDeviceKey; |
92 | 92 | |
93 | - @Column(name=ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY) | |
93 | + @Column(name = ModelConstants.DEVICE_PROFILE_FIRMWARE_ID_PROPERTY) | |
94 | 94 | private UUID firmwareId; |
95 | 95 | |
96 | + @Column(name = ModelConstants.DEVICE_PROFILE_SOFTWARE_ID_PROPERTY) | |
97 | + private UUID softwareId; | |
98 | + | |
96 | 99 | public DeviceProfileEntity() { |
97 | 100 | super(); |
98 | 101 | } |
... | ... | @@ -120,6 +123,9 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl |
120 | 123 | if (deviceProfile.getFirmwareId() != null) { |
121 | 124 | this.firmwareId = deviceProfile.getFirmwareId().getId(); |
122 | 125 | } |
126 | + if (deviceProfile.getSoftwareId() != null) { | |
127 | + this.firmwareId = deviceProfile.getSoftwareId().getId(); | |
128 | + } | |
123 | 129 | } |
124 | 130 | |
125 | 131 | @Override |
... | ... | @@ -160,6 +166,10 @@ public final class DeviceProfileEntity extends BaseSqlEntity<DeviceProfile> impl |
160 | 166 | deviceProfile.setFirmwareId(new FirmwareId(firmwareId)); |
161 | 167 | } |
162 | 168 | |
169 | + if (softwareId != null) { | |
170 | + deviceProfile.setSoftwareId(new FirmwareId(softwareId)); | |
171 | + } | |
172 | + | |
163 | 173 | return deviceProfile; |
164 | 174 | } |
165 | 175 | } | ... | ... |
... | ... | @@ -22,6 +22,8 @@ import org.hibernate.annotations.Type; |
22 | 22 | import org.hibernate.annotations.TypeDef; |
23 | 23 | import org.thingsboard.server.common.data.Firmware; |
24 | 24 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
25 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
26 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
25 | 27 | import org.thingsboard.server.common.data.id.FirmwareId; |
26 | 28 | import org.thingsboard.server.common.data.id.TenantId; |
27 | 29 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
... | ... | @@ -43,10 +45,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ |
43 | 45 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; |
44 | 46 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; |
45 | 47 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; |
48 | +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN; | |
46 | 49 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; |
47 | 50 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; |
48 | 51 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; |
49 | 52 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; |
53 | +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN; | |
50 | 54 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; |
51 | 55 | import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; |
52 | 56 | |
... | ... | @@ -60,6 +64,13 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex |
60 | 64 | @Column(name = FIRMWARE_TENANT_ID_COLUMN) |
61 | 65 | private UUID tenantId; |
62 | 66 | |
67 | + @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN) | |
68 | + private UUID deviceProfileId; | |
69 | + | |
70 | + @Enumerated(EnumType.STRING) | |
71 | + @Column(name = FIRMWARE_TYPE_COLUMN) | |
72 | + private FirmwareType type; | |
73 | + | |
63 | 74 | @Column(name = FIRMWARE_TITLE_COLUMN) |
64 | 75 | private String title; |
65 | 76 | |
... | ... | @@ -101,6 +112,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex |
101 | 112 | this.createdTime = firmware.getCreatedTime(); |
102 | 113 | this.setUuid(firmware.getUuidId()); |
103 | 114 | this.tenantId = firmware.getTenantId().getId(); |
115 | + if (firmware.getDeviceProfileId() != null) { | |
116 | + this.deviceProfileId = firmware.getDeviceProfileId().getId(); | |
117 | + } | |
118 | + this.type = firmware.getType(); | |
104 | 119 | this.title = firmware.getTitle(); |
105 | 120 | this.version = firmware.getVersion(); |
106 | 121 | this.fileName = firmware.getFileName(); |
... | ... | @@ -127,6 +142,10 @@ public class FirmwareEntity extends BaseSqlEntity<Firmware> implements SearchTex |
127 | 142 | Firmware firmware = new Firmware(new FirmwareId(id)); |
128 | 143 | firmware.setCreatedTime(createdTime); |
129 | 144 | firmware.setTenantId(new TenantId(tenantId)); |
145 | + if (deviceProfileId != null) { | |
146 | + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); | |
147 | + } | |
148 | + firmware.setType(type); | |
130 | 149 | firmware.setTitle(title); |
131 | 150 | firmware.setVersion(version); |
132 | 151 | firmware.setFileName(fileName); | ... | ... |
... | ... | @@ -23,6 +23,8 @@ import org.hibernate.annotations.TypeDef; |
23 | 23 | import org.thingsboard.common.util.JacksonUtil; |
24 | 24 | import org.thingsboard.server.common.data.FirmwareInfo; |
25 | 25 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
26 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
27 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
26 | 28 | import org.thingsboard.server.common.data.id.FirmwareId; |
27 | 29 | import org.thingsboard.server.common.data.id.TenantId; |
28 | 30 | import org.thingsboard.server.dao.model.BaseSqlEntity; |
... | ... | @@ -41,13 +43,13 @@ import java.util.UUID; |
41 | 43 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_ALGORITHM_COLUMN; |
42 | 44 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CHECKSUM_COLUMN; |
43 | 45 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_CONTENT_TYPE_COLUMN; |
44 | -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_COLUMN; | |
45 | 46 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DATA_SIZE_COLUMN; |
47 | +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_DEVICE_PROFILE_ID_COLUMN; | |
46 | 48 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_FILE_NAME_COLUMN; |
47 | -import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_HAS_DATA_PROPERTY; | |
48 | 49 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TABLE_NAME; |
49 | 50 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TENANT_ID_COLUMN; |
50 | 51 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TITLE_COLUMN; |
52 | +import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_TYPE_COLUMN; | |
51 | 53 | import static org.thingsboard.server.dao.model.ModelConstants.FIRMWARE_VERSION_COLUMN; |
52 | 54 | import static org.thingsboard.server.dao.model.ModelConstants.SEARCH_TEXT_PROPERTY; |
53 | 55 | |
... | ... | @@ -61,6 +63,13 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S |
61 | 63 | @Column(name = FIRMWARE_TENANT_ID_COLUMN) |
62 | 64 | private UUID tenantId; |
63 | 65 | |
66 | + @Column(name = FIRMWARE_DEVICE_PROFILE_ID_COLUMN) | |
67 | + private UUID deviceProfileId; | |
68 | + | |
69 | + @Enumerated(EnumType.STRING) | |
70 | + @Column(name = FIRMWARE_TYPE_COLUMN) | |
71 | + private FirmwareType type; | |
72 | + | |
64 | 73 | @Column(name = FIRMWARE_TITLE_COLUMN) |
65 | 74 | private String title; |
66 | 75 | |
... | ... | @@ -101,6 +110,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S |
101 | 110 | this.createdTime = firmware.getCreatedTime(); |
102 | 111 | this.setUuid(firmware.getUuidId()); |
103 | 112 | this.tenantId = firmware.getTenantId().getId(); |
113 | + this.type = firmware.getType(); | |
114 | + if (firmware.getDeviceProfileId() != null) { | |
115 | + this.deviceProfileId = firmware.getDeviceProfileId().getId(); | |
116 | + } | |
104 | 117 | this.title = firmware.getTitle(); |
105 | 118 | this.version = firmware.getVersion(); |
106 | 119 | this.fileName = firmware.getFileName(); |
... | ... | @@ -111,12 +124,14 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S |
111 | 124 | this.additionalInfo = firmware.getAdditionalInfo(); |
112 | 125 | } |
113 | 126 | |
114 | - public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, String title, String version, | |
127 | + public FirmwareInfoEntity(UUID id, long createdTime, UUID tenantId, UUID deviceProfileId, FirmwareType type, String title, String version, | |
115 | 128 | String fileName, String contentType, ChecksumAlgorithm checksumAlgorithm, String checksum, Long dataSize, |
116 | 129 | Object additionalInfo, boolean hasData) { |
117 | 130 | this.id = id; |
118 | 131 | this.createdTime = createdTime; |
119 | 132 | this.tenantId = tenantId; |
133 | + this.deviceProfileId = deviceProfileId; | |
134 | + this.type = type; | |
120 | 135 | this.title = title; |
121 | 136 | this.version = version; |
122 | 137 | this.fileName = fileName; |
... | ... | @@ -143,6 +158,10 @@ public class FirmwareInfoEntity extends BaseSqlEntity<FirmwareInfo> implements S |
143 | 158 | FirmwareInfo firmware = new FirmwareInfo(new FirmwareId(id)); |
144 | 159 | firmware.setCreatedTime(createdTime); |
145 | 160 | firmware.setTenantId(new TenantId(tenantId)); |
161 | + if (deviceProfileId != null) { | |
162 | + firmware.setDeviceProfileId(new DeviceProfileId(deviceProfileId)); | |
163 | + } | |
164 | + firmware.setType(type); | |
146 | 165 | firmware.setTitle(title); |
147 | 166 | firmware.setVersion(version); |
148 | 167 | firmware.setFileName(fileName); | ... | ... |
... | ... | @@ -104,6 +104,15 @@ public interface DeviceRepository extends PagingAndSortingRepository<DeviceEntit |
104 | 104 | @Param("textSearch") String textSearch, |
105 | 105 | Pageable pageable); |
106 | 106 | |
107 | + @Query("SELECT d FROM DeviceEntity d WHERE d.tenantId = :tenantId " + | |
108 | + "AND d.type = :type " + | |
109 | + "AND d.softwareId = null " + | |
110 | + "AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))") | |
111 | + Page<DeviceEntity> findByTenantIdAndTypeAndSoftwareIdIsNull(@Param("tenantId") UUID tenantId, | |
112 | + @Param("type") String type, | |
113 | + @Param("textSearch") String textSearch, | |
114 | + Pageable pageable); | |
115 | + | |
107 | 116 | @Query("SELECT new org.thingsboard.server.dao.model.sql.DeviceInfoEntity(d, c.title, c.additionalInfo, p.name) " + |
108 | 117 | "FROM DeviceEntity d " + |
109 | 118 | "LEFT JOIN CustomerEntity c on c.id = d.customerId " + | ... | ... |
... | ... | @@ -165,6 +165,16 @@ public class JpaDeviceDao extends JpaAbstractSearchTextDao<DeviceEntity, Device> |
165 | 165 | } |
166 | 166 | |
167 | 167 | @Override |
168 | + public PageData<Device> findDevicesByTenantIdAndTypeAndEmptySoftware(UUID tenantId, String type, PageLink pageLink) { | |
169 | + return DaoUtil.toPageData( | |
170 | + deviceRepository.findByTenantIdAndTypeAndSoftwareIdIsNull( | |
171 | + tenantId, | |
172 | + type, | |
173 | + Objects.toString(pageLink.getTextSearch(), ""), | |
174 | + DaoUtil.toPageable(pageLink))); | |
175 | + } | |
176 | + | |
177 | + @Override | |
168 | 178 | public PageData<DeviceInfo> findDeviceInfosByTenantIdAndType(UUID tenantId, String type, PageLink pageLink) { |
169 | 179 | return DaoUtil.toPageData( |
170 | 180 | deviceRepository.findDeviceInfosByTenantIdAndType( | ... | ... |
... | ... | @@ -20,27 +20,41 @@ import org.springframework.data.domain.Pageable; |
20 | 20 | import org.springframework.data.jpa.repository.Query; |
21 | 21 | import org.springframework.data.repository.CrudRepository; |
22 | 22 | import org.springframework.data.repository.query.Param; |
23 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
23 | 24 | import org.thingsboard.server.dao.model.sql.FirmwareInfoEntity; |
24 | 25 | |
25 | 26 | import java.util.UUID; |
26 | 27 | |
27 | 28 | public interface FirmwareInfoRepository extends CrudRepository<FirmwareInfoEntity, UUID> { |
28 | - @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 " + | |
29 | + @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 " + | |
29 | 30 | "f.tenantId = :tenantId " + |
30 | 31 | "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") |
31 | 32 | Page<FirmwareInfoEntity> findAllByTenantId(@Param("tenantId") UUID tenantId, |
32 | 33 | @Param("searchText") String searchText, |
33 | 34 | Pageable pageable); |
34 | 35 | |
35 | - @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 " + | |
36 | + @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 " + | |
36 | 37 | "f.tenantId = :tenantId " + |
38 | + "AND f.deviceProfileId = :deviceProfileId " + | |
39 | + "AND f.type = :type " + | |
37 | 40 | "AND ((f.data IS NOT NULL AND :hasData = true) OR (f.data IS NULL AND :hasData = false ))" + |
38 | 41 | "AND LOWER(f.searchText) LIKE LOWER(CONCAT(:searchText, '%'))") |
39 | - Page<FirmwareInfoEntity> findAllByTenantIdAndHasData(@Param("tenantId") UUID tenantId, | |
40 | - @Param("hasData") boolean hasData, | |
41 | - @Param("searchText") String searchText, | |
42 | - Pageable pageable); | |
42 | + Page<FirmwareInfoEntity> findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData(@Param("tenantId") UUID tenantId, | |
43 | + @Param("deviceProfileId") UUID deviceProfileId, | |
44 | + @Param("type") FirmwareType type, | |
45 | + @Param("hasData") boolean hasData, | |
46 | + @Param("searchText") String searchText, | |
47 | + Pageable pageable); | |
43 | 48 | |
44 | - @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") | |
49 | + @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") | |
45 | 50 | FirmwareInfoEntity findFirmwareInfoById(@Param("id") UUID id); |
51 | + | |
52 | + @Query(value = "SELECT exists(SELECT * " + | |
53 | + "FROM device_profile AS dp " + | |
54 | + "LEFT JOIN device AS d ON dp.id = d.device_profile_id " + | |
55 | + "WHERE dp.id = :deviceProfileId AND " + | |
56 | + "(('FIRMWARE' = :type AND (dp.firmware_id = :firmwareId OR d.firmware_id = :firmwareId)) " + | |
57 | + "OR ('SOFTWARE' = :type AND (dp.software_id = :firmwareId or d.software_id = :firmwareId))))", nativeQuery = true) | |
58 | + boolean isFirmwareUsed(@Param("firmwareId") UUID firmwareId, @Param("deviceProfileId") UUID deviceProfileId, @Param("type") String type); | |
59 | + | |
46 | 60 | } | ... | ... |
... | ... | @@ -20,6 +20,9 @@ import org.springframework.beans.factory.annotation.Autowired; |
20 | 20 | import org.springframework.data.repository.CrudRepository; |
21 | 21 | import org.springframework.stereotype.Component; |
22 | 22 | import org.thingsboard.server.common.data.FirmwareInfo; |
23 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
24 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
25 | +import org.thingsboard.server.common.data.id.FirmwareId; | |
23 | 26 | import org.thingsboard.server.common.data.id.TenantId; |
24 | 27 | import org.thingsboard.server.common.data.page.PageData; |
25 | 28 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -73,12 +76,19 @@ public class JpaFirmwareInfoDao extends JpaAbstractSearchTextDao<FirmwareInfoEnt |
73 | 76 | } |
74 | 77 | |
75 | 78 | @Override |
76 | - public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndHasData(TenantId tenantId, boolean hasData, PageLink pageLink) { | |
79 | + public PageData<FirmwareInfo> findFirmwareInfoByTenantIdAndDeviceProfileIdAndTypeAndHasData(TenantId tenantId, DeviceProfileId deviceProfileId, FirmwareType firmwareType, boolean hasData, PageLink pageLink) { | |
77 | 80 | return DaoUtil.toPageData(firmwareInfoRepository |
78 | - .findAllByTenantIdAndHasData( | |
81 | + .findAllByTenantIdAndTypeAndDeviceProfileIdAndHasData( | |
79 | 82 | tenantId.getId(), |
83 | + deviceProfileId.getId(), | |
84 | + firmwareType, | |
80 | 85 | hasData, |
81 | 86 | Objects.toString(pageLink.getTextSearch(), ""), |
82 | 87 | DaoUtil.toPageable(pageLink))); |
83 | 88 | } |
89 | + | |
90 | + @Override | |
91 | + public boolean isFirmwareUsed(FirmwareId firmwareId, FirmwareType type, DeviceProfileId deviceProfileId) { | |
92 | + return firmwareInfoRepository.isFirmwareUsed(firmwareId.getId(), deviceProfileId.getId(), type.name()); | |
93 | + } | |
84 | 94 | } | ... | ... |
... | ... | @@ -17,12 +17,8 @@ package org.thingsboard.server.dao.sql.query; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.apache.commons.lang3.StringUtils; |
20 | -import org.springframework.beans.factory.annotation.Autowired; | |
21 | -import org.springframework.beans.factory.annotation.Value; | |
22 | 20 | import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; |
23 | 21 | import org.springframework.stereotype.Repository; |
24 | -import org.springframework.transaction.TransactionStatus; | |
25 | -import org.springframework.transaction.support.TransactionCallback; | |
26 | 22 | import org.springframework.transaction.support.TransactionTemplate; |
27 | 23 | import org.thingsboard.server.common.data.EntityType; |
28 | 24 | import org.thingsboard.server.common.data.alarm.AlarmSearchStatus; |
... | ... | @@ -40,8 +36,6 @@ import org.thingsboard.server.common.data.query.EntityKey; |
40 | 36 | import org.thingsboard.server.common.data.query.EntityKeyType; |
41 | 37 | import org.thingsboard.server.dao.model.ModelConstants; |
42 | 38 | |
43 | -import java.util.ArrayList; | |
44 | -import java.util.Arrays; | |
45 | 39 | import java.util.Collection; |
46 | 40 | import java.util.Collections; |
47 | 41 | import java.util.HashMap; |
... | ... | @@ -134,6 +128,7 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { |
134 | 128 | StringBuilder fromPart = new StringBuilder(" from alarm a "); |
135 | 129 | StringBuilder wherePart = new StringBuilder(" where "); |
136 | 130 | StringBuilder sortPart = new StringBuilder(" order by "); |
131 | + StringBuilder joinPart = new StringBuilder(); | |
137 | 132 | boolean addAnd = false; |
138 | 133 | if (pageLink.isSearchPropagatedAlarms()) { |
139 | 134 | selectPart.append(" CASE WHEN r.from_id IS NULL THEN a.originator_id ELSE r.from_id END as entity_id "); |
... | ... | @@ -156,23 +151,23 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { |
156 | 151 | wherePart.append(" a.originator_id in (:entity_ids)"); |
157 | 152 | } |
158 | 153 | } else { |
159 | - fromPart.append(" inner join (select * from (VALUES"); | |
154 | + joinPart.append(" inner join (select * from (VALUES"); | |
160 | 155 | int entityIdIdx = 0; |
161 | 156 | int lastEntityIdIdx = orderedEntityIds.size() - 1; |
162 | 157 | for (EntityId entityId : orderedEntityIds) { |
163 | - fromPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")"); | |
158 | + joinPart.append("(uuid('").append(entityId.getId().toString()).append("'), ").append(entityIdIdx).append(")"); | |
164 | 159 | if (entityIdIdx != lastEntityIdIdx) { |
165 | - fromPart.append(","); | |
160 | + joinPart.append(","); | |
166 | 161 | } else { |
167 | - fromPart.append(")"); | |
162 | + joinPart.append(")"); | |
168 | 163 | } |
169 | 164 | entityIdIdx++; |
170 | 165 | } |
171 | - fromPart.append(" as e(id, priority)) e "); | |
166 | + joinPart.append(" as e(id, priority)) e "); | |
172 | 167 | if (pageLink.isSearchPropagatedAlarms()) { |
173 | - fromPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)"); | |
168 | + joinPart.append("on (r.from_id IS NULL and a.originator_id = e.id) or (r.from_id IS NOT NULL and r.from_id = e.id)"); | |
174 | 169 | } else { |
175 | - fromPart.append("on a.originator_id = e.id"); | |
170 | + joinPart.append("on a.originator_id = e.id"); | |
176 | 171 | } |
177 | 172 | sortPart.append("e.priority"); |
178 | 173 | } |
... | ... | @@ -226,9 +221,12 @@ public class DefaultAlarmQueryRepository implements AlarmQueryRepository { |
226 | 221 | } |
227 | 222 | |
228 | 223 | String textSearchQuery = buildTextSearchQuery(ctx, query.getAlarmFields(), pageLink.getTextSearch()); |
229 | - String mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString(); | |
224 | + String mainQuery; | |
230 | 225 | if (!textSearchQuery.isEmpty()) { |
231 | - mainQuery = String.format("select * from (%s) a WHERE %s", mainQuery, textSearchQuery); | |
226 | + mainQuery = selectPart.toString() + fromPart.toString() + wherePart.toString(); | |
227 | + mainQuery = String.format("select * from (%s) a %s WHERE %s", mainQuery, joinPart, textSearchQuery); | |
228 | + } else { | |
229 | + mainQuery = selectPart.toString() + fromPart.toString() + joinPart.toString() + wherePart.toString(); | |
232 | 230 | } |
233 | 231 | String countQuery = String.format("select count(*) from (%s) result", mainQuery); |
234 | 232 | long queryTs = System.currentTimeMillis(); | ... | ... |
... | ... | @@ -243,23 +243,21 @@ public class EntityKeyMapping { |
243 | 243 | } else { |
244 | 244 | entityTypeStr = "'" + entityType.name() + "'"; |
245 | 245 | } |
246 | - ctx.addStringParameter(alias + "_key_id", entityKey.getKey()); | |
246 | + ctx.addStringParameter(getKeyId(), entityKey.getKey()); | |
247 | 247 | String filterQuery = toQueries(ctx, entityFilter.getType()) |
248 | 248 | .filter(StringUtils::isNotEmpty) |
249 | 249 | .collect(Collectors.joining(" and ")); |
250 | - if (StringUtils.isEmpty(filterQuery)) { | |
251 | - filterQuery = ""; | |
252 | - } else { | |
250 | + if (StringUtils.isNotEmpty(filterQuery)) { | |
253 | 251 | filterQuery = " AND (" + filterQuery + ")"; |
254 | 252 | } |
255 | 253 | if (entityKey.getType().equals(EntityKeyType.TIME_SERIES)) { |
256 | - String join = hasFilter() ? "inner join" : "left join"; | |
254 | + String join = (hasFilter() && hasFilterValues(ctx)) ? "inner join" : "left join"; | |
257 | 255 | return String.format("%s ts_kv_latest %s ON %s.entity_id=entities.id AND %s.key = (select key_id from ts_kv_dictionary where key = :%s_key_id) %s", |
258 | 256 | join, alias, alias, alias, alias, filterQuery); |
259 | 257 | } else { |
260 | 258 | String query; |
261 | 259 | if (!entityKey.getType().equals(EntityKeyType.ATTRIBUTE)) { |
262 | - String join = hasFilter() ? "inner join" : "left join"; | |
260 | + String join = (hasFilter() && hasFilterValues(ctx)) ? "inner join" : "left join"; | |
263 | 261 | query = String.format("%s attribute_kv %s ON %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key=:%s_key_id ", |
264 | 262 | join, alias, alias, alias, entityTypeStr, alias, alias); |
265 | 263 | String scope; |
... | ... | @@ -272,7 +270,7 @@ public class EntityKeyMapping { |
272 | 270 | } |
273 | 271 | query = String.format("%s AND %s.attribute_type='%s' %s", query, alias, scope, filterQuery); |
274 | 272 | } else { |
275 | - String join = hasFilter() ? "join LATERAL" : "left join LATERAL"; | |
273 | + String join = (hasFilter() && hasFilterValues(ctx)) ? "join LATERAL" : "left join LATERAL"; | |
276 | 274 | query = String.format("%s (select * from attribute_kv %s WHERE %s.entity_id=entities.id AND %s.entity_type=%s AND %s.attribute_key=:%s_key_id %s " + |
277 | 275 | "ORDER BY %s.last_update_ts DESC limit 1) as %s ON true", |
278 | 276 | join, alias, alias, alias, entityTypeStr, alias, alias, filterQuery, alias, alias); |
... | ... | @@ -281,15 +279,26 @@ public class EntityKeyMapping { |
281 | 279 | } |
282 | 280 | } |
283 | 281 | |
282 | + private boolean hasFilterValues(QueryContext ctx) { | |
283 | + return Arrays.stream(ctx.getParameterNames()).anyMatch(parameterName -> { | |
284 | + return !parameterName.equals(getKeyId()) && parameterName.startsWith(alias); | |
285 | + }); | |
286 | + } | |
287 | + | |
288 | + private String getKeyId() { | |
289 | + return alias + "_key_id"; | |
290 | + } | |
291 | + | |
284 | 292 | public static String buildSelections(List<EntityKeyMapping> mappings, EntityFilterType filterType, EntityType entityType) { |
285 | 293 | return mappings.stream().map(mapping -> mapping.toSelection(filterType, entityType)).collect( |
286 | 294 | Collectors.joining(", ")); |
287 | 295 | } |
288 | 296 | |
289 | 297 | public static String buildLatestJoins(QueryContext ctx, EntityFilter entityFilter, EntityType entityType, List<EntityKeyMapping> latestMappings, boolean countQuery) { |
290 | - return latestMappings.stream().filter(mapping -> !countQuery || mapping.hasFilter()) | |
291 | - .map(mapping -> mapping.toLatestJoin(ctx, entityFilter, entityType)).collect( | |
292 | - Collectors.joining(" ")); | |
298 | + return latestMappings.stream() | |
299 | + .filter(mapping -> !countQuery || mapping.hasFilter()) | |
300 | + .map(mapping -> mapping.toLatestJoin(ctx, entityFilter, entityType)) | |
301 | + .collect(Collectors.joining(" ")); | |
293 | 302 | } |
294 | 303 | |
295 | 304 | public static String buildQuery(QueryContext ctx, List<EntityKeyMapping> mappings, EntityFilterType filterType) { | ... | ... |
... | ... | @@ -28,6 +28,7 @@ import com.google.common.util.concurrent.Futures; |
28 | 28 | import com.google.common.util.concurrent.ListenableFuture; |
29 | 29 | import com.google.common.util.concurrent.SettableFuture; |
30 | 30 | import lombok.extern.slf4j.Slf4j; |
31 | +import org.thingsboard.common.util.ThingsBoardExecutors; | |
31 | 32 | import org.thingsboard.common.util.ThingsBoardThreadFactory; |
32 | 33 | import org.thingsboard.server.common.data.id.TenantId; |
33 | 34 | import org.thingsboard.server.common.stats.StatsFactory; |
... | ... | @@ -82,7 +83,7 @@ public abstract class AbstractBufferedRateExecutor<T extends AsyncTask, F extend |
82 | 83 | this.printQueriesFreq = printQueriesFreq; |
83 | 84 | this.queue = new LinkedBlockingDeque<>(queueLimit); |
84 | 85 | this.dispatcherExecutor = Executors.newFixedThreadPool(dispatcherThreads, ThingsBoardThreadFactory.forName("nosql-dispatcher")); |
85 | - this.callbackExecutor = Executors.newWorkStealingPool(callbackThreads); | |
86 | + this.callbackExecutor = ThingsBoardExecutors.newWorkStealingPool(callbackThreads, getClass()); | |
86 | 87 | this.timeoutExecutor = Executors.newSingleThreadScheduledExecutor(ThingsBoardThreadFactory.forName("nosql-timeout")); |
87 | 88 | this.perTenantLimitsEnabled = perTenantLimitsEnabled; |
88 | 89 | this.perTenantLimitsConfiguration = perTenantLimitsConfiguration; | ... | ... |
... | ... | @@ -162,6 +162,8 @@ CREATE TABLE IF NOT EXISTS firmware ( |
162 | 162 | id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, |
163 | 163 | created_time bigint NOT NULL, |
164 | 164 | tenant_id uuid NOT NULL, |
165 | + device_profile_id uuid , | |
166 | + type varchar(32) NOT NULL, | |
165 | 167 | title varchar(255) NOT NULL, |
166 | 168 | version varchar(255) NOT NULL, |
167 | 169 | file_name varchar(255), |
... | ... | @@ -188,13 +190,15 @@ CREATE TABLE IF NOT EXISTS device_profile ( |
188 | 190 | is_default boolean, |
189 | 191 | tenant_id uuid, |
190 | 192 | firmware_id uuid, |
193 | + software_id uuid, | |
191 | 194 | default_rule_chain_id uuid, |
192 | 195 | default_queue_name varchar(255), |
193 | 196 | provision_device_key varchar, |
194 | 197 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), |
195 | 198 | CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), |
196 | 199 | CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), |
197 | - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) | |
200 | + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id), | |
201 | + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id) | |
198 | 202 | ); |
199 | 203 | |
200 | 204 | CREATE TABLE IF NOT EXISTS device ( |
... | ... | @@ -210,9 +214,11 @@ CREATE TABLE IF NOT EXISTS device ( |
210 | 214 | search_text varchar(255), |
211 | 215 | tenant_id uuid, |
212 | 216 | firmware_id uuid, |
217 | + software_id uuid, | |
213 | 218 | CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), |
214 | 219 | CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), |
215 | - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) | |
220 | + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id), | |
221 | + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id) | |
216 | 222 | ); |
217 | 223 | |
218 | 224 | CREATE TABLE IF NOT EXISTS device_credentials ( | ... | ... |
... | ... | @@ -45,3 +45,4 @@ CREATE INDEX IF NOT EXISTS idx_asset_type ON asset(tenant_id, type); |
45 | 45 | 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); |
46 | 46 | |
47 | 47 | CREATE INDEX IF NOT EXISTS idx_audit_log_tenant_id_and_created_time ON audit_log(tenant_id, created_time); |
48 | + | ... | ... |
... | ... | @@ -180,6 +180,8 @@ CREATE TABLE IF NOT EXISTS firmware ( |
180 | 180 | id uuid NOT NULL CONSTRAINT firmware_pkey PRIMARY KEY, |
181 | 181 | created_time bigint NOT NULL, |
182 | 182 | tenant_id uuid NOT NULL, |
183 | + device_profile_id uuid , | |
184 | + type varchar(32) NOT NULL, | |
183 | 185 | title varchar(255) NOT NULL, |
184 | 186 | version varchar(255) NOT NULL, |
185 | 187 | file_name varchar(255), |
... | ... | @@ -191,6 +193,7 @@ CREATE TABLE IF NOT EXISTS firmware ( |
191 | 193 | additional_info varchar, |
192 | 194 | search_text varchar(255), |
193 | 195 | CONSTRAINT firmware_tenant_title_version_unq_key UNIQUE (tenant_id, title, version) |
196 | +-- CONSTRAINT fk_device_profile_firmware FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE | |
194 | 197 | ); |
195 | 198 | |
196 | 199 | CREATE TABLE IF NOT EXISTS device_profile ( |
... | ... | @@ -206,15 +209,26 @@ CREATE TABLE IF NOT EXISTS device_profile ( |
206 | 209 | is_default boolean, |
207 | 210 | tenant_id uuid, |
208 | 211 | firmware_id uuid, |
212 | + software_id uuid, | |
209 | 213 | default_rule_chain_id uuid, |
210 | 214 | default_queue_name varchar(255), |
211 | 215 | provision_device_key varchar, |
212 | 216 | CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), |
213 | 217 | CONSTRAINT device_provision_key_unq_key UNIQUE (provision_device_key), |
214 | 218 | CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), |
215 | - CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id) | |
219 | + CONSTRAINT fk_firmware_device_profile FOREIGN KEY (firmware_id) REFERENCES firmware(id), | |
220 | + CONSTRAINT fk_software_device_profile FOREIGN KEY (software_id) REFERENCES firmware(id) | |
216 | 221 | ); |
217 | 222 | |
223 | +-- We will use one-to-many relation in the first release and extend this feature in case of user requests | |
224 | +-- CREATE TABLE IF NOT EXISTS device_profile_firmware ( | |
225 | +-- device_profile_id uuid NOT NULL, | |
226 | +-- firmware_id uuid NOT NULL, | |
227 | +-- CONSTRAINT device_profile_firmware_unq_key UNIQUE (device_profile_id, firmware_id), | |
228 | +-- CONSTRAINT fk_device_profile_firmware_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id) ON DELETE CASCADE, | |
229 | +-- CONSTRAINT fk_device_profile_firmware_firmware FOREIGN KEY (firmware_id) REFERENCES firmware(id) ON DELETE CASCADE, | |
230 | +-- ); | |
231 | + | |
218 | 232 | CREATE TABLE IF NOT EXISTS device ( |
219 | 233 | id uuid NOT NULL CONSTRAINT device_pkey PRIMARY KEY, |
220 | 234 | created_time bigint NOT NULL, |
... | ... | @@ -228,9 +242,11 @@ CREATE TABLE IF NOT EXISTS device ( |
228 | 242 | search_text varchar(255), |
229 | 243 | tenant_id uuid, |
230 | 244 | firmware_id uuid, |
245 | + software_id uuid, | |
231 | 246 | CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name), |
232 | 247 | CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id), |
233 | - CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id) | |
248 | + CONSTRAINT fk_firmware_device FOREIGN KEY (firmware_id) REFERENCES firmware(id), | |
249 | + CONSTRAINT fk_software_device FOREIGN KEY (software_id) REFERENCES firmware(id) | |
234 | 250 | ); |
235 | 251 | |
236 | 252 | CREATE TABLE IF NOT EXISTS device_credentials ( | ... | ... |
... | ... | @@ -30,6 +30,7 @@ import org.thingsboard.server.common.data.DeviceProfileInfo; |
30 | 30 | import org.thingsboard.server.common.data.DeviceTransportType; |
31 | 31 | import org.thingsboard.server.common.data.Firmware; |
32 | 32 | import org.thingsboard.server.common.data.Tenant; |
33 | +import org.thingsboard.server.common.data.firmware.FirmwareType; | |
33 | 34 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
34 | 35 | import org.thingsboard.server.common.data.id.TenantId; |
35 | 36 | import org.thingsboard.server.common.data.page.PageData; |
... | ... | @@ -44,6 +45,8 @@ import java.util.concurrent.ExecutionException; |
44 | 45 | import java.util.concurrent.Executors; |
45 | 46 | import java.util.stream.Collectors; |
46 | 47 | |
48 | +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; | |
49 | + | |
47 | 50 | public class BaseDeviceProfileServiceTest extends AbstractServiceTest { |
48 | 51 | |
49 | 52 | private IdComparator<DeviceProfile> idComparator = new IdComparator<>(); |
... | ... | @@ -98,6 +101,8 @@ public class BaseDeviceProfileServiceTest extends AbstractServiceTest { |
98 | 101 | |
99 | 102 | Firmware firmware = new Firmware(); |
100 | 103 | firmware.setTenantId(tenantId); |
104 | + firmware.setDeviceProfileId(savedDeviceProfile.getId()); | |
105 | + firmware.setType(FIRMWARE); | |
101 | 106 | firmware.setTitle("my firmware"); |
102 | 107 | firmware.setVersion("v1.0"); |
103 | 108 | firmware.setFileName("test.txt"); | ... | ... |
... | ... | @@ -20,10 +20,13 @@ import org.apache.commons.lang3.RandomStringUtils; |
20 | 20 | import org.junit.After; |
21 | 21 | import org.junit.Assert; |
22 | 22 | import org.junit.Before; |
23 | +import org.junit.Rule; | |
23 | 24 | import org.junit.Test; |
25 | +import org.junit.rules.ExpectedException; | |
24 | 26 | import org.thingsboard.server.common.data.Customer; |
25 | 27 | import org.thingsboard.server.common.data.Device; |
26 | 28 | import org.thingsboard.server.common.data.DeviceInfo; |
29 | +import org.thingsboard.server.common.data.DeviceProfile; | |
27 | 30 | import org.thingsboard.server.common.data.EntitySubtype; |
28 | 31 | import org.thingsboard.server.common.data.Firmware; |
29 | 32 | import org.thingsboard.server.common.data.Tenant; |
... | ... | @@ -43,6 +46,7 @@ import java.util.ArrayList; |
43 | 46 | import java.util.Collections; |
44 | 47 | import java.util.List; |
45 | 48 | |
49 | +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; | |
46 | 50 | import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; |
47 | 51 | |
48 | 52 | public abstract class BaseDeviceServiceTest extends AbstractServiceTest { |
... | ... | @@ -67,6 +71,9 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { |
67 | 71 | tenantProfileService.deleteTenantProfiles(anotherTenantId); |
68 | 72 | } |
69 | 73 | |
74 | + @Rule | |
75 | + public ExpectedException thrown = ExpectedException.none(); | |
76 | + | |
70 | 77 | @Test |
71 | 78 | public void testSaveDevicesWithoutMaxDeviceLimit() { |
72 | 79 | Device device = this.saveDevice(tenantId, "My device"); |
... | ... | @@ -184,6 +191,8 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { |
184 | 191 | |
185 | 192 | Firmware firmware = new Firmware(); |
186 | 193 | firmware.setTenantId(tenantId); |
194 | + firmware.setDeviceProfileId(device.getDeviceProfileId()); | |
195 | + firmware.setType(FIRMWARE); | |
187 | 196 | firmware.setTitle("my firmware"); |
188 | 197 | firmware.setVersion("v1.0"); |
189 | 198 | firmware.setFileName("test.txt"); |
... | ... | @@ -199,6 +208,40 @@ public abstract class BaseDeviceServiceTest extends AbstractServiceTest { |
199 | 208 | Device foundDevice = deviceService.findDeviceById(tenantId, savedDevice.getId()); |
200 | 209 | Assert.assertEquals(foundDevice.getName(), savedDevice.getName()); |
201 | 210 | } |
211 | + | |
212 | + @Test | |
213 | + public void testAssignFirmwareToDeviceWithDifferentDeviceProfile() { | |
214 | + Device device = new Device(); | |
215 | + device.setTenantId(tenantId); | |
216 | + device.setName("My device"); | |
217 | + device.setType("default"); | |
218 | + Device savedDevice = deviceService.saveDevice(device); | |
219 | + | |
220 | + Assert.assertNotNull(savedDevice); | |
221 | + | |
222 | + DeviceProfile deviceProfile = createDeviceProfile(tenantId, "New device Profile"); | |
223 | + DeviceProfile savedProfile = deviceProfileService.saveDeviceProfile(deviceProfile); | |
224 | + Assert.assertNotNull(savedProfile); | |
225 | + | |
226 | + Firmware firmware = new Firmware(); | |
227 | + firmware.setTenantId(tenantId); | |
228 | + firmware.setDeviceProfileId(savedProfile.getId()); | |
229 | + firmware.setType(FIRMWARE); | |
230 | + firmware.setTitle("my firmware"); | |
231 | + firmware.setVersion("v1.0"); | |
232 | + firmware.setFileName("test.txt"); | |
233 | + firmware.setContentType("text/plain"); | |
234 | + firmware.setChecksumAlgorithm(ChecksumAlgorithm.SHA256); | |
235 | + firmware.setChecksum("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a"); | |
236 | + firmware.setData(ByteBuffer.wrap(new byte[]{1})); | |
237 | + Firmware savedFirmware = firmwareService.saveFirmware(firmware); | |
238 | + | |
239 | + savedDevice.setFirmwareId(savedFirmware.getId()); | |
240 | + | |
241 | + thrown.expect(DataValidationException.class); | |
242 | + thrown.expectMessage("Can't assign firmware with different deviceProfile!"); | |
243 | + deviceService.saveDevice(savedDevice); | |
244 | + } | |
202 | 245 | |
203 | 246 | @Test(expected = DataValidationException.class) |
204 | 247 | public void testSaveDeviceWithEmptyName() { | ... | ... |
... | ... | @@ -19,7 +19,9 @@ import com.datastax.oss.driver.api.core.uuid.Uuids; |
19 | 19 | import org.junit.After; |
20 | 20 | import org.junit.Assert; |
21 | 21 | import org.junit.Before; |
22 | +import org.junit.Rule; | |
22 | 23 | import org.junit.Test; |
24 | +import org.junit.rules.ExpectedException; | |
23 | 25 | import org.thingsboard.common.util.JacksonUtil; |
24 | 26 | import org.thingsboard.server.common.data.Device; |
25 | 27 | import org.thingsboard.server.common.data.DeviceProfile; |
... | ... | @@ -27,6 +29,7 @@ import org.thingsboard.server.common.data.Firmware; |
27 | 29 | import org.thingsboard.server.common.data.FirmwareInfo; |
28 | 30 | import org.thingsboard.server.common.data.Tenant; |
29 | 31 | import org.thingsboard.server.common.data.firmware.ChecksumAlgorithm; |
32 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
30 | 33 | import org.thingsboard.server.common.data.id.TenantId; |
31 | 34 | import org.thingsboard.server.common.data.page.PageData; |
32 | 35 | import org.thingsboard.server.common.data.page.PageLink; |
... | ... | @@ -37,6 +40,8 @@ import java.util.ArrayList; |
37 | 40 | import java.util.Collections; |
38 | 41 | import java.util.List; |
39 | 42 | |
43 | +import static org.thingsboard.server.common.data.firmware.FirmwareType.FIRMWARE; | |
44 | + | |
40 | 45 | public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
41 | 46 | |
42 | 47 | public static final String TITLE = "My firmware"; |
... | ... | @@ -51,6 +56,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
51 | 56 | |
52 | 57 | private TenantId tenantId; |
53 | 58 | |
59 | + private DeviceProfileId deviceProfileId; | |
60 | + | |
54 | 61 | @Before |
55 | 62 | public void before() { |
56 | 63 | Tenant tenant = new Tenant(); |
... | ... | @@ -58,8 +65,16 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
58 | 65 | Tenant savedTenant = tenantService.saveTenant(tenant); |
59 | 66 | Assert.assertNotNull(savedTenant); |
60 | 67 | tenantId = savedTenant.getId(); |
68 | + | |
69 | + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); | |
70 | + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); | |
71 | + Assert.assertNotNull(savedDeviceProfile); | |
72 | + deviceProfileId = savedDeviceProfile.getId(); | |
61 | 73 | } |
62 | 74 | |
75 | + @Rule | |
76 | + public ExpectedException thrown = ExpectedException.none(); | |
77 | + | |
63 | 78 | @After |
64 | 79 | public void after() { |
65 | 80 | tenantService.deleteTenant(tenantId); |
... | ... | @@ -69,6 +84,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
69 | 84 | public void testSaveFirmware() { |
70 | 85 | Firmware firmware = new Firmware(); |
71 | 86 | firmware.setTenantId(tenantId); |
87 | + firmware.setDeviceProfileId(deviceProfileId); | |
88 | + firmware.setType(FIRMWARE); | |
72 | 89 | firmware.setTitle(TITLE); |
73 | 90 | firmware.setVersion(VERSION); |
74 | 91 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -100,6 +117,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
100 | 117 | public void testSaveFirmwareInfoAndUpdateWithData() { |
101 | 118 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
102 | 119 | firmwareInfo.setTenantId(tenantId); |
120 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
121 | + firmwareInfo.setType(FIRMWARE); | |
103 | 122 | firmwareInfo.setTitle(TITLE); |
104 | 123 | firmwareInfo.setVersion(VERSION); |
105 | 124 | FirmwareInfo savedFirmwareInfo = firmwareService.saveFirmwareInfo(firmwareInfo); |
... | ... | @@ -113,6 +132,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
113 | 132 | Firmware firmware = new Firmware(savedFirmwareInfo.getId()); |
114 | 133 | firmware.setCreatedTime(firmwareInfo.getCreatedTime()); |
115 | 134 | firmware.setTenantId(tenantId); |
135 | + firmware.setDeviceProfileId(deviceProfileId); | |
136 | + firmware.setType(FIRMWARE); | |
116 | 137 | firmware.setTitle(TITLE); |
117 | 138 | firmware.setVersion(VERSION); |
118 | 139 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -136,9 +157,11 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
136 | 157 | firmwareService.deleteFirmware(tenantId, savedFirmwareInfo.getId()); |
137 | 158 | } |
138 | 159 | |
139 | - @Test(expected = DataValidationException.class) | |
160 | + @Test | |
140 | 161 | public void testSaveFirmwareWithEmptyTenant() { |
141 | 162 | Firmware firmware = new Firmware(); |
163 | + firmware.setDeviceProfileId(deviceProfileId); | |
164 | + firmware.setType(FIRMWARE); | |
142 | 165 | firmware.setTitle(TITLE); |
143 | 166 | firmware.setVersion(VERSION); |
144 | 167 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -146,65 +169,108 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
146 | 169 | firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
147 | 170 | firmware.setChecksum(CHECKSUM); |
148 | 171 | firmware.setData(DATA); |
172 | + | |
173 | + thrown.expect(DataValidationException.class); | |
174 | + thrown.expectMessage("Firmware should be assigned to tenant!"); | |
149 | 175 | firmwareService.saveFirmware(firmware); |
150 | 176 | } |
151 | 177 | |
152 | - @Test(expected = DataValidationException.class) | |
178 | + @Test | |
179 | + public void testSaveFirmwareWithEmptyType() { | |
180 | + Firmware firmware = new Firmware(); | |
181 | + firmware.setTenantId(tenantId); | |
182 | + firmware.setDeviceProfileId(deviceProfileId); | |
183 | + firmware.setTitle(TITLE); | |
184 | + firmware.setVersion(VERSION); | |
185 | + firmware.setFileName(FILE_NAME); | |
186 | + firmware.setContentType(CONTENT_TYPE); | |
187 | + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); | |
188 | + firmware.setChecksum(CHECKSUM); | |
189 | + firmware.setData(DATA); | |
190 | + | |
191 | + thrown.expect(DataValidationException.class); | |
192 | + thrown.expectMessage("Type should be specified!"); | |
193 | + firmwareService.saveFirmware(firmware); | |
194 | + } | |
195 | + | |
196 | + @Test | |
153 | 197 | public void testSaveFirmwareWithEmptyTitle() { |
154 | 198 | Firmware firmware = new Firmware(); |
155 | 199 | firmware.setTenantId(tenantId); |
200 | + firmware.setDeviceProfileId(deviceProfileId); | |
201 | + firmware.setType(FIRMWARE); | |
156 | 202 | firmware.setVersion(VERSION); |
157 | 203 | firmware.setFileName(FILE_NAME); |
158 | 204 | firmware.setContentType(CONTENT_TYPE); |
159 | 205 | firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
160 | 206 | firmware.setChecksum(CHECKSUM); |
161 | 207 | firmware.setData(DATA); |
208 | + | |
209 | + thrown.expect(DataValidationException.class); | |
210 | + thrown.expectMessage("Firmware title should be specified!"); | |
162 | 211 | firmwareService.saveFirmware(firmware); |
163 | 212 | } |
164 | 213 | |
165 | - @Test(expected = DataValidationException.class) | |
214 | + @Test | |
166 | 215 | public void testSaveFirmwareWithEmptyFileName() { |
167 | 216 | Firmware firmware = new Firmware(); |
168 | 217 | firmware.setTenantId(tenantId); |
218 | + firmware.setDeviceProfileId(deviceProfileId); | |
219 | + firmware.setType(FIRMWARE); | |
169 | 220 | firmware.setTitle(TITLE); |
170 | 221 | firmware.setVersion(VERSION); |
171 | 222 | firmware.setContentType(CONTENT_TYPE); |
172 | 223 | firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
173 | 224 | firmware.setChecksum(CHECKSUM); |
174 | 225 | firmware.setData(DATA); |
226 | + | |
227 | + thrown.expect(DataValidationException.class); | |
228 | + thrown.expectMessage("Firmware file name should be specified!"); | |
175 | 229 | firmwareService.saveFirmware(firmware); |
176 | 230 | } |
177 | 231 | |
178 | - @Test(expected = DataValidationException.class) | |
232 | + @Test | |
179 | 233 | public void testSaveFirmwareWithEmptyContentType() { |
180 | 234 | Firmware firmware = new Firmware(); |
181 | 235 | firmware.setTenantId(tenantId); |
236 | + firmware.setDeviceProfileId(deviceProfileId); | |
237 | + firmware.setType(FIRMWARE); | |
182 | 238 | firmware.setTitle(TITLE); |
183 | 239 | firmware.setVersion(VERSION); |
184 | 240 | firmware.setFileName(FILE_NAME); |
185 | 241 | firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
186 | 242 | firmware.setChecksum(CHECKSUM); |
187 | 243 | firmware.setData(DATA); |
244 | + | |
245 | + thrown.expect(DataValidationException.class); | |
246 | + thrown.expectMessage("Firmware content type should be specified!"); | |
188 | 247 | firmwareService.saveFirmware(firmware); |
189 | 248 | } |
190 | 249 | |
191 | - @Test(expected = DataValidationException.class) | |
250 | + @Test | |
192 | 251 | public void testSaveFirmwareWithEmptyData() { |
193 | 252 | Firmware firmware = new Firmware(); |
194 | 253 | firmware.setTenantId(tenantId); |
254 | + firmware.setDeviceProfileId(deviceProfileId); | |
255 | + firmware.setType(FIRMWARE); | |
195 | 256 | firmware.setTitle(TITLE); |
196 | 257 | firmware.setVersion(VERSION); |
197 | 258 | firmware.setFileName(FILE_NAME); |
198 | 259 | firmware.setContentType(CONTENT_TYPE); |
199 | 260 | firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
200 | 261 | firmware.setChecksum(CHECKSUM); |
262 | + | |
263 | + thrown.expect(DataValidationException.class); | |
264 | + thrown.expectMessage("Firmware data should be specified!"); | |
201 | 265 | firmwareService.saveFirmware(firmware); |
202 | 266 | } |
203 | 267 | |
204 | - @Test(expected = DataValidationException.class) | |
268 | + @Test | |
205 | 269 | public void testSaveFirmwareWithInvalidTenant() { |
206 | 270 | Firmware firmware = new Firmware(); |
207 | 271 | firmware.setTenantId(new TenantId(Uuids.timeBased())); |
272 | + firmware.setDeviceProfileId(deviceProfileId); | |
273 | + firmware.setType(FIRMWARE); | |
208 | 274 | firmware.setTitle(TITLE); |
209 | 275 | firmware.setVersion(VERSION); |
210 | 276 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -212,41 +278,77 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
212 | 278 | firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
213 | 279 | firmware.setChecksum(CHECKSUM); |
214 | 280 | firmware.setData(DATA); |
281 | + | |
282 | + thrown.expect(DataValidationException.class); | |
283 | + thrown.expectMessage("Firmware is referencing to non-existent tenant!"); | |
215 | 284 | firmwareService.saveFirmware(firmware); |
216 | 285 | } |
217 | 286 | |
218 | - @Test(expected = DataValidationException.class) | |
287 | + @Test | |
288 | + public void testSaveFirmwareWithInvalidDeviceProfileId() { | |
289 | + Firmware firmware = new Firmware(); | |
290 | + firmware.setTenantId(tenantId); | |
291 | + firmware.setDeviceProfileId(new DeviceProfileId(Uuids.timeBased())); | |
292 | + firmware.setType(FIRMWARE); | |
293 | + firmware.setTitle(TITLE); | |
294 | + firmware.setVersion(VERSION); | |
295 | + firmware.setFileName(FILE_NAME); | |
296 | + firmware.setContentType(CONTENT_TYPE); | |
297 | + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); | |
298 | + firmware.setChecksum(CHECKSUM); | |
299 | + firmware.setData(DATA); | |
300 | + | |
301 | + thrown.expect(DataValidationException.class); | |
302 | + thrown.expectMessage("Firmware is referencing to non-existent device profile!"); | |
303 | + firmwareService.saveFirmware(firmware); | |
304 | + } | |
305 | + | |
306 | + @Test | |
219 | 307 | public void testSaveFirmwareWithEmptyChecksum() { |
220 | 308 | Firmware firmware = new Firmware(); |
221 | - firmware.setTenantId(new TenantId(Uuids.timeBased())); | |
309 | + firmware.setTenantId(tenantId); | |
310 | + firmware.setDeviceProfileId(deviceProfileId); | |
311 | + firmware.setType(FIRMWARE); | |
222 | 312 | firmware.setTitle(TITLE); |
223 | 313 | firmware.setVersion(VERSION); |
224 | 314 | firmware.setFileName(FILE_NAME); |
225 | 315 | firmware.setContentType(CONTENT_TYPE); |
226 | 316 | firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); |
227 | 317 | firmware.setData(DATA); |
318 | + | |
319 | + thrown.expect(DataValidationException.class); | |
320 | + thrown.expectMessage("Firmware checksum should be specified!"); | |
228 | 321 | firmwareService.saveFirmware(firmware); |
229 | 322 | } |
230 | 323 | |
231 | - @Test(expected = DataValidationException.class) | |
324 | + @Test | |
232 | 325 | public void testSaveFirmwareInfoWithExistingTitleAndVersion() { |
233 | 326 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
234 | 327 | firmwareInfo.setTenantId(tenantId); |
328 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
329 | + firmwareInfo.setType(FIRMWARE); | |
235 | 330 | firmwareInfo.setTitle(TITLE); |
236 | 331 | firmwareInfo.setVersion(VERSION); |
237 | 332 | firmwareService.saveFirmwareInfo(firmwareInfo); |
238 | 333 | |
239 | 334 | FirmwareInfo newFirmwareInfo = new FirmwareInfo(); |
240 | 335 | newFirmwareInfo.setTenantId(tenantId); |
336 | + newFirmwareInfo.setDeviceProfileId(deviceProfileId); | |
337 | + newFirmwareInfo.setType(FIRMWARE); | |
241 | 338 | newFirmwareInfo.setTitle(TITLE); |
242 | 339 | newFirmwareInfo.setVersion(VERSION); |
340 | + | |
341 | + thrown.expect(DataValidationException.class); | |
342 | + thrown.expectMessage("Firmware with such title and version already exists!"); | |
243 | 343 | firmwareService.saveFirmwareInfo(newFirmwareInfo); |
244 | 344 | } |
245 | 345 | |
246 | - @Test(expected = DataValidationException.class) | |
346 | + @Test | |
247 | 347 | public void testSaveFirmwareWithExistingTitleAndVersion() { |
248 | 348 | Firmware firmware = new Firmware(); |
249 | 349 | firmware.setTenantId(tenantId); |
350 | + firmware.setDeviceProfileId(deviceProfileId); | |
351 | + firmware.setType(FIRMWARE); | |
250 | 352 | firmware.setTitle(TITLE); |
251 | 353 | firmware.setVersion(VERSION); |
252 | 354 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -258,18 +360,27 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
258 | 360 | |
259 | 361 | Firmware newFirmware = new Firmware(); |
260 | 362 | newFirmware.setTenantId(tenantId); |
363 | + newFirmware.setDeviceProfileId(deviceProfileId); | |
364 | + newFirmware.setType(FIRMWARE); | |
261 | 365 | newFirmware.setTitle(TITLE); |
262 | 366 | newFirmware.setVersion(VERSION); |
263 | 367 | newFirmware.setFileName(FILE_NAME); |
264 | 368 | newFirmware.setContentType(CONTENT_TYPE); |
369 | + newFirmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); | |
370 | + newFirmware.setChecksum(CHECKSUM); | |
265 | 371 | newFirmware.setData(DATA); |
372 | + | |
373 | + thrown.expect(DataValidationException.class); | |
374 | + thrown.expectMessage("Firmware with such title and version already exists!"); | |
266 | 375 | firmwareService.saveFirmware(newFirmware); |
267 | 376 | } |
268 | 377 | |
269 | - @Test(expected = DataValidationException.class) | |
378 | + @Test | |
270 | 379 | public void testDeleteFirmwareWithReferenceByDevice() { |
271 | 380 | Firmware firmware = new Firmware(); |
272 | 381 | firmware.setTenantId(tenantId); |
382 | + firmware.setDeviceProfileId(deviceProfileId); | |
383 | + firmware.setType(FIRMWARE); | |
273 | 384 | firmware.setTitle(TITLE); |
274 | 385 | firmware.setVersion(VERSION); |
275 | 386 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -282,11 +393,13 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
282 | 393 | Device device = new Device(); |
283 | 394 | device.setTenantId(tenantId); |
284 | 395 | device.setName("My device"); |
285 | - device.setType("default"); | |
396 | + device.setDeviceProfileId(deviceProfileId); | |
286 | 397 | device.setFirmwareId(savedFirmware.getId()); |
287 | 398 | Device savedDevice = deviceService.saveDevice(device); |
288 | 399 | |
289 | 400 | try { |
401 | + thrown.expect(DataValidationException.class); | |
402 | + thrown.expectMessage("The firmware referenced by the devices cannot be deleted!"); | |
290 | 403 | firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
291 | 404 | } finally { |
292 | 405 | deviceService.deleteDevice(tenantId, savedDevice.getId()); |
... | ... | @@ -294,10 +407,12 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
294 | 407 | } |
295 | 408 | } |
296 | 409 | |
297 | - @Test(expected = DataValidationException.class) | |
298 | - public void testDeleteFirmwareWithReferenceByDeviceProfile() { | |
410 | + @Test | |
411 | + public void testUpdateDeviceProfileIdWithReferenceByDevice() { | |
299 | 412 | Firmware firmware = new Firmware(); |
300 | 413 | firmware.setTenantId(tenantId); |
414 | + firmware.setDeviceProfileId(deviceProfileId); | |
415 | + firmware.setType(FIRMWARE); | |
301 | 416 | firmware.setTitle(TITLE); |
302 | 417 | firmware.setVersion(VERSION); |
303 | 418 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -307,12 +422,81 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
307 | 422 | firmware.setData(DATA); |
308 | 423 | Firmware savedFirmware = firmwareService.saveFirmware(firmware); |
309 | 424 | |
310 | - DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Device Profile"); | |
311 | - deviceProfile.setFirmwareId(savedFirmware.getId()); | |
425 | + Device device = new Device(); | |
426 | + device.setTenantId(tenantId); | |
427 | + device.setName("My device"); | |
428 | + device.setDeviceProfileId(deviceProfileId); | |
429 | + device.setFirmwareId(savedFirmware.getId()); | |
430 | + Device savedDevice = deviceService.saveDevice(device); | |
431 | + | |
432 | + try { | |
433 | + thrown.expect(DataValidationException.class); | |
434 | + thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!"); | |
435 | + savedFirmware.setDeviceProfileId(null); | |
436 | + firmwareService.saveFirmware(savedFirmware); | |
437 | + } finally { | |
438 | + deviceService.deleteDevice(tenantId, savedDevice.getId()); | |
439 | + firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); | |
440 | + } | |
441 | + } | |
442 | + | |
443 | + @Test | |
444 | + public void testDeleteFirmwareWithReferenceByDeviceProfile() { | |
445 | + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); | |
312 | 446 | DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); |
313 | 447 | |
448 | + Firmware firmware = new Firmware(); | |
449 | + firmware.setTenantId(tenantId); | |
450 | + firmware.setDeviceProfileId(savedDeviceProfile.getId()); | |
451 | + firmware.setType(FIRMWARE); | |
452 | + firmware.setTitle(TITLE); | |
453 | + firmware.setVersion(VERSION); | |
454 | + firmware.setFileName(FILE_NAME); | |
455 | + firmware.setContentType(CONTENT_TYPE); | |
456 | + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); | |
457 | + firmware.setChecksum(CHECKSUM); | |
458 | + firmware.setData(DATA); | |
459 | + Firmware savedFirmware = firmwareService.saveFirmware(firmware); | |
460 | + | |
461 | + savedDeviceProfile.setFirmwareId(savedFirmware.getId()); | |
462 | + deviceProfileService.saveDeviceProfile(savedDeviceProfile); | |
463 | + | |
314 | 464 | try { |
465 | + thrown.expect(DataValidationException.class); | |
466 | + thrown.expectMessage("The firmware referenced by the device profile cannot be deleted!"); | |
467 | + firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); | |
468 | + } finally { | |
469 | + deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); | |
315 | 470 | firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
471 | + } | |
472 | + } | |
473 | + | |
474 | + @Test | |
475 | + public void testUpdateDeviceProfileIdWithReferenceByDeviceProfile() { | |
476 | + DeviceProfile deviceProfile = this.createDeviceProfile(tenantId, "Test Device Profile"); | |
477 | + DeviceProfile savedDeviceProfile = deviceProfileService.saveDeviceProfile(deviceProfile); | |
478 | + | |
479 | + Firmware firmware = new Firmware(); | |
480 | + firmware.setTenantId(tenantId); | |
481 | + firmware.setDeviceProfileId(savedDeviceProfile.getId()); | |
482 | + firmware.setType(FIRMWARE); | |
483 | + firmware.setTitle(TITLE); | |
484 | + firmware.setVersion(VERSION); | |
485 | + firmware.setFileName(FILE_NAME); | |
486 | + firmware.setContentType(CONTENT_TYPE); | |
487 | + firmware.setChecksumAlgorithm(CHECKSUM_ALGORITHM); | |
488 | + firmware.setChecksum(CHECKSUM); | |
489 | + firmware.setData(DATA); | |
490 | + Firmware savedFirmware = firmwareService.saveFirmware(firmware); | |
491 | + | |
492 | + savedDeviceProfile.setFirmwareId(savedFirmware.getId()); | |
493 | + deviceProfileService.saveDeviceProfile(savedDeviceProfile); | |
494 | + | |
495 | + try { | |
496 | + thrown.expect(DataValidationException.class); | |
497 | + thrown.expectMessage("Can`t update deviceProfileId because firmware is already in use!"); | |
498 | + savedFirmware.setDeviceProfileId(null); | |
499 | + firmwareService.saveFirmware(savedFirmware); | |
316 | 500 | } finally { |
317 | 501 | deviceProfileService.deleteDeviceProfile(tenantId, savedDeviceProfile.getId()); |
318 | 502 | firmwareService.deleteFirmware(tenantId, savedFirmware.getId()); |
... | ... | @@ -323,6 +507,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
323 | 507 | public void testFindFirmwareById() { |
324 | 508 | Firmware firmware = new Firmware(); |
325 | 509 | firmware.setTenantId(tenantId); |
510 | + firmware.setDeviceProfileId(deviceProfileId); | |
511 | + firmware.setType(FIRMWARE); | |
326 | 512 | firmware.setTitle(TITLE); |
327 | 513 | firmware.setVersion(VERSION); |
328 | 514 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -342,6 +528,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
342 | 528 | public void testFindFirmwareInfoById() { |
343 | 529 | FirmwareInfo firmware = new FirmwareInfo(); |
344 | 530 | firmware.setTenantId(tenantId); |
531 | + firmware.setDeviceProfileId(deviceProfileId); | |
532 | + firmware.setType(FIRMWARE); | |
345 | 533 | firmware.setTitle(TITLE); |
346 | 534 | firmware.setVersion(VERSION); |
347 | 535 | FirmwareInfo savedFirmware = firmwareService.saveFirmwareInfo(firmware); |
... | ... | @@ -356,6 +544,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
356 | 544 | public void testDeleteFirmware() { |
357 | 545 | Firmware firmware = new Firmware(); |
358 | 546 | firmware.setTenantId(tenantId); |
547 | + firmware.setDeviceProfileId(deviceProfileId); | |
548 | + firmware.setType(FIRMWARE); | |
359 | 549 | firmware.setTitle(TITLE); |
360 | 550 | firmware.setVersion(VERSION); |
361 | 551 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -378,6 +568,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
378 | 568 | for (int i = 0; i < 165; i++) { |
379 | 569 | Firmware firmware = new Firmware(); |
380 | 570 | firmware.setTenantId(tenantId); |
571 | + firmware.setDeviceProfileId(deviceProfileId); | |
572 | + firmware.setType(FIRMWARE); | |
381 | 573 | firmware.setTitle(TITLE); |
382 | 574 | firmware.setVersion(VERSION + i); |
383 | 575 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -421,6 +613,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
421 | 613 | for (int i = 0; i < 165; i++) { |
422 | 614 | FirmwareInfo firmwareInfo = new FirmwareInfo(); |
423 | 615 | firmwareInfo.setTenantId(tenantId); |
616 | + firmwareInfo.setDeviceProfileId(deviceProfileId); | |
617 | + firmwareInfo.setType(FIRMWARE); | |
424 | 618 | firmwareInfo.setTitle(TITLE); |
425 | 619 | firmwareInfo.setVersion(VERSION + i); |
426 | 620 | firmwareInfo.setFileName(FILE_NAME); |
... | ... | @@ -435,7 +629,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
435 | 629 | PageLink pageLink = new PageLink(16); |
436 | 630 | PageData<FirmwareInfo> pageData; |
437 | 631 | do { |
438 | - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, false, pageLink); | |
632 | + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, false, pageLink); | |
439 | 633 | loadedFirmwares.addAll(pageData.getData()); |
440 | 634 | if (pageData.hasNext()) { |
441 | 635 | pageLink = pageLink.nextPageLink(); |
... | ... | @@ -451,6 +645,8 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
451 | 645 | Firmware firmware = new Firmware(f.getId()); |
452 | 646 | firmware.setCreatedTime(f.getCreatedTime()); |
453 | 647 | firmware.setTenantId(f.getTenantId()); |
648 | + firmware.setDeviceProfileId(deviceProfileId); | |
649 | + firmware.setType(FIRMWARE); | |
454 | 650 | firmware.setTitle(f.getTitle()); |
455 | 651 | firmware.setVersion(f.getVersion()); |
456 | 652 | firmware.setFileName(FILE_NAME); |
... | ... | @@ -466,7 +662,7 @@ public abstract class BaseFirmwareServiceTest extends AbstractServiceTest { |
466 | 662 | loadedFirmwares = new ArrayList<>(); |
467 | 663 | pageLink = new PageLink(16); |
468 | 664 | do { |
469 | - pageData = firmwareService.findTenantFirmwaresByTenantIdAndHasData(tenantId, true, pageLink); | |
665 | + pageData = firmwareService.findTenantFirmwaresByTenantIdAndDeviceProfileIdAndTypeAndHasData(tenantId, deviceProfileId, FIRMWARE, true, pageLink); | |
470 | 666 | loadedFirmwares.addAll(pageData.getData()); |
471 | 667 | if (pageData.hasNext()) { |
472 | 668 | pageLink = pageLink.nextPageLink(); | ... | ... |