Option to use a TCP socket instead of a serial port. Closes #2.
This commit is contained in:
@@ -43,7 +43,7 @@ vara_mode: false
|
|||||||
|
|
||||||
| Variable | Meaning |
|
| Variable | Meaning |
|
||||||
|-------------------|-----------------------------------------------------------------------------------------------------------------------------|
|
|-------------------|-----------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `device` | Path to the serial device where the modem is connected |
|
| `device` | Path to the serial device where the modem is connected **or** `tcp://address:port` (useful for Android) |
|
||||||
| `baudrate` | baud rate of the modem |
|
| `baudrate` | baud rate of the modem |
|
||||||
| `mycall` | The callsign to be used on HF |
|
| `mycall` | The callsign to be used on HF |
|
||||||
| `server_address` | server socket address for the **commands** |
|
| `server_address` | server socket address for the **commands** |
|
||||||
|
169
ptc.go
169
ptc.go
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/albenik/go-serial/v2"
|
"github.com/albenik/go-serial/v2"
|
||||||
"log"
|
"log"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
"runtime"
|
||||||
@@ -46,13 +47,10 @@ type pflags struct {
|
|||||||
|
|
||||||
type pmux struct {
|
type pmux struct {
|
||||||
device sync.Mutex
|
device sync.Mutex
|
||||||
pactor sync.Mutex
|
|
||||||
write sync.Mutex
|
write sync.Mutex
|
||||||
read sync.Mutex
|
read sync.Mutex
|
||||||
close sync.Mutex
|
close sync.Mutex
|
||||||
bufLen sync.Mutex
|
bufLen sync.Mutex
|
||||||
// sendbuf sync.Mutex
|
|
||||||
// recvbuf sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Modem struct {
|
type Modem struct {
|
||||||
@@ -64,13 +62,11 @@ type Modem struct {
|
|||||||
state State
|
state State
|
||||||
|
|
||||||
device *serial.Port
|
device *serial.Port
|
||||||
|
tcpdevice net.Conn
|
||||||
mux pmux
|
mux pmux
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
flags pflags
|
flags pflags
|
||||||
goodChunks int
|
goodChunks int
|
||||||
// recvBuf bytes.Buffer
|
|
||||||
// cmdBuf chan string
|
|
||||||
// sendBuf bytes.Buffer
|
|
||||||
packetcounter bool
|
packetcounter bool
|
||||||
chanbusy bool
|
chanbusy bool
|
||||||
cmdlineinit string
|
cmdlineinit string
|
||||||
@@ -149,17 +145,24 @@ func OpenModem(path string, baudRate int, myCall string, initfile string, cmdlin
|
|||||||
}
|
}
|
||||||
|
|
||||||
writeDebug("Initialising pactor modem", 1)
|
writeDebug("Initialising pactor modem", 1)
|
||||||
if err := p.checkSerialDevice(); err != nil {
|
|
||||||
writeDebug(err.Error(), 1)
|
if strings.HasPrefix(p.devicePath, "tcp://") {
|
||||||
|
if p.tcpdevice, err = net.Dial("tcp", strings.Replace(p.devicePath, "tcp://", "", 1)); err != nil {
|
||||||
|
writeDebug(err.Error(), 0)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Check if serial device exists
|
||||||
|
if err := p.checkSerialDevice(); err != nil {
|
||||||
|
writeDebug(err.Error(), 0)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setup serial device
|
//Setup serial device
|
||||||
if p.device, err = serial.Open(p.devicePath, serial.WithBaudrate(baudRate), serial.WithReadTimeout(SerialTimeout)); err != nil {
|
if p.device, err = serial.Open(p.devicePath, serial.WithBaudrate(baudRate), serial.WithReadTimeout(SerialTimeout)); err != nil {
|
||||||
writeDebug(err.Error(), 1)
|
writeDebug(err.Error(), 1)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
err = p.init()
|
err = p.init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -410,10 +413,11 @@ func (p *Modem) stophostmode() {
|
|||||||
|
|
||||||
for ok == false {
|
for ok == false {
|
||||||
buff := make([]byte, 100)
|
buff := make([]byte, 100)
|
||||||
p.device.Write([]byte{0xaa, 0xaa, 0x00, 0x01, 0x05, 0x4a, 0x48, 0x4f, 0x53, 0x54, 0x30, 0xfb, 0x3d})
|
//var n int
|
||||||
|
_ = p.write(string([]byte{0xaa, 0xaa, 0x00, 0x01, 0x05, 0x4a, 0x48, 0x4f, 0x53, 0x54, 0x30, 0xfb, 0x3d}))
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
for {
|
for {
|
||||||
n, err := p.device.Read(buff)
|
n, _, err := p.read(100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
break
|
break
|
||||||
@@ -421,12 +425,11 @@ func (p *Modem) stophostmode() {
|
|||||||
if n == 0 {
|
if n == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
//fmt.Printf("%v", string(buff[:n]))
|
|
||||||
}
|
}
|
||||||
p.device.Write([]byte("\rRESTART\r"))
|
p.write("\rRESTART\r")
|
||||||
time.Sleep(1000 * time.Millisecond)
|
time.Sleep(1000 * time.Millisecond)
|
||||||
for {
|
for {
|
||||||
n, err := p.device.Read(buff)
|
n, a, err := p.read(100)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
break
|
break
|
||||||
@@ -434,6 +437,7 @@ func (p *Modem) stophostmode() {
|
|||||||
if n == 0 {
|
if n == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
copy(buff, a)
|
||||||
//fmt.Printf("%v", string(buff[:n]))
|
//fmt.Printf("%v", string(buff[:n]))
|
||||||
}
|
}
|
||||||
ok, err = regexp.Match("cmd:", buff)
|
ok, err = regexp.Match("cmd:", buff)
|
||||||
@@ -481,16 +485,12 @@ func (p *Modem) close() (err error) {
|
|||||||
writeDebug("PACTOR close called", 1)
|
writeDebug("PACTOR close called", 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
//p.disconnect()
|
|
||||||
writeDebug("signal modem thread to stop", 1)
|
writeDebug("signal modem thread to stop", 1)
|
||||||
p.flags.stopmodem = true
|
p.flags.stopmodem = true
|
||||||
writeDebug("waiting for all threads to exit", 1)
|
writeDebug("waiting for all threads to exit", 1)
|
||||||
p.wg.Wait()
|
p.wg.Wait()
|
||||||
writeDebug("modem thread stopped", 1)
|
writeDebug("modem thread stopped", 1)
|
||||||
|
|
||||||
//p.hostmodeQuit()
|
|
||||||
// will not close the serial port as we reuse it
|
|
||||||
//p.device.Close()
|
|
||||||
p.stophostmode()
|
p.stophostmode()
|
||||||
p.setState(Closed)
|
p.setState(Closed)
|
||||||
writeDebug("PACTOR close() finished", 1)
|
writeDebug("PACTOR close() finished", 1)
|
||||||
@@ -641,7 +641,7 @@ func (p *Modem) writeAndGetResponse(msg string, ch int, isCommand bool, chunkSiz
|
|||||||
var n int
|
var n int
|
||||||
var str string
|
var str string
|
||||||
if ch < 0 {
|
if ch < 0 {
|
||||||
err = p._write(msg + "\r")
|
err = p.write(msg + "\r")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, "", err
|
return 0, "", err
|
||||||
}
|
}
|
||||||
@@ -649,7 +649,7 @@ func (p *Modem) writeAndGetResponse(msg string, ch int, isCommand bool, chunkSiz
|
|||||||
i := 0
|
i := 0
|
||||||
var tmp []byte
|
var tmp []byte
|
||||||
for {
|
for {
|
||||||
n, tmp, err = p._read(chunkSize)
|
n, tmp, err = p.read(chunkSize)
|
||||||
str = string(tmp)
|
str = string(tmp)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
@@ -685,7 +685,7 @@ func (p *Modem) writeAndGetResponse(msg string, ch int, isCommand bool, chunkSiz
|
|||||||
return 0, "", err
|
return 0, "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
br, b, err := p._read(1)
|
br, b, err := p.read(1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeDebug("ERROR at _read: "+error.Error(err), 1)
|
writeDebug("ERROR at _read: "+error.Error(err), 1)
|
||||||
}
|
}
|
||||||
@@ -737,56 +737,6 @@ func (p *Modem) Busy() bool {
|
|||||||
return p.chanbusy
|
return p.chanbusy
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to serial connection (thread safe)
|
|
||||||
//
|
|
||||||
// No other read/write operation allowed during this time
|
|
||||||
func (p *Modem) write(cmd string) error {
|
|
||||||
p.mux.pactor.Lock()
|
|
||||||
defer p.mux.pactor.Unlock()
|
|
||||||
return p._write(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write to serial connection (NOT thread safe)
|
|
||||||
//
|
|
||||||
// If used, make shure to lock/unlock p.mux.pactor mutex!
|
|
||||||
func (p *Modem) _write(cmd string) error {
|
|
||||||
if err := p.checkSerialDevice(); err != nil {
|
|
||||||
writeDebug(err.Error(), 1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writeDebug("write: \n"+hex.Dump([]byte(cmd)), 3)
|
|
||||||
|
|
||||||
p.mux.device.Lock()
|
|
||||||
defer p.mux.device.Unlock()
|
|
||||||
out := cmd + "\r"
|
|
||||||
for {
|
|
||||||
|
|
||||||
// check if the serial line is clear-to-send
|
|
||||||
status, err := cts(p.device)
|
|
||||||
if err != nil {
|
|
||||||
writeDebug("GetModemStatusBits failed. cmd: "+cmd+" Error: "+err.Error(), 1)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if status {
|
|
||||||
for {
|
|
||||||
sent, err := p.device.Write([]byte(out))
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
// log.Errorf("ERROR while sending serial command: %s\n", out)
|
|
||||||
writeDebug(err.Error(), 2)
|
|
||||||
out = out[sent:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// *** Helper functions for the CRC hostmode
|
// *** Helper functions for the CRC hostmode
|
||||||
// Although these functions are small, I prefer to keep their functionality
|
// Although these functions are small, I prefer to keep their functionality
|
||||||
// separate. They follow the steps in the SCS documentation
|
// separate. They follow the steps in the SCS documentation
|
||||||
@@ -908,20 +858,21 @@ func (p *Modem) writeChannel(msg string, ch int, isCommand bool) error {
|
|||||||
|
|
||||||
// read: Read from serial connection (thread safe)
|
// read: Read from serial connection (thread safe)
|
||||||
func (p *Modem) read(chunkSize int) (int, []byte, error) {
|
func (p *Modem) read(chunkSize int) (int, []byte, error) {
|
||||||
p.mux.pactor.Lock()
|
buf := make([]byte, chunkSize)
|
||||||
defer p.mux.pactor.Unlock()
|
if strings.HasPrefix(p.devicePath, "tcp://") {
|
||||||
return p._read(chunkSize)
|
p.tcpdevice.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
|
||||||
|
n, err := p.tcpdevice.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
writeDebug("Error received during read: "+err.Error(), 1)
|
||||||
|
return 0, []byte{}, err
|
||||||
}
|
}
|
||||||
|
return n, buf[0:n], nil
|
||||||
// _read: Read from serial connection (NOT thread safe). To be used from read
|
} else {
|
||||||
func (p *Modem) _read(chunkSize int) (int, []byte, error) {
|
|
||||||
if err := p.checkSerialDevice(); err != nil {
|
if err := p.checkSerialDevice(); err != nil {
|
||||||
writeDebug(err.Error(), 1)
|
writeDebug(err.Error(), 1)
|
||||||
return 0, []byte{}, err
|
return 0, []byte{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := make([]byte, chunkSize)
|
|
||||||
|
|
||||||
p.mux.device.Lock()
|
p.mux.device.Lock()
|
||||||
defer p.mux.device.Unlock()
|
defer p.mux.device.Unlock()
|
||||||
n, err := p.device.Read(buf)
|
n, err := p.device.Read(buf)
|
||||||
@@ -932,3 +883,61 @@ func (p *Modem) _read(chunkSize int) (int, []byte, error) {
|
|||||||
|
|
||||||
return n, buf[0:n], nil
|
return n, buf[0:n], nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write to serial connection (thread safe)
|
||||||
|
//
|
||||||
|
// No other read/write operation allowed during this time
|
||||||
|
func (p *Modem) write(cmd string) error {
|
||||||
|
if strings.HasPrefix(p.devicePath, "tcp://") {
|
||||||
|
|
||||||
|
//TCP connection
|
||||||
|
out := cmd + "\r"
|
||||||
|
sent, err := p.tcpdevice.Write([]byte(out))
|
||||||
|
if err == nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
writeDebug(err.Error(), 2)
|
||||||
|
out = out[sent:]
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//Serial connection
|
||||||
|
if err := p.checkSerialDevice(); err != nil {
|
||||||
|
writeDebug(err.Error(), 1)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
writeDebug("write: \n"+hex.Dump([]byte(cmd)), 3)
|
||||||
|
|
||||||
|
p.mux.device.Lock()
|
||||||
|
defer p.mux.device.Unlock()
|
||||||
|
out := cmd + "\r"
|
||||||
|
for {
|
||||||
|
|
||||||
|
// check if the serial line is clear-to-send
|
||||||
|
status, err := cts(p.device)
|
||||||
|
if err != nil {
|
||||||
|
writeDebug("GetModemStatusBits failed. cmd: "+cmd+" Error: "+err.Error(), 1)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if status {
|
||||||
|
for {
|
||||||
|
sent, err := p.device.Write([]byte(out))
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
writeDebug(err.Error(), 2)
|
||||||
|
out = out[sent:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user