kappanhang/serialtcpsrv.go

189 lines
3.7 KiB
Go
Raw Normal View History

2020-10-25 16:15:39 +01:00
package main
import (
"fmt"
"io"
"net"
2020-11-03 22:26:34 +01:00
"sync"
2020-10-25 16:15:39 +01:00
)
type serialTCPSrvStruct struct {
2020-10-25 16:15:39 +01:00
listener net.Listener
client net.Conn
fromClient chan []byte
toClient chan []byte
clientLoopDeinitNeededChan chan bool
clientLoopDeinitFinishedChan chan bool
2020-10-25 16:15:39 +01:00
deinitNeededChan chan bool
2020-10-25 16:15:39 +01:00
deinitFinishedChan chan bool
2020-11-03 22:26:34 +01:00
clientConnected bool
mutex sync.Mutex
2020-10-25 16:15:39 +01:00
}
var serialTCPSrv serialTCPSrvStruct
func (s *serialTCPSrvStruct) isClientConnected() bool {
s.mutex.Lock()
defer s.mutex.Unlock()
return s.clientConnected
}
2020-10-25 16:15:39 +01:00
func (s *serialTCPSrvStruct) writeLoop(writeLoopDeinitNeededChan, writeLoopDeinitFinishedChan chan bool,
errChan chan error) {
2020-10-25 16:15:39 +01:00
var b []byte
for {
select {
case b = <-s.toClient:
case <-writeLoopDeinitNeededChan:
writeLoopDeinitFinishedChan <- true
2020-10-25 16:15:39 +01:00
return
}
for len(b) > 0 {
written, err := s.client.Write(b)
if err != nil {
errChan <- err
break
2020-10-25 16:15:39 +01:00
}
b = b[written:]
}
}
}
func (s *serialTCPSrvStruct) disconnectClient() {
2020-10-25 16:15:39 +01:00
if s.client != nil {
s.client.Close()
}
2020-11-03 22:26:34 +01:00
}
func (s *serialTCPSrvStruct) deinitClient() {
if s.clientLoopDeinitNeededChan != nil {
s.clientLoopDeinitNeededChan <- true
<-s.clientLoopDeinitFinishedChan
2020-10-25 16:15:39 +01:00
s.clientLoopDeinitNeededChan = nil
s.clientLoopDeinitFinishedChan = nil
2020-10-25 16:15:39 +01:00
}
}
func (s *serialTCPSrvStruct) clientLoop() {
s.mutex.Lock()
s.clientConnected = true
s.mutex.Unlock()
defer func() {
s.mutex.Lock()
s.clientConnected = false
s.mutex.Unlock()
}()
log.Print("client ", s.client.RemoteAddr().String(), " connected")
writeLoopDeinitNeededChan := make(chan bool)
writeLoopDeinitFinishedChan := make(chan bool)
writeErrChan := make(chan error)
go s.writeLoop(writeLoopDeinitNeededChan, writeLoopDeinitFinishedChan, writeErrChan)
connected:
for {
b := make([]byte, maxSerialFrameLength)
n, err := s.client.Read(b)
if err != nil {
break
}
select {
case s.fromClient <- b[:n]:
case <-writeErrChan:
break connected
case <-s.clientLoopDeinitNeededChan:
writeLoopDeinitNeededChan <- true
<-writeLoopDeinitFinishedChan
s.clientLoopDeinitFinishedChan <- true
return
}
}
log.Print("client ", s.client.RemoteAddr().String(), " disconnected")
writeLoopDeinitNeededChan <- true
<-writeLoopDeinitFinishedChan
<-s.clientLoopDeinitNeededChan
s.clientLoopDeinitFinishedChan <- true
}
func (s *serialTCPSrvStruct) loop() {
2020-10-25 16:15:39 +01:00
for {
newClient, err := s.listener.Accept()
s.disconnectClient()
s.deinitClient()
2020-10-25 16:15:39 +01:00
s.clientLoopDeinitNeededChan = make(chan bool)
s.clientLoopDeinitFinishedChan = make(chan bool)
2020-10-25 16:15:39 +01:00
if err != nil {
if err != io.EOF {
reportError(err)
}
<-s.deinitNeededChan
2020-10-25 16:15:39 +01:00
s.deinitFinishedChan <- true
return
}
s.client = newClient
go s.clientLoop()
2020-10-25 16:15:39 +01:00
}
}
// We only init the serial port TCP server once, with the first device name we acquire, so apps using the
// serial port TCP server won't have issues with the interface going down while the app is running.
func (s *serialTCPSrvStruct) initIfNeeded() (err error) {
if s.listener != nil {
// Depleting channel which may contain data while the serial connection to the server was offline.
for {
select {
case <-s.fromClient:
default:
return
}
}
}
2020-10-25 16:15:39 +01:00
s.listener, err = net.Listen("tcp", fmt.Sprint(":", serialTCPPort))
if err != nil {
fmt.Println(err)
return
}
log.Print("exposing serial port on tcp port ", serialTCPPort)
s.fromClient = make(chan []byte)
s.toClient = make(chan []byte)
2020-10-25 16:15:39 +01:00
s.deinitNeededChan = make(chan bool)
2020-10-25 16:15:39 +01:00
s.deinitFinishedChan = make(chan bool)
go s.loop()
return
}
func (s *serialTCPSrvStruct) deinit() {
2020-10-25 16:15:39 +01:00
if s.listener != nil {
s.listener.Close()
}
2020-10-28 22:40:35 +01:00
if s.deinitNeededChan != nil {
s.deinitNeededChan <- true
<-s.deinitFinishedChan
}
2020-10-25 16:15:39 +01:00
}