30 Commits
v0.0.3 ... main

Author SHA1 Message Date
Adrian Zuercher
a34f93e169 add status server for driver state
All checks were successful
Build ArtNet Driver / build (amd64, .exe, windows) (push) Successful in 3m49s
Build ArtNet Driver / build (amd64, , linux) (push) Successful in 3m58s
Build ArtNet Driver / build (arm, 6, , linux) (push) Successful in 3m30s
Build ArtNet Driver / build (arm64, , linux) (push) Successful in 3m31s
2025-09-07 14:22:44 +02:00
Adrian Zuercher
ede86b71ef repo name change
All checks were successful
Build ArtNet Driver / build (amd64, , linux) (push) Successful in 1m40s
Build ArtNet Driver / build (amd64, .exe, windows) (push) Successful in 1m41s
Build ArtNet Driver / build (arm, 6, , linux) (push) Successful in 1m39s
Build ArtNet Driver / build (arm64, , linux) (push) Successful in 1m38s
2025-08-06 22:41:34 +02:00
Adrian Zuercher
67f25c76ae rename folder .github t .gitea
Some checks failed
Build Go Binaries / build (arm, linux) (push) Has been cancelled
Build Go Binaries / build (arm64, linux) (push) Has been cancelled
Build Go Binaries / build (amd64, linux) (push) Has been cancelled
Build Go Binaries / build (amd64, windows) (push) Has been cancelled
2025-08-02 09:58:12 +02:00
Adrian Zuercher
7ef6e614cc update to newest json_data model 2025-07-31 12:27:53 +02:00
Adrian Zuercher
49dff4f609 update json_data and model so more than one address can be revieved from server 2025-07-30 17:16:50 +02:00
Adrian Zuercher
3704edebd5 fix wrong call and missing send set 2025-07-27 13:07:16 +02:00
Adrian Zuercher
a503b71fb6 add read write lock to dmx data 2025-07-26 07:18:26 +02:00
Adrian Zuercher
05409c2544 change send data to dmx as 23 millisecond ticker when new data 2025-07-25 22:37:35 +02:00
Adrian Zuercher
b484745ed1 increase timeout of ping from 10 to 40 2025-07-25 20:59:17 +02:00
Adrian Zuercher
04e306c6da update to latest json_data 2025-07-24 09:57:16 +02:00
Adrian Zuercher
239e0050a1 change ping pong handler and fix blocking channel 2025-07-23 08:56:43 +02:00
Adrian Zuercher
de4758c563 fix mmissing defer for close channel and move make channel up earlier 2025-07-20 09:03:45 +02:00
Adrian Zuercher
a85c9ccd66 Merge branch 'main' of https://github.com/tecamino/tecamino-driver-artNet 2025-07-16 22:00:53 +02:00
Adrian Zuercher
da78a00446 add heartbeat send data every 30 Seconds 2025-07-16 21:53:47 +02:00
Adrian Zuercher
ba3c55dc34 add mac DS_Store 2025-07-16 21:52:31 +02:00
zuadi
e0950b44ee Update build.yml add flags for smaller executable 2025-07-13 20:07:44 +02:00
Adrian Zuercher
76a036707f fix faulty data send to artNet with new chan queue 2025-07-13 19:56:00 +02:00
Adrian Zuercher
258323f5b7 add new executable name 2025-07-13 19:54:27 +02:00
Adrian Zuercher
473eb22b97 fix missing subscribe and publish on artNet Protocol 2025-06-29 15:36:14 +02:00
Adrian Zürcher
64ad4e8b3e improve websocket connection according to gorrila example 2025-06-22 09:03:18 +02:00
Adrian Zürcher
b29e7a97b8 lift version of json_data 2025-06-19 19:19:16 +02:00
Adrian Zürcher
c85590e5a5 updated websocket as client broker 2025-06-19 18:32:47 +02:00
Adrian Zürcher
b5d9d96c52 add resubscribe (in progress)
fix crash when no bus found in map
2025-05-28 22:00:04 +02:00
Adrian Zürcher
dc64d08e9d new son_data model 2025-05-05 07:32:26 +02:00
Adrian Zürcher
a92995d766 removie unused websocket 2025-04-29 17:16:36 +02:00
Adrian Zürcher
348a6f7622 move client context to correct place 2025-04-29 17:15:20 +02:00
Adrian Zürcher
83cbcdaa42 updated 2025-04-29 09:16:27 +02:00
Adrian Zürcher
2839b615c3 implement new json_data model 2025-04-29 08:34:51 +02:00
Adrian Zürcher
f5e66af3d8 add new ip ping for udp device
add new function start and stop bus
2025-04-22 18:01:22 +02:00
Adrian Zürcher
274e6acf0e update logger 2025-04-22 17:59:07 +02:00
21 changed files with 927 additions and 325 deletions

View File

@@ -0,0 +1,89 @@
name: Build ArtNet Driver
on:
push:
tags:
- '*'
env:
APP_NAME: tecamino-driver-artNet
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- os: windows
arch: amd64
ext: .exe
- os: linux
arch: amd64
ext: ""
- os: linux
arch: arm64
ext: ""
- os: linux
arch: arm
arm_version: 6
ext: ""
steps:
- uses: actions/checkout@v4
- name: Ensure latest Go is installed in /data/go
run: |
export GOROOT=/data/go/go
export PATH=$GOROOT/bin:$PATH
export GOCACHE=/data/gocache
export GOMODCACHE=/data/gomodcache
mkdir -p $GOCACHE $GOMODCACHE
if [ ! -x "$GOROOT/bin/go" ]; then
echo "Go not found in $GOROOT, downloading latest stable..."
GO_VERSION=$(curl -s https://go.dev/VERSION?m=text | head -n1)
echo "Latest version is $GO_VERSION"
mkdir -p /data/go
curl -sSL "https://go.dev/dl/${GO_VERSION}.linux-amd64.tar.gz" -o /tmp/go.tar.gz
tar -C /data/go -xzf /tmp/go.tar.gz
else
echo "Using cached Go from $GOROOT"
fi
go version
- name: Download Go dependencies
run: |
export GOROOT=/data/go/go
export PATH=$GOROOT/bin:$PATH
export GOCACHE=/data/gocache
export GOMODCACHE=/data/gomodcache
mkdir -p $GOCACHE $GOMODCACHE
go mod download
- name: Build binary
run: |
export GOROOT=/data/go/go
export PATH=$GOROOT/bin:$PATH
export GOCACHE=/data/gocache
export GOMODCACHE=/data/gomodcache
mkdir -p $GOCACHE $GOMODCACHE
OUTPUT="bin/${APP_NAME}-${{ matrix.os }}-${{ matrix.arch }}"
if [ -n "${{ matrix.arm_version }}" ]; then
OUTPUT="${OUTPUT}v${{ matrix.arm_version }}"
export GOARM=${{ matrix.arm_version }}
fi
OUTPUT="${OUTPUT}${{ matrix.ext }}"
echo "Building $OUTPUT"
GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build -ldflags="-s -w" -trimpath -o "$OUTPUT"
shell: bash
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: ${{ env.APP_NAME }}-${{ matrix.os }}-${{ matrix.arch }}
path: bin/

3
.gitignore vendored
View File

@@ -1,2 +1,5 @@
.DS_Store
*.cfg *.cfg
*.log *.log
artNetDriver-arm64
tecamino-driver-artNet-linux-arm64

View File

@@ -13,3 +13,11 @@ func GetIDFromAuth(c *gin.Context) (string, error) {
} }
return "", errors.New("authorization token missing") return "", errors.New("authorization token missing")
} }
func GetIDFromQuery(c *gin.Context) (string, error) {
auth, exists := c.GetQuery("id")
if !exists {
return "", errors.New("id missing")
}
return auth, nil
}

View File

@@ -1,7 +1,6 @@
package cfg package cfg
import ( import (
"fmt"
"os" "os"
"path" "path"
@@ -51,7 +50,7 @@ func (c *Cfg) SaveCfg(data any) error {
func (c *Cfg) LoadCfg(data any) error { func (c *Cfg) LoadCfg(data any) error {
p := path.Join(c.Dir, c.Name) + ".cfg" p := path.Join(c.Dir, c.Name) + ".cfg"
if _, err := os.Stat(p); err != nil { if _, err := os.Stat(p); err != nil {
return fmt.Errorf("no configuration file: '%s' found", p) return nil
} }
b, err := os.ReadFile(p) b, err := os.ReadFile(p)

View File

@@ -3,47 +3,64 @@ package driver
import ( import (
"artNet/cfg" "artNet/cfg"
"artNet/models" "artNet/models"
serverModels "artNet/server/models" ws "artNet/websocket"
"encoding/json"
"errors"
"fmt" "fmt"
"path"
"github.com/tecamino/tecamino-logger/logging" "gitea.tecamino.com/paadi/statusServer"
json_data "gitea.tecamino.com/paadi/tecamino-json_data"
"gitea.tecamino.com/paadi/tecamino-logger/logging"
"github.com/gin-gonic/gin"
) )
type ArtNetDriver struct { type ArtNetDriver struct {
statusServer *statusServer.StatusClient `yaml:"-" json:"-"`
Name string `yaml:"driver" json:"driver"` Name string `yaml:"driver" json:"driver"`
Buses map[string]*models.Bus `yaml:"buses,omitempty" json:"buses,omitempty"` Buses map[string]*models.Bus `yaml:"buses,omitempty" json:"buses,omitempty"`
cfgHandler *cfg.Cfg `yaml:"-" json:"-"` cfgHandler *cfg.Cfg `yaml:"-" json:"-"`
Connections serverModels.Clients `yaml:"-"` Subscriptions models.Subscriptions `yaml:"-" json:"-"`
Log *logging.Logger `yaml:"-"` Log *logging.Logger `yaml:"-" json:"-"`
} }
// initialize new Art-Net driver // initialize new Art-Net driver
// cfgDir config directory // cfgDir config directory
// name name of driver // name name of driver
func NewDriver(cfgDir, name string, debug bool) (*ArtNetDriver, error) { func NewDriver(cfgDir, name, ip string, port uint, debug bool) (*ArtNetDriver, error) {
if cfgDir == "" { if cfgDir == "" {
cfgDir = "./cfg" cfgDir = "./cfg"
} }
logger, err := logging.NewLogger(name, debug) logger, err := logging.NewLogger(path.Join(cfgDir, name+".log"), &logging.Config{
MaxSize: 1,
MaxBackup: 3,
MaxAge: 28,
Debug: debug,
TerminalOut: true,
})
if err != nil { if err != nil {
panic(err) panic(err)
} }
statusS, err := statusServer.NewStatusClient(name, ip, "api/status", port, false)
if err != nil {
return nil, errors.New("status server: " + err.Error())
}
logger.Debug("artNet.NewDriver", "initialize "+name+" driver") logger.Debug("artNet.NewDriver", "initialize "+name+" driver")
d := ArtNetDriver{ d := ArtNetDriver{
statusServer: statusS,
Name: name, Name: name,
Buses: make(map[string]*models.Bus), Buses: make(map[string]*models.Bus),
cfgHandler: cfg.NewCfgHandler(cfgDir, name), cfgHandler: cfg.NewCfgHandler(cfgDir, name),
Connections: serverModels.NewClients(),
Log: logger, Log: logger,
} }
if err := d.LoadCfg(); err != nil { if err := d.LoadCfg(); err != nil {
logger.Error("artNet.NewDriver", "error load driver configuration: "+err.Error()) logger.Error("artNet.NewDriver.LoadCfg", "error load driver configuration: "+err.Error())
return nil, err return nil, err
} }
return &d, nil return &d, nil
} }
@@ -54,8 +71,8 @@ func (d *ArtNetDriver) LoadCfg() error {
return err return err
} }
for _, b := range d.Buses { for name, b := range d.Buses {
d.NewBus(b.Name, b.Ip, *b.Port) d.NewBus(name, b.Ip, *b.Port)
} }
return nil return nil
} }
@@ -67,11 +84,90 @@ func (d *ArtNetDriver) NewBus(name, ip string, port int) *models.Bus {
return b return b
} }
func (d *ArtNetDriver) SetValue(set models.Set) error { func (d *ArtNetDriver) SetValue(bus string, address uint, value uint8) error {
if _, ok := d.Buses[set.Bus]; !ok { if _, ok := d.Buses[bus]; !ok {
return fmt.Errorf("no bus '%s' found", set.Bus) return fmt.Errorf("no bus '%s' found", bus)
}
if d.Buses[bus].DMX.Data == nil {
return fmt.Errorf("no dmx data on bus '%s' found", bus)
}
d.Buses[bus].SetDMXData(address, value)
return nil
}
// connect to websocket server and listen to subscriptions
// ip: ip address of server
// id: id of driver
// port: port of server
func (d *ArtNetDriver) Connect(ip, id string, port uint) error {
topic := "status/" + id
d.statusServer.Publish(topic, gin.H{
"state": "Starting",
})
defer d.statusServer.Publish(topic, gin.H{
"state": "Stopped",
})
errChan := make(chan error)
client, err := ws.NewClient(ip, id, port)
if err != nil {
d.statusServer.Publish(topic, gin.H{
"state": "Error",
"message": err.Error()})
return err
}
client.OnError = func(err error) {
d.statusServer.Publish(topic, gin.H{"state": "Error", "message": err.Error()})
d.Log.Error("websocket connection", err)
errChan <- err
}
client.OnMessage = func(data []byte) {
request := json_data.NewResponse()
err = json.Unmarshal(data, &request)
if err != nil {
d.statusServer.Publish(topic, gin.H{"state": "Error", "message": err.Error()})
return
}
if request.Subscribe != nil {
d.Subscribe(request.Subscribe...)
}
if request.Publish != nil {
d.Publish(request.Publish...)
}
}
if err != nil {
d.statusServer.Publish("error", err.Error())
d.Log.Error("artNet.Connect", err)
return err
}
req := json_data.NewRequest()
req.AddDriverSubscription(".*", id, 0, true, false, false)
if err := client.SendRequest(req); err != nil {
errChan <- err
d.Log.Error("websocket send data", err)
}
d.statusServer.Publish(topic, gin.H{"state": "Running"})
for err := range errChan {
d.statusServer.Publish(topic, gin.H{"state": "Error", "message": err.Error()})
return err
}
return nil
}
// send data to all buses that the send flage is true
func (d *ArtNetDriver) SendData() {
for _, bus := range d.Buses {
bus.Send = bus.Reachable
} }
d.Buses[set.Bus].Data.SetValue(set.Address, set.Value)
return d.Buses[set.Bus].SendData()
} }

View File

@@ -7,6 +7,8 @@ import (
"net" "net"
"net/http" "net/http"
json_data "gitea.tecamino.com/paadi/tecamino-json_data"
json_dataModels "gitea.tecamino.com/paadi/tecamino-json_data/models"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -25,70 +27,172 @@ func (d *ArtNetDriver) GetAllBuses(c *gin.Context) {
} }
func (d *ArtNetDriver) CreateBus(c *gin.Context) { func (d *ArtNetDriver) CreateBus(c *gin.Context) {
_, err := auth.GetIDFromAuth(c) _, err := auth.GetIDFromQuery(c)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "id: " + err.Error()}) r := json_data.NewResponse()
r.SetError()
r.SetMessage(err.Error())
c.JSON(http.StatusBadRequest, r)
return return
} }
var payload models.Bus busPayload := models.Bus{}
if err := busPayload.ParsePayload(c); err != nil {
if err := c.BindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "json: " + err.Error()})
return return
} }
if payload.Name == "" { if addr := net.ParseIP(busPayload.Ip); addr == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "bus name missing"}) r := json_data.NewResponse()
return r.SetError()
} else if addr := net.ParseIP(payload.Ip); addr == nil { r.SetMessage("wrong ip '" + busPayload.Ip + "'")
c.JSON(http.StatusBadRequest, r)
c.JSON(http.StatusBadRequest, gin.H{"error": "wrong ip '" + payload.Ip + "'"})
return return
} }
if _, ok := d.Buses[payload.Name]; ok { if _, ok := d.Buses[busPayload.Name]; ok {
c.JSON(http.StatusOK, gin.H{"message": "bus " + payload.Name + " exists already"}) r := json_data.NewResponse()
r.SetError()
r.SetMessage("bus " + busPayload.Name + " exists already")
c.JSON(http.StatusOK, r)
return return
} }
bus := d.NewBus(payload.Name, payload.Ip, payload.GetPort()) bus := d.NewBus(busPayload.Name, busPayload.Ip, busPayload.GetPort())
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("bus '%s' successfully created with ip: %s and on port: %d", bus.Name, bus.Ip, bus.GetPort()), r := json_data.NewResponse()
}) r.SetMessage(fmt.Sprintf("bus '%s' successfully created with ip: %s and on port: %d", bus.Name, bus.Ip, bus.GetPort()))
c.JSON(http.StatusOK, r)
d.cfgHandler.SaveCfg(*d) d.cfgHandler.SaveCfg(*d)
} }
func (d *ArtNetDriver) RemoveBus(c *gin.Context) { func (d *ArtNetDriver) RemoveBus(c *gin.Context) {
_, err := auth.GetIDFromAuth(c) _, err := auth.GetIDFromQuery(c)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "id: " + err.Error()}) r := json_data.NewResponse()
r.SetError()
r.SetMessage("id: " + err.Error())
c.JSON(http.StatusBadRequest, r)
return return
} }
var payload models.Bus busPayload := models.Bus{}
if err := busPayload.ParsePayload(c); err != nil {
if err := c.BindJSON(&payload); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "json: " + err.Error()})
return return
} }
if payload.Name == "" { if _, ok := d.Buses[busPayload.Name]; !ok {
c.JSON(http.StatusBadRequest, gin.H{"error": "bus name missing"}) r := json_dataModels.NewResponse()
return r.SetMessage("bus " + busPayload.Name + " not found")
} c.JSON(http.StatusBadRequest, r)
if _, ok := d.Buses[payload.Name]; !ok {
c.JSON(http.StatusOK, gin.H{"message": "bus " + payload.Name + " not found"})
c.JSON(http.StatusOK, models.JsonResponse{
Message: "bus " + payload.Name + " not found"})
return return
} else { } else {
delete(d.Buses, payload.Name) delete(d.Buses, busPayload.Name)
} }
r := json_dataModels.NewResponse()
c.JSON(http.StatusOK, models.JsonResponse{ r.SetMessage(fmt.Sprintf("bus '%s' successfully removed", busPayload.Name))
Message: fmt.Sprintf("bus '%s' successfully removed", payload.Name), c.JSON(http.StatusOK, r)
})
d.cfgHandler.SaveCfg(*d) d.cfgHandler.SaveCfg(*d)
} }
func (d *ArtNetDriver) Start(c *gin.Context) {
_, err := auth.GetIDFromQuery(c)
if err != nil {
r := json_data.NewResponse()
r.SetError()
r.SetMessage("id: " + err.Error())
c.JSON(http.StatusBadRequest, r)
return
}
busPayload := models.Bus{}
if err := busPayload.ParsePayload(c); err != nil {
return
}
d.Buses[busPayload.Name].Start(d.Log)
d.statusServer.Publish(fmt.Sprintf("status/%s/%s", d.Name, busPayload.Name), "Running")
r := json_dataModels.NewResponse()
r.SetMessage(fmt.Sprintf("bus '%s' running", busPayload.Name))
c.JSON(http.StatusOK, r)
}
func (d *ArtNetDriver) Stop(c *gin.Context) {
_, err := auth.GetIDFromQuery(c)
if err != nil {
r := json_data.NewResponse()
r.SetError()
r.SetMessage("id: " + err.Error())
c.JSON(http.StatusBadRequest, r)
return
}
busPayload := models.Bus{}
if err := busPayload.ParsePayload(c); err != nil {
return
}
d.Buses[busPayload.Name].Stop()
d.statusServer.Publish(fmt.Sprintf("status/%s/%s", d.Name, busPayload.Name), "Stopped")
r := json_dataModels.NewResponse()
r.SetMessage(fmt.Sprintf("bus '%s' stopped", busPayload.Name))
c.JSON(http.StatusOK, r)
}
func (d *ArtNetDriver) Resubscribe(c *gin.Context) {
_, err := auth.GetIDFromQuery(c)
if err != nil {
r := json_data.NewResponse()
r.SetError()
r.SetMessage("id: " + err.Error())
c.JSON(http.StatusBadRequest, r)
return
}
busPayload := models.Bus{}
if err := busPayload.ParsePayload(c); err != nil {
return
}
if _, ok := d.Buses[busPayload.Name]; !ok {
r := json_dataModels.NewResponse()
r.SetMessage("bus " + busPayload.Name + " not found")
c.JSON(http.StatusBadRequest, r)
return
}
if busPayload.Resubscribe == nil {
r := json_dataModels.NewResponse()
r.SetMessage("no resubscriptions in request")
c.JSON(http.StatusBadRequest, r)
return
}
d.Subscribe(*busPayload.Resubscribe...)
}
func (d *ArtNetDriver) Status(c *gin.Context) {
_, err := auth.GetIDFromQuery(c)
if err != nil {
r := json_data.NewResponse()
r.SetError()
r.SetMessage("id: " + err.Error())
c.JSON(http.StatusBadRequest, r)
return
}
busPayload := models.Bus{}
if err := busPayload.ParsePayload(c); err != nil {
return
}
r := json_dataModels.NewResponse()
state := "stopped"
if d.Buses[busPayload.Name].Status() {
state = "running"
}
r.SetMessage(fmt.Sprintf("bus '%s' %s", busPayload.Name, state))
c.JSON(http.StatusOK, r)
}

23
driver/publish.go Normal file
View File

@@ -0,0 +1,23 @@
package driver
import (
json_dataModels "gitea.tecamino.com/paadi/tecamino-json_data/models"
)
func (d *ArtNetDriver) Publish(pubs ...json_dataModels.Publish) error {
if d.Subscriptions == nil {
return nil
}
for _, pub := range pubs {
if subs, ok := (d.Subscriptions)[pub.Uuid]; ok {
for _, sub := range subs {
for _, address := range sub.Address {
d.SetValue(sub.Bus, address, uint8(pub.Value.(float64)))
}
}
}
}
d.SendData()
return nil
}

26
driver/subscribe.go Normal file
View File

@@ -0,0 +1,26 @@
package driver
import (
"artNet/models"
json_dataModels "gitea.tecamino.com/paadi/tecamino-json_data/models"
)
func (d *ArtNetDriver) Subscribe(subs ...json_dataModels.Subscription) {
if d.Subscriptions == nil {
d.Subscriptions = models.NewSubscriptions()
}
for _, sub := range subs {
if drv, ok := (*sub.Drivers)[sub.Driver]; ok {
d.Subscriptions.AddSubscription(sub.Uuid, drv)
for _, bus := range drv.Buses {
for _, address := range bus.Address {
d.SetValue(bus.Name, address, uint8(sub.Value.(float64)))
}
}
}
}
d.SendData()
}

View File

@@ -1,63 +0,0 @@
package driver
import (
"context"
"log"
"time"
"artNet/auth"
"artNet/models"
"github.com/coder/websocket"
"github.com/coder/websocket/wsjson"
"github.com/gin-gonic/gin"
)
const (
OnCreate = "onCreate"
OnChange = "onChange"
OnDelete = "onDelete"
)
func (d *ArtNetDriver) Websocket(c *gin.Context) {
id, err := auth.GetIDFromAuth(c)
if err != nil {
d.Log.Error("artNet.webSocket.Websocket", "error GetIDFromAuth: "+err.Error())
log.Println("error id:", err)
return
}
d.Log.Debug("artNet.webSocket.Websocket", "authorization id token: "+id)
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Minute)
defer cancel()
conn, err := d.Connections.ConnectRecievingWsConnection(id, c)
if err != nil {
d.Log.Error("artNet.webSocket.Websocket", "error connecting recieving websocket conection: "+err.Error())
return
}
defer d.Connections.DisconnectRecievingWsConnection(id, websocket.StatusInternalError, "Internal error")
var request models.JsonData
//Read loop
for {
err := wsjson.Read(ctx, conn, &request)
if err != nil {
d.Log.Error("artNet.webSocket.Websocket", "read error:"+err.Error())
log.Println("WebSocket read error:", err)
break
}
// Set
if request.Set != nil {
for _, set := range *request.Set {
if err = d.SetValue(set); err != nil {
d.Log.Error("artNet.webSocket.Websocket", "set value error"+err.Error())
log.Println(err)
continue
}
time.Sleep(23 * time.Millisecond)
}
}
}
}

52
go.mod
View File

@@ -1,43 +1,47 @@
module artNet module artNet
go 1.23.0 go 1.24.0
toolchain go1.23.8
require ( require (
github.com/coder/websocket v1.8.13 gitea.tecamino.com/paadi/statusServer v1.0.3
github.com/gin-gonic/gin v1.10.0 gitea.tecamino.com/paadi/tecamino-json_data v0.1.0
gitea.tecamino.com/paadi/tecamino-logger v0.2.1
github.com/gin-gonic/gin v1.10.1
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
github.com/tecamino/tecamino-logger v0.1.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/bytedance/sonic v1.11.6 // indirect gitea.tecamino.com/paadi/pubSub v1.0.2 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect gitea.tecamino.com/paadi/tecamino-dbm v0.1.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect github.com/bytedance/sonic v1.13.3 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/cloudwego/base64x v0.1.5 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gin-contrib/cors v1.7.6 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/leodido/go-urn v1.4.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect github.com/ugorji/go/codec v1.3.0 // indirect
go.uber.org/multierr v1.10.0 // indirect go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/arch v0.8.0 // indirect golang.org/x/arch v0.18.0 // indirect
golang.org/x/crypto v0.23.0 // indirect golang.org/x/crypto v0.39.0 // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.20.0 // indirect golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.15.0 // indirect golang.org/x/text v0.26.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
) )

114
go.sum
View File

@@ -1,41 +1,59 @@
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= gitea.tecamino.com/paadi/pubSub v1.0.2 h1:9Q9KLTIHRSjxrTkDyuF8mDYOLkI8DjjPNSnoS2GKflY=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= gitea.tecamino.com/paadi/pubSub v1.0.2/go.mod h1:SBPTSD/JQWRbwqsSNoSPhV81IDTreP0TMyvLhmK3P2M=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= gitea.tecamino.com/paadi/statusServer v1.0.3 h1:bRw+Jz9AIoiwqNLTBnrus2aUxq2Em2gA8NDANtqVHEA=
gitea.tecamino.com/paadi/statusServer v1.0.3/go.mod h1:kNO/ASrrmsLGgo+k49TLpVP6PpxG3I3D1moJ6Ke+ocg=
gitea.tecamino.com/paadi/tecamino-dbm v0.1.1 h1:vAq7mwUxlxJuLzCQSDMrZCwo8ky5usWi9Qz+UP+WnkI=
gitea.tecamino.com/paadi/tecamino-dbm v0.1.1/go.mod h1:+tmf1rjPaKEoNeUcr1vdtoFIFweNG3aUGevDAl3NMBk=
gitea.tecamino.com/paadi/tecamino-json_data v0.1.0 h1:zp3L8qUvkVqzuesQdMh/SgZZZbX3pGD9NYa6jtz+JvA=
gitea.tecamino.com/paadi/tecamino-json_data v0.1.0/go.mod h1:/FKhbVYuhiNlQp4552rJJQIhynjLarDzfrgXpupkwZU=
gitea.tecamino.com/paadi/tecamino-logger v0.2.1 h1:sQTBKYPdzn9mmWX2JXZBtGBvNQH7cuXIwsl4TD0aMgE=
gitea.tecamino.com/paadi/tecamino-logger v0.2.1/go.mod h1:FkzRTldUBBOd/iy2upycArDftSZ5trbsX5Ira5OzJgM=
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/coder/websocket v1.8.13 h1:f3QZdXy7uGVz+4uCJy2nTZyM0yTBj8yANEHhqlXZ9FE=
github.com/coder/websocket v1.8.13/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
@@ -45,57 +63,53 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e h1:nt2877sKfojlHCTOBXbpWjBkuWKritFaGIfgQwbQUls= github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e h1:nt2877sKfojlHCTOBXbpWjBkuWKritFaGIfgQwbQUls=
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e/go.mod h1:B4+Kq1u5FlULTjFSM707Q6e/cOHFv0z/6QRoxubDIQ8= github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e/go.mod h1:B4+Kq1u5FlULTjFSM707Q6e/cOHFv0z/6QRoxubDIQ8=
github.com/tecamino/tecamino-logger v0.1.1 h1:bK0SEQpbjui42OsnM6JCDThaGMBoqwVj/Op6ESqDwV4=
github.com/tecamino/tecamino-logger v0.1.1/go.mod h1:sGysmiFGIdr4vLJRAI+fJgsa7EoRRuxvRrKW7GnGQkw=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

46
main.go
View File

@@ -4,7 +4,9 @@ import (
"artNet/driver" "artNet/driver"
"artNet/server" "artNet/server"
"flag" "flag"
"fmt"
"os" "os"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -13,10 +15,15 @@ const DriverName string = "ArtNetDriver"
func main() { func main() {
// cli flags // cli flags
port := flag.Uint("port-http", 8110, "http server listening port")
cfgDir := flag.String("cfg", "./cfg", "config directory") cfgDir := flag.String("cfg", "./cfg", "config directory")
workingDir := flag.String("workingDirectory", ".", "current working directory") workingDir := flag.String("workingDirectory", ".", "current working directory")
statusServerIp := flag.String("statusIp", "127.0.0.1", "ip address of status server")
statusServerPort := flag.Uint("statusPort", 9500, "port of status server")
serverIp := flag.String("serverIp", "127.0.0.1", "ip address of server")
serverPort := flag.Uint("serverPort", 8100, "port of server")
wsPort := flag.Uint("port", 8200, "websocket port")
debug := flag.Bool("debug", false, "debug logging") debug := flag.Bool("debug", false, "debug logging")
start := flag.Bool("start", false, "starts all buses on startup")
flag.Parse() flag.Parse()
//change working directory only if value is given //change working directory only if value is given
@@ -25,9 +32,8 @@ func main() {
} }
//initialize new ArtNet driver //initialize new ArtNet driver
artNetDriver, err := driver.NewDriver(*cfgDir, DriverName, *debug) artNetDriver, err := driver.NewDriver(*cfgDir, DriverName, *statusServerIp, *statusServerPort, *debug)
if err != nil { if err != nil {
artNetDriver.Log.Error("main", "error initialize new artnet driver "+err.Error())
panic(err) panic(err)
} }
@@ -37,18 +43,42 @@ func main() {
//set routes //set routes
artNetDriver.Log.Debug("main", "setting routes") artNetDriver.Log.Debug("main", "setting routes")
s.Routes.GET("/ws", artNetDriver.Websocket)
s.Routes.GET("/buses/all", artNetDriver.GetAllBuses) s.Routes.GET("/buses/all", artNetDriver.GetAllBuses)
s.Routes.POST("/buses/create", artNetDriver.CreateBus) s.Routes.POST("/buses/create", artNetDriver.CreateBus)
s.Routes.POST("/buses/remove", artNetDriver.RemoveBus) s.Routes.POST("/buses/remove", artNetDriver.RemoveBus)
s.Routes.POST("/buses/start", artNetDriver.Start)
s.Routes.POST("/buses/status", artNetDriver.Status)
s.Routes.POST("/buses/stop", artNetDriver.Stop)
s.Routes.POST("/buses/resubscribe", artNetDriver.Resubscribe)
s.Routes.GET("/", func(c *gin.Context) { s.Routes.GET("/", func(c *gin.Context) {
c.String(200, "ArtNet Driver WebSocket Server is running!") c.String(200, "ArtNet Driver WebSocket Server is running!")
}) })
// start http server go func() {
if err := s.ServeHttp(*port); err != nil { if err := s.ServeHttp(*wsPort); err != nil {
artNetDriver.Log.Error("main", "error http server "+err.Error()) artNetDriver.Log.Error("main", err)
panic(err)
} }
}()
artNetDriver.Log.Info("main", fmt.Sprintf("connect to server ws://%s:%d with id %s", *serverIp, *serverPort, DriverName))
if *start {
for busName, bus := range artNetDriver.Buses {
artNetDriver.Log.Info("main", fmt.Sprintf("starting bus %s", busName))
// start bus and listen to subscriptions
bus.Start(artNetDriver.Log)
}
}
// connect to server
for {
if err := artNetDriver.Connect(*serverIp, DriverName, *serverPort); err != nil {
artNetDriver.Log.Error("main", err)
}
artNetDriver.Log.Info("main", "next reconnecting attempt in 10 seconds")
time.Sleep(10 * time.Second)
}
} }

View File

@@ -1,11 +1,18 @@
package models package models
import ( import (
"context"
"errors"
"fmt" "fmt"
"log"
"net" "net"
"net/http"
"sync"
"time" "time"
json_data "gitea.tecamino.com/paadi/tecamino-json_data"
json_dataModels "gitea.tecamino.com/paadi/tecamino-json_data/models"
"gitea.tecamino.com/paadi/tecamino-logger/logging"
"github.com/gin-gonic/gin"
"github.com/tatsushid/go-fastping" "github.com/tatsushid/go-fastping"
) )
@@ -19,7 +26,12 @@ type Bus struct {
Name string `yaml:"name" json:"name"` Name string `yaml:"name" json:"name"`
Ip string `yaml:"ip" json:"ip"` Ip string `yaml:"ip" json:"ip"`
Port *int `yaml:"port" json:"port,omitempty"` Port *int `yaml:"port" json:"port,omitempty"`
Data *DMX `yaml:"-" json:"-"` DMX *DMX `yaml:"-" json:"-"`
Resubscribe *[]json_dataModels.Subscription `yaml:"-" json:"resubscribe"`
Watchdog context.CancelFunc `yaml:"-" json:"-"`
mu sync.Mutex `yaml:"-" json:"-"`
Reachable bool `yaml:"-" json:"-"`
Send bool `yaml:"-" json:"-"`
} }
// adds new Art-Net interface to driver port 0 = 6454 (default art-net) // adds new Art-Net interface to driver port 0 = 6454 (default art-net)
@@ -32,7 +44,7 @@ func NewBus(name, ip string, port int) *Bus {
Name: name, Name: name,
Ip: ip, Ip: ip,
Port: &port, Port: &port,
Data: NewDMXUniverse(), DMX: NewDMXUniverse(),
} }
return &i return &i
} }
@@ -80,7 +92,7 @@ func (b *Bus) Poll(interval time.Duration) error {
} }
}() }()
_, err = conn.Write(NewArtNetPackage(b.Data)) _, err = conn.Write(NewArtNetPackage(b.DMX))
if err != nil { if err != nil {
return err return err
} }
@@ -92,9 +104,40 @@ func (b *Bus) Poll(interval time.Duration) error {
} }
} }
// start polling dmx data in milliseconds 0 = aprox. 44Hertz // start bus
func (b *Bus) SendData() error { func (b *Bus) Start(log *logging.Logger) error {
// Send packet over UDP var ctx context.Context
ctx, b.Watchdog = context.WithCancel(context.Background())
go func() {
var interval time.Duration = 10 * time.Second
log.Info("bus.Start", fmt.Sprintf("device:%s ip:%s watchdog starting", b.Name, b.Ip))
for {
select {
case <-ctx.Done():
log.Info("bus.Start", fmt.Sprintf("device:%s ip:%s watchdog stopped", b.Name, b.Ip))
b.Reachable = false
return
default:
b.Reachable = false
if reached, err := isUDPReachable(b.Ip); err != nil {
log.Error("bus.Start", err)
interval = 5 * time.Second
} else if !reached {
log.Error("bus.Start", fmt.Sprintf("device:%s ip:%s not reached", b.Name, b.Ip))
interval = 5 * time.Second
} else {
b.Reachable = true
// send data as a heartbeat for she ArtNet Protocol
b.Send = true
log.Info("bus.Start", fmt.Sprintf("device:%s ip:%s watchdog running", b.Name, b.Ip))
interval = 30 * time.Second
}
time.Sleep(interval)
}
}
}()
conn, err := net.DialUDP("udp", nil, &net.UDPAddr{ conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
IP: net.ParseIP(b.Ip), IP: net.ParseIP(b.Ip),
Port: *b.Port, Port: *b.Port,
@@ -103,21 +146,79 @@ func (b *Bus) SendData() error {
if err != nil { if err != nil {
return err return err
} }
defer conn.Close()
go func() { go func() {
if reached, err := isUDPReachable(b.Ip); err != nil { ticker := time.NewTicker(23 * time.Millisecond)
log.Println(err)
return defer func() {
} else if !reached { b.Send = false
log.Println("device not reachable") conn.Close()
return ticker.Stop()
}
}() }()
_, err = conn.Write(NewArtNetPackage(b.Data)) for range ticker.C {
if !b.Send {
continue
}
b.Send = false
b.mu.Lock()
data := NewDMXUniverse()
copy(data.Data, b.DMX.GetDMXData())
b.mu.Unlock()
_, err = conn.Write(NewArtNetPackage(data))
if err != nil {
log.Error("bus.Start", err)
return
}
}
}()
return nil
}
// stop bus
func (b *Bus) Stop() {
if b.Watchdog != nil {
//cancels context
b.Watchdog()
b.Send = false
}
}
// status bus
func (b *Bus) Status() bool {
return b.Watchdog != nil
}
func (b *Bus) ParsePayload(c *gin.Context) error {
if err := c.BindJSON(b); err != nil {
r := json_data.NewResponse()
r.SetError()
r.SetMessage("json: " + err.Error())
c.JSON(http.StatusBadRequest, r)
return err return err
}
if b.Name == "" {
r := json_data.NewResponse()
r.SetError()
r.SetMessage("bus name missing")
c.JSON(http.StatusBadRequest, r)
return errors.New("bus name missing")
}
return nil
}
func (b *Bus) SetDMXData(channel uint, value uint8) error {
b.DMX.SetValue(channel, value)
b.Send = true
return nil
} }
func isUDPReachable(ip string) (recieved bool, err error) { func isUDPReachable(ip string) (recieved bool, err error) {

View File

@@ -1,12 +1,29 @@
package models package models
type DMX []byte import (
"sync"
)
type DMX struct {
Data []byte
mu sync.Mutex
}
func NewDMXUniverse() *DMX { func NewDMXUniverse() *DMX {
dmx := make(DMX, 512) return &DMX{
return &dmx Data: make([]byte, 512),
}
}
func (d *DMX) GetDMXData() (data []byte) {
d.mu.Lock()
data = d.Data
d.mu.Unlock()
return
} }
func (d *DMX) SetValue(channel uint, value uint8) { func (d *DMX) SetValue(channel uint, value uint8) {
(*d)[channel] = value d.mu.Lock()
d.Data[channel] = value
d.mu.Unlock()
} }

View File

@@ -1,23 +0,0 @@
package models
type JsonData struct {
Set *[]Set `json:"set,omitempty"`
Create *[]Bus `json:"create,omitempty"`
}
func NewRequest() *JsonData {
return &JsonData{}
}
func (r *JsonData) AddSet(bus string, address uint, value uint8) {
if r.Set == nil {
r.Set = &[]Set{}
}
*r.Set = append(*r.Set, Set{
Bus: bus,
Address: address,
Value: value,
})
}

View File

@@ -1,7 +0,0 @@
package models
type JsonResponse struct {
Error bool `json:"error,omitempty"`
Message string `json:"message,omitempty"`
Data string `json:"data,omitempty"`
}

View File

@@ -23,7 +23,7 @@ func NewArtNetPackage(data *DMX) Package {
packet.WriteByte(0x00) // Sequence packet.WriteByte(0x00) // Sequence
packet.WriteByte(0x00) // Physical packet.WriteByte(0x00) // Physical
binary.Write(packet, binary.BigEndian, uint16(0)) // Universe (net:subuni, usually 0) binary.Write(packet, binary.BigEndian, uint16(0)) // Universe (net:subuni, usually 0)
binary.Write(packet, binary.BigEndian, uint16(len(*data))) // Length binary.Write(packet, binary.BigEndian, uint16(len(data.Data))) // Length
packet.Write(*data) packet.Write(data.Data)
return packet.Bytes() return packet.Bytes()
} }

27
models/subscriptions.go Normal file
View File

@@ -0,0 +1,27 @@
package models
import (
json_dataModels "gitea.tecamino.com/paadi/tecamino-json_data/models"
"github.com/google/uuid"
)
type Subscriptions map[uuid.UUID][]Subscription
type Subscription struct {
Bus string
Address []uint
}
func NewSubscriptions() Subscriptions {
return make(Subscriptions)
}
func (s *Subscriptions) AddSubscription(uid uuid.UUID, drv *json_dataModels.Driver) {
subs := []Subscription{}
for _, bus := range drv.Buses {
sub := Subscription{Bus: bus.Name}
sub.Address = append(sub.Address, bus.Address...)
subs = append(subs, sub)
}
(*s)[uid] = subs
}

View File

@@ -1,51 +0,0 @@
package models
import (
"fmt"
"github.com/coder/websocket"
"github.com/gin-gonic/gin"
)
type Clients map[string]Client
type Client struct {
Connected *bool `json:"connected"`
SndConn *websocket.Conn `json:"-"` //sending connection
RvcConn *websocket.Conn `json:"-"` // revieving connection
}
func NewClients() Clients {
return make(Clients)
}
// Connect a recieving websocket connection
func (c *Clients) ConnectRecievingWsConnection(id string, ctx *gin.Context) (*websocket.Conn, error) {
conn, err := websocket.Accept(ctx.Writer, ctx.Request, &websocket.AcceptOptions{
OriginPatterns: []string{"*"},
})
if err != nil {
return nil, fmt.Errorf("error accept websocket client: %s", err)
}
b := true
(*c)[id] = Client{
Connected: &b,
RvcConn: conn,
}
return conn, nil
}
func (c *Clients) RemoveClient(id string) {
delete(*c, id)
}
func (c *Clients) GetClientPointer(id string) *bool {
return (*c)[id].Connected
}
func (c *Clients) DisconnectRecievingWsConnection(id string, code websocket.StatusCode, reason string) {
*(*c)[id].Connected = false
(*c)[id].RvcConn.Close(code, reason)
}

View File

@@ -1,9 +1,8 @@
package server package server
import ( import (
"fmt"
"artNet/cert" "artNet/cert"
"fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@@ -12,6 +11,17 @@ type Server struct {
Routes *gin.Engine Routes *gin.Engine
} }
type Send struct {
Set []Set `json:"subscribe"`
}
type Set struct {
Path string `json:"path"`
OnChange bool `json:"onChange"`
Depth int `json:"depth"`
Driver string `json:"driver"`
}
// Initialize new websocket server // Initialize new websocket server
func NewServer() *Server { func NewServer() *Server {
return &Server{ return &Server{

195
websocket/client.go Normal file
View File

@@ -0,0 +1,195 @@
package websocket
import (
"encoding/json"
"fmt"
"log"
"time"
json_dataModels "gitea.tecamino.com/paadi/tecamino-json_data/models"
"github.com/gorilla/websocket"
)
const (
// Time allowed to write a message to the peer.
writeWait = 40 * time.Second
// Time allowed to read the next pong message from the peer.
pongWait = 40 * time.Second
)
type Client struct {
ip string
port uint
Connected bool
conn *websocket.Conn
OnOpen func()
OnMessage func(data []byte)
OnClose func(code int, reason string)
OnError func(err error)
OnPing func()
OnPong func()
send chan []byte
unregister chan []byte
}
// Connect to websocket server
// ip: ip address of server
func NewClient(ip, id string, port uint) (*Client, error) {
url := fmt.Sprintf("ws://%s:%d/ws?id=%s", ip, port, id)
c := &Client{
ip: ip,
port: port,
Connected: true,
send: make(chan []byte, 256),
unregister: make(chan []byte, 256),
}
dialer := websocket.DefaultDialer
conn, resp, err := dialer.Dial(url, nil)
if err != nil {
if c.OnError != nil {
c.OnError(err)
}
return nil, fmt.Errorf("dial error %v (status %v)", err, resp)
}
c.conn = conn
//Setup control handlers
conn.SetPingHandler(func(appData string) error {
if c.OnPing != nil {
c.OnPing()
}
conn.SetReadDeadline(time.Now().Add(pongWait))
conn.SetWriteDeadline(time.Now().Add(pongWait))
if err := conn.WriteControl(websocket.PongMessage, []byte(appData), time.Now().Add(2*pongWait)); err != nil {
c.OnError(err)
return err
}
return nil
})
conn.SetPongHandler(func(appData string) error {
conn.SetReadDeadline(time.Now().Add(pongWait))
if c.OnPong != nil {
c.OnPong()
}
return nil
})
// Start reading messages from client
go c.Read()
go c.Write()
return c, nil
}
func (c *Client) Read() {
if c.OnOpen != nil {
c.OnOpen()
}
c.conn.SetReadDeadline(time.Now().Add(pongWait))
for c.Connected {
msgType, msg, err := c.conn.ReadMessage()
c.conn.SetReadDeadline(time.Now().Add(pongWait))
if err != nil {
c.handleError(fmt.Errorf("read error (ip:%s): %w", c.ip, err))
return
}
switch msgType {
case websocket.CloseMessage:
c.Close(websocket.CloseNormalClosure, "Client closed")
return
case websocket.TextMessage:
if c.OnMessage != nil {
c.OnMessage(msg)
} else {
log.Printf("Received message but no handler set (ip:%s): %s", c.ip, string(msg))
}
default:
log.Printf("Unhandled message type %d (ip:%s)", msgType, c.ip)
}
}
}
func (c *Client) Write() {
defer c.conn.Close()
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
for {
select {
case message, ok := <-c.send:
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
if !ok {
// The hub closed the channel.
if err := c.conn.WriteMessage(websocket.CloseMessage, []byte("ping")); err != nil {
c.handleError(err)
return
}
c.handleError(fmt.Errorf("server %s closed channel", c.ip))
return
} else {
if err := c.conn.WriteMessage(websocket.TextMessage, message); err != nil {
c.handleError(err)
return
}
}
case message := <-c.unregister:
c.conn.WriteMessage(websocket.CloseMessage, message)
c.Connected = false
close(c.send)
close(c.unregister)
return
}
}
}
func (c *Client) SendRequest(req *json_dataModels.Request) error {
if !c.Connected {
return nil
}
data, err := json.Marshal(*req)
if err != nil {
return err
}
c.send <- data
return nil
}
func (c *Client) ReadJsonData(data []byte) (json_dataModels.Response, error) {
var resp json_dataModels.Response
err := json.Unmarshal(data, &resp)
return resp, err
}
// Close connection to websocket server
func (c *Client) Close(code int, reason string) error {
closeMsg := websocket.FormatCloseMessage(code, reason)
select {
case c.unregister <- closeMsg: // Attempt to send
default: // If the channel is full, this runs
return fmt.Errorf("attempt close client socket failed")
}
if c.OnClose != nil {
c.OnClose(code, reason)
}
return nil
}
func (c *Client) handleError(err error) {
if c.OnError != nil {
c.OnError(err)
}
if err := c.Close(websocket.CloseInternalServerErr, err.Error()); err != nil {
if c.OnError != nil {
c.OnError(err)
} else {
log.Println("error:", err)
}
}
}