be257a9b8392375f03ab678e42e4328fbdbbd425
[ais.git] / bin / inputs / virtual.py
1 # -*- coding: utf-8 -*-
2 '''
3 AIS virtual bases for Channels and Services.
4 '''
5
6 #
7 # Un Service c'est udp ou serialin ou tcpout
8 # Une source a un id4, donc des stats un et logger, mais pas de buffer
9 # Un canal a un buffer, et est associ√© √† une source
10
11 from __future__ import division
12 import sys
13
14 __all__ = [
15     'Service',
16     ]
17
18 class Service:
19     '''
20     A service provides data for processing.
21     This is a pure virtual class, an interface, that need to be subclassed.
22     Example services are TcpOutService, UdpService, and SerialService.
23     '''
24     #def __init__(self, stdout):
25     #    '''
26     #    Set stdout = True to get a raw output suitable for piping.
27     #    '''
28     #    self.stdout = stdout
29         
30
31     def fileno(self):
32         '''
33         All services must implement such a function.
34         It should return a file descriptor suitable for os.select()
35         '''
36         raise NotImplemented
37
38     def get_activity(self):
39         '''
40         All services must implement such a function.
41         It should return a tuple (from_, channel)
42         from_ is a low-level id of the source.
43         channels contain a "data" buffer with bytes waiting to be processed,
44           and a reference to a source.
45         '''
46         raise NotImplemented
47
48
49 class Channel:
50     '''
51     A channel is a unique souce of AIVDM data.
52     Each channel has its data buffer and its own decoder context.
53     '''
54     def __init__(self):
55         self.data = '' # previous line fragment that was not fully parsed
56
57     def ais_line_reader(self):
58         """
59         parse self.data and returns lines, without \r\n
60         """
61         data = self.data
62         while True:
63             idx_line_end = sys.maxint
64             for sep in '\r\0\n':
65                 idx = data.find(sep)
66                 if idx != -1 and idx < idx_line_end:
67                     idx_line_end = idx
68             if idx_line_end == sys.maxint:
69                 self.data = data
70                 break
71     
72             ##logging.debug('data=%s idxll=%s', repr(data), idx_line_end)
73             #TODO improve me
74             line = data[:idx_line_end]
75             while data[idx_line_end] in '\r\n\0' \
76               and idx_line_end < len(data) - 1:
77                 idx_line_end += 1
78             if idx_line_end == len(data)-1:
79                 data = ''
80             else:
81                 data = data[idx_line_end:]
82             #logging.debug('line=%s data=%s', repr(line), repr(data))
83     
84             line = line.rstrip('\r\n\0')
85          
86             if not line:
87                 continue # ignore empty line
88     
89             yield line