Stefan Schuermans commited on 2017-05-25 22:18:34
Showing 7 changed files, with 223 additions and 0 deletions.
... | ... |
@@ -0,0 +1,106 @@ |
1 |
+import msg |
|
2 |
+import parse |
|
3 |
+ |
|
4 |
+ |
|
5 |
+class Display(object): |
|
6 |
+ |
|
7 |
+ def __init__(self, config_file, msg_obj=None): |
|
8 |
+ """create a new EtherPix display |
|
9 |
+ config_file: name of config file to read |
|
10 |
+ msg_obj: message callback object or None""" |
|
11 |
+ self._msg = msg_obj |
|
12 |
+ if self._msg is None: # use default message callback if none given |
|
13 |
+ self._msg = msg.MsgDef() |
|
14 |
+ # default settings |
|
15 |
+ self._bind_addr = ("0.0.0.0", 0) # local network address to bind to |
|
16 |
+ self._size = (0, 0) # size of display |
|
17 |
+ # read config file |
|
18 |
+ if not self._proc_config_file(config_file): |
|
19 |
+ raise Exception("error(s) while reading config file") |
|
20 |
+ |
|
21 |
+ def _proc_config_file(self, config_file): |
|
22 |
+ """process config file |
|
23 |
+ config_file: name of config file to read |
|
24 |
+ returns True on success, False on error""" |
|
25 |
+ self._msg.msg(msg.Msg.INFO, "using config file \"%s\"" % config_file) |
|
26 |
+ # process all lines in config file |
|
27 |
+ okay = True |
|
28 |
+ try: |
|
29 |
+ with open(config_file, "r") as cfile: |
|
30 |
+ lineno = 1 |
|
31 |
+ for line in cfile: |
|
32 |
+ if not self._proc_config_line(line, lineno): |
|
33 |
+ okay = False |
|
34 |
+ lineno += 1 |
|
35 |
+ except (IOError, OSError) as e: |
|
36 |
+ self._msg.msg(msg.Msg.ERR, str(e)) |
|
37 |
+ okay = False |
|
38 |
+ return okay |
|
39 |
+ |
|
40 |
+ def _proc_config_line(self, line, lineno): |
|
41 |
+ """process line from config file |
|
42 |
+ line: line read from config file |
|
43 |
+ lineno: line number |
|
44 |
+ returns True on success, False on error""" |
|
45 |
+ # parse config file line "setting = value # comment" |
|
46 |
+ line_no_comment = line.split("#", 1)[0].strip() |
|
47 |
+ if line_no_comment == "": |
|
48 |
+ return True # ignore empty lines |
|
49 |
+ else: |
|
50 |
+ fields = line_no_comment.split("=", 1) |
|
51 |
+ if len(fields) < 2: |
|
52 |
+ self._msg.msg(msg.Msg.WARN, |
|
53 |
+ "invalid line %u in config file, ignored" |
|
54 |
+ % lineno) |
|
55 |
+ return True |
|
56 |
+ else: |
|
57 |
+ setting = fields[0].strip() |
|
58 |
+ value = fields[1].strip() |
|
59 |
+ return self._proc_config_setting(setting, value, lineno) |
|
60 |
+ |
|
61 |
+ def _proc_config_setting(self, setting, value, lineno): |
|
62 |
+ """process setting from config file |
|
63 |
+ setting: name of setting |
|
64 |
+ value: value of setting |
|
65 |
+ returns True on success, False on error""" |
|
66 |
+ # replace all whitespace sequences in setting with single spaces |
|
67 |
+ setting = " ".join(setting.split()) |
|
68 |
+ # process setting |
|
69 |
+ if setting == "bindAddr": |
|
70 |
+ addr = parse._parse_addr(value) |
|
71 |
+ if addr is None: |
|
72 |
+ self._msg.msg(msg.Msg.ERR, |
|
73 |
+ "invalid address \"%s\" for \"bindAddr\"" |
|
74 |
+ " in line %u in config file" |
|
75 |
+ % (value, lineno)) |
|
76 |
+ return False |
|
77 |
+ else: |
|
78 |
+ self._bind_addr = addr |
|
79 |
+ self._msg.msg(msg.Msg.INFO, |
|
80 |
+ "bind address \"%s:%u\"" % (addr[0], addr[1])) |
|
81 |
+ return True |
|
82 |
+ elif setting == "size": |
|
83 |
+ size = parse._parse_two_nos(value) |
|
84 |
+ if size is None: |
|
85 |
+ self._msg.msg(msg.Msg.ERR, |
|
86 |
+ "invalid address \"%s\" for \"size\"" |
|
87 |
+ " in line %u in config file" |
|
88 |
+ % (value, lineno)) |
|
89 |
+ return False |
|
90 |
+ else: |
|
91 |
+ self._size = size |
|
92 |
+ return True |
|
93 |
+ elif setting.startswith("distributor "): |
|
94 |
+ return True # TODO |
|
95 |
+ elif setting.startswith("distributorAddr "): |
|
96 |
+ return True # TODO |
|
97 |
+ elif setting.startswith("mapping "): |
|
98 |
+ return True # TODO |
|
99 |
+ elif setting.startswith("output "): |
|
100 |
+ return True # TODO |
|
101 |
+ else: |
|
102 |
+ self._msg.msg(msg.Msg.WARN, |
|
103 |
+ "unknown setting \"%s\" in line %u in config file," |
|
104 |
+ " ignored" % (setting, lineno)) |
|
105 |
+ return True |
|
106 |
+ |
... | ... |
@@ -0,0 +1,18 @@ |
1 |
+#! /usr/bin/env python |
|
2 |
+ |
|
3 |
+import sys |
|
4 |
+import pyetherpix |
|
5 |
+ |
|
6 |
+ |
|
7 |
+def main(argv): |
|
8 |
+ if len(argv) < 2: |
|
9 |
+ print >>sys.stderr, "usage: %s <config.etp>" % argv[0] |
|
10 |
+ return 2 |
|
11 |
+ config_file = argv[1] |
|
12 |
+ pyetherpix.Display(config_file) |
|
13 |
+ return 0 |
|
14 |
+ |
|
15 |
+ |
|
16 |
+if __name__ == "__main__": |
|
17 |
+ sys.exit(main(sys.argv)) |
|
18 |
+ |
... | ... |
@@ -0,0 +1,42 @@ |
1 |
+import sys |
|
2 |
+ |
|
3 |
+ |
|
4 |
+class Msg(object): |
|
5 |
+ |
|
6 |
+ ERR = 1 |
|
7 |
+ WARN = 2 |
|
8 |
+ INFO = 3 |
|
9 |
+ |
|
10 |
+ def __init__(self): |
|
11 |
+ """interface for message callback object""" |
|
12 |
+ |
|
13 |
+ def msg(self, level, text): |
|
14 |
+ """message is delivered |
|
15 |
+ level: ERR for errors, WARN for warnings, INFO for information |
|
16 |
+ text: message text""" |
|
17 |
+ raise NotImplementedError("Msg.msg is not implemented") |
|
18 |
+ |
|
19 |
+ |
|
20 |
+class MsgDef(Msg): |
|
21 |
+ |
|
22 |
+ def __init__(self, level=Msg.INFO): |
|
23 |
+ """default message callback implementation |
|
24 |
+ level: level of messages to output""" |
|
25 |
+ Msg.__init__(self) |
|
26 |
+ self._level = level |
|
27 |
+ |
|
28 |
+ def msg(self, level, text): |
|
29 |
+ """message is delivered |
|
30 |
+ level: ERR for errors, WARN for warnings, INFO for information |
|
31 |
+ text: message text""" |
|
32 |
+ if level == Msg.ERR: |
|
33 |
+ prefix = "error" |
|
34 |
+ elif level == Msg.WARN: |
|
35 |
+ prefix = "warning" |
|
36 |
+ elif level == Msg.INFO: |
|
37 |
+ prefix = "info" |
|
38 |
+ else: |
|
39 |
+ prefix = "unknown" |
|
40 |
+ if level <= self._level: |
|
41 |
+ print >>sys.stderr, prefix + ": " + text |
|
42 |
+ |
... | ... |
@@ -0,0 +1,37 @@ |
1 |
+def _parse_addr(addr_str): |
|
2 |
+ """parse an IPv4 address, e.g. \"1.2.3.4:567\", |
|
3 |
+ return tuple of IP and port, e.g. ("1.2.3.4", 567), None on error""" |
|
4 |
+ fields = addr_str.split(":") |
|
5 |
+ if len(fields) > 2: |
|
6 |
+ return None |
|
7 |
+ ip = fields[0] |
|
8 |
+ port = fields[1] |
|
9 |
+ if len(ip.split(".")) != 4: |
|
10 |
+ return None |
|
11 |
+ try: |
|
12 |
+ port = int(port) |
|
13 |
+ except: |
|
14 |
+ return None |
|
15 |
+ if port < 0 or port > 65535: |
|
16 |
+ return None |
|
17 |
+ return (ip, port) |
|
18 |
+ |
|
19 |
+def _parse_two_nos(txt): |
|
20 |
+ """parse two comma separated decimal unsigned integers from string, |
|
21 |
+ e.g. \"12,34\", return tuple of two numbers, e.g. (12, 34), |
|
22 |
+ None on error""" |
|
23 |
+ fields = txt.strip().split() |
|
24 |
+ if len(fields) > 1: |
|
25 |
+ return None |
|
26 |
+ fields = fields[0].split(",") |
|
27 |
+ if len(fields) != 2: |
|
28 |
+ return None |
|
29 |
+ try: |
|
30 |
+ no1 = int(fields[0]) |
|
31 |
+ no2 = int(fields[1]) |
|
32 |
+ except: |
|
33 |
+ return None |
|
34 |
+ if no1 < 0 or no2 < 0: |
|
35 |
+ return None |
|
36 |
+ return (no1, no2) |
|
37 |
+ |
... | ... |
@@ -0,0 +1,13 @@ |
1 |
+from setuptools import setup |
|
2 |
+ |
|
3 |
+setup(name='pyetherpix', |
|
4 |
+ version='0.1', |
|
5 |
+ description='python implementation of EtherPix output library', |
|
6 |
+ url='https://git.blinkenarea.org?p=libetherpix', |
|
7 |
+ author='Stefan Schuermans', |
|
8 |
+ author_email='stefan@schuermans.info', |
|
9 |
+ license='LGPLv3', |
|
10 |
+ packages=['pyetherpix'], |
|
11 |
+ install_requires=[], |
|
12 |
+ zip_safe=False) |
|
13 |
+ |
|
0 | 14 |