|
@@ -1,5 +1,6 @@
|
|
|
import fs from 'fs';
|
|
import fs from 'fs';
|
|
|
import fsp from 'fs/promises';
|
|
import fsp from 'fs/promises';
|
|
|
|
|
+import moment from 'moment';
|
|
|
import path from 'path';
|
|
import path from 'path';
|
|
|
import { Database as SQLiteDB, OPEN_CREATE, OPEN_READWRITE, RunResult } from 'sqlite3';
|
|
import { Database as SQLiteDB, OPEN_CREATE, OPEN_READWRITE, RunResult } from 'sqlite3';
|
|
|
|
|
|
|
@@ -146,50 +147,71 @@ export class Database {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public async getServerData(serverID: number, start: Date, end: Date): Promise<ReducedData[]> {
|
|
|
|
|
- /* FIRST DRAFT - SIMPLY GET ALL DATA POINTS OF ALL TYPES */
|
|
|
|
|
- /* TODO: ONLY GET DATA OF ONE TYPE, REDUCE DOWN TO FEWER DATA POINTS, COMPUTING AVGs & PEAKs */
|
|
|
|
|
-
|
|
|
|
|
- const result = await this.stmt(
|
|
|
|
|
- `SELECT
|
|
|
|
|
- ServerDataEntry.*,
|
|
|
|
|
- ServerDataValue.Type,
|
|
|
|
|
- ServerDataValue.Key,
|
|
|
|
|
- ServerDataValue.Value
|
|
|
|
|
- FROM ServerDataEntry
|
|
|
|
|
- JOIN ServerDataValue ON ServerDataEntry.ID = ServerDataValue.EntryID
|
|
|
|
|
- WHERE ServerID = ?
|
|
|
|
|
- AND Timestamp BETWEEN ? AND ?
|
|
|
|
|
- ORDER BY Timestamp, Type, Key;`,
|
|
|
|
|
- [serverID, start.getTime(), end.getTime()]
|
|
|
|
|
|
|
+ public async getServerDataTypes(serverID: number) {
|
|
|
|
|
+ const results = await this.stmt(
|
|
|
|
|
+ `
|
|
|
|
|
+ SELECT
|
|
|
|
|
+ ServerDataValue.Type
|
|
|
|
|
+ FROM ServerDataEntry
|
|
|
|
|
+ JOIN ServerDataValue ON ServerDataEntry.ID = ServerDataValue.EntryID
|
|
|
|
|
+ WHERE ServerDataEntry.ServerID = ?
|
|
|
|
|
+ GROUP BY ServerDataValue.Type
|
|
|
|
|
+ ORDER BY ServerDataValue.Type;
|
|
|
|
|
+ `,
|
|
|
|
|
+ [serverID]
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- return result.rows.reduce((res, line, i) => {
|
|
|
|
|
- const timestamp = line['Timestamp'];
|
|
|
|
|
- let entry: ReducedData;
|
|
|
|
|
- if (i === 0 || res[res.length - 1].time.getTime() !== timestamp) {
|
|
|
|
|
- entry = { time: new Date(timestamp) } as ReducedData;
|
|
|
|
|
- res.push(entry);
|
|
|
|
|
|
|
+ return results.rows.reduce((res: Array<ServerDataTypesConfig>, { Type: type }) => {
|
|
|
|
|
+ if (!type.startsWith('hdd:')) {
|
|
|
|
|
+ res.push({ type });
|
|
|
} else {
|
|
} else {
|
|
|
- entry = res[res.length - 1];
|
|
|
|
|
|
|
+ let hdd = res.find(c => c.type === 'hdd');
|
|
|
|
|
+ if (!hdd) {
|
|
|
|
|
+ hdd = { type: 'hdd', subtypes: [] };
|
|
|
|
|
+ res.push(hdd);
|
|
|
|
|
+ }
|
|
|
|
|
+ hdd.subtypes.push({ type: type.substring(4) });
|
|
|
}
|
|
}
|
|
|
|
|
+ return res;
|
|
|
|
|
+ }, []) as Array<ServerDataTypesConfig>;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- let type: string = line['Type'];
|
|
|
|
|
- if (type.startsWith('hdd:')) {
|
|
|
|
|
- const mount = type.substring(4);
|
|
|
|
|
- if (typeof entry.hdd === 'undefined') entry.hdd = {};
|
|
|
|
|
- if (typeof entry.hdd[mount] === 'undefined') entry.hdd[mount] = {} as ReducedValuesMinMax;
|
|
|
|
|
- entry.hdd[mount][line['Key']] = line['Value'];
|
|
|
|
|
- } else {
|
|
|
|
|
- if (typeof entry[type] === 'undefined') entry[type] = {};
|
|
|
|
|
- entry[type][line['Key']] = line['Value'];
|
|
|
|
|
|
|
+ public async queryServerData(serverID: number, type: ServerDataType, from: Date, to: Date): Promise<ServerData[]> {
|
|
|
|
|
+ const diffMs = moment(to).diff(moment(from));
|
|
|
|
|
+ const sectionMs = Math.floor(diffMs / 100);
|
|
|
|
|
+ const select_max = type !== 'cpu';
|
|
|
|
|
+ const select_types = select_max ? [type, type, type] : [type, type];
|
|
|
|
|
+ const result = await this.stmt(
|
|
|
|
|
+ `
|
|
|
|
|
+ SELECT
|
|
|
|
|
+ CEIL(Timestamp / ?) * ? as 'Timegroup',
|
|
|
|
|
+ AVG(VALUE_AVG.Value) as 'avg',
|
|
|
|
|
+ MAX(VALUE_PEAK.Value) as 'peak'${
|
|
|
|
|
+ select_max
|
|
|
|
|
+ ? `,
|
|
|
|
|
+ MAX(VALUE_MAX.Value) as 'max'`
|
|
|
|
|
+ : ''
|
|
|
|
|
+ }
|
|
|
|
|
+ FROM ServerDataEntry
|
|
|
|
|
+ JOIN ServerDataValue AS VALUE_AVG ON ServerDataEntry.ID = VALUE_AVG.EntryID AND VALUE_AVG.Type = ? AND VALUE_AVG.Key = 'avg'
|
|
|
|
|
+ JOIN ServerDataValue AS VALUE_PEAK ON ServerDataEntry.ID = VALUE_PEAK.EntryID AND VALUE_PEAK.Type = ? AND VALUE_PEAK.Key = 'peak'
|
|
|
|
|
+ ${
|
|
|
|
|
+ select_max
|
|
|
|
|
+ ? "JOIN ServerDataValue AS VALUE_MAX ON ServerDataEntry.ID = VALUE_MAX.EntryID AND VALUE_MAX.Type = ? AND VALUE_MAX.Key = 'max'"
|
|
|
|
|
+ : ''
|
|
|
}
|
|
}
|
|
|
|
|
+ WHERE ServerDataEntry.ServerID = ?
|
|
|
|
|
+ AND ServerDataEntry.Timestamp BETWEEN ? AND ?
|
|
|
|
|
+ GROUP BY Timegroup
|
|
|
|
|
+ ORDER BY Timegroup;
|
|
|
|
|
+ `,
|
|
|
|
|
+ [sectionMs, sectionMs, ...select_types, serverID, from.getTime(), to.getTime()]
|
|
|
|
|
+ );
|
|
|
|
|
|
|
|
- return res;
|
|
|
|
|
- }, [] as ReducedData[]);
|
|
|
|
|
|
|
+ return result.rows.map(r => ({ time: new Date(r.Timegroup), avg: r.avg, peak: r.peak, max: r.max }));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private async run(sql: string, params: any): Promise<RunResult> {
|
|
|
|
|
|
|
+ public async run(sql: string, params: any): Promise<RunResult> {
|
|
|
return new Promise<RunResult>((res, rej) => {
|
|
return new Promise<RunResult>((res, rej) => {
|
|
|
this.db.run(sql, params, function (err) {
|
|
this.db.run(sql, params, function (err) {
|
|
|
if (err) return rej(err);
|
|
if (err) return rej(err);
|
|
@@ -198,7 +220,7 @@ export class Database {
|
|
|
});
|
|
});
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private async stmt(sql: string, params: any[]) {
|
|
|
|
|
|
|
+ public async stmt(sql: string, params: any[]) {
|
|
|
return new Promise<{ result: RunResult; rows: any[] }>((res, rej) => {
|
|
return new Promise<{ result: RunResult; rows: any[] }>((res, rej) => {
|
|
|
const stmt = this.db.prepare(sql);
|
|
const stmt = this.db.prepare(sql);
|
|
|
stmt.all(params, function (err, rows) {
|
|
stmt.all(params, function (err, rows) {
|