new daemon

non blocking and multi client support also attempt of fixing #129
This commit is contained in:
dj2ls 2022-01-22 20:39:37 +01:00
parent 805a8450c5
commit 2c57923c11
6 changed files with 407 additions and 342 deletions

View file

@ -85,7 +85,7 @@ daemon.on('data', function(data) {
msg += data.toString('utf8'); /*append data to buffer so we can stick long data together */ msg += data.toString('utf8'); /*append data to buffer so we can stick long data together */
/* check if we reached an EOF, if true, clear buffer and parse JSON data */ /* check if we reached an EOF, if true, clear buffer and parse JSON data */
if (data.endsWith('}')) { if (data.endsWith('}\n')) {
/*console.log(msg)*/ /*console.log(msg)*/
try { try {
/*console.log(msg)*/ /*console.log(msg)*/
@ -163,7 +163,7 @@ exports.startTNC = function(mycall, mygrid, rx_audio, tx_audio, radiocontrol, de
}] }]
}) })
//console.log(json_command) console.log(json_command)
writeDaemonCommand(json_command) writeDaemonCommand(json_command)
} }

69
tnc/audio.py Normal file
View file

@ -0,0 +1,69 @@
import json
####################################################
# https://stackoverflow.com/questions/7088672/pyaudio-working-but-spits-out-error-messages-each-time
# https://github.com/DJ2LS/FreeDATA/issues/22
# we need to have a look at this if we want to run this on Windows and MacOS !
# Currently it seems, this is a Linux-only problem
from ctypes import *
from contextlib import contextmanager
import pyaudio
ERROR_HANDLER_FUNC = CFUNCTYPE(None, c_char_p, c_int, c_char_p, c_int, c_char_p)
def py_error_handler(filename, line, function, err, fmt):
pass
c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)
@contextmanager
def noalsaerr():
asound = cdll.LoadLibrary('libasound.so')
asound.snd_lib_error_set_handler(c_error_handler)
yield
asound.snd_lib_error_set_handler(None)
# with noalsaerr():
# p = pyaudio.PyAudio()
######################################################
def get_input_devices():
# UPDATE LIST OF AUDIO DEVICES
try:
# we need to "try" this, because sometimes libasound.so isn't in the default place
# try to supress error messages
with noalsaerr(): # https://github.com/DJ2LS/FreeDATA/issues/22
p = pyaudio.PyAudio()
# else do it the default way
except Exception as e:
p = pyaudio.PyAudio()
input_devices = []
output_devices = []
for i in range(0, p.get_device_count()):
# we need to do a try exception, beacuse for windows theres now audio device range
try:
maxInputChannels = p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')
maxOutputChannels = p.get_device_info_by_host_api_device_index(0, i).get('maxOutputChannels')
name = p.get_device_info_by_host_api_device_index(0, i).get('name')
except:
maxInputChannels = 0
maxOutputChannels = 0
name = ''
if maxInputChannels > 0:
input_devices.append({"ID": i, "NAME": str(name)})
if maxOutputChannels > 0:
output_devices.append({"ID": i, "NAME": str(name)})
p.terminate()
return [input_devices, output_devices]
def get_output_devices():
pass

364
tnc/daemon.py Executable file → Normal file
View file

@ -25,171 +25,113 @@ import helpers
import os import os
import queue import queue
import audio import audio
import sock
DAEMON_QUEUE = queue.Queue() class DAEMON():
def __init__(self):
self.daemon_queue = sock.DAEMON_QUEUE
update_audio_devices = threading.Thread(target=self.update_audio_devices, name="UPDATE_AUDIO_DEVICES")
update_audio_devices.start()
update_serial_devices = threading.Thread(target=self.update_serial_devices, name="UPDATE_SERIAL_DEVICES")
update_serial_devices.start()
worker = threading.Thread(target=self.worker, name="WORKER")
worker.start()
def update_audio_devices(self):
while 1:
static.AUDIO_INPUT_DEVICES, static.AUDIO_OUTPUT_DEVICES = audio.get_input_devices()
time.sleep(1)
log_handler.setup_logging("daemon") def update_serial_devices(self):
structlog.get_logger("structlog").info("[DMN] Starting FreeDATA daemon", author="DJ2LS", year="2022", version="0.1") while 1:
serial_devices = []
ports = serial.tools.list_ports.comports()
for port, desc, hwid in ports:
# get python version, which is needed later for determining installation path # calculate hex of hwid if we have unique names
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1]) crc_hwid = crc_algorithm(bytes(hwid, encoding='utf-8'))
structlog.get_logger("structlog").info("[DMN] Python", version=python_version) crc_hwid = crc_hwid.to_bytes(2, byteorder='big')
crc_hwid = crc_hwid.hex()
description = desc + ' [' + crc_hwid + ']'
serial_devices.append({"PORT": str(port), "DESCRIPTION": str(description) })
static.SERIAL_DEVICES = serial_devices
time.sleep(1)
def worker(self):
while 1:
# load crc engine data = self.daemon_queue.get()
crc_algorithm = crcengine.new('crc16-ccitt-false') # load crc8 library
# data[1] mycall
def start_daemon(): # data[2] mygrid
# data[3] rx_audio
try: # data[4] tx_audio
structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=PORT) # data[5] devicename
# https://stackoverflow.com/a/16641793 # data[6] deviceport
socketserver.TCPServer.allow_reuse_address = True # data[7] serialspeed
daemon = socketserver.TCPServer(('0.0.0.0', PORT), CMDTCPRequestHandler) # data[8] pttprotocol
daemon.serve_forever() # data[9] pttport
# data[10] data_bits
finally: # data[11] stop_bits
structlog.get_logger("structlog").warning("[DMN] Closing socket", port=PORT) # data[12] handshake
daemon.server_close() # data[13] radiocontrol
# data[14] rigctld_ip
# data[15] rigctld_port
class CMDTCPRequestHandler(socketserver.BaseRequestHandler): if data[0] == 'STARTTNC':
structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=data[5], port=data[6])
def handle(self, hamlib_version = 0):
structlog.get_logger("structlog").debug("[DMN] Client connected", ip=self.client_address[0])
# loop through socket buffer until timeout is reached. then close buffer
socketTimeout = time.time() + 6
while socketTimeout > time.time():
time.sleep(0.01)
encoding = 'utf-8'
#data = str(self.request.recv(1024), 'utf-8')
data = bytes()
# we need to loop through buffer until end of chunk is reached or timeout occured
while socketTimeout > time.time():
data += self.request.recv(64)
# or chunk.endswith(b'\n'):
if data.startswith(b'{"type"') and data.endswith(b'}\n'):
break
data = data[:-1] # remove b'\n'
data = str(data, encoding)
if len(data) > 0:
# reset socket timeout
socketTimeout = time.time() + static.SOCKET_TIMEOUT
# only read first line of string. multiple lines will cause an json error
# this occurs possibly, if we are getting data too fast
# data = data.splitlines()[0]
data = data.splitlines()[0]
# we need to do some error handling in case of socket timeout or decoding issue
try:
# convert data to json object
received_json = json.loads(data)
# GET COMMANDS
# "command" : "..."
# SET COMMANDS
# "command" : "..."
# "parameter" : " ..."
# DATA COMMANDS
# "command" : "..."
# "type" : "..."
# "dxcallsign" : "..."
# "data" : "..."
# print(received_json)
# print(received_json["type"])
# print(received_json["command"])
# try:
if received_json["type"] == 'SET' and received_json["command"] == 'MYCALLSIGN':
callsign = received_json["parameter"]
print(received_json)
if bytes(callsign, 'utf-8') == b'':
self.request.sendall(b'INVALID CALLSIGN')
structlog.get_logger("structlog").warning("[DMN] SET MYCALL FAILED", call=static.MYCALLSIGN, crc=static.MYCALLSIGN_CRC8)
else:
static.MYCALLSIGN = bytes(callsign, 'utf-8')
static.MYCALLSIGN_CRC8 = helpers.get_crc_8(static.MYCALLSIGN)
structlog.get_logger("structlog").info("[DMN] SET MYCALL", call=static.MYCALLSIGN, crc=static.MYCALLSIGN_CRC8)
if received_json["type"] == 'SET' and received_json["command"] == 'MYGRID':
mygrid = received_json["parameter"]
if bytes(mygrid, 'utf-8') == b'':
self.request.sendall(b'INVALID GRID')
else:
static.MYGRID = bytes(mygrid, 'utf-8')
structlog.get_logger("structlog").info("[DMN] SET MYGRID", grid=static.MYGRID)
if received_json["type"] == 'SET' and received_json["command"] == 'STARTTNC' and not static.TNCSTARTED:
mycall = str(received_json["parameter"][0]["mycall"])
mygrid = str(received_json["parameter"][0]["mygrid"])
rx_audio = str(received_json["parameter"][0]["rx_audio"])
tx_audio = str(received_json["parameter"][0]["tx_audio"])
devicename = str(received_json["parameter"][0]["devicename"])
deviceport = str(received_json["parameter"][0]["deviceport"])
serialspeed = str(received_json["parameter"][0]["serialspeed"])
pttprotocol = str(received_json["parameter"][0]["pttprotocol"])
pttport = str(received_json["parameter"][0]["pttport"])
data_bits = str(received_json["parameter"][0]["data_bits"])
stop_bits = str(received_json["parameter"][0]["stop_bits"])
handshake = str(received_json["parameter"][0]["handshake"])
radiocontrol = str(received_json["parameter"][0]["radiocontrol"])
rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"])
rigctld_port = str(received_json["parameter"][0]["rigctld_port"])
structlog.get_logger("structlog").warning("[DMN] Starting TNC", rig=devicename, port=deviceport)
# list of parameters, necessary for running subprocess command as a list # list of parameters, necessary for running subprocess command as a list
options = [] options = []
options.append('--mycall') options.append('--mycall')
options.append(mycall) options.append(data[1])
options.append('--mygrid') options.append('--mygrid')
options.append(mygrid) options.append(data[2])
options.append('--rx') options.append('--rx')
options.append(rx_audio) options.append(data[3])
options.append('--tx') options.append('--tx')
options.append(tx_audio) options.append(data[4])
options.append('--deviceport')
options.append(deviceport)
options.append('--devicename') options.append('--devicename')
options.append(devicename) options.append(data[5])
options.append('--deviceport')
options.append(data[6])
options.append('--serialspeed') options.append('--serialspeed')
options.append(serialspeed) options.append(data[7])
options.append('--pttprotocol') options.append('--pttprotocol')
options.append(pttprotocol) options.append(data[8])
options.append('--pttport') options.append('--pttport')
options.append(pttport) options.append(data[9])
options.append('--data_bits') options.append('--data_bits')
options.append(data_bits) options.append(data[10])
options.append('--stop_bits') options.append('--stop_bits')
options.append(stop_bits) options.append(data[11])
options.append('--handshake') options.append('--handshake')
options.append(handshake) options.append(data[12])
options.append('--radiocontrol') options.append('--radiocontrol')
options.append(radiocontrol) options.append(data[13])
options.append('--rigctld_ip') options.append('--rigctld_ip')
options.append(rigctld_ip) options.append(data[14])
options.append('--rigctld_port') options.append('--rigctld_port')
options.append(rigctld_port) options.append(data[15])
# try running tnc from binary, else run from source # try running tnc from binary, else run from source
# this helps running the tnc in a developer environment # this helps running the tnc in a developer environment
@ -218,92 +160,30 @@ class CMDTCPRequestHandler(socketserver.BaseRequestHandler):
static.TNCPROCESS = p # .pid static.TNCPROCESS = p # .pid
static.TNCSTARTED = True static.TNCSTARTED = True
if received_json["type"] == 'SET' and received_json["command"] == 'STOPTNC': # data[1] devicename
static.TNCPROCESS.kill() # data[2] deviceport
structlog.get_logger("structlog").warning("[DMN] Stopping TNC") # data[3] serialspeed
#os.kill(static.TNCPROCESS, signal.SIGKILL) # data[4] pttprotocol
static.TNCSTARTED = False # 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
if data[0] == 'TEST_HAMLIB':
if received_json["type"] == 'GET' and received_json["command"] == 'DAEMON_STATE': devicename = data[1]
deviceport = data[2]
data = { serialspeed = data[3]
'COMMAND': 'DAEMON_STATE', pttprotocol = data[4]
'DAEMON_STATE': [], pttport = data[5]
'PYTHON_VERSION': str(python_version), data_bits = data[6]
'HAMLIB_VERSION': str(hamlib_version), stop_bits = data[7]
'INPUT_DEVICES': [], handshake = data[8]
'OUTPUT_DEVICES': [], radiocontrol = data[9]
'SERIAL_DEVICES': [], rigctld_ip = data[10]
"CPU": str(psutil.cpu_percent()), "RAM": str(psutil.virtual_memory().percent), "VERSION": "0.1-prototype"} rigctld_port = data[11]
if static.TNCSTARTED:
data["DAEMON_STATE"].append({"STATUS": "running"})
else:
data["DAEMON_STATE"].append({"STATUS": "stopped"})
# UPDATE LIST OF AUDIO DEVICES
try:
# we need to "try" this, because sometimes libasound.so isn't in the default place
# try to supress error messages
with audio.noalsaerr(): # https://github.com/DJ2LS/FreeDATA/issues/22
p = audio.pyaudio.PyAudio()
# else do it the default way
except Exception as e:
p = audio.pyaudio.PyAudio()
for i in range(0, p.get_device_count()):
# we need to do a try exception, beacuse for windows theres now audio device range
try:
maxInputChannels = p.get_device_info_by_host_api_device_index(0, i).get('maxInputChannels')
maxOutputChannels = p.get_device_info_by_host_api_device_index(0, i).get('maxOutputChannels')
name = p.get_device_info_by_host_api_device_index(0, i).get('name')
except:
maxInputChannels = 0
maxOutputChannels = 0
name = ''
if maxInputChannels > 0:
data["INPUT_DEVICES"].append(
{"ID": i, "NAME": str(name)})
if maxOutputChannels > 0:
data["OUTPUT_DEVICES"].append(
{"ID": i, "NAME": str(name)})
p.terminate()
# UPDATE LIST OF SERIAL DEVICES
ports = serial.tools.list_ports.comports()
for port, desc, hwid in ports:
# calculate hex of hwid if we have unique names
crc_hwid = crc_algorithm(bytes(hwid, encoding='utf-8'))
crc_hwid = crc_hwid.to_bytes(2, byteorder='big')
crc_hwid = crc_hwid.hex()
description = desc + ' [' + crc_hwid + ']'
data["SERIAL_DEVICES"].append(
{"PORT": str(port), "DESCRIPTION": str(description) })
jsondata = json.dumps(data)
self.request.sendall(bytes(jsondata, encoding))
if received_json["type"] == 'GET' and received_json["command"] == 'TEST_HAMLIB':
try:
print(received_json["parameter"])
devicename = str(received_json["parameter"][0]["devicename"])
deviceport = str(received_json["parameter"][0]["deviceport"])
serialspeed = str(received_json["parameter"][0]["serialspeed"])
pttprotocol = str(received_json["parameter"][0]["pttprotocol"])
pttport = str(received_json["parameter"][0]["pttport"])
data_bits = str(received_json["parameter"][0]["data_bits"])
stop_bits = str(received_json["parameter"][0]["stop_bits"])
handshake = str(received_json["parameter"][0]["handshake"])
radiocontrol = str(received_json["parameter"][0]["radiocontrol"])
rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"])
rigctld_port = str(received_json["parameter"][0]["rigctld_port"])
# check how we want to control the radio # check how we want to control the radio
@ -323,28 +203,23 @@ class CMDTCPRequestHandler(socketserver.BaseRequestHandler):
hamlib.set_ptt(True) hamlib.set_ptt(True)
pttstate = hamlib.get_ptt() pttstate = hamlib.get_ptt()
if pttstate: if pttstate:
structlog.get_logger("structlog").info("[DMN] Hamlib PTT", status = 'SUCCESS') structlog.get_logger("structlog").info("[DMN] Hamlib PTT", status = 'SUCCESS')
data = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'SUCCESS'} response = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'SUCCESS'}
elif not pttstate: elif not pttstate:
structlog.get_logger("structlog").warning("[DMN] Hamlib PTT", status = 'NO SUCCESS') structlog.get_logger("structlog").warning("[DMN] Hamlib PTT", status = 'NO SUCCESS')
data = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'NOSUCCESS'} response = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'NOSUCCESS'}
else: else:
structlog.get_logger("structlog").error("[DMN] Hamlib PTT", status = 'FAILED') structlog.get_logger("structlog").error("[DMN] Hamlib PTT", status = 'FAILED')
data = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'FAILED'} response = {'COMMAND': 'TEST_HAMLIB', 'RESULT': 'FAILED'}
hamlib.set_ptt(False) hamlib.set_ptt(False)
hamlib.close_rig() hamlib.close_rig()
jsondata = json.dumps(data) jsondata = json.dumps(response)
self.request.sendall(bytes(jsondata, encoding)) sock.SOCKET_QUEUE.put(jsondata)
except Exception as e:
structlog.get_logger("structlog").error("[DMN] Hamlib: Can't open rig", e = sys.exc_info()[0], error=e)
except Exception as e:
structlog.get_logger("structlog").error("[DMN] Network error", error=e)
structlog.get_logger("structlog").warning("[DMN] Closing client socket", ip=self.client_address[0], port=self.client_address[1])
if __name__ == '__main__': if __name__ == '__main__':
@ -354,9 +229,22 @@ if __name__ == '__main__':
PARSER.add_argument('--port', dest="socket_port",default=3001, help="Socket port", type=int) PARSER.add_argument('--port', dest="socket_port",default=3001, help="Socket port", type=int)
ARGS = PARSER.parse_args() ARGS = PARSER.parse_args()
PORT = ARGS.socket_port static.DAEMONPORT = ARGS.socket_port
# --------------------------------------------START CMD SERVER
DAEMON_THREAD = threading.Thread(target=start_daemon, name="daemon") try:
DAEMON_THREAD.start() structlog.get_logger("structlog").info("[DMN] Starting TCP/IP socket", port=static.DAEMONPORT)
# https://stackoverflow.com/a/16641793
socketserver.TCPServer.allow_reuse_address = True
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:
structlog.get_logger("structlog").error("[DMN] Starting TCP/IP socket failed", port=static.DAEMONPORT, e=e)
daemon = DAEMON()
while True:
time.sleep(1)

View file

@ -48,6 +48,8 @@ class DATA():
self.mode_list = [14,14,14,12,10] # mode list of available modes, each mode will be used 2times per speed level self.mode_list = [14,14,14,12,10] # mode list of available modes, each mode will be used 2times per speed level
self.speed_level = len(self.mode_list) - 1 # speed level for selecting mode
self.rx_frame_bof_received = False self.rx_frame_bof_received = False
self.rx_frame_eof_received = False self.rx_frame_eof_received = False

View file

@ -31,32 +31,56 @@ import sys
import os import os
import logging, structlog, log_handler import logging, structlog, log_handler
import queue import queue
import psutil
import audio
SOCKET_QUEUE = queue.Queue() SOCKET_QUEUE = queue.Queue()
DAEMON_QUEUE = queue.Queue()
CONNECTED_CLIENTS = set()
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass pass
class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler): class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
def send_to_client(self): def send_to_client(self):
while self.connection_alive: while self.connection_alive:
# send tnc state as network stream # send tnc state as network stream
# check server port against daemon port and send corresponding data
if self.server.server_address[1] == static.PORT and not static.TNCSTARTED:
data = send_tnc_state() data = send_tnc_state()
SOCKET_QUEUE.put(data)
else:
data = send_daemon_state()
SOCKET_QUEUE.put(data)
time.sleep(0.5)
while not SOCKET_QUEUE.empty():
data = SOCKET_QUEUE.get()
sock_data = bytes(data, 'utf-8')
sock_data += b'\n' # append line limiter
# send data to all clients
for client in CONNECTED_CLIENTS:
client.send(sock_data)
# we want to transmit scatter data only once to reduce network traffic # we want to transmit scatter data only once to reduce network traffic
static.SCATTER = [] static.SCATTER = []
# we want to display INFO messages only once # we want to display INFO messages only once
static.INFO = [] static.INFO = []
#self.request.sendall(sock_data)
sock_data = bytes(data, 'utf-8')
sock_data += b'\n' # append line limiter
self.request.sendall(sock_data)
time.sleep(0.15) time.sleep(0.15)
def receive_from_client(self): def receive_from_client(self):
data = bytes() data = bytes()
while self.connection_alive: while self.connection_alive:
# BrokenPipeError: [Errno 32] Broken pipe
chunk = self.request.recv(2) chunk = self.request.recv(2)
data += chunk data += chunk
@ -66,18 +90,20 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
if data.startswith(b'{"type"') and data.endswith(b'}\n'): if data.startswith(b'{"type"') and data.endswith(b'}\n'):
data = data[:-1] # remove b'\n' data = data[:-1] # remove b'\n'
if self.server.server_address[1] == static.PORT:
process_tnc_commands(data) process_tnc_commands(data)
data = bytes() else:
process_daemon_commands(data)
if data.endswith(b'1\n'):
print(data)
data = bytes() data = bytes()
def handle(self): def handle(self):
CONNECTED_CLIENTS.add(self.request)
structlog.get_logger("structlog").debug("[TNC] Client connected", ip=self.client_address[0], port=self.client_address[1]) structlog.get_logger("structlog").debug("[TNC] Client connected", ip=self.client_address[0], port=self.client_address[1])
self.connection_alive = True self.connection_alive = True
self.sendThread = threading.Thread(target=self.send_to_client, args=[]).start() self.sendThread = threading.Thread(target=self.send_to_client, args=[]).start()
self.receiveThread = threading.Thread(target=self.receive_from_client, args=[]).start() self.receiveThread = threading.Thread(target=self.receive_from_client, args=[]).start()
@ -85,9 +111,13 @@ class ThreadedTCPRequestHandler(socketserver.StreamRequestHandler):
while self.connection_alive: while self.connection_alive:
time.sleep(1) time.sleep(1)
structlog.get_logger("structlog").warning("[TNC] Closing client socket", ip=self.client_address[0], port=self.client_address[1])
def finish(self):
structlog.get_logger("structlog").warning("[TNC] Closing client socket", ip=self.client_address[0], port=self.client_address[1])
CONNECTED_CLIENTS.remove(self.request)
print(CONNECTED_CLIENTS)
def process_tnc_commands(data): def process_tnc_commands(data):
# we need to do some error handling in case of socket timeout or decoding issue # we need to do some error handling in case of socket timeout or decoding issue
@ -204,7 +234,8 @@ def process_tnc_commands(data):
output["DATA-ARRAY"].append({"DXCALLSIGN": str(static.RX_BUFFER[i][0], 'utf-8'), "DXGRID": str(static.RX_BUFFER[i][1], 'utf-8'), "TIMESTAMP": static.RX_BUFFER[i][2], "RXDATA": [rawdata]}) output["DATA-ARRAY"].append({"DXCALLSIGN": str(static.RX_BUFFER[i][0], 'utf-8'), "DXGRID": str(static.RX_BUFFER[i][1], 'utf-8'), "TIMESTAMP": static.RX_BUFFER[i][2], "RXDATA": [rawdata]})
jsondata = json.dumps(output) jsondata = json.dumps(output)
self.request.sendall(bytes(jsondata, encoding)) #self.request.sendall(bytes(jsondata, encoding))
SOCKET_QUEUE.put(jsondata)
if received_json["type"] == 'GET' and received_json["command"] == 'RX_MSG_BUFFER': if received_json["type"] == 'GET' and received_json["command"] == 'RX_MSG_BUFFER':
output = { output = {
@ -218,13 +249,15 @@ def process_tnc_commands(data):
output["DATA-ARRAY"].append({"DXCALLSIGN": str(static.RX_MSG_BUFFER[i][0], 'utf-8'), "DXGRID": str(static.RX_MSG_BUFFER[i][1], 'utf-8'), "TIMESTAMP": static.RX_MSG_BUFFER[i][2], "RXDATA": [rawdata]}) output["DATA-ARRAY"].append({"DXCALLSIGN": str(static.RX_MSG_BUFFER[i][0], 'utf-8'), "DXGRID": str(static.RX_MSG_BUFFER[i][1], 'utf-8'), "TIMESTAMP": static.RX_MSG_BUFFER[i][2], "RXDATA": [rawdata]})
jsondata = json.dumps(output) jsondata = json.dumps(output)
self.request.sendall(bytes(jsondata, encoding)) #self.request.sendall(bytes(jsondata, encoding))
SOCKET_QUEUE.put(jsondata)
if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_BUFFER': if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_BUFFER':
static.RX_BUFFER = [] static.RX_BUFFER = []
if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_MSG_BUFFER': if received_json["type"] == 'SET' and received_json["command"] == 'DEL_RX_MSG_BUFFER':
static.RX_MSG_BUFFER = [] static.RX_MSG_BUFFER = []
# exception, if JSON cant be decoded # exception, if JSON cant be decoded
except Exception as e: except Exception as e:
structlog.get_logger("structlog").error("[TNC] Network error", e=e) structlog.get_logger("structlog").error("[TNC] Network error", e=e)
@ -265,11 +298,12 @@ def send_tnc_state():
output["STATIONS"].append({"DXCALLSIGN": str(static.HEARD_STATIONS[i][0], 'utf-8'), "DXGRID": str(static.HEARD_STATIONS[i][1], 'utf-8'),"TIMESTAMP": static.HEARD_STATIONS[i][2], "DATATYPE": static.HEARD_STATIONS[i][3], "SNR": static.HEARD_STATIONS[i][4], "OFFSET": static.HEARD_STATIONS[i][5], "FREQUENCY": static.HEARD_STATIONS[i][6]}) output["STATIONS"].append({"DXCALLSIGN": str(static.HEARD_STATIONS[i][0], 'utf-8'), "DXGRID": str(static.HEARD_STATIONS[i][1], 'utf-8'),"TIMESTAMP": static.HEARD_STATIONS[i][2], "DATATYPE": static.HEARD_STATIONS[i][3], "SNR": static.HEARD_STATIONS[i][4], "OFFSET": static.HEARD_STATIONS[i][5], "FREQUENCY": static.HEARD_STATIONS[i][6]})
jsondata = json.dumps(output) jsondata = json.dumps(output)
static.NETWORK_BUFFER = jsondata
return jsondata return jsondata
def process_daemon_commands(): def process_daemon_commands(data):
# convert data to json object
received_json = json.loads(data)
if received_json["type"] == 'SET' and received_json["command"] == 'MYCALLSIGN': if received_json["type"] == 'SET' and received_json["command"] == 'MYCALLSIGN':
callsign = received_json["parameter"] callsign = received_json["parameter"]
@ -292,8 +326,8 @@ def process_daemon_commands():
static.MYGRID = bytes(mygrid, 'utf-8') static.MYGRID = bytes(mygrid, 'utf-8')
structlog.get_logger("structlog").info("[DMN] SET MYGRID", grid=static.MYGRID) structlog.get_logger("structlog").info("[DMN] SET MYGRID", grid=static.MYGRID)
if received_json["type"] == 'SET' and received_json["command"] == 'STARTTNC' and not static.TNCSTARTED: if received_json["type"] == 'SET' and received_json["command"] == 'STARTTNC' and not static.TNCSTARTED:
mycall = str(received_json["parameter"][0]["mycall"]) mycall = str(received_json["parameter"][0]["mycall"])
mygrid = str(received_json["parameter"][0]["mygrid"]) mygrid = str(received_json["parameter"][0]["mygrid"])
rx_audio = str(received_json["parameter"][0]["rx_audio"]) rx_audio = str(received_json["parameter"][0]["rx_audio"])
@ -309,6 +343,52 @@ def process_daemon_commands():
radiocontrol = str(received_json["parameter"][0]["radiocontrol"]) radiocontrol = str(received_json["parameter"][0]["radiocontrol"])
rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"]) rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"])
rigctld_port = str(received_json["parameter"][0]["rigctld_port"]) rigctld_port = str(received_json["parameter"][0]["rigctld_port"])
DAEMON_QUEUE.put(['STARTTNC', \
mycall, \
mygrid, \
rx_audio, \
tx_audio, \
devicename, \
deviceport, \
serialspeed, \
pttprotocol, \
pttport, \
data_bits, \
stop_bits, \
handshake, \
radiocontrol, \
rigctld_ip, \
rigctld_port \
])
if received_json["type"] == 'GET' and received_json["command"] == 'TEST_HAMLIB':
devicename = str(received_json["parameter"][0]["devicename"])
deviceport = str(received_json["parameter"][0]["deviceport"])
serialspeed = str(received_json["parameter"][0]["serialspeed"])
pttprotocol = str(received_json["parameter"][0]["pttprotocol"])
pttport = str(received_json["parameter"][0]["pttport"])
data_bits = str(received_json["parameter"][0]["data_bits"])
stop_bits = str(received_json["parameter"][0]["stop_bits"])
handshake = str(received_json["parameter"][0]["handshake"])
radiocontrol = str(received_json["parameter"][0]["radiocontrol"])
rigctld_ip = str(received_json["parameter"][0]["rigctld_ip"])
rigctld_port = str(received_json["parameter"][0]["rigctld_port"])
DAEMON_QUEUE.put(['TEST_HAMLIB', \
devicename, \
deviceport, \
serialspeed, \
pttprotocol, \
pttport, \
data_bits, \
stop_bits, \
handshake, \
radiocontrol, \
rigctld_ip, \
rigctld_port \
])
if received_json["type"] == 'SET' and received_json["command"] == 'STOPTNC': if received_json["type"] == 'SET' and received_json["command"] == 'STOPTNC':
static.TNCPROCESS.kill() static.TNCPROCESS.kill()
@ -316,5 +396,28 @@ def process_daemon_commands():
static.TNCSTARTED = False static.TNCSTARTED = False
def sent_daemon_state(): def send_daemon_state():
pass
python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])
output = {
'COMMAND': 'DAEMON_STATE',
'DAEMON_STATE': [],
'PYTHON_VERSION': str(python_version),
'HAMLIB_VERSION': static.HAMLIB_VERSION,
'INPUT_DEVICES': static.AUDIO_INPUT_DEVICES,
'OUTPUT_DEVICES': static.AUDIO_OUTPUT_DEVICES,
'SERIAL_DEVICES': static.SERIAL_DEVICES,
'CPU': str(psutil.cpu_percent()),
'RAM': str(psutil.virtual_memory().percent),
'VERSION': '0.1'
}
if static.TNCSTARTED:
output["DAEMON_STATE"].append({"STATUS": "running"})
else:
output["DAEMON_STATE"].append({"STATUS": "stopped"})
jsondata = json.dumps(output)
return jsondata

View file

@ -7,7 +7,6 @@ Created on Wed Dec 23 11:13:57 2020
Here we are saving application wide variables and stats, which have to be accessed everywhere. Here we are saving application wide variables and stats, which have to be accessed everywhere.
Not nice, tipps are appreciated :-) Not nice, tipps are appreciated :-)
""" """
NETWORK_BUFFER = b''
# DAEMON # DAEMON
DAEMONPORT = 3001 DAEMONPORT = 3001
TNCSTARTED = False TNCSTARTED = False
@ -31,12 +30,14 @@ HOST = "0.0.0.0"
PORT = 3000 PORT = 3000
SOCKET_TIMEOUT = 1 # seconds SOCKET_TIMEOUT = 1 # seconds
# --------------------------------- # ---------------------------------
SERIAL_DEVICES = []
# ---------------------------------
PTT_STATE = False PTT_STATE = False
TRANSMITTING = False TRANSMITTING = False
HAMLIB_VERSION = '0'
HAMLIB_PTT_TYPE = 'RTS' HAMLIB_PTT_TYPE = 'RTS'
HAMLIB_DEVICE_NAME = 'RIG_MODEL_DUMMY_NOVFO' HAMLIB_DEVICE_NAME = 'RIG_MODEL_DUMMY_NOVFO'
HAMLIB_DEVICE_PORT = '/dev/ttyUSB0' HAMLIB_DEVICE_PORT = '/dev/ttyUSB0'
@ -61,6 +62,8 @@ SCATTER = []
# --------------------------------- # ---------------------------------
# Audio Defaults # Audio Defaults
AUDIO_INPUT_DEVICES = []
AUDIO_OUTPUT_DEVICES = []
AUDIO_INPUT_DEVICE = -2 AUDIO_INPUT_DEVICE = -2
AUDIO_OUTPUT_DEVICE = -2 AUDIO_OUTPUT_DEVICE = -2
BUFFER_OVERFLOW_COUNTER = [0,0,0] BUFFER_OVERFLOW_COUNTER = [0,0,0]