-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserial.go
More file actions
130 lines (118 loc) · 2.71 KB
/
serial.go
File metadata and controls
130 lines (118 loc) · 2.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package main
import (
"context"
"fmt"
"log"
"os"
"strconv"
"sync"
"time"
"github.com/tarm/serial"
)
func checker(serialPort string) {
for {
time.Sleep(30 * time.Second)
busy, err := IsBusy(serialPort)
if err == nil {
if busy {
debugPrint(log.Printf, levelError, "%s is busy!", serialPort)
}
} else {
debugPrint(log.Printf, levelWarning, "Can't check if %s is busy", serialPort)
}
}
}
func SerialHandler(serialPort string, BaudRate int, serialIn <-chan byte, serialOut chan<- byte) {
var wg sync.WaitGroup
busy, err := IsBusy(serialPort)
if err == nil {
if busy {
debugPrint(log.Printf, levelPanic, "serial port %s is busy", serialPort)
}
} else {
debugPrint(log.Printf, levelWarning, "Can't check if %s is busy", serialPort)
}
go checker(serialPort)
cfg := &serial.Config{Name: serialPort, Baud: BaudRate}
serialPortInstance, err := serial.OpenPort(cfg)
if err != nil {
debugPrint(log.Printf, levelPanic, "Error opening serial port: %s", err.Error())
}
defer serialPortInstance.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
wg.Add(1)
go func() {
defer wg.Done()
buf := make([]byte, 4096)
for {
n, err := serialPortInstance.Read(buf)
if err != nil {
debugPrint(log.Printf, levelError, "Error reading from serial port: %s", err.Error())
cancel()
return
}
if n <= 0 {
continue
}
debugPrint(log.Printf, levelCrazy, "Read %d bytes '%s'", n, string(buf[:n]))
// send bytes one by one (compatible) but honour ctx
for i := 0; i < n; i++ {
select {
case serialOut <- buf[i]:
case <-ctx.Done():
return
}
}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case data := <-serialIn:
// consider batching multiple bytes into a small buffer for efficiency
_, err := serialPortInstance.Write([]byte{data})
if err != nil {
debugPrint(log.Printf, levelError, "Error writing to serial port: %s", err.Error())
cancel()
return
}
case <-ctx.Done():
return
}
}
}()
wg.Wait()
}
func IsBusy(filePath string) (bool, error) {
currentPID := os.Getpid()
entries, err := os.ReadDir("/proc")
if err != nil {
return false, err
}
for _, entry := range entries {
name := entry.Name()
pid, err := strconv.Atoi(name)
if err != nil || pid == currentPID {
continue
}
fdDirPath := fmt.Sprintf("/proc/%d/fd", pid)
fdEntries, err := os.ReadDir(fdDirPath)
if err != nil {
continue
}
for _, fdEntry := range fdEntries {
fdPath := fmt.Sprintf("/proc/%d/fd/%s", pid, fdEntry.Name())
target, err := os.Readlink(fdPath)
if err != nil {
continue
}
if target == filePath {
return true, nil
}
}
}
return false, nil
}