1# -*- coding: utf-8 -*-
2#
3# (c) Copyright 2003-2015 HP Development Company, L.P.
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18#
19# Author: Don Welch
20#
21
22
23
24# Std Lib
25import sys
26import os
27import os.path
28import time
29import threading
30from base.sixext.moves import queue
31from io import StringIO
32
33# Local
34from base.g import *
35from base.codes import *
36from base import device, utils, status, pml
37
38# Event queue values (UI ==> Copy thread)
39COPY_CANCELED = 1
40
41# Update queue values (Copy thread ==> UI)
42STATUS_IDLE = 0
43STATUS_SETTING_UP = 1
44STATUS_WARMING_UP = 2
45STATUS_ACTIVE = 3
46STATUS_DONE = 4
47STATUS_ERROR = 5
48
49
50# PML Copier Only
51class PMLCopyDevice(device.Device):
52    def __init__(self, device_uri=None, printer_name=None,
53                 service=None, callback=None):
54
55        device.Device.__init__(self, device_uri, printer_name,
56                               service, callback)
57
58        self.copy_thread = None
59
60    def copy(self, num_copies=1, contrast=0, reduction=100,
61             quality=pml.COPIER_QUALITY_NORMAL,
62             fit_to_page=pml.COPIER_FIT_TO_PAGE_ENABLED,
63             scan_src=SCAN_SRC_FLATBED,
64             update_queue=None, event_queue=None):
65
66        if not self.isCopyActive():
67            self.copy_thread = PMLCopyThread(self, num_copies, contrast, reduction, quality,
68                                             fit_to_page, scan_src, update_queue, event_queue)
69            self.copy_thread.start()
70            return True
71        else:
72            return False
73
74    def isCopyActive(self):
75        if self.copy_thread is not None:
76            return self.copy_thread.isAlive()
77        else:
78            return False
79
80    def waitForCopyThread(self):
81        if self.copy_thread is not None and \
82            self.copy_thread.isAlive():
83
84            self.copy_thread.join()
85
86
87
88class PMLCopyThread(threading.Thread):
89    def __init__(self, dev, num_copies, contrast, reduction, quality,
90                 fit_to_page, scan_src,
91                 update_queue=None, event_queue=None):
92
93        threading.Thread.__init__(self)
94        self.dev = dev
95        self.num_copies = num_copies
96        self.contrast = contrast
97        self.reduction = reduction
98        self.quality = quality
99        self.fit_to_page = fit_to_page
100        self.scan_src = scan_src
101        self.event_queue = event_queue
102        self.update_queue = update_queue
103        self.prev_update = ''
104        self.copy_type = self.dev.copy_type
105        log.debug("Copy-type = %d" % self.copy_type)
106
107    def run(self):
108        STATE_DONE = 0
109        STATE_ERROR = 5
110        STATE_ABORTED = 10
111        STATE_SUCCESS = 20
112        STATE_BUSY = 25
113        STATE_SET_TOKEN = 30
114        STATE_SETUP_STATE = 40
115        STATE_SETUP_PARAMS = 50
116        STATE_START = 60
117        STATE_ACTIVE = 70
118        STATE_RESET_TOKEN = 80
119
120#       state = STATE_SET_TOKEN
121        state = STATE_SETUP_STATE
122
123        while state != STATE_DONE: # ------------------------- Copier Thread
124            # revisit - Checking cancel and setting state here means
125            # every state can unconditionally transition to STATE_ABORTED.
126            # This has not been verified.
127            # if self.check_for_cancel():
128                # state = STATE_ABORTED
129
130            if state == STATE_ABORTED:
131                log.debug("%s State: Aborted" % ("*"*20))
132                self.write_queue(STATUS_DONE) # This was STATUS_ERROR.
133                state = STATE_RESET_TOKEN
134
135            if state == STATE_ERROR:
136                log.debug("%s State: Error" % ("*"*20))
137                self.write_queue(STATUS_ERROR)
138                state = STATE_RESET_TOKEN
139
140            elif state == STATE_SUCCESS:
141                log.debug("%s State: Success" % ("*"*20))
142                self.write_queue(STATUS_DONE)
143                state = STATE_RESET_TOKEN
144
145            elif state == STATE_BUSY:
146                log.debug("%s State: Busy" % ("*"*20))
147                self.write_queue(STATUS_ERROR)
148                state = STATE_RESET_TOKEN
149
150            elif state == STATE_SET_TOKEN:
151                log.debug("%s State: Acquire copy token" % ("*"*20))
152
153                self.write_queue(STATUS_SETTING_UP)
154
155                try:
156                    result_code, token = self.dev.getPML(pml.OID_COPIER_TOKEN)
157                except Error:
158                    log.debug("Unable to acquire copy token (1).")
159                    state = STATE_SETUP_STATE
160                else:
161                    if result_code > pml.ERROR_MAX_OK:
162                        state = STATE_SETUP_STATE
163                        log.debug("Skipping token acquisition.")
164                    else:
165                        token = time.strftime("%d%m%Y%H:%M:%S", time.gmtime())
166                        log.debug("Setting token: %s" % token)
167                        try:
168                            self.dev.setPML(pml.OID_COPIER_TOKEN, token)
169                        except Error:
170                            log.error("Unable to acquire copy token (2).")
171                            state = STATUS_ERROR
172                        else:
173                            result_code, check_token = self.dev.getPML(pml.OID_COPIER_TOKEN)
174
175                            if check_token == token:
176                                state = STATE_SETUP_STATE
177                            else:
178                                log.error("Unable to acquire copy token (3).")
179                                state = STATE_ERROR
180
181            elif state == STATE_SETUP_STATE:
182                log.debug("%s State: Setup state" % ("*"*20))
183
184                if self.copy_type == COPY_TYPE_DEVICE:
185                    result_code, copy_state = self.dev.getPML(pml.OID_COPIER_JOB)
186
187                    if copy_state == pml.COPIER_JOB_IDLE:
188                        self.dev.setPML(pml.OID_COPIER_JOB, pml.COPIER_JOB_SETUP)
189                        state = STATE_SETUP_PARAMS
190
191                    else:
192                        state = STATE_BUSY
193
194                elif self.copy_type == COPY_TYPE_AIO_DEVICE:
195                    result_code, copy_state = self.dev.getPML(pml.OID_SCAN_TO_PRINTER)
196
197                    if copy_state == pml.SCAN_TO_PRINTER_IDLE:
198                        state = STATE_SETUP_PARAMS
199
200                    else:
201                        state = STATE_BUSY
202
203
204
205            elif state == STATE_SETUP_PARAMS:
206                log.debug("%s State: Setup Params" % ("*"*20))
207
208                if self.num_copies < 0: self.num_copies = 1
209                if self.num_copies > 99: self.num_copies = 99
210
211                if self.copy_type == COPY_TYPE_DEVICE: # MFP
212
213                    # num_copies
214                    self.dev.setPML(pml.OID_COPIER_JOB_NUM_COPIES, self.num_copies)
215
216                    # contrast
217                    self.dev.setPML(pml.OID_COPIER_JOB_CONTRAST, self.contrast)
218
219                    # reduction
220                    self.dev.setPML(pml.OID_COPIER_JOB_REDUCTION, self.reduction)
221
222                    # quality
223                    self.dev.setPML(pml.OID_COPIER_JOB_QUALITY, self.quality)
224
225                    # fit_to_page
226                    if self.scan_src == SCAN_SRC_FLATBED:
227                        self.dev.setPML(pml.OID_COPIER_JOB_FIT_TO_PAGE, self.fit_to_page)
228
229                else: # AiO
230                    # num_copies
231                    self.dev.setPML(pml.OID_COPIER_NUM_COPIES_AIO, self.num_copies)
232
233                    # contrast
234                    self.contrast = (self.contrast * 10 / 25) + 50
235                    self.dev.setPML(pml.OID_COPIER_CONTRAST_AIO, self.contrast)
236
237                    if self.fit_to_page == pml.COPIER_FIT_TO_PAGE_ENABLED:
238                        self.reduction = 0
239
240                    # reduction
241                    self.dev.setPML(pml.OID_COPIER_REDUCTION_AIO, self.reduction)
242
243                    # quality
244                    self.dev.setPML(pml.OID_COPIER_QUALITY_AIO, self.quality)
245
246                    self.dev.setPML(pml.OID_PIXEL_DATA_TYPE, pml.PIXEL_DATA_TYPE_COLOR_24_BIT)
247                    self.dev.setPML(pml.OID_COPIER_SPECIAL_FEATURES, pml.COPY_FEATURE_NONE)
248                    self.dev.setPML(pml.OID_COPIER_PHOTO_MODE, pml.ENHANCE_LIGHT_COLORS | pml.ENHANCE_TEXT)
249
250                    # tray select
251                    self.dev.setPML(pml.OID_COPIER_JOB_INPUT_TRAY_SELECT, pml.COPIER_JOB_INPUT_TRAY_1)
252
253                    # media type
254                    self.dev.setPML(pml.OID_COPIER_MEDIA_TYPE, pml.COPIER_MEDIA_TYPE_AUTOMATIC)
255
256                    # pixel data type
257                    self.dev.setPML(pml.OID_PIXEL_DATA_TYPE, pml.PIXEL_DATA_TYPE_COLOR_24_BIT)
258
259                    # special features
260                    self.dev.setPML(pml.OID_COPIER_SPECIAL_FEATURES, pml.COPY_FEATURE_NONE)
261
262                    # media size
263                    self.dev.setPML(pml.OID_COPIER_JOB_MEDIA_SIZE, pml.COPIER_JOB_MEDIA_SIZE_US_LETTER)
264
265
266
267
268                log.debug("num_copies = %d" % self.num_copies)
269                log.debug("contrast= %d" % self.contrast)
270                log.debug("reduction = %d" % self.reduction)
271                log.debug("quality = %d" % self.quality)
272                log.debug("fit_to_page = %d" % self.fit_to_page)
273
274                state = STATE_START
275
276            elif state == STATE_START:
277                log.debug("%s State: Start" % ("*"*20))
278
279                if self.copy_type == COPY_TYPE_DEVICE:
280                    self.dev.setPML(pml.OID_COPIER_JOB, pml.COPIER_JOB_START)
281
282                elif self.copy_type == COPY_TYPE_AIO_DEVICE:
283                    self.dev.setPML(pml.OID_SCAN_TO_PRINTER, pml.SCAN_TO_PRINTER_START)
284
285                state = STATE_ACTIVE
286
287            elif state == STATE_ACTIVE:
288                log.debug("%s State: Active" % ("*"*20))
289
290                if self.copy_type == COPY_TYPE_DEVICE:
291                    while True:
292                        result_code, copy_state = self.dev.getPML(pml.OID_COPIER_JOB)
293
294                        if self.check_for_cancel():
295                            self.dev.setPML(pml.OID_COPIER_JOB, pml.COPIER_JOB_IDLE) # cancel
296                            state = STATE_ABORTED
297                            break
298
299                        if copy_state == pml.COPIER_JOB_START:
300                            log.debug("state = start")
301                            time.sleep(1)
302                            continue
303
304                        if copy_state == pml.COPIER_JOB_ACTIVE:
305                            self.write_queue(STATUS_ACTIVE)
306                            log.debug("state = active")
307                            time.sleep(2)
308                            continue
309
310                        elif copy_state == pml.COPIER_JOB_ABORTING:
311                            log.debug("state = aborting")
312                            state = STATE_ABORTED
313                            break
314
315                        elif copy_state == pml.COPIER_JOB_IDLE:
316                            log.debug("state = idle")
317                            state = STATE_SUCCESS
318                            break
319
320                elif self.copy_type == COPY_TYPE_AIO_DEVICE:
321                    while True:
322                        result_code, copy_state = self.dev.getPML(pml.OID_SCAN_TO_PRINTER)
323
324                        if self.check_for_cancel():
325                            self.dev.setPML(pml.OID_SCAN_TO_PRINTER, pml.SCAN_TO_PRINTER_IDLE) # cancel
326                            state = STATE_ABORTED
327                            break
328
329                        if copy_state == pml.SCAN_TO_PRINTER_START:
330                            log.debug("state = start")
331                            time.sleep(1)
332                            continue
333
334                        if copy_state == pml.SCAN_TO_PRINTER_ACTIVE:
335                            self.write_queue(STATUS_ACTIVE)
336                            log.debug("state = active")
337                            time.sleep(2)
338                            continue
339
340                        elif copy_state == pml.SCAN_TO_PRINTER_ABORTED:
341                            log.debug("state = aborting")
342                            state = STATE_ABORTED
343                            break
344
345                        elif copy_state == pml.SCAN_TO_PRINTER_IDLE:
346                            log.debug("state = idle")
347                            state = STATE_SUCCESS
348                            break
349
350
351            elif state == STATE_RESET_TOKEN:
352                log.debug("%s State: Release copy token" % ("*"*20))
353
354                try:
355                    self.dev.setPML(pml.OID_COPIER_TOKEN, '\x00'*16)
356                except Error:
357                    log.error("Unable to release copier token.")
358
359                self.dev.close() # Close the device.
360
361                state = STATE_DONE
362
363
364    def check_for_cancel(self):
365        canceled = False
366        while self.event_queue.qsize():
367            try:
368                event = self.event_queue.get(0)
369                if event == COPY_CANCELED:
370                    canceled = True
371                    log.debug("Cancel pressed!")
372            except queue.Empty:
373                break
374
375        return canceled
376
377    def write_queue(self, message):
378        if self.update_queue is not None and message != self.prev_update:
379            self.update_queue.put(message)
380            time.sleep(0)
381            self.prev_update = message
382