@ -15,15 +15,24 @@
///
///
import { Component , forwardRef , Input , OnInit } from '@angular/core' ;
import { Component , forwardRef , Input , OnInit } from '@angular/core' ;
import { ControlValueAccessor , FormBuilder , FormGroup , NG_VALUE_ACCESSOR , Validators } from '@angular/forms' ;
import {
ControlValueAccessor ,
FormBuilder ,
FormControl ,
FormGroup ,
NG_VALUE_ACCESSOR ,
ValidatorFn ,
Validators
} from '@angular/forms' ;
import { Store } from '@ngrx/store' ;
import { Store } from '@ngrx/store' ;
import { AppState } from '@app/core/core.state' ;
import { AppState } from '@app/core/core.state' ;
import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
import {
import {
DeviceProfileTransportConfiguration ,
DeviceProfileTransportConfiguration ,
DeviceTransportType , MqttDeviceProfileTransportConfiguration
DeviceTransportType ,
MqttDeviceProfileTransportConfiguration
} from '@shared/models/device.models' ;
} from '@shared/models/device.models' ;
import { isDefinedAndNotNull } from '../../../../../core/utils' ;
import { isDefinedAndNotNull } from '@ core/utils' ;
@Component ( {
@Component ( {
selector : 'tb-mqtt-device-profile-transport-configuration' ,
selector : 'tb-mqtt-device-profile-transport-configuration' ,
@ -41,11 +50,10 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
private requiredValue : boolean ;
private requiredValue : boolean ;
private MQTTTopicPattern = new RegExp ( '^((?![#+]).)*$|^(.*[^#]\/|^)#$|^(.*\/|^)\+(\/.*|$)$' ) ;
get required ( ) : boolean {
get required ( ) : boolean {
return this . requiredValue ;
return this . requiredValue ;
}
}
@Input ( )
@Input ( )
set required ( value : boolean ) {
set required ( value : boolean ) {
this . requiredValue = coerceBooleanProperty ( value ) ;
this . requiredValue = coerceBooleanProperty ( value ) ;
@ -70,10 +78,10 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
ngOnInit() {
ngOnInit() {
this . mqttDeviceProfileTransportConfigurationFormGroup = this . fb . group ( {
this . mqttDeviceProfileTransportConfigurationFormGroup = this . fb . group ( {
configuration : this.fb.group ( {
configuration : this.fb.group ( {
deviceAttributesTopic : [ null , [ Validators . required , Validators . pattern ( this . MQTTTopicPattern ) ] ] ,
deviceAttributesTopic : [ null , [ Validators . required , this . validationMQTTTopic ( ) ] ] ,
deviceTelemetryTopic : [ null , [ Validators . required , Validators . pattern ( this . MQTTTopicPattern ) ] ] ,
deviceTelemetryTopic : [ null , [ Validators . required , this . validationMQTTTopic ( ) ] ] ,
deviceRpcRequestTopic : [ null , [ Validators . required , Validators . pattern ( this . MQTTTopicPattern ) ] ] ,
deviceRpcRequestTopic : [ null , [ Validators . required , this . validationMQTTTopic ( ) ] ] ,
deviceRpcResponseTopic : [ null , [ Validators . required , Validators . pattern ( this . MQTTTopicPattern ) ] ]
deviceRpcResponseTopic : [ null , [ Validators . required , this . validationMQTTTopic ( ) ] ]
} )
} )
} ) ;
} ) ;
this . mqttDeviceProfileTransportConfigurationFormGroup . valueChanges . subscribe ( ( ) = > {
this . mqttDeviceProfileTransportConfigurationFormGroup . valueChanges . subscribe ( ( ) = > {
@ -104,4 +112,34 @@ export class MqttDeviceProfileTransportConfigurationComponent implements Control
}
}
this . propagateChange ( configuration ) ;
this . propagateChange ( configuration ) ;
}
}
private validationMQTTTopic ( ) : ValidatorFn {
return ( c : FormControl ) = > {
const newTopic = c . value ;
const wildcardSymbols = /[#+]/g ;
let findSymbol = wildcardSymbols . exec ( newTopic ) ;
while ( findSymbol ) {
const index = findSymbol . index ;
const currentSymbol = findSymbol [ 0 ] ;
const prevSymbol = index > 0 ? newTopic [ index - 1 ] : null ;
const nextSymbol = index < ( newTopic . length - 1 ) ? newTopic [ index + 1 ] : null ;
if ( currentSymbol === '#' && ( index !== ( newTopic . length - 1 ) || ( prevSymbol !== null && prevSymbol !== '/' ) ) ) {
return {
invalidMultiTopicCharacter : {
valid : false
}
} ;
}
if ( currentSymbol === '+' && ( ( prevSymbol !== null && prevSymbol !== '/' ) || ( nextSymbol !== null && nextSymbol !== '/' ) ) ) {
return {
invalidSingleTopicCharacter : {
valid : false
}
} ;
}
findSymbol = wildcardSymbols . exec ( newTopic ) ;
}
return null ;
} ;
}
}
}