Skip to content

Commit

Permalink
Show gateway status for whole time period (#101)
Browse files Browse the repository at this point in the history
* Show gateway status for whole time period

* Comment gateway status
  • Loading branch information
AramAlsabti authored Jun 2, 2022
1 parent 8ccca2f commit d531172
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 24 deletions.
17 changes: 17 additions & 0 deletions src/app/gateway/enums/gateway-status-interval.enum.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
import * as moment from 'moment';

export enum GatewayStatusInterval {
DAY = 'DAY',
WEEK = 'WEEK',
MONTH = 'MONTH',
}

export const gatewayStatusIntervalToDate = (
interval: GatewayStatusInterval
): Date => {
const now = new Date();

switch (interval) {
case GatewayStatusInterval.WEEK:
return moment(now).subtract(7, 'days').toDate();
case GatewayStatusInterval.MONTH:
return moment(now).subtract(30, 'days').toDate();
default:
return moment(now).subtract(1, 'days').toDate();
}
};
3 changes: 3 additions & 0 deletions src/app/gateway/gateway-status/gateway-status.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ <h3 *ngIf="title" class="title">{{title}}</h3>
</div>
<table #gatewayStatus mat-table [dataSource]="dataSource" class="status-table"
*ngIf="dataSource?.data.length && timeColumns.length; else noGatewayStatusData">
<!-- Gateway name is the first column -->
<ng-container matColumnDef="gatewayName">
<td mat-cell *matCellDef="let element">
<a [routerLink]="'/gateways/gateway-detail/' + element.id" routerLinkActive="active" *ngIf="shouldLinkToDetails">{{element.name}}</a>
Expand All @@ -31,6 +32,7 @@ <h3 *ngIf="title" class="title">{{title}}</h3>
</td>
</ng-container>

<!-- Timestamp columns which are dynamically generated -->
<ng-container *ngFor="let time of timeColumns; let index = index" matColumnDef="{{time.exactTimestamp}}">
<td mat-cell *matCellDef="let gateway"
[matTooltip]="!!gateway.statusTimestamps.length | gatewayStatusTooltip: neverSeenText:timestampText:time.tooltip:nameText:gateway.name"
Expand All @@ -44,6 +46,7 @@ <h3 *ngIf="title" class="title">{{title}}</h3>
</td>
</ng-container>

<!-- The columns for a row must be an array of simple types (string, number, ...) -->
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
<tr mat-footer-row *matFooterRowDef="displayedColumns"></tr>
</table>
Expand Down
40 changes: 25 additions & 15 deletions src/app/gateway/gateway-status/gateway-status.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { recordToEntries } from '@shared/helpers/record.helper';
import { LoRaWANGatewayService } from '@shared/services/lorawan-gateway.service';
import * as moment from 'moment';
import { Observable, Subject, Subscription } from 'rxjs';
import { GatewayStatusInterval } from '../enums/gateway-status-interval.enum';
import { GatewayStatusInterval, gatewayStatusIntervalToDate } from '../enums/gateway-status-interval.enum';
import { GatewayStatus, AllGatewayStatusResponse } from '../gateway.model';
import { map } from 'rxjs/operators';
import { DefaultPageSizeOptions } from '@shared/constants/page.constants';
Expand Down Expand Up @@ -46,7 +46,10 @@ export class GatewayStatusComponent implements AfterContentInit, OnDestroy {
* List of pre-processed timestamps for performance
*/
timeColumns: TimeColumn[] = [];
displayedColumns: (TimeColumn | string)[] = [];
/**
* Columns to display. Must not contain objects in order for the table to render.
*/
displayedColumns: string[] = [];
nameText = '';
neverSeenText = '';
timestampText = '';
Expand Down Expand Up @@ -135,14 +138,16 @@ export class GatewayStatusComponent implements AfterContentInit, OnDestroy {
timeInterval
).subscribe((response) => {
this.isLoadingResults = false;
// Get the earliest date from the selected interval
const fromDate = gatewayStatusIntervalToDate(timeInterval);

if (response) {
this.handleStatusResponse(response);
if (Array.isArray(response?.data)) {
this.handleStatusResponse(response, fromDate);
}
});
}

private handleStatusResponse(response: AllGatewayStatusResponse) {
private handleStatusResponse(response: AllGatewayStatusResponse, fromDate: Date) {
this.resultsLength = response.count;
const gatewaysWithLatestTimestampsPerHour = this.takeLatestTimestampInHour(
response.data
Expand All @@ -151,11 +156,18 @@ export class GatewayStatusComponent implements AfterContentInit, OnDestroy {
gatewaysWithLatestTimestampsPerHour
);

// Sort the gateways and their status timestamps
const sortedData = gatewaysWithWholeHourTimestamps
.slice()
.sort((a, b) => a.name.localeCompare(b.name));
.sort((a, b) => a.name.localeCompare(b.name))
.map((gateway) => ({
...gateway,
statusTimestamps: gateway.statusTimestamps.sort(
(a, b) => a.timestamp.getTime() - b.timestamp.getTime()
),
}));

this.buildColumns(sortedData);
this.buildColumns(sortedData, fromDate);
this.visibleFooterTimeInterval = Math.round(
this.clamp(this.timeColumns.length / 4, 1, 6)
);
Expand All @@ -164,28 +176,26 @@ export class GatewayStatusComponent implements AfterContentInit, OnDestroy {
this.dataSource.paginator = this.paginator;
}

private buildColumns(response: GatewayStatus[]) {
let minDate: Date | null | undefined;
private buildColumns(response: GatewayStatus[], fromDate: Date) {
// Ensure the first column is the (earliest) selected date
const minDate = fromDate;
let maxDate: Date | null | undefined;
this.timeColumns = [];

// Determine the date of the first and last column
response.forEach((gateway) => {
gateway.statusTimestamps.forEach(({ timestamp }) => {
if (!minDate) {
minDate = timestamp;
}
if (!maxDate) {
maxDate = timestamp;
}

if (timestamp < minDate) {
minDate = timestamp;
} else if (timestamp > maxDate) {
if (timestamp > maxDate) {
maxDate = timestamp;
}
});
});

// If there's a date range, build the columns from them
if (minDate && maxDate) {
const currDate = moment(minDate).startOf('hour');
const lastDate = moment(maxDate).startOf('hour');
Expand Down
36 changes: 27 additions & 9 deletions src/app/shared/pipes/gateway/gateway-status-class.pipe.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Pipe, PipeTransform } from '@angular/core';
import { StatusTimestamp } from '@app/gateway/gateway.model';
import * as moment from 'moment';

const neverSeenClass = 'never-seen';
const offlineClass = 'offline';
const onlineClass = 'online';

@Pipe({
name: 'gatewayStatusClass',
Expand All @@ -14,14 +19,27 @@ export class GatewayStatusClassPipe implements PipeTransform {
timestamp: string,
..._: unknown[]
): string {
return !statusTimestamps.length
? 'never-seen'
: statusTimestamps.some(
(gatewayTimestamp) =>
gatewayTimestamp.timestamp.toISOString() === timestamp &&
gatewayTimestamp.wasOnline
)
? 'online'
: 'offline';
if (!statusTimestamps.length) {
return neverSeenClass;
}

let currentStatusClass = offlineClass;
const selectedDate = moment(timestamp).toDate();

for (const gatewayTimestamp of statusTimestamps) {
const isoGatewayTimestamp = gatewayTimestamp.timestamp.toISOString();

if (isoGatewayTimestamp === timestamp) {
return gatewayTimestamp.wasOnline ? onlineClass : offlineClass;
}

if (gatewayTimestamp.timestamp > selectedDate) {
return currentStatusClass;
}

currentStatusClass = gatewayTimestamp.wasOnline ? onlineClass : offlineClass;
}

return currentStatusClass;
}
}

0 comments on commit d531172

Please sign in to comment.