2020-10-25 16:15:39 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"net"
|
|
|
|
|
)
|
|
|
|
|
|
2020-10-28 22:32:45 +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
|
|
|
|
|
|
|
|
|
|
writeLoopDeinitNeededChan chan bool
|
|
|
|
|
writeLoopDeinitFinishedChan chan bool
|
|
|
|
|
|
2020-10-28 22:32:45 +01:00
|
|
|
deinitNeededChan chan bool
|
2020-10-25 16:15:39 +01:00
|
|
|
deinitFinishedChan chan bool
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 22:32:45 +01:00
|
|
|
var serialTCPSrv serialTCPSrvStruct
|
|
|
|
|
|
|
|
|
|
func (s *serialTCPSrvStruct) isClientConnected() bool {
|
|
|
|
|
return s.writeLoopDeinitNeededChan != nil
|
|
|
|
|
}
|
2020-10-25 16:15:39 +01:00
|
|
|
|
2020-10-28 22:32:45 +01:00
|
|
|
func (s *serialTCPSrvStruct) writeLoop(errChan chan error) {
|
2020-10-25 16:15:39 +01:00
|
|
|
var b []byte
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case b = <-s.toClient:
|
|
|
|
|
case <-s.writeLoopDeinitNeededChan:
|
|
|
|
|
s.writeLoopDeinitFinishedChan <- true
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for len(b) > 0 {
|
|
|
|
|
written, err := s.client.Write(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
errChan <- err
|
2020-10-28 22:32:45 +01:00
|
|
|
break
|
2020-10-25 16:15:39 +01:00
|
|
|
}
|
|
|
|
|
b = b[written:]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 22:32:45 +01:00
|
|
|
func (s *serialTCPSrvStruct) disconnectClient() {
|
2020-10-25 16:15:39 +01:00
|
|
|
if s.client != nil {
|
|
|
|
|
s.client.Close()
|
|
|
|
|
}
|
|
|
|
|
if s.writeLoopDeinitNeededChan != nil {
|
|
|
|
|
s.writeLoopDeinitNeededChan <- true
|
|
|
|
|
<-s.writeLoopDeinitFinishedChan
|
|
|
|
|
|
|
|
|
|
s.writeLoopDeinitNeededChan = nil
|
|
|
|
|
s.writeLoopDeinitFinishedChan = nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 22:32:45 +01:00
|
|
|
func (s *serialTCPSrvStruct) loop() {
|
2020-10-25 16:15:39 +01:00
|
|
|
for {
|
|
|
|
|
var err error
|
|
|
|
|
s.client, err = s.listener.Accept()
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
if err != io.EOF {
|
|
|
|
|
reportError(err)
|
|
|
|
|
}
|
|
|
|
|
s.disconnectClient()
|
2020-10-28 22:32:45 +01:00
|
|
|
<-s.deinitNeededChan
|
2020-10-25 16:15:39 +01:00
|
|
|
s.deinitFinishedChan <- true
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Print("client ", s.client.RemoteAddr().String(), " connected")
|
|
|
|
|
|
|
|
|
|
s.writeLoopDeinitNeededChan = make(chan bool)
|
|
|
|
|
s.writeLoopDeinitFinishedChan = make(chan bool)
|
|
|
|
|
writeErrChan := make(chan error)
|
|
|
|
|
go s.writeLoop(writeErrChan)
|
|
|
|
|
|
|
|
|
|
connected := true
|
|
|
|
|
for connected {
|
2020-10-28 22:15:54 +01:00
|
|
|
b := make([]byte, maxSerialFrameLength)
|
2020-10-25 16:15:39 +01:00
|
|
|
n, err := s.client.Read(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
case s.fromClient <- b[:n]:
|
|
|
|
|
case <-writeErrChan:
|
|
|
|
|
connected = false
|
2020-10-28 22:32:45 +01:00
|
|
|
case <-s.deinitNeededChan:
|
|
|
|
|
s.disconnectClient()
|
|
|
|
|
s.deinitFinishedChan <- true
|
|
|
|
|
return
|
2020-10-25 16:15:39 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 22:32:45 +01:00
|
|
|
s.disconnectClient()
|
2020-10-25 16:15:39 +01:00
|
|
|
log.Print("client ", s.client.RemoteAddr().String(), " disconnected")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 22:32:45 +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)
|
2020-10-28 22:32:45 +01:00
|
|
|
s.toClient = make(chan []byte)
|
2020-10-25 16:15:39 +01:00
|
|
|
|
2020-10-28 22:32:45 +01:00
|
|
|
s.deinitNeededChan = make(chan bool)
|
2020-10-25 16:15:39 +01:00
|
|
|
s.deinitFinishedChan = make(chan bool)
|
|
|
|
|
go s.loop()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 22:32:45 +01:00
|
|
|
func (s *serialTCPSrvStruct) deinit() {
|
2020-10-25 16:15:39 +01:00
|
|
|
if s.listener != nil {
|
|
|
|
|
s.listener.Close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s.disconnectClient()
|
2020-10-28 22:32:45 +01:00
|
|
|
s.deinitNeededChan <- true
|
|
|
|
|
<-s.deinitFinishedChan
|
2020-10-25 16:15:39 +01:00
|
|
|
}
|