Merge branch 'master' of github.com:thingsboard/thingsboard
This commit is contained in:
commit
1e41be45ba
@ -21,6 +21,7 @@ import org.eclipse.californium.core.coap.MediaTypeRegistry;
|
||||
import org.eclipse.californium.core.coap.Request;
|
||||
import org.eclipse.californium.core.coap.Response;
|
||||
import org.eclipse.californium.core.server.resources.CoapExchange;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.transport.SessionMsgListener;
|
||||
import org.thingsboard.server.gen.transport.TransportProtos;
|
||||
import org.thingsboard.server.transport.coap.client.TbCoapClientState;
|
||||
@ -52,6 +53,11 @@ public abstract class AbstractSyncSessionCallback implements SessionMsgListener
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDeleted(DeviceId deviceId) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg toDeviceRequest) {
|
||||
logUnsupportedCommandMessage(toDeviceRequest);
|
||||
|
||||
@ -603,6 +603,13 @@ public class DeviceApiController implements TbTransportService {
|
||||
responseWriter.setResult(new ResponseEntity<>(JsonConverter.toJson(msg).toString(), HttpStatus.OK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDeleted(DeviceId deviceId) {
|
||||
UUID sessionId = new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB());
|
||||
log.trace("[{}] Received device deleted notification for device with id: {}",sessionId, deviceId);
|
||||
responseWriter.setResult(new ResponseEntity<>("Device was deleted!", HttpStatus.FORBIDDEN));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static MediaType parseMediaType(String contentType) {
|
||||
|
||||
@ -71,7 +71,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
|
||||
|
||||
private LeshanServer server;
|
||||
|
||||
@AfterStartUp
|
||||
@AfterStartUp(order = Integer.MAX_VALUE - 1)
|
||||
public void init() {
|
||||
this.server = getLhServer();
|
||||
/*
|
||||
@ -83,8 +83,8 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
|
||||
*/
|
||||
LwM2mTransportCoapResource otaCoapResource = new LwM2mTransportCoapResource(otaPackageDataCache, FIRMWARE_UPDATE_COAP_RESOURCE);
|
||||
this.server.coap().getServer().add(otaCoapResource);
|
||||
this.startLhServer();
|
||||
this.context.setServer(server);
|
||||
this.startLhServer();
|
||||
}
|
||||
|
||||
private void startLhServer() {
|
||||
|
||||
@ -20,6 +20,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.leshan.core.link.LinkParamValue;
|
||||
import org.eclipse.leshan.core.model.ObjectModel;
|
||||
import org.eclipse.leshan.core.model.ResourceModel;
|
||||
import org.eclipse.leshan.core.node.LwM2mMultipleResource;
|
||||
@ -108,7 +109,7 @@ public class LwM2mClient implements Serializable {
|
||||
@Getter
|
||||
private Long edrxCycle;
|
||||
@Getter
|
||||
private Registration registration;
|
||||
private transient Registration registration;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean asleep;
|
||||
@ -121,9 +122,9 @@ public class LwM2mClient implements Serializable {
|
||||
private boolean firstEdrxDownlink = true;
|
||||
|
||||
@Getter
|
||||
private Set<ContentFormat> clientSupportContentFormats;
|
||||
private transient Set<ContentFormat> clientSupportContentFormats;
|
||||
@Getter
|
||||
private ContentFormat defaultContentFormat;
|
||||
private transient ContentFormat defaultContentFormat;
|
||||
@Getter
|
||||
private final AtomicInteger retryAttempts;
|
||||
|
||||
@ -430,9 +431,9 @@ public class LwM2mClient implements Serializable {
|
||||
static private Set<ContentFormat> clientSupportContentFormat(Registration registration) {
|
||||
Set<ContentFormat> contentFormats = new HashSet<>();
|
||||
contentFormats.add(ContentFormat.DEFAULT);
|
||||
String code = Arrays.stream(registration.getObjectLinks()).filter(link -> link.getUriReference().equals("/")).findFirst().get().getLinkParams().get("ct").getUnquoted();
|
||||
if (code != null) {
|
||||
Set<ContentFormat> codes = Stream.of(code.replaceAll("\"", "").split(" ", -1))
|
||||
LinkParamValue ct = Arrays.stream(registration.getObjectLinks()).filter(link -> link.getUriReference().equals("/")).findFirst().get().getLinkParams().get("ct");
|
||||
if (ct != null) {
|
||||
Set<ContentFormat> codes = Stream.of(ct.getUnquoted().replaceAll("\"", "").split(" ", -1))
|
||||
.map(String::trim)
|
||||
.map(Integer::parseInt)
|
||||
.map(ContentFormat::fromCode)
|
||||
|
||||
@ -20,6 +20,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.leshan.core.SecurityMode;
|
||||
import org.eclipse.leshan.core.node.LwM2mPath;
|
||||
import org.eclipse.leshan.server.registration.Registration;
|
||||
import org.eclipse.leshan.server.registration.RegistrationStore;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
@ -72,6 +73,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
|
||||
private final LwM2MSessionManager sessionManager;
|
||||
private final TransportDeviceProfileCache deviceProfileCache;
|
||||
private final LwM2MModelConfigService modelConfigService;
|
||||
private final RegistrationStore registrationStore;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
@ -118,8 +120,11 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
|
||||
|
||||
private void updateFetchedClient(String nodeId, LwM2mClient client) {
|
||||
boolean updated = false;
|
||||
if (client.getRegistration() != null) {
|
||||
lwM2mClientsByRegistrationId.put(client.getRegistration().getId(), client);
|
||||
Registration registration = registrationStore.getRegistrationByEndpoint(client.getEndpoint());
|
||||
|
||||
if (registration != null) {
|
||||
client.setRegistration(registration);
|
||||
lwM2mClientsByRegistrationId.put(registration.getId(), client);
|
||||
}
|
||||
if (client.getSession() != null) {
|
||||
client.refreshSessionId(nodeId);
|
||||
|
||||
@ -111,7 +111,11 @@ public abstract class RpcDownlinkRequestCallbackProxy<R, T> implements DownlinkR
|
||||
}
|
||||
|
||||
protected void sendRpcReplyOnError(Exception e) {
|
||||
reply(LwM2MRpcResponseBody.builder().result(ResponseCode.INTERNAL_SERVER_ERROR.getName()).error(e.getMessage()).build());
|
||||
String error = e.getMessage();
|
||||
if (error == null) {
|
||||
error = e.toString();
|
||||
}
|
||||
reply(LwM2MRpcResponseBody.builder().result(ResponseCode.INTERNAL_SERVER_ERROR.getName()).error(error).build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.DeviceTransportType;
|
||||
import org.thingsboard.server.common.data.TransportPayloadType;
|
||||
import org.thingsboard.server.common.data.device.profile.MqttTopics;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.id.OtaPackageId;
|
||||
import org.thingsboard.server.common.data.ota.OtaPackageType;
|
||||
import org.thingsboard.server.common.data.rpc.RpcStatus;
|
||||
@ -1066,4 +1067,11 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
|
||||
deviceSessionCtx.onDeviceUpdate(sessionInfo, device, deviceProfileOpt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDeleted(DeviceId deviceId) {
|
||||
context.onAuthFailure(address);
|
||||
ChannelHandlerContext ctx = deviceSessionCtx.getChannel();
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.handler.codec.mqtt.MqttMessage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.thingsboard.server.common.data.DeviceProfile;
|
||||
import org.thingsboard.server.common.data.id.DeviceId;
|
||||
import org.thingsboard.server.common.data.rpc.RpcStatus;
|
||||
import org.thingsboard.server.common.transport.SessionMsgListener;
|
||||
import org.thingsboard.server.common.transport.TransportService;
|
||||
@ -136,6 +137,11 @@ public class GatewayDeviceSessionCtx extends MqttDeviceAwareSessionContext imple
|
||||
// This feature is not supported in the TB IoT Gateway yet.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeviceDeleted(DeviceId deviceId) {
|
||||
parent.onDeviceDeleted(this.getSessionInfo().getDeviceName());
|
||||
}
|
||||
|
||||
private boolean isAckExpected(MqttMessage message) {
|
||||
return message.fixedHeader().qosLevel().value() > 0;
|
||||
}
|
||||
|
||||
@ -178,6 +178,10 @@ public class GatewaySessionHandler {
|
||||
devices.forEach(this::deregisterSession);
|
||||
}
|
||||
|
||||
public void onDeviceDeleted(String deviceName) {
|
||||
deregisterSession(deviceName);
|
||||
}
|
||||
|
||||
public String getNodeId() {
|
||||
return context.getNodeId();
|
||||
}
|
||||
|
||||
@ -45,6 +45,8 @@ public interface SessionMsgListener {
|
||||
|
||||
void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse);
|
||||
|
||||
void onDeviceDeleted(DeviceId deviceId);
|
||||
|
||||
default void onUplinkNotification(UplinkNotificationMsg notificationMsg){};
|
||||
|
||||
default void onToTransportUpdateCredentials(ToTransportUpdateCredentialsProto toTransportUpdateCredentials){}
|
||||
@ -54,8 +56,6 @@ public interface SessionMsgListener {
|
||||
default void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device,
|
||||
Optional<DeviceProfile> deviceProfileOpt) {}
|
||||
|
||||
default void onDeviceDeleted(DeviceId deviceId) {}
|
||||
|
||||
default void onResourceUpdate(TransportProtos.ResourceUpdateMsg resourceUpdateMsgOpt) {}
|
||||
|
||||
default void onResourceDelete(TransportProtos.ResourceDeleteMsg resourceUpdateMsgOpt) {}
|
||||
|
||||
@ -91,6 +91,7 @@ import org.thingsboard.server.queue.provider.TbQueueProducerProvider;
|
||||
import org.thingsboard.server.queue.provider.TbTransportQueueFactory;
|
||||
import org.thingsboard.server.queue.scheduler.SchedulerComponent;
|
||||
import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
|
||||
import org.thingsboard.server.queue.util.AfterStartUp;
|
||||
import org.thingsboard.server.queue.util.TbTransportComponent;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
@ -227,6 +228,10 @@ public class DefaultTransportService implements TransportService {
|
||||
transportNotificationsConsumer.subscribe(Collections.singleton(tpi));
|
||||
transportApiRequestTemplate.init();
|
||||
mainConsumerExecutor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("transport-consumer"));
|
||||
}
|
||||
|
||||
@AfterStartUp
|
||||
private void start() {
|
||||
mainConsumerExecutor.execute(() -> {
|
||||
while (!stopped) {
|
||||
try {
|
||||
|
||||
@ -339,6 +339,21 @@ public class MqttClientTest extends AbstractContainerTest {
|
||||
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deviceDeletedClosingSession() throws Exception {
|
||||
restClient.login("tenant@thingsboard.org", "tenant");
|
||||
String deviceForDeletingTestName = "Device for deleting notification test";
|
||||
Device device = createDevice(deviceForDeletingTestName);
|
||||
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
|
||||
|
||||
MqttMessageListener listener = new MqttMessageListener();
|
||||
MqttClient mqttClient = getMqttClient(deviceCredentials, listener);
|
||||
|
||||
restClient.deleteDevice(device.getId());
|
||||
TimeUnit.SECONDS.sleep(3);
|
||||
Assert.assertFalse(mqttClient.isConnected());
|
||||
}
|
||||
|
||||
private RuleChainId createRootRuleChainForRpcResponse() throws Exception {
|
||||
RuleChain newRuleChain = new RuleChain();
|
||||
newRuleChain.setName("testRuleChain");
|
||||
|
||||
@ -374,7 +374,15 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
|
||||
Assert.assertEquals(clientResponse.toString(), serverResponse.getBody());
|
||||
}
|
||||
|
||||
private void checkAttribute(boolean client, String expectedValue) throws Exception{
|
||||
@Test
|
||||
public void deviceCreationAfterDeleted() throws Exception {
|
||||
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + this.createdDevice.getId());
|
||||
Optional<Device> deletedDevice = restClient.getDeviceById(this.createdDevice.getId());
|
||||
Assert.assertTrue(deletedDevice.isEmpty());
|
||||
this.createdDevice = createDeviceThroughGateway(mqttClient, gatewayDevice);
|
||||
}
|
||||
|
||||
private void checkAttribute(boolean client, String expectedValue) throws Exception {
|
||||
JsonObject gatewayAttributesRequest = new JsonObject();
|
||||
int messageId = new Random().nextInt(100);
|
||||
gatewayAttributesRequest.addProperty("id", messageId);
|
||||
|
||||
@ -134,6 +134,7 @@ import {
|
||||
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
||||
import cssjs from '@core/css/css';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { IAliasController } from '@core/api/widget-api.models';
|
||||
|
||||
// @dynamic
|
||||
@Component({
|
||||
@ -179,6 +180,9 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
||||
@Input()
|
||||
parentDashboard?: IDashboardComponent = null;
|
||||
|
||||
@Input()
|
||||
parentAliasController?: IAliasController = null;
|
||||
|
||||
@ViewChild('dashboardContainer') dashboardContainer: ElementRef<HTMLElement>;
|
||||
|
||||
prevDashboard: Dashboard;
|
||||
@ -419,7 +423,7 @@ export class DashboardPageComponent extends PageComponent implements IDashboardC
|
||||
this.readonly = this.embedded || (this.singlePageMode && !this.widgetEditMode && !this.route.snapshot.queryParamMap.get('edit'))
|
||||
|| this.forceFullscreen || this.isMobileApp || this.authUser.authority === Authority.CUSTOMER_USER;
|
||||
|
||||
this.dashboardCtx.aliasController = new AliasController(this.utils,
|
||||
this.dashboardCtx.aliasController = this.parentAliasController ? this.parentAliasController : new AliasController(this.utils,
|
||||
this.entityService,
|
||||
this.translate,
|
||||
() => this.dashboardCtx.stateController,
|
||||
|
||||
@ -21,7 +21,8 @@
|
||||
[hideToolbar]="true"
|
||||
[currentState]="currentState"
|
||||
[dashboard]="dashboard"
|
||||
[parentDashboard]="parentDashboard">
|
||||
[parentDashboard]="parentDashboard"
|
||||
[parentAliasController]="parentAliasController">
|
||||
</tb-dashboard-page>
|
||||
<div class="tb-absolute-fill tb-widget-error" *ngIf="!stateExists">
|
||||
<span>{{ 'dashboard.non-existent-dashboard-state-error' | translate:{stateId} }}</span>
|
||||
|
||||
@ -19,7 +19,7 @@ import { PageComponent } from '@shared/components/page.component';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { Dashboard, DashboardLayoutId } from '@shared/models/dashboard.models';
|
||||
import { StateObject } from '@core/api/widget-api.models';
|
||||
import { IAliasController, StateObject } from '@core/api/widget-api.models';
|
||||
import { updateEntityParams, WidgetContext } from '@home/models/widget-component.models';
|
||||
import { deepClone, isDefinedAndNotNull, isNotEmptyStr, objToBase64 } from '@core/utils';
|
||||
import { IDashboardComponent } from '@home/models/dashboard-component.models';
|
||||
@ -63,6 +63,8 @@ export class DashboardStateComponent extends PageComponent implements OnInit, On
|
||||
|
||||
parentDashboard: IDashboardComponent;
|
||||
|
||||
parentAliasController: IAliasController;
|
||||
|
||||
stateExists = true;
|
||||
|
||||
private stateSubscription: Subscription;
|
||||
@ -92,6 +94,7 @@ export class DashboardStateComponent extends PageComponent implements OnInit, On
|
||||
this.parentDashboard = this.ctx.parentDashboard ?
|
||||
this.ctx.parentDashboard : this.ctx.dashboard;
|
||||
if (this.syncParentStateParams) {
|
||||
this.parentAliasController = this.parentDashboard.aliasController;
|
||||
this.stateSubscription = this.ctx.stateController.dashboardCtrl.dashboardCtx.stateChanged.subscribe(() => {
|
||||
this.updateCurrentState();
|
||||
this.cd.markForCheck();
|
||||
|
||||
@ -41,6 +41,22 @@ export function createTooltip(target: L.Layer,
|
||||
return popup;
|
||||
}
|
||||
|
||||
export function disablePopup(target: L.Layer) {
|
||||
if (target.isPopupOpen()) {
|
||||
target.closePopup();
|
||||
}
|
||||
target.unbindPopup();
|
||||
target.off('popupopen');
|
||||
}
|
||||
|
||||
export function enablePopup(target: L.Layer, popup: L.Popup,
|
||||
settings: MarkerSettings | PolylineSettings | PolygonSettings, datasource: Datasource) {
|
||||
target.bindPopup(popup);
|
||||
target.on('popupopen', () => {
|
||||
bindPopupActions(popup, settings, datasource);
|
||||
});
|
||||
}
|
||||
|
||||
export function bindPopupActions(popup: L.Popup, settings: MarkerSettings | PolylineSettings | PolygonSettings,
|
||||
datasource: Datasource) {
|
||||
const actions = popup.getElement().getElementsByClassName('tb-custom-action');
|
||||
|
||||
@ -23,7 +23,7 @@ import {
|
||||
MarkerSettings,
|
||||
UnitedMapSettings
|
||||
} from './map-models';
|
||||
import { bindPopupActions, createTooltip, } from './maps-utils';
|
||||
import { bindPopupActions, createTooltip, disablePopup, enablePopup, } from './maps-utils';
|
||||
import { aspectCache, fillPattern, parseWithTranslation, processPattern, safeExecute } from './common-maps-utils';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import { isDefined, isDefinedAndNotNull } from '@core/utils';
|
||||
@ -37,6 +37,7 @@ export class Marker {
|
||||
tooltip: L.Popup;
|
||||
data: FormattedData;
|
||||
dataSources: FormattedData[];
|
||||
isDragging = false;
|
||||
|
||||
constructor(private map: LeafletMap, private location: L.LatLng, public settings: UnitedMapSettings,
|
||||
data?: FormattedData, dataSources?, onDragendListener?) {
|
||||
@ -64,6 +65,7 @@ export class Marker {
|
||||
}
|
||||
|
||||
if (this.settings.markerClick) {
|
||||
if (!this.isDragging) {
|
||||
this.leafletMarker.on('click', (event: LeafletMouseEvent) => {
|
||||
for (const action in this.settings.markerClick) {
|
||||
if (typeof (this.settings.markerClick[action]) === 'function') {
|
||||
@ -72,9 +74,22 @@ export class Marker {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.draggableMarker && onDragendListener) {
|
||||
this.leafletMarker.on('pm:dragend', (e) => onDragendListener(e, this.data));
|
||||
this.leafletMarker.on('pm:dragend', (e) => {
|
||||
onDragendListener(e, this.data);
|
||||
this.isDragging = false;
|
||||
if (settings.showTooltip && settings.showTooltipAction === 'click') {
|
||||
enablePopup(this.leafletMarker, this.tooltip, settings, this.data.$datasource);
|
||||
}
|
||||
});
|
||||
this.leafletMarker.on('pm:dragstart', (e) => {
|
||||
this.isDragging = true;
|
||||
if (settings.showTooltip && settings.showTooltipAction === 'click') {
|
||||
disablePopup(this.leafletMarker);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user