From 3f9df5c1eb6c7543b6aab8daaed62f38700ef412 Mon Sep 17 00:00:00 2001 From: saji Date: Sat, 17 Feb 2024 19:26:13 -0600 Subject: [PATCH] wip: add openmct-type historical api --- http.go | 8 +-- internal/db/getters.go | 49 +++++++++++++++++++ ...table_down.sql => 4_import_table_down.sql} | 0 ...ort_table_up.sql => 4_import_table_up.sql} | 2 +- internal/logparsers/parsers.go | 5 ++ 5 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 internal/db/getters.go rename internal/db/migrations/{4_add_import_table_down.sql => 4_import_table_down.sql} (100%) rename internal/db/migrations/{4_add_import_table_up.sql => 4_import_table_up.sql} (83%) diff --git a/http.go b/http.go index 20a75fc..dbffc5c 100644 --- a/http.go +++ b/http.go @@ -81,11 +81,11 @@ func apiV1(broker *Broker, db *db.TelemDb) chi.Router { }) - // this is to get packets by a name. - r.Get("/{name:[a-z_]+}", func(w http.ResponseWriter, r *http.Request) { - // support field getting (matching too?) - // support limit + // this is to get a single field + r.Get("/{name:[a-z_]+}/{field:[a-z_]}", func(w http.ResponseWriter, r *http.Request) { + // we need a start and end time. If none is provided, + // we use unix epoch as start, and now + 1 day as end. }) }) diff --git a/internal/db/getters.go b/internal/db/getters.go new file mode 100644 index 0000000..30506bc --- /dev/null +++ b/internal/db/getters.go @@ -0,0 +1,49 @@ +package db + +import ( + "context" + "strings" + "time" +) + +// Datum is a single measurement - it is more granular than a packet. +// the classic example is bms_measurement.current +type Datum struct { + Timestamp time.Time `db:"timestamp"` + Value any `db:"val"` +} + +// GetValues queries the database for values in a given time range. +// A value is a specific data point. For example, bms_measurement.current +// would be a value. +func (tdb *TelemDb) GetValues(ctx context.Context, packetName, field string , start time.Time, + end time.Time) ([]Datum, error) { + // this fragment uses json_extract from sqlite to get a single + // nested value. + const SqlFrag = ` + SELECT + datetime(ts /1000.0, 'unixepoch', 'subsec') as timestamp, + json_extract(data, ?) as val, + FROM bus_events WHERE name IS ? AND timestamp BETWEEN ? AND ? + ` + + fieldJson := "$." + field + + rows, err := tdb.db.QueryxContext(ctx, fieldJson, packetName, start, end) + defer rows.Close() + if err != nil { + return nil, err + } + data := make([]Datum, 0, 10) + for rows.Next() { + var d Datum + err = rows.StructScan(&d) + if err != nil { + return data, err + } + data = append(data, d) + } + + return data, nil +} + diff --git a/internal/db/migrations/4_add_import_table_down.sql b/internal/db/migrations/4_import_table_down.sql similarity index 100% rename from internal/db/migrations/4_add_import_table_down.sql rename to internal/db/migrations/4_import_table_down.sql diff --git a/internal/db/migrations/4_add_import_table_up.sql b/internal/db/migrations/4_import_table_up.sql similarity index 83% rename from internal/db/migrations/4_add_import_table_up.sql rename to internal/db/migrations/4_import_table_up.sql index 5d798e0..2a5343e 100644 --- a/internal/db/migrations/4_add_import_table_up.sql +++ b/internal/db/migrations/4_import_table_up.sql @@ -1,6 +1,6 @@ CREATE TABLE "import_log" ( "filename" TEXT NOT NULL, - "date" INTEGER NOT NULL, + "date" TIMESTAMP NOT NULL, "count" INTEGER NOT NULL, "start_time" INTEGER NOT NULL, "end_time" INTEGER NOT NULL diff --git a/internal/logparsers/parsers.go b/internal/logparsers/parsers.go index 24dce35..7259ef6 100644 --- a/internal/logparsers/parsers.go +++ b/internal/logparsers/parsers.go @@ -128,6 +128,11 @@ func parseTelemLogLine(line string) (b skylab.BusEvent, err error) { } ts := time.Unix(unixSeconds, unixMillis*1e6) + // VALIDATION STEP: sometimes the data gets really whack. + // We check that the time is between 2017 and 2032. + // Realistically we will not be using this software then. + // TODO: add this + id, err := strconv.ParseUint(a[3], 16, 16) if err != nil { err = NewFormatError("failed to parse id", err)