FreeDATA/tnc/daemon.py

425 lines
14 KiB
Python
Raw Normal View History

#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
daemon.py
Author: DJ2LS, January 2022
daemon for providing basic information for the tnc like audio or serial devices
"""
import argparse
import threading
import socketserver
import time
2021-08-23 16:14:00 +00:00
import sys
2021-09-25 13:24:25 +00:00
import subprocess
import ujson as json
import psutil
2021-09-04 20:13:15 +00:00
import serial.tools.list_ports
2021-09-25 13:24:25 +00:00
import static
import crcengine
2021-11-18 18:40:22 +00:00
import re
import structlog
import log_handler
import helpers
import os
import queue
import audio
import sock
import atexit
import signal
2022-02-17 13:25:22 +00:00
import multiprocessing
# signal handler for closing aplication
def signal_handler(sig, frame):
"""
signal handler for closing the network socket on app exit
Args:
2022-04-11 09:03:54 +00:00
sig:
frame:
Returns: system exit
"""
2022-04-11 09:03:54 +00:00
print("Closing daemon...")
sock.CLOSE_SIGNAL = True
sys.exit(0)
2022-04-11 09:03:54 +00:00
signal.signal(signal.SIGINT, signal_handler)
2022-04-11 09:03:54 +00:00
class DAEMON:
"""
daemon class
2022-04-11 09:03:54 +00:00
"""
2022-04-11 09:03:54 +00:00
def __init__(self):
2022-04-11 09:03:54 +00:00
# load crc engine
self.crc_algorithm = crcengine.new("crc16-ccitt-false") # load crc8 library
self.daemon_queue = sock.DAEMON_QUEUE
2022-04-11 09:03:54 +00:00
update_audio_devices = threading.Thread(
target=self.update_audio_devices, name="UPDATE_AUDIO_DEVICES", daemon=True
)
update_audio_devices.start()
2022-04-11 09:03:54 +00:00
update_serial_devices = threading.Thread(
target=self.update_serial_devices, name="UPDATE_SERIAL_DEVICES", daemon=True
)
update_serial_devices.start()
worker = threading.Thread(target=self.worker, name="WORKER", daemon=True)
worker.start()
2022-04-11 09:03:54 +00:00
def update_audio_devices(self):
2022-04-11 09:03:54 +00:00
"""
update audio devices and set to static
"""
while 1:
2022-02-15 17:10:14 +00:00
try:
2022-02-17 13:25:22 +00:00
if not static.TNCSTARTED:
2022-04-11 09:03:54 +00:00
(
static.AUDIO_INPUT_DEVICES,
static.AUDIO_OUTPUT_DEVICES,
) = audio.get_audio_devices()
2022-02-15 17:10:14 +00:00
except Exception as e:
print(e)
2022-02-19 20:30:52 +00:00
time.sleep(1)
2022-04-11 09:03:54 +00:00
def update_serial_devices(self):
"""
update serial devices and set to static
"""
while 1:
2022-02-15 17:10:14 +00:00
try:
2022-04-11 09:03:54 +00:00
# print("update serial")
2022-02-15 17:10:14 +00:00
serial_devices = []
ports = serial.tools.list_ports.comports()
for port, desc, hwid in ports:
2022-04-11 09:03:54 +00:00
2022-02-15 17:10:14 +00:00
# calculate hex of hwid if we have unique names
2022-04-11 09:03:54 +00:00
crc_hwid = self.crc_algorithm(bytes(hwid, encoding="utf-8"))
crc_hwid = crc_hwid.to_bytes(2, byteorder="big")
2022-02-15 17:10:14 +00:00
crc_hwid = crc_hwid.hex()
2022-04-11 09:03:54 +00:00
description = desc + " [" + crc_hwid + "]"
serial_devices.append(
{"port": str(port), "description": str(description)}
)
2022-02-15 17:10:14 +00:00
static.SERIAL_DEVICES = serial_devices
time.sleep(1)
except Exception as e:
print(e)
2022-04-11 09:03:54 +00:00
def worker(self):
"""
a worker for the received commands
"""
while 1:
2022-02-15 17:10:14 +00:00
try:
2022-04-11 09:03:54 +00:00
2022-02-15 17:10:14 +00:00
data = self.daemon_queue.get()
2022-02-15 17:10:14 +00:00
# data[1] mycall
# data[2] mygrid
# data[3] rx_audio
# data[4] tx_audio
# data[5] devicename
# data[6] deviceport
# data[7] serialspeed
# data[8] pttprotocol
# data[9] pttport
# data[10] data_bits
# data[11] stop_bits
# data[12] handshake
# data[13] radiocontrol
# data[14] rigctld_ip
# data[15] rigctld_port
# data[16] send_scatter
# data[17] send_fft
# data[18] low_bandwith_mode
2022-03-19 11:42:10 +00:00
# data[19] tuning_range_fmin
# data[20] tuning_range_fmax
2022-04-11 09:03:54 +00:00
if data[0] == "STARTTNC":
structlog.get_logger("structlog").warning(
"[DMN] Starting TNC", rig=data[5], port=data[6]
)
2022-02-15 17:10:14 +00:00
# list of parameters, necessary for running subprocess command as a list
options = []
2022-04-11 09:03:54 +00:00
options.append("--port")
2022-02-15 17:10:14 +00:00
options.append(str(static.DAEMONPORT - 1))
2022-04-11 09:03:54 +00:00
options.append("--mycall")
2022-02-15 17:10:14 +00:00
options.append(data[1])
2022-04-11 09:03:54 +00:00
options.append("--mygrid")
2022-02-15 17:10:14 +00:00
options.append(data[2])
2022-04-11 09:03:54 +00:00
options.append("--rx")
2022-02-15 17:10:14 +00:00
options.append(data[3])
2022-04-11 09:03:54 +00:00
options.append("--tx")
2022-02-15 17:10:14 +00:00
options.append(data[4])
2022-04-11 09:03:54 +00:00
# if radiocontrol != disabled
2022-04-11 09:03:54 +00:00
# this should hopefully avoid a ton of problems if we are just running in
# disabled mode
2022-04-11 09:03:54 +00:00
if data[13] != "disabled":
options.append("--devicename")
options.append(data[5])
2022-04-11 09:03:54 +00:00
options.append("--deviceport")
options.append(data[6])
2022-04-11 09:03:54 +00:00
options.append("--serialspeed")
options.append(data[7])
2022-04-11 09:03:54 +00:00
options.append("--pttprotocol")
options.append(data[8])
2022-04-11 09:03:54 +00:00
options.append("--pttport")
options.append(data[9])
2022-04-11 09:03:54 +00:00
options.append("--data_bits")
options.append(data[10])
2022-04-11 09:03:54 +00:00
options.append("--stop_bits")
options.append(data[11])
2022-04-11 09:03:54 +00:00
options.append("--handshake")
options.append(data[12])
2022-04-11 09:03:54 +00:00
options.append("--radiocontrol")
options.append(data[13])
2022-04-11 09:03:54 +00:00
if data[13] != "rigctld":
options.append("--rigctld_ip")
options.append(data[14])
2022-04-11 09:03:54 +00:00
options.append("--rigctld_port")
options.append(data[15])
2022-04-11 09:03:54 +00:00
if data[16] == "True":
options.append("--scatter")
if data[17] == "True":
options.append("--fft")
if data[18] == "True":
options.append("--500hz")
options.append("--tuning_range_fmin")
2022-03-19 11:42:10 +00:00
options.append(data[19])
2022-04-11 09:03:54 +00:00
options.append("--tuning_range_fmax")
2022-03-19 11:42:10 +00:00
options.append(data[20])
# overriding FSK mode
2022-04-11 09:03:54 +00:00
# if data[21] == 'True':
# options.append('--fsk')
2022-03-31 19:13:30 +00:00
2022-04-11 09:03:54 +00:00
options.append("--tx-audio-level")
options.append(data[22])
2022-02-15 17:10:14 +00:00
# try running tnc from binary, else run from source
# this helps running the tnc in a developer environment
try:
command = []
2022-04-11 09:03:54 +00:00
if sys.platform == "linux" or sys.platform == "darwin":
command.append("./freedata-tnc")
elif sys.platform == "win32" or sys.platform == "win64":
command.append("freedata-tnc.exe")
2022-02-15 17:10:14 +00:00
command += options
p = subprocess.Popen(command)
2022-04-11 09:03:54 +00:00
2022-02-15 17:10:14 +00:00
atexit.register(p.kill)
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").info(
"[DMN] TNC started", path="binary"
)
2022-02-15 17:10:14 +00:00
except:
command = []
2022-04-11 09:03:54 +00:00
if sys.platform == "linux" or sys.platform == "darwin":
command.append("python3")
elif sys.platform == "win32" or sys.platform == "win64":
command.append("python")
command.append("main.py")
2022-02-15 17:10:14 +00:00
command += options
p = subprocess.Popen(command)
atexit.register(p.kill)
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").info(
"[DMN] TNC started", path="source"
)
2022-02-15 17:10:14 +00:00
static.TNCPROCESS = p # .pid
static.TNCSTARTED = True
2022-04-11 09:03:54 +00:00
"""
2022-02-15 17:10:14 +00:00
# WE HAVE THIS PART in SOCKET
if data[0] == 'STOPTNC':
static.TNCPROCESS.kill()
structlog.get_logger("structlog").warning("[DMN] Stopping TNC")
#os.kill(static.TNCPROCESS, signal.SIGKILL)
static.TNCSTARTED = False
2022-04-11 09:03:54 +00:00
"""
2022-02-15 17:10:14 +00:00
# data[1] devicename
# data[2] deviceport
# data[3] serialspeed
# data[4] pttprotocol
# data[5] pttport
# data[6] data_bits
# data[7] stop_bits
# data[8] handshake
# data[9] radiocontrol
# data[10] rigctld_ip
# data[11] rigctld_port
2022-04-11 09:03:54 +00:00
if data[0] == "TEST_HAMLIB":
2022-02-15 17:10:14 +00:00
devicename = data[1]
deviceport = data[2]
serialspeed = data[3]
pttprotocol = data[4]
pttport = data[5]
data_bits = data[6]
stop_bits = data[7]
handshake = data[8]
radiocontrol = data[9]
rigctld_ip = data[10]
rigctld_port = data[11]
2022-02-15 17:10:14 +00:00
# check how we want to control the radio
2022-04-11 09:03:54 +00:00
if radiocontrol == "direct":
2022-02-15 17:10:14 +00:00
import rig
2022-04-11 09:03:54 +00:00
elif radiocontrol == "rigctl":
2022-02-15 17:10:14 +00:00
import rigctl as rig
2022-04-11 09:03:54 +00:00
elif radiocontrol == "rigctld":
2022-02-15 17:10:14 +00:00
import rigctld as rig
else:
import rigdummy as rig
2022-04-11 09:03:54 +00:00
2022-02-15 17:10:14 +00:00
hamlib = rig.radio()
2022-04-11 09:03:54 +00:00
hamlib.open_rig(
devicename=devicename,
deviceport=deviceport,
hamlib_ptt_type=pttprotocol,
serialspeed=serialspeed,
pttport=pttport,
data_bits=data_bits,
stop_bits=stop_bits,
handshake=handshake,
rigctld_ip=rigctld_ip,
rigctld_port=rigctld_port,
)
2022-02-15 17:10:14 +00:00
hamlib_version = rig.hamlib_version
2022-04-11 09:03:54 +00:00
hamlib.set_ptt(True)
2022-02-15 17:10:14 +00:00
pttstate = hamlib.get_ptt()
2022-04-11 09:03:54 +00:00
2022-02-15 17:10:14 +00:00
if pttstate:
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").info(
"[DMN] Hamlib PTT", status="SUCCESS"
)
response = {"command": "test_hamlib", "result": "SUCCESS"}
2022-02-15 17:10:14 +00:00
elif not pttstate:
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").warning(
"[DMN] Hamlib PTT", status="NO SUCCESS"
)
response = {"command": "test_hamlib", "result": "NOSUCCESS"}
2022-02-15 17:10:14 +00:00
else:
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").error(
"[DMN] Hamlib PTT", status="FAILED"
)
response = {"command": "test_hamlib", "result": "FAILED"}
hamlib.set_ptt(False)
2022-02-15 17:10:14 +00:00
hamlib.close_rig()
2022-04-11 09:03:54 +00:00
2022-02-15 17:10:14 +00:00
jsondata = json.dumps(response)
sock.SOCKET_QUEUE.put(jsondata)
2022-04-11 09:03:54 +00:00
2022-02-15 17:10:14 +00:00
except Exception as e:
print(e)
2022-04-11 09:03:54 +00:00
if __name__ == "__main__":
2022-02-17 13:25:22 +00:00
# we need to run this on windows for multiprocessing support
multiprocessing.freeze_support()
# --------------------------------------------GET PARAMETER INPUTS
2022-04-11 09:03:54 +00:00
PARSER = argparse.ArgumentParser(description="FreeDATA Daemon")
PARSER.add_argument(
"--port",
dest="socket_port",
default=3001,
help="Socket port in the range of 1024-65536",
type=int,
)
ARGS = PARSER.parse_args()
2022-04-11 09:03:54 +00:00
static.DAEMONPORT = ARGS.socket_port
2022-02-21 16:58:44 +00:00
try:
2022-04-11 09:03:54 +00:00
if sys.platform == "linux":
logging_path = os.getenv("HOME") + "/.config/" + "FreeDATA/" + "daemon"
if sys.platform == "darwin":
logging_path = (
os.getenv("HOME")
+ "/Library/"
+ "Application Support/"
+ "FreeDATA/"
+ "daemon"
)
if sys.platform == "win32" or sys.platform == "win64":
logging_path = os.getenv("APPDATA") + "/" + "FreeDATA/" + "daemon"
2022-02-21 16:58:44 +00:00
if not os.path.exists(logging_path):
os.makedirs(logging_path)
log_handler.setup_logging(logging_path)
except:
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").error("[DMN] logger init error")
try:
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").info(
"[DMN] Starting TCP/IP socket", port=static.DAEMONPORT
)
# https://stackoverflow.com/a/16641793
socketserver.TCPServer.allow_reuse_address = True
2022-04-11 09:03:54 +00:00
cmdserver = sock.ThreadedTCPServer(
(static.HOST, static.DAEMONPORT), sock.ThreadedTCPRequestHandler
)
server_thread = threading.Thread(target=cmdserver.serve_forever)
server_thread.daemon = True
server_thread.start()
except Exception as e:
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").error(
"[DMN] Starting TCP/IP socket failed", port=static.DAEMONPORT, e=e
)
2022-02-19 20:30:52 +00:00
os._exit(1)
daemon = DAEMON()
2022-04-11 09:03:54 +00:00
structlog.get_logger("structlog").info(
"[DMN] Starting FreeDATA Daemon",
author="DJ2LS",
year="2022",
version=static.VERSION,
)
while True:
2022-02-17 13:25:22 +00:00
time.sleep(1)