initial version of http/rest API
This commit is contained in:
parent
8f996ffee1
commit
6827006f94
304
api/http/config.go
Normal file
304
api/http/config.go
Normal file
@ -0,0 +1,304 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
ccc "th7/config"
|
||||
"th7/data/config"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrConfigChannelNotFound = errors.New("a configured channel with this ID was not found")
|
||||
ErrConfigRestrictedDB = errors.New("access to the DB map is restricted")
|
||||
)
|
||||
|
||||
func (g *GinAdapter) getConfigByID(id int) (config.Channel, error) {
|
||||
for channel := range g.cfg.Channels {
|
||||
if g.cfg.Channels[channel].Id == id {
|
||||
return g.cfg.Channels[channel], nil
|
||||
}
|
||||
}
|
||||
|
||||
return config.Channel{}, ErrConfigChannelNotFound
|
||||
}
|
||||
|
||||
// check if a supplied key string is in the device struct (TH7)
|
||||
func configDeviceIsValidKey(key string) bool {
|
||||
deviceKeys := []string{
|
||||
"debug", "nolog", "noweb", "led",
|
||||
}
|
||||
|
||||
key = strings.ToLower(key)
|
||||
|
||||
for k := range deviceKeys {
|
||||
if key == deviceKeys[k] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func configDeviceIsValidValue(key string, value string) bool {
|
||||
// all the values are bools for now
|
||||
value = strings.ToLower(value)
|
||||
return (value == "true" || value == "false")
|
||||
}
|
||||
|
||||
// this doesn't actually do anything to program state... yet
|
||||
func (g *GinAdapter) updateConfigDevice(key string, value string) error {
|
||||
bValue, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key = strings.ToLower(key)
|
||||
|
||||
// might have to sync these writes ?
|
||||
// go doesn't have switch case fall-thru
|
||||
switch key {
|
||||
case "led":
|
||||
g.cfg.Board.Led = bValue
|
||||
|
||||
case "nolog":
|
||||
g.cfg.Board.NoLog = bValue
|
||||
|
||||
case "noweb":
|
||||
g.cfg.Board.NoWeb = bValue
|
||||
|
||||
case "debug":
|
||||
g.cfg.Board.Debug = bValue
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GinAdapter) validConfigChannel(id int) bool {
|
||||
|
||||
for c := range g.cfg.Channels {
|
||||
if g.cfg.Channels[c].Id == id {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func configChannelIsValidKey(key string) bool {
|
||||
// will add more later
|
||||
channelKeys := []string{
|
||||
"thermocouple", "gain", "offset",
|
||||
}
|
||||
|
||||
key = strings.ToLower(key)
|
||||
|
||||
for k := range channelKeys {
|
||||
if key == channelKeys[k] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (g *GinAdapter) configChannelUpdateValue(id int, key string, value string) error {
|
||||
|
||||
key = strings.ToLower(key)
|
||||
value = strings.ToLower(value)
|
||||
var cfgChannelPtr *config.Channel
|
||||
foundChannel := false
|
||||
|
||||
// find the channel, given and checked by id
|
||||
for k := range g.cfg.Channels {
|
||||
if g.cfg.Channels[k].Id == id {
|
||||
cfgChannelPtr = &(g.cfg.Channels[k])
|
||||
foundChannel = true
|
||||
}
|
||||
}
|
||||
|
||||
if !foundChannel {
|
||||
return ErrConfigChannelNotFound
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "thermocouple":
|
||||
tc, err := ccc.GetThermocoupleType(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgChannelPtr.Thermo = tc
|
||||
case "gain":
|
||||
fValue, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgChannelPtr.Gain = fValue
|
||||
case "offset":
|
||||
fValue, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfgChannelPtr.Offset = fValue
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/*************************************************************************/
|
||||
/*************************************************************************/
|
||||
|
||||
func (g *GinAdapter) GetConfigChannelByIdHandler(c *gin.Context) {
|
||||
var timestamp string
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := g.getConfigByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
timestamp = g.getTimestamp()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"channel": channel,
|
||||
"time": timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) GetConfigChannelsHandler(c *gin.Context) {
|
||||
timestamp := g.getTimestamp()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"channels": g.cfg.Channels,
|
||||
"time": timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) GetConfigDeviceHandler(c *gin.Context) {
|
||||
timestamp := g.getTimestamp()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"channels": g.cfg.Board,
|
||||
"time": timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) GetConfigDBHandler(c *gin.Context) {
|
||||
var timestamp string
|
||||
|
||||
if g.cfg.API[0].Restricted {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"error": ErrConfigRestrictedDB,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
timestamp = g.getTimestamp()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
// "db": g.getConfigDB(),
|
||||
"db": g.cfg.DB,
|
||||
"time": timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) SetConfigDeviceHandler(c *gin.Context) {
|
||||
var req PostRequest
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
validKey := configDeviceIsValidKey(req.Key)
|
||||
if !validKey {
|
||||
c.String(http.StatusBadRequest, "invalid device config key")
|
||||
return
|
||||
}
|
||||
|
||||
validValue := configDeviceIsValidValue(req.Key, req.Value)
|
||||
if !validValue {
|
||||
c.String(http.StatusBadRequest, "invalid device config value")
|
||||
return
|
||||
}
|
||||
|
||||
err := g.updateConfigDevice(req.Key, req.Value)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, "OK")
|
||||
}
|
||||
|
||||
// this function can only modify already set up channels, for now.
|
||||
func (g *GinAdapter) SetConfigChannelByIdHandler(c *gin.Context) {
|
||||
|
||||
var req PostRequest
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
validChannel := g.validConfigChannel(id)
|
||||
if !validChannel {
|
||||
c.String(http.StatusBadRequest, "channel not initially configured. TODO fix")
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
validKey := configChannelIsValidKey(req.Key)
|
||||
if !validKey {
|
||||
c.String(http.StatusBadRequest, "invalid channel config key")
|
||||
return
|
||||
}
|
||||
|
||||
err = g.configChannelUpdateValue(id, req.Key, req.Value)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, "OK")
|
||||
}
|
||||
|
||||
func (g *GinAdapter) SetConfigDBHandler(c *gin.Context) {
|
||||
|
||||
var req PostRequest
|
||||
|
||||
if g.cfg.API[0].Restricted {
|
||||
c.String(http.StatusForbidden, ErrConfigRestrictedDB.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// what could go wrong?
|
||||
g.cfg.DB[req.Key] = req.Value
|
||||
|
||||
c.String(http.StatusOK, "OK")
|
||||
}
|
85
api/http/data.go
Normal file
85
api/http/data.go
Normal file
@ -0,0 +1,85 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"th7/data/core"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// helper functions
|
||||
|
||||
func (g *GinAdapter) getRatio() core.Ratio {
|
||||
return g.corePort.GetRatio()
|
||||
}
|
||||
|
||||
func (g *GinAdapter) getChannels() []core.Channel {
|
||||
return g.corePort.GetChannels()
|
||||
}
|
||||
|
||||
func (g *GinAdapter) getChannelByID(id int) (core.Channel, error) {
|
||||
return g.corePort.GetChannel(id)
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/*************************************************************************/
|
||||
/*************************************************************************/
|
||||
|
||||
func (g *GinAdapter) GetChannelByIDHandler(c *gin.Context) {
|
||||
var timestamp string
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := g.getChannelByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
timestamp = g.getTimestamp()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"channel": channel,
|
||||
"time": timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) GetChannelsHandler(c *gin.Context) {
|
||||
channels := g.getChannels()
|
||||
timestamp := g.getTimestamp()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"channels": channels,
|
||||
"time": timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) GetRatioHandler(c *gin.Context) {
|
||||
ratio := g.getRatio()
|
||||
timestamp := g.getTimestamp()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"ratio": ratio,
|
||||
"time": timestamp,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) GetDataHandler(c *gin.Context) {
|
||||
timestamp := g.getTimestamp()
|
||||
ratio := g.getRatio()
|
||||
channels := g.getChannels()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"channels": channels,
|
||||
"ratio": ratio,
|
||||
"time": timestamp,
|
||||
})
|
||||
}
|
64
api/http/gin.go
Normal file
64
api/http/gin.go
Normal file
@ -0,0 +1,64 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"th7/data/config"
|
||||
"th7/ports"
|
||||
)
|
||||
|
||||
type GinAdapter struct {
|
||||
router *gin.Engine
|
||||
corePort ports.CorePort
|
||||
cfg config.Config
|
||||
port int
|
||||
}
|
||||
|
||||
func (g *GinAdapter) IndexHandler(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "page_index", gin.H{
|
||||
"title": "TH7",
|
||||
})
|
||||
}
|
||||
|
||||
func NewGinAdapter(corePort ports.CorePort, cfg config.Config) *GinAdapter {
|
||||
var adapter GinAdapter
|
||||
|
||||
adapter.corePort = corePort
|
||||
adapter.port = cfg.API[0].Port
|
||||
adapter.cfg = cfg
|
||||
|
||||
//gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
adapter.router = gin.New()
|
||||
|
||||
// API
|
||||
adapter.router.GET("/data/channel/:id", adapter.GetChannelByIDHandler)
|
||||
adapter.router.GET("/data/channels", adapter.GetChannelsHandler)
|
||||
adapter.router.GET("/data/ratio", adapter.GetRatioHandler)
|
||||
adapter.router.GET("/data", adapter.GetDataHandler)
|
||||
|
||||
adapter.router.GET("/config/channel/:id", adapter.GetConfigChannelByIdHandler)
|
||||
adapter.router.GET("/config/channels", adapter.GetConfigChannelsHandler)
|
||||
adapter.router.GET("/config/device", adapter.GetConfigDeviceHandler)
|
||||
adapter.router.GET("/config/db", adapter.GetConfigDBHandler)
|
||||
|
||||
adapter.router.POST("/config/device", adapter.SetConfigDeviceHandler)
|
||||
adapter.router.POST("/config/channel/:id", adapter.SetConfigChannelByIdHandler)
|
||||
adapter.router.POST("/config/db", adapter.SetConfigDBHandler)
|
||||
|
||||
//adapter.router.GET("/time", adapter.TimestampHandler)
|
||||
|
||||
adapter.router.LoadHTMLGlob("./templates/*.tmpl")
|
||||
adapter.router.Static("/assets", "./static")
|
||||
adapter.router.GET("/", adapter.IndexHandler)
|
||||
|
||||
return &adapter
|
||||
}
|
||||
|
||||
func (g *GinAdapter) Run() {
|
||||
portStr := fmt.Sprintf(":%d", g.port)
|
||||
g.router.Run(portStr)
|
||||
}
|
12
api/http/util.go
Normal file
12
api/http/util.go
Normal file
@ -0,0 +1,12 @@
|
||||
package http
|
||||
|
||||
import "time"
|
||||
|
||||
type PostRequest struct {
|
||||
Key string `form:"key" binding:"required"`
|
||||
Value string `form:"value" binding:"required"`
|
||||
}
|
||||
|
||||
func (g *GinAdapter) getTimestamp() string {
|
||||
return time.Now().Format(time.RFC1123)
|
||||
}
|
10
config.toml
10
config.toml
@ -1,13 +1,17 @@
|
||||
[TH7]
|
||||
port = 8080
|
||||
debug = true
|
||||
nolog = true
|
||||
noweb = true
|
||||
noweb = false
|
||||
|
||||
[API]
|
||||
HTTP.enabled = true
|
||||
HTTP.port = 8080
|
||||
HTTP.restricted = false
|
||||
|
||||
[DB]
|
||||
type = "sqlite3"
|
||||
path = "test.db"
|
||||
freq = 60
|
||||
freq = 300
|
||||
|
||||
[Channel_1]
|
||||
type = 'U'
|
||||
|
@ -28,7 +28,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func getThermocoupleType(t string) (thermocouple.Type, error) {
|
||||
func GetThermocoupleType(t string) (thermocouple.Type, error) {
|
||||
t = strings.ToUpper(t)
|
||||
if val, ok := thermocoupleTypes[t]; ok {
|
||||
return val, nil
|
||||
@ -53,15 +53,20 @@ func Load() (config.Config, error) {
|
||||
|
||||
SetDefaultConfig(v)
|
||||
|
||||
cfg.Board.Port = v.GetInt("TH7.port")
|
||||
cfg.Board.Cache = v.GetBool("TH7.cache")
|
||||
cfg.Board.Led = v.GetBool("TH7.LED")
|
||||
cfg.Board.Logfreq = v.GetInt("DB.freq")
|
||||
cfg.Board.Debug = v.GetBool("TH7.debug")
|
||||
cfg.Board.NoLogging = v.GetBool("TH7.nolog")
|
||||
cfg.Board.NoLog = v.GetBool("TH7.nolog")
|
||||
cfg.Board.NoWeb = v.GetBool("TH7.noweb")
|
||||
|
||||
cfg.Channels = make([]config.Channel, 0)
|
||||
cfg.API = make([]config.API, 0)
|
||||
|
||||
// just REST API for now ..
|
||||
cfg.API = append(cfg.API, config.API{
|
||||
Port: v.GetInt("API.HTTP.port"),
|
||||
Enabled: v.GetBool("API.HTTP.enabled"),
|
||||
Restricted: v.GetBool("API.HTTP.restricted"),
|
||||
})
|
||||
|
||||
for i := 1; i < 8; i++ {
|
||||
var c config.Channel
|
||||
@ -76,7 +81,7 @@ func Load() (config.Config, error) {
|
||||
c.Gain = v.GetFloat64(head + ".gain")
|
||||
c.Offset = v.GetFloat64(head + ".offset")
|
||||
|
||||
tc, err := getThermocoupleType(v.GetString(head + ".type"))
|
||||
tc, err := GetThermocoupleType(v.GetString(head + ".type"))
|
||||
if err != nil {
|
||||
fmt.Printf("%s.type=%s\n", head, v.GetString(head+".type"))
|
||||
return cfg, err
|
||||
|
@ -7,13 +7,15 @@ import (
|
||||
)
|
||||
|
||||
func SetDefaultConfig(v *viper.Viper) {
|
||||
v.SetDefault("TH7.port", 8080)
|
||||
v.SetDefault("TH7.cache", true)
|
||||
v.SetDefault("TH7.LED", true)
|
||||
v.SetDefault("TH7.debug", false)
|
||||
v.SetDefault("TH7.nolog", false)
|
||||
v.SetDefault("TH7.noweb", false)
|
||||
v.SetDefault("DB.freq", 600)
|
||||
|
||||
// set defaults for REST API
|
||||
v.SetDefault("API.HTTP.enabled", false)
|
||||
v.SetDefault("API.HTTP.restricted", true)
|
||||
v.SetDefault("API.HTTP.port", 8080)
|
||||
|
||||
// set defaults for channels
|
||||
for i := 1; i < 8; i++ {
|
||||
|
@ -4,6 +4,12 @@ import (
|
||||
"th7/data/thermocouple"
|
||||
)
|
||||
|
||||
type API struct {
|
||||
Port int `json:"port"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Restricted bool `json:"restricted"` // can read/modify DB settings?
|
||||
}
|
||||
|
||||
type Filter struct {
|
||||
SampleSize int `json:"sample_size"`
|
||||
Type int `json:"type"`
|
||||
@ -19,17 +25,15 @@ type Channel struct {
|
||||
}
|
||||
|
||||
type TH7 struct {
|
||||
Port int `json:"port"`
|
||||
Cache bool `json:"cache"`
|
||||
Led bool `json:"LED"`
|
||||
Logfreq int `json:"logfreq"`
|
||||
Debug bool `json:"debug"`
|
||||
NoLogging bool `json:"nologging"`
|
||||
NoWeb bool `json:"noweb"`
|
||||
Led bool `json:"LED"`
|
||||
Debug bool `json:"debug"`
|
||||
NoLog bool `json:"nolog"`
|
||||
NoWeb bool `json:"noweb"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Board TH7 `json:"TH7"`
|
||||
Channels []Channel `json:"channels"`
|
||||
DB map[string]interface{} `json:"db"`
|
||||
API []API `json:"API"`
|
||||
}
|
||||
|
@ -8,18 +8,25 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DB_LOGGER_DEFAULT_DUR = 300
|
||||
)
|
||||
|
||||
func NewAdapter(corePort ports.CorePort, cfg config.Config) (ports.DBPort, error) {
|
||||
|
||||
var duration time.Duration
|
||||
var no_logging bool
|
||||
var adapter ports.DBPort
|
||||
var err error
|
||||
|
||||
no_logging = cfg.Board.NoLogging
|
||||
duration = time.Duration(cfg.Board.Logfreq) * time.Second
|
||||
// if `freq' is not present in the DB map, use default of 5 minutes (300 sec)
|
||||
if _, ok := cfg.DB["freq"]; !ok {
|
||||
duration = time.Duration(DB_LOGGER_DEFAULT_DUR) * time.Second
|
||||
} else {
|
||||
duration = time.Duration(cfg.DB["freq"].(int64)) * time.Second
|
||||
}
|
||||
|
||||
// if no DB is specified, or nolog=true then use a dummy db adapter
|
||||
if _, ok := cfg.DB["type"]; !ok || no_logging {
|
||||
if _, ok := cfg.DB["type"]; !ok || cfg.Board.NoLog {
|
||||
adapter, _ = NewDummyAdapter(cfg)
|
||||
go startLoggingProcess(adapter, corePort, duration)
|
||||
return adapter, nil
|
||||
|
@ -8,6 +8,8 @@ import (
|
||||
|
||||
func startLoggingProcess(db ports.DBPort, core ports.CorePort, dur time.Duration) {
|
||||
|
||||
log.Printf("[DB] logging frequency=%v\n", dur)
|
||||
|
||||
// wait 15 seconds to not log start up values that can be noisy
|
||||
time.Sleep(15 * time.Second)
|
||||
|
||||
|
16
main.go
16
main.go
@ -6,12 +6,12 @@ import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"th7/api/http"
|
||||
"th7/config"
|
||||
"th7/core"
|
||||
"th7/db"
|
||||
"th7/pcb"
|
||||
"th7/ports"
|
||||
"th7/web"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
@ -22,7 +22,7 @@ func main() {
|
||||
var pcbPort ports.PCBPort
|
||||
var corePort ports.CorePort
|
||||
var dbPort ports.DBPort
|
||||
var webPort ports.WebPort
|
||||
var apiPort ports.APIPort
|
||||
var err error
|
||||
|
||||
kill := make(chan os.Signal, 1)
|
||||
@ -55,21 +55,23 @@ func main() {
|
||||
defer dbPort.Close()
|
||||
|
||||
// if noweb is false, then start web service
|
||||
if !cfg.Board.NoWeb {
|
||||
webPort = web.NewGinAdapter(corePort, cfg)
|
||||
go webPort.Run()
|
||||
if !cfg.Board.NoWeb && cfg.API[0].Enabled {
|
||||
apiPort = http.NewGinAdapter(corePort, cfg)
|
||||
go apiPort.Run()
|
||||
}
|
||||
|
||||
color.Set(color.FgHiRed)
|
||||
fmt.Printf("Started on: %s\n", time.Now().Format(time.DateTime))
|
||||
color.Unset()
|
||||
|
||||
if !cfg.Board.NoWeb {
|
||||
if !cfg.Board.NoWeb && cfg.API[0].Enabled {
|
||||
color.Set(color.FgHiGreen)
|
||||
fmt.Printf("TH7 web view is live on http://localhost:%d/\n", cfg.Board.Port)
|
||||
fmt.Printf("TH7 API is live on http://localhost:%d/\n", cfg.API[0].Port)
|
||||
color.Unset()
|
||||
}
|
||||
|
||||
fmt.Println(cfg.API[0])
|
||||
|
||||
sig := <-kill
|
||||
log.Printf("Caught signal %v", sig)
|
||||
}
|
||||
|
5
ports/api.go
Normal file
5
ports/api.go
Normal file
@ -0,0 +1,5 @@
|
||||
package ports
|
||||
|
||||
type APIPort interface {
|
||||
Run()
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package ports
|
||||
|
||||
type WebPort interface {
|
||||
Run()
|
||||
}
|
@ -36,7 +36,7 @@ GET /data/ratio -> {
|
||||
}
|
||||
|
||||
# returns all data fields that a user may want to log/access
|
||||
GET /data/all -> {
|
||||
GET /data -> {
|
||||
|
||||
return {
|
||||
/data/channels without timestamp
|
||||
@ -97,8 +97,8 @@ GET /config/db -> {
|
||||
========== write configuration info ==========
|
||||
|
||||
=== The POST request payload should be of the form:
|
||||
=== { key: value } and several options can be specified at once like such:
|
||||
=== [{ key1: value }, { key2: value}, {key3: value }]
|
||||
=== { key: value }
|
||||
=== $ curl -d "key=testkey&value=testvalue" -X POST "http://localhost:8080/config/device"
|
||||
|
||||
# write device-specific key:value pair(s) to the device config
|
||||
# returns success or error
|
||||
|
129
web/gin.go
129
web/gin.go
@ -1,129 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"th7/data/config"
|
||||
"th7/data/core"
|
||||
"th7/ports"
|
||||
)
|
||||
|
||||
type GinAdapter struct {
|
||||
router *gin.Engine
|
||||
corePort ports.CorePort
|
||||
cfg config.Config
|
||||
port int
|
||||
}
|
||||
|
||||
func (g *GinAdapter) getRatio() core.Ratio {
|
||||
return g.corePort.GetRatio()
|
||||
}
|
||||
|
||||
func (g *GinAdapter) RatioHandler(c *gin.Context) {
|
||||
ratio := g.getRatio()
|
||||
c.JSON(http.StatusOK, ratio)
|
||||
}
|
||||
|
||||
func (g *GinAdapter) getChannels() []core.Channel {
|
||||
return g.corePort.GetChannels()
|
||||
}
|
||||
|
||||
func (g *GinAdapter) ChannelsHandler(c *gin.Context) {
|
||||
channels := g.getChannels()
|
||||
c.JSON(http.StatusOK, channels)
|
||||
}
|
||||
|
||||
func (g *GinAdapter) getChannelByID(id int) (core.Channel, error) {
|
||||
return g.corePort.GetChannel(id)
|
||||
}
|
||||
|
||||
func (g *GinAdapter) ChannelByIDHandler(c *gin.Context) {
|
||||
id, err := strconv.Atoi(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
channel, err := g.getChannelByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": err,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, channel)
|
||||
}
|
||||
|
||||
func (g *GinAdapter) getTimestamp() string {
|
||||
return time.Now().Format(time.RFC1123)
|
||||
}
|
||||
|
||||
func (g *GinAdapter) TimestampHandler(c *gin.Context) {
|
||||
ts := g.getTimestamp()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"time": ts,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) DataHandler(c *gin.Context) {
|
||||
timestamp := g.getTimestamp()
|
||||
ratio := g.getRatio()
|
||||
channels := g.getChannels()
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"time": timestamp,
|
||||
"ratio": ratio,
|
||||
"channels": channels,
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) IndexHandler(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "page_index", gin.H{
|
||||
"title": "TH7",
|
||||
})
|
||||
}
|
||||
|
||||
func (g *GinAdapter) ConfigHandler(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"config": g.cfg,
|
||||
})
|
||||
}
|
||||
|
||||
func NewGinAdapter(corePort ports.CorePort, cfg config.Config) *GinAdapter {
|
||||
var adapter GinAdapter
|
||||
|
||||
adapter.corePort = corePort
|
||||
adapter.port = cfg.Board.Port
|
||||
adapter.cfg = cfg
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
adapter.router = gin.New()
|
||||
|
||||
// API
|
||||
adapter.router.GET("/ratio", adapter.RatioHandler)
|
||||
adapter.router.GET("/channels", adapter.ChannelsHandler)
|
||||
adapter.router.GET("/channel/:id", adapter.ChannelByIDHandler)
|
||||
adapter.router.GET("/time", adapter.TimestampHandler)
|
||||
adapter.router.GET("/data", adapter.DataHandler)
|
||||
adapter.router.GET("/config", adapter.ConfigHandler)
|
||||
|
||||
adapter.router.LoadHTMLGlob("./templates/*.tmpl")
|
||||
adapter.router.Static("/assets", "./static")
|
||||
adapter.router.GET("/", adapter.IndexHandler)
|
||||
|
||||
return &adapter
|
||||
}
|
||||
|
||||
func (g *GinAdapter) Run() {
|
||||
portStr := fmt.Sprintf(":%d", g.port)
|
||||
g.router.Run(portStr)
|
||||
}
|
Loading…
Reference in New Issue
Block a user