From 429c6faa5f80f80ba7883a817d05f9f2aafbb8e8 Mon Sep 17 00:00:00 2001 From: Manuel Imperiale Date: Wed, 4 Dec 2019 12:01:19 +0100 Subject: [PATCH] NOISSUE - Add OPC-UA page Signed-off-by: Manuel Imperiale --- .../components/header/header.component.ts | 2 +- src/app/common/common.module.ts | 2 + .../common/interfaces/bootstrap.interface.ts | 22 ++ .../common/interfaces/gateway.interface.ts | 15 ++ .../common/interfaces/mainflux.interface.ts | 31 +++ src/app/common/interfaces/models.ts | 70 ------- src/app/common/interfaces/opcua.interface.ts | 15 ++ .../services/bootstrap/bootstrap.service.ts | 3 +- .../services/channels/channels.service.ts | 2 +- .../services/gateways/gateways.service.ts | 4 +- .../services/messages/messages.service.ts | 2 +- .../services/mqtt/mqtt.manager.service.ts | 2 +- .../common/services/opcua/opcua.service.ts | 107 ++++++++++ .../common/services/things/things.service.ts | 2 +- .../common/services/users/users.service.ts | 2 +- src/app/common/store/channels.store.ts | 2 +- src/app/common/store/gateways.store.ts | 2 +- src/app/pages/pages-menu.ts | 5 + .../things/channels/channels.component.ts | 4 +- .../details/channels.details.component.ts | 2 +- .../details/devices.details.component.ts | 2 +- .../pages/things/devices/devices.component.ts | 4 +- .../config/gateways.config.component.ts | 5 +- .../details/gateways.details.component.ts | 2 +- .../details/info/gateways.info.component.ts | 2 +- .../details/xterm/gateways.xterm.component.ts | 3 +- .../things/gateways/gateways.component.ts | 4 +- .../lora/details/lora.details.component.ts | 3 - src/app/pages/things/lora/lora.component.ts | 19 +- .../details/opcua.details.component.html | 50 +++++ .../details/opcua.details.component.scss | 9 + .../opcua/details/opcua.details.component.ts | 43 ++++ .../pages/things/opcua/opcua.component.html | 20 ++ .../pages/things/opcua/opcua.component.scss | 7 + src/app/pages/things/opcua/opcua.component.ts | 195 ++++++++++++++++++ src/app/pages/things/things.module.ts | 4 + src/app/pages/things/things.routing.module.ts | 10 + src/app/shared/chart/chart.component.html | 6 +- src/app/shared/chart/chart.component.ts | 17 +- src/app/shared/details/details.component.ts | 20 +- .../map/leaflet/map.leaflet.component.ts | 2 +- 41 files changed, 583 insertions(+), 140 deletions(-) create mode 100644 src/app/common/interfaces/bootstrap.interface.ts create mode 100644 src/app/common/interfaces/gateway.interface.ts create mode 100644 src/app/common/interfaces/mainflux.interface.ts delete mode 100644 src/app/common/interfaces/models.ts create mode 100644 src/app/common/interfaces/opcua.interface.ts create mode 100644 src/app/common/services/opcua/opcua.service.ts create mode 100644 src/app/pages/things/opcua/details/opcua.details.component.html create mode 100644 src/app/pages/things/opcua/details/opcua.details.component.scss create mode 100644 src/app/pages/things/opcua/details/opcua.details.component.ts create mode 100644 src/app/pages/things/opcua/opcua.component.html create mode 100644 src/app/pages/things/opcua/opcua.component.scss create mode 100644 src/app/pages/things/opcua/opcua.component.ts diff --git a/src/app/@theme/components/header/header.component.ts b/src/app/@theme/components/header/header.component.ts index 00a8492723..259a3a0d00 100644 --- a/src/app/@theme/components/header/header.component.ts +++ b/src/app/@theme/components/header/header.component.ts @@ -5,7 +5,7 @@ import { LayoutService } from 'app/@core/utils'; import { Subject, Subscription } from 'rxjs'; import { Router } from '@angular/router'; -import { User } from 'app/common/interfaces/models'; +import { User } from 'app/common/interfaces/mainflux.interface'; import { UsersService } from 'app/common/services/users/users.service'; @Component({ diff --git a/src/app/common/common.module.ts b/src/app/common/common.module.ts index 66ca96aa15..5f461f98f3 100644 --- a/src/app/common/common.module.ts +++ b/src/app/common/common.module.ts @@ -4,6 +4,7 @@ import { BootstrapService } from './services/bootstrap/bootstrap.service'; import { ChannelsService } from './services/channels/channels.service'; import { GatewaysService } from './services/gateways/gateways.service'; import { LoraService } from './services/lora/lora.service'; +import { OpcuaService } from './services/opcua/opcua.service'; import { MessagesService } from './services/messages/messages.service'; import { MqttManagerService } from './services/mqtt/mqtt.manager.service'; import { NotificationsService } from './services/notifications/notifications.service'; @@ -20,6 +21,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http'; ChannelsService, GatewaysService, LoraService, + OpcuaService, MessagesService, MqttManagerService, NotificationsService, diff --git a/src/app/common/interfaces/bootstrap.interface.ts b/src/app/common/interfaces/bootstrap.interface.ts new file mode 100644 index 0000000000..fa5709d5cd --- /dev/null +++ b/src/app/common/interfaces/bootstrap.interface.ts @@ -0,0 +1,22 @@ +export interface Config { + thing_id: string; + thing_key: string; + channels: Array; + external_id: string; + external_key: string; + content: string; + state: number; +} + +export interface ConfigContent { + log_level: string; + http_port: string; + mqtt_url: string; + edgex_url: string; + wowza_url: string; +} + +export interface ConfigUpdate { + content: string; + name: string; +} diff --git a/src/app/common/interfaces/gateway.interface.ts b/src/app/common/interfaces/gateway.interface.ts new file mode 100644 index 0000000000..92468a6922 --- /dev/null +++ b/src/app/common/interfaces/gateway.interface.ts @@ -0,0 +1,15 @@ +export interface GatewayMetadata { + ctrlChannelID?: string; + dataChannelID?: string; + gwPassword?: string; + mac?: string; + cfgID?: string; + type?: string; +} + +export interface Gateway { + id?: string; + key?: string; + name?: string; + metadata?: GatewayMetadata; +} diff --git a/src/app/common/interfaces/mainflux.interface.ts b/src/app/common/interfaces/mainflux.interface.ts new file mode 100644 index 0000000000..9196d2bf77 --- /dev/null +++ b/src/app/common/interfaces/mainflux.interface.ts @@ -0,0 +1,31 @@ +export interface User { + email?: string; + password?: string; + picture?: string; + metadata?: Object; +} + +export interface Channel { + id?: string; + name?: string; + metadata?: any; +} + +export interface Thing { + id?: string; + key?: string; + name?: string; + metadata?: any; +} + +export interface Message { + bn: string; + bt: number; + bu: string; + bver: number; + n: string; + t: number; + u: string; + v: number; + vs: string; +} diff --git a/src/app/common/interfaces/models.ts b/src/app/common/interfaces/models.ts deleted file mode 100644 index 34cb9b9821..0000000000 --- a/src/app/common/interfaces/models.ts +++ /dev/null @@ -1,70 +0,0 @@ -export interface User { - email?: string; - password?: string; - picture?: string; - metadata?: Object; -} - -export interface Channel { - id?: string; - name?: string; - metadata?: any; -} - -export interface Thing { - id?: string; - key?: string; - name?: string; - metadata?: any; -} - -export interface GatewayMetadata { - ctrlChannelID?: string; - dataChannelID?: string; - gwPassword?: string; - mac?: string; - cfgID?: string; - type?: string; -} - -export interface Gateway { - id?: string; - key?: string; - name?: string; - metadata?: GatewayMetadata; -} - -export interface Config { - thing_id: string; - thing_key: string; - channels: Array; - external_id: string; - external_key: string; - content: string; - state: number; -} - -export interface ConfigContent { - log_level: string; - http_port: string; - mqtt_url: string; - edgex_url: string; - wowza_url: string; -} - -export interface ConfigUpdate { - content: string; - name: string; -} - -export interface Message { - bn: string; - bt: number; - bu: string; - bver: number; - n: string; - t: number; - u: string; - v: number; - vs: string; -} diff --git a/src/app/common/interfaces/opcua.interface.ts b/src/app/common/interfaces/opcua.interface.ts new file mode 100644 index 0000000000..e6923f2bfa --- /dev/null +++ b/src/app/common/interfaces/opcua.interface.ts @@ -0,0 +1,15 @@ +export interface OpcuaMetadata { + type?: string; + opcua: { + serverURI?: string, + nodeID?: string, + }; + channelID?: string; +} + +export interface OpcuaNode { + name?: string; + id?: string; + key?: string; + metadata?: OpcuaMetadata; +} diff --git a/src/app/common/services/bootstrap/bootstrap.service.ts b/src/app/common/services/bootstrap/bootstrap.service.ts index f54b5b59e8..ffee2d114d 100644 --- a/src/app/common/services/bootstrap/bootstrap.service.ts +++ b/src/app/common/services/bootstrap/bootstrap.service.ts @@ -6,7 +6,8 @@ import 'rxjs/add/operator/switchMap'; import 'rxjs/add/operator/map'; import { environment } from 'environments/environment'; -import { Config, ConfigContent, ConfigUpdate, Gateway } from 'app/common/interfaces/models'; +import { Gateway } from 'app/common/interfaces/gateway.interface'; +import { Config, ConfigContent, ConfigUpdate } from 'app/common/interfaces/bootstrap.interface'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; import { ThingsService } from 'app/common/services/things/things.service'; diff --git a/src/app/common/services/channels/channels.service.ts b/src/app/common/services/channels/channels.service.ts index 9e99994f3a..af33b5b92f 100644 --- a/src/app/common/services/channels/channels.service.ts +++ b/src/app/common/services/channels/channels.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import 'rxjs/add/operator/map'; import { environment } from 'environments/environment'; -import { Channel } from 'app/common/interfaces/models'; +import { Channel } from 'app/common/interfaces/mainflux.interface'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; diff --git a/src/app/common/services/gateways/gateways.service.ts b/src/app/common/services/gateways/gateways.service.ts index ff1bea156a..f598f7a327 100644 --- a/src/app/common/services/gateways/gateways.service.ts +++ b/src/app/common/services/gateways/gateways.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@angular/core'; import { v4 as uuid } from 'uuid'; -import { Gateway } from 'app/common/interfaces/models'; -import { Channel } from 'app/common/interfaces/models'; +import { Gateway } from 'app/common/interfaces/gateway.interface'; +import { Channel } from 'app/common/interfaces/mainflux.interface'; import { ThingsService } from 'app/common/services/things/things.service'; import { ChannelsService } from 'app/common/services/channels/channels.service'; import { BootstrapService } from 'app/common/services/bootstrap/bootstrap.service'; diff --git a/src/app/common/services/messages/messages.service.ts b/src/app/common/services/messages/messages.service.ts index 332834b4bf..0236847bcd 100644 --- a/src/app/common/services/messages/messages.service.ts +++ b/src/app/common/services/messages/messages.service.ts @@ -17,7 +17,7 @@ export class MessagesService { getMessages(channel: string, key: string) { const params = new HttpParams() .set('offset', '0') - .set('limit', '500'); + .set('limit', '1000'); const headers = new HttpHeaders({ 'Authorization': key, diff --git a/src/app/common/services/mqtt/mqtt.manager.service.ts b/src/app/common/services/mqtt/mqtt.manager.service.ts index 7a8b175994..bbe025708e 100644 --- a/src/app/common/services/mqtt/mqtt.manager.service.ts +++ b/src/app/common/services/mqtt/mqtt.manager.service.ts @@ -2,7 +2,7 @@ import { Injectable, EventEmitter } from '@angular/core'; import { MqttService, IMqttMessage, MqttConnectionState } from 'ngx-mqtt'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; -import { Message } from 'app/common/interfaces/models'; +import { Message } from 'app/common/interfaces/mainflux.interface'; import { Subscription } from 'rxjs'; @Injectable() diff --git a/src/app/common/services/opcua/opcua.service.ts b/src/app/common/services/opcua/opcua.service.ts new file mode 100644 index 0000000000..3bef710518 --- /dev/null +++ b/src/app/common/services/opcua/opcua.service.ts @@ -0,0 +1,107 @@ +import { Injectable } from '@angular/core'; + +import { OpcuaNode } from 'app/common/interfaces/opcua.interface'; +import { ThingsService } from 'app/common/services/things/things.service'; +import { ChannelsService } from 'app/common/services/channels/channels.service'; +import { NotificationsService } from 'app/common/services/notifications/notifications.service'; + +@Injectable() +export class OpcuaService { + typeOpcua = 'opcua'; + typeOpcuaServer = 'OPC-UA-Server'; + + constructor( + private thingsService: ThingsService, + private channelsService: ChannelsService, + private notificationsService: NotificationsService, + ) { } + + getNode(id: string) { + return this.thingsService.getThing(id); + } + + getNodes(offset: number, limit: number) { + return this.thingsService.getThings(offset, limit, this.typeOpcua); + } + + addNode(node: any) { + const chanReq = { + name: `${this.typeOpcuaServer}-${node.name}`, + metadata: { + type: this.typeOpcua, + opcua: { + serverURI: node.serverURI, + }, + }, + }; + + // TODO - Check if channel exist + return this.channelsService.addChannel(chanReq).map( + respChan => { + const chanID = respChan.headers.get('location').replace('/channels/', ''); + const nodeReq: OpcuaNode = { + name: node.name, + metadata: { + type: this.typeOpcua, + channelID: chanID, + opcua: { + nodeID: node.nodeID, + serverURI: node.serverURI, + }, + }, + }; + + this.thingsService.addThing(nodeReq).subscribe( + respThing => { + const thingID = respThing.headers.get('location').replace('/things/', ''); + this.channelsService.connectThing(chanID, thingID).subscribe( + respCon => { + this.notificationsService.success('OPC-UA Node successfully created', ''); + }, + err => { + this.thingsService.deleteThing(thingID).subscribe(); + this.channelsService.deleteChannel(chanID).subscribe(); + }, + ); + }, + err => { + this.channelsService.deleteChannel(chanID).subscribe(); + }, + ); + }, + ); + } + + editNode(node: any) { + const nodeReq: OpcuaNode = { + id: node.id, + name: node.name, + metadata: { + type: this.typeOpcua, + opcua: { + serverURI: node.serverURI, + nodeID: node.nodeID, + }, + }, + }; + + return this.thingsService.editThing(nodeReq).map( + resp => { + this.notificationsService.success('OPC-UA Node successfully edited', ''); + }, + ); + } + + deleteNode(node: any) { + const channelID = node.metadata.channelID; + return this.channelsService.deleteChannel(channelID).map( + respChan => { + this.thingsService.deleteThing(node.id).subscribe( + respThing => { + this.notificationsService.success('OPC-UA Node successfully deleted', ''); + }, + ); + }, + ); + } +} diff --git a/src/app/common/services/things/things.service.ts b/src/app/common/services/things/things.service.ts index 21e60ec8f3..41166b58ca 100644 --- a/src/app/common/services/things/things.service.ts +++ b/src/app/common/services/things/things.service.ts @@ -3,7 +3,7 @@ import { Injectable } from '@angular/core'; import 'rxjs/add/operator/map'; import { environment } from 'environments/environment'; -import { Thing } from 'app/common/interfaces/models'; +import { Thing } from 'app/common/interfaces/mainflux.interface'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; @Injectable() diff --git a/src/app/common/services/users/users.service.ts b/src/app/common/services/users/users.service.ts index 76ae15b6ed..9b8562dd99 100644 --- a/src/app/common/services/users/users.service.ts +++ b/src/app/common/services/users/users.service.ts @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { environment } from 'environments/environment'; -import { User } from 'app/common/interfaces/models'; +import { User } from 'app/common/interfaces/mainflux.interface'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; @Injectable() diff --git a/src/app/common/store/channels.store.ts b/src/app/common/store/channels.store.ts index 76f5103970..4ca309b903 100644 --- a/src/app/common/store/channels.store.ts +++ b/src/app/common/store/channels.store.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { action, observable } from 'mobx'; import { ChannelsService } from 'app/common/services/channels/channels.service'; -import { Channel } from 'app/common/interfaces/models'; +import { Channel } from 'app/common/interfaces/mainflux.interface'; import { UiStore } from './ui.store'; @Injectable() diff --git a/src/app/common/store/gateways.store.ts b/src/app/common/store/gateways.store.ts index 88e03a3206..b359404811 100644 --- a/src/app/common/store/gateways.store.ts +++ b/src/app/common/store/gateways.store.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { action, observable } from 'mobx'; import { GatewaysService } from 'app/common/services/gateways/gateways.service'; -import { Gateway } from 'app/common/interfaces/models'; +import { Gateway } from 'app/common/interfaces/gateway.interface'; import { UiStore } from './ui.store'; @Injectable() diff --git a/src/app/pages/pages-menu.ts b/src/app/pages/pages-menu.ts index f8e18e5a6b..b5d770385e 100644 --- a/src/app/pages/pages-menu.ts +++ b/src/app/pages/pages-menu.ts @@ -26,6 +26,11 @@ export const MENU_ITEMS: NbMenuItem[] = [ icon: 'radio-outline', link: '/pages/things/lora', }, + { + title: 'OPC-UA', + icon: 'globe-outline', + link: '/pages/things/opcua', + }, { title: 'Gateways', icon: 'hard-drive-outline', diff --git a/src/app/pages/things/channels/channels.component.ts b/src/app/pages/things/channels/channels.component.ts index d188a2d657..a7b7d9d13d 100644 --- a/src/app/pages/things/channels/channels.component.ts +++ b/src/app/pages/things/channels/channels.component.ts @@ -3,7 +3,7 @@ import { Component, OnInit } from '@angular/core'; import { NbDialogService } from '@nebular/theme'; import { LocalDataSource } from 'ng2-smart-table'; -import { Channel } from 'app/common/interfaces/models'; +import { Channel } from 'app/common/interfaces/mainflux.interface'; import { ChannelsService } from 'app/common/services/channels/channels.service'; import { GatewaysService } from 'app/common/services/gateways/gateways.service'; import { LoraService } from 'app/common/services/lora/lora.service'; @@ -51,7 +51,7 @@ export class ChannelsComponent implements OnInit { type: 'custom', renderComponent: DetailsComponent, valuePrepareFunction: (cell, row) => { - row.type = 'channel'; + row.type = 'channels'; return row; }, editable: false, diff --git a/src/app/pages/things/channels/details/channels.details.component.ts b/src/app/pages/things/channels/details/channels.details.component.ts index ebfc0b3b8d..43ddab9079 100644 --- a/src/app/pages/things/channels/details/channels.details.component.ts +++ b/src/app/pages/things/channels/details/channels.details.component.ts @@ -4,7 +4,7 @@ import { ActivatedRoute } from '@angular/router'; import { ThingsService } from 'app/common/services/things/things.service'; import { ChannelsService } from 'app/common/services/channels/channels.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; -import { Channel } from 'app/common/interfaces/models'; +import { Channel } from 'app/common/interfaces/mainflux.interface'; import { JsonEditorComponent, JsonEditorOptions } from 'ang-jsoneditor'; diff --git a/src/app/pages/things/devices/details/devices.details.component.ts b/src/app/pages/things/devices/details/devices.details.component.ts index fb0132e531..04714422b9 100644 --- a/src/app/pages/things/devices/details/devices.details.component.ts +++ b/src/app/pages/things/devices/details/devices.details.component.ts @@ -5,7 +5,7 @@ import { ThingsService } from 'app/common/services/things/things.service'; import { ChannelsService } from 'app/common/services/channels/channels.service'; import { MessagesService } from 'app/common/services/messages/messages.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; -import { Thing } from 'app/common/interfaces/models'; +import { Thing } from 'app/common/interfaces/mainflux.interface'; import { JsonEditorComponent, JsonEditorOptions } from 'ang-jsoneditor'; diff --git a/src/app/pages/things/devices/devices.component.ts b/src/app/pages/things/devices/devices.component.ts index f90394f356..47bc3352ee 100644 --- a/src/app/pages/things/devices/devices.component.ts +++ b/src/app/pages/things/devices/devices.component.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs'; import { NbDialogService } from '@nebular/theme'; import { LocalDataSource } from 'ng2-smart-table'; -import { Thing } from 'app/common/interfaces/models'; +import { Thing } from 'app/common/interfaces/mainflux.interface'; import { ThingsService } from 'app/common/services/things/things.service'; import { GatewaysService } from 'app/common/services/gateways/gateways.service'; @@ -57,7 +57,7 @@ export class DevicesComponent implements OnInit { type: 'custom', renderComponent: DetailsComponent, valuePrepareFunction: (cell, row) => { - row.type = 'device'; + row.type = 'devices'; return row; }, editable: false, diff --git a/src/app/pages/things/gateways/details/config/gateways.config.component.ts b/src/app/pages/things/gateways/details/config/gateways.config.component.ts index 75eb9637c2..5bda4d36a8 100644 --- a/src/app/pages/things/gateways/details/config/gateways.config.component.ts +++ b/src/app/pages/things/gateways/details/config/gateways.config.component.ts @@ -1,7 +1,6 @@ import { Component, Input, OnInit, OnChanges } from '@angular/core'; -import { Gateway } from 'app/common/interfaces/models'; -import { Config } from 'app/common/interfaces/models'; -import { ConfigContent, ConfigUpdate } from 'app/common/interfaces/models'; +import { Gateway } from 'app/common/interfaces/gateway.interface'; +import { Config, ConfigContent, ConfigUpdate } from 'app/common/interfaces/bootstrap.interface'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; import { BootstrapService } from 'app/common/services/bootstrap/bootstrap.service'; diff --git a/src/app/pages/things/gateways/details/gateways.details.component.ts b/src/app/pages/things/gateways/details/gateways.details.component.ts index f56f855761..02263b2cae 100644 --- a/src/app/pages/things/gateways/details/gateways.details.component.ts +++ b/src/app/pages/things/gateways/details/gateways.details.component.ts @@ -3,7 +3,7 @@ import { ActivatedRoute } from '@angular/router'; import { GatewaysService } from 'app/common/services/gateways/gateways.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; -import { Gateway } from 'app/common/interfaces/models'; +import { Gateway } from 'app/common/interfaces/gateway.interface'; import { MqttManagerService } from 'app/common/services/mqtt/mqtt.manager.service'; diff --git a/src/app/pages/things/gateways/details/info/gateways.info.component.ts b/src/app/pages/things/gateways/details/info/gateways.info.component.ts index dd3e3203e8..2ae4880aed 100644 --- a/src/app/pages/things/gateways/details/info/gateways.info.component.ts +++ b/src/app/pages/things/gateways/details/info/gateways.info.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; -import { Gateway } from 'app/common/interfaces/models'; +import { Gateway } from 'app/common/interfaces/gateway.interface'; @Component({ selector: 'ngx-gateways-info', diff --git a/src/app/pages/things/gateways/details/xterm/gateways.xterm.component.ts b/src/app/pages/things/gateways/details/xterm/gateways.xterm.component.ts index 5df30e8400..6750a934e4 100644 --- a/src/app/pages/things/gateways/details/xterm/gateways.xterm.component.ts +++ b/src/app/pages/things/gateways/details/xterm/gateways.xterm.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnInit, AfterViewInit, ViewChild, ElementRef, ViewEncapsulation, OnDestroy } from '@angular/core'; -import { Gateway, Message } from 'app/common/interfaces/models'; +import { Gateway } from 'app/common/interfaces/gateway.interface'; +import { Message } from 'app/common/interfaces/mainflux.interface'; import { Terminal } from 'xterm'; import { MqttManagerService } from 'app/common/services/mqtt/mqtt.manager.service'; import { MqttConnectionState } from 'ngx-mqtt'; diff --git a/src/app/pages/things/gateways/gateways.component.ts b/src/app/pages/things/gateways/gateways.component.ts index e26f4963f6..a0ad363d48 100644 --- a/src/app/pages/things/gateways/gateways.component.ts +++ b/src/app/pages/things/gateways/gateways.component.ts @@ -3,7 +3,7 @@ import { NbDialogService } from '@nebular/theme'; import { LocalDataSource } from 'ng2-smart-table'; -import { Gateway } from 'app/common/interfaces/models'; +import { Gateway } from 'app/common/interfaces/gateway.interface'; import { GatewaysService } from 'app/common/services/gateways/gateways.service'; import { NotificationsService } from 'app/common/services/notifications/notifications.service'; import { MessagesService } from 'app/common/services/messages/messages.service'; @@ -91,7 +91,7 @@ export class GatewaysComponent implements OnInit { type: 'custom', renderComponent: DetailsComponent, valuePrepareFunction: (cell, row) => { - row.type = 'gateway'; + row.type = 'gateways'; return row; }, editable: 'false', diff --git a/src/app/pages/things/lora/details/lora.details.component.ts b/src/app/pages/things/lora/details/lora.details.component.ts index b2968f759e..897c171dfe 100644 --- a/src/app/pages/things/lora/details/lora.details.component.ts +++ b/src/app/pages/things/lora/details/lora.details.component.ts @@ -14,9 +14,6 @@ export class LoraDetailsComponent implements OnInit { loraDevice: LoraDevice = { name: '', }; - - connections: Array<{name: string, id: string}> = []; - messages = []; constructor( diff --git a/src/app/pages/things/lora/lora.component.ts b/src/app/pages/things/lora/lora.component.ts index 9262bc5e02..4ad834216d 100644 --- a/src/app/pages/things/lora/lora.component.ts +++ b/src/app/pages/things/lora/lora.component.ts @@ -85,7 +85,7 @@ export class LoraComponent implements OnInit { type: 'custom', renderComponent: DetailsComponent, valuePrepareFunction: (cell, row) => { - row.type = 'loraDevice'; + row.type = 'lora'; return row; }, editable: false, @@ -120,15 +120,6 @@ export class LoraComponent implements OnInit { this.getLoraChanNum(); } - isJSON(metadata: string): boolean { - try { - JSON.parse(metadata); - } catch (e) { - return false; - } - return true; - } - getLoraDevices(): void { this.loraDevices = []; @@ -167,7 +158,7 @@ export class LoraComponent implements OnInit { } onCreateConfirm(event): void { - // Check if there is an edited devEUI + // Check appID and devEUI if (event.newData.devEUI !== '' && event.newData.appID !== '') { // close create row event.confirm.resolve(); @@ -188,8 +179,8 @@ export class LoraComponent implements OnInit { } onEditConfirm(event): void { - // Check if there is an edited devEUI - if (event.newData.devEUI !== '') { + // Check appID and devEUI + if (event.newData.devEUI !== '' && event.newData.appID !== '') { // close edit row event.confirm.resolve(); @@ -198,7 +189,7 @@ export class LoraComponent implements OnInit { }, ); } else { - this.notificationsService.warn('DeviceEUI is required', ''); + this.notificationsService.warn('AppID and DeviceEUI are required', ''); } } diff --git a/src/app/pages/things/opcua/details/opcua.details.component.html b/src/app/pages/things/opcua/details/opcua.details.component.html new file mode 100644 index 0000000000..0eef9720c5 --- /dev/null +++ b/src/app/pages/things/opcua/details/opcua.details.component.html @@ -0,0 +1,50 @@ +
+
+ + + OPC-UA Infos + + +
+
Name:
{{ opcuaNode.name }}
+
ID:
{{ opcuaNode.id }}
+
Key:
{{ opcuaNode.key }}
+
+
+
+ + + + Raw Messages + + +
+ No messages to display +
+
{{ message | json }}
+
+
+
+ +
+ + + Metadata + + +
{{ opcuaNode.metadata | json}}
+
+
+ + + +
+ No charts to display +
+
+ +
+
+
+
+
diff --git a/src/app/pages/things/opcua/details/opcua.details.component.scss b/src/app/pages/things/opcua/details/opcua.details.component.scss new file mode 100644 index 0000000000..10851bd596 --- /dev/null +++ b/src/app/pages/things/opcua/details/opcua.details.component.scss @@ -0,0 +1,9 @@ +nb-icon { + vertical-align: middle; +} + +.col-2 { + text-align: right; + padding: 0; + font-weight: bold; +} diff --git a/src/app/pages/things/opcua/details/opcua.details.component.ts b/src/app/pages/things/opcua/details/opcua.details.component.ts new file mode 100644 index 0000000000..8757c566e6 --- /dev/null +++ b/src/app/pages/things/opcua/details/opcua.details.component.ts @@ -0,0 +1,43 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { LoraService } from 'app/common/services/lora/lora.service'; +import { MessagesService } from 'app/common/services/messages/messages.service'; +import { OpcuaNode } from 'app/common/interfaces/opcua.interface'; + +@Component({ + selector: 'ngx-opcua-details-component', + templateUrl: './opcua.details.component.html', + styleUrls: ['./opcua.details.component.scss'], +}) +export class OpcuaDetailsComponent implements OnInit { + opcuaNode: OpcuaNode = { + name: '', + }; + messages = []; + + constructor( + private route: ActivatedRoute, + private loraService: LoraService, + private messagesService: MessagesService, + ) { } + + ngOnInit() { + const id = this.route.snapshot.paramMap.get('id'); + + this.loraService.getDevice(id).subscribe( + resp => { + this.opcuaNode = resp; + + this.messagesService.getMessages(this.opcuaNode.metadata.channelID, this.opcuaNode.key).subscribe( + (msgResp: any) => { + this.messages = []; + if (msgResp.messages) { + this.messages = msgResp.messages; + } + }, + ); + }, + ); + } +} diff --git a/src/app/pages/things/opcua/opcua.component.html b/src/app/pages/things/opcua/opcua.component.html new file mode 100644 index 0000000000..6584e25b2a --- /dev/null +++ b/src/app/pages/things/opcua/opcua.component.html @@ -0,0 +1,20 @@ +
+
+ + + {{ opcuaNodes.length }}   OPC-UA Server Nodes + + +
+
+ + + + + + + diff --git a/src/app/pages/things/opcua/opcua.component.scss b/src/app/pages/things/opcua/opcua.component.scss new file mode 100644 index 0000000000..345cf268cf --- /dev/null +++ b/src/app/pages/things/opcua/opcua.component.scss @@ -0,0 +1,7 @@ +nb-card-header { + text-align: center; +} + +i { + font-size: 8px; +} diff --git a/src/app/pages/things/opcua/opcua.component.ts b/src/app/pages/things/opcua/opcua.component.ts new file mode 100644 index 0000000000..362e9c8ebb --- /dev/null +++ b/src/app/pages/things/opcua/opcua.component.ts @@ -0,0 +1,195 @@ +import { Component, OnInit } from '@angular/core'; +import { NbDialogService } from '@nebular/theme'; + +import { LocalDataSource } from 'ng2-smart-table'; + +import { OpcuaService } from 'app/common/services/opcua/opcua.service'; +import { NotificationsService } from 'app/common/services/notifications/notifications.service'; +import { ConfirmationComponent } from 'app/shared/confirmation/confirmation.component'; +import { DetailsComponent } from 'app/shared/details/details.component'; +import { MessagesService } from 'app/common/services/messages/messages.service'; + +@Component({ + selector: 'ngx-opcua-component', + templateUrl: './opcua.component.html', + styleUrls: ['./opcua.component.scss'], +}) +export class OpcuaComponent implements OnInit { + settings = { + add: { + addButtonContent: '', + createButtonContent: '', + cancelButtonContent: '', + confirmCreate: true, + }, + edit: { + editButtonContent: '', + saveButtonContent: '', + cancelButtonContent: '', + confirmSave: true, + }, + delete: { + deleteButtonContent: '', + confirmDelete: true, + }, + columns: { + name: { + title: 'Name', + type: 'string', + placeholder: 'Search name', + filter: { + placeholder: 'Search name', + }, + }, + serverURI: { + width: '20%', + title: 'Server URI', + editable: true, + addable: true, + filter: true, + }, + nodeID: { + width: '20%', + title: 'Node ID', + editable: true, + addable: true, + filter: true, + }, + messages: { + title: 'Messages', + type: 'text', + editable: 'false', + addable: false, + filter: false, + valuePrepareFunction: cell => { + if (cell > 0) { + return cell; + } + return '0'; + }, + }, + seen: { + width: '20%', + title: 'Last Seen', + type: 'text', + editable: 'false', + addable: false, + filter: false, + valuePrepareFunction: (cell, row) => { + if (cell > 0) { + return new Date(cell * 1000).toLocaleString(); + } + return ' undefined '; + }, + }, + details: { + title: 'Details', + type: 'custom', + renderComponent: DetailsComponent, + valuePrepareFunction: (cell, row) => { + row.type = 'opcua'; + return row; + }, + editable: false, + addable: false, + filter: false, + }, + }, + pager: { + display: true, + perPage: 6, + }, + }; + + source: LocalDataSource = new LocalDataSource(); + + opcuaNodes = []; + + offset = 0; + limit = 20; + + constructor( + private opcuaService: OpcuaService, + private messagesService: MessagesService, + private notificationsService: NotificationsService, + private dialogService: NbDialogService, + ) { } + + ngOnInit() { + this.getOpcuaNodes(); + } + + getOpcuaNodes(): void { + this.opcuaNodes = []; + + this.opcuaService.getNodes(this.offset, this.limit).subscribe( + (resp: any) => { + resp.things.forEach(node => { + node.serverURI = node.metadata.opcua.serverURI; + node.nodeID = node.metadata.opcua.nodeID; + + const chanID: string = node.metadata ? node.metadata.channelID : ''; + this.messagesService.getMessages(chanID, node.key).subscribe( + (msgResp: any) => { + if (msgResp.messages) { + node.seen = msgResp.messages[0].time; + node.messages = msgResp.total; + } + + this.opcuaNodes.push(node); + this.source.load(this.opcuaNodes); + this.source.refresh(); + }, + ); + }); + }, + ); + } + + onCreateConfirm(event): void { + // Check ServerURI and NodeID + if (event.newData.serverURI !== '' && event.newData.nodeID !== '') { + // close create row + event.confirm.resolve(); + + this.opcuaService.addNode(event.newData).subscribe( + resp => { + setTimeout( + () => { + this.getOpcuaNodes(); + }, 3000, + ); + }, + ); + } else { + this.notificationsService.warn('Server URI and Node ID are required', ''); + } + } + + onEditConfirm(event): void { + // Check ServerURI and NodeID + if (event.newData.serverURI !== '' && event.newData.nodeID !== '') { + // close edit row + event.confirm.resolve(); + + this.opcuaService.editNode(event.newData).subscribe(); + } else { + this.notificationsService.warn('Server URI and Node ID are required', ''); + } + } + + onDeleteConfirm(event): void { + this.dialogService.open(ConfirmationComponent, { context: { type: 'device' } }).onClose.subscribe( + confirm => { + if (confirm) { + event.confirm.resolve(); + + this.opcuaService.deleteNode(event.data).subscribe( + resp => { + }, + ); + } + }, + ); + } +} diff --git a/src/app/pages/things/things.module.ts b/src/app/pages/things/things.module.ts index c8669a1e77..cdfa9a9b10 100644 --- a/src/app/pages/things/things.module.ts +++ b/src/app/pages/things/things.module.ts @@ -17,6 +17,8 @@ import { DevicesDetailsComponent } from 'app/pages/things/devices/details/device import { ChannelsDetailsComponent } from 'app/pages/things/channels/details/channels.details.component'; import { LoraComponent } from 'app/pages/things/lora/lora.component'; import { LoraDetailsComponent } from 'app/pages/things/lora/details/lora.details.component'; +import { OpcuaComponent } from 'app/pages/things/opcua/opcua.component'; +import { OpcuaDetailsComponent } from 'app/pages/things/opcua/details/opcua.details.component'; import { GatewaysComponent } from 'app/pages/things/gateways/gateways.component'; import { GatewaysDetailsComponent } from 'app/pages/things/gateways/details/gateways.details.component'; import { GatewaysInfoComponent } from 'app/pages/things/gateways/details/info/gateways.info.component'; @@ -42,6 +44,8 @@ import { NgJsonEditorModule } from 'ang-jsoneditor'; ChannelsDetailsComponent, LoraComponent, LoraDetailsComponent, + OpcuaComponent, + OpcuaDetailsComponent, GatewaysComponent, GatewaysDetailsComponent, GatewaysInfoComponent, diff --git a/src/app/pages/things/things.routing.module.ts b/src/app/pages/things/things.routing.module.ts index 3e12672d20..317afdec11 100644 --- a/src/app/pages/things/things.routing.module.ts +++ b/src/app/pages/things/things.routing.module.ts @@ -7,6 +7,8 @@ import { DevicesDetailsComponent } from './devices/details/devices.details.compo import { ChannelsDetailsComponent } from './channels/details/channels.details.component'; import { LoraComponent } from 'app/pages/things/lora/lora.component'; import { LoraDetailsComponent } from 'app/pages/things/lora/details/lora.details.component'; +import { OpcuaComponent } from 'app/pages/things/opcua/opcua.component'; +import { OpcuaDetailsComponent } from 'app/pages/things/opcua/details/opcua.details.component'; import { GatewaysComponent } from 'app/pages/things/gateways/gateways.component'; import { GatewaysDetailsComponent } from 'app/pages/things/gateways/details/gateways.details.component'; @@ -35,6 +37,14 @@ const routes: Routes = [ path: 'lora/details/:id', component: LoraDetailsComponent, }, + { + path: 'opcua', + component: OpcuaComponent, + }, + { + path: 'opcua/details/:id', + component: OpcuaDetailsComponent, + }, { path: 'gateways', component: GatewaysComponent, diff --git a/src/app/shared/chart/chart.component.html b/src/app/shared/chart/chart.component.html index 76d07d269a..13dedf1204 100644 --- a/src/app/shared/chart/chart.component.html +++ b/src/app/shared/chart/chart.component.html @@ -1,4 +1,8 @@ -
+