openmct historical plugin MVP
This commit is contained in:
parent
9ec01c39de
commit
d90d7a0af4
13
db.go
13
db.go
|
@ -163,7 +163,7 @@ func (tdb *TelemDb) GetPackets(ctx context.Context, filter BusEventFilter, optio
|
||||||
// if we're filtering by names, add a where clause for it.
|
// if we're filtering by names, add a where clause for it.
|
||||||
if len(filter.Names) > 0 {
|
if len(filter.Names) > 0 {
|
||||||
names := strings.Join(filter.Names, ", ")
|
names := strings.Join(filter.Names, ", ")
|
||||||
qString := fmt.Sprintf("name IN (%s)", names)
|
qString := fmt.Sprintf(`name IN ("%s")`, names)
|
||||||
whereFrags = append(whereFrags, qString)
|
whereFrags = append(whereFrags, qString)
|
||||||
}
|
}
|
||||||
// TODO: identify if we need a special case for both time ranges
|
// TODO: identify if we need a special case for both time ranges
|
||||||
|
@ -181,7 +181,7 @@ func (tdb *TelemDb) GetPackets(ctx context.Context, filter BusEventFilter, optio
|
||||||
}
|
}
|
||||||
|
|
||||||
sb := strings.Builder{}
|
sb := strings.Builder{}
|
||||||
sb.WriteString("SELECT * from \"bus_events\"")
|
sb.WriteString(`SELECT * from "bus_events"`)
|
||||||
// construct the full statement.
|
// construct the full statement.
|
||||||
if len(whereFrags) > 0 {
|
if len(whereFrags) > 0 {
|
||||||
// use the where clauses.
|
// use the where clauses.
|
||||||
|
@ -231,8 +231,8 @@ func (tdb *TelemDb) GetPackets(ctx context.Context, filter BusEventFilter, optio
|
||||||
// Datum is a single measurement - it is more granular than a packet.
|
// Datum is a single measurement - it is more granular than a packet.
|
||||||
// the classic example is bms_measurement.current
|
// the classic example is bms_measurement.current
|
||||||
type Datum struct {
|
type Datum struct {
|
||||||
Timestamp time.Time `db:"timestamp"`
|
Timestamp time.Time `db:"timestamp" json:"ts"`
|
||||||
Value any `db:"val"`
|
Value any `db:"val" json:"val"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValues queries the database for values in a given time range.
|
// GetValues queries the database for values in a given time range.
|
||||||
|
@ -291,7 +291,6 @@ func (tdb *TelemDb) GetValues(ctx context.Context, bef BusEventFilter,
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// PacketDef is a database packet model
|
// PacketDef is a database packet model
|
||||||
type PacketDef struct {
|
type PacketDef struct {
|
||||||
Name string
|
Name string
|
||||||
|
@ -313,7 +312,6 @@ func (e *PacketNotFoundError) Error() string {
|
||||||
return "packet not found: " + string(*e)
|
return "packet not found: " + string(*e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// GetPacketDefN retrieves a packet matching the given name, if it exists.
|
// GetPacketDefN retrieves a packet matching the given name, if it exists.
|
||||||
// returns PacketNotFoundError if a matching packet could not be found.
|
// returns PacketNotFoundError if a matching packet could not be found.
|
||||||
func (tdb *TelemDb) GetPacketDefN(name string) (*PacketDef, error) {
|
func (tdb *TelemDb) GetPacketDefN(name string) (*PacketDef, error) {
|
||||||
|
@ -326,10 +324,7 @@ func (tdb *TelemDb) GetPacketDefF(field FieldDef) (*PacketDef, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// GetFieldDefs returns the given fields for a given packet definition.
|
// GetFieldDefs returns the given fields for a given packet definition.
|
||||||
func (tdb *TelemDb) GetFieldDefs(pkt PacketDef) ([]FieldDef, error) {
|
func (tdb *TelemDb) GetFieldDefs(pkt PacketDef) ([]FieldDef, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
22
http.go
22
http.go
|
@ -39,7 +39,7 @@ func extractBusEventFilter(r *http.Request) (*BusEventFilter, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return bef, err
|
return bef, err
|
||||||
}
|
}
|
||||||
bef.TimerangeStart = t
|
bef.TimerangeEnd = t
|
||||||
}
|
}
|
||||||
return bef, nil
|
return bef, nil
|
||||||
}
|
}
|
||||||
|
@ -69,9 +69,9 @@ func extractLimitModifier(r *http.Request) (*LimitOffsetModifier, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type RouterMod func (chi.Router)
|
type RouterMod func(chi.Router)
|
||||||
var RouterMods = []RouterMod{}
|
|
||||||
|
|
||||||
|
var RouterMods = []RouterMod{}
|
||||||
|
|
||||||
func TelemRouter(log *slog.Logger, broker *Broker, db *TelemDb) http.Handler {
|
func TelemRouter(log *slog.Logger, broker *Broker, db *TelemDb) http.Handler {
|
||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
|
@ -222,18 +222,13 @@ func apiV1GetPackets(tdb *TelemDb) http.HandlerFunc {
|
||||||
var res []skylab.BusEvent
|
var res []skylab.BusEvent
|
||||||
if lim != nil {
|
if lim != nil {
|
||||||
res, err = tdb.GetPackets(r.Context(), *bef, lim)
|
res, err = tdb.GetPackets(r.Context(), *bef, lim)
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
res, err = tdb.GetPackets(r.Context(), *bef)
|
res, err = tdb.GetPackets(r.Context(), *bef)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
b, err := json.Marshal(res)
|
b, err := json.Marshal(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
@ -269,7 +264,13 @@ func apiV1GetValues(db *TelemDb) http.HandlerFunc {
|
||||||
// override the bus event filter name option
|
// override the bus event filter name option
|
||||||
bef.Names = []string{name}
|
bef.Names = []string{name}
|
||||||
|
|
||||||
res, err := db.GetValues(r.Context(), *bef, field, lim)
|
var res []Datum
|
||||||
|
// make the call, skip the limit modifier if it's nil.
|
||||||
|
if lim == nil {
|
||||||
|
res, err = db.GetValues(r.Context(), *bef, field)
|
||||||
|
} else {
|
||||||
|
res, err = db.GetValues(r.Context(), *bef, field, lim)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// 500 server error:
|
// 500 server error:
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
@ -309,4 +310,3 @@ func apiV1GetRecord(db *TelemDb) http.HandlerFunc {
|
||||||
func apiV1UpdateRecord(db *TelemDb) http.HandlerFunc {
|
func apiV1UpdateRecord(db *TelemDb) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {}
|
return func(w http.ResponseWriter, r *http.Request) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
156
web/src/app.js
156
web/src/app.js
|
@ -3,17 +3,171 @@ import PhoebusPlugin from "./phoebusPlugin";
|
||||||
openmct.setAssetPath('openmct');
|
openmct.setAssetPath('openmct');
|
||||||
openmct.install(openmct.plugins.LocalStorage());
|
openmct.install(openmct.plugins.LocalStorage());
|
||||||
openmct.install(openmct.plugins.MyItems());
|
openmct.install(openmct.plugins.MyItems());
|
||||||
|
openmct.install(openmct.plugins.Timeline());
|
||||||
openmct.install(openmct.plugins.UTCTimeSystem());
|
openmct.install(openmct.plugins.UTCTimeSystem());
|
||||||
|
openmct.install(openmct.plugins.Clock({ enableClockIndicator: true }));
|
||||||
|
openmct.install(openmct.plugins.Timer());
|
||||||
|
openmct.install(openmct.plugins.Timelist());
|
||||||
openmct.time.clock('local', {start: -5 * 60 * 1000, end: 0});
|
openmct.time.clock('local', {start: -5 * 60 * 1000, end: 0});
|
||||||
openmct.time.timeSystem('utc');
|
openmct.time.timeSystem('utc');
|
||||||
openmct.install(openmct.plugins.Espresso());
|
openmct.install(openmct.plugins.Espresso());
|
||||||
|
|
||||||
|
openmct.install(
|
||||||
|
openmct.plugins.Conductor({
|
||||||
|
menuOptions: [
|
||||||
|
{
|
||||||
|
name: 'Fixed',
|
||||||
|
timeSystem: 'utc',
|
||||||
|
bounds: {
|
||||||
|
start: Date.now() - 30000000,
|
||||||
|
end: Date.now()
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Realtime',
|
||||||
|
timeSystem: 'utc',
|
||||||
|
clock: 'local',
|
||||||
|
clockOffsets: {
|
||||||
|
start: -30000000,
|
||||||
|
end: 30000
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (process.env.BASE_URL) {
|
if (process.env.BASE_URL) {
|
||||||
console.log("got a thing")
|
console.log("got a thing")
|
||||||
console.log(process.env.BASE_URL)
|
console.log(process.env.BASE_URL)
|
||||||
}
|
}
|
||||||
function GotelemPlugin() {
|
|
||||||
|
|
||||||
|
var schemaCached = null;
|
||||||
|
function getSchema() {
|
||||||
|
if (schemaCached === null) {
|
||||||
|
return fetch(`${process.env.BASE_URL}/api/v1/schema`).then((resp) => {
|
||||||
|
const res = resp.json()
|
||||||
|
console.log("got schema, caching", res);
|
||||||
|
schemaCached = res
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Promise.resolve(schemaCached)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const objectProvider = {
|
||||||
|
get: function (id) {
|
||||||
|
return getSchema().then((schema) => {
|
||||||
|
if (id.key === "car") {
|
||||||
|
const comp = schema.packets.map((x) => {
|
||||||
|
return {
|
||||||
|
key: x.name,
|
||||||
|
namespace: "umnsvp"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
identifier: id,
|
||||||
|
name: "the solar car",
|
||||||
|
type: 'folder',
|
||||||
|
location: 'ROOT',
|
||||||
|
composition: comp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var pkt = schema.packets.find((x) => x.name === id.key)
|
||||||
|
if (pkt) {
|
||||||
|
// if the key matches one of the packet names,
|
||||||
|
// we know it's a field.
|
||||||
|
const comp = pkt.data.map((field) => {
|
||||||
|
return {
|
||||||
|
// we have to do this since
|
||||||
|
// we can't get the packet name otherwise.
|
||||||
|
key: `${pkt.name}.${field.name}`,
|
||||||
|
namespace: "umnsvp"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
identifier: id,
|
||||||
|
name: pkt.name,
|
||||||
|
type: 'folder',
|
||||||
|
composition: comp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// at this point it's definitely a field aka umnsvp-datum
|
||||||
|
var [pktName, fieldName] = id.key.split('.')
|
||||||
|
return {
|
||||||
|
identifier: id,
|
||||||
|
name: fieldName,
|
||||||
|
type: 'umnsvp-datum',
|
||||||
|
telemetry: {
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
key: "value",
|
||||||
|
source: "val",
|
||||||
|
name: "Value",
|
||||||
|
"format": "float",
|
||||||
|
hints: {
|
||||||
|
range: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "utc",
|
||||||
|
source: "ts",
|
||||||
|
name: "Timestamp",
|
||||||
|
format: "utc",
|
||||||
|
hints: {
|
||||||
|
domain: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const TelemHistoryProvider = {
|
||||||
|
supportsRequest: function (dObj) {
|
||||||
|
return dObj.type === 'umnsvp-datum'
|
||||||
|
},
|
||||||
|
request: function (dObj, opt) {
|
||||||
|
var [pktName, fieldName] = dObj.identifier.key.split('.')
|
||||||
|
var url = `${process.env.BASE_URL}/api/v1/packets/${pktName}/${fieldName}?`
|
||||||
|
var params = new URLSearchParams({
|
||||||
|
start: new Date(opt.start).toISOString(),
|
||||||
|
end: new Date(opt.end).toISOString(),
|
||||||
|
})
|
||||||
|
return fetch(url + params).then((resp) => {
|
||||||
|
return resp.json()
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function GotelemPlugin() {
|
||||||
|
return function install(openmct) {
|
||||||
|
|
||||||
|
openmct.types.addType('umnsvp-datum', {
|
||||||
|
name: "UMN SVP Data Field",
|
||||||
|
description: "A data field of a packet from the car",
|
||||||
|
creatable: false,
|
||||||
|
cssClass: "icon-telemetry"
|
||||||
|
})
|
||||||
|
openmct.objects.addRoot({
|
||||||
|
namespace: "umnsvp",
|
||||||
|
key: 'car'
|
||||||
|
}, openmct.priority.HIGH)
|
||||||
|
openmct.objects.addProvider('umnsvp', objectProvider);
|
||||||
|
openmct.telemetry.addProvider(TelemHistoryProvider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openmct.install(GotelemPlugin())
|
||||||
|
|
||||||
openmct.start();
|
openmct.start();
|
||||||
|
|
Loading…
Reference in a new issue