Added "handle device renaming parameter to the gateway"

This commit is contained in:
zbeacon 2021-12-17 13:49:05 +02:00
parent 1ebf7f7039
commit b76088892b
8 changed files with 142 additions and 45 deletions

View File

@ -1,6 +1,22 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.gateway_device; package org.thingsboard.server.service.gateway_device;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
@ -15,6 +31,7 @@ import org.thingsboard.common.util.DonAsynchron;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
@ -23,11 +40,11 @@ import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.attributes.AttributesService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -39,8 +56,11 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi
private static final String RENAMED_GATEWAY_DEVICES = "renamedGatewayDevices"; private static final String RENAMED_GATEWAY_DEVICES = "renamedGatewayDevices";
private static final String DELETED_GATEWAY_DEVICES = "deletedGatewayDevices"; private static final String DELETED_GATEWAY_DEVICES = "deletedGatewayDevices";
private static final String HANDLE_DEVICE_RENAMING_PARAMETER = "handleDeviceRenaming";
private final AttributesService attributesService; private final AttributesService attributesService;
private final RelationService relationService; private final RelationService relationService;
private final DeviceService deviceService;
@Lazy @Lazy
@Autowired @Autowired
private TelemetrySubscriptionService tsSubService; private TelemetrySubscriptionService tsSubService;
@ -49,7 +69,11 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi
public void update(Device device, Device oldDevice) { public void update(Device device, Device oldDevice) {
List<EntityRelation> relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON); List<EntityRelation> relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON);
if (!relationToGatewayList.isEmpty()) { if (!relationToGatewayList.isEmpty()) {
if (oldDevice != null) {
EntityRelation relationToGateway = relationToGatewayList.get(0); EntityRelation relationToGateway = relationToGatewayList.get(0);
Device gatewayDevice = deviceService.findDeviceById(device.getTenantId(), (DeviceId) relationToGateway.getTo());
if (isHandleDeviceRenamingEnabled(gatewayDevice.getAdditionalInfo())) {
ListenableFuture<List<AttributeKvEntry>> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices")); ListenableFuture<List<AttributeKvEntry>> renamedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("renamedGatewayDevices"));
DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> { DonAsynchron.withCallback(renamedGatewayDevicesFuture, renamedGatewayDevicesList -> {
ObjectNode renamedGatewayDevicesNode; ObjectNode renamedGatewayDevicesNode;
@ -89,6 +113,8 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi
e -> log.error("Cannot get gateway renamed devices attribute", e)); e -> log.error("Cannot get gateway renamed devices attribute", e));
} }
} }
}
}
@Override @Override
public void delete(Device device) { public void delete(Device device) {
@ -105,8 +131,9 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi
if (renamedGatewayDevicesNode.findValue(deletedDeviceName[0]) != null) { if (renamedGatewayDevicesNode.findValue(deletedDeviceName[0]) != null) {
renamedGatewayDevicesNode.remove(deletedDeviceName[0]); renamedGatewayDevicesNode.remove(deletedDeviceName[0]);
renamedListChanged.set(true); renamedListChanged.set(true);
} else { }
Map<String, String> renamedGatewayDevicesMap = JacksonUtil.OBJECT_MAPPER.convertValue(renamedGatewayDevicesNode, new TypeReference<>() {}); Map<String, String> renamedGatewayDevicesMap = JacksonUtil.OBJECT_MAPPER.convertValue(renamedGatewayDevicesNode, new TypeReference<>() {
});
renamedGatewayDevicesMap.forEach((key, value) -> { renamedGatewayDevicesMap.forEach((key, value) -> {
// If device was renamed earlier // If device was renamed earlier
if (deletedDeviceName[0].equals(value)) { if (deletedDeviceName[0].equals(value)) {
@ -115,7 +142,6 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi
renamedListChanged.set(true); renamedListChanged.set(true);
} }
}); });
}
if (renamedListChanged.get()) { if (renamedListChanged.get()) {
KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode)); KvEntry renamedGatewayDevicesKvEntry = new JsonDataEntry(RENAMED_GATEWAY_DEVICES, JacksonUtil.toString(renamedGatewayDevicesNode));
saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry); saveGatewayDevicesAttribute(device, relationToGateway, renamedGatewayDevicesKvEntry);
@ -138,6 +164,32 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi
} }
} }
@Override
public void checkAndUpdateDeletedGatewayDevicesAttribute(Device device) {
List<EntityRelation> relationToGatewayList = relationService.findByFromAndType(TenantId.SYS_TENANT_ID, device.getId(), DataConstants.LAST_CONNECTED_GATEWAY, RelationTypeGroup.COMMON);
if (!relationToGatewayList.isEmpty()) {
EntityRelation relationToGateway = relationToGatewayList.get(0);
ListenableFuture<List<AttributeKvEntry>> deletedGatewayDevicesFuture = attributesService.find(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, Collections.singletonList("deletedGatewayDevices"));
DonAsynchron.withCallback(deletedGatewayDevicesFuture, deletedGatewayDevicesList -> {
ArrayNode deletedGatewayDevicesNode;
if (!deletedGatewayDevicesList.isEmpty()) {
int deletedDeviceIndex = -1;
deletedGatewayDevicesNode = (ArrayNode) JacksonUtil.toJsonNode(deletedGatewayDevicesList.get(0).getValueAsString());
for (int i = 0; i < deletedGatewayDevicesNode.size(); i++) {
if (deletedGatewayDevicesNode.get(i).asText().equals(device.getName())) {
deletedDeviceIndex = i;
}
}
if (deletedDeviceIndex != -1) {
deletedGatewayDevicesNode.remove(deletedDeviceIndex);
KvEntry deletedGatewayDevicesKvEntry = new JsonDataEntry(DELETED_GATEWAY_DEVICES, JacksonUtil.toString(deletedGatewayDevicesNode));
saveGatewayDevicesAttribute(device, relationToGateway, deletedGatewayDevicesKvEntry);
}
}
}, e -> log.error("Cannot get gateway deleted devices attribute", e));
}
}
private void saveGatewayDevicesAttribute(Device device, EntityRelation relationToGateway, KvEntry gatewayDevicesKvEntry) { private void saveGatewayDevicesAttribute(Device device, EntityRelation relationToGateway, KvEntry gatewayDevicesKvEntry) {
AttributeKvEntry attrKvEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), gatewayDevicesKvEntry); AttributeKvEntry attrKvEntry = new BaseAttributeKvEntry(System.currentTimeMillis(), gatewayDevicesKvEntry);
tsSubService.saveAndNotify(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, List.of(attrKvEntry), true, new FutureCallback<Void>() { tsSubService.saveAndNotify(device.getTenantId(), relationToGateway.getTo(), DataConstants.SHARED_SCOPE, List.of(attrKvEntry), true, new FutureCallback<Void>() {
@ -152,4 +204,11 @@ public class DefaultGatewayDeviceStateService implements GatewayDeviceStateServi
} }
}); });
} }
private boolean isHandleDeviceRenamingEnabled(JsonNode additionalInfo) {
if (additionalInfo.get(HANDLE_DEVICE_RENAMING_PARAMETER) != null) {
return additionalInfo.get(HANDLE_DEVICE_RENAMING_PARAMETER).asBoolean();
}
return false;
}
} }

View File

@ -1,3 +1,18 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.gateway_device; package org.thingsboard.server.service.gateway_device;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
@ -7,4 +22,6 @@ public interface GatewayDeviceStateService {
void update(Device device, Device oldDevice); void update(Device device, Device oldDevice);
void delete(Device device); void delete(Device device);
void checkAndUpdateDeletedGatewayDevicesAttribute(Device device);
} }

View File

@ -95,6 +95,7 @@ import org.thingsboard.server.queue.common.TbProtoQueueMsg;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.apiusage.TbApiUsageStateService; import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
import org.thingsboard.server.service.executors.DbCallbackExecutorService; import org.thingsboard.server.service.executors.DbCallbackExecutorService;
import org.thingsboard.server.service.gateway_device.GatewayDeviceStateService;
import org.thingsboard.server.service.profile.TbDeviceProfileCache; import org.thingsboard.server.service.profile.TbDeviceProfileCache;
import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.service.resource.TbResourceService; import org.thingsboard.server.service.resource.TbResourceService;
@ -137,6 +138,7 @@ public class DefaultTransportApiService implements TransportApiService {
private final TbResourceService resourceService; private final TbResourceService resourceService;
private final OtaPackageService otaPackageService; private final OtaPackageService otaPackageService;
private final OtaPackageDataCache otaPackageDataCache; private final OtaPackageDataCache otaPackageDataCache;
private final GatewayDeviceStateService gatewayDeviceStateService;
private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>(); private final ConcurrentMap<String, ReentrantLock> deviceCreationLocks = new ConcurrentHashMap<>();
@ -317,6 +319,8 @@ public class DefaultTransportApiService implements TransportApiService {
} }
relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, lastConnectedGatewayRelation); relationService.saveRelationAsync(TenantId.SYS_TENANT_ID, lastConnectedGatewayRelation);
gatewayDeviceStateService.checkAndUpdateDeletedGatewayDevicesAttribute(device);
GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder() GetOrCreateDeviceFromGatewayResponseMsg.Builder builder = GetOrCreateDeviceFromGatewayResponseMsg.newBuilder()
.setDeviceInfo(getDeviceInfoProto(device)); .setDeviceInfo(getDeviceInfoProto(device));
DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId()); DeviceProfile deviceProfile = deviceProfileCache.get(device.getTenantId(), device.getDeviceProfileId());

View File

@ -103,10 +103,16 @@
<mat-checkbox formControlName="gateway"> <mat-checkbox formControlName="gateway">
{{ 'device.is-gateway' | translate }} {{ 'device.is-gateway' | translate }}
</mat-checkbox> </mat-checkbox>
</div>
<div fxLayout="row" fxLayout.xs="column" style="padding-bottom: 16px;">
<mat-checkbox *ngIf="deviceWizardFormGroup.get('gateway').value" <mat-checkbox *ngIf="deviceWizardFormGroup.get('gateway').value"
formControlName="overwriteActivityTime"> formControlName="overwriteActivityTime">
{{ 'device.overwrite-activity-time' | translate }} {{ 'device.overwrite-activity-time' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox *ngIf="deviceWizardFormGroup.get('gateway').value"
formControlName="handleDeviceRenaming">
{{ 'device.handle-device-renaming' | translate }}
</mat-checkbox>
</div> </div>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>device.description</mat-label> <mat-label translate>device.description</mat-label>

View File

@ -109,6 +109,7 @@ export class DeviceWizardDialogComponent extends
label: ['', Validators.maxLength(255)], label: ['', Validators.maxLength(255)],
gateway: [false], gateway: [false],
overwriteActivityTime: [false], overwriteActivityTime: [false],
handleDeviceRenaming: [false],
addProfileType: [0], addProfileType: [0],
deviceProfileId: [null, Validators.required], deviceProfileId: [null, Validators.required],
newDeviceProfileTitle: [{value: null, disabled: true}], newDeviceProfileTitle: [{value: null, disabled: true}],
@ -326,6 +327,7 @@ export class DeviceWizardDialogComponent extends
additionalInfo: { additionalInfo: {
gateway: this.deviceWizardFormGroup.get('gateway').value, gateway: this.deviceWizardFormGroup.get('gateway').value,
overwriteActivityTime: this.deviceWizardFormGroup.get('overwriteActivityTime').value, overwriteActivityTime: this.deviceWizardFormGroup.get('overwriteActivityTime').value,
handleDeviceRenaming: this.deviceWizardFormGroup.get('handleDeviceRenaming').value,
description: this.deviceWizardFormGroup.get('description').value description: this.deviceWizardFormGroup.get('description').value
}, },
customerId: null customerId: null

View File

@ -128,10 +128,16 @@
<mat-checkbox fxFlex.gt-sm="30" fxFlex formControlName="gateway"> <mat-checkbox fxFlex.gt-sm="30" fxFlex formControlName="gateway">
{{ 'device.is-gateway' | translate }} {{ 'device.is-gateway' | translate }}
</mat-checkbox> </mat-checkbox>
</div>
<div fxLayout="row" fxLayout.xs="column" style="padding-bottom: 16px;">
<mat-checkbox fxFlex *ngIf="entityForm.get('additionalInfo.gateway').value" <mat-checkbox fxFlex *ngIf="entityForm.get('additionalInfo.gateway').value"
formControlName="overwriteActivityTime"> formControlName="overwriteActivityTime">
{{ 'device.overwrite-activity-time' | translate }} {{ 'device.overwrite-activity-time' | translate }}
</mat-checkbox> </mat-checkbox>
<mat-checkbox *ngIf="entityForm.get('additionalInfo.gateway').value"
formControlName="handleDeviceRenaming">
{{ 'device.handle-device-renaming' | translate }}
</mat-checkbox>
</div> </div>
<mat-form-field class="mat-block"> <mat-form-field class="mat-block">
<mat-label translate>device.description</mat-label> <mat-label translate>device.description</mat-label>

View File

@ -92,6 +92,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
{ {
gateway: [entity && entity.additionalInfo ? entity.additionalInfo.gateway : false], gateway: [entity && entity.additionalInfo ? entity.additionalInfo.gateway : false],
overwriteActivityTime: [entity && entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false], overwriteActivityTime: [entity && entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false],
handleDeviceRenaming: [entity && entity.additionalInfo ? entity.additionalInfo.handleDeviceRenaming : false],
description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''],
} }
) )
@ -121,6 +122,7 @@ export class DeviceComponent extends EntityComponent<DeviceInfo> {
additionalInfo: { additionalInfo: {
gateway: entity.additionalInfo ? entity.additionalInfo.gateway : false, gateway: entity.additionalInfo ? entity.additionalInfo.gateway : false,
overwriteActivityTime: entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false, overwriteActivityTime: entity.additionalInfo ? entity.additionalInfo.overwriteActivityTime : false,
handleDeviceRenaming: entity.additionalInfo ? entity.additionalInfo.handleDeviceRenaming : false,
description: entity.additionalInfo ? entity.additionalInfo.description : '' description: entity.additionalInfo ? entity.additionalInfo.description : ''
} }
}); });

View File

@ -1042,6 +1042,7 @@
"unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}", "unable-delete-device-alias-text": "Device alias '{{deviceAlias}}' can't be deleted as it used by the following widget(s):<br/>{{widgetsList}}",
"is-gateway": "Is gateway", "is-gateway": "Is gateway",
"overwrite-activity-time": "Overwrite activity time for connected device", "overwrite-activity-time": "Overwrite activity time for connected device",
"handle-device-renaming": "Handle device renaming",
"public": "Public", "public": "Public",
"device-public": "Device is public", "device-public": "Device is public",
"select-device": "Select device", "select-device": "Select device",