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