Completed
Pull Request — master (#2)
by Jeffrey
04:10
created

AprsInternetService.send()   B

Complexity

Conditions 6

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

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