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__ )
2020-10-25 20:23:40 +00:00
self . logger . addHandler ( logging . NullHandler ( ) )
2020-10-25 20:21:58 +00:00
2016-02-28 11:11:16 +00:00
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
2020-10-25 20:21:58 +00:00
def connect ( self , retries = 1 , wait_period = 15 ) :
2016-02-28 11:11:16 +00:00
# create socket, connect to server, login and make a file object associated with the socket
2020-10-25 20:21:58 +00:00
while retries > 0 :
retries - = 1
try :
self . sock = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
self . sock . setsockopt ( socket . SOL_SOCKET , socket . SO_KEEPALIVE , 1 )
self . sock . settimeout ( 5 )
if self . aprs_filter :
port = self . settings . APRS_SERVER_PORT_CLIENT_DEFINED_FILTERS
else :
port = self . settings . APRS_SERVER_PORT_FULL_FEED
self . sock . connect ( ( self . settings . APRS_SERVER_HOST , port ) )
login = create_aprs_login ( self . aprs_user , - 1 , self . settings . APRS_APP_NAME , self . settings . APRS_APP_VER , self . aprs_filter )
self . sock . send ( login . encode ( ) )
self . sock_file = self . sock . makefile ( ' rw ' )
self . _kill = False
self . logger . info ( " Connect to OGN ( {} : {} ) as {} with filter: {} " . format ( self . settings . APRS_SERVER_HOST , port , self . aprs_user , " ' " + self . aprs_filter + " ' " if self . aprs_filter else ' none (full-feed) ' ) )
break
except ( socket . error , ConnectionError ) as e :
self . logger . error ( ' Connect error: {} ' . format ( e ) )
if retries > 0 :
self . logger . info ( ' Waiting {} s before next connection try ( {} attempts left). ' . format ( wait_period , retries ) )
sleep ( wait_period )
else :
self . _kill = True
self . logger . info ( ' Giving up. ' )
2017-07-20 13:28:01 +00:00
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 :
2020-10-25 20:21:58 +00:00
self . logger . error ( ' Socket close error ' )
2016-02-28 11:11:16 +00:00
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 :
2020-10-25 20:21:58 +00:00
self . logger . error ( ' socket.error ' )
2020-10-11 12:01:55 +00:00
except OSError :
2020-10-25 20:21:58 +00:00
self . logger . error ( ' OSError ' )
2017-12-13 18:08:57 +00:00
except UnicodeDecodeError :
2020-10-25 20:21:58 +00:00
self . logger . error ( ' UnicodeDecodeError ' )
2016-02-28 11:11:16 +00:00
2017-07-20 08:58:28 +00:00
if autoreconnect and not self . _kill :
2020-10-25 20:21:58 +00:00
self . connect ( retries = 100 )
2016-02-28 11:11:16 +00:00
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 ( )