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 | | 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** |

171
ptc.go
View File

@@ -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 {
// _read: Read from serial connection (NOT thread safe). To be used from read writeDebug("Error received during read: "+err.Error(), 1)
func (p *Modem) _read(chunkSize int) (int, []byte, error) { return 0, []byte{}, err
}
return n, buf[0:n], nil
} else {
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)
@@ -931,4 +882,62 @@ 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
}
} }