Fix hanging exec cmd instances, rework exec cmdline args

This commit is contained in:
Nonoo 2020-11-03 10:42:38 +01:00
parent 47a22e5cce
commit c98f1bd7a8
6 changed files with 93 additions and 59 deletions

View file

@ -53,33 +53,19 @@ host **ic-705** (ic-705.local or ic-705.localdomain).
After it is connected and logged in: After it is connected and logged in:
- Creates a virtual PulseAudio **sound card** (48kHz, s16le, mono). This can be - Creates a virtual PulseAudio **sound card** (48kHz, s16le, mono). This can be
used to record/play audio from/to the server (the radio). You can also set used to record/play audio from/to the server (the transceiver). You can also
this sound card in [WSJT-X](https://physics.princeton.edu/pulsar/K1JT/wsjtx.html). set this sound card in [WSJT-X](https://physics.princeton.edu/pulsar/K1JT/wsjtx.html).
If you want to listen to the audio coming from this sound card in real time,
then you can create a [PulseAudio loopback](https://github.com/alentoghostflame/Python-Pulseaudio-Loopback-Tool)
between the kappanhang sound card and your real sound card. You can also
create a loopback for your microphone using this tool, so you'll be able to
transmit your voice.
- Starts a **TCP server** on port `4533` for exposing the **serial port**. - Starts a **TCP server** on port `4533` for exposing the **serial port**.
- Starts `rigctld` and connects it to kappanhang's TCP serial port server.
This can be used for controlling the server (the transceiver) with This can be used for controlling the server (the transceiver) with
[Hamlib](https://hamlib.github.io/) (`rigctld`). [Hamlib](https://hamlib.github.io/) (`rigctld`).
- Runs the command `rigctld -m 3085 -r :4533` which starts `rigctld` and
connects it to kappanhang's TCP serial port server. You can specify a custom
command with the `-r` command line argument. Running any command can be
disabled with `-r -`. The command is only executed once, as the TCP serial
port server will stay on even if the RS-BA1 server disconnects. If the TCP
serial port client disconnects (rigctld hangs) then the command will be
automatically restarted. This can be disabled with the `-e`.
3085 is the model number of the Icom IC-705. `rigctld` will connect to
kappanhang's TCP serial port server, and waits connections on it's default
TCP port `4532`.
To use this with for example [WSJT-X](https://physics.princeton.edu/pulsar/K1JT/wsjtx.html), To use this with for example [WSJT-X](https://physics.princeton.edu/pulsar/K1JT/wsjtx.html),
open WSJT-X settings, go to the *Radio* tab, set the *rig type* to `Hamlib NET open WSJT-X settings, go to the *Radio* tab, set the *rig type* to `Hamlib NET
rigctl`, and the *Network server* to `localhost`. rigctl`, and the *Network server* to `localhost`.
### Notes on command line arguments
If the `-s` command line argument is specified, then kappanhang will create a If the `-s` command line argument is specified, then kappanhang will create a
**virtual serial port**, so other apps which don't support Hamlib can access **virtual serial port**, so other apps which don't support Hamlib can access
the transceiver directly. Look at the app log to find out the name of the the transceiver directly. Look at the app log to find out the name of the
@ -148,18 +134,12 @@ not available.
### Hotkeys ### Hotkeys
- `q` (quit): closes the app - `q` (quit): closes the app
- `l` (listen): toggles audio stream playback to the default sound device - `l` (listen): toggles audio stream playback to the default sound device.
This is useful for quickly listening into the audio stream coming from the This is useful for quickly listening into the audio stream coming from the
server (the transceiver). server (the transceiver).
Note that audio will be played to the previously created virtual sound card
regardless of this setting.
- `space`: toggles PTT and audio stream recording from the default sound - `space`: toggles PTT and audio stream recording from the default sound
device device. You can transmit your own voice using a mic attached to your
computer for example.
You can transmit your own voice using a mic attached to your computer for
example.
Some basic CAT control hotkeys are also supported: Some basic CAT control hotkeys are also supported:

17
args.go
View file

@ -12,8 +12,9 @@ var verboseLog bool
var connectAddress string var connectAddress string
var serialTCPPort uint16 var serialTCPPort uint16
var enableSerialDevice bool var enableSerialDevice bool
var rigctldModel uint
var disableRigctld bool
var runCmd string var runCmd string
var disableReRunCmd bool
var runCmdOnSerialPortCreated string var runCmdOnSerialPortCreated string
var statusLogInterval time.Duration var statusLogInterval time.Duration
@ -21,11 +22,12 @@ func parseArgs() {
h := getopt.BoolLong("help", 'h', "display help") h := getopt.BoolLong("help", 'h', "display help")
v := getopt.BoolLong("verbose", 'v', "Enable verbose (debug) logging") v := getopt.BoolLong("verbose", 'v', "Enable verbose (debug) logging")
a := getopt.StringLong("address", 'a', "IC-705", "Connect to address") a := getopt.StringLong("address", 'a', "IC-705", "Connect to address")
t := getopt.Uint16Long("serial-tcp-port", 'p', 4533, "Expose radio's serial port on this TCP port") t := getopt.Uint16Long("serial-tcp-port", 't', 4533, "Expose radio's serial port on this TCP port")
s := getopt.BoolLong("enable-serial-device", 's', "Expose radio's serial port as a virtual serial port") s := getopt.BoolLong("enable-serial-device", 's', "Expose radio's serial port as a virtual serial port")
r := getopt.StringLong("run", 'r', "rigctld -m 3085 -r :4533", "Exec cmd when connected, set to - to disable") m := getopt.UintLong("rigctld-model", 'm', 3085, "rigctld model number")
e := getopt.BoolLong("disable-rerun", 'e', "Disable re-execing the cmd on TCP serial port disconnect") r := getopt.BoolLong("disable-rigctld", 'r', "Disable starting rigctld")
o := getopt.StringLong("run-serial", 'o', "socat /tmp/kappanhang-IC-705.pty /tmp/vmware.pty", "Exec cmd when virtual serial port is created, set to - to disable") e := getopt.StringLong("exec", 'e', "", "Exec cmd when connected")
o := getopt.StringLong("exec-serial", 'o', "socat /tmp/kappanhang-IC-705.pty /tmp/vmware.pty", "Exec cmd when virtual serial port is created, set to - to disable")
i := getopt.Uint16Long("log-interval", 'i', 100, "Status bar/log interval in milliseconds") i := getopt.Uint16Long("log-interval", 'i', 100, "Status bar/log interval in milliseconds")
getopt.Parse() getopt.Parse()
@ -40,8 +42,9 @@ func parseArgs() {
connectAddress = *a connectAddress = *a
serialTCPPort = *t serialTCPPort = *t
enableSerialDevice = *s enableSerialDevice = *s
runCmd = *r rigctldModel = *m
disableReRunCmd = *e disableRigctld = *r
runCmd = *e
runCmdOnSerialPortCreated = *o runCmdOnSerialPortCreated = *o
statusLogInterval = time.Duration(*i) * time.Millisecond statusLogInterval = time.Duration(*i) * time.Millisecond
} }

View file

@ -259,9 +259,8 @@ func (s *controlStream) handleRead(r []byte) error {
statusLog.startPeriodicPrint() statusLog.startPeriodicPrint()
startCmdIfNeeded() startCmdIfNeeded()
if enableSerialDevice { startSerialPortCmdIfNeeded()
startSerialPortCmdIfNeeded() startRigctldCmdIfNeeded()
}
} }
} }
return nil return nil

View file

@ -134,6 +134,7 @@ func main() {
stopCmd() stopCmd()
stopSerialPortCmd() stopSerialPortCmd()
stopRigctldCmd()
audio.deinit() audio.deinit()
serialTCPSrv.deinit() serialTCPSrv.deinit()
serialPort.deinit() serialPort.deinit()

View file

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"os/exec" "os/exec"
"strings" "strings"
"time" "time"
@ -8,8 +9,48 @@ import (
const startCmdDelay = time.Second const startCmdDelay = time.Second
var startedRigctldCmd *exec.Cmd
var rigctldCmdStartTimer *time.Timer
var startedCmd *exec.Cmd var startedCmd *exec.Cmd
var cmdStartTimer *time.Timer
var serialPortStartedCmd *exec.Cmd var serialPortStartedCmd *exec.Cmd
var serialPortCmdStartTimer *time.Timer
func doStartRigctldCmd() {
startedRigctldCmd = exec.Command("rigctld", "-m", fmt.Sprint(rigctldModel), "-r",
fmt.Sprint(":", serialTCPPort))
err := startedRigctldCmd.Start()
if err == nil {
log.Print("rigctld started: ", startedRigctldCmd)
} else {
log.Error("error starting rigctld: ", err)
startedCmd = nil
}
rigctldCmdStartTimer = nil
}
func startRigctldCmdIfNeeded() {
if startedRigctldCmd != nil || disableRigctld {
return
}
if rigctldCmdStartTimer != nil {
rigctldCmdStartTimer.Stop()
}
rigctldCmdStartTimer = time.AfterFunc(startCmdDelay, doStartRigctldCmd)
}
func stopRigctldCmd() {
if startedRigctldCmd == nil {
return
}
if err := startedRigctldCmd.Process.Kill(); err != nil {
log.Error("failed to stop rigctld: ", err)
}
_ = startedRigctldCmd.Wait()
startedRigctldCmd = nil
}
func doStartCmd() { func doStartCmd() {
c := strings.Split(runCmd, " ") c := strings.Split(runCmd, " ")
@ -21,14 +62,29 @@ func doStartCmd() {
log.Error("error starting ", runCmd, ": ", err) log.Error("error starting ", runCmd, ": ", err)
startedCmd = nil startedCmd = nil
} }
cmdStartTimer = nil
} }
func startCmdIfNeeded() { func startCmdIfNeeded() {
if startedCmd != nil || runCmd == "-" { if startedCmd != nil || runCmd == "" {
return return
} }
time.AfterFunc(startCmdDelay, doStartCmd) if cmdStartTimer != nil {
cmdStartTimer.Stop()
}
cmdStartTimer = time.AfterFunc(startCmdDelay, doStartCmd)
}
func stopCmd() {
if startedCmd == nil {
return
}
if err := startedCmd.Process.Kill(); err != nil {
log.Error("failed to stop cmd ", runCmd, ": ", err)
}
startedCmd = nil
} }
func doSerialPortStartCmd() { func doSerialPortStartCmd() {
@ -41,30 +97,27 @@ func doSerialPortStartCmd() {
log.Error("error starting ", runCmdOnSerialPortCreated, ": ", err) log.Error("error starting ", runCmdOnSerialPortCreated, ": ", err)
serialPortStartedCmd = nil serialPortStartedCmd = nil
} }
serialPortCmdStartTimer = nil
} }
func startSerialPortCmdIfNeeded() { func startSerialPortCmdIfNeeded() {
if serialPortStartedCmd != nil || runCmdOnSerialPortCreated == "-" { if !enableSerialDevice || serialPortStartedCmd != nil || runCmdOnSerialPortCreated == "-" {
return return
} }
time.AfterFunc(startCmdDelay, doSerialPortStartCmd) if serialPortCmdStartTimer != nil {
} serialPortCmdStartTimer.Stop()
func stopCmd() {
if startedCmd != nil {
if err := startedCmd.Process.Kill(); err != nil {
log.Error("failed to stop cmd ", runCmd, ": ", err)
}
startedCmd = nil
} }
serialPortCmdStartTimer = time.AfterFunc(startCmdDelay, doSerialPortStartCmd)
} }
func stopSerialPortCmd() { func stopSerialPortCmd() {
if serialPortStartedCmd != nil { if serialPortStartedCmd == nil {
if err := serialPortStartedCmd.Process.Kill(); err != nil { return
log.Error("failed to stop cmd ", runCmdOnSerialPortCreated, ": ", err)
}
serialPortStartedCmd = nil
} }
if err := serialPortStartedCmd.Process.Kill(); err != nil {
log.Error("failed to stop cmd ", runCmdOnSerialPortCreated, ": ", err)
}
serialPortStartedCmd = nil
} }

View file

@ -104,10 +104,8 @@ func (s *serialTCPSrvStruct) loop() {
s.disconnectClient() s.disconnectClient()
log.Print("client ", s.client.RemoteAddr().String(), " disconnected") log.Print("client ", s.client.RemoteAddr().String(), " disconnected")
if !disableReRunCmd { stopRigctldCmd()
stopCmd() startRigctldCmdIfNeeded()
startCmdIfNeeded()
}
} }
} }