Initial commit
This commit is contained in:
commit
6bc66975db
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
api
|
||||||
|
log
|
||||||
|
**/model/tables.go
|
21
app/db.go
Normal file
21
app/db.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
|
||||||
|
"git.ultraware.nl/NiseVoid/qb/driver/autoqb"
|
||||||
|
"git.ultraware.nl/NiseVoid/qb/qbdb"
|
||||||
|
_ "github.com/lib/pq" // PostgreSQL driver
|
||||||
|
)
|
||||||
|
|
||||||
|
var db *qbdb.DB
|
||||||
|
|
||||||
|
// InitDB initializes the database connection
|
||||||
|
func InitDB() {
|
||||||
|
d, err := sql.Open(`postgres`, `host=/tmp dbname=power_quality`)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db = autoqb.New(d)
|
||||||
|
}
|
175
app/internal/model/db.json
Normal file
175
app/internal/model/db.json
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "public.measurement",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"name": "c_gem_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "c_gem_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "c_gem_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ep_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ep_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ep_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "i_gem_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "i_gem_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "i_gem_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "i_max_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "i_max_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "i_max_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "p_gem_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "p_gem_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "p_gem_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "p_max_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "p_max_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "p_max_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s_gem_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s_gem_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s_gem_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s_max_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s_max_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s_max_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "time",
|
||||||
|
"type": "timestamp with time zone",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "u_gem_1",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "u_gem_2",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "u_gem_3",
|
||||||
|
"type": "double precision",
|
||||||
|
"null": false,
|
||||||
|
"size": 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
3
app/internal/model/model.go
Normal file
3
app/internal/model/model.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
//go:generate qb-generator db.json tables.go
|
71
app/stats.go
Normal file
71
app/stats.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.fuyu.moe/5GPowerQuality/api/app/internal/model"
|
||||||
|
"git.ultraware.nl/NiseVoid/qb"
|
||||||
|
"git.ultraware.nl/NiseVoid/qb/qc"
|
||||||
|
"git.ultraware.nl/NiseVoid/qb/qf"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Count returns the number of measurements
|
||||||
|
func Count() int {
|
||||||
|
m := model.Measurement()
|
||||||
|
|
||||||
|
q := m.Select(qf.CountAll())
|
||||||
|
|
||||||
|
var count *int
|
||||||
|
err := db.QueryRow(q).Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if count == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return *count
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAttribute returns the specified attribute for all phases since the specified date
|
||||||
|
func GetAttribute(attr string, date time.Time) (data []Phases) {
|
||||||
|
m := model.Measurement()
|
||||||
|
|
||||||
|
f := reflect.ValueOf(m).Elem().FieldByName(attr + `1`)
|
||||||
|
if !f.IsValid() || f.Type().Name() != `Field` {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
f1 := f.Interface().(*qb.TableField)
|
||||||
|
f2 := reflect.ValueOf(m).Elem().FieldByName(attr + `2`).Interface().(*qb.TableField)
|
||||||
|
f3 := reflect.ValueOf(m).Elem().FieldByName(attr + `3`).Interface().(*qb.TableField)
|
||||||
|
|
||||||
|
q := m.Select(m.Time, f1, f2, f3).
|
||||||
|
OrderBy(qb.Desc(m.Time)).
|
||||||
|
Where(qc.Gte(m.Time, date))
|
||||||
|
|
||||||
|
rows, err := db.Query(q)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var phases Phases
|
||||||
|
err := rows.Scan(&phases.Time, &phases.P1, &phases.P2, &phases.P3)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data = append(data, phases)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phases contains data for all phases
|
||||||
|
type Phases struct {
|
||||||
|
Time time.Time `json:"time"`
|
||||||
|
P1 float64 `json:"phase_1"`
|
||||||
|
P2 float64 `json:"phase_2"`
|
||||||
|
P3 float64 `json:"phase_3"`
|
||||||
|
}
|
19
server/logger.go
Normal file
19
server/logger.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"git.fuyu.moe/Fuyu/flog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func catchSignals() {
|
||||||
|
sc := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sc, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
|
||||||
|
go func() {
|
||||||
|
s := <-sc
|
||||||
|
flog.Info(`Stopping, signal: `, s)
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
}
|
12
server/logger_debug.go
Normal file
12
server/logger_debug.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// +build cgo
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.fuyu.moe/Fuyu/flog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setLogger() {
|
||||||
|
flog.MinLevel = flog.LevelDebug
|
||||||
|
flog.MinStackLevel = flog.LevelWarning
|
||||||
|
}
|
20
server/logger_release.go
Normal file
20
server/logger_release.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// +build !cgo
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.fuyu.moe/Fuyu/flog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func setLogger() {
|
||||||
|
f, err := os.OpenFile(`log`, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
flog.Output = f
|
||||||
|
flog.MinLevel = flog.LevelInfo
|
||||||
|
flog.MinStackLevel = flog.LevelWarning
|
||||||
|
}
|
63
server/main.go
Normal file
63
server/main.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.fuyu.moe/5GPowerQuality/api/app"
|
||||||
|
"git.fuyu.moe/Fuyu/flog"
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
defer func() {
|
||||||
|
v := recover()
|
||||||
|
if v != nil {
|
||||||
|
flog.Critical(`panic: `, v)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
setLogger()
|
||||||
|
|
||||||
|
flog.Info(`Starting`)
|
||||||
|
catchSignals()
|
||||||
|
|
||||||
|
app.InitDB()
|
||||||
|
|
||||||
|
e := echo.New()
|
||||||
|
e.HideBanner = true
|
||||||
|
e.Use(recoverMiddleware)
|
||||||
|
|
||||||
|
e.GET(`/count`, count)
|
||||||
|
e.GET(`/:attr/:date`, pmax)
|
||||||
|
|
||||||
|
panic(e.Start(`localhost:33333`))
|
||||||
|
}
|
||||||
|
|
||||||
|
func count(c echo.Context) error {
|
||||||
|
return c.JSON(200, app.Count())
|
||||||
|
}
|
||||||
|
|
||||||
|
func pmax(c echo.Context) error {
|
||||||
|
attr := c.Param(`attr`)
|
||||||
|
if len(attr) < 2 {
|
||||||
|
return c.NoContent(400)
|
||||||
|
}
|
||||||
|
if len(attr) == 2 {
|
||||||
|
attr = strings.ToUpper(attr[:2])
|
||||||
|
} else {
|
||||||
|
attr = strings.ToUpper(attr[:2]) + strings.ToLower(attr[2:])
|
||||||
|
}
|
||||||
|
|
||||||
|
date, err := time.Parse(time.RFC3339, c.Param(`date`))
|
||||||
|
if err != nil {
|
||||||
|
return c.NoContent(400)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := app.GetAttribute(attr, date)
|
||||||
|
if data == nil {
|
||||||
|
return c.NoContent(404)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(200, data)
|
||||||
|
}
|
20
server/panic.go
Normal file
20
server/panic.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.fuyu.moe/Fuyu/flog"
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func recoverMiddleware(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
defer func() {
|
||||||
|
v := recover()
|
||||||
|
if v != nil {
|
||||||
|
flog.Critical(`panic: `, v)
|
||||||
|
_ = c.JSON(500, `Fatal error occurred`)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user