igate_tcp.rb 5.08 KB
Newer Older
1
2
3
4
5
6
7
module Apex
    class IGateTcp
        DEFAULT_APRSIS_URL = 'http://srvr.aprs-is.net:8080'
        DEFAULT_APRSIS_SERVER = 'rotate.aprs.net'
        DEFAULT_APRSIS_FILTER_PORT = 14580

        protected
8
        def initialize(user, password='-1')
9
            @user = user
10
            @auth = ['user', user, 'pass', password, 'vers', 'APEX-APRS Ruby Module'].join(' ')
11
12
13
14
15
16
17
18
19
20
21
22
23
            @aprsis_sock = nil
            @data_buffer = ''
            @packet_buffer = []
            @lock = Mutex.new
        end

        private
        def reset_buffer
            @data_buffer = ''
            @packet_buffer = []
        end

        private
24
        def self.format_path(path_list)
25
26
27
28
            path_list.join(',')
        end

        private
29
        def self.encode_frame(frame)
30
31
            formatted_frame = [frame[:source], frame[:destination]].join('>')
            if frame[:path] and frame[:path].length > 0
32
                formatted_frame = [formatted_frame, IGateTcp::format_path(frame[:path])].join(',')
33
34
35
36
37
38
39
            end
            formatted_frame += ':'
            formatted_frame += frame[:text]
            return formatted_frame
        end

        private
40
        def self.decode_frame(frame)
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
            decoded_frame = {}
            frame_so_far = ''
            path = nil
            frame.chars.each do |char|
                if char == '>' and !decoded_frame.include? :source
                    decoded_frame[:source] = frame_so_far
                    frame_so_far = ''
                elsif char == ':' and !path
                    path = frame_so_far
                    frame_so_far = ''
                else
                    frame_so_far = [frame_so_far, char].join
                end
            end

            path = path.split(',')
            decoded_frame[:destination] = path.shift
            decoded_frame[:path] = path
            decoded_frame[:text] = frame_so_far

            decoded_frame
        end

        public
        def connect(server=nil, port=nil, aprs_filter=nil, *args, **kwargs)
            @lock.synchronize do
                unless @aprsis_sock
                    reset_buffer

                    unless server
                        server = DEFAULT_APRSIS_SERVER
                    end

                    unless port
                        port = DEFAULT_APRSIS_FILTER_PORT
                    end

                    unless aprs_filter
                        aprs_filter = ['p', @user].join('/')
                    end

                    @full_auth = [@auth, 'filter', aprs_filter].join(' ')

                    @server = server
                    @port = port
                    @aprsis_sock = TCPSocket.open(@server, @port)
87
                    @aprsis_sock.puts( @full_auth + '\r\n' )
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
                end
            end
        end

        public
        def close(*args, **kwargs)
            @lock.synchronize do
                if @aprsis_sock
                    @aprsis_sock.close
                    reset_buffer
                    @aprsis_sock = nil
                end
            end
        end

        public
        def read(filter_logresp=true, *args, **kwargs)
            @lock.synchronize do
                # check if there is any data waiting
                read_more = true
                while read_more
                    selected = IO.select([@aprsis_sock], [], [], 0)
110
111
112
113
114
115
116
                    if selected.nil? == false
                        ready_len = selected.first.length
                        if ready_len > 0
                            recvd_data = @aprsis_sock.read(ready_len)
                            if recvd_data
                                @data_buffer += recvd_data
                            end
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
                        end
                    else
                        read_more = false
                    end
                end

                # check for any complete packets and move them to the packet buffer
                if @data_buffer.include? '\r\n'
                    partial = true
                    if @data_buffer.end_with? '\r\n'
                        partial = false
                    end

                    packets = @data_buffer.split('\r\n')
                    if partial
                        @data_buffer = packets.pop.dup
                    else
                        @data_buffer = ''
                    end

                    packets.each do |packet|
                        @packet_buffer << packet.dup
                    end
                end

                # return the next packet that matches the filter
                while @packet_buffer.length > 0
                    packet = @packet_buffer.pop
                    unless filter_logresp and packet.start_with?('#') and packet.include? 'logresp'
146
                        return IGateTcp::decode_frame(packet)
147
148
149
150
151
152
153
154
155
156
                    end
                end

                return nil
            end
        end

        public
        def write(frame, *args, **kwargs)
            @lock.synchronize do
157
                encoded_frame = IGateTcp::encode_frame(frame)
158
                @aprsis_sock.puts( encoded_frame )
159
160
161
            end
        end
    end
162
end