committed by
GitHub
43 changed files with 1097 additions and 513 deletions
@ -0,0 +1,29 @@ |
|||||
|
/** |
||||
|
* 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.common.data.device.profile.lwm2m.bootstrap; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
|
||||
|
@ApiModel |
||||
|
@Data |
||||
|
public class LwM2MServerSecurityConfigDefault extends LwM2MServerSecurityConfig { |
||||
|
@ApiModelProperty(position = 5, value = "Host for 'Security' mode (DTLS)", example = "0.0.0.0", readOnly = true) |
||||
|
protected String securityHost; |
||||
|
@ApiModelProperty(position = 6, value = "Port for 'Security' mode (DTLS): Lwm2m Server or Bootstrap Server", example = "5686 or 5688", readOnly = true) |
||||
|
protected Integer securityPort; |
||||
|
} |
||||
@ -0,0 +1,255 @@ |
|||||
|
/** |
||||
|
* 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.transport.lwm2m.bootstrap.store; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.leshan.core.Link; |
||||
|
import org.eclipse.leshan.core.node.LwM2mObject; |
||||
|
import org.eclipse.leshan.core.node.LwM2mPath; |
||||
|
import org.eclipse.leshan.core.request.*; |
||||
|
import org.eclipse.leshan.core.response.BootstrapDiscoverResponse; |
||||
|
import org.eclipse.leshan.core.response.BootstrapReadResponse; |
||||
|
import org.eclipse.leshan.core.response.LwM2mResponse; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapConfig; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapConfigStore; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapSession; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapTaskProvider; |
||||
|
import org.eclipse.leshan.server.bootstrap.BootstrapUtil; |
||||
|
|
||||
|
import java.net.InetSocketAddress; |
||||
|
import java.util.*; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
import static org.eclipse.leshan.server.bootstrap.BootstrapUtil.toWriteRequest; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class LwM2MBootstrapConfigStoreTaskProvider implements BootstrapTaskProvider { |
||||
|
|
||||
|
private BootstrapConfigStore store; |
||||
|
|
||||
|
private Map<Integer, String> supportedObjects; |
||||
|
|
||||
|
/** |
||||
|
* Map<serverId, InstanceId> |
||||
|
*/ |
||||
|
protected Map<Integer, Integer> securityInstances; |
||||
|
protected Map<Integer, Integer> serverInstances; |
||||
|
protected Integer bootstrapServerIdOld; |
||||
|
protected Integer bootstrapServerIdNew; |
||||
|
|
||||
|
public LwM2MBootstrapConfigStoreTaskProvider(BootstrapConfigStore store) { |
||||
|
this.store = store; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Tasks getTasks(BootstrapSession session, List<LwM2mResponse> previousResponse) { |
||||
|
|
||||
|
BootstrapConfig config = store.get(session.getEndpoint(), session.getIdentity(), session); |
||||
|
if (config == null) { |
||||
|
return null; |
||||
|
} |
||||
|
if (previousResponse == null && shouldStartWithDiscover(config)) { |
||||
|
Tasks tasks = new Tasks(); |
||||
|
tasks.requestsToSend = new ArrayList<>(1); |
||||
|
tasks.requestsToSend.add(new BootstrapDiscoverRequest()); |
||||
|
tasks.last = false; |
||||
|
return tasks; |
||||
|
} else { |
||||
|
Tasks tasks = new Tasks(); |
||||
|
if (this.supportedObjects == null) { |
||||
|
initSupportedObjectsDefault(); |
||||
|
|
||||
|
} |
||||
|
// add supportedObjects
|
||||
|
tasks.supportedObjects = this.supportedObjects; |
||||
|
// handle bootstrap discover response
|
||||
|
if (previousResponse != null) { |
||||
|
if (previousResponse.get(0) instanceof BootstrapDiscoverResponse) { |
||||
|
BootstrapDiscoverResponse discoverResponse = (BootstrapDiscoverResponse) previousResponse.get(0); |
||||
|
if (discoverResponse.isSuccess()) { |
||||
|
this.initAfterBootstrapDiscover(discoverResponse); |
||||
|
findSecurityInstanceId(discoverResponse.getObjectLinks()); |
||||
|
} else { |
||||
|
log.info( |
||||
|
"Bootstrap Discover return error {} : to continue bootstrap session without autoIdForSecurityObject mode. {}", |
||||
|
discoverResponse, session); |
||||
|
} |
||||
|
if (this.securityInstances.get(0) == null) { |
||||
|
log.error( |
||||
|
"Unable to find bootstrap server instance in Security Object (0) in response {}: unable to continue bootstrap session with autoIdForSecurityObject mode. {}", |
||||
|
discoverResponse, session); |
||||
|
return null; |
||||
|
} |
||||
|
tasks.requestsToSend = new ArrayList<>(1); |
||||
|
tasks.requestsToSend.add(new BootstrapReadRequest("/1")); |
||||
|
tasks.last = false; |
||||
|
return tasks; |
||||
|
} |
||||
|
BootstrapReadResponse readResponse = (BootstrapReadResponse) previousResponse.get(0); |
||||
|
findServerInstanceId(readResponse); |
||||
|
// create requests from config
|
||||
|
tasks.requestsToSend = this.toRequests(config, |
||||
|
config.contentFormat != null ? config.contentFormat : session.getContentFormat()); |
||||
|
} else { |
||||
|
// create requests from config
|
||||
|
tasks.requestsToSend = BootstrapUtil.toRequests(config, |
||||
|
config.contentFormat != null ? config.contentFormat : session.getContentFormat()); |
||||
|
|
||||
|
} |
||||
|
return tasks; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected boolean shouldStartWithDiscover(BootstrapConfig config) { |
||||
|
return config.autoIdForSecurityObject; |
||||
|
} |
||||
|
|
||||
|
protected void findSecurityInstanceId(Link[] objectLinks) { |
||||
|
log.info("Object after discover: [{}]", objectLinks); |
||||
|
this.securityInstances = new HashMap<>(); |
||||
|
for (Link link : objectLinks) { |
||||
|
if (link.getUrl().startsWith("/0/")) { |
||||
|
try { |
||||
|
LwM2mPath path = new LwM2mPath(link.getUrl()); |
||||
|
if (path.isObjectInstance()) { |
||||
|
if (link.getAttributes().containsKey("ssid")) { |
||||
|
int serverId = Integer.valueOf(link.getAttributes().get("ssid")); |
||||
|
if (!this.securityInstances.containsKey(serverId)) { |
||||
|
this.securityInstances.put(serverId, path.getObjectInstanceId()); |
||||
|
} else { |
||||
|
log.error(String.format("Invalid lwm2mSecurityInstance by [{}]", path.getObjectInstanceId())); |
||||
|
} |
||||
|
this.securityInstances.put(Integer.valueOf(link.getAttributes().get("ssid")), path.getObjectInstanceId()); |
||||
|
} else { |
||||
|
if (!this.securityInstances.containsKey(0)) { |
||||
|
this.securityInstances.put(0, path.getObjectInstanceId()); |
||||
|
} else { |
||||
|
log.error(String.format("Invalid bootstrapSecurityInstance by [{}]", path.getObjectInstanceId())); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
// ignore if this is not a LWM2M path
|
||||
|
log.error("Invalid LwM2MPath starting by \"/0/\""); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void findServerInstanceId(BootstrapReadResponse readResponse) { |
||||
|
this.serverInstances = new HashMap<>(); |
||||
|
((LwM2mObject) readResponse.getContent()).getInstances().values().forEach(instance -> { |
||||
|
serverInstances.put(((Long) instance.getResource(0).getValue()).intValue(), instance.getId()); |
||||
|
}); |
||||
|
if (this.securityInstances != null && this.securityInstances.size() > 0 && this.serverInstances != null && this.serverInstances.size() > 0) { |
||||
|
this.findBootstrapServerId(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected void findBootstrapServerId() { |
||||
|
Map<Integer, Integer> filteredMap = this.serverInstances.entrySet() |
||||
|
.stream().filter(x -> !this.securityInstances.containsKey(x.getKey())) |
||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); |
||||
|
this.bootstrapServerIdOld = filteredMap.keySet().stream().findFirst().get(); |
||||
|
} |
||||
|
|
||||
|
public BootstrapConfigStore getStore() { |
||||
|
return this.store; |
||||
|
} |
||||
|
|
||||
|
private void initAfterBootstrapDiscover(BootstrapDiscoverResponse response) { |
||||
|
Link[] links = response.getObjectLinks(); |
||||
|
Arrays.stream(links).forEach(link -> { |
||||
|
LwM2mPath path = new LwM2mPath(link.getUrl()); |
||||
|
if (path != null && !path.isRoot() && path.getObjectId() < 3) { |
||||
|
if (path.isObject()) { |
||||
|
String ver = link.getAttributes().get("ver") != null ? link.getAttributes().get("ver") : "1.0"; |
||||
|
this.supportedObjects.put(path.getObjectId(), ver); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public List<BootstrapDownlinkRequest<? extends LwM2mResponse>> toRequests(BootstrapConfig bootstrapConfig, |
||||
|
ContentFormat contentFormat) { |
||||
|
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requests = new ArrayList<>(); |
||||
|
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requestsDelete = new ArrayList<>(); |
||||
|
List<BootstrapDownlinkRequest<? extends LwM2mResponse>> requestsWrite = new ArrayList<>(); |
||||
|
boolean isBsServer = false; |
||||
|
boolean isLwServer = false; |
||||
|
/** Map<serverId, InstanceId> */ |
||||
|
Map<Integer, Integer> instances = new HashMap<>(); |
||||
|
// handle security
|
||||
|
int id = 0; |
||||
|
for (BootstrapConfig.ServerSecurity security : new TreeMap<>(bootstrapConfig.security).values()) { |
||||
|
if (security.bootstrapServer) { |
||||
|
requestsWrite.add(toWriteRequest(this.securityInstances.get(0), security, contentFormat)); |
||||
|
isBsServer = true; |
||||
|
this.bootstrapServerIdNew = security.serverId; |
||||
|
instances.put(security.serverId, this.securityInstances.get(0)); |
||||
|
} else { |
||||
|
if (id == this.securityInstances.get(0)) { |
||||
|
id++; |
||||
|
} |
||||
|
requestsWrite.add(toWriteRequest(id, security, contentFormat)); |
||||
|
instances.put(security.serverId, id); |
||||
|
isLwServer = true; |
||||
|
if (!isBsServer && this.securityInstances.containsKey(security.serverId) && id != this.securityInstances.get(security.serverId)) { |
||||
|
requestsDelete.add(new BootstrapDeleteRequest("/0/" + this.securityInstances.get(security.serverId))); |
||||
|
} |
||||
|
id++; |
||||
|
} |
||||
|
} |
||||
|
// handle server
|
||||
|
for (Map.Entry<Integer, BootstrapConfig.ServerConfig> server : bootstrapConfig.servers.entrySet()) { |
||||
|
int securityInstanceId = instances.get(server.getValue().shortId); |
||||
|
requestsWrite.add(toWriteRequest(securityInstanceId, server.getValue(), contentFormat)); |
||||
|
if (!isLwServer && this.bootstrapServerIdNew != null && server.getValue().shortId == this.bootstrapServerIdNew && |
||||
|
(this.bootstrapServerIdNew != this.bootstrapServerIdOld || securityInstanceId != this.serverInstances.get(this.bootstrapServerIdOld))) { |
||||
|
requestsDelete.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(this.bootstrapServerIdOld))); |
||||
|
|
||||
|
} else { |
||||
|
if (!isBsServer && this.serverInstances.containsKey(server.getValue().shortId) && securityInstanceId != this.serverInstances.get(server.getValue().shortId)) { |
||||
|
requestsDelete.add(new BootstrapDeleteRequest("/1/" + this.serverInstances.get(server.getValue().shortId))); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// handle acl
|
||||
|
for (Map.Entry<Integer, BootstrapConfig.ACLConfig> acl : bootstrapConfig.acls.entrySet()) { |
||||
|
requestsWrite.add(toWriteRequest(acl.getKey(), acl.getValue(), contentFormat)); |
||||
|
} |
||||
|
// handle delete
|
||||
|
if (isBsServer & isLwServer) { |
||||
|
requests.add(new BootstrapDeleteRequest("/0")); |
||||
|
requests.add(new BootstrapDeleteRequest("/1")); |
||||
|
} else if (requestsDelete.size() > 0) { |
||||
|
requests.addAll(requestsDelete); |
||||
|
} |
||||
|
// handle write
|
||||
|
if (requestsWrite.size() > 0) { |
||||
|
requests.addAll(requestsWrite); |
||||
|
} |
||||
|
return (requests); |
||||
|
} |
||||
|
|
||||
|
private void initSupportedObjectsDefault() { |
||||
|
this.supportedObjects = new HashMap<>(); |
||||
|
this.supportedObjects.put(0, "1.1"); |
||||
|
this.supportedObjects.put(1, "1.1"); |
||||
|
this.supportedObjects.put(2, "1.0"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<form [formGroup]="addConfigServerFormGroup" style="width: 400px;"> |
||||
|
<mat-toolbar fxLayout="row" color="primary"> |
||||
|
<h2 translate>device-profile.lwm2m.add-new-server-title</h2> |
||||
|
<span fxFlex></span> |
||||
|
<button mat-button mat-icon-button (click)="cancel()" type="button"> |
||||
|
<mat-icon class="material-icons">close</mat-icon> |
||||
|
</button> |
||||
|
</mat-toolbar> |
||||
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> |
||||
|
</mat-progress-bar> |
||||
|
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> |
||||
|
<div mat-dialog-content fxLayout="column"> |
||||
|
<mat-form-field> |
||||
|
<mat-label>{{ 'device-profile.lwm2m.server-type' | translate }}</mat-label> |
||||
|
<mat-select formControlName="serverType"> |
||||
|
<mat-option *ngFor="let serverType of serverTypes" |
||||
|
[value]="serverType"> |
||||
|
{{ serverConfigTypeNamesMap.get(serverType) | translate }} |
||||
|
</mat-option> |
||||
|
</mat-select> |
||||
|
</mat-form-field> |
||||
|
</div> |
||||
|
<div mat-dialog-actions fxLayout="row"> |
||||
|
<span fxFlex></span> |
||||
|
<button mat-button color="primary" |
||||
|
type="button" |
||||
|
[disabled]="(isLoading$ | async)" |
||||
|
(click)="cancel()" cdkFocusInitial> |
||||
|
{{ 'action.cancel' | translate }} |
||||
|
</button> |
||||
|
<button mat-button mat-raised-button color="primary" |
||||
|
type="button" |
||||
|
[disabled]="(isLoading$ | async) || addConfigServerFormGroup.invalid" |
||||
|
(click)="addServerConfig()"> |
||||
|
{{ 'action.add' | translate }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</form> |
||||
@ -0,0 +1,60 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component } from '@angular/core'; |
||||
|
import { DialogComponent } from '@shared/components/dialog.component'; |
||||
|
import { FormBuilder, FormGroup } from '@angular/forms'; |
||||
|
import { Store } from '@ngrx/store'; |
||||
|
import { AppState } from '@core/core.state'; |
||||
|
import { Router } from '@angular/router'; |
||||
|
import { MatDialogRef } from '@angular/material/dialog'; |
||||
|
import { |
||||
|
ServerConfigType, |
||||
|
ServerConfigTypeTranslationMap |
||||
|
} from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-profile-lwm2m-bootstrap-add-config-server-dialog', |
||||
|
templateUrl: './lwm2m-bootstrap-add-config-server-dialog.component.html' |
||||
|
}) |
||||
|
export class Lwm2mBootstrapAddConfigServerDialogComponent extends DialogComponent<Lwm2mBootstrapAddConfigServerDialogComponent> { |
||||
|
|
||||
|
addConfigServerFormGroup: FormGroup; |
||||
|
|
||||
|
serverTypes = Object.values(ServerConfigType); |
||||
|
serverConfigTypeNamesMap = ServerConfigTypeTranslationMap; |
||||
|
|
||||
|
constructor(protected store: Store<AppState>, |
||||
|
protected router: Router, |
||||
|
private fb: FormBuilder, |
||||
|
public dialogRef: MatDialogRef<Lwm2mBootstrapAddConfigServerDialogComponent, boolean>, |
||||
|
) { |
||||
|
super(store, router, dialogRef); |
||||
|
this.addConfigServerFormGroup = this.fb.group({ |
||||
|
serverType: [ServerConfigType.LWM2M] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
addServerConfig() { |
||||
|
this.dialogRef.close(this.addConfigServerFormGroup.get('serverType').value === ServerConfigType.BOOTSTRAP); |
||||
|
} |
||||
|
|
||||
|
cancel(): void { |
||||
|
this.dialogRef.close(null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,39 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
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. |
||||
|
|
||||
|
--> |
||||
|
<div fxLayout="column"> |
||||
|
<mat-accordion multi="true"> |
||||
|
<div *ngFor="let serverConfig of serverConfigsFromArray().controls; trackBy: trackByParams; let $index = index;"> |
||||
|
<tb-profile-lwm2m-device-config-server |
||||
|
[formControl]="serverConfig" |
||||
|
(removeServer)="removeServerConfig($event, $index)"> |
||||
|
</tb-profile-lwm2m-device-config-server> |
||||
|
</div> |
||||
|
</mat-accordion> |
||||
|
<div *ngIf="!serverConfigsFromArray().controls.length" style="margin:32px 0"> |
||||
|
<span translate fxLayoutAlign="center center" |
||||
|
class="tb-prompt">device-profile.lwm2m.no-config-servers</span> |
||||
|
</div> |
||||
|
<div *ngIf="!disabled" style="padding-top: 16px;"> |
||||
|
<button mat-raised-button color="primary" |
||||
|
type="button" |
||||
|
(click)="addServerConfig()"> |
||||
|
<span>{{ (isBootstrapAdded() ? 'device-profile.lwm2m.add-lwm2m-server-config' : |
||||
|
'device-profile.lwm2m.add-server-config') | translate}}</span> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
@ -0,0 +1,185 @@ |
|||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
|
||||
|
import { Component, forwardRef, Input, OnInit } from '@angular/core'; |
||||
|
import { |
||||
|
AbstractControl, |
||||
|
ControlValueAccessor, |
||||
|
FormArray, |
||||
|
FormBuilder, FormControl, |
||||
|
FormGroup, |
||||
|
NG_VALIDATORS, |
||||
|
NG_VALUE_ACCESSOR |
||||
|
} from '@angular/forms'; |
||||
|
import { of, Subscription } from 'rxjs'; |
||||
|
import { ServerSecurityConfig } from '@home/components/profile/device/lwm2m/lwm2m-profile-config.models'; |
||||
|
import { TranslateService } from '@ngx-translate/core'; |
||||
|
import { DialogService } from '@core/services/dialog.service'; |
||||
|
import { MatDialog } from '@angular/material/dialog'; |
||||
|
import { Lwm2mBootstrapAddConfigServerDialogComponent } from '@home/components/profile/device/lwm2m/lwm2m-bootstrap-add-config-server-dialog.component'; |
||||
|
import { mergeMap } from 'rxjs/operators'; |
||||
|
import { DeviceProfileService } from '@core/http/device-profile.service'; |
||||
|
import { Lwm2mSecurityType } from '@shared/models/lwm2m-security-config.models'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-profile-lwm2m-bootstrap-config-servers', |
||||
|
templateUrl: './lwm2m-bootstrap-config-servers.component.html', |
||||
|
providers: [ |
||||
|
{ |
||||
|
provide: NG_VALUE_ACCESSOR, |
||||
|
useExisting: forwardRef(() => Lwm2mBootstrapConfigServersComponent), |
||||
|
multi: true |
||||
|
}, |
||||
|
{ |
||||
|
provide: NG_VALIDATORS, |
||||
|
useExisting: forwardRef(() => Lwm2mBootstrapConfigServersComponent), |
||||
|
multi: true, |
||||
|
} |
||||
|
] |
||||
|
}) |
||||
|
export class Lwm2mBootstrapConfigServersComponent implements OnInit, ControlValueAccessor { |
||||
|
|
||||
|
bootstrapConfigServersFormGroup: FormGroup; |
||||
|
|
||||
|
@Input() |
||||
|
disabled: boolean; |
||||
|
|
||||
|
private valueChangeSubscription: Subscription = null; |
||||
|
|
||||
|
private propagateChange = (v: any) => { }; |
||||
|
|
||||
|
constructor(public translate: TranslateService, |
||||
|
public matDialog: MatDialog, |
||||
|
private dialogService: DialogService, |
||||
|
private deviceProfileService: DeviceProfileService, |
||||
|
private fb: FormBuilder) { |
||||
|
} |
||||
|
|
||||
|
registerOnChange(fn: any): void { |
||||
|
this.propagateChange = fn; |
||||
|
} |
||||
|
|
||||
|
registerOnTouched(fn: any): void { |
||||
|
} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
this.bootstrapConfigServersFormGroup = this.fb.group({ |
||||
|
serverConfigs: this.fb.array([]) |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
serverConfigsFromArray(): FormArray { |
||||
|
return this.bootstrapConfigServersFormGroup.get('serverConfigs') as FormArray; |
||||
|
} |
||||
|
|
||||
|
setDisabledState(isDisabled: boolean): void { |
||||
|
this.disabled = isDisabled; |
||||
|
if (this.disabled) { |
||||
|
this.bootstrapConfigServersFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.bootstrapConfigServersFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
writeValue(serverConfigs: Array<ServerSecurityConfig> | null): void { |
||||
|
if (this.valueChangeSubscription) { |
||||
|
this.valueChangeSubscription.unsubscribe(); |
||||
|
} |
||||
|
const serverConfigsControls: Array<AbstractControl> = []; |
||||
|
if (serverConfigs) { |
||||
|
serverConfigs.forEach((serverConfig) => { |
||||
|
serverConfigsControls.push(this.fb.control(serverConfig)); |
||||
|
}); |
||||
|
} |
||||
|
this.bootstrapConfigServersFormGroup.setControl('serverConfigs', this.fb.array(serverConfigsControls)); |
||||
|
if (this.disabled) { |
||||
|
this.bootstrapConfigServersFormGroup.disable({emitEvent: false}); |
||||
|
} else { |
||||
|
this.bootstrapConfigServersFormGroup.enable({emitEvent: false}); |
||||
|
} |
||||
|
this.valueChangeSubscription = this.bootstrapConfigServersFormGroup.valueChanges.subscribe(() => { |
||||
|
this.updateModel(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
trackByParams(index: number): number { |
||||
|
return index; |
||||
|
} |
||||
|
|
||||
|
removeServerConfig($event: Event, index: number) { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
$event.preventDefault(); |
||||
|
} |
||||
|
this.dialogService.confirm( |
||||
|
this.translate.instant('device-profile.lwm2m.delete-server-title'), |
||||
|
this.translate.instant('device-profile.lwm2m.delete-server-text'), |
||||
|
this.translate.instant('action.no'), |
||||
|
this.translate.instant('action.yes'), |
||||
|
true |
||||
|
).subscribe((result) => { |
||||
|
if (result) { |
||||
|
this.serverConfigsFromArray().removeAt(index); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
addServerConfig(): void { |
||||
|
const addDialogObs = this.isBootstrapAdded() ? of(false) : |
||||
|
this.matDialog.open<Lwm2mBootstrapAddConfigServerDialogComponent>(Lwm2mBootstrapAddConfigServerDialogComponent, { |
||||
|
disableClose: true, |
||||
|
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'] |
||||
|
}).afterClosed(); |
||||
|
const addServerConfigObs = addDialogObs.pipe( |
||||
|
mergeMap((isBootstrap) => { |
||||
|
if (isBootstrap === null) { |
||||
|
return of(null); |
||||
|
} |
||||
|
return this.deviceProfileService.getLwm2mBootstrapSecurityInfoBySecurityType(isBootstrap, Lwm2mSecurityType.NO_SEC); |
||||
|
}) |
||||
|
); |
||||
|
addServerConfigObs.subscribe((serverConfig) => { |
||||
|
if (serverConfig) { |
||||
|
serverConfig.securityMode = Lwm2mSecurityType.NO_SEC; |
||||
|
this.serverConfigsFromArray().push(this.fb.control(serverConfig)); |
||||
|
this.updateModel(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public validate(c: FormControl) { |
||||
|
return (this.bootstrapConfigServersFormGroup.valid) ? null : { |
||||
|
serverConfigs: { |
||||
|
valid: false, |
||||
|
}, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public isBootstrapAdded() { |
||||
|
const serverConfigsArray = this.serverConfigsFromArray().getRawValue(); |
||||
|
for (let i = 0; i < serverConfigsArray.length; i++) { |
||||
|
if (serverConfigsArray[i].bootstrapServerIs) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
private updateModel() { |
||||
|
const serverConfigs: Array<ServerSecurityConfig> = this.serverConfigsFromArray().value; |
||||
|
this.propagateChange(serverConfigs); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue