Skip to content

Commit

Permalink
Overview + sample service
Browse files Browse the repository at this point in the history
The sample service will take care of all queries and updates.
  • Loading branch information
stalehd committed Apr 10, 2023
1 parent 0c9ae8f commit 87e1d37
Show file tree
Hide file tree
Showing 17 changed files with 352 additions and 143 deletions.
4 changes: 3 additions & 1 deletion frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import { MainPageComponent } from './main-page/main-page.component';
import { DeviceViewComponent } from './device-view/device-view.component';
import { DeviceMapViewComponent } from './device-map-view/device-map-view.component';
import { NgxMapLibreGLModule } from '@maplibre/ngx-maplibre-gl';
import { DeviceOverviewComponent } from './device-overview/device-overview.component';

@NgModule({
declarations: [
AppComponent,
MainChartComponent,
MainPageComponent,
DeviceViewComponent,
DeviceMapViewComponent
DeviceMapViewComponent,
DeviceOverviewComponent,
],
imports: [
BrowserModule,
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/app/device-map-view/device-map-view.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,24 @@ export class DeviceMapViewComponent implements OnInit, AfterViewInit, OnDestroy,
return;
}
if (!device.lat || device.lat == 0 || !device.lon || device.lon == 0) {
this.enableRotation = false;
this.cameraBearing = 0;
// Move out to ovewview
this.map?.flyTo({
center: [10.14, 63.42], // starting position [lng, lat]
zoom: 3, // starting zoom
pitch: 0,
bearing: 0,
speed: 2, // make the flying slow
curve: 2, // change the speed at which it zooms out

// This can be any easing function: it takes a number between
// 0 and 1 and returns another number between 0 and 1.
easing: (t) => t,

// this animation is considered essential with respect to prefers-reduced-motion
essential: true
});
return
}
let d_lat = device.lat
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/app/device-overview/device-overview.component.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.chart {
width: 80vw;
height: 30vh;
}


.chartlegend {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}

.legend svg {
text-align: center;
}

.allmap {
width: 100%;
height: 50vh;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="chartlegend">
<div #legend id="legend" class="legend"></div>
<div #chart id="chart" class="chart"></div>
<div>Sist oppdatert {{ samples.lastPoll |date:'YYYY-mm-dd HH:MM'}}</div>
</div>
<div class="allmap">
<app-device-map-view class="allmap"></app-device-map-view>
</div>
23 changes: 23 additions & 0 deletions frontend/src/app/device-overview/device-overview.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { DeviceOverviewComponent } from './device-overview.component';

describe('DeviceOverviewComponent', () => {
let component: DeviceOverviewComponent;
let fixture: ComponentFixture<DeviceOverviewComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DeviceOverviewComponent ]
})
.compileComponents();

fixture = TestBed.createComponent(DeviceOverviewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
95 changes: 95 additions & 0 deletions frontend/src/app/device-overview/device-overview.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { AfterViewInit, Component, ElementRef, OnInit, Renderer2, ViewChild } from '@angular/core';
import { DeviceSample, SampleService } from '../sample.service';
import * as d3 from 'd3';
import * as Plot from '@observablehq/plot';

@Component({
selector: 'app-device-overview',
templateUrl: './device-overview.component.html',
styleUrls: ['./device-overview.component.css']
})
export class DeviceOverviewComponent implements OnInit, AfterViewInit {
@ViewChild("chart") chartRef?: ElementRef;
chart?: (SVGElement | HTMLElement);
@ViewChild("legend") legendRef?: ElementRef;
legend?: (SVGElement | HTMLElement);

constructor(
protected samples: SampleService,
private renderer: Renderer2,
) { }

ngOnInit(): void {
}

ngAfterViewInit(): void {
this.showChart();
}

showChart(): void {
if (this.chart) {
// Remove the old one if it already exists
this.renderer.removeChild(this.chartRef?.nativeElement, this.chart, false);
}
let data = this.samples.allData();

let width = this.chartRef?.nativeElement.offsetWidth;
let max = (d3.max(data, (d: DeviceSample) => (d.ble + d.wifi)) || 1);
const hhmmFormat = d3.timeFormat("%m-%d %H:00")

this.chart = Plot.plot({
width: width,
marginLeft: 250,
marginBottom: 100,
x: {
label: "Tid",
tickRotate: -90,
},
y: {
label: "Enhet",
},
marks: [
Plot.cell(data, {
x: d => hhmmFormat(d.time),
y: "name",
fill: (d: DeviceSample) => {
let f = (d.ble + d.wifi);
return f;
},
}),
Plot.frame()
],
color: {
legend: false,
scheme: "gnbu",
reverse: false,
type: "linear"
},
style: {
fontFamily: 'sans-serif',
fontSize: '10pt',
background: '#eeeeee',
fill: '#808080',
}
});

this.renderer.appendChild(this.chartRef?.nativeElement, this.chart)

if (!this.legend) {
this.legend = Plot.legend({
color: {
domain: [0, max],
ticks: 3,
scheme: "gnbu",
reverse: false,
type: "linear"
},
width: 350,
ticks: 5,
label: "Antall enheter",
style: { background: "#eeeeee" }
},)
this.renderer.appendChild(this.legendRef?.nativeElement, this.legend)
}
}
}
8 changes: 2 additions & 6 deletions frontend/src/app/device-view/device-view.component.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@
vertical-align: middle;
}

#chart {
display: flex;
}

#chart svg {
flex: 1
.chart {
width: 80vw;
}
2 changes: 1 addition & 1 deletion frontend/src/app/device-view/device-view.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="device-status-page">
<div class="data">
<div #chart id="chart" class="chart"></div>
Sist oppdatert {{ lastPoll |date:'YYYY-mm-dd HH:MM'}}
Sist oppdatert {{ samples.lastPoll |date:'YYYY-mm-dd HH:MM'}}
</div>
<div class="device-info">
<div *ngFor="let metric of metrics" class="metric">
Expand Down
68 changes: 20 additions & 48 deletions frontend/src/app/device-view/device-view.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,8 @@
import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, Renderer2, SimpleChanges, ViewChild } from '@angular/core';
import { PaxServiceService, V1Data, V1Device, V1ListDataResponse, V1Sample } from '../api/pax';
import { HttpErrorResponse } from '@angular/common/http';
import { V1Device } from '../api/pax';
import * as d3 from 'd3';
import * as Plot from '@observablehq/plot';

// use a dedicated data struct for the samples since it's PITA to convert from strings to dates in
// the chart library
interface Sample {
time: Date;
ble: number;
wifi: number;
}
import { DeviceSample, SampleService } from '../sample.service';

interface Metric {
bigText: string;
Expand All @@ -28,67 +20,47 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
@ViewChild("chart") chartRef?: ElementRef;

metrics: Metric[] = [];
data: Sample[] = [];
data: DeviceSample[] = [];
errorMessage: string = "";
lastPoll: Date = new Date();

chartIntervalHours: number = 24;

chart?: (SVGElement | HTMLElement);

constructor(
protected paxService: PaxServiceService,
protected samples: SampleService,
private renderer: Renderer2,
) { }

ngOnInit(): void {
}

ngAfterViewInit(): void {
this.loadData();
}

ngOnChanges(changes: SimpleChanges): void {
this.loadData();
// This might trigger before the view is visible
if (this.chartRef) {
this.loadData();
}
}

loadData(): void {
let dayAgo: string = "" + (new Date().getTime() - (this.chartIntervalHours * 3600 * 1000));
let now: string = "" + (new Date().getTime());
this.paxService.paxServiceListData(dayAgo, now, 19000000).subscribe({
next: (value: V1ListDataResponse) => {
if (value.data) {
value.data.forEach((v, i) => {
if (v.deviceId == this.device.id) {
this.data = v.samples?.map(d => {
return {
time: new Date(parseInt(d.timestamp!)),
ble: d.bluetoothCount || 0,
wifi: d.wifiCount || 0,
};
}) || [];
}
});
}
},
error: (e: HttpErrorResponse) => {
this.errorMessage = e.message;
},
complete: () => {
this.buildMetrics();
this.lastPoll = new Date();
this.showChart();
},
});
this.data = this.samples.dataForDevice(this.device.id!);
this.buildMetrics();
this.showChart();
}

chart?: (SVGElement | HTMLElement);

showChart(): void {
if (this.chart) {
// Remove the old one if it already exists
this.renderer.removeChild(this.chartRef?.nativeElement, this.chart, false);
}
let width = this.chartRef?.nativeElement.offsetWidth;
let height = this.chartRef?.nativeElement.height;
let startDate = d3.min(this.data, (d: Sample) => d.time);
let endDate = d3.max(this.data, (d: Sample) => d.time)
let startDate = d3.min(this.data, (d: DeviceSample) => d.time);
let endDate = d3.max(this.data, (d: DeviceSample) => d.time)

/**
* Plot the actual samples as dots and moving window with averages as a solid line
Expand Down Expand Up @@ -118,7 +90,7 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
Plot.line(
this.data,
Plot.windowY(
{ reduce: "mean", k: 7, anchor: "middle" },
{ reduce: "mean", k: 20, anchor: "middle" },
{
x: "time",
y: "ble",
Expand All @@ -137,7 +109,7 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
Plot.line(
this.data,
Plot.windowY(
{ reduce: "mean", k: 7, anchor: "middle" },
{ reduce: "mean", k: 20, anchor: "middle" },
{
x: "time",
y: "wifi",
Expand All @@ -147,6 +119,7 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
}
)
),
Plot.frame(),
],
color: {
legend: true,
Expand All @@ -158,7 +131,6 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
fontSize: '10pt',
background: '#eeeeee',
fill: '#808080',
border: 'solid 1px silver',
}
})
this.renderer.appendChild(this.chartRef?.nativeElement, this.chart)
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/app/main-page/main-page.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
Vis aggregerte data for alle tellere
</div>
</div>
<div *ngFor="let device of devices" class="list-item" (click)="setActiveDevice(device)"
<div *ngFor="let device of samples.devicesWithData" class="list-item" (click)="setActiveDevice(device)"
[ngClass]="{ 'active' : (activeDevice == device)}">
<div class="item-title">
{{ device.name }}
Expand All @@ -29,8 +29,7 @@
</div>
</div>
<div class="detail">

<app-main-chart *ngIf="activeDevice==null"></app-main-chart>
<app-device-overview *ngIf="activeDevice==null"></app-device-overview>
<app-device-view *ngIf="activeDevice != null" [device]="activeDevice"></app-device-view>
</div>
</div>
Expand Down
Loading

0 comments on commit 87e1d37

Please sign in to comment.