Skip to content

Commit e7fa56d

Browse files
Merge pull request #184 from OS2iot/feature/IoT-1522_TestDatatarget
Feature/iot - 1522 test datatarget
2 parents 751e255 + 52770e9 commit e7fa56d

25 files changed

+681
-251
lines changed

src/app/applications/applications-routing.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { IoTDeviceDetailComponent } from "./iot-devices/iot-device-detail/iot-de
2323
import { IotDeviceEditComponent } from "./iot-devices/iot-device-edit/iot-device-edit.component";
2424
import { MulticastDetailComponent } from "./multicast/multicast-detail/multicast-detail.component";
2525
import { MulticastEditComponent } from "./multicast/multicast-edit/multicast-edit.component";
26+
import { DatatargetTestConnectionComponent } from "./datatarget/datatarget-test-connection/datatarget-test-connection.component";
2627

2728
const applicationRoutes: Routes = [
2829
{
@@ -76,6 +77,7 @@ const applicationRoutes: Routes = [
7677
component: DatatargetDetailComponent,
7778
children: [
7879
{ path: "datatarget-log", component: DatatargetLogComponent },
80+
{ path: "datatarget-test-connection", component: DatatargetTestConnectionComponent },
7981
{ path: "httppush-detail", component: HttppushDetailComponent },
8082
{ path: "fiware-detail", component: FiwareDetailComponent },
8183
{ path: "mqtt-detail", component: MqttDetailComponent },

src/app/applications/datatarget/datatarget-detail/datatarget-detail-tabs-common.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,9 @@ import { NavTab } from "@shared/types/nav-tabs.type";
1515
import { Subscription } from "rxjs";
1616

1717
export abstract class DatatargetDetailTabsCommon implements DatatargetDetail {
18-
protected abstract getDetailsLink(): string;
19-
2018
faExclamationTriangle = faExclamationTriangle;
21-
2219
logLink: "datatarget-log" = "datatarget-log";
20+
testConnectionLink: "datatarget-test-connection" = "datatarget-test-connection";
2321
navTabs: NavTab[] = [
2422
{
2523
label: "APPLICATION.DETAILS",
@@ -31,13 +29,16 @@ export abstract class DatatargetDetailTabsCommon implements DatatargetDetail {
3129
link: this.logLink,
3230
index: 1,
3331
},
32+
{
33+
label: "DATATARGET.TEST_CONNECTION.TEST-CONNECTION",
34+
link: this.testConnectionLink,
35+
index: 2,
36+
},
3437
];
35-
3638
datatarget: Datatarget;
3739
backButton: BackButton = { label: "", routerLink: undefined };
3840
dropdownButton: DropdownButton;
3941
canEdit: boolean;
40-
4142
private subscriptions: Subscription[] = [];
4243
private deleteDialogSubscription: Subscription;
4344

@@ -79,16 +80,6 @@ export abstract class DatatargetDetailTabsCommon implements DatatargetDetail {
7980
this.canEdit = meService.hasAccessToTargetOrganization(OrganizationAccessScope.ApplicationWrite, undefined, appId);
8081
}
8182

82-
protected onDestroy(): void {
83-
this.subscriptions?.forEach(s => s?.unsubscribe());
84-
this.deleteDialogSubscription?.unsubscribe();
85-
}
86-
87-
private getDatatarget = (id: number) =>
88-
this.datatargetService.get(id).subscribe((dataTarget: Datatarget) => {
89-
this.datatarget = dataTarget;
90-
});
91-
9283
onDeleteDatatarget() {
9384
this.deleteDialogSubscription?.unsubscribe();
9485
this.deleteDialogSubscription = this.deleteDialogService.showSimpleDialog().subscribe(response => {
@@ -106,4 +97,16 @@ export abstract class DatatargetDetailTabsCommon implements DatatargetDetail {
10697
);
10798
});
10899
}
100+
101+
protected abstract getDetailsLink(): string;
102+
103+
protected onDestroy(): void {
104+
this.subscriptions?.forEach(s => s?.unsubscribe());
105+
this.deleteDialogSubscription?.unsubscribe();
106+
}
107+
108+
private getDatatarget = (id: number) =>
109+
this.datatargetService.get(id).subscribe((dataTarget: Datatarget) => {
110+
this.datatarget = dataTarget;
111+
});
109112
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<div class="container-fluid">
2+
<div class="row">
3+
<div class="col-md-6 d-flex align-items-stretch">
4+
<div class="jumbotron jumbotron--full-width">
5+
<h3>{{ "DATATARGET.TEST_CONNECTION.TEST-CONNECTION" | translate }}</h3>
6+
<div *ngIf="dataTarget.type === DataTargetType.MQTT">
7+
<button (click)="pingDataTarget()"
8+
class="mt-3 btn btn-primary">
9+
{{ "DATATARGET.TEST_CONNECTION.TEST-CONNECTION" | translate }}
10+
</button>
11+
<p class="mt-2">{{ "DATATARGET.TEST_CONNECTION.SEND-PING-TO-DATATARGET" | translate }}</p>
12+
<mat-divider></mat-divider>
13+
</div>
14+
<p class="mt-3 font-weight-bold">{{ "DATATARGET.TEST_CONNECTION.SEND-LATEST-PACKAGE" | translate }}</p>
15+
<mat-select
16+
(selectionChange)="getLastPackage($event)"
17+
[placeholder]="'DATATARGET.TEST_CONNECTION.CHOOSE-IOT-DEVICE' | translate"
18+
class="mt-3 form-control"
19+
>
20+
<mat-option *ngFor="let iotDevice of iotDevices" [value]="iotDevice.id">
21+
{{ iotDevice.name }}
22+
</mat-option>
23+
</mat-select>
24+
<mat-select
25+
[(value)]="payloadDecoderId"
26+
[placeholder]="'DATATARGET.TEST_CONNECTION.CHOOSE-PAYLOAD-DECODER' | translate"
27+
class="mt-3 form-control">
28+
<mat-option [value]="0">
29+
{{ "QUESTION.DATATARGET.NO-PAYLOAD-DECODER-SELECTED" | translate }}
30+
</mat-option>
31+
<mat-option *ngFor="let payloadDecoder of payloadDecoders" [value]="payloadDecoder.id">
32+
{{ payloadDecoder.name }}
33+
</mat-option>
34+
</mat-select>
35+
<button (click)="pingDataTarget(payloadData)" [disabled]="!payloadData"
36+
class="mt-3 btn btn-primary">{{ "DATATARGET.TEST_CONNECTION.SEND-DATA-PACKAGE" | translate }}
37+
</button>
38+
<p class="mt-2" style="white-space: pre-wrap">
39+
{{ "DATATARGET.TEST_CONNECTION.SEND-DATA-PACKAGE-DESCRIPTION" | translate }}
40+
{{ dataTarget.type === DataTargetType.MQTT ? ("DATATARGET.TEST_CONNECTION.MQTT-DISCLAIMER"| translate) : "" }}
41+
</p>
42+
</div>
43+
</div>
44+
<div class="col-md-6 d-flex align-items-stretch min-height-400">
45+
<div class="jumbotron jumbotron--full-width">
46+
<h3>{{ "DATATARGET.TEST_CONNECTION.CHOSEN-DATA-PACKAGE" | translate }}</h3>
47+
<ngx-monaco-editor
48+
[(ngModel)]="payloadData"
49+
[ngModelOptions]="{ standalone: true }"
50+
[options]="editorJsonOutpuOptions"
51+
class="height-85"
52+
>
53+
</ngx-monaco-editor>
54+
</div>
55+
</div>
56+
<div class="col-md-6 d-flex align-items-stretch min-height-400">
57+
<div class="jumbotron jumbotron--full-width">
58+
<h3>{{ "DATATARGET.TEST_CONNECTION.SENT-DECODED-REQUEST" | translate }}</h3>
59+
<ngx-monaco-editor
60+
[(ngModel)]="decodedData"
61+
[ngModelOptions]="{ standalone: true }"
62+
[options]="editorJsonOutpuOptions"
63+
class="height-85"
64+
>
65+
</ngx-monaco-editor>
66+
</div>
67+
</div>
68+
<div class="col-md-6 d-flex align-items-stretch min-height-400">
69+
<div class="jumbotron jumbotron--full-width">
70+
<h3>{{ "DATATARGET.TEST_CONNECTION.RECEIVED-RESPONSE" | translate }}</h3>
71+
<ngx-monaco-editor
72+
[(ngModel)]="testResponse"
73+
[ngModelOptions]="{ standalone: true }"
74+
[options]="editorJsonOutpuOptions"
75+
class="height-85"
76+
>
77+
</ngx-monaco-editor>
78+
</div>
79+
</div>
80+
<div *ngIf="loading">
81+
<app-loading-spinner></app-loading-spinner>
82+
</div>
83+
</div>
84+
</div>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.height-85 {
2+
height: 85%;
3+
}
4+
5+
.min-height-400 {
6+
min-height: 400px;
7+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { Component, OnDestroy, OnInit } from "@angular/core";
2+
import { IoTDeviceService } from "@applications/iot-devices/iot-device.service";
3+
import { ApplicationService } from "@applications/application.service";
4+
import { Application } from "@applications/application.model";
5+
import { IotDevice } from "@applications/iot-devices/iot-device.model";
6+
import { ActivatedRoute } from "@angular/router";
7+
import { MatSelectChange } from "@angular/material/select";
8+
import { DatatargetService } from "@applications/datatarget/datatarget.service";
9+
import { Datatarget } from "@applications/datatarget/datatarget.model";
10+
import { DataTargetType } from "@shared/enums/datatarget-type";
11+
import { PayloadDecoderMinimal } from "@payload-decoder/payload-decoder.model";
12+
import { PayloadDecoderService } from "@payload-decoder/payload-decoder.service";
13+
import { PayloadDeviceDatatargetService } from "@payload-decoder/payload-device-datatarget.service";
14+
15+
@Component({
16+
selector: "app-datatarget-test-connection",
17+
templateUrl: "./datatarget-test-connection.component.html",
18+
styleUrl: "./datatarget-test-connection.component.scss",
19+
})
20+
export class DatatargetTestConnectionComponent implements OnInit, OnDestroy {
21+
editorJsonOutpuOptions = {
22+
theme: "vs",
23+
language: "json",
24+
autoIndent: true,
25+
roundedSelection: true,
26+
minimap: { enabled: false },
27+
readOnly: true,
28+
};
29+
30+
public iotDevices: IotDevice[];
31+
public payloadDecoders: PayloadDecoderMinimal[];
32+
public payloadData = "";
33+
public testResponse = "";
34+
public decodedData = "";
35+
public dataTarget: Datatarget;
36+
public payloadDecoderId: number;
37+
public loading = false;
38+
protected readonly DataTargetType = DataTargetType;
39+
private applicationId: number;
40+
private dataTargetId: number;
41+
private iotDeviceId: number;
42+
private subscriptions = [];
43+
44+
constructor(
45+
private deviceService: IoTDeviceService,
46+
private dataTargetService: DatatargetService,
47+
private route: ActivatedRoute,
48+
private payloadDecoderService: PayloadDecoderService,
49+
private payloadDeviceDataTargetService: PayloadDeviceDatatargetService,
50+
public applicationService: ApplicationService
51+
) {}
52+
53+
ngOnInit(): void {
54+
this.applicationId = +this.route.parent.snapshot.paramMap.get("id");
55+
this.dataTargetId = +this.route.parent.snapshot.paramMap.get("datatargetId");
56+
this.getDevices();
57+
this.getDatatarget();
58+
this.getPayloadDecoders();
59+
}
60+
61+
ngOnDestroy() {
62+
this.subscriptions.forEach(s => s?.unsubscribe());
63+
}
64+
65+
getDevices() {
66+
const deviceSubscription = this.applicationService
67+
.getApplication(this.applicationId)
68+
.subscribe((application: Application) => {
69+
this.iotDevices = application.iotDevices.sort((a, b) => a.name.localeCompare(b.name, "en", { numeric: true }));
70+
});
71+
72+
this.subscriptions.push(deviceSubscription);
73+
}
74+
75+
getLastPackage(event: MatSelectChange) {
76+
this.iotDeviceId = event.value;
77+
const deviceSub = this.deviceService.getIoTDevice(event.value).subscribe((device: IotDevice) => {
78+
if (device.latestReceivedMessage) {
79+
this.payloadData = JSON.stringify(device.latestReceivedMessage.rawData, null, 2);
80+
}
81+
});
82+
83+
const connectionSub = this.payloadDeviceDataTargetService.getByDataTarget(this.dataTargetId).subscribe(c => {
84+
const connection = c.data.find(x => x.iotDevices.some(d => d.id === event.value));
85+
this.payloadDecoderId = connection.payloadDecoder.id;
86+
});
87+
88+
this.subscriptions.push(deviceSub, connectionSub);
89+
}
90+
91+
pingDataTarget(dataPackage?: string) {
92+
this.loading = true;
93+
this.dataTargetService
94+
.testDataTarget({
95+
dataTargetId: this.dataTargetId,
96+
iotDeviceId: this.iotDeviceId,
97+
payloadDecoderId: this.payloadDecoderId,
98+
dataPackage: dataPackage,
99+
})
100+
.subscribe(
101+
response => {
102+
this.testResponse = response?.result ? JSON.stringify(response.result, null, 2) : "";
103+
this.decodedData = response?.decodedPayload ? JSON.stringify(response.decodedPayload, null, 2) : "";
104+
},
105+
error => {
106+
console.log(error);
107+
},
108+
() => {
109+
this.loading = false;
110+
}
111+
);
112+
}
113+
114+
private getDatatarget() {
115+
const dataTargetSubscription = this.dataTargetService.get(this.dataTargetId).subscribe((datatarget: Datatarget) => {
116+
this.dataTarget = datatarget;
117+
});
118+
119+
this.subscriptions.push(dataTargetSubscription);
120+
}
121+
122+
private getPayloadDecoders() {
123+
const sub = this.payloadDecoderService.getMinimal().subscribe(result => {
124+
this.payloadDecoders = result.data;
125+
});
126+
127+
this.subscriptions.push(sub);
128+
}
129+
}

src/app/applications/datatarget/datatarget.model.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,10 @@ export class OddkMailInfo {
5353
comment?: string;
5454
sharingUrl?: string;
5555
}
56+
57+
export class TestDataTargetDto {
58+
dataTargetId: number;
59+
iotDeviceId?: number;
60+
payloadDecoderId?: number;
61+
dataPackage?: string;
62+
}

src/app/applications/datatarget/datatarget.module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import { OpendatadkDetailComponent } from "./opendatadk/opendatadk-detail/openda
2929
import { OpendatadkEditComponent } from "./opendatadk/opendatadk-edit/opendatadk-edit.component";
3030
import { OpenDataDkMailDialogComponent } from "./opendatadk/opendatadk-edit/opendatadk-mail-dialog/opendatadk-mail-dialog";
3131
import { OpenDataDkWarningDialogComponent } from "./opendatadk/opendatadk-edit/opendatadk-warning-dialog/opendatadk-warning-dialog";
32+
import { DatatargetTestConnectionComponent } from "./datatarget-test-connection/datatarget-test-connection.component";
33+
import { MonacoEditorModule } from "ngx-monaco-editor-v2";
3234

3335
@NgModule({
3436
declarations: [
@@ -53,6 +55,7 @@ import { OpenDataDkWarningDialogComponent } from "./opendatadk/opendatadk-edit/o
5355
DatatargetDetailTypeSelectorDirective,
5456
DatatargetEditTypeSelectorDirective,
5557
DatatargetTabComponent,
58+
DatatargetTestConnectionComponent,
5659
],
5760
imports: [
5861
CommonModule,
@@ -65,6 +68,7 @@ import { OpenDataDkWarningDialogComponent } from "./opendatadk/opendatadk-edit/o
6568
FormsModule,
6669
SharedModule,
6770
PipesModule,
71+
MonacoEditorModule,
6872
],
6973
exports: [
7074
DatatargetTableComponent,

0 commit comments

Comments
 (0)