Option to use a TCP socket instead of a serial port. Closes #2.

This commit is contained in:
Torsten Harenberg
2025-02-05 16:18:05 +01:00
parent 71d2cfb652
commit 0c543393d8
2 changed files with 112 additions and 103 deletions

View File

@@ -43,7 +43,7 @@ vara_mode: false
| 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 |
| `mycall` | The callsign to be used on HF |
| `server_address` | server socket address for the **commands** |

169
ptc.go
View File

@@ -11,6 +11,7 @@ import (
"github.com/albenik/go-serial/v2"
"log"
"math/bits"
"net"
"os"
"regexp"
"runtime"
@@ -46,13 +47,10 @@ type pflags struct {
type pmux struct {
device sync.Mutex
pactor sync.Mutex
write sync.Mutex
read sync.Mutex
close sync.Mutex
bufLen sync.Mutex
// sendbuf sync.Mutex
// recvbuf sync.Mutex
}
type Modem struct {
@@ -64,13 +62,11 @@ type Modem struct {
state State
device *serial.Port
tcpdevice net.Conn
mux pmux
wg sync.WaitGroup
flags pflags
goodChunks int
// recvBuf bytes.Buffer
// cmdBuf chan string
// sendBuf bytes.Buffer
packetcounter bool
chanbusy bool
cmdlineinit string
@@ -149,17 +145,24 @@ func OpenModem(path string, baudRate int, myCall string, initfile string, cmdlin
}
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
}
//Setup serial device
if p.device, err = serial.Open(p.devicePath, serial.WithBaudrate(baudRate), serial.WithReadTimeout(SerialTimeout)); err != nil {
writeDebug(err.Error(), 1)
return nil, err
}
}
err = p.init()
if err != nil {
return nil, err
@@ -410,10 +413,11 @@ func (p *Modem) stophostmode() {
for ok == false {
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)
for {
n, err := p.device.Read(buff)
n, _, err := p.read(100)
if err != nil {
log.Fatal(err)
break
@@ -421,12 +425,11 @@ func (p *Modem) stophostmode() {
if n == 0 {
break
}
//fmt.Printf("%v", string(buff[:n]))
}
p.device.Write([]byte("\rRESTART\r"))
p.write("\rRESTART\r")
time.Sleep(1000 * time.Millisecond)
for {
n, err := p.device.Read(buff)
n, a, err := p.read(100)
if err != nil {
log.Fatal(err)
break
@@ -434,6 +437,7 @@ func (p *Modem) stophostmode() {
if n == 0 {
break
}
copy(buff, a)
//fmt.Printf("%v", string(buff[:n]))
}
ok, err = regexp.Match("cmd:", buff)
@@ -481,16 +485,12 @@ func (p *Modem) close() (err error) {
writeDebug("PACTOR close called", 1)
}
//p.disconnect()
writeDebug("signal modem thread to stop", 1)
p.flags.stopmodem = true
writeDebug("waiting for all threads to exit", 1)
p.wg.Wait()
writeDebug("modem thread stopped", 1)
//p.hostmodeQuit()
// will not close the serial port as we reuse it
//p.device.Close()
p.stophostmode()
p.setState(Closed)
writeDebug("PACTOR close() finished", 1)
@@ -641,7 +641,7 @@ func (p *Modem) writeAndGetResponse(msg string, ch int, isCommand bool, chunkSiz
var n int
var str string
if ch < 0 {
err = p._write(msg + "\r")
err = p.write(msg + "\r")
if err != nil {
return 0, "", err
}
@@ -649,7 +649,7 @@ func (p *Modem) writeAndGetResponse(msg string, ch int, isCommand bool, chunkSiz
i := 0
var tmp []byte
for {
n, tmp, err = p._read(chunkSize)
n, tmp, err = p.read(chunkSize)
str = string(tmp)
if err == nil {
break
@@ -685,7 +685,7 @@ func (p *Modem) writeAndGetResponse(msg string, ch int, isCommand bool, chunkSiz
return 0, "", err
}
}
br, b, err := p._read(1)
br, b, err := p.read(1)
if err != nil {
writeDebug("ERROR at _read: "+error.Error(err), 1)
}
@@ -737,56 +737,6 @@ func (p *Modem) Busy() bool {
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
// Although these functions are small, I prefer to keep their functionality
// 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)
func (p *Modem) read(chunkSize int) (int, []byte, error) {
p.mux.pactor.Lock()
defer p.mux.pactor.Unlock()
return p._read(chunkSize)
buf := make([]byte, chunkSize)
if strings.HasPrefix(p.devicePath, "tcp://") {
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
}
// _read: Read from serial connection (NOT thread safe). To be used from read
func (p *Modem) _read(chunkSize int) (int, []byte, error) {
return n, buf[0:n], nil
} else {
if err := p.checkSerialDevice(); err != nil {
writeDebug(err.Error(), 1)
return 0, []byte{}, err
}
buf := make([]byte, chunkSize)
p.mux.device.Lock()
defer p.mux.device.Unlock()
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
}
}
// 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
}
}