Skip to content

Commit

Permalink
Subscribe to data changes in device view, auto player
Browse files Browse the repository at this point in the history
  • Loading branch information
stalehd committed Apr 10, 2023
1 parent 7426e29 commit a5a994e
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<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>Sist oppdatert {{ samples.lastDataUpdate |date:'YYYY-mm-dd HH:MM'}}</div>
</div>
<div class="allmap">
<app-device-map-view class="allmap"></app-device-map-view>
Expand Down
20 changes: 16 additions & 4 deletions frontend/src/app/device-view/device-view.component.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
<div class="device-status-page">
<div class="data">
<div #chart id="chart" class="chart"></div>
Sist oppdatert {{ samples.lastPoll |date:'YYYY-mm-dd HH:MM'}}
Sist oppdatert {{ samples.lastDataUpdate |date:'YYYY-mm-dd HH:MM'}}
</div>
<div class="device-info">
<div *ngFor="let metric of metrics" class="metric">
<div class="metric-value">{{ metric.bigText }}</div>
<div class="metric-desc">{{ metric.smallText }}</div>
<div class="metric">
<div class="metric-value">{{ metricBLE }}</div>
<div class="metric-desc">Maks antall bluetooth-enheter</div>
</div>
<div class="metric">
<div class="metric-value">{{ metricWifi }}</div>
<div class="metric-desc">Maks antall WiFi-enheter</div>
</div>
<div class="metric">
<div class="metric-value">{{ metricSampleCount }}</div>
<div class="metric-desc">Målinger i perioden</div>
</div>
<div class="metric">
<div class="metric-value">{{ metricDensity }}</div>
<div class="metric-desc">Folketetthet ({{ metricDensityPercent }}% av maks)</div>
</div>
</div>
<div class="location">
Expand Down
49 changes: 33 additions & 16 deletions frontend/src/app/device-view/device-view.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, Input, OnChanges, OnInit, Renderer2, SimpleChanges, ViewChild } from '@angular/core';
import { AfterContentInit, AfterViewInit, ChangeDetectorRef, Component, ElementRef, Input, OnChanges, OnInit, Renderer2, SimpleChanges, ViewChild } from '@angular/core';
import { V1Device } from '../api/pax';
import * as d3 from 'd3';
import * as Plot from '@observablehq/plot';
Expand All @@ -19,17 +19,21 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
@Input("device") device: V1Device = {};
@ViewChild("chart") chartRef?: ElementRef;

metrics: Metric[] = [];
metricDensity: string = "";
metricDensityPercent: number = 0;
metricSampleCount: number = 0;
metricWifi: number = 0;
metricBLE: number = 0;

data: DeviceSample[] = [];
errorMessage: string = "";

chartIntervalHours: number = 24;

chart?: (SVGElement | HTMLElement);

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

ngOnInit(): void {
Expand All @@ -47,9 +51,23 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
}

loadData(): void {
this.data = this.samples.dataForDevice(this.device.id!);
this.buildMetrics();
this.showChart();
var elements: DeviceSample[] = [];
this.samples.allData().subscribe({
next: (d: DeviceSample) => {
if (d.id == this.device.id) {
elements.push(d);
}
},
complete: () => {
this.data = elements;
this.showChart();
this.buildMetrics();
// We change the bindings in a change event but it won't cascade. This forces the
// change detection to run one more time without issuing an error
// see: https://angular.io/errors/NG0100
this.cd.detectChanges();
},
});
}

showChart(): void {
Expand Down Expand Up @@ -77,7 +95,7 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
x: {
label: "Klokkeslett",
grid: true,
tickFormat: d3.utcFormat("%H:%M"),
tickFormat: d3.timeFormat("%H:%M"),
domain: [startDate, endDate]
},
marks: [
Expand Down Expand Up @@ -122,7 +140,7 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
Plot.frame(),
],
color: {
legend: true,
legend: false,
domain: ["wifi", "ble"],
range: ["red", "blue"]
},
Expand All @@ -132,7 +150,7 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
background: '#eeeeee',
fill: '#808080',
}
})
});
this.renderer.appendChild(this.chartRef?.nativeElement, this.chart)
}

Expand All @@ -159,11 +177,10 @@ export class DeviceViewComponent implements OnInit, AfterViewInit, OnChanges {
if (percent < 25) {
busyIndicator = "Svært Lav";
}
this.metrics = [
{ bigText: String(maxBle), smallText: "Maks antall BLE" },
{ bigText: String(maxWifi), smallText: "Maks antall WiFi" },
{ bigText: String(this.data.length), smallText: "Målinger i perioden" },
{ bigText: busyIndicator, smallText: "Folketetthet (" + percent + "% av maks)" }
];
this.metricBLE = maxBle;
this.metricWifi = maxWifi;
this.metricSampleCount = this.data.length;
this.metricDensity = busyIndicator;
this.metricDensityPercent = percent;
}
}
3 changes: 3 additions & 0 deletions frontend/src/app/main-page/main-page.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
<span class="title">
PAX
</span>
<a [href]="" (click)="autoPlay(!this.autoPlayEnabled)">Play</a>
<span *ngIf="this.autoPlayEnabled">[Playing]</span>
<span *ngIf="!this.autoPlayEnabled">[Stopped]</span>
</div>
<div class="content">
<div class="list">
Expand Down
39 changes: 38 additions & 1 deletion frontend/src/app/main-page/main-page.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { V1Device } from '../api/pax';
import { SampleService } from '../sample.service';
import { Subscription, interval } from 'rxjs';

@Component({
selector: 'app-main-page',
Expand All @@ -11,13 +12,20 @@ export class MainPageComponent implements OnInit {

activeDevices: V1Device[] = [];
activeDevice?: V1Device;
autoPlayEnabled: boolean = false;
activeStep: number = 0;
autoPlayer?: Subscription;

constructor(
protected samples: SampleService,
) {
let list: V1Device[] = [];
this.samples.activeDevices().subscribe({
next: (d: V1Device) => {
this.activeDevices.push(d);
list.push(d);
},
complete: () => {
this.activeDevices = list;
},
})
}
Expand All @@ -28,4 +36,33 @@ export class MainPageComponent implements OnInit {
setActiveDevice(device?: V1Device): void {
this.activeDevice = device;
}

autoPlay(enable: boolean) {
if (!enable) {
this.autoPlayEnabled = false;
this.autoPlayer?.unsubscribe();
return;
}
this.activeStep = -1;
this.activeDevice = undefined;
this.autoPlayEnabled = true;
this.autoPlayer = interval(15000).subscribe((val) => this.autoStep());
}

autoStep(): void {
if (!this.autoPlayEnabled) {
return;
}
this.activeStep++;
if (this.activeStep >= this.activeDevices.length) {
this.activeStep = -1;
}
if (this.activeStep == -1) {
this.activeDevice = undefined;
return;
}
this.activeDevice = this.activeDevices[this.activeStep];
}


}
67 changes: 43 additions & 24 deletions frontend/src/app/sample.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { PaxServiceService, V1Data, V1Device, V1ListDataResponse, V1ListDevicesResponse, V1Sample } from './api/pax';
import { Observable, ReplaySubject, combineLatest, interval } from 'rxjs';
import { Observable, ReplaySubject, interval } from 'rxjs';
import { HttpErrorResponse } from '@angular/common/http';

// Also a custom sample setup
Expand All @@ -19,14 +19,12 @@ export class SampleService {

public errorMessage: string = "";

public lastPoll: Date = new Date();
public lastDataUpdate: Date = new Date();
private lastPoll: Date = new Date();

// Subject for initial data load
private dataSubject = new ReplaySubject<DeviceSample>();

// Subjects for individual device load
private deviceSubjects: Map<string, ReplaySubject<DeviceSample[]>> = new Map<string, ReplaySubject<DeviceSample[]>>();

// Subject for updates
private updateSubject = new ReplaySubject<DeviceSample>();

Expand All @@ -41,7 +39,7 @@ export class SampleService {
return this.activeDeviceSubject;
}

//dataUpdater = interval(60000).subscribe((val) => console.debug('Called update', val));
dataUpdater = interval(60000).subscribe((val) => this.pollForChanges());

constructor(
protected paxService: PaxServiceService,
Expand All @@ -53,6 +51,8 @@ export class SampleService {
let chartIntervalHours = 24;
let dayAgo: string = "" + (new Date().getTime() - (chartIntervalHours * 3600 * 1000));
let now: string = "" + (new Date().getTime());
this.lastDataUpdate = new Date();
this.lastPoll = new Date();
this.paxService.paxServiceListData(dayAgo, now).subscribe({
next: (value: V1ListDataResponse) => {
if (value.data) {
Expand Down Expand Up @@ -88,36 +88,55 @@ export class SampleService {
this.dataSubject.complete();
},
complete: () => {
this.lastPoll = new Date();

// FIXME: Postpone the complete call when we start polling?
this.activeDeviceSubject.complete();
this.dataSubject.complete();
},
});
}

public hasError(): boolean {
return this.errorMessage != "";
private pollForChanges(): void {
let lastCheck: string = "" + this.lastPoll.getTime();
this.lastPoll = new Date();
this.paxService.paxServiceListData(lastCheck).subscribe({
next: (value: V1ListDataResponse) => {
if (value.data) {
value.data.forEach((data) => this.addDataToSamples(data));
}
},
error: (e: HttpErrorResponse) => {
this.errorMessage = e.message;
this.updateSubject.error(e);
},
complete: () => {
},
});
}

public dataForDevice(id: string): DeviceSample[] {
let ret = this.allSamples.find((v: V1Data) => v.deviceId == id);
if (ret && ret.samples) {
return ret.samples.map(d => {
return {
id: "",
name: "",
time: new Date(parseInt(d.timestamp!)),
ble: d.bluetoothCount || 0,
wifi: d.wifiCount || 0,
};
});
// Add samples to the existing data set
private addDataToSamples(data: V1Data): void {
var exists: boolean = false;
this.allSamples.forEach((set, index) => {
if (set.deviceId == data.deviceId) {
exists = true;
set.samples?.forEach((sample) => {
this.allSamples[index].samples?.push(sample);
});
console.debug("Added " + (set.samples?.length || 0) + " samples to data for " + set.deviceId)
}
});
if (!exists) {
console.debug("New device; adding new sample set: ", data);
this.allSamples.push(data);
}
return [];
}

public hasError(): boolean {
return this.errorMessage != "";
}

public allData(): Observable<DeviceSample> {
// TODO: If the data is more than N minutes old return a new sample set
return this.dataSubject;
}

}

0 comments on commit a5a994e

Please sign in to comment.