From 0a137c9d864f469e4e8e83c7b9dea280fce29fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Z=C3=BCrcher?= Date: Wed, 23 Apr 2025 21:53:01 +0200 Subject: [PATCH] major change of websocket dbmHandler structure --- args/args.go | 36 +++ auth/auth.go | 15 ++ cert/cert.go | 89 +++++++ cert/cert.pem | 19 ++ cert/key.pem | 27 ++ dbm/dbmHandler.go | 197 ++++++++++++++ dbm/publish.go | 34 +++ dbm/subscribe.go | 46 ++++ dbm/webSocket.go | 91 +++++++ dbmServer.log | 560 +++++++++++++++++++++++++++++++++++++++ drivers/artNet.go | 16 ++ drivers/drivers.go | 7 + go.mod | 8 +- go.sum | 10 + handlers/dbmHandler.go | 136 ---------- main.go | 42 ++- models/datapoints.go | 346 +++++++++++++++--------- models/driver.go | 6 + models/get.go | 6 + models/jsonData.go | 36 ++- models/jsonResponse.go | 10 + models/port.go | 6 + models/query.go | 6 + models/set.go | 8 + models/subscribe.go | 10 + models/subscribtion.go | 13 + models/type.go | 2 +- server/clients.go | 49 ---- server/jsonRequest.go | 27 ++ server/models/clients.go | 82 ++++++ server/routes.go | 13 - server/server.go | 24 +- server/webSocket.go | 62 ----- test/dbm_test.go | 59 ++++- test/helper.go | 2 +- 35 files changed, 1676 insertions(+), 424 deletions(-) create mode 100644 args/args.go create mode 100644 auth/auth.go create mode 100644 cert/cert.go create mode 100644 cert/cert.pem create mode 100644 cert/key.pem create mode 100644 dbm/dbmHandler.go create mode 100644 dbm/publish.go create mode 100644 dbm/subscribe.go create mode 100644 dbm/webSocket.go create mode 100644 dbmServer.log create mode 100644 drivers/artNet.go create mode 100644 drivers/drivers.go delete mode 100644 handlers/dbmHandler.go create mode 100644 models/driver.go create mode 100644 models/get.go create mode 100644 models/jsonResponse.go create mode 100644 models/port.go create mode 100644 models/query.go create mode 100644 models/set.go create mode 100644 models/subscribe.go create mode 100644 models/subscribtion.go delete mode 100644 server/clients.go create mode 100644 server/jsonRequest.go create mode 100644 server/models/clients.go delete mode 100644 server/routes.go delete mode 100644 server/webSocket.go diff --git a/args/args.go b/args/args.go new file mode 100644 index 0000000..23de884 --- /dev/null +++ b/args/args.go @@ -0,0 +1,36 @@ +package args + +import ( + "flag" + + "github.com/zuadi/tecamino-dbm/cert" + "github.com/zuadi/tecamino-dbm/models" +) + +type Args struct { + Port models.Port + Cert cert.Cert + RootDir string + DMAFile string + Debug bool +} + +func Init() *Args { + + a := Args{ + Cert: cert.Cert{ + Organization: *flag.String("org", "tecamino", "name of organization for certificate"), + CertFile: *flag.String("certFile", "./cert/cert.pem", "path of certfile"), + KeyFile: *flag.String("keyFile", "./cert/key.pem", "path of keyfile"), + }, + Port: models.Port{ + Http: *flag.Uint("http-port", 8100, "json server communication for http/ws"), + Https: *flag.Uint("https-port", 8101, "json server communication for http/wss"), + }, + RootDir: *flag.String("workingDir", "./", "working directory"), + DMAFile: *flag.String("dma", "/test/test", "dma file name"), + Debug: *flag.Bool("debug", false, "debug flag"), + } + flag.Parse() + return &a +} diff --git a/auth/auth.go b/auth/auth.go new file mode 100644 index 0000000..fc47c39 --- /dev/null +++ b/auth/auth.go @@ -0,0 +1,15 @@ +package auth + +import ( + "errors" + + "github.com/gin-gonic/gin" +) + +func GetIDFromAuth(c *gin.Context) (string, error) { + auth := c.GetHeader("Authorization") + if len(auth) > 7 && auth[:7] == "Bearer " { + return auth[7:], nil + } + return "", errors.New("authorization token missing") +} diff --git a/cert/cert.go b/cert/cert.go new file mode 100644 index 0000000..f7d89cf --- /dev/null +++ b/cert/cert.go @@ -0,0 +1,89 @@ +package cert + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "os" + "path" + "time" +) + +type Cert struct { + Organization string + CertFile string + KeyFile string +} + +// Initialize a new ssl certificate handler with organization name +func NewCertHandler(org string) *Cert { + return &Cert{ + Organization: org, + } +} + +// generates a new self signed ssl certificate foe localhost and development use +func (c *Cert) GenerateSelfSignedCert() error { + + // do not generate certs if they exist + // _, err := os.Stat(c.CertFile) + // _, err2 := os.Stat(c.KeyFile) + // if !os.IsNotExist(err) && !os.IsNotExist(err2) { + // return nil + // } + + priv, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return err + } + + serialNumber, _ := rand.Int(rand.Reader, big.NewInt(1<<62)) + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + CommonName: "localhost", + Organization: []string{c.Organization}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().Add(365 * 24 * time.Hour), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + DNSNames: []string{"localhost"}, + } + + certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv) + if err != nil { + return err + } + + if _, err := os.Stat(path.Dir(c.CertFile)); os.IsNotExist(err) { + os.MkdirAll(path.Dir(c.CertFile), 0666) + } + + certOut, err := os.Create(c.CertFile) + if err != nil { + return err + } + defer certOut.Close() + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: certDER}) + + if _, err := os.Stat(path.Dir(c.KeyFile)); os.IsNotExist(err) { + os.MkdirAll(path.Dir(c.KeyFile), 0666) + } + + keyOut, err := os.Create(c.KeyFile) + if err != nil { + return err + } + defer keyOut.Close() + + pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) + + return nil +} diff --git a/cert/cert.pem b/cert/cert.pem new file mode 100644 index 0000000..590fa19 --- /dev/null +++ b/cert/cert.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGzCCAgOgAwIBAgIIB2N9YzgUYFAwDQYJKoZIhvcNAQELBQAwJzERMA8GA1UE +ChMIdGVjYW1pbm8xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0yNTA0MTYxNjE2NDJa +Fw0yNjA0MTYxNjE2NDJaMCcxETAPBgNVBAoTCHRlY2FtaW5vMRIwEAYDVQQDEwls +b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDFOsJDNRd +wX25lB1QGCFhvjK+yN/pZHNtjQvHOcajON+Fhm56RWKbuKR4BQdNWF/uWe/wH6kC +xrYXzuAqJIzB/trFZ14whNblxjjxSmGhkMNFFTIIIdTICcoQu3+zzXxUc4s9ni4R +uGXudFB7uSZBx5x2TWrdFzBIfAuWfQfCwMWqiDoTH09T7DxJJyuvKf4yNPyDq+oe +4WGEXCpk3VBjggqYDGknMUzreEEa8JaIuDMFhQz4J4A5QGZOHOEyaP839cDblY31 +ot5Pd6PUAs5yvmvIZUCscW7bJH2vUqDC2tJ4WjkVkykULLIDIbe4Thi4//9oKzKV +fhP2TM9t+OeVAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr +BgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkq +hkiG9w0BAQsFAAOCAQEANfjOOkU/fl7Y2pJ6V+qKv9vdBb4nEpiNOnSl8sSgZP4r +wa1ArfALCPY1Gu+XDrwqcVLii511xT4cFuegxaOdu2+5j4+WjIR9ke/AeEuyNU1X +mm/xgBOSibxqSWVHTGhLLY4jwyU3GYx+4ODNmLoQ2eNQ7NDsCDAQq+OAbR+f5486 +j8AcrjEjWI5Nh9p4DiqEA1DwNCKnpYcw8QBiawNFli3mvFSu1KSTG5UGM8vwzCOu +nk2GtBvhDODVhDuM3BjqAmT7xbIJGXdW25+FG9++Vc+36LVSJVxMeOWx4u07Ggqk +4y39spP+xOzXegFCJXu+OkxjvZ7mRGaPp1zKeaoVVQ== +-----END CERTIFICATE----- diff --git a/cert/key.pem b/cert/key.pem new file mode 100644 index 0000000..4e0cc3a --- /dev/null +++ b/cert/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAwxTrCQzUXcF9uZQdUBghYb4yvsjf6WRzbY0LxznGozjfhYZu +ekVim7ikeAUHTVhf7lnv8B+pAsa2F87gKiSMwf7axWdeMITW5cY48UphoZDDRRUy +CCHUyAnKELt/s818VHOLPZ4uEbhl7nRQe7kmQcecdk1q3RcwSHwLln0HwsDFqog6 +Ex9PU+w8SScrryn+MjT8g6vqHuFhhFwqZN1QY4IKmAxpJzFM63hBGvCWiLgzBYUM ++CeAOUBmThzhMmj/N/XA25WN9aLeT3ej1ALOcr5ryGVArHFu2yR9r1KgwtrSeFo5 +FZMpFCyyAyG3uE4YuP//aCsylX4T9kzPbfjnlQIDAQABAoIBAAfekaahTyj8puMA +39489ifFVOW2U7X/nw2Y+Xpb0P2azAV//HfpOAbN5kopjCrC35SaKZNdq5ZllRXi +LgL1MeQBF5ElnNQtn6YDa4U5E2j0xOb5NmkpxQKDk2E+aj92EOEu2G78l24pxDVG +Zhkl/GGXheIna4hBdbZCCFoI1b9o4w1iFLsc1MJxDszRqxD5M9uZ4YMtI6rGrUtx +xAeYqSvEnMfYHRXbQlFhZF2F9suW+rsUpL264bQ7OPMNsQAtsu5Q9x1dEMuuj+NX +4Tavu78CyVGjBrBrCDJ0P/r/EyK72n8noZBto+Xzk4S3PjxWfb0HVsHYVaeq2O+I +3et0C+cCgYEA8fd9NB31KU2sjJtNWBq7Vs/6F6IPlZat6FCHFDjt0wCm9LK0gMii +l79mJyKwI3i2K3HKVDrpuplYBJ9Z7XQCQin6844Bcp0OizIHX4OYf1CSWsQbJZFm +SdrUwKyijYR9nHKmjbFa+PWFh/HD630kkkLEFt+Ti+CsdNe0+Hmfc7cCgYEAzmVS +JX9DZVZMvEUPanPrHI1vAkFTd9YSGPx088yadomF8cL6QbYXUFq+ZMq3st70S+qr +XxBXjjmDTuE8tLepZemIIM9UC6AQ+2IE/RiThLyKrhk9b/7myqWSz9cY8/SqMOsW +015U4Jc+KRS1/Tse8Y7tyzHFlthACoKX5HrxNxMCgYBuOQ1B1nu9ivKVQpGjFtpM +G4WTinGK9Q7XiwddgOlleyCSy21KVRssATZpkXWnUu+5Lqa6Y/Pg2sWrpWNztarp +tPHqTMAAE+dyJSISsoGfTXa9/iNXo7py3kqYUovh537I67lPRoFoc3+Wg915wpIM +RnnI6aPuzjQBLdn0boLiVQKBgQC/cv6y54ylmFqPnOPC1Am3r33UMrJxC3I4GR2G +9DgnUkOb0Ud/4p9XmwTWy6+ATQ2AygnyoV8F/1VMuuMrot2QOgJapNaJ/g0ikXad +KsnTq2xcN+9kTqbYPKOlBRoRWNbxj2/Z2ruSpNg1FRAG+GsomHL9M4rb9HXbCe5J +Mr1DXwKBgQDmsYhXErxHiQ0jwDgqFmcKRjm/UvEGzuJ1wMZzCBU1GNAOid25QHvI +VIsJjYT6idRNHNxFr7AoW5bpTRMsfh8nLD4VfK9k2HbiRbZmDhC03zxXpgbzRWrp +bWTuoTIidPr5pt6XFlPU9NYPfgpGJvcCSbE15UQBd4DLts+UcZZ3Zw== +-----END RSA PRIVATE KEY----- diff --git a/dbm/dbmHandler.go b/dbm/dbmHandler.go new file mode 100644 index 0000000..3883913 --- /dev/null +++ b/dbm/dbmHandler.go @@ -0,0 +1,197 @@ +package dbm + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "os" + "runtime" + "sync" + "time" + + "github.com/tecamino/tecamino-logger/logging" + "github.com/zuadi/tecamino-dbm/args" + "github.com/zuadi/tecamino-dbm/models" + serverModels "github.com/zuadi/tecamino-dbm/server/models" +) + +type DBMHandler struct { + filePath string + DB models.Datapoint + Clients serverModels.Clients + sync.RWMutex + Log *logging.Logger + arg *args.Args +} + +// initialze new Database Manager +// it will call cli arguments +func NewDbmHandler(a *args.Args) (*DBMHandler, error) { + + logger, err := logging.NewLogger("dbmServer.log", &logging.Config{ + MaxSize: 1, + MaxBackup: 3, + MaxAge: 28, + Debug: a.Debug, + TerminalOut: true, + }) + + if err != nil { + return nil, err + } + logger.Info("main", "start dma") + + // Initialize dtabase manager handler + dmaHandler := DBMHandler{ + arg: a, + filePath: fmt.Sprintf("%s/%s.dma", a.RootDir, a.DMAFile), + Log: logger, + Clients: serverModels.NewClients(), + } + + // initialize system datapoint and periodically update it + if err := dmaHandler.AddSystemDps(); err != nil { + return nil, err + } + + // check if dtabase file exists to load data + if _, err := os.Stat(dmaHandler.filePath); err == nil { + + f, err := os.Open(dmaHandler.filePath) + if err != nil { + return nil, err + } + defer f.Close() + + // read in dtaabase file content + scanner := bufio.NewScanner(f) + + for scanner.Scan() { + dp := models.Datapoint{} + err = json.Unmarshal(scanner.Bytes(), &dp) + if err != nil { + return nil, err + } + dmaHandler.ImportDatapoints(&dp) + } + } + + return &dmaHandler, nil +} + +func (d *DBMHandler) SaveDb() (err error) { + f, err := os.OpenFile(d.filePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666) + if err != nil { + return err + } + defer f.Close() + + for _, dp := range d.DB.GetAllDatapoints(0) { + b, er := json.Marshal(dp) + if er != nil { + return er + } + + _, err = f.Write(b) + if err != nil { + return + } + _, err = f.Write([]byte("\n")) + if err != nil { + return + } + } + return +} + +func (d *DBMHandler) CreateDatapoint(typ models.Type, value any, right models.Rights, path string) error { + if err := d.DB.CreateDatapoint(typ, value, right, path); err != nil { + return err + } + dp := d.QueryDatapoints(1, "System:Datapoints") + d.UpdateDatapointValue("System:Datapoints", dp[0].GetValueUint64()+1) + return nil +} + +func (d *DBMHandler) ImportDatapoints(dps ...*models.Datapoint) error { + for _, dp := range dps { + err := d.DB.ImportDatapoint(dp, dp.Path) + if err != nil { + return err + } + dp := d.QueryDatapoints(1, "System:Datapoints") + d.UpdateDatapointValue("System:Datapoints", dp[0].GetValueUint64()+1) + } + return nil +} + +func (d *DBMHandler) UpdateDatapointValue(path string, value any) error { + return d.DB.UpdateDatapointValue(value, path) +} + +func (d *DBMHandler) RemoveDatapoint(path string) error { + if err := d.DB.RemoveDatapoint(path); err != nil { + return err + } + dp := d.QueryDatapoints(1, "System:Datapoints") + d.UpdateDatapointValue("System:Datapoints", dp[0].GetValueUint64()+1) + return nil +} + +func (d *DBMHandler) QueryDatapoints(depth int, key string) []*models.Datapoint { + return d.DB.QueryDatapoints(depth, key) +} + +func (d *DBMHandler) AddSystemDps() (err error) { + tim := "System:Time" + memory := "System:UsedMemory" + var m runtime.MemStats + var mOld uint64 + var tOld int64 + + err = d.DB.CreateDatapoint(models.LOU, 0, models.Read, "System:Datapoints") + if err != nil { + d.Log.Error("dmb.Handler.AddSystemDps.CreateDatapoint", err.Error()) + return + } + + err = d.DB.CreateDatapoint(models.STR, nil, models.Read, tim) + if err != nil { + d.Log.Error("dmb.Handler.AddSystemDps.CreateDatapoint", err.Error()) + return + } + err = d.DB.CreateDatapoint(models.STR, nil, models.Read, memory) + if err != nil { + d.Log.Error("dmb.Handler.AddSystemDps.CreateDatapoint", err.Error()) + return + } + + go func() { + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute) + defer cancel() + + for { + t := time.Now().UnixMilli() + if tOld != t { + if er := d.DB.UpdateDatapointValue(t, tim); er != nil { + d.Log.Error("dmb.Handler.AddSystemDps.UpdateDatapointValue", er.Error()) + } + d.Publish(ctx, OnChange, tim, t) + tOld = t + } + runtime.ReadMemStats(&m) + if m.Sys != mOld { + mem := fmt.Sprintf("%.2f MB", float64(m.Sys)/1024/1024) + if er := d.DB.UpdateDatapointValue(mem, memory); er != nil { + d.Log.Error("dmb.Handler.AddSystemDps.UpdateDatapointValue", er.Error()) + } + d.Publish(ctx, OnChange, memory, mem) + mOld = m.Sys + } + time.Sleep(1 * time.Second) + } + }() + return +} diff --git a/dbm/publish.go b/dbm/publish.go new file mode 100644 index 0000000..5bb906b --- /dev/null +++ b/dbm/publish.go @@ -0,0 +1,34 @@ +package dbm + +import ( + "context" + + "github.com/coder/websocket/wsjson" + "github.com/zuadi/tecamino-dbm/models" +) + +func (d *DBMHandler) Publish(ctx context.Context, eventType, path string, value any) error { + d.RLock() + defer d.RUnlock() + + for _, dp := range d.DB.QueryDatapoints(1, path) { + for id, pub := range dp.Subscribtions { + if client, ok := d.Clients[id]; !ok { + delete(dp.Subscribtions, id) + } else { + if pub.OnChange { + err := wsjson.Write(ctx, client.Conn, models.JsonResponse{ + Event: OnChange, + Path: dp.Path, + Value: value, + }) + if err != nil { + d.Log.Error("publish.Publish", err.Error()) + return err + } + } + } + } + } + return nil +} diff --git a/dbm/subscribe.go b/dbm/subscribe.go new file mode 100644 index 0000000..d045ed1 --- /dev/null +++ b/dbm/subscribe.go @@ -0,0 +1,46 @@ +package dbm + +import ( + "github.com/coder/websocket/wsjson" + "github.com/zuadi/tecamino-dbm/models" + "golang.org/x/net/context" +) + +func (d *DBMHandler) Subscribe(ctx context.Context, sub *models.Subscribe, id string) { + d.RLock() + defer d.RUnlock() + + for _, dp := range d.DB.QueryDatapoints(sub.Depth, sub.Path) { + if sub.Driver != nil { + if dp.Drivers == nil { + continue + } else if _, ok := dp.Drivers[*sub.Driver]; !ok { + continue + } + } + + dp.AddSubscribtion(id, sub) + + err := wsjson.Write(ctx, d.Clients[id].Conn, models.JsonResponse{ + Event: OnCreate, + Path: dp.Path, + Value: dp.Value, + }) + if err != nil { + d.Log.Error("subscribe.Subscribe", err.Error()) + } + } +} + +func (d *DBMHandler) Unsubscribe(ctx context.Context, sub *models.Subscribe, id string) error { + d.RLock() + defer d.RUnlock() + + for _, dp := range d.DB.QueryDatapoints(sub.Depth, sub.Path) { + if _, ok := dp.Subscribtions[id]; !ok { + continue + } + dp.RemoveSubscribtion(id) + } + return nil +} diff --git a/dbm/webSocket.go b/dbm/webSocket.go new file mode 100644 index 0000000..4cfebe9 --- /dev/null +++ b/dbm/webSocket.go @@ -0,0 +1,91 @@ +package dbm + +import ( + "context" + "fmt" + + "github.com/coder/websocket" + "github.com/coder/websocket/wsjson" + "github.com/gin-gonic/gin" + "github.com/zuadi/tecamino-dbm/models" +) + +const ( + OnCreate = "onCreate" + OnChange = "onChange" + OnDelete = "onDelete" +) + +func (d *DBMHandler) WebSocket(c *gin.Context) { + // id, err := auth.GetIDFromAuth(c) + // if err != nil { + // d.Log.Error("dbmHandler.webSocket.Websocket", "error GetIDFromAuth: "+err.Error()) + // return + // } + id := "test" + + d.Log.Debug("dbmHandler.webSocket.Websocket", "authorization id token: "+id) + + ctx, cancel := context.WithCancel(c.Request.Context()) + defer cancel() + err := d.Clients.ConnectRecievingWsConnection(id, c) + defer d.Clients.RemoveClient(id) + if err != nil { + d.Log.Error("dbmHandler.webSocket.Websocket", "error connecting recieving websocket conection: "+err.Error()) + return + } + defer d.Clients.DisconnectWsConnection(id, websocket.StatusInternalError, "Internal error") + + //Read loop + for { + request, err := d.readJsonData(ctx, id) + if err != nil { + break + } + + // Subscribe + + if request.Subscribe != nil { + go func() { + for _, sub := range *request.Subscribe { + d.Subscribe(ctx, &sub, id) + } + }() + } + + // Unsubscribe + if request.Unsubscribe != nil { + for _, unsub := range *request.Unsubscribe { + err = d.Unsubscribe(ctx, &unsub, id) + if err != nil { + d.Log.Error("dbmHandler.WebSocket unsubscribe", err) + break + } + } + } + + if err != nil { + break + } + } +} + +func (d *DBMHandler) readJsonData(ctx context.Context, id string) (*models.JsonData, error) { + var request models.JsonData + err := wsjson.Read(ctx, d.Clients[id].Conn, &request) + if err != nil { + code := websocket.CloseStatus(err) + + switch code { + case websocket.StatusNormalClosure, + websocket.StatusGoingAway, + websocket.StatusNoStatusRcvd: + d.Log.Info("webSocket.readJsonData", fmt.Sprintf("WebSocket closed: %v (code: %v)\n", err, code)) + return nil, err + default: + d.Log.Error("webSocket.readJsonData", fmt.Sprintf("WebSocket read error: %v (code: %v)\n", err, code)) + return nil, err + } + } + return &request, nil +} diff --git a/dbmServer.log b/dbmServer.log new file mode 100644 index 0000000..a30ac44 --- /dev/null +++ b/dbmServer.log @@ -0,0 +1,560 @@ +{"level":"info","timestamp":"2025-04-23T08:02:12.084","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:02:12.348","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps"} +{"level":"info","timestamp":"2025-04-23T08:02:12.348","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:02:12.348","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:02:12.566","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps"} +{"level":"error","timestamp":"2025-04-23T08:02:13.834","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps"} +{"level":"error","timestamp":"2025-04-23T08:02:14.031","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps"} +{"level":"info","timestamp":"2025-04-23T08:03:55.063","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:03:55.346","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:03:55.347","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:03:55.347","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:03:55.554","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:03:56.853","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:03:57.041","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:05:18.061","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:05:18.332","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:05:18.332","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:05:18.332","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:05:18.538","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:05:19.842","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:05:21.010","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:06:19.179","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:06:19.439","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:06:19.439","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:06:19.439","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:06:19.617","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:06:20.861","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:07:45.597","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:07:45.879","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:07:45.879","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:07:45.879","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:07:46.160","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:07:47.447","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:08:23.028","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:08:23.295","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:08:23.296","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:08:23.296","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:08:23.474","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:08:24.714","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:09:06.062","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:09:06.326","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:09:06.326","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:09:06.326","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:09:06.534","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:09:07.841","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:09:08.005","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:09:29.794","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:09:30.036","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:09:30.037","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:09:30.037","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:09:30.208","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:09:31.467","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:10:00.754","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:10:01.044","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:10:01.045","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:10:01.045","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:10:01.248","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:10:02.507","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:11:50.298","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:11:50.570","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:11:50.570","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:11:50.570","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:11:50.768","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:11:52.046","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:11:52.222","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:14:17.351","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:14:48.221","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:15:28.790","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:16:02.029","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:16:02.037","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:16:02.037","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:16:02.037","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:16:02.037","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:16:03.038","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:16:04.038","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:16:05.038","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:17:30.755","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:17:30.765","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:17:30.765","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:17:30.765","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:17:30.766","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:17:31.767","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:17:31.768","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:17:32.768","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:17:33.770","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:18:37.367","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:18:37.374","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:18:37.374","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:18:37.374","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:18:37.375","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:18:38.376","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:18:38.377","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:19:02.577","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:19:02.584","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:19:02.585","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:19:02.585","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:19:02.586","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:19:03.588","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:22:01.802","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:22:01.809","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:22:01.809","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:22:01.809","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:22:01.809","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:22:02.811","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:22:02.811","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:23:09.752","msg":"start dma","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:23:09.759","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:23:09.759","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:23:09.759","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:23:09.760","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:23:10.762","msg":"datapoint 'System:Time' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"error","timestamp":"2025-04-23T08:23:10.763","msg":"datapoint 'System:UsedMemory' not found","caller":"dmb.Handler.AddSystemDps.UpdateDatapointValue"} +{"level":"info","timestamp":"2025-04-23T08:24:46.880","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:24:46.889","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:24:46.889","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:25:26.842","msg":"error GetIDFromAuth: authorization token missing","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T08:25:35.344","msg":"error GetIDFromAuth: authorization token missing","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T08:26:04.855","msg":"error GetIDFromAuth: authorization token missing","caller":"artNet.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T08:26:53.623","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:26:53.630","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:26:53.630","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:26:57.252","msg":"error GetIDFromAuth: authorization token missing","caller":"artNet.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T08:28:05.758","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:28:05.765","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:28:05.765","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:28:22.491","msg":"error GetIDFromAuth: authorization token missing","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T08:36:30.757","msg":"error GetIDFromAuth: authorization token missing","caller":"artNet.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T08:37:09.352","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:37:09.360","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:37:09.360","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:38:28.101","msg":"read error:failed to read JSON message: failed to get reader: use of closed network connection","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T08:38:34.491","msg":"read error:failed to read JSON message: failed to get reader: use of closed network connection","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T08:38:36.178","msg":"read error:failed to read JSON message: failed to get reader: use of closed network connection","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T08:38:37.265","msg":"read error:failed to read JSON message: failed to get reader: use of closed network connection","caller":"artNet.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T08:39:48.364","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:39:48.371","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:39:48.371","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:39:52.813","msg":"read error:failed to read JSON message: failed to unmarshal JSON: invalid character ':' after array element","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T08:39:56.608","msg":"read error:failed to read JSON message: failed to unmarshal JSON: invalid character ':' after array element","caller":"artNet.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T08:41:31.574","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:41:31.582","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:41:31.582","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:42:38.600","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:42:38.607","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:42:38.607","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:43:56.663","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:43:56.669","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:43:56.669","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:44:19.860","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:44:19.868","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:44:19.868","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:46:59.540","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:46:59.548","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T08:46:59.548","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T08:48:07.605","msg":"read error:failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\"","caller":"artNet.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T09:36:14.713","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:36:14.720","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:36:14.720","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:38:52.202","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:38:52.209","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:38:52.209","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:41:53.037","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:41:53.044","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:41:53.044","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:43:35.980","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:43:35.986","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:43:35.986","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T09:44:07.385","msg":"read error:failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\"","caller":"artNet.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T09:45:56.213","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:45:56.221","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:45:56.221","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:47:40.184","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:47:40.193","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T09:47:40.193","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:05:31.052","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:05:31.304","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:05:31.304","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:12:24.313","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:12:24.571","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:12:24.571","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T12:20:28.805","msg":"read error:failed to read JSON message: failed to get reader: use of closed network connection","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T12:20:48.504","msg":"read error:failed to read JSON message: failed to get reader: use of closed network connection","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T12:22:08.000","msg":"read error:failed to read JSON message: failed to get reader: use of closed network connection","caller":"artNet.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T12:24:00.791","msg":"read error:failed to read JSON message: failed to get reader: use of closed network connection","caller":"artNet.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T12:24:25.263","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:24:25.517","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:24:25.517","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:42:11.099","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:42:11.364","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:42:11.364","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T12:45:18.145","msg":"read error:failed to read JSON message: failed to get reader: context deadline exceeded","caller":"dbmHandler.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T12:48:43.020","msg":"read error:failed to read JSON message: failed to get reader: context deadline exceeded","caller":"dbmHandler.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T12:49:40.208","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:49:40.486","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:49:40.486","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:55:05.401","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:55:05.671","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T12:55:05.671","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T12:58:11.602","msg":"read error:failed to read JSON message: failed to get reader: context deadline exceeded","caller":"dbmHandler.webSocket.Websocket"} +{"level":"error","timestamp":"2025-04-23T13:08:06.888","msg":"read error:failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\"","caller":"dbmHandler.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T13:09:04.736","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:09:05.042","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:09:05.042","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T13:09:10.775","msg":"read error:failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\"","caller":"dbmHandler.webSocket.Websocket"} +{"level":"info","timestamp":"2025-04-23T13:22:27.316","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:22:27.673","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:22:27.673","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:22:48.485","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"error","timestamp":"2025-04-23T13:23:26.354","msg":"failed to write JSON message: failed to marshal JSON: failed to write msg: use of closed network connection","caller":"publish.Publish"} +{"level":"info","timestamp":"2025-04-23T13:23:40.800","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:23:41.121","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:23:41.121","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:23:45.546","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T13:26:22.134","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:26:22.395","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:26:22.396","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:26:27.705","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T13:28:49.207","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:28:49.481","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:28:49.481","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:28:54.557","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"error","timestamp":"2025-04-23T13:28:56.630","msg":"WebSocket read error: failed to read JSON message: failed to get reader: use of closed network connection (code: StatusCode(-1))\n","caller":"webSocket.readJsonData"} +{"level":"error","timestamp":"2025-04-23T13:28:58.939","msg":"WebSocket read error: failed to read JSON message: failed to get reader: use of closed network connection (code: StatusCode(-1))\n","caller":"webSocket.readJsonData"} +{"level":"error","timestamp":"2025-04-23T13:29:00.193","msg":"WebSocket read error: failed to read JSON message: failed to get reader: use of closed network connection (code: StatusCode(-1))\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T13:29:48.517","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:29:48.779","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:29:48.779","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:29:55.335","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T13:30:20.462","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:30:20.783","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:30:20.783","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:30:28.263","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T13:30:36.849","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T13:38:08.451","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:38:08.756","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:38:08.756","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:39:36.799","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:39:37.075","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:39:37.075","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:40:44.651","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:40:44.973","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:40:44.973","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:41:54.047","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:41:54.352","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:41:54.352","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:54:32.110","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:54:32.407","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:54:32.408","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:55:49.618","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:55:49.900","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:55:49.900","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:56:34.529","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:56:34.860","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:56:34.860","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:57:00.522","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:57:00.823","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:57:00.823","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:57:22.103","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:57:22.367","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T13:57:22.367","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:05.868","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:06.135","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:06.135","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:29.804","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:30.055","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:30.055","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:39.419","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:39.753","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:00:39.753","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:01:26.135","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:01:26.431","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:01:26.431","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:02:07.851","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:02:08.136","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:02:08.136","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:02:36.620","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:02:36.922","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:02:36.922","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:03:44.025","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:03:44.292","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:03:44.292","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:04:32.978","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T14:05:24.731","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:05:25.011","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:05:25.011","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:06:00.079","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T14:06:06.589","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T14:06:46.334","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:06:46.629","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:06:46.629","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:09:01.329","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:09:01.638","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:09:01.638","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:11:10.493","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:11:10.793","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:11:10.798","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:14:33.137","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:14:33.407","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:14:33.407","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:16:47.484","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:16:47.778","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:16:47.778","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:18:29.845","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:18:30.159","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:18:30.159","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:19:09.524","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:19:09.847","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:19:09.847","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:21:05.124","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:21:05.395","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:21:05.395","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:21:38.789","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:21:39.079","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:21:39.079","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:22:02.321","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:22:02.597","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:22:02.598","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:22:17.086","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:22:17.405","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:22:17.405","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:24:08.156","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:24:08.431","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:24:08.431","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:41:40.222","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:41:40.522","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:41:40.522","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:42:33.463","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:42:33.816","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:42:33.816","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:42:55.048","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:42:55.333","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:42:55.333","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:44:23.194","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:44:23.478","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:44:23.478","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:48:31.721","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:48:32.003","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:48:32.003","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:50:44.138","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:50:44.434","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:50:44.434","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:52:27.355","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:52:27.657","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T14:52:27.657","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:13:04.232","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:13:04.511","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:13:04.511","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:14:14.199","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:14:14.529","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:14:14.529","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:14:47.927","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:14:48.213","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:14:48.213","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:15:55.558","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:15:55.856","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:15:55.856","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:18:06.585","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:18:06.896","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:18:06.896","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:19:48.203","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:19:48.560","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:19:48.560","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:21:47.452","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:21:47.731","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:21:47.731","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:22:26.167","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:22:26.453","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:22:26.453","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:24:30.840","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:24:31.154","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:24:31.154","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:25:21.097","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:25:21.448","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:25:21.448","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:26:19.462","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:26:19.770","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:26:19.770","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:27:56.148","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:27:56.425","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:27:56.425","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:29:19.768","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:29:20.055","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:29:20.055","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:30:37.990","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:30:38.306","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:30:38.306","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:33:04.614","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:33:04.872","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:33:04.872","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:33:42.125","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:33:42.405","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:33:42.405","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:35:29.364","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:35:29.645","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:35:29.645","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:36:27.148","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:36:27.439","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:36:27.439","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:37:26.182","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:37:26.468","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:37:26.468","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:39:44.950","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:39:45.259","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:39:45.259","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:42:56.735","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:42:57.000","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:42:57.000","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:44:07.340","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:44:07.617","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:44:07.617","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:49:39.704","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:49:40.013","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:49:40.013","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:50:33.848","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:50:58.614","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:51:15.662","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:51:29.317","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T15:51:37.704","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:08:38.719","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:09:00.713","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:09:01.005","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:09:01.005","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:13:13.940","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:13:14.214","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:13:14.214","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:15:14.715","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:15:15.003","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:15:15.003","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:15:44.461","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:15:44.726","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:15:44.726","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:16:37.294","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:16:37.634","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:16:37.634","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:17:12.883","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:17:13.188","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:17:13.188","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:17:36.481","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:17:36.753","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:17:36.753","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:18:35.785","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:18:36.060","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:18:36.060","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:19:18.201","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:19:18.472","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:19:18.472","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:20:00.617","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:20:00.931","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:20:00.931","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:20:29.238","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:20:29.523","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:20:29.523","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:21:12.711","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:21:12.995","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:21:12.995","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:21:46.808","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:21:47.089","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:21:47.089","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:24:34.513","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:24:34.766","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:24:34.767","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:25:08.863","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:25:09.125","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:25:09.125","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:25:20.500","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:25:20.758","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:25:20.758","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:26:35.367","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:26:35.640","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:26:35.640","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:27:24.719","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:27:25.026","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:27:25.026","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:28:45.695","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:28:45.978","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:28:45.978","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:33:31.621","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:33:31.908","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:33:31.908","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:40:29.559","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:40:29.827","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:40:29.827","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:40:54.193","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:40:54.523","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:40:54.525","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:41:50.625","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T16:42:41.188","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:42:41.483","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:42:41.483","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:43:26.920","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:43:27.222","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:43:27.222","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:43:46.937","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:43:47.210","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:43:47.211","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:43:57.348","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T16:44:18.299","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:44:18.584","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:44:18.584","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:47:28.208","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:47:28.525","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:47:28.525","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:55:08.835","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:55:09.098","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:55:09.098","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:56:37.960","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:56:38.242","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:56:38.242","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:58:01.228","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:58:01.513","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:58:01.513","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:59:20.426","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:59:20.707","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T16:59:20.707","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:12:15.307","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:12:16.391","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:12:16.391","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:13:58.319","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T17:14:39.566","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:14:40.644","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:14:40.644","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:15:48.856","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:15:50.428","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:15:50.428","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:16:59.161","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T17:17:54.578","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:17:55.858","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:17:55.858","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:19:30.348","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"error","timestamp":"2025-04-23T17:19:30.348","msg":"failed to write JSON message: failed to marshal JSON: failed to write msg: use of closed network connection","caller":"subscribe.Subscribe"} +{"level":"info","timestamp":"2025-04-23T17:19:43.223","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:19:44.757","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:19:44.757","msg":"http listen on 8100","caller":"main"} +{"level":"error","timestamp":"2025-04-23T17:20:07.653","msg":"WebSocket read error: failed to read JSON message: failed to unmarshal JSON: invalid character ',' looking for beginning of value (code: StatusCode(-1))\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T17:20:17.815","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusNoStatusRcvd and reason = \"\" (code: StatusNoStatusRcvd)\n","caller":"webSocket.readJsonData"} +{"level":"info","timestamp":"2025-04-23T17:22:06.954","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:22:08.494","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:22:08.494","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:22:49.284","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:22:50.545","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:22:50.545","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:23:21.885","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:23:23.201","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:23:23.201","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:24:20.189","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:24:21.524","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:24:21.524","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:31:09.915","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:31:10.999","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:31:10.999","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:31:48.721","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:31:49.832","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:31:49.832","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:33:31.847","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:33:32.898","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:33:32.898","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:35:01.005","msg":"start dma","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:35:02.192","msg":"https listen on 8101","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:35:02.192","msg":"http listen on 8100","caller":"main"} +{"level":"info","timestamp":"2025-04-23T17:36:14.186","msg":"WebSocket closed: failed to read JSON message: failed to get reader: received close frame: status = StatusGoingAway and reason = \"\" (code: StatusGoingAway)\n","caller":"webSocket.readJsonData"} diff --git a/drivers/artNet.go b/drivers/artNet.go new file mode 100644 index 0000000..f84fe4a --- /dev/null +++ b/drivers/artNet.go @@ -0,0 +1,16 @@ +package drivers + +type ArtNetDriver struct { + Bus string + Addresses []uint +} + +func NewArtNetDriver(bus string) *ArtNetDriver { + return &ArtNetDriver{ + Bus: bus, + } +} + +func (a *ArtNetDriver) AddAddress(adr uint) { + a.Addresses = append(a.Addresses, adr) +} diff --git a/drivers/drivers.go b/drivers/drivers.go new file mode 100644 index 0000000..e2dbb87 --- /dev/null +++ b/drivers/drivers.go @@ -0,0 +1,7 @@ +package drivers + +type Drivers []Driver + +type Driver interface { + AddAddress() +} diff --git a/go.mod b/go.mod index 3e9e93b..cc64156 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,12 @@ -module "github.com/zuadi/tecamino-dbm.git" +module github.com/zuadi/tecamino-dbm go 1.24.0 require ( github.com/coder/websocket v1.8.13 github.com/gin-gonic/gin v1.10.0 + github.com/tecamino/tecamino-logger v0.2.0 + golang.org/x/net v0.25.0 ) require ( @@ -27,11 +29,13 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect + go.uber.org/multierr v1.10.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.23.0 // indirect - golang.org/x/net v0.25.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 890a621..da0c828 100644 --- a/go.sum +++ b/go.sum @@ -61,10 +61,18 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tecamino/tecamino-logger v0.2.0 h1:NPH/Gg9qRhmVoW8b39i1eXu/LEftHc74nyISpcRG+XU= +github.com/tecamino/tecamino-logger v0.2.0/go.mod h1:0M1E9Uei/qw3e3WA1x3lBo1eP3H5oeYE7GjYrMahnj8= 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/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +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/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +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/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= @@ -84,6 +92,8 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW 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/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handlers/dbmHandler.go b/handlers/dbmHandler.go deleted file mode 100644 index 205ee5e..0000000 --- a/handlers/dbmHandler.go +++ /dev/null @@ -1,136 +0,0 @@ -package handlers - -import ( - "bufio" - "encoding/json" - "fmt" - "log" - "os" - "runtime" - "time" - - "github.com/zuadi/tecamino-dbm.git/models" - "github.com/zuadi/tecamino-dbm.git/server" -) - -type DBMHandler struct { - filePath string - DB models.Datapoint - Server *server.Server -} - -func NewDbmHandler(rootDir, dbName string) (*DBMHandler, error) { - dmaHandler := DBMHandler{ - filePath: fmt.Sprintf("%s/%s.dma", rootDir, dbName), - } - - if _, err := os.Stat(dmaHandler.filePath); err == nil { - - f, err := os.Open(dmaHandler.filePath) - if err != nil { - return nil, err - } - defer f.Close() - - scanner := bufio.NewScanner(f) - - for scanner.Scan() { - dp := models.Datapoint{} - err = json.Unmarshal(scanner.Bytes(), &dp) - if err != nil { - return nil, err - } - dmaHandler.ImportDatapoints(&dp) - } - } - if err := dmaHandler.AddSystemDps(); err != nil { - return nil, err - } - - return &dmaHandler, nil -} - -func (d *DBMHandler) SaveDb() (err error) { - f, err := os.OpenFile(d.filePath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0666) - if err != nil { - return err - } - defer f.Close() - - for _, dp := range d.DB.GetAllDatapoints() { - b, er := json.Marshal(dp) - if er != nil { - return er - } - - _, err = f.Write(b) - if err != nil { - return - } - _, err = f.Write([]byte("\n")) - if err != nil { - return - } - } - return -} - -func (d *DBMHandler) CreateDatapoint(typ models.Type, value any, right models.Rights, path ...string) error { - return d.DB.CreateDatapoint(typ, value, right, path...) -} - -func (d *DBMHandler) ImportDatapoints(dps ...*models.Datapoint) error { - for _, dp := range dps { - err := d.DB.ImportDatapoint(dp, dp.Path) - if err != nil { - return err - } - } - return nil -} - -func (d *DBMHandler) UpdateDatapointValue(path string, value any) error { - return d.DB.UpdateDatapointValue(value, path) -} - -func (d *DBMHandler) RemoveDatapoint(path string) error { - return d.DB.RemoveDatapoint(path) -} - -func (d *DBMHandler) QueryDatapoints(key string) []*models.Datapoint { - return d.DB.QueryDatapoints(key) -} - -func (d *DBMHandler) AddSystemDps() (err error) { - - go func() { - tim := "System:Time" - memory := "System:UsedMemory" - var m runtime.MemStats - - err = d.DB.CreateDatapoint(models.STR, nil, models.Read, tim) - if err != nil { - return - } - err = d.DB.CreateDatapoint(models.STR, nil, models.Read, memory) - if err != nil { - return - } - for { - t := time.Now().UnixMilli() - if er := d.DB.UpdateDatapointValue(t, tim); er != nil { - if err = d.DB.CreateDatapoint(models.STR, nil, models.Read, tim); err != nil { - log.Fatal(err) - } - } - runtime.ReadMemStats(&m) - if er := d.DB.UpdateDatapointValue(fmt.Sprintf("%.2f MB", float64(m.Sys)/1024/1024), memory); er != nil { - if err = d.DB.CreateDatapoint(models.STR, nil, models.Read, memory); err != nil { - log.Fatal(err) - } - } - time.Sleep(1 * time.Second) - } - }() - return -} diff --git a/main.go b/main.go index 5743b53..81fdde8 100644 --- a/main.go +++ b/main.go @@ -3,11 +3,45 @@ package main import ( "fmt" - "github.com/zuadi/tecamino-dbm.git/server" + "github.com/gin-gonic/gin" + "github.com/zuadi/tecamino-dbm/args" + "github.com/zuadi/tecamino-dbm/dbm" + "github.com/zuadi/tecamino-dbm/server" ) func main() { - fmt.Println("start") - server := server.NewServer() - panic(server.Serve(8100)) + //cli arguments + a := args.Init() + + dbmHandler, err := dbm.NewDbmHandler(a) + if err != nil { + panic(err) + } + defer dbmHandler.SaveDb() + + //initialize new server + dbmHandler.Log.Debug("main", "initialize new server instance") + s := server.NewServer() + + //set routes + dbmHandler.Log.Debug("main", "setting routes") + s.Routes.GET("/ws", dbmHandler.WebSocket) + s.Routes.POST("/json_data", s.JsonRequest) + + s.Routes.GET("/", func(c *gin.Context) { + c.String(200, "DBM WebSocket Server is running!") + }) + + go func() { + dbmHandler.Log.Info("main", fmt.Sprintf("http listen on %d", a.Port.Http)) + // start http server + if err := s.ServeHttp(a.Port.Http); err != nil { + dbmHandler.Log.Error("main", "error http server "+err.Error()) + panic(err) + } + }() + + dbmHandler.Log.Info("main", fmt.Sprintf("https listen on %d", a.Port.Https)) + panic(s.ServeHttps(a.Port.Https, a.Cert)) + } diff --git a/models/datapoints.go b/models/datapoints.go index 14fb30a..a379793 100644 --- a/models/datapoints.go +++ b/models/datapoints.go @@ -5,6 +5,8 @@ import ( "regexp" "strings" "time" + + "github.com/zuadi/tecamino-dbm/utils" ) type Datapoint struct { @@ -15,169 +17,261 @@ type Datapoint struct { UpdateDateTime int64 `json:"updateDateTime,omitempty"` Type Type `json:"type"` ReadWrite Rights `json:"readWrite"` + Drivers map[string]*Driver `json:"-"` + Subscribtions Subscribtions `json:"-"` } -var depth1 int +func (d *Datapoint) CreateDatapoint(typ Type, value any, rights Rights, path string) error { + parts := regexp.MustCompile(`[:]+`).Split(path, -1) -func (d *Datapoint) CreateDatapoint(typ Type, value any, rights Rights, paths ...string) error { - l := len(paths) - 1 - if l == 0 { - paths = regexp.MustCompile(`[:]+`).Split(paths[0], -1) - l = len(paths) - 1 - } + current := d + for i, part := range parts { + if current.Datapoints == nil { + current.Datapoints = make(map[string]*Datapoint) + } - if d.Datapoints == nil { - d.Datapoints = make(map[string]*Datapoint) - } + if i == len(parts)-1 { + // Leaf node: create or update datapoint + if existing, ok := current.Datapoints[part]; ok { + // Update existing + existing.Type = typ + existing.ReadWrite = rights.GetRights() + existing.Value = typ.ConvertValue(value) + existing.UpdateDateTime = time.Now().UnixMilli() + } else { + // Create new + current.Datapoints[part] = &Datapoint{ + Path: strings.Join(parts, ":"), + Type: typ, + Value: typ.ConvertValue(value), + ReadWrite: rights.GetRights(), + CreateDateTime: time.Now().UnixMilli(), + UpdateDateTime: time.Now().UnixMilli(), + Subscribtions: InitSubscribtion(), + } + } + return nil + } - if l == depth1 { - if od, ok := d.Datapoints[paths[depth1]]; !ok { - d.Datapoints[paths[depth1]] = &Datapoint{ - Path: strings.Join(paths, ":"), - Type: typ, - Value: typ.ConvertValue(value), + // Traverse or create intermediate datapoints + if next, ok := current.Datapoints[part]; ok { + current = next + } else { + newDp := &Datapoint{ + Path: strings.Join(parts[:i+1], ":"), + Type: NONE, ReadWrite: rights.GetRights(), CreateDateTime: time.Now().UnixMilli(), UpdateDateTime: time.Now().UnixMilli(), + Subscribtions: InitSubscribtion(), } - } else { - od.Type = typ - od.ReadWrite = rights.GetRights() - od.Value = typ.ConvertValue(value) - od.UpdateDateTime = time.Now().UnixMilli() + current.Datapoints[part] = newDp + current = newDp } - depth1 = 0 - } else if datapoint, ok := d.Datapoints[paths[depth1]]; ok { - depth1 += 1 - datapoint.CreateDatapoint(typ, value, rights, paths...) - } else { - da := Datapoint{ - Path: strings.Join(paths[:depth1+1], ":"), - Type: NONE, - ReadWrite: rights.GetRights(), - CreateDateTime: time.Now().UnixMilli(), - UpdateDateTime: time.Now().UnixMilli(), - } - - d.Datapoints[paths[depth1]] = &da - depth1 += 1 - da.CreateDatapoint(typ, value, rights, paths...) } return nil } -var depth2 int +func (d *Datapoint) ImportDatapoint(dp *Datapoint, path string) error { + parts := regexp.MustCompile(`[:]+`).Split(path, -1) -func (d *Datapoint) ImportDatapoint(dp *Datapoint, paths ...string) error { - l := len(paths) - 1 - if l == 0 { - paths = regexp.MustCompile(`[:]+`).Split(paths[0], -1) - l = len(paths) - 1 - } - - if d.Datapoints == nil { - d.Datapoints = make(map[string]*Datapoint) - } - if l == depth2 { - if od, ok := d.Datapoints[paths[depth2]]; !ok { - d.Datapoints[paths[depth2]] = dp - dp.ReadWrite = dp.ReadWrite.GetRights() - dp.UpdateDateTime = time.Now().UnixMilli() - } else { - od.Type = dp.Type - od.Value = d.Type.ConvertValue(dp.Value) - od.ReadWrite = dp.ReadWrite.GetRights() - od.UpdateDateTime = time.Now().UnixMilli() + current := d + for i, part := range parts { + if current.Datapoints == nil { + current.Datapoints = make(map[string]*Datapoint) } - depth2 = 0 - } else if datapoint, ok := d.Datapoints[paths[depth2]]; ok { - depth2 += 1 - datapoint.ImportDatapoint(dp, paths...) - } else { - da := Datapoint{ - Path: strings.Join(paths[:depth2+1], ":"), - Type: NONE, - UpdateDateTime: time.Now().UnixMilli(), - } - da.ReadWrite = da.ReadWrite.GetRights() - d.Datapoints[paths[depth2]] = &da - depth2 += 1 - da.ImportDatapoint(dp, paths...) - } - return nil -} -var depth3 int - -func (d *Datapoint) UpdateDatapointValue(value any, paths ...string) error { - l := len(paths) - 1 - if l == 0 { - paths = regexp.MustCompile(`[:]+`).Split(paths[0], -1) - l = len(paths) - 1 - } - - if l == depth3 { - if dp, ok := d.Datapoints[paths[depth3]]; ok { - fmt.Print("update dp:", dp.Path, " old value:", dp.Value) - dp.Value = dp.Type.ConvertValue(value) - fmt.Println(" new value:", dp.Value) - - dp.UpdateDateTime = time.Now().UnixMilli() - depth3 = 0 + if i == len(parts)-1 { + // Leaf node: import the datapoint + if existing, ok := current.Datapoints[part]; ok { + existing.Type = dp.Type + existing.Value = current.Type.ConvertValue(dp.Value) + existing.ReadWrite = dp.ReadWrite.GetRights() + existing.UpdateDateTime = time.Now().UnixMilli() + } else { + dp.Path = strings.Join(parts, ":") + dp.ReadWrite = dp.ReadWrite.GetRights() + dp.UpdateDateTime = time.Now().UnixMilli() + dp.Subscribtions = InitSubscribtion() + current.Datapoints[part] = dp + } return nil } - depth3 = 0 - return fmt.Errorf("datapoint '%s' not found", strings.Join(paths, ":")) - } else if datapoint, ok := d.Datapoints[paths[depth3]]; ok { - depth3 += 1 - if err := datapoint.UpdateDatapointValue(value, paths...); err != nil { - fmt.Println(100, err) - return err + + // Traverse or create intermediate nodes + if next, ok := current.Datapoints[part]; ok { + current = next + } else { + newDp := &Datapoint{ + Path: strings.Join(parts[:i+1], ":"), + Type: NONE, + ReadWrite: dp.ReadWrite.GetRights(), + UpdateDateTime: time.Now().UnixMilli(), + } + newDp.ReadWrite = newDp.ReadWrite.GetRights() + current.Datapoints[part] = newDp + current = newDp } } - depth3 = 0 return nil } -var depth4 int +func (d *Datapoint) UpdateDatapointValue(value any, path string) error { -func (d *Datapoint) RemoveDatapoint(paths ...string) error { - l := len(paths) - 1 - if l == 0 { - paths = regexp.MustCompile(`[:]+`).Split(paths[0], -1) - l = len(paths) - 1 - } + paths := regexp.MustCompile(`[:]+`).Split(path, -1) - if l == depth4 { - if _, ok := d.Datapoints[paths[depth4]]; ok { - delete(d.Datapoints, paths[depth4]) - fmt.Println("removed dp:", strings.Join(paths, ":")) + current := d + for i, part := range paths { + dp, ok := current.Datapoints[part] + if !ok { + return fmt.Errorf("datapoint path not found: %s (at %s)", path, part) } - depth4 = 0 - } else if datapoint, ok := d.Datapoints[paths[depth4]]; ok { - depth4 += 1 - datapoint.RemoveDatapoint(paths...) + if i == len(paths)-1 { + dp.Value = dp.Type.ConvertValue(value) + dp.UpdateDateTime = time.Now().UnixMilli() + return nil + } + current = dp } - return fmt.Errorf("datapoint '%s' not found", strings.Join(paths, ":")) + return nil } -func (d *Datapoint) GetAllDatapoints() (dps []*Datapoint) { - for _, dp := range d.Datapoints { - dps = append(dps, dp.GetAllDatapoints()...) - dps = append(dps, dp) +func (d *Datapoint) RemoveDatapoint(path string) error { + parts := regexp.MustCompile(`[:]+`).Split(path, -1) + if len(parts) < 1 { + return fmt.Errorf("invalid path: '%s'", path) } - return + + current := d + for i := range len(parts) - 1 { + next, ok := current.Datapoints[parts[i]] + if !ok { + return fmt.Errorf("path not found: '%s'", strings.Join(parts[:i+1], ":")) + } + current = next + } + + toDelete := parts[len(parts)-1] + if _, ok := current.Datapoints[toDelete]; ok { + delete(current.Datapoints, toDelete) + fmt.Println("Removed datapoint:", path) + return nil + } + + return fmt.Errorf("datapoint '%s' not found", path) } -func (d *Datapoint) QueryDatapoints(key string) (dps []*Datapoint) { - reg := regexp.MustCompile(key) - for _, dp := range d.Datapoints { - if reg.MatchString(dp.Path) { +func (d *Datapoint) GetAllDatapoints(depth int) (dps []*Datapoint) { + + var dfs func(dp *Datapoint, currentDepth int) + dfs = func(dp *Datapoint, currentDepth int) { + if currentDepth == 0 { dps = append(dps, dp) } - dps = append(dps, dp.QueryDatapoints(key)...) + if depth == 1 { + return + } else if depth == 0 { + // Return all descendants + for _, child := range dp.Datapoints { + dps = append(dps, child) + dfs(child, currentDepth+1) + } + return + } + + if currentDepth == depth-1 { + return + } + + for _, child := range dp.Datapoints { + dfs(child, currentDepth+1) + } } + + dfs(d, 0) return } + +func (d *Datapoint) QueryDatapoints(depth int, path string) (dps []*Datapoint) { + parts := strings.Split(path, ":") + + var dfs func(current *Datapoint, index int) + dfs = func(current *Datapoint, index int) { + + if index == len(parts) { + dps = append(dps, current.GetAllDatapoints(depth)...) + return + } + + pattern := "^" + parts[index] + "$" + re, err := regexp.Compile(pattern) + if err != nil { + return + } + + for name, dp := range current.Datapoints { + if re.MatchString(name) { + dfs(dp, index+1) + } + } + } + + dfs(d, 0) + return +} + +func (d *Datapoint) AddSubscribtion(id string, sub *Subscribe) { + if d.Subscribtions == nil { + return + } + + if s, ok := d.Subscribtions[id]; ok { + s.OnCreate = sub.OnCreate + s.OnChange = sub.OnChange + s.OnDelete = sub.OnDelete + } else { + d.Subscribtions[id] = &Subscribtion{ + OnCreate: sub.OnCreate, + OnChange: sub.OnChange, + OnDelete: sub.OnDelete, + } + } +} + +func (d *Datapoint) RemoveSubscribtion(id string) { + if _, ok := d.Subscribtions[id]; !ok { + return + } + delete(d.Subscribtions, id) +} + +func (d *Datapoint) AddDriver(driver, bus string, adr int) { + if d.Drivers == nil { + d.Drivers = make(map[string]*Driver) + } + + d.Drivers[driver] = &Driver{ + Bus: bus, + Address: adr, + } +} + +func (d *Datapoint) AddDriverSubscribtion(id string, sub *Subscribe) { + if s, ok := d.Subscribtions[id]; ok { + s.OnCreate = sub.OnCreate + s.OnChange = sub.OnChange + s.OnDelete = sub.OnDelete + } else { + d.Subscribtions[id] = &Subscribtion{ + OnCreate: sub.OnCreate, + OnChange: sub.OnChange, + OnDelete: sub.OnDelete, + } + } +} + +func (d *Datapoint) GetValueUint64() uint64 { + return utils.Uint64From(d.Value) +} diff --git a/models/driver.go b/models/driver.go new file mode 100644 index 0000000..e97e74e --- /dev/null +++ b/models/driver.go @@ -0,0 +1,6 @@ +package models + +type Driver struct { + Bus string + Address int +} diff --git a/models/get.go b/models/get.go new file mode 100644 index 0000000..765b161 --- /dev/null +++ b/models/get.go @@ -0,0 +1,6 @@ +package models + +type Get struct { + Path string `json:"path"` + Query *Query `json:"query,omitempty"` +} diff --git a/models/jsonData.go b/models/jsonData.go index 649d513..ae469e3 100644 --- a/models/jsonData.go +++ b/models/jsonData.go @@ -1,21 +1,35 @@ package models type JsonData struct { - Get *[]Get `json:"get,omitempty"` - Set *[]Set `json:"set,omitempty"` + Get *[]Get `json:"get,omitempty"` + Set *[]Set `json:"set,omitempty"` + Subscribe *[]Subscribe `json:"subscribe,omitempty"` + Unsubscribe *[]Subscribe `json:"unsubscribe,omitempty"` } -type Get struct { - Path string `json:"path"` - Query *Query `json:"query,omitempty"` +func NewRequest() *JsonData { + return &JsonData{} + } -type Set struct { - Path string `json:"path"` - Value any `json:"value"` +func (r *JsonData) AddGet(path string, query Query) { + if r.Get == nil { + r.Get = &[]Get{} + } + + *r.Get = append(*r.Get, Get{ + Path: path, + Query: &query, + }) } -type Query struct { - Depth int `json:"depth,omitempty"` - RegExp string `json:"regExp,omitempty"` +func (r *JsonData) AddSet(path string, value any, create bool) { + if r.Set == nil { + r.Set = &[]Set{} + } + + *r.Set = append(*r.Set, Set{ + Path: path, + Value: value, + }) } diff --git a/models/jsonResponse.go b/models/jsonResponse.go new file mode 100644 index 0000000..ea1414b --- /dev/null +++ b/models/jsonResponse.go @@ -0,0 +1,10 @@ +package models + +type JsonResponse struct { + Error *bool `json:"error,omitempty"` + Message string `json:"message,omitempty"` + Data string `json:"data,omitempty"` + Event string `json:"event,omitempty"` + Path string `json:"path,omitempty"` + Value any `json:"value,omitempty"` +} diff --git a/models/port.go b/models/port.go new file mode 100644 index 0000000..5394e13 --- /dev/null +++ b/models/port.go @@ -0,0 +1,6 @@ +package models + +type Port struct { + Http uint + Https uint +} diff --git a/models/query.go b/models/query.go new file mode 100644 index 0000000..2a06e93 --- /dev/null +++ b/models/query.go @@ -0,0 +1,6 @@ +package models + +type Query struct { + Depth int `json:"depth,omitempty"` + RegExp string `json:"regExp,omitempty"` +} diff --git a/models/set.go b/models/set.go new file mode 100644 index 0000000..92d920c --- /dev/null +++ b/models/set.go @@ -0,0 +1,8 @@ +package models + +type Set struct { + Path string `json:"path"` + Driver *Driver `json:"driver,omitempty"` + Value any `json:"value,omitempty"` + Create bool `json:"create,omitempty"` +} diff --git a/models/subscribe.go b/models/subscribe.go new file mode 100644 index 0000000..2045328 --- /dev/null +++ b/models/subscribe.go @@ -0,0 +1,10 @@ +package models + +type Subscribe struct { + Path string `json:"path"` + Depth int `json:"depth"` + Driver *string `json:"driver,omitempty"` + OnCreate bool `json:"onCreate"` + OnDelete bool `json:"onDelete"` + OnChange bool `json:"onChange"` +} diff --git a/models/subscribtion.go b/models/subscribtion.go new file mode 100644 index 0000000..f267fc0 --- /dev/null +++ b/models/subscribtion.go @@ -0,0 +1,13 @@ +package models + +type Subscribtions map[string]*Subscribtion + +type Subscribtion struct { + OnCreate bool + OnDelete bool + OnChange bool +} + +func InitSubscribtion() Subscribtions { + return make(Subscribtions) +} diff --git a/models/type.go b/models/type.go index 735adf8..1e3abb4 100644 --- a/models/type.go +++ b/models/type.go @@ -3,7 +3,7 @@ package models import ( "fmt" - "github.com/zuadi/tecamino-dbm.git/utils" + "github.com/zuadi/tecamino-dbm/utils" ) const ( diff --git a/server/clients.go b/server/clients.go deleted file mode 100644 index e5221c3..0000000 --- a/server/clients.go +++ /dev/null @@ -1,49 +0,0 @@ -package server - -import ( - "context" - "log" - "sync" - - "github.com/coder/websocket" - "github.com/coder/websocket/wsjson" - "github.com/zuadi/tecamino-dbm.git/models" -) - -var ( - clients = make(map[*Client]bool) - clientsMu sync.Mutex -) - -type Client struct { - conn *websocket.Conn - ctx context.Context -} - -func registerClient(c *Client) { - clientsMu.Lock() - defer clientsMu.Unlock() - clients[c] = true - log.Printf("Client connected (%d total)", len(clients)) -} - -func unregisterClient(c *Client) { - clientsMu.Lock() - defer clientsMu.Unlock() - delete(clients, c) - log.Printf("Client disconnected (%d total)", len(clients)) -} - -func broadcast(data models.JsonData) { - clientsMu.Lock() - defer clientsMu.Unlock() - - for c := range clients { - go func(client *Client) { - err := wsjson.Write(client.ctx, client.conn, data) - if err != nil { - log.Printf("Broadcast error: %v", err) - } - }(c) - } -} diff --git a/server/jsonRequest.go b/server/jsonRequest.go new file mode 100644 index 0000000..27dd76c --- /dev/null +++ b/server/jsonRequest.go @@ -0,0 +1,27 @@ +package server + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/zuadi/tecamino-dbm/models" +) + +func (s *Server) JsonRequest(c *gin.Context) { + var payload models.JsonData + + if err := c.BindJSON(&payload); err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if payload.Set != nil { + + } + + c.JSON(200, gin.H{ + "name": payload, + }) + return + +} diff --git a/server/models/clients.go b/server/models/clients.go new file mode 100644 index 0000000..b899679 --- /dev/null +++ b/server/models/clients.go @@ -0,0 +1,82 @@ +package models + +import ( + "context" + "fmt" + "net/http" + + "github.com/coder/websocket" + "github.com/gin-gonic/gin" +) + +var Origins []string = []string{"*"} + +type Clients map[string]*Client + +type Client struct { + Connected *bool `json:"connected"` + Conn *websocket.Conn `json:"-"` +} + +func NewClients() Clients { + return make(Clients) +} + +// Connect a recieving websocket connection +func (c *Clients) ConnectRecievingWsConnection(id string, ctx *gin.Context) error { + if _, exists := (*c)[id]; exists { + return nil + } + + conn, err := websocket.Accept(ctx.Writer, ctx.Request, &websocket.AcceptOptions{ + OriginPatterns: Origins, + }) + + if err != nil { + return fmt.Errorf("error accept websocket client: %s", err) + } + + b := true + (*c)[id] = &Client{ + Connected: &b, + Conn: conn, + } + return nil +} + +// Connect a recieving websocket connection +func (c *Clients) ConnectSendingWsConnection(id, url string) (*websocket.Conn, error) { + if _, exists := (*c)[id]; exists { + return (*c)[id].Conn, nil + } + + header := http.Header{} + header.Set("Authorization", "Bearer "+id) + + conn, _, err := websocket.Dial(context.Background(), url, &websocket.DialOptions{ + HTTPHeader: header, + }) + if err != nil { + return nil, err + } + + b := true + (*c)[id] = &Client{ + Connected: &b, + Conn: 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) DisconnectWsConnection(id string, code websocket.StatusCode, reason string) { + *(*c)[id].Connected = false + (*c)[id].Conn.Close(code, reason) +} diff --git a/server/routes.go b/server/routes.go deleted file mode 100644 index e194760..0000000 --- a/server/routes.go +++ /dev/null @@ -1,13 +0,0 @@ -package server - -import "github.com/gin-gonic/gin" - -func (s *Server) AddRoutes() { - s.engine.GET("/json_data", func(c *gin.Context) { - handleWebSocket(c.Writer, c.Request) - }) - - s.engine.GET("/", func(c *gin.Context) { - c.String(200, "WebSocket Broadcast Server is running!") - }) -} diff --git a/server/server.go b/server/server.go index eae5e15..7afe4f5 100644 --- a/server/server.go +++ b/server/server.go @@ -2,25 +2,29 @@ package server import ( "fmt" + "sync" "github.com/gin-gonic/gin" + "github.com/tecamino/tecamino-logger/logging" + "github.com/zuadi/tecamino-dbm/cert" ) type Server struct { - engine *gin.Engine + Routes *gin.Engine + sync.RWMutex + Logger *logging.Logger } func NewServer() *Server { - s := Server{ - engine: gin.Default(), + return &Server{ + Routes: gin.Default(), } - s.AddRoutes() - return &s } -func (s *Server) Serve(port uint) error { - if err := s.engine.Run(fmt.Sprintf(":%d", port)); err != nil { - return fmt.Errorf("failed to run server: %v", err) - } - return nil +func (s *Server) ServeHttp(port uint) error { + return s.Routes.Run(fmt.Sprintf(":%d", port)) +} + +func (s *Server) ServeHttps(port uint, cert cert.Cert) error { + return s.Routes.RunTLS(fmt.Sprintf(":%d", port), cert.CertFile, cert.KeyFile) } diff --git a/server/webSocket.go b/server/webSocket.go deleted file mode 100644 index f35d1f4..0000000 --- a/server/webSocket.go +++ /dev/null @@ -1,62 +0,0 @@ -package server - -import ( - "context" - "log" - "net/http" - "time" - - "github.com/coder/websocket" - "github.com/coder/websocket/wsjson" - "github.com/zuadi/tecamino-dbm.git/models" -) - -func handleWebSocket(w http.ResponseWriter, r *http.Request) { - conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{ - OriginPatterns: []string{"*"}, - }) - if err != nil { - log.Println("WebSocket accept error:", err) - return - } - defer conn.Close(websocket.StatusInternalError, "Internal error") - - ctx, cancel := context.WithTimeout(r.Context(), 10*time.Minute) - defer cancel() - - client := &Client{conn: conn, ctx: ctx} - - // Register client - registerClient(client) - defer unregisterClient(client) - - // Read loop - for { - var data models.JsonData - err := wsjson.Read(ctx, conn, &data) - if err != nil { - log.Println("Read error:", err) - var response struct { - Code int `json:"errorCode"` - Message string `json:"message"` - Error bool `json:"error"` - } - response.Code = 404 - response.Error = true - response.Message = err.Error() - - err = wsjson.Write(ctx, conn, response) - if err != nil { - log.Println("Read error:", err) - } - break - } - - log.Printf("Received: %v", data) - - // Broadcast to all - broadcast(data) - } - - conn.Close(websocket.StatusNormalClosure, "Normal closure") -} diff --git a/test/dbm_test.go b/test/dbm_test.go index 203ca5e..ff836df 100644 --- a/test/dbm_test.go +++ b/test/dbm_test.go @@ -7,14 +7,29 @@ import ( "testing" "time" - "github.com/zuadi/tecamino-dbm.git/handlers" - "github.com/zuadi/tecamino-dbm.git/models" - "github.com/zuadi/tecamino-dbm.git/server" - "github.com/zuadi/tecamino-dbm.git/utils" + "github.com/zuadi/tecamino-dbm/args" + "github.com/zuadi/tecamino-dbm/cert" + "github.com/zuadi/tecamino-dbm/dbm" + "github.com/zuadi/tecamino-dbm/models" + "github.com/zuadi/tecamino-dbm/server" + "github.com/zuadi/tecamino-dbm/utils" ) func TestCreateDps(t *testing.T) { - dmaHandler, err := handlers.NewDbmHandler(".", "test") + dmaHandler, err := dbm.NewDbmHandler(&args.Args{ + Port: models.Port{ + Http: 8100, + Https: 8101, + }, + Cert: cert.Cert{ + Organization: "tecamino", + CertFile: "./cert/cert.pem", + KeyFile: "./cert/key.pem", + }, + RootDir: ".", + DMAFile: "Test", + Debug: false, + }) if err != nil { t.Fatal(err) } @@ -54,7 +69,20 @@ func TestCreateDps(t *testing.T) { } func TestQuery(t *testing.T) { - dmaHandler, err := handlers.NewDbmHandler(".", "test") + dmaHandler, err := dbm.NewDbmHandler(&args.Args{ + Port: models.Port{ + Http: 8100, + Https: 8101, + }, + Cert: cert.Cert{ + Organization: "tecamino", + CertFile: "./cert/cert.pem", + KeyFile: "./cert/key.pem", + }, + RootDir: ".", + DMAFile: "Test", + Debug: false, + }) if err != nil { panic(err) } @@ -63,14 +91,27 @@ func TestQuery(t *testing.T) { // fmt.Println(600, i, o) // } - for i, o := range dmaHandler.QueryDatapoints("Test:A:000") { + for i, o := range dmaHandler.QueryDatapoints(1, "Test:A:000") { fmt.Println(600, i, o) } } func TestUpdateDps(t *testing.T) { - dmaHandler, err := handlers.NewDbmHandler(".", "test") + dmaHandler, err := dbm.NewDbmHandler(&args.Args{ + Port: models.Port{ + Http: 8100, + Https: 8101, + }, + Cert: cert.Cert{ + Organization: "tecamino", + CertFile: "./cert/cert.pem", + KeyFile: "./cert/key.pem", + }, + RootDir: ".", + DMAFile: "Test", + Debug: false, + }) if err != nil { t.Fatal(err) } @@ -105,6 +146,6 @@ func TestUpdateDps(t *testing.T) { func TestServer(t *testing.T) { fmt.Println("start") server := server.NewServer() - t.Fatal(server.Serve(8100)) + t.Fatal(server.ServeHttp(8100)) } diff --git a/test/helper.go b/test/helper.go index 4572d0f..d3b3047 100644 --- a/test/helper.go +++ b/test/helper.go @@ -3,7 +3,7 @@ package test import ( "math/rand" - "github.com/zuadi/tecamino-dbm.git/models" + "github.com/zuadi/tecamino-dbm/models" ) func RandomType() models.Type {