IGate.read()   F
last analyzed

Complexity

Conditions 12

Size

Total Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 12
dl 0
loc 42
rs 2.7855
c 1
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like IGate.read() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
"""APRS Internet Service Class Definitions"""
5
6
# These imports are for python3 compatibility inside python2
7
from __future__ import absolute_import
8
from __future__ import division
9
from __future__ import print_function
10
11
import logging
12
import select
13
import socket
14
15
import requests
16
17
from apex.aprs import constants as aprs_constants
18
from apex.aprs import util as aprs_util
19
20
__author__ = 'Jeffrey Phillips Freeman (WI2ARD)'
21
__maintainer__ = 'Jeffrey Phillips Freeman (WI2ARD)'
22
__email__ = '[email protected]'
23
__license__ = 'Apache License, Version 2.0'
24
__copyright__ = 'Copyright 2016, Syncleus, Inc. and contributors'
25
__credits__ = []
26
27
28
class IGate(object):
29
30
    """APRS Object."""
31
32
    logger = logging.getLogger(__name__)
33
    logger.setLevel(aprs_constants.LOG_LEVEL)
34
    console_handler = logging.StreamHandler()
35
    console_handler.setLevel(aprs_constants.LOG_LEVEL)
36
    console_handler.setFormatter(aprs_constants.LOG_FORMAT)
37
    logger.addHandler(console_handler)
38
    logger.propagate = False
39
40
    def __init__(self, user, password='-1', input_url=None):
41
        self.user = user
42
        self._url = input_url or aprs_constants.APRSIS_URL
43
        self._auth = ' '.join(
44
            ['user', user, 'pass', password, 'vers', 'APRS Python Module'])
45
        self.aprsis_sock = None
46
        self.data_buffer = ''
47
        self.packet_buffer = []
48
49
    def __reset_buffer(self):
50
        self.data_buffer = ''
51
        self.packet_buffer = []
52
53
    def connect(self, server=None, port=None, aprs_filter=None):
54
        """
55
        Connects & logs in to APRS-IS.
56
57
        :param server: Optional alternative APRS-IS server.
58
        :param port: Optional APRS-IS port.
59
        :param filter: Optional filter to use.
60
        :type server: str
61
        :type port: int
62
        :type filte: str
63
        """
64
        if not self.aprsis_sock:
65
            self.__reset_buffer()
66
67
            server = server or aprs_constants.APRSIS_SERVER
68
            port = port or aprs_constants.APRSIS_FILTER_PORT
69
            aprs_filter = aprs_filter or '/'.join(['p', self.user])
70
71
            self.full_auth = ' '.join([self._auth, 'filter', aprs_filter])
72
73
            self.server = server
74
            self.port = port
75
            self.aprsis_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
76
            self.aprsis_sock.connect((server, port))
77
            self.logger.info('Connected to server=%s port=%s', server, port)
78
            self.logger.debug('Sending full_auth=%s', self.full_auth)
79
            self.aprsis_sock.sendall([ord(c) for c in (self.full_auth + '\n\r')])
80
81
    def close(self):
82
        if self.aprsis_sock:
83
            self.aprsis_sock.close()
84
            self.__reset_buffer()
85
            self.aprsis_sock = None
86
87
    def write(self, frame_decoded, headers=None, protocol='TCP'):
88
        """
89
        Sends message to APRS-IS.
90
91
        :param message: Message to send to APRS-IS.
92
        :param headers: Optional HTTP headers to post.
93
        :param protocol: Protocol to use: One of TCP, HTTP or UDP.
94
        :type message: str
95
        :type headers: dict
96
97
        :return: True on success, False otherwise.
98
        :rtype: bool
99
        """
100
101
        frame = aprs_util.encode_frame(frame_decoded)
102
        if 'TCP' in protocol:
103
            self.aprsis_sock.sendall([ord(c) for c in frame])
104
            return True
105
        elif 'HTTP' in protocol:
106
            content = '\n'.join([self._auth, frame])
107
            headers = headers or aprs_constants.APRSIS_HTTP_HEADERS
108
            result = requests.post(self._url, data=content, headers=headers)
109
            return 204 == result.status_code
110
        elif 'UDP' in protocol:
111
            content = '\n'.join([self._auth, frame])
112
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
113
            sock.sendto(
114
                content,
115
                (aprs_constants.APRSIS_SERVER, aprs_constants.APRSIS_RX_PORT)
116
            )
117
            return True
118
119
    def read(self, filter_logresp=True):
120
        """
121
        Receives from APRS-IS.
122
123
        :param callback: Optional callback to deliver data to.
124
        :type callback: func
125
        """
126
        # check if there is any data waiting
127
        read_more = True
128
        while read_more:
129
            selected = select.select([self.aprsis_sock], [], [], 0)
130
            if len(selected[0]) > 0:
131
                recvd_data = self.aprsis_sock.recv(aprs_constants.RECV_BUFFER)
132
                if recvd_data:
133
                    self.data_buffer += recvd_data
134
                else:
135
                    read_more = False
136
            else:
137
                read_more = False
138
139
        # check for any complete packets and move them to the packet buffer
140
        if '\r\n' in self.data_buffer:
141
            partial = True
142
            if self.data_buffer.endswith('\r\n'):
143
                partial = False
144
            packets = recvd_data.split('\r\n')
145
            if partial:
146
                self.data_buffer = str(packets.pop(-1))
147
            else:
148
                self.data_buffer = ''
149
            for packet in packets:
150
                self.packet_buffer += [str(packet)]
151
152
        # return the next packet that matches the filter
153
        while len(self.packet_buffer):
154
            packet = self.packet_buffer.pop(0)
155
            if filter_logresp and packet.startswith('#') and 'logresp' in packet:
156
                pass
157
            else:
158
                return aprs_util.decode_frame(packet)
159
160
        return None
161