commit 0c0d82db4236224bfdc4e8033188aa57c08ae809 Author: Gašper Dobrovoljc Date: Mon Mar 13 14:50:49 2023 +0100 Initial commit diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..a20502b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "all" +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..96a2e83 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/gapidobri/siemens-ws + +go 1.20 + +require ( + github.com/danclive/snap7-go v0.1.2 + github.com/gorilla/websocket v1.5.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..15502b9 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/danclive/snap7-go v0.1.2 h1:DL4HWS9efPzJfTk1x9As0QUwc/Y3C0oTbm6plvHnNWg= +github.com/danclive/snap7-go v0.1.2/go.mod h1:zON3ZLTb9Cr3GJAWGkMzJ8gf/o2Sqyd/MrZ1wxQsiQw= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/hardware/hardware.go b/hardware/hardware.go new file mode 100644 index 0000000..1ae9c81 --- /dev/null +++ b/hardware/hardware.go @@ -0,0 +1,18 @@ +package hardware + +type ( + Hardware interface { + Read() (uint16, error) + } + Client struct { + hardware Hardware + } +) + +func NewClient(h Hardware) Client { + return Client{hardware: h} +} + +func (c Client) Read() (uint16, error) { + return c.hardware.Read() +} diff --git a/hardware/mock.go b/hardware/mock.go new file mode 100644 index 0000000..527fdbc --- /dev/null +++ b/hardware/mock.go @@ -0,0 +1,22 @@ +package hardware + +type Mock struct { + currValue int16 + delta int16 +} + +func NewMock() *Mock { + return &Mock{currValue: 0, delta: 1} +} + +func (m *Mock) Read() (uint16, error) { + m.currValue += m.delta + + if m.currValue >= 100 { + m.delta = -1 + } else if m.currValue <= 0 { + m.delta = 1 + } + + return uint16(m.currValue), nil +} diff --git a/hardware/siemens.go b/hardware/siemens.go new file mode 100644 index 0000000..3014058 --- /dev/null +++ b/hardware/siemens.go @@ -0,0 +1,26 @@ +package hardware + +import ( + "encoding/binary" + + snap7 "github.com/danclive/snap7-go" +) + +type Siemens struct { + client snap7.Snap7Client +} + +func NewSiemens(client snap7.Snap7Client) Siemens { + return Siemens{client: client} +} + +func (s Siemens) Read() (uint16, error) { + db, err := s.client.DBRead(1, 0, 8) + if err != nil { + return 0, err + } + + value := binary.BigEndian.Uint16(db[2:4]) + + return value, nil +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..4d82757 --- /dev/null +++ b/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "time" + + "github.com/danclive/snap7-go" + "github.com/gapidobri/siemens-ws/hardware" + "github.com/gorilla/websocket" +) + +var ( + upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { + return true + }, + } + clients = []chan uint16{} +) + +func main() { + siemens, err := snap7.ConnentTo("192.168.117.222", 0, 1, 0) + if err != nil { + fmt.Println(err) + } + + // client := hardware.NewClient(hardware.NewMock()) + client := hardware.NewSiemens(siemens) + + go func() { + for { + val, err := client.Read() + if err != nil { + log.Println("read err:", err) + } + + fmt.Println(val) + + for _, c := range clients { + c <- val + } + + time.Sleep(time.Millisecond * 100) + } + }() + + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Println("open handler") + + c, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Println("upgrade err:", err) + return + } + defer c.Close() + + dataCh := make(chan uint16) + + clients = append(clients, dataCh) + + for { + val, more := <-dataCh + if !more { + break + } + err = c.WriteMessage(websocket.TextMessage, []byte(fmt.Sprintf("%d", val))) + if err != nil { + log.Println("write err:", err) + fmt.Println("close handler") + c.Close() + close(dataCh) + for i, c := range clients { + if c == dataCh { + clients = append(clients[:i], clients[i+1:]...) + break + } + } + } + } + + }) + + log.Fatal(http.ListenAndServe(":1234", nil)) + +} diff --git a/static/index.html b/static/index.html new file mode 100644 index 0000000..86cbc73 --- /dev/null +++ b/static/index.html @@ -0,0 +1,47 @@ + + + + + Document + + + + + + + + + + + +
+ +
+ + + + + + + + +
TimeValue
+
+ + diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..a7d54e9 --- /dev/null +++ b/static/script.js @@ -0,0 +1,82 @@ +const dataNode = document.getElementById('data'); +const ctx = document.getElementById('chart'); +const table = document.getElementById('table'); +const target = document.getElementById('target'); + +const socket = new WebSocket('ws://127.0.0.1:1234'); + +socket.addEventListener('open', (e) => { + console.log('connection opened'); +}); + +socket.addEventListener('close', (e) => { + console.log('connection closed'); +}); + +Chart.defaults.set('plugins.streaming', { + duration: 20000, +}); + +const data = []; +const labels = []; +const targets = []; + +const chart = new Chart(ctx, { + type: 'line', + data: { + labels, + datasets: [ + { + label: 'Value', + data: data, + borderWidth: 1, + }, + { + label: 'Target', + data: targets, + borderWidth: 1, + }, + ], + }, + options: { + animations: false, + scales: { + y: { + beginAtZero: true, + }, + }, + }, +}); + +chart.resize(1300, 600); + +function updateTable(data) { + if (table.rows.length == 20) { + table.rows[table.rows.length - 1].remove(); + } + + const tr = table.insertRow(1); + tr.innerHTML = ` + ${new Date().toLocaleString()} + ${data} + `; +} + +socket.addEventListener('message', (e) => { + const value = Math.round(parseInt(e.data) / 2.83) / 100; + + dataNode.innerText = value; + data.push(value); + labels.push(new Date().toLocaleString()); + if (data.length > 100) { + data.shift(); + labels.shift(); + targets.shift(); + } + + chart.update(); + + updateTable(value); + + targets.push(target.value); +});