diff --git a/api/endpoints.go b/api/endpoints.go index 106f6ae..9b4d38a 100644 --- a/api/endpoints.go +++ b/api/endpoints.go @@ -2,6 +2,7 @@ package api import ( "net/http" + "path/filepath" "time" "git.tek.govt.hu/dowerx/place/api/read" @@ -14,6 +15,7 @@ func Listen(address string, staticPath string) { router.GET("/info", read.Info) router.GET("/tile", read.Tile) + router.GET("/updates", read.Continuous) router.POST("/set", write.Pixel) router.OPTIONS("/set", func(c *gin.Context) { c.Header("Access-Control-Allow-Origin", "*") @@ -24,6 +26,7 @@ func Listen(address string, staticPath string) { }) if staticPath != "" { + router.StaticFile("/", filepath.Join(staticPath, "index.html")) router.Static("/static", staticPath) } diff --git a/api/read/read.go b/api/read/read.go index fd55bb1..66cb52f 100644 --- a/api/read/read.go +++ b/api/read/read.go @@ -1,14 +1,37 @@ package read import ( - "errors" + "log" + "sync" + "time" "git.tek.govt.hu/dowerx/place/api/structs" "git.tek.govt.hu/dowerx/place/config" "git.tek.govt.hu/dowerx/place/storage" "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" ) +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +type ConnectionsMap struct { + sync.Mutex + Items map[*websocket.Conn]chan structs.Pixel +} + +func (c *ConnectionsMap) Send(pixel structs.Pixel) { + c.Lock() + for _, v := range c.Items { + v <- pixel + } + c.Unlock() +} + +var Connections ConnectionsMap = ConnectionsMap{Items: make(map[*websocket.Conn]chan structs.Pixel)} + func Info(c *gin.Context) { conf := config.GetConfig() c.JSON(200, gin.H{ @@ -37,5 +60,60 @@ func Tile(c *gin.Context) { } func Continuous(c *gin.Context) { - panic(errors.New("unimplomented")) + conn, err := upgrader.Upgrade(c.Writer, c.Request, nil) + if err != nil { + return + } + + Connections.Lock() + Connections.Items[conn] = make(chan structs.Pixel) + Connections.Unlock() + + conn.SetReadDeadline(time.Now().Add(time.Second * 60)) + conn.SetPongHandler(func(string) error { + conn.SetReadDeadline(time.Now().Add(time.Second * 60)) + return nil + }) + + // send pings + go func() { + ticker := time.NewTicker(30 * time.Second) + defer ticker.Stop() + + for range ticker.C { + if err := conn.WriteControl(websocket.PingMessage, nil, time.Now().Add(10*time.Second)); err != nil { + return + } + } + }() + + // defer: close connection, remove channel + defer func() { + Connections.Lock() + for k, v := range Connections.Items { + if k == conn { + close(v) + delete(Connections.Items, k) + break + } + } + Connections.Unlock() + conn.Close() + log.Println("removed connection") + }() + + // send updates + go func() { + for pixel := range Connections.Items[conn] { + conn.WriteJSON(pixel) + } + }() + + // read loop to keep the connetion alive + for { + _, _, err := conn.ReadMessage() + if err != nil { + break + } + } } diff --git a/api/structs/structs.go b/api/structs/structs.go index 8937ca8..9a6de48 100644 --- a/api/structs/structs.go +++ b/api/structs/structs.go @@ -11,6 +11,11 @@ type Color struct { B uint32 `form:"b" json:"b"` } +type Pixel struct { + Coordinates + Color +} + func (c *Color) RGBA() (r, g, b, a uint32) { return c.R, c.G, c.B, 65535 } diff --git a/api/write/write.go b/api/write/write.go index 46ea9ba..aaf12ea 100644 --- a/api/write/write.go +++ b/api/write/write.go @@ -3,6 +3,7 @@ package write import ( "errors" + "git.tek.govt.hu/dowerx/place/api/read" "git.tek.govt.hu/dowerx/place/api/structs" "git.tek.govt.hu/dowerx/place/auth" "git.tek.govt.hu/dowerx/place/config" @@ -13,10 +14,7 @@ import ( var timeout int = config.GetConfig().Timeout func Pixel(c *gin.Context) { - var info struct { - structs.Color - structs.Coordinates - } + var info structs.Pixel if c.ShouldBind(&info) != nil { c.AbortWithStatus(400) return @@ -38,6 +36,8 @@ func Pixel(c *gin.Context) { return } + go read.Connections.Send(info) + c.Status(200) } diff --git a/storage/storage.go b/storage/storage.go index 1323721..c4b3331 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -6,7 +6,6 @@ import ( "fmt" "image" "image/color" - "log" "path/filepath" "time" ) @@ -34,13 +33,7 @@ func Load(path string, canvasSize int, tileSize int) { for x := range tiles[y] { err := tiles[y][x].load(filepath.Join(path, fmt.Sprintf("%d-%d.png", x, y)), tileSize) if err != nil { - tiles[y][x].fill() - if err = tiles[y][x].save(); err != nil { - panic(err) - } - log.Printf("Created tile (%d-%d)\n", x, y) - } else { - log.Printf("Loaded tile (%d-%d)\n", x, y) + panic(err) } } } diff --git a/storage/tile.go b/storage/tile.go index 17eb3fc..3db71cb 100644 --- a/storage/tile.go +++ b/storage/tile.go @@ -19,13 +19,15 @@ func (t *tile) load(path string, tileSize int) error { t.path = path file, err := os.Open(path) if err != nil { - return err + t.fill() + return t.save() } defer file.Close() img, _, err := image.Decode(file) if err != nil { - return err + t.fill() + return t.save() } draw.Draw(t.image, img.Bounds(), img, img.Bounds().Min, draw.Src) diff --git a/web/index.html b/web/index.html index e131eb8..a6e5357 100644 --- a/web/index.html +++ b/web/index.html @@ -4,8 +4,8 @@