From 8843cd9dab942462dbb8968d4a8e7e4a3e13d7d3 Mon Sep 17 00:00:00 2001 From: leandrofars Date: Wed, 1 Nov 2023 01:50:12 -0300 Subject: [PATCH] feat(api): dashboard and general info [ close #125 ] --- .../services/controller/internal/api/api.go | 11 +- .../services/controller/internal/api/info.go | 146 ++++++++++++++++++ .../services/controller/internal/api/mtp.go | 6 +- .../services/controller/internal/db/device.go | 15 +- .../services/controller/internal/db/info.go | 97 ++++++++++++ .../services/controller/internal/db/status.go | 4 +- .../services/controller/internal/mqtt/mqtt.go | 3 + 7 files changed, 270 insertions(+), 12 deletions(-) create mode 100644 backend/services/controller/internal/api/info.go create mode 100644 backend/services/controller/internal/db/info.go diff --git a/backend/services/controller/internal/api/api.go b/backend/services/controller/internal/api/api.go index 408e589..cbb8643 100644 --- a/backend/services/controller/internal/api/api.go +++ b/backend/services/controller/internal/api/api.go @@ -62,7 +62,12 @@ func StartApi(a Api) { iot.HandleFunc("/{sn}/fw_update", a.deviceFwUpdate).Methods("PUT") iot.HandleFunc("/{sn}/wifi", a.deviceWifi).Methods("PUT", "GET") mtp := r.PathPrefix("/api/mtp").Subrouter() - mtp.HandleFunc("/mqtt", a.MqttInfo).Methods("GET") + mtp.HandleFunc("", a.mtpInfo).Methods("GET") + dash := r.PathPrefix("/api/info").Subrouter() + dash.HandleFunc("/vendors", a.vendorsInfo).Methods("GET") + dash.HandleFunc("/status", a.statusInfo).Methods("GET") + dash.HandleFunc("/device_class", a.productClassInfo).Methods("GET") + dash.HandleFunc("/general", a.generalInfo).Methods("GET") users := r.PathPrefix("/api/users").Subrouter() users.HandleFunc("", a.retrieveUsers).Methods("GET") @@ -75,6 +80,10 @@ func StartApi(a Api) { return middleware.Middleware(handler) }) + dash.Use(func(handler http.Handler) http.Handler { + return middleware.Middleware(handler) + }) + users.Use(func(handler http.Handler) http.Handler { return middleware.Middleware(handler) }) diff --git a/backend/services/controller/internal/api/info.go b/backend/services/controller/internal/api/info.go new file mode 100644 index 0000000..2a3d3c8 --- /dev/null +++ b/backend/services/controller/internal/api/info.go @@ -0,0 +1,146 @@ +package api + +import ( + "encoding/json" + "log" + "net" + "net/http" + "time" + + "github.com/leandrofars/oktopus/internal/db" + "github.com/leandrofars/oktopus/internal/utils" +) + +type StatusCount struct { + Online int + Offline int +} + +type GeneralInfo struct { + MqttRtt time.Duration + ProductClassCount []db.ProductClassCount + StatusCount StatusCount + VendorsCount []db.VendorsCount +} + +func (a *Api) generalInfo(w http.ResponseWriter, r *http.Request) { + + var result GeneralInfo + + productclasscount, err := a.Db.RetrieveProductsClassInfo() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + vendorcount, err := a.Db.RetrieveVendorsInfo() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + statuscount, err := a.Db.RetrieveStatusInfo() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + for _, v := range statuscount { + switch v.Status { + case utils.Online: + result.StatusCount.Online = v.Count + case utils.Offline: + result.StatusCount.Offline = v.Count + } + } + + result.VendorsCount = vendorcount + result.ProductClassCount = productclasscount + + /* ------------ TODO: [mqtt rtt] create common function for this ------------ */ + //TODO: address with value from env or something like that + conn, err := net.Dial("tcp", "127.0.0.1:1883") + if err != nil { + json.NewEncoder(w).Encode("Error to connect to broker") + w.WriteHeader(http.StatusInternalServerError) + return + } + defer conn.Close() + + info, err := tcpInfo(conn.(*net.TCPConn)) + if err != nil { + json.NewEncoder(w).Encode("Error to get TCP socket info") + w.WriteHeader(http.StatusInternalServerError) + return + } + rtt := time.Duration(info.Rtt) * time.Microsecond + /* -------------------------------------------------------------------------- */ + + result.MqttRtt = rtt / 1000 + + err = json.NewEncoder(w).Encode(result) + if err != nil { + log.Println(err) + } + + return +} + +func (a *Api) vendorsInfo(w http.ResponseWriter, r *http.Request) { + vendors, err := a.Db.RetrieveVendorsInfo() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + err = json.NewEncoder(w).Encode(vendors) + if err != nil { + log.Println(err) + } + + return +} + +func (a *Api) productClassInfo(w http.ResponseWriter, r *http.Request) { + vendors, err := a.Db.RetrieveProductsClassInfo() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + err = json.NewEncoder(w).Encode(vendors) + if err != nil { + log.Println(err) + } + + return +} + +func (a *Api) statusInfo(w http.ResponseWriter, r *http.Request) { + vendors, err := a.Db.RetrieveStatusInfo() + if err != nil { + log.Println(err) + w.WriteHeader(http.StatusInternalServerError) + return + } + + var status StatusCount + for _, v := range vendors { + switch v.Status { + case utils.Online: + status.Online = v.Count + case utils.Offline: + status.Offline = v.Count + } + } + + err = json.NewEncoder(w).Encode(status) + if err != nil { + log.Println(err) + } + + return +} diff --git a/backend/services/controller/internal/api/mtp.go b/backend/services/controller/internal/api/mtp.go index 10c4ca6..ae97c53 100644 --- a/backend/services/controller/internal/api/mtp.go +++ b/backend/services/controller/internal/api/mtp.go @@ -10,10 +10,10 @@ import ( ) type mqttInfo struct { - Rtt time.Duration + MqttRtt time.Duration } -func (a *Api) MqttInfo(w http.ResponseWriter, r *http.Request) { +func (a *Api) mtpInfo(w http.ResponseWriter, r *http.Request) { //TODO: address with value from env or something like that conn, err := net.Dial("tcp", "127.0.0.1:1883") if err != nil { @@ -31,7 +31,7 @@ func (a *Api) MqttInfo(w http.ResponseWriter, r *http.Request) { } rtt := time.Duration(info.Rtt) * time.Microsecond json.NewEncoder(w).Encode(mqttInfo{ - Rtt: rtt / 1000, + MqttRtt: rtt / 1000, }) } diff --git a/backend/services/controller/internal/db/device.go b/backend/services/controller/internal/db/device.go index ee9b92b..0e7f9b2 100644 --- a/backend/services/controller/internal/db/device.go +++ b/backend/services/controller/internal/db/device.go @@ -18,13 +18,14 @@ const ( ) type Device struct { - SN string - Model string - Customer string - Vendor string - Version string - Status uint8 - MTP []map[string]string + SN string + Model string + Customer string + Vendor string + Version string + ProductClass string + Status uint8 + MTP []map[string]string } func (d *Database) CreateDevice(device Device) error { diff --git a/backend/services/controller/internal/db/info.go b/backend/services/controller/internal/db/info.go new file mode 100644 index 0000000..0ddb91f --- /dev/null +++ b/backend/services/controller/internal/db/info.go @@ -0,0 +1,97 @@ +package db + +import ( + "log" + + "go.mongodb.org/mongo-driver/bson" +) + +type VendorsCount struct { + Vendor string `bson:"_id" json:"vendor"` + Count int `bson:"count" json:"count"` +} + +type ProductClassCount struct { + ProductClass string `bson:"_id" json:"productClass"` + Count int `bson:"count" json:"count"` +} + +type StatusCount struct { + Status int `bson:"_id" json:"status"` + Count int `bson:"count" json:"count"` +} + +func (d *Database) RetrieveVendorsInfo() ([]VendorsCount, error) { + var results []VendorsCount + cursor, err := d.devices.Aggregate(d.ctx, []bson.M{ + { + "$group": bson.M{ + "_id": "$vendor", + "count": bson.M{"$sum": 1}, + }, + }, + }) + if err != nil { + log.Println(err) + return nil, err + } + defer cursor.Close(d.ctx) + if err := cursor.All(d.ctx, &results); err != nil { + log.Println(err) + return nil, err + } + for _, result := range results { + log.Println(result) + } + return results, nil +} + +func (d *Database) RetrieveStatusInfo() ([]StatusCount, error) { + var results []StatusCount + cursor, err := d.devices.Aggregate(d.ctx, []bson.M{ + { + "$group": bson.M{ + "_id": "$status", + "count": bson.M{"$sum": 1}, + }, + }, + }) + if err != nil { + log.Println(err) + return nil, err + } + defer cursor.Close(d.ctx) + if err := cursor.All(d.ctx, &results); err != nil { + log.Println(err) + return nil, err + } + for _, result := range results { + log.Println(result) + } + return results, nil +} + +func (d *Database) RetrieveProductsClassInfo() ([]ProductClassCount, error) { + var results []ProductClassCount + cursor, err := d.devices.Aggregate(d.ctx, []bson.M{ + { + "$group": bson.M{ + "_id": "$productclass", + "count": bson.M{"$sum": 1}, + }, + }, + }) + if err != nil { + log.Println(err) + return nil, err + } + defer cursor.Close(d.ctx) + if err := cursor.All(d.ctx, &results); err != nil { + log.Println(err) + return nil, err + } + for _, result := range results { + log.Println(result) + } + return results, nil +} diff --git a/backend/services/controller/internal/db/status.go b/backend/services/controller/internal/db/status.go index 517690d..72a1142 100644 --- a/backend/services/controller/internal/db/status.go +++ b/backend/services/controller/internal/db/status.go @@ -1,11 +1,13 @@ package db import ( + "log" + "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" - "log" ) +// TODO: fix this function to also change device status at different mtp func (d *Database) UpdateStatus(sn string, status uint8) error { var result bson.M err := d.devices.FindOneAndUpdate(d.ctx, bson.D{{"sn", sn}}, bson.D{{"$set", bson.D{{"status", status}}}}).Decode(&result) diff --git a/backend/services/controller/internal/mqtt/mqtt.go b/backend/services/controller/internal/mqtt/mqtt.go index 2321afe..861b908 100644 --- a/backend/services/controller/internal/mqtt/mqtt.go +++ b/backend/services/controller/internal/mqtt/mqtt.go @@ -247,6 +247,7 @@ func (m *Mqtt) handleNewDevice(deviceMac string) { "Device.DeviceInfo.ModelName", "Device.DeviceInfo.SoftwareVersion", "Device.DeviceInfo.SerialNumber", + "Device.DeviceInfo.ProductClass", }, MaxDepth: 1, }, @@ -284,6 +285,7 @@ func (m *Mqtt) handleNewDevicesResponse(p []byte, sn string) { device.Vendor = msg.ReqPathResults[0].ResolvedPathResults[0].ResultParams["Manufacturer"] device.Model = msg.ReqPathResults[1].ResolvedPathResults[0].ResultParams["ModelName"] device.Version = msg.ReqPathResults[2].ResolvedPathResults[0].ResultParams["SoftwareVersion"] + device.ProductClass = msg.ReqPathResults[4].ResolvedPathResults[0].ResultParams["ProductClass"] device.SN = sn mtp := map[string]string{ @@ -291,6 +293,7 @@ func (m *Mqtt) handleNewDevicesResponse(p []byte, sn string) { } device.MTP = append(device.MTP, mtp) + device.Status = utils.Online err = m.DB.CreateDevice(device) if err != nil {