RIC Dashboard Release Notes
===========================
-Version 1.0.2, 10 May 2019
+Version 1.0.2, 13 May 2019
--------------------------
* Update A1 mediator client to version 0.4.0
* Add E2 response message with timestamp and status code
* Fetch xAPP instance status information from xAPP Manager and display in dashboard
* Allow the user to initiate an E2 (X2) connection between RIC and gNB/eNB
+* User input validations on connections between RIC and eNB/gNB in the dashboard
* Add ANR xApp backend with mock implementation
Version 1.0.1, 6 May 2019
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
-import { CatalogComponent, AppCatalogDeployDialog, } from './catalog/catalog.component';
-import { UiService} from './services/ui/ui.service';
-import { AdminService} from './services/admin/admin.service';
-import { CatalogService} from './services/catalog/catalog.service';
-import { ControlService} from './services/control/control.service';
-import { SignalService} from './services/signal/signal.service';
+import { CatalogComponent, AppCatalogDeployDialog } from './catalog/catalog.component';
+import { UiService } from './services/ui/ui.service';
+import { AdminService } from './services/admin/admin.service';
+import { CatalogService } from './services/catalog/catalog.service';
+import { ControlService } from './services/control/control.service';
+import { SignalService } from './services/signal/signal.service';
import { SidenavListComponent } from './navigation/sidenav-list/sidenav-list.component';
import { ControlComponent } from './control/control.component';
-import { SignalComponent, AppRANConnectDialog} from './signal/signal.component';
+import { SignalComponent, AppRANConnectDialog } from './signal/signal.component';
import { StatsComponent } from './stats/stats.component';
import { AdminComponent } from './admin/admin.component';
-import { CatalogCardComponent} from './ui/catalog-card/catalog-card.component';
-import { ControlCardComponent} from './ui/control-card/control-card.component';
-import { StatCardComponent} from './ui/stat-card/stat-card.component';
+import { CatalogCardComponent } from './ui/catalog-card/catalog-card.component';
+import { ControlCardComponent } from './ui/control-card/control-card.component';
+import { StatCardComponent } from './ui/stat-card/stat-card.component';
import { ModalEventComponent } from './ui/modal-event/modal-event.component';
import { XappComponent } from './xapp/xapp.component';
import { ConfigEventComponent } from './ui/config-event/config-event.component';
Setup RAN Connection
</div>
-<div mat-dialog-content>
- <mat-form-field class="input-display-block">
- <input matInput placeholder="eNB/gNB Name" [(ngModel)]="data.ranName">
- </mat-form-field>
- <mat-form-field class="input-display-block">
- <input matInput placeholder="IP" [(ngModel)]="data.ranIp">
- </mat-form-field>
- <mat-form-field class="input-display-block">
- <input matInput placeholder="Port" [(ngModel)]="data.ranPort">
- </mat-form-field>
-</div>
-
- <div mat-dialog-actions class="modal-footer justify-content-center">
- <button class="mat-raised-button" (click)="close()">Cancel</button>
- <button class="mat-raised-button mat-primary" (click)="connectRAN()">Connect</button>
-</div>
+<form [formGroup]="ranDialogForm" novalidate autocomplete="off">
+ <div mat-dialog-content>
+ <mat-form-field class="input-display-block">
+ <input matInput type="text" placeholder="eNB/gNB Name" [(ngModel)]="data.ranName" formControlName="ranName">
+ <mat-hint align="end">Not more then 50 characters long.</mat-hint>
+ <mat-error *ngIf="validateControl('ranName') && hasError('ranName', 'required')">Name is required</mat-error>
+ <mat-error *ngIf="hasError('ranName', 'length')">Valid name is required</mat-error>
+ </mat-form-field>
+ <mat-form-field class="input-display-block">
+ <input matInput type="text" placeholder="IP" [(ngModel)]="data.ranIp" formControlName="ranIp">
+ <mat-error *ngIf="validateControl('ranIp') && hasError('ranIp', 'required')">IP is required</mat-error>
+ <mat-error *ngIf="hasError('ranIp', 'pattern')">Valid IP is required</mat-error>
+ </mat-form-field>
+ <mat-form-field class="input-display-block">
+ <input matInput type="text" placeholder="Port" [(ngModel)]="data.ranPort" formControlName="ranPort">
+ <mat-error *ngIf="validateControl('ranPort') && hasError('ranPort', 'required')">Port is required</mat-error>
+ <mat-error *ngIf="hasError('ranPort', 'pattern')">Valid port number is required</mat-error>
+ </mat-form-field>
+ </div>
+ <div mat-dialog-actions class="modal-footer justify-content-center">
+ <button class="mat-raised-button" (click)="close()">Cancel</button>
+ <button class="mat-raised-button mat-primary"
+ [disabled]="(validateControl('ranName') && hasError('ranName', 'required'))
+ || (validateControl('ranIp') && hasError('ranIp', 'required'))
+ || (validateControl('ranPort') && hasError('ranPort', 'required'))
+ || hasError('ranName', 'maxlength')
+ || hasError('ranIp', 'pattern')
+ || hasError('ranPort', 'pattern')
+ || (!data.ranName || !data.ranIp || !data.ranPort)"
+ (click)="connectRAN()">Connect</button>
+ </div>
+</form>
import { SignalService } from '../services/signal/signal.service';
import { Router } from '@angular/router';
import { MatDialog, MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
-import {MatFormFieldModule} from '@angular/material';
-import { FormGroup, FormControl, FormBuilder } from '@angular/forms';
+import { MatFormFieldModule } from '@angular/material';
+import { FormGroup, FormControl, FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Rx';
templateUrl: 'signal.component.html',
styleUrls: ['signal.component.css']
})
-export class SignalComponent {
+export class SignalComponent {
settings = {
hideSubHeader: true,
actions: {
@Inject(MAT_DIALOG_DATA) public data: DialogData) {
}
+ public ranDialogForm: FormGroup;
+
+ public ranName: string;
+
+ public ranIp: number;
+
+ public ranPort: number;
+
ngOnInit() {
+ const ipPattern = '((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))';
+ const portPattern = '^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$';
+ this.ranDialogForm = new FormGroup({
+ ranName: new FormControl('', [Validators.required, Validators.maxLength(50)]),
+ ranIp: new FormControl('', [Validators.required, Validators.pattern(ipPattern)]),
+ ranPort: new FormControl('', [Validators.required, Validators.pattern(portPattern)])
+ });
}
close() {
this.dialogRef.close();
}
+ public hasError(controlName: string, errorName: string) {
+ if (this.ranDialogForm.controls[controlName].hasError(errorName))
+ return true;
+ return false;
+ }
+
+ public validateControl(controlName: string) {
+ if (this.ranDialogForm.controls[controlName].invalid && this.ranDialogForm.controls[controlName].touched)
+ return true;
+ return false;
+ }
}