1import logging
2import os
3import pickle
4import struct
5import sys
6
7import pyinsane2.sane.abstract as pyinsane
8
9
10logger = logging.getLogger("Pyinsane_daemon")
11
12
13device_cache = {}
14scan_sessions = {}
15
16
17def get_devices(local_only):
18    global device_cache
19
20    devices = pyinsane.get_devices(local_only)
21    device_cache = {}
22    for device in devices:
23        device_cache[device.name] = device
24    return devices
25
26
27def get_device(scanner_name):
28    global device_cache
29    if scanner_name in device_cache:
30        return device_cache[scanner_name]
31    scanner = pyinsane.Scanner(scanner_name)
32    device_cache[scanner_name] = scanner
33    return scanner
34
35
36def get_options(scanner_name):
37    return get_device(scanner_name).options
38
39
40def get_option_value(scanner_name, option_name):
41    return get_device(scanner_name).options[option_name].value
42
43
44def set_option_value(scanner_name, option_name, option_value):
45    get_device(scanner_name).options[option_name].value = option_value
46
47
48def make_scan_session(scanner_name, multiple=False):
49    global scan_sessions
50
51    scan_session = get_device(scanner_name).scan(multiple)
52    scan_sessions[scanner_name] = scan_session
53    return scan_session
54
55
56def get_images(scanner_name):
57    global scan_sessions
58    imgs = scan_sessions[scanner_name].images
59    imgs = [(img.mode, img.size, img.tobytes()) for img in imgs]
60    return imgs
61
62
63def scan_read(scanner_name):
64    global scan_sessions
65    return scan_sessions[scanner_name].scan.read()
66
67
68def get_available_lines(scanner_name):
69    global scan_sessions
70    return scan_sessions[scanner_name].scan.available_lines
71
72
73def get_expected_size(scanner_name):
74    global scan_sessions
75    return scan_sessions[scanner_name].scan.expected_size
76
77
78def get_image(scanner_name, start_line, end_line):
79    global scan_sessions
80    img = scan_sessions[scanner_name].scan.get_image(start_line, end_line)
81    return (img.mode, img.size, img.tobytes())
82
83
84def cancel(scanner_name):
85    global scan_sessions
86    return scan_sessions[scanner_name].scan.cancel()
87
88
89def exit():
90    pass
91
92
93COMMANDS = {
94    "get_devices": get_devices,
95    "get_options": get_options,
96    "get_option_value": get_option_value,
97    "set_option_value": set_option_value,
98    "scan": make_scan_session,
99    "get_images": get_images,
100    "scan_read": scan_read,
101    "scan_get_available_lines": get_available_lines,
102    "scan_get_expected_size": get_expected_size,
103    "scan_get_image": get_image,
104    "scan_cancel": cancel,
105    "exit": exit,
106}
107
108
109def main_loop(fifo_dir, fifo_filepaths):
110    global COMMANDS
111
112    pyinsane.init()
113
114    length_size = len(struct.pack("i", 0))
115    fifo_c2s = os.open(fifo_filepaths[0], os.O_RDONLY)
116    fifo_s2c = os.open(fifo_filepaths[1], os.O_WRONLY)
117
118    try:
119        logger.info("Ready")
120
121        while True:
122            length = os.read(fifo_c2s, length_size)
123            if length == b'':
124                break
125            length = struct.unpack("i", length)[0]
126            cmd = os.read(fifo_c2s, length)
127            if cmd == b'':
128                break
129            assert(len(cmd) == length)
130            cmd = pickle.loads(cmd)
131
132            logger.debug("> {}".format(cmd['command']))
133            f = COMMANDS[cmd['command']]
134            result = {}
135            try:
136                result['out'] = f(*cmd['args'], **cmd['kwargs'])
137            except BaseException as exc:
138                if (not isinstance(exc, EOFError) and
139                        not isinstance(exc, StopIteration)):
140                    logger.warning("Exception", exc_info=exc)
141                result['exception'] = str(exc.__class__.__name__)
142                result['exception_args'] = exc.args
143                logger.debug("< {}".format(result))
144
145            result = pickle.dumps(result)
146            length = len(result)
147            length = struct.pack("i", length)
148            os.write(fifo_s2c, length)
149            os.write(fifo_s2c, result)
150
151            if cmd['command'] == 'exit':
152                break
153    finally:
154        os.close(fifo_s2c)
155        os.close(fifo_c2s)
156
157    logger.info("Daemon stopped")
158
159
160if __name__ == "__main__":
161    formatter = logging.Formatter(
162        '%(levelname)-6s %(name)-10s %(message)s'
163    )
164    log = logging.getLogger()
165    handler = logging.StreamHandler()
166    handler.setFormatter(formatter)
167    log.addHandler(handler)
168    log.setLevel(logging.INFO)
169
170    main_loop(sys.argv[1], sys.argv[2:4])
171