From 0026655100d28d09a34130eb6327f9808c27ae15 Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Fri, 26 Dec 2014 11:30:02 +0000 Subject: [PATCH 1/6] improve I/O waiting using select --- aprslib/IS.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/aprslib/IS.py b/aprslib/IS.py index 4faef96..447c0f2 100644 --- a/aprslib/IS.py +++ b/aprslib/IS.py @@ -19,6 +19,7 @@ IS class is used for connection to APRS-IS network """ import socket +import select import time import logging import sys @@ -155,6 +156,8 @@ class IS(object): if not self._connected: raise ConnectionError("not connected to a server") + line = '' + while True: try: for line in self._socket_readlines(blocking): @@ -265,8 +268,10 @@ class IS(object): while True: short_buf = '' + select.select([self.sock], [], [], None if blocking else 0) + try: - short_buf = self.sock.recv(1024) + short_buf = self.sock.recv(4096) # sock.recv returns empty if the connection drops if not short_buf: @@ -285,7 +290,3 @@ class IS(object): line, self.buf = self.buf.split("\r\n", 1) yield line - - # lets not hog the CPU when there's nothing to do - if blocking: - time.sleep(0.1) From cffc7219bb8a2f46db70890963c5f403835e7348 Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Fri, 26 Dec 2014 11:31:47 +0000 Subject: [PATCH 2/6] minor code clean up in IS class --- aprslib/IS.py | 49 +++++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/aprslib/IS.py b/aprslib/IS.py index 447c0f2..58a1f18 100644 --- a/aprslib/IS.py +++ b/aprslib/IS.py @@ -63,17 +63,6 @@ class IS(object): self._connected = False self.buf = '' - def callsign_filter(self, callsigns): - """ - Sets a filter for the specified callsigns. - Only those will be sent to us by the server - """ - - if type(callsigns) is not list or len(callsigns) == 0: - return False - - return self.set_filter("b/%s" % "/".join(callsigns)) - def set_filter(self, filter_text): """ Set a specified aprs-is filter for this connection @@ -85,8 +74,6 @@ class IS(object): if self._connected: self.sock.sendall("#filter %s\r\n" % self.filter) - return True - def set_login(self, callsign, passwd): """ Set callsign and password @@ -105,28 +92,30 @@ class IS(object): Initiate connection to APRS server and attempt to login """ - if not self._connected: - while True: - try: - self.logger.info("Attempting connection to %s:%s", self.server[0], self.server[1]) - self._connect() + if self._connected: + return - self.logger.info("Sending login information") - self._send_login() + while True: + try: + self.logger.info("Attempting connection to %s:%s", self.server[0], self.server[1]) + self._connect() - self.logger.info("Filter set to: %s", self.filter) + self.logger.info("Sending login information") + self._send_login() - if self.passwd == "-1": - self.logger.info("Login successful (receive only)") - else: - self.logger.info("Login successful") + self.logger.info("Filter set to: %s", self.filter) - break - except: - if not blocking: - raise + if self.passwd == "-1": + self.logger.info("Login successful (receive only)") + else: + self.logger.info("Login successful") - time.sleep(30) # attempt to reconnect after 30 seconds + break + except: + if not blocking: + raise + + time.sleep(30) # attempt to reconnect after 30 seconds def close(self): """ From 4b1dad52d28b707ab71af50c9408312c5d1eb4a4 Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Fri, 26 Dec 2014 12:52:38 +0000 Subject: [PATCH 3/6] improved and expanded logging in IS class --- aprslib/IS.py | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/aprslib/IS.py b/aprslib/IS.py index 58a1f18..5baeb7b 100644 --- a/aprslib/IS.py +++ b/aprslib/IS.py @@ -97,19 +97,8 @@ class IS(object): while True: try: - self.logger.info("Attempting connection to %s:%s", self.server[0], self.server[1]) self._connect() - - self.logger.info("Sending login information") self._send_login() - - self.logger.info("Filter set to: %s", self.filter) - - if self.passwd == "-1": - self.logger.info("Login successful (receive only)") - else: - self.logger.info("Login successful") - break except: if not blocking: @@ -155,6 +144,8 @@ class IS(object): callback(line) else: callback(parse(line)) + else: + self.logger.debug("Server: %s", line) except (KeyboardInterrupt, SystemExit): raise except (ConnectionDrop, ConnectionError): @@ -181,10 +172,16 @@ class IS(object): Attemps to open a connection to the server """ + self.logger.info("Attempting connection to %s:%s", self.server[0], self.server[1]) + try: # 15 seconds connection timeout self.sock = socket.create_connection(self.server, 15) + raddr, rport = self.sock.getpeername() + + self.logger.info("Connected to %s:%s", raddr, rport) + # 5 second timeout to receive server banner self.sock.setblocking(1) self.sock.settimeout(5) @@ -199,7 +196,11 @@ class IS(object): self.sock.setsockopt(socket.SOL_TCP, socket.TCP_KEEPINTVL, 5) # pylint: enable=E1103 - if self.sock.recv(512)[0] != "#": + banner = self.sock.recv(512) + + if banner[0] == "#": + self.logger.debug("Banner: %s", banner.rstrip()) + else: raise ConnectionError("invalid banner from server") except Exception, e: @@ -224,10 +225,14 @@ class IS(object): __version__ ) + self.logger.info("Sending login information") + try: self.sock.sendall(login_str) self.sock.settimeout(5) - test = self.sock.recv(len(login_str) + 100) + test = self.sock.recv(len(login_str) + 100).rstrip() + + self.logger.info("Server: %s", test) (x, x, callsign, status, x) = test.split(' ', 4) @@ -238,9 +243,14 @@ class IS(object): if status != "verified," and self.passwd != "-1": raise LoginError("Password is incorrect") - except LoginError, e: + if self.passwd == "-1": + self.logger.info("Login successful (receive only)") + else: + self.logger.info("Login successful") + + except LoginError: self.close() - raise LoginError("failed to login: %s" % e) + raise except: self.close() raise LoginError("failed to login") From 0d80f20e33b49257de5245d37c42f7527f43d79e Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Fri, 26 Dec 2014 14:00:33 +0000 Subject: [PATCH 4/6] add underscore as valid char for message addresse fixes #1 --- aprslib/parse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aprslib/parse.py b/aprslib/parse.py index f9f24e5..137fe75 100644 --- a/aprslib/parse.py +++ b/aprslib/parse.py @@ -365,7 +365,7 @@ def parse(raw_sentence): # the while loop is used to easily break out once a match is found while True: # try to match bulletin - match = re.findall(r"^BLN([0-9])([a-zA-Z0-9 \-]{5}):(.{0,67})", body) + match = re.findall(r"^BLN([0-9])([a-z0-9_ \-]{5}):(.{0,67})", body, re.I) if match: bid, identifier, text = match[0] identifier = identifier.rstrip(' ') From 0e05efe56a6078f2316d1aa8d332e01357d9db6c Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Fri, 26 Dec 2014 14:04:08 +0000 Subject: [PATCH 5/6] catch ValueError when decoding lat/lng for MicE fixes #2 --- aprslib/parse.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/aprslib/parse.py b/aprslib/parse.py index 137fe75..84581d7 100644 --- a/aprslib/parse.py +++ b/aprslib/parse.py @@ -514,8 +514,11 @@ def parse(raw_sentence): symbol_table = packet[0] symbol = packet[9] - latitude = 90 - (base91.to_decimal(packet[1:5]) / 380926.0) - longitude = -180 + (base91.to_decimal(packet[5:9]) / 190463.0) + try: + latitude = 90 - (base91.to_decimal(packet[1:5]) / 380926.0) + longitude = -180 + (base91.to_decimal(packet[5:9]) / 190463.0) + except ValueError: + raise ParseError("invalid characters in latitude/longitude encoding") # parse csT From 004b1712563cbf1b7c89b4fddf59b877c0fd0775 Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Fri, 26 Dec 2014 15:03:05 +0000 Subject: [PATCH 6/6] bump to v0.6.29 --- aprslib/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aprslib/version.py b/aprslib/version.py index 691aa16..28f71fc 100644 --- a/aprslib/version.py +++ b/aprslib/version.py @@ -1 +1 @@ -__version__ = '0.6.28' +__version__ = '0.6.29'