|
@ -11,14 +11,21 @@ import { interval } from 'rxjs'; |
|
|
styleUrls: ['./chart.component.scss'] |
|
|
styleUrls: ['./chart.component.scss'] |
|
|
}) |
|
|
}) |
|
|
export class ChartComponent implements OnInit { |
|
|
export class ChartComponent implements OnInit { |
|
|
|
|
|
// The subscribe object for the rxjs interval
|
|
|
sub: any; |
|
|
sub: any; |
|
|
|
|
|
|
|
|
constructor(private logsService: LogsService) {} |
|
|
constructor(private logsService: LogsService) {} |
|
|
|
|
|
|
|
|
|
|
|
// We're going to keep all our data here.
|
|
|
|
|
|
// This is a two dimmensional Object array.
|
|
|
|
|
|
// It keeps all the data points for every sensor we have to display on the chart.
|
|
|
dataPoints: Array<Array<Object>> = []; |
|
|
dataPoints: Array<Array<Object>> = []; |
|
|
|
|
|
|
|
|
|
|
|
// Global object to keep our chart.
|
|
|
chart: any; |
|
|
chart: any; |
|
|
|
|
|
|
|
|
|
|
|
// Method to toggle data series visibility on and off.
|
|
|
|
|
|
// This will be used on the chart legend.
|
|
|
toggleDataSeries(e: any) { |
|
|
toggleDataSeries(e: any) { |
|
|
if (typeof e.dataSeries.visible === 'undefined' || e.dataSeries.visible) { |
|
|
if (typeof e.dataSeries.visible === 'undefined' || e.dataSeries.visible) { |
|
|
e.dataSeries.visible = false; |
|
|
e.dataSeries.visible = false; |
|
@ -28,14 +35,22 @@ export class ChartComponent implements OnInit { |
|
|
this.chart.render(); |
|
|
this.chart.render(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Variable to store the data config of our chart.
|
|
|
|
|
|
// We'll fill this with stuff about all of our sensors later on.
|
|
|
dataConfig: Array<Object> = []; |
|
|
dataConfig: Array<Object> = []; |
|
|
|
|
|
|
|
|
async ngOnInit() { |
|
|
async ngOnInit() { |
|
|
|
|
|
// OnInit, get the logs for the first time
|
|
|
let data = await this.logsService.getLogsFirstRun(); |
|
|
let data = await this.logsService.getLogsFirstRun(); |
|
|
|
|
|
|
|
|
|
|
|
// For every sensor in the sensorReadings
|
|
|
for (const key in data[0]['sensorReadings']) { |
|
|
for (const key in data[0]['sensorReadings']) { |
|
|
if (data[0]['sensorReadings'].hasOwnProperty(key)) { |
|
|
if (data[0]['sensorReadings'].hasOwnProperty(key)) { |
|
|
|
|
|
// Get the value for the sensor
|
|
|
const element = data[0]['sensorReadings'][key]; |
|
|
const element = data[0]['sensorReadings'][key]; |
|
|
|
|
|
// Create a new array inside dataPoint for the sensor
|
|
|
this.dataPoints[key] = []; |
|
|
this.dataPoints[key] = []; |
|
|
|
|
|
// Add configuration for the sensor
|
|
|
this.dataConfig.push({ |
|
|
this.dataConfig.push({ |
|
|
type: 'line', |
|
|
type: 'line', |
|
|
xValueType: 'dateTime', |
|
|
xValueType: 'dateTime', |
|
@ -47,20 +62,31 @@ export class ChartComponent implements OnInit { |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
let dpsLength = 0; |
|
|
let dpsLength = 0; |
|
|
|
|
|
|
|
|
|
|
|
// Create a new CanvasJS chart on #chartContainer
|
|
|
this.chart = new CanvasJS.Chart('chartContainer', { |
|
|
this.chart = new CanvasJS.Chart('chartContainer', { |
|
|
zoomEnabled: true, |
|
|
zoomEnabled: true, |
|
|
animationEnabled: true, |
|
|
animationEnabled: true, |
|
|
exportEnabled: true, |
|
|
exportEnabled: true, |
|
|
|
|
|
// When we have large time gaps between our data,
|
|
|
|
|
|
// it'd be better to shrink it so it doesn't mess up
|
|
|
|
|
|
// the way our useful data is displayed.
|
|
|
|
|
|
// That's where scaleBreaks come in.
|
|
|
axisX: { |
|
|
axisX: { |
|
|
scaleBreaks: { |
|
|
scaleBreaks: { |
|
|
autoCalculate: true, |
|
|
autoCalculate: true, |
|
|
maxNumberOfAutoBreaks: 5 |
|
|
maxNumberOfAutoBreaks: 5 |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
// Share the same tooltip between on data lines.
|
|
|
toolTip: { |
|
|
toolTip: { |
|
|
shared: true |
|
|
shared: true |
|
|
}, |
|
|
}, |
|
|
|
|
|
// Set up a legend.
|
|
|
|
|
|
// We also want to be able to toggle our data series,
|
|
|
|
|
|
// so we do that with toggleDataSeries on click.
|
|
|
legend: { |
|
|
legend: { |
|
|
cursor: 'pointer', |
|
|
cursor: 'pointer', |
|
|
verticalAlign: 'top', |
|
|
verticalAlign: 'top', |
|
@ -68,46 +94,84 @@ export class ChartComponent implements OnInit { |
|
|
fontColor: 'dimGrey', |
|
|
fontColor: 'dimGrey', |
|
|
itemclick: this.toggleDataSeries |
|
|
itemclick: this.toggleDataSeries |
|
|
}, |
|
|
}, |
|
|
|
|
|
// That's the sensor series configuration we created earlier.
|
|
|
data: [...this.dataConfig] |
|
|
data: [...this.dataConfig] |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Add the createdAt field of the log to the sensorReadings object
|
|
|
|
|
|
// for ever log item we have. This way we will have it available
|
|
|
|
|
|
// without doing anything weird later on.
|
|
|
let sensorReadings: any = data.map(log => { |
|
|
let sensorReadings: any = data.map(log => { |
|
|
|
|
|
// Dump the logs into a new object so we don't have to touch the original one
|
|
|
let d = log['sensorReadings']; |
|
|
let d = log['sensorReadings']; |
|
|
|
|
|
|
|
|
|
|
|
// and add the createdAt field to it.
|
|
|
d['time'] = log['createdAt']; |
|
|
d['time'] = log['createdAt']; |
|
|
|
|
|
|
|
|
|
|
|
// Then we just return the whole thing.
|
|
|
return d; |
|
|
return d; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
let i = 0; |
|
|
// For every log we have
|
|
|
|
|
|
|
|
|
sensorReadings.forEach((element: any) => { |
|
|
sensorReadings.forEach((element: any) => { |
|
|
for (const key in element) { |
|
|
for (const key in element) { |
|
|
|
|
|
// and for every field/sensor that is not time, since we just need this for the X Axis
|
|
|
if (element.hasOwnProperty(key) && key != 'time') { |
|
|
if (element.hasOwnProperty(key) && key != 'time') { |
|
|
|
|
|
// keep the value of the sensor
|
|
|
const value = element[key]; |
|
|
const value = element[key]; |
|
|
|
|
|
|
|
|
|
|
|
// and add a new data point for that sensor, with it's createdAt time
|
|
|
|
|
|
// on the X Axis and the sensor value on the Y Axis.
|
|
|
this.dataPoints[key].push({ x: new Date(element['time']), y: parseInt(value) }); |
|
|
this.dataPoints[key].push({ x: new Date(element['time']), y: parseInt(value) }); |
|
|
|
|
|
|
|
|
|
|
|
// Set the lenght of the datapoints to the amount of datapoints after we added them
|
|
|
dpsLength = this.dataPoints.length; |
|
|
dpsLength = this.dataPoints.length; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Now that we've added some data to the chart, render it.
|
|
|
this.chart.render(); |
|
|
this.chart.render(); |
|
|
|
|
|
|
|
|
|
|
|
// Run the chart update every second.
|
|
|
this.sub = interval(1000).subscribe(async val => { |
|
|
this.sub = interval(1000).subscribe(async val => { |
|
|
|
|
|
// If live updates are enabled:
|
|
|
if (this.logsService.liveUpdate) { |
|
|
if (this.logsService.liveUpdate) { |
|
|
|
|
|
// Get the changes since the last time we got the logs.
|
|
|
let data = await this.logsService.getUpdates(); |
|
|
let data = await this.logsService.getUpdates(); |
|
|
|
|
|
|
|
|
|
|
|
// Again, we need to add the createAt field of the log to the sensor readings object.
|
|
|
let sensorReadings: any = data.map(log => { |
|
|
let sensorReadings: any = data.map(log => { |
|
|
|
|
|
// Dump the logs into a new object so we don't have to touch the original one
|
|
|
let d = log['sensorReadings']; |
|
|
let d = log['sensorReadings']; |
|
|
|
|
|
|
|
|
|
|
|
// and add the createdAt field to it.
|
|
|
d['time'] = log['createdAt']; |
|
|
d['time'] = log['createdAt']; |
|
|
|
|
|
|
|
|
|
|
|
// Then we just return the whole thing.
|
|
|
return d; |
|
|
return d; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// For every new log we got
|
|
|
sensorReadings.forEach((element: any) => { |
|
|
sensorReadings.forEach((element: any) => { |
|
|
for (const key in element) { |
|
|
for (const key in element) { |
|
|
|
|
|
// and for every field/sensor that is not time, since we just need this for the X Axis
|
|
|
if (element.hasOwnProperty(key) && key != 'time') { |
|
|
if (element.hasOwnProperty(key) && key != 'time') { |
|
|
|
|
|
// keep the value of the sensor
|
|
|
const value = element[key]; |
|
|
const value = element[key]; |
|
|
|
|
|
|
|
|
|
|
|
// and add a new data point for that sensor, with it's createdAt time
|
|
|
|
|
|
// on the X Axis and the sensor value on the Y Axis.
|
|
|
this.dataPoints[key].push({ x: new Date(element['time']), y: parseInt(value) }); |
|
|
this.dataPoints[key].push({ x: new Date(element['time']), y: parseInt(value) }); |
|
|
|
|
|
|
|
|
|
|
|
// Set the lenght of the datapoints to the amount of datapoints after we added them
|
|
|
dpsLength = this.dataPoints.length; |
|
|
dpsLength = this.dataPoints.length; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
dpsLength++; |
|
|
dpsLength++; |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
// Render the chart again, in case we've added any new log values to it.
|
|
|
this.chart.render(); |
|
|
this.chart.render(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|