continuous updates
This commit is contained in:
parent
133b7749d4
commit
7888c5a7d1
@ -2,6 +2,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.tek.govt.hu/dowerx/place/api/read"
|
"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("/info", read.Info)
|
||||||
router.GET("/tile", read.Tile)
|
router.GET("/tile", read.Tile)
|
||||||
|
router.GET("/updates", read.Continuous)
|
||||||
router.POST("/set", write.Pixel)
|
router.POST("/set", write.Pixel)
|
||||||
router.OPTIONS("/set", func(c *gin.Context) {
|
router.OPTIONS("/set", func(c *gin.Context) {
|
||||||
c.Header("Access-Control-Allow-Origin", "*")
|
c.Header("Access-Control-Allow-Origin", "*")
|
||||||
@ -24,6 +26,7 @@ func Listen(address string, staticPath string) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if staticPath != "" {
|
if staticPath != "" {
|
||||||
|
router.StaticFile("/", filepath.Join(staticPath, "index.html"))
|
||||||
router.Static("/static", staticPath)
|
router.Static("/static", staticPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,14 +1,37 @@
|
|||||||
package read
|
package read
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"log"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.tek.govt.hu/dowerx/place/api/structs"
|
"git.tek.govt.hu/dowerx/place/api/structs"
|
||||||
"git.tek.govt.hu/dowerx/place/config"
|
"git.tek.govt.hu/dowerx/place/config"
|
||||||
"git.tek.govt.hu/dowerx/place/storage"
|
"git.tek.govt.hu/dowerx/place/storage"
|
||||||
"github.com/gin-gonic/gin"
|
"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) {
|
func Info(c *gin.Context) {
|
||||||
conf := config.GetConfig()
|
conf := config.GetConfig()
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
@ -37,5 +60,60 @@ func Tile(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Continuous(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
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,11 @@ type Color struct {
|
|||||||
B uint32 `form:"b" json:"b"`
|
B uint32 `form:"b" json:"b"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Pixel struct {
|
||||||
|
Coordinates
|
||||||
|
Color
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Color) RGBA() (r, g, b, a uint32) {
|
func (c *Color) RGBA() (r, g, b, a uint32) {
|
||||||
return c.R, c.G, c.B, 65535
|
return c.R, c.G, c.B, 65535
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package write
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"git.tek.govt.hu/dowerx/place/api/read"
|
||||||
"git.tek.govt.hu/dowerx/place/api/structs"
|
"git.tek.govt.hu/dowerx/place/api/structs"
|
||||||
"git.tek.govt.hu/dowerx/place/auth"
|
"git.tek.govt.hu/dowerx/place/auth"
|
||||||
"git.tek.govt.hu/dowerx/place/config"
|
"git.tek.govt.hu/dowerx/place/config"
|
||||||
@ -13,10 +14,7 @@ import (
|
|||||||
var timeout int = config.GetConfig().Timeout
|
var timeout int = config.GetConfig().Timeout
|
||||||
|
|
||||||
func Pixel(c *gin.Context) {
|
func Pixel(c *gin.Context) {
|
||||||
var info struct {
|
var info structs.Pixel
|
||||||
structs.Color
|
|
||||||
structs.Coordinates
|
|
||||||
}
|
|
||||||
if c.ShouldBind(&info) != nil {
|
if c.ShouldBind(&info) != nil {
|
||||||
c.AbortWithStatus(400)
|
c.AbortWithStatus(400)
|
||||||
return
|
return
|
||||||
@ -38,6 +36,8 @@ func Pixel(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go read.Connections.Send(info)
|
||||||
|
|
||||||
c.Status(200)
|
c.Status(200)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"log"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -34,13 +33,7 @@ func Load(path string, canvasSize int, tileSize int) {
|
|||||||
for x := range tiles[y] {
|
for x := range tiles[y] {
|
||||||
err := tiles[y][x].load(filepath.Join(path, fmt.Sprintf("%d-%d.png", x, y)), tileSize)
|
err := tiles[y][x].load(filepath.Join(path, fmt.Sprintf("%d-%d.png", x, y)), tileSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tiles[y][x].fill()
|
panic(err)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,15 @@ func (t *tile) load(path string, tileSize int) error {
|
|||||||
t.path = path
|
t.path = path
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
t.fill()
|
||||||
|
return t.save()
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
img, _, err := image.Decode(file)
|
img, _, err := image.Decode(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
t.fill()
|
||||||
|
return t.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
draw.Draw(t.image, img.Bounds(), img, img.Bounds().Min, draw.Src)
|
draw.Draw(t.image, img.Bounds(), img, img.Bounds().Min, draw.Src)
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Place</title>
|
<title>Place</title>
|
||||||
<script defer src="place.js"></script>
|
<script defer src="static/place.js"></script>
|
||||||
<link rel="stylesheet" href="style.css">
|
<link rel="stylesheet" href="static/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<canvas id="place" width="512" height="512"></canvas>
|
<canvas id="place" width="512" height="512"></canvas>
|
||||||
|
30
web/place.js
30
web/place.js
@ -1,4 +1,5 @@
|
|||||||
const apiBase = "http://localhost:8080";
|
const apiBase = "localhost:8080";
|
||||||
|
const ssl = false;
|
||||||
|
|
||||||
var tiles = [];
|
var tiles = [];
|
||||||
var color = {
|
var color = {
|
||||||
@ -8,16 +9,16 @@ var color = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function getInfo() {
|
async function getInfo() {
|
||||||
let response = await fetch(`${apiBase}/info`);
|
let response = await fetch(`${ssl ? "https" : "http"}://${apiBase}/info`);
|
||||||
return await response.json();
|
return await response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setPixel(canvas, ctx, x, y, c) {
|
async function sendPixel(canvas, x, y, c) {
|
||||||
let bounds = canvas.getBoundingClientRect();
|
let bounds = canvas.getBoundingClientRect();
|
||||||
x = x - bounds.left;
|
x = x - bounds.left;
|
||||||
y = y - bounds.top;
|
y = y - bounds.top;
|
||||||
|
|
||||||
let response = await fetch(`${apiBase}/set`, {
|
let response = await fetch(`${ssl ? "https" : "http"}://${apiBase}/set`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
@ -32,13 +33,7 @@ async function setPixel(canvas, ctx, x, y, c) {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(response);
|
|
||||||
|
|
||||||
switch (response.status) {
|
switch (response.status) {
|
||||||
case 200:
|
|
||||||
ctx.fillStyle = `rgb(${c.r}, ${c.g}, ${c.b})`;
|
|
||||||
ctx.fillRect(x, y, 1, 1);
|
|
||||||
break;
|
|
||||||
case 400:
|
case 400:
|
||||||
alert("missing parameters in request");
|
alert("missing parameters in request");
|
||||||
break;
|
break;
|
||||||
@ -57,12 +52,21 @@ function init(info, ctx) {
|
|||||||
for (let x = 0; x < info.canvasSize; x++) {
|
for (let x = 0; x < info.canvasSize; x++) {
|
||||||
let tile = new Image();
|
let tile = new Image();
|
||||||
tile.onload = () => ctx.drawImage(tile, x * info.tileSize, y * info.tileSize);
|
tile.onload = () => ctx.drawImage(tile, x * info.tileSize, y * info.tileSize);
|
||||||
tile.src = `${apiBase}/tile?x=${x}&y=${y}`;
|
tile.src = `${ssl ? "https" : "http"}://${apiBase}/tile?x=${x}&y=${y}`;
|
||||||
tiles[y].push(tile);
|
tiles[y].push(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updates(ctx) {
|
||||||
|
let socket = new WebSocket(`ws://${apiBase}/updates`);
|
||||||
|
socket.onmessage = async e => {
|
||||||
|
let pixel = await JSON.parse(e.data);
|
||||||
|
ctx.fillStyle = `rgb(${pixel.r}, ${pixel.g}, ${pixel.b})`;
|
||||||
|
ctx.fillRect(pixel.x, pixel.y, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
let info = await getInfo();
|
let info = await getInfo();
|
||||||
console.log(info);
|
console.log(info);
|
||||||
@ -71,7 +75,9 @@ async function main() {
|
|||||||
let ctx = canvas.getContext("2d");
|
let ctx = canvas.getContext("2d");
|
||||||
init(info, ctx);
|
init(info, ctx);
|
||||||
|
|
||||||
canvas.onclick = e => setPixel(canvas, ctx, e.clientX, e.clientY, color);
|
updates(ctx);
|
||||||
|
|
||||||
|
canvas.onclick = e => sendPixel(canvas, ctx, e.clientX, e.clientY, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
Loading…
Reference in New Issue
Block a user