Skip to content

Commit 45bf557

Browse files
committed
Use a channel to better sync the closing of a port
1 parent 99e523e commit 45bf557

File tree

1 file changed

+82
-80
lines changed

1 file changed

+82
-80
lines changed

serialport.go

Lines changed: 82 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -95,95 +95,95 @@ func (p *serport) reader() {
9595
ch := make([]byte, 1024)
9696
timeCheckOpen := time.Now()
9797

98+
mainLoop:
9899
for {
99-
100-
n, err := p.portIo.Read(ch)
101-
102-
//if we detect that port is closing, break out o this for{} loop.
103-
if p.isClosing {
100+
select {
101+
case <-p.done:
104102
strmsg := "Shutting down reader on " + p.portConf.Name
105103
log.Println(strmsg)
106104
h.broadcastSys <- []byte(strmsg)
107-
break
108-
}
109-
110-
// read can return legitimate bytes as well as an error
111-
// so process the bytes if n > 0
112-
if n > 0 {
113-
//log.Print("Read " + strconv.Itoa(n) + " bytes ch: " + string(ch))
114-
data := string(ch[:n])
115-
//log.Print("The data i will convert to json is:")
116-
//log.Print(data)
117-
118-
// give the data to our bufferflow so it can do it's work
119-
// to read/translate the data to see if it wants to block
120-
// writes to the serialport. each bufferflow type will decide
121-
// this on its own based on its logic, i.e. tinyg vs grbl vs others
122-
//p.b.bufferwatcher..OnIncomingData(data)
123-
p.bufferwatcher.OnIncomingData(data)
124-
125-
// see if the OnIncomingData handled the broadcast back
126-
// to the user. this option was added in case the OnIncomingData wanted
127-
// to do something fancier or implementation specific, i.e. TinyG Buffer
128-
// actually sends back data on a perline basis rather than our method
129-
// where we just send the moment we get it. the reason for this is that
130-
// the browser was sometimes getting back packets out of order which
131-
// of course would screw things up when parsing
132-
133-
if p.bufferwatcher.IsBufferGloballySendingBackIncomingData() == false {
134-
//m := SpPortMessage{"Alice", "Hello"}
135-
m := SpPortMessage{data}
136-
//log.Print("The m obj struct is:")
137-
//log.Print(m)
138-
139-
//b, err := json.MarshalIndent(m, "", "\t")
140-
b, err := json.Marshal(m)
141-
if err != nil {
142-
log.Println(err)
143-
h.broadcastSys <- []byte("Error creating json on " + p.portConf.Name + " " +
144-
err.Error() + " The data we were trying to convert is: " + string(ch[:n]))
145-
break
105+
break mainLoop
106+
default:
107+
n, err := p.portIo.Read(ch)
108+
109+
// read can return legitimate bytes as well as an error
110+
// so process the bytes if n > 0
111+
if n > 0 {
112+
//log.Print("Read " + strconv.Itoa(n) + " bytes ch: " + string(ch))
113+
data := string(ch[:n])
114+
//log.Print("The data i will convert to json is:")
115+
//log.Print(data)
116+
117+
// give the data to our bufferflow so it can do it's work
118+
// to read/translate the data to see if it wants to block
119+
// writes to the serialport. each bufferflow type will decide
120+
// this on its own based on its logic, i.e. tinyg vs grbl vs others
121+
//p.b.bufferwatcher..OnIncomingData(data)
122+
p.bufferwatcher.OnIncomingData(data)
123+
124+
// see if the OnIncomingData handled the broadcast back
125+
// to the user. this option was added in case the OnIncomingData wanted
126+
// to do something fancier or implementation specific, i.e. TinyG Buffer
127+
// actually sends back data on a perline basis rather than our method
128+
// where we just send the moment we get it. the reason for this is that
129+
// the browser was sometimes getting back packets out of order which
130+
// of course would screw things up when parsing
131+
132+
if p.bufferwatcher.IsBufferGloballySendingBackIncomingData() == false {
133+
//m := SpPortMessage{"Alice", "Hello"}
134+
m := SpPortMessage{data}
135+
//log.Print("The m obj struct is:")
136+
//log.Print(m)
137+
138+
//b, err := json.MarshalIndent(m, "", "\t")
139+
b, err := json.Marshal(m)
140+
if err != nil {
141+
log.Println(err)
142+
h.broadcastSys <- []byte("Error creating json on " + p.portConf.Name + " " +
143+
err.Error() + " The data we were trying to convert is: " + string(ch[:n]))
144+
break
145+
}
146+
//log.Print("Printing out json byte data...")
147+
//log.Print(string(b))
148+
h.broadcastSys <- b
149+
//h.broadcastSys <- []byte("{ \"p\" : \"" + p.portConf.Name + "\", \"d\": \"" + string(ch[:n]) + "\" }\n")
146150
}
147-
//log.Print("Printing out json byte data...")
148-
//log.Print(string(b))
149-
h.broadcastSys <- b
150-
//h.broadcastSys <- []byte("{ \"p\" : \"" + p.portConf.Name + "\", \"d\": \"" + string(ch[:n]) + "\" }\n")
151151
}
152-
}
153152

154-
// double check that we got characters in the buffer
155-
// before deciding if an EOF is legitimately a reason
156-
// to close the port because we're seeing that on some
157-
// os's like Linux/Ubuntu you get an EOF when you open
158-
// the port. Perhaps the EOF was buffered from a previous
159-
// close and the OS doesn't clear out that buffer on a new
160-
// connect. This means we'll only catch EOF's when there are
161-
// other characters with it, but that seems to work ok
162-
if n <= 0 {
163-
if err == io.EOF || err == io.ErrUnexpectedEOF {
164-
// hit end of file
165-
log.Println("Hit end of file on serial port")
166-
h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got EOF (End of File) on port which usually means another app other than Serial Port JSON Server is locking your port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
153+
// double check that we got characters in the buffer
154+
// before deciding if an EOF is legitimately a reason
155+
// to close the port because we're seeing that on some
156+
// os's like Linux/Ubuntu you get an EOF when you open
157+
// the port. Perhaps the EOF was buffered from a previous
158+
// close and the OS doesn't clear out that buffer on a new
159+
// connect. This means we'll only catch EOF's when there are
160+
// other characters with it, but that seems to work ok
161+
if n <= 0 {
162+
if err == io.EOF || err == io.ErrUnexpectedEOF {
163+
// hit end of file
164+
log.Println("Hit end of file on serial port")
165+
h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got EOF (End of File) on port which usually means another app other than Serial Port JSON Server is locking your port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
167166

168-
}
167+
}
169168

170-
if err != nil {
171-
log.Println(err)
172-
h.broadcastSys <- []byte("Error reading on " + p.portConf.Name + " " +
173-
err.Error() + " Closing port.")
174-
h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got error reading on port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
175-
break
176-
}
169+
if err != nil {
170+
log.Println(err)
171+
h.broadcastSys <- []byte("Error reading on " + p.portConf.Name + " " +
172+
err.Error() + " Closing port.")
173+
h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got error reading on port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
174+
break
175+
}
177176

178-
// Keep track of time difference between two consecutive read with n == 0 and err == nil
179-
// we get here if the port has been disconnected while open (cpu usage will jump to 100%)
180-
// let's close the port only if the events are extremely fast (<1ms)
181-
if err == nil {
182-
diff := time.Since(timeCheckOpen)
183-
if diff.Nanoseconds() < 1000000 {
184-
p.isClosing = true
177+
// Keep track of time difference between two consecutive read with n == 0 and err == nil
178+
// we get here if the port has been disconnected while open (cpu usage will jump to 100%)
179+
// let's close the port only if the events are extremely fast (<1ms)
180+
if err == nil {
181+
diff := time.Since(timeCheckOpen)
182+
if diff.Nanoseconds() < 1000000 {
183+
p.isClosing = true
184+
}
185+
timeCheckOpen = time.Now()
185186
}
186-
timeCheckOpen = time.Now()
187187
}
188188
}
189189
}
@@ -339,7 +339,7 @@ func spHandlerOpen(portname string, baud int, buftype string, isSecondary bool)
339339
log.Print("Opened port successfully")
340340
//p := &serport{send: make(chan []byte, 256), portConf: conf, portIo: sp}
341341
// we can go up to 256,000 lines of gcode in the buffer
342-
p := &serport{sendBuffered: make(chan Cmd, 256000), sendNoBuf: make(chan Cmd), portConf: conf, portIo: sp, BufferType: buftype, IsPrimary: isPrimary, IsSecondary: isSecondary}
342+
p := &serport{sendBuffered: make(chan Cmd, 256000), done: make(chan bool), sendNoBuf: make(chan Cmd), portConf: conf, portIo: sp, BufferType: buftype, IsPrimary: isPrimary, IsSecondary: isSecondary}
343343

344344
// if user asked for a buffer watcher, i.e. tinyg/grbl then attach here
345345
if buftype == "tinyg" {
@@ -413,7 +413,9 @@ func spHandlerCloseExperimental(p *serport) {
413413
}
414414

415415
func spHandlerClose(p *serport) {
416-
p.isClosing = true
416+
// p.isClosing = true
417+
p.done <- true
418+
417419
//close the port
418420
//elicit response from hardware to close out p.reader()
419421
_, _ = p.portIo.Write([]byte("?"))

0 commit comments

Comments
 (0)