diff --git a/README.md b/README.md index fc8928232cab06fadcc52f17c52e9f3c9d7a6d91..dec866c946bd662ee91c652f8566263c6a62e38e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ - new positions - ```json { - "object": [ + "objects": [ { "id": "Employee1", "sensorId": "UWB_1", @@ -28,8 +28,8 @@ "position": { "refSystemId": "ROOT", "point": { - "x": 13, - "y": 22, + "x": 3, + "y": 2, "z": 3 }, "accuracy": 1 @@ -40,7 +40,13 @@ "z": 1, "w": 1.5 }, - "lastPosUpdate": "2021-09-14T09:41:20+00:00" + "lastPosUpdate": "2021-09-14T09:41:20+00:00", + "zoneDescriptors": [ + { + "zoneId": "door_zone", + "notificationType": "EntryNotification" + } + ] } ], "type": "EntryNotification" diff --git a/angular.json b/angular.json index 1e78e97c311669631b4a8637eb7801efe05055ec..4336c79ac3a9ede1dce37964ffd35a95b40e11f1 100644 --- a/angular.json +++ b/angular.json @@ -38,7 +38,9 @@ "src/styles.scss", "./node_modules/leaflet/dist/leaflet.css" ], - "scripts": [] + "scripts": [ + "node_modules/apexcharts/dist/apexcharts.min.js" + ] }, "configurations": { "production": { @@ -107,7 +109,9 @@ "./node_modules/@angular/material/prebuilt-themes/indigo-pink.css", "src/styles.scss" ], - "scripts": [] + "scripts": [ + "node_modules/apexcharts/dist/apexcharts.min.js" + ] } } } diff --git a/package-lock.json b/package-lock.json index 47f91f0bf7ce54778b292b9e0faa57af7c8436bd..0fe80af26df540b8e7268e04018d6bf1d11dc64e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,10 @@ "@angular/platform-browser-dynamic": "~12.2.0", "@angular/router": "~12.2.0", "@asymmetrik/ngx-leaflet": "^8.1.0", + "apexcharts": "^3.29.0", "leaflet": "^1.7.1", + "moment": "^2.24.0", + "ng-apexcharts": "^1.5.12", "ngx-mqtt": "^9.0.5", "rxjs": "~6.6.0", "tslib": "^2.3.0", @@ -3002,6 +3005,19 @@ "node": ">= 8" } }, + "node_modules/apexcharts": { + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.29.0.tgz", + "integrity": "sha512-PhI17VayidYAbLb5/g+7WOeirgFrVopzt0qGwLq8V+cd6NXx4CeHYq3S0pDZiUGO7UFQ4YIrT8+ie9/Fnler+w==", + "dependencies": { + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, "node_modules/aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -9419,6 +9435,14 @@ "node": ">=10" } }, + "node_modules/moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "engines": { + "node": "*" + } + }, "node_modules/mqtt": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.6.tgz", @@ -9585,6 +9609,25 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/ng-apexcharts": { + "version": "1.5.12", + "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.5.12.tgz", + "integrity": "sha512-k82AdWNbZs5yqGCjiX7PGS11Cy1+1Oo/RGt2lT89xReD9N9Vvo1t34p1dmzS+U6W5wOFlLEKKVLGNQqENW8cTQ==", + "dependencies": { + "tslib": "^1.10.0" + }, + "peerDependencies": { + "@angular/common": ">=9.0.0 <13.0.0", + "@angular/core": ">=9.0.0 <13.0.0", + "apexcharts": "^3.19.2", + "rxjs": "^6.5.5" + } + }, + "node_modules/ng-apexcharts/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, "node_modules/ngx-mqtt": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ngx-mqtt/-/ngx-mqtt-9.0.5.tgz", @@ -14402,6 +14445,89 @@ "node": ">=4" } }, + "node_modules/svg.draggable.js": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", + "dependencies": { + "svg.js": "^2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.easing.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", + "integrity": "sha1-iqmUawqOJ4V6XEChDrpAkeVpHxI=", + "dependencies": { + "svg.js": ">=2.3.x" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.filter.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", + "integrity": "sha1-kQCOFROJ3ZIwd5/L5uLJo2LRwgM=", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" + }, + "node_modules/svg.pathmorphing.js": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", + "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", + "dependencies": { + "svg.js": "^2.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", + "dependencies": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.resize.js/node_modules/svg.select.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", + "dependencies": { + "svg.js": "^2.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/svg.select.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", + "dependencies": { + "svg.js": "^2.6.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/svgo": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.7.0.tgz", @@ -18525,6 +18651,19 @@ "picomatch": "^2.0.4" } }, + "apexcharts": { + "version": "3.29.0", + "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.29.0.tgz", + "integrity": "sha512-PhI17VayidYAbLb5/g+7WOeirgFrVopzt0qGwLq8V+cd6NXx4CeHYq3S0pDZiUGO7UFQ4YIrT8+ie9/Fnler+w==", + "requires": { + "svg.draggable.js": "^2.2.2", + "svg.easing.js": "^2.0.0", + "svg.filter.js": "^2.0.2", + "svg.pathmorphing.js": "^0.1.3", + "svg.resize.js": "^1.4.3", + "svg.select.js": "^3.0.1" + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -23508,6 +23647,11 @@ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", "dev": true }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + }, "mqtt": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-4.2.6.tgz", @@ -23647,6 +23791,21 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "ng-apexcharts": { + "version": "1.5.12", + "resolved": "https://registry.npmjs.org/ng-apexcharts/-/ng-apexcharts-1.5.12.tgz", + "integrity": "sha512-k82AdWNbZs5yqGCjiX7PGS11Cy1+1Oo/RGt2lT89xReD9N9Vvo1t34p1dmzS+U6W5wOFlLEKKVLGNQqENW8cTQ==", + "requires": { + "tslib": "^1.10.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "ngx-mqtt": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ngx-mqtt/-/ngx-mqtt-9.0.5.tgz", @@ -27259,6 +27418,70 @@ "has-flag": "^3.0.0" } }, + "svg.draggable.js": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", + "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", + "requires": { + "svg.js": "^2.0.1" + } + }, + "svg.easing.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", + "integrity": "sha1-iqmUawqOJ4V6XEChDrpAkeVpHxI=", + "requires": { + "svg.js": ">=2.3.x" + } + }, + "svg.filter.js": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", + "integrity": "sha1-kQCOFROJ3ZIwd5/L5uLJo2LRwgM=", + "requires": { + "svg.js": "^2.2.5" + } + }, + "svg.js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", + "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" + }, + "svg.pathmorphing.js": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", + "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", + "requires": { + "svg.js": "^2.4.0" + } + }, + "svg.resize.js": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", + "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", + "requires": { + "svg.js": "^2.6.5", + "svg.select.js": "^2.1.2" + }, + "dependencies": { + "svg.select.js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", + "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", + "requires": { + "svg.js": "^2.2.5" + } + } + } + }, + "svg.select.js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", + "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", + "requires": { + "svg.js": "^2.6.5" + } + }, "svgo": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.7.0.tgz", diff --git a/package.json b/package.json index 08742d619469e3fe50100cbd5a5e91bd829aed17..850736e5059c57628a8d29217007a405f5f6d4d3 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,10 @@ "@angular/platform-browser-dynamic": "~12.2.0", "@angular/router": "~12.2.0", "@asymmetrik/ngx-leaflet": "^8.1.0", + "apexcharts": "^3.29.0", "leaflet": "^1.7.1", + "moment": "^2.24.0", + "ng-apexcharts": "^1.5.12", "ngx-mqtt": "^9.0.5", "rxjs": "~6.6.0", "tslib": "^2.3.0", diff --git a/src/app/app.component.html b/src/app/app.component.html index 6874549124409c92900b64146a08f158b10207ab..81866bc777ed353014e371e2ff61d5f744b17b1b 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,6 +1,3 @@ -<div class="wrapper"> - <h1>Consumer</h1> -</div> - - <app-map class="wrapper"></app-map> + +<chart-timeline></chart-timeline> \ No newline at end of file diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 11343d6aaab51e1f55c84f3561a8ef0c1eb6fb38..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -1,5 +0,0 @@ -@import './src/variables.scss'; - -.wrapper { - padding: 0 calc((100% - #{$break-large})/2); -} \ No newline at end of file diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 5e2c1ee4ce5721bb96f32a7d8de9b1b613ebdaee..4a78b20e4d20b43c054750214fb229be1c710bb2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -4,11 +4,13 @@ import { BrowserModule } from '@angular/platform-browser'; // --- Map import { LeafletModule } from '@asymmetrik/ngx-leaflet'; +// --- Charts +import { NgApexchartsModule } from 'ng-apexcharts'; + // --- MQTT import { MqttModule } from 'ngx-mqtt'; import { MQTTconfig } from './../environments/environment'; - // --- WS // import * as SockJS from 'sockjs-client'; @@ -25,13 +27,16 @@ import { AppComponent } from './app.component'; import { MapComponent } from './components/map/map.component'; import { AppRoutingModule } from './app-routing.module'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ChartTimelineComponent } from './components/chart-timeline/chart-timeline.component'; + @NgModule({ declarations: [ AppComponent, - MapComponent + MapComponent, + ChartTimelineComponent ], imports: [ BrowserModule, @@ -40,7 +45,8 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; CoreModule, LeafletModule, BrowserAnimationsModule, - MqttModule.forRoot(MQTTconfig) + MqttModule.forRoot(MQTTconfig), + NgApexchartsModule ], providers: [ApiService], bootstrap: [AppComponent] diff --git a/src/app/components/chart-timeline/chart-timeline.component.html b/src/app/components/chart-timeline/chart-timeline.component.html new file mode 100644 index 0000000000000000000000000000000000000000..0630e1b469e88478d849c62b224a57da3d50a91a --- /dev/null +++ b/src/app/components/chart-timeline/chart-timeline.component.html @@ -0,0 +1,12 @@ +<div id="chart" class="wrapper"> + <apx-chart + [series]="chartOptions.series" + [chart]="chartOptions.chart" + [dataLabels]="chartOptions.dataLabels" + [plotOptions]="chartOptions.plotOptions" + [xaxis]="chartOptions.xaxis" + [legend]="chartOptions.legend" + ></apx-chart> +</div> + + diff --git a/src/app/components/chart-timeline/chart-timeline.component.scss b/src/app/components/chart-timeline/chart-timeline.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..fc5fc0fa3ed732cbc2e8069eb01133a50a0e1188 --- /dev/null +++ b/src/app/components/chart-timeline/chart-timeline.component.scss @@ -0,0 +1,8 @@ +#chart { + margin: 35px auto; +} + +apx-chart { + height: 300px; +} + \ No newline at end of file diff --git a/src/app/components/chart-timeline/chart-timeline.component.spec.ts b/src/app/components/chart-timeline/chart-timeline.component.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..072cfa824040837afbffa328f100c4ace3d4100e --- /dev/null +++ b/src/app/components/chart-timeline/chart-timeline.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ChartTimelineComponent } from './chart-timeline.component'; + +describe('ChartTimelineComponent', () => { + let component: ChartTimelineComponent; + let fixture: ComponentFixture<ChartTimelineComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ChartTimelineComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ChartTimelineComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/chart-timeline/chart-timeline.component.ts b/src/app/components/chart-timeline/chart-timeline.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f3d41a72d625125816087429b03508f15a540ae --- /dev/null +++ b/src/app/components/chart-timeline/chart-timeline.component.ts @@ -0,0 +1,182 @@ +import { Component, OnInit, ViewChild } from '@angular/core'; + +// --- MQTT +import { Subscription } from 'rxjs'; +import { IMqttMessage, MqttService } from 'ngx-mqtt'; + +// --- Chart +import * as moment from "moment"; +import { + ApexAxisChartSeries, + ApexChart, + ApexDataLabels, + ApexLegend, + ApexPlotOptions, + ApexXAxis, + ChartComponent +} from 'ng-apexcharts'; + +import { MarkerColorMap } from 'src/environments/environment'; +import {Object, PositionUpdate} from 'src/app/model/base-model'; + +export type ChartOptions = { + series: ApexAxisChartSeries; + chart: ApexChart; + dataLabels: ApexDataLabels; + legend: ApexLegend; + xaxis: ApexXAxis; + plotOptions: ApexPlotOptions; +}; + +@Component({ + selector: 'chart-timeline', + templateUrl: './chart-timeline.component.html', + styleUrls: ['./chart-timeline.component.scss'] +}) +export class ChartTimelineComponent implements OnInit { + + @ViewChild("chart", { static: false }) chart!: ChartComponent; + + private subsPosition: Subscription; + + public chartOptions: Partial<ChartOptions> | any; + // public series: ApexAxisChartSeries; + + public seriesData: ApexAxisChartSeries; + + + constructor(private _mqttService: MqttService) { + + this.seriesData = [ + { + name: "Bob", + data: [ + { + x: "Design", + y: [ + new Date("2021-09-14T09:41:20+00:00").getTime(), + new Date("2021-09-14T09:51:20+00:00").getTime() + ] + }, + { + x: "Code", + y: [ + new Date("2021-09-14T09:52:20+00:00").getTime(), + new Date("2021-09-14T09:54:20+00:00").getTime() + ] + }, + { + x: "Test", + y: [ + new Date("2021-09-14T10:21:20+00:00").getTime(), + new Date("2021-09-14T10:31:20+00:00").getTime() + ] + } + ] + }, + { + name: "Joe", + data: [ + { + x: "Design", + y: [ + new Date("2021-09-14T09:11:20+00:00").getTime(), + new Date("2021-09-14T09:21:20+00:00").getTime() + ] + }, + { + x: "Code", + y: [ + new Date("2021-09-14T09:11:20+00:00").getTime(), + new Date("2021-09-14T09:41:20+00:00").getTime() + ] + }, + { + x: "Test", + y: [ + new Date("2021-09-14T11:31:20+00:00").getTime(), + new Date("2021-09-14T11:41:20+00:00").getTime() + ] + } + ] + } + ], + + + this.chartOptions = { + series: [], + chart: { + height: 350, + type: "rangeBar" + }, + plotOptions: { + bar: { + horizontal: true + } + }, + dataLabels: { + enabled: true, + formatter: function(val: any) { + var a = moment(val[0]); + var b = moment(val[1]); + var diff = b.diff(a, "minutes"); + return diff + "min"; + } + }, + xaxis: { + type: "datetime" + }, + legend: { + position: "top" + } + }; + + + this.subsPosition = this._mqttService.observe('ipos/client/position').subscribe((message: IMqttMessage) => { + let upd: PositionUpdate = JSON.parse(message.payload.toString()) + upd.objects.forEach(obj => this.addNewSeries(obj)) + }); + + } + + ngOnInit(): void { + } + + addNewSeries(obj: Object) { + let name = obj.id + let idx = this.chartOptions.series.findIndex((s: { name: string; }) => s.name === name) + if (idx == -1) { + idx = this.chartOptions.series.push({"name": name, "data": []}) + idx = idx - 1 + } + + // --- Marker + if (name in MarkerColorMap) { + var markerConf = MarkerColorMap[name] + this.chartOptions.series[idx]["color"] = markerConf?.circle + } + + // --- Colect new data + let dataPatch: any[] = obj.zoneDescriptors.map(zone => { + return { + "x": zone.zoneId, + "y": [ + new Date(obj.lastPosUpdate).getTime(), + new Date(obj.lastPosUpdate).getTime() + 360000 + ], + "fillColor": markerConf?.circle, + "strokeColor": markerConf?.circle + } + }); + + // --- Add it + this.chartOptions.series[idx]["data"] = dataPatch.concat(this.chartOptions.series[idx]["data"]) + + this.chartOptions.series = this.chartOptions.series.map((s: any) => Object.assign({}, s)); + } + + public ngOnDestroy() { + this.subsPosition.unsubscribe(); + } + +} diff --git a/src/app/components/map/map.component.html b/src/app/components/map/map.component.html index 2da3bd7a5c4412170d7849fc993266781fe285f7..706ec90543345ee10757fe7ba3b36d21082612fd 100644 --- a/src/app/components/map/map.component.html +++ b/src/app/components/map/map.component.html @@ -1,4 +1,4 @@ -<div style="height: 80vh;" +<div class="map" leaflet [leafletOptions]="options" (leafletMapReady)="onMapReady($event)"> diff --git a/src/app/components/map/map.component.scss b/src/app/components/map/map.component.scss index 21b328c5f4ede6d00d8cf8f45a2d0e110099d061..98616f131310ed98de858095556b88e3772aa651 100644 --- a/src/app/components/map/map.component.scss +++ b/src/app/components/map/map.component.scss @@ -1,10 +1,10 @@ .map { margin-bottom: 20px; - height: 70vh + height: 60vh } table { -width: 100%; + width: 100%; } .table { diff --git a/src/app/components/map/map.component.ts b/src/app/components/map/map.component.ts index c7f44f6d966b09c19972acc16e683497f0be5074..fa735be447e5d0580679850df9204a4a1ebfd99b 100644 --- a/src/app/components/map/map.component.ts +++ b/src/app/components/map/map.component.ts @@ -16,7 +16,7 @@ import {Position, PositionUpdate, RelativePos, WGS84} from 'src/app/model/base-m export class MapComponent implements OnInit { private subsPosition: Subscription; - private subsOrigin: Subscription; + private subsRoot: Subscription; private map!: L.Map; private root!: Position; @@ -28,15 +28,15 @@ export class MapComponent implements OnInit { constructor(private _mqttService: MqttService) { this.root = {"refSystemId": "ROOT", "point": {"latitude": 51.02545, "longitude": 13.72295}} - this.subsPosition = this._mqttService.observe('ipos/client/root').subscribe((message: IMqttMessage) => { + this.subsRoot = this._mqttService.observe('ipos/client/root').subscribe((message: IMqttMessage) => { this.root = JSON.parse(message.payload.toString()) console.log("New root: ", this.root) this.registerPoint("ROOT", {"position": this.root}) }); - this.subsOrigin = this._mqttService.observe('ipos/client/position').subscribe((message: IMqttMessage) => { + this.subsPosition = this._mqttService.observe('ipos/client/position').subscribe((message: IMqttMessage) => { let upd: PositionUpdate = JSON.parse(message.payload.toString()) - upd.object.forEach(obj => this.registerPoint(obj.id, obj)); + upd.objects.forEach(obj => this.registerPoint(obj.id, obj)); }); } @@ -173,7 +173,7 @@ export class MapComponent implements OnInit { public ngOnDestroy() { this.subsPosition.unsubscribe(); - this.subsOrigin.unsubscribe(); + this.subsRoot.unsubscribe(); } } diff --git a/src/app/model/base-model.ts b/src/app/model/base-model.ts index eecefd6d9f348e0d4b519c1eeec7811b5ef5e2f5..28427926e1df77cc40cb1add8083813dd6bff34b 100644 --- a/src/app/model/base-model.ts +++ b/src/app/model/base-model.ts @@ -1,6 +1,6 @@ export interface PositionUpdate { - object: Object[] + objects: Object[] type: NotificationType } @@ -12,6 +12,7 @@ export interface Object { position: Position orientation?: Orientation lastPosUpdate: string + zoneDescriptors: ZoneDesc[] } export interface Position { @@ -32,6 +33,11 @@ export interface RelativePos { z?: number } +interface ZoneDesc { + zoneId: string + notificationType: NotificationType +} + interface Orientation { x: number y: number diff --git a/src/styles.scss b/src/styles.scss index 1d42792fc78f166a51b413713a51f9294317ee9c..4ad47da56dd87f3b9602e7b757ea8b1df4c157a4 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -16,9 +16,9 @@ app-root { min-height: 100vh; } -html, body { height: 100%; } -body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } - +.wrapper { + padding: 0 calc((100% - #{$break-large})/2); +} /*Legend specific*/ .legend { diff --git a/src/variables.scss b/src/variables.scss index 9ebbb5f85e1dd89436317fa55822e661d81978b9..a9e9dc67addc9bbe88b098d4598ce897fcc692a5 100644 --- a/src/variables.scss +++ b/src/variables.scss @@ -15,4 +15,4 @@ $divider-color: #BDBDBD; $break-small: 380px; -$break-large: 1100px; +$break-large: 1200px;