2016-02-28 11:11:16 +00:00
|
|
|
import socket
|
|
|
|
import logging
|
2017-03-07 20:41:22 +00:00
|
|
|
from time import time, sleep
|
2016-02-28 11:11:16 +00:00
|
|
|
|
2016-02-28 11:28:43 +00:00
|
|
|
from ogn.client import settings
|
2016-02-28 11:11:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
def create_aprs_login(user_name, pass_code, app_name, app_version, aprs_filter=None):
|
|
|
|
if not aprs_filter:
|
|
|
|
return "user {} pass {} vers {} {}\n".format(user_name, pass_code, app_name, app_version)
|
|
|
|
else:
|
|
|
|
return "user {} pass {} vers {} {} filter {}\n".format(user_name, pass_code, app_name, app_version, aprs_filter)
|
|
|
|
|
|
|
|
|
2016-02-28 11:28:43 +00:00
|
|
|
class AprsClient:
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
def __init__(self, aprs_user, aprs_filter='', settings=settings):
|
2016-02-28 11:11:16 +00:00
|
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
self.logger.info("Connect to OGN as {} with filter '{}'".format(aprs_user, (aprs_filter if aprs_filter else 'full-feed')))
|
|
|
|
self.aprs_user = aprs_user
|
|
|
|
self.aprs_filter = aprs_filter
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
self.settings = settings
|
2016-02-28 11:11:16 +00:00
|
|
|
|
2017-07-20 08:58:28 +00:00
|
|
|
self._kill = False
|
|
|
|
|
2016-02-28 11:11:16 +00:00
|
|
|
def connect(self):
|
|
|
|
# create socket, connect to server, login and make a file object associated with the socket
|
|
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
|
|
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
2020-10-11 12:01:55 +00:00
|
|
|
self.sock.settimeout(5)
|
2016-02-28 11:11:16 +00:00
|
|
|
|
|
|
|
if self.aprs_filter:
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
port = self.settings.APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS
|
2016-02-28 11:11:16 +00:00
|
|
|
else:
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
port = self.settings.APRS_SERVER_PORT_FULL_FEED
|
2016-02-28 11:11:16 +00:00
|
|
|
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
self.sock.connect((self.settings.APRS_SERVER_HOST, port))
|
2016-02-28 11:11:16 +00:00
|
|
|
self.logger.debug('Server port {}'.format(port))
|
|
|
|
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
login = create_aprs_login(self.aprs_user, -1, self.settings.APRS_APP_NAME, self.settings.APRS_APP_VER, self.aprs_filter)
|
2016-02-28 11:11:16 +00:00
|
|
|
self.sock.send(login.encode())
|
|
|
|
self.sock_file = self.sock.makefile('rw')
|
|
|
|
|
2017-07-20 13:28:01 +00:00
|
|
|
self._kill = False
|
|
|
|
|
2016-02-28 11:11:16 +00:00
|
|
|
def disconnect(self):
|
|
|
|
self.logger.info('Disconnect')
|
|
|
|
try:
|
|
|
|
# close everything
|
|
|
|
self.sock.shutdown(0)
|
|
|
|
self.sock.close()
|
|
|
|
except OSError:
|
|
|
|
self.logger.error('Socket close error', exc_info=True)
|
|
|
|
|
2017-07-20 08:58:28 +00:00
|
|
|
self._kill = True
|
|
|
|
|
2020-07-18 10:21:18 +00:00
|
|
|
def run(self, callback, timed_callback=lambda client: None, autoreconnect=False, **kwargs):
|
2017-07-20 08:58:28 +00:00
|
|
|
while not self._kill:
|
2016-02-28 11:11:16 +00:00
|
|
|
try:
|
|
|
|
keepalive_time = time()
|
2017-07-20 08:58:28 +00:00
|
|
|
while not self._kill:
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
if time() - keepalive_time > self.settings.APRS_KEEPALIVE_TIME:
|
2016-02-28 11:11:16 +00:00
|
|
|
self.logger.info('Send keepalive')
|
2016-03-25 17:48:44 +00:00
|
|
|
self.sock.send('#keepalive\n'.encode())
|
|
|
|
timed_callback(self)
|
2016-02-28 11:11:16 +00:00
|
|
|
keepalive_time = time()
|
|
|
|
|
|
|
|
# Read packet string from socket
|
|
|
|
packet_str = self.sock_file.readline().strip()
|
|
|
|
|
|
|
|
# A zero length line should not be return if keepalives are being sent
|
|
|
|
# A zero length line will only be returned after ~30m if keepalives are not sent
|
|
|
|
if len(packet_str) == 0:
|
|
|
|
self.logger.warning('Read returns zero length string. Failure. Orderly closeout')
|
|
|
|
break
|
|
|
|
|
2020-07-18 10:21:18 +00:00
|
|
|
callback(packet_str, **kwargs)
|
2016-02-28 11:11:16 +00:00
|
|
|
except socket.error:
|
|
|
|
self.logger.error('socket.error', exc_info=True)
|
2020-10-11 12:01:55 +00:00
|
|
|
except OSError:
|
|
|
|
self.logger.error('OSError', exc_info=True)
|
2017-12-13 18:08:57 +00:00
|
|
|
except UnicodeDecodeError:
|
|
|
|
self.logger.error('UnicodeDecodeError', exc_info=True)
|
2016-02-28 11:11:16 +00:00
|
|
|
|
2017-07-20 08:58:28 +00:00
|
|
|
if autoreconnect and not self._kill:
|
2016-02-28 11:11:16 +00:00
|
|
|
self.connect()
|
|
|
|
else:
|
|
|
|
return
|
2017-03-07 20:41:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TelnetClient:
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
def __init__(self, settings=settings):
|
2017-03-07 20:41:22 +00:00
|
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
self.logger.info("Connect to local telnet server")
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
self.settings = settings
|
2017-03-07 20:41:22 +00:00
|
|
|
|
|
|
|
def connect(self):
|
|
|
|
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
Allow dynamic settings override
Additional (optional) 'settings' attribute in AprsClient,
to be able to pass custom settings to the client,
without changing the settings.py file.
Added 'CustomSettings' class for backward compatible attribute access.
Example (myapp.py):
...
args = ... parse arguments from command line
settings = CustomSettings(
APRS_SERVER_HOST = args.server,
APRS_SERVER_PORT_FULL_FEED = args.port,
APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS = args.port,
APRS_APP_NAME = 'python-ogn-client',
PACKAGE_VERSION = '0.9.5',
APRS_APP_VER = '0.9',
APRS_KEEPALIVE_TIME = 240,
TELNET_SERVER_HOST = 'localhost',
TELNET_SERVER_PORT = 50001,
)
client = AprsClient(aprs_user='N0CALL', aprs_filter=args.filter, settings=settings)
client.connect()
...
2019-12-18 07:30:39 +00:00
|
|
|
self.sock.connect((self.settings.TELNET_SERVER_HOST, self.settings.TELNET_SERVER_PORT))
|
2017-03-07 20:41:22 +00:00
|
|
|
|
|
|
|
def run(self, callback, autoreconnect=False):
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
self.sock_file = self.sock.makefile(mode='rw', encoding='iso-8859-1')
|
|
|
|
while True:
|
|
|
|
packet_str = self.sock_file.readline().strip()
|
|
|
|
callback(packet_str)
|
|
|
|
|
|
|
|
except ConnectionRefusedError:
|
|
|
|
self.logger.error('Telnet server not running', exc_info=True)
|
|
|
|
|
|
|
|
if autoreconnect:
|
|
|
|
sleep(1)
|
|
|
|
self.connect()
|
|
|
|
else:
|
|
|
|
return
|
|
|
|
|
|
|
|
def disconnect(self):
|
|
|
|
self.logger.info('Disconnect')
|
|
|
|
self.sock.shutdown(0)
|
|
|
|
self.sock.close()
|