summaryrefslogtreecommitdiff
path: root/fax/pmlfax.py
diff options
context:
space:
mode:
Diffstat (limited to 'fax/pmlfax.py')
-rw-r--r--fax/pmlfax.py1026
1 files changed, 1026 insertions, 0 deletions
diff --git a/fax/pmlfax.py b/fax/pmlfax.py
new file mode 100644
index 0000000..45a55f2
--- /dev/null
+++ b/fax/pmlfax.py
@@ -0,0 +1,1026 @@
+# -*- coding: utf-8 -*-
+#
+# (c) Copyright 2003-2007 Hewlett-Packard Development Company, L.P.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# Author: Don Welch
+#
+
+# Std Lib
+import sys
+import os
+import os.path
+import struct
+import time
+import threading
+
+# Local
+from base.g import *
+from base.codes import *
+from base import device, utils, pml, codes
+from prnt import cups
+from fax import *
+
+
+# **************************************************************************** #
+
+# Page flags
+PAGE_FLAG_NONE = 0x00
+PAGE_FLAG_NEW_PAGE = 0x01
+PAGE_FLAG_END_PAGE = 0x02
+PAGE_FLAG_NEW_DOC = 0x04
+PAGE_FLAG_END_DOC = 0x08
+PAGE_FLAG_END_STREAM = 0x10
+
+MAJOR_VER = 2
+MINOR_VER = 0
+
+MFPDTF_RASTER_BITMAP = 0 # Not used
+MFPDTF_RASTER_GRAYMAP = 1 # Not used
+MFPDTF_RASTER_MH = 2 # OfficeJets B&W Fax
+MFPDTF_RASTER_MR = 3 # Not used
+MFPDTF_RASTER_MMR = 4 # LaserJets B&W Fax
+MFPDTF_RASTER_RGB = 5 # Not used
+MFPDTF_RASTER_YCC411 = 6 # Not used
+MFPDTF_RASTER_JPEG = 7 # Color Fax
+MFPDTF_RASTER_PCL = 8 # Not used
+MFPDTF_RASTER_NOT = 9 # Not used
+
+# Data types for FH
+DT_UNKNOWN = 0
+DT_FAX_IMAGES = 1
+DT_SCANNED_IMAGES= 2
+DT_DIAL_STRINGS = 3
+DT_DEMO_PAGES = 4
+DT_SPEED_DIALS = 5
+DT_FAX_LOGS = 6
+DT_CFG_PARMS = 7
+DT_LANG_STRS = 8
+DT_JUNK_FAX_CSIDS= 9
+DT_REPORT_STRS = 10
+DT_FONTS = 11
+DT_TTI_BITMAP = 12
+DT_COUNTERS = 13
+DT_DEF_PARMS = 14
+DT_SCAN_OPTIONS = 15
+DT_FW_JOB_TABLE = 17
+
+# Raster data record types
+RT_START_PAGE = 0
+RT_RASTER = 1
+RT_END_PAGE = 2
+
+# FH
+FIXED_HEADER_SIZE = 8
+
+# Variants
+IMAGE_VARIANT_HEADER_SIZE = 10
+DIAL_STRINGS_VARIANT_HEADER_SIZE = 6
+FAX_IMAGE_VARIANT_HEADER_SIZE = 74
+
+# Data records
+SOP_RECORD_SIZE = 36
+RASTER_RECORD_SIZE = 4
+EOP_RECORD_SIZE = 12
+DIAL_STRING_RECORD_SIZE = 51
+
+# Page flags
+PAGE_FLAG_NEW_PAGE = 0x01
+PAGE_FLAG_END_PAGE = 0x02
+PAGE_FLAG_NEW_DOC = 0x04
+PAGE_FLAG_END_DOC = 0x08
+PAGE_FLAG_END_STREAM = 0x10
+
+# Fax data variant header data source
+SRC_UNKNOWN = 0
+SRC_HOST = 2
+SRC_SCANNER = 5
+SRC_HOST_THEN_SCANNER = 6
+SRC_SCANNER_THEN_HOST = 7
+
+# Fax data variant header TTI header control
+TTI_NONE = 0
+TTI_PREPENDED_TO_IMAGE = 1
+TTI_OVERLAYED_ON_IMAGE = 2
+
+RASTER_DATA_SIZE = 504
+
+
+
+# **************************************************************************** #
+class PMLFaxDevice(FaxDevice):
+
+ def __init__(self, device_uri=None, printer_name=None,
+ callback=None,
+ fax_type=FAX_TYPE_NONE,
+ disable_dbus=False):
+
+ FaxDevice.__init__(self, device_uri,
+ printer_name,
+ callback, fax_type,
+ disable_dbus)
+
+ self.send_fax_thread = None
+ self.upload_log_thread = None
+
+
+ def setPhoneNum(self, num):
+ return self.setPML(pml.OID_FAX_LOCAL_PHONE_NUM, str(num))
+
+ def getPhoneNum(self):
+ return utils.printable(str(self.getPML(pml.OID_FAX_LOCAL_PHONE_NUM)[1]))
+
+ phone_num = property(getPhoneNum, setPhoneNum, doc="OID_FAX_LOCAL_PHONE_NUM")
+
+
+ def setStationName(self, name):
+ return self.setPML(pml.OID_FAX_STATION_NAME, str(name))
+
+ def getStationName(self):
+ return utils.printable(str(self.getPML(pml.OID_FAX_STATION_NAME)[1]))
+
+ station_name = property(getStationName, setStationName, doc="OID_FAX_STATION_NAME")
+
+ def setDateAndTime(self):
+ t = time.localtime()
+ p = struct.pack("BBBBBBB", t[0]-2000, t[1], t[2], t[6]+1, t[3], t[4], t[5])
+ log.debug(repr(p))
+ return self.setPML(pml.OID_DATE_AND_TIME, p)
+
+ def uploadLog(self):
+ if not self.isUloadLogActive():
+ self.upload_log_thread = UploadLogThread(self)
+ self.upload_log_thread.start()
+ return True
+ else:
+ return False
+
+ def isUploadLogActive(self):
+ if self.upload_log_thread is not None:
+ return self.upload_log_thread.isAlive()
+ else:
+ return False
+
+ def waitForUploadLogThread(self):
+ if self.upload_log_thread is not None and \
+ self.upload_log_thread.isAlive():
+
+ self.upload_log_thread.join()
+
+ def sendFaxes(self, phone_num_list, fax_file_list, cover_message='', cover_re='',
+ cover_func=None, preserve_formatting=False, printer_name='',
+ update_queue=None, event_queue=None):
+
+ if not self.isSendFaxActive():
+
+ self.send_fax_thread = PMLFaxSendThread(self, self.service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func,
+ preserve_formatting,
+ printer_name, update_queue,
+ event_queue)
+
+ self.send_fax_thread.start()
+ return True
+ else:
+ return False
+
+
+
+# **************************************************************************** #
+class PMLUploadLogThread(threading.Thread):
+ def __init__(self, dev):
+ threading.Thread.__init__(self)
+ self.dev = dev
+
+
+ def run(self):
+ STATE_DONE = 0
+ STATE_ABORT = 10
+ STATE_SUCCESS = 20
+ STATE_BUSY = 25
+ STATE_DEVICE_OPEN = 28
+ STATE_CHECK_IDLE = 30
+ STATE_REQUEST_START = 40
+ STATE_WAIT_FOR_ACTIVE = 50
+ STATE_UPLOAD_DATA = 60
+ STATE_DEVICE_CLOSE = 70
+
+ state = STATE_CHECK_IDLE
+
+ while state != STATE_DONE: # --------------------------------- Log upload state machine
+ if state == STATE_ABORT:
+ pass
+ elif state == STATE_SUCCESS:
+ pass
+ elif state == STATE_BUSY:
+ pass
+
+ elif state == STATE_DEVICE_OPEN: # --------------------------------- Open device (28)
+ state = STATE_REQUEST_START
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ state = STATE_ERROR
+ else:
+ try:
+ dev.setPML(pml.OID_UPLOAD_TIMEOUT, pml.DEFAULT_UPLOAD_TIMEOUT)
+ except Error:
+ state = STATE_ERROR
+
+ elif state == STATE_CHECK_IDLE: # --------------------------------- Check idle (30)
+ state = STATE_REQUEST_START
+ ul_state = self.getCfgUploadState()
+
+ if ul_state != pml.UPDN_STATE_IDLE:
+ state = STATE_BUSY
+
+
+ elif state == STATE_REQUEST_START: # --------------------------------- Request start (40)
+ state = STATE_WAIT_FOR_ACTIVE
+ self.dev.setPML(pml.OID_FAX_CFG_UPLOAD_DATA_TYPE, pml.FAX_CFG_UPLOAD_DATA_TYPE_FAXLOGS)
+ self.dev.setPML(pml.OID_DEVICE_CFG_UPLOAD, pml.UPDN_STATE_REQSTART)
+
+ elif state == STATE_WAIT_FOR_ACTIVE: # --------------------------------- Wait for active state (50)
+ state = STATE_UPLOAD_DATA
+
+ tries = 0
+ while True:
+ tries += 1
+ ul_state = self.getCfgUploadState()
+
+ if ul_state == pml.UPDN_STATE_XFERACTIVE:
+ break
+
+ if ul_state in (pml.UPDN_STATE_ERRORABORT, pml.UPDN_STATE_XFERDONE):
+ log.error("Cfg upload aborted!")
+ state = STATE_ERROR
+ break
+
+ if tries > 10:
+ state = STATE_ERROR
+ log.error("Unable to get into active state!")
+ break
+
+ time.sleep(0.5)
+
+ elif state == STATE_UPLOAD_DATA: # --------------------------------- Upload log data (60)
+ pass
+
+ elif state == STATE_DEVICE_CLOSE: # --------------------------------- Close device (70)
+ self.dev.close()
+
+
+
+# **************************************************************************** #
+class PMLFaxSendThread(FaxSendThread):
+ def __init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message='', cover_re='', cover_func=None, preserve_formatting=False,
+ printer_name='', update_queue=None, event_queue=None):
+
+ FaxSendThread.__init__(self, dev, service, phone_num_list, fax_file_list,
+ cover_message, cover_re, cover_func, preserve_formatting,
+ printer_name, update_queue, event_queue)
+
+
+ def run(self):
+ #results = {} # {'file' : error_code,...}
+
+ STATE_DONE = 0
+ STATE_ABORTED = 10
+ STATE_SUCCESS = 20
+ STATE_BUSY = 25
+ STATE_READ_SENDER_INFO = 30
+ STATE_PRERENDER = 40
+ STATE_COUNT_PAGES = 50
+ STATE_NEXT_RECIPIENT = 60
+ STATE_COVER_PAGE = 70
+ STATE_SINGLE_FILE = 80
+ STATE_MERGE_FILES = 90
+ STATE_SINGLE_FILE = 100
+ STATE_SEND_FAX = 110
+ STATE_CLEANUP = 120
+ STATE_ERROR = 130
+
+ next_recipient = self.next_recipient_gen()
+
+ state = STATE_READ_SENDER_INFO
+ self.rendered_file_list = []
+
+ while state != STATE_DONE: # --------------------------------- Fax state machine
+ if self.check_for_cancel():
+ state = STATE_ABORTED
+
+ log.debug("STATE=(%d, 0, 0)" % state)
+
+ if state == STATE_ABORTED: # --------------------------------- Aborted (10, 0, 0)
+ log.error("Aborted by user.")
+ self.write_queue((STATUS_IDLE, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_SUCCESS: # --------------------------------- Success (20, 0, 0)
+ log.debug("Success.")
+ self.write_queue((STATUS_COMPLETED, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_ERROR: # --------------------------------- Error (130, 0, 0)
+ log.error("Error, aborting.")
+ self.write_queue((STATUS_ERROR, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_BUSY: # --------------------------------- Busy (25, 0, 0)
+ log.error("Device busy, aborting.")
+ self.write_queue((STATUS_BUSY, 0, ''))
+ state = STATE_CLEANUP
+
+
+ elif state == STATE_READ_SENDER_INFO: # --------------------------------- Get sender info (30, 0, 0)
+ log.debug("%s State: Get sender info" % ("*"*20))
+ state = STATE_PRERENDER
+ try:
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ state = STATE_ERROR
+ else:
+ try:
+ self.sender_name = self.dev.station_name
+ log.debug("Sender name=%s" % self.sender_name)
+ self.sender_fax = self.dev.phone_num
+ log.debug("Sender fax=%s" % self.sender_fax)
+ except Error:
+ log.error("PML get failed!")
+ state = STATE_ERROR
+
+ finally:
+ self.dev.close()
+
+
+ elif state == STATE_PRERENDER: # --------------------------------- Pre-render non-G3 files (40, 0, 0)
+ log.debug("%s State: Pre-render non-G3 files" % ("*"*20))
+ state = self.pre_render(STATE_COUNT_PAGES)
+
+
+ elif state == STATE_COUNT_PAGES: # --------------------------------- Get total page count (50, 0, 0)
+ log.debug("%s State: Get total page count" % ("*"*20))
+ state = self.count_pages(STATE_NEXT_RECIPIENT)
+
+
+ elif state == STATE_NEXT_RECIPIENT: # --------------------------------- Loop for multiple recipients (60, 0, 0)
+ log.debug("%s State: Next recipient" % ("*"*20))
+ state = STATE_COVER_PAGE
+
+ try:
+ recipient = next_recipient.next()
+ #print recipient
+ log.debug("Processing for recipient %s" % recipient['name'])
+
+ self.write_queue((STATUS_SENDING_TO_RECIPIENT, 0, recipient['name']))
+
+ except StopIteration:
+ state = STATE_SUCCESS
+ log.debug("Last recipient.")
+ continue
+
+ self.recipient_file_list = self.rendered_file_list[:]
+
+
+ elif state == STATE_COVER_PAGE: # --------------------------------- Create cover page (70, 0, 0)
+ log.debug("%s State: Render cover page" % ("*"*20))
+ state = self.cover_page(recipient)
+
+
+ elif state == STATE_SINGLE_FILE: # --------------------------------- Special case for single file (no merge) (80, 0, 0)
+ log.debug("%s State: Handle single file" % ("*"*20))
+ state = self.single_file(STATE_SEND_FAX)
+
+ elif state == STATE_MERGE_FILES: # --------------------------------- Merge multiple G3 files (90, 0, 0)
+ log.debug("%s State: Merge multiple files" % ("*"*20))
+ state = self.merge_files(STATE_SEND_FAX)
+
+ elif state == STATE_SEND_FAX: # --------------------------------- Send fax state machine (110, 0, 0)
+ log.debug("%s State: Send fax" % ("*"*20))
+ state = STATE_NEXT_RECIPIENT
+
+ FAX_SEND_STATE_DONE = 0
+ FAX_SEND_STATE_ABORT = 10
+ FAX_SEND_STATE_ERROR = 20
+ FAX_SEND_STATE_BUSY = 25
+ FAX_SEND_STATE_SUCCESS = 30
+ FAX_SEND_STATE_DEVICE_OPEN = 40
+ FAX_SEND_STATE_SET_TOKEN = 50
+ FAX_SEND_STATE_EARLY_OPEN = 60
+ FAX_SEND_STATE_SET_PARAMS = 70
+ FAX_SEND_STATE_CHECK_IDLE = 80
+ FAX_SEND_STATE_START_REQUEST = 90
+ FAX_SEND_STATE_LATE_OPEN = 100
+ FAX_SEND_STATE_SEND_DIAL_STRINGS = 110
+ FAX_SEND_STATE_SEND_FAX_HEADER = 120
+ FAX_SEND_STATE_SEND_PAGES = 130
+ FAX_SEND_STATE_SEND_END_OF_STREAM = 140
+ FAX_SEND_STATE_WAIT_FOR_COMPLETE = 150
+ FAX_SEND_STATE_RESET_TOKEN = 160
+ FAX_SEND_STATE_CLOSE_SESSION = 170
+
+ monitor_state = False
+ error_state = pml.DN_ERROR_NONE
+ fax_send_state = FAX_SEND_STATE_DEVICE_OPEN
+
+ while fax_send_state != FAX_SEND_STATE_DONE:
+
+ if self.check_for_cancel():
+ log.error("Fax send aborted.")
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if monitor_state:
+ fax_state = self.getFaxDownloadState()
+ if not fax_state in (pml.UPDN_STATE_XFERACTIVE, pml.UPDN_STATE_XFERDONE):
+ log.error("D/L error state=%d" % fax_state)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ state = STATE_ERROR
+
+ log.debug("STATE=(%d, %d, 0)" % (STATE_SEND_FAX, fax_send_state))
+
+ if fax_send_state == FAX_SEND_STATE_ABORT: # -------------- Abort (110, 10, 0)
+ # TODO: Set D/L state to ???
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_ABORTED
+
+ elif fax_send_state == FAX_SEND_STATE_ERROR: # -------------- Error (110, 20, 0)
+ log.error("Fax send error.")
+ error_state = self.getFaxDownloadError()
+ log.debug("Error State=%d (%s)" % (error_state, pml.DN_ERROR_STR.get(error_state, "Unknown")))
+ monitor_state = False
+
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_BUSY: # -------------- Busy (110, 25, 0)
+ log.error("Fax device busy.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_BUSY
+
+ elif fax_send_state == FAX_SEND_STATE_SUCCESS: # -------------- Success (110, 30, 0)
+ log.debug("Fax send success.")
+ monitor_state = False
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_NEXT_RECIPIENT
+
+ elif fax_send_state == FAX_SEND_STATE_DEVICE_OPEN: # -------------- Device open (110, 40, 0)
+ log.debug("%s State: Open device" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SET_TOKEN
+ try:
+ self.dev.open()
+ except Error, e:
+ log.error("Unable to open device (%s)." % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ if self.dev.device_state == DEVICE_STATE_NOT_FOUND:
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ elif fax_send_state == FAX_SEND_STATE_SET_TOKEN: # -------------- Acquire fax token (110, 50, 0)
+ log.debug("%s State: Acquire fax token" % ("*"*20))
+ try:
+ result_code, token = self.dev.getPML(pml.OID_FAX_TOKEN)
+ except Error:
+ log.debug("Unable to acquire fax token (1).")
+ fax_send_state = FAX_SEND_STATE_EARLY_OPEN
+ else:
+ if result_code > pml.ERROR_MAX_OK:
+ fax_send_state = FAX_SEND_STATE_EARLY_OPEN
+ log.debug("Skipping token acquisition.")
+ else:
+ token = time.strftime("%d%m%Y%H:%M:%S", time.gmtime())
+ log.debug("Setting token: %s" % token)
+ try:
+ self.dev.setPML(pml.OID_FAX_TOKEN, token)
+ except Error:
+ log.error("Unable to acquire fax token (2).")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ result_code, check_token = self.dev.getPML(pml.OID_FAX_TOKEN)
+
+ if check_token == token:
+ fax_send_state = FAX_SEND_STATE_EARLY_OPEN
+ else:
+ log.error("Unable to acquire fax token (3).")
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_EARLY_OPEN: # -------------- Early open (newer models) (110, 60, 0)
+ log.debug("%s State: Early open" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_CHECK_IDLE
+
+ if self.dev.fax_type == FAX_TYPE_BLACK_SEND_EARLY_OPEN: # newer
+ log.debug("Opening fax channel.")
+ try:
+ self.dev.openFax()
+ except Error, e:
+ log.error("Unable to open channel (%s)." % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Skipped.")
+
+
+ elif fax_send_state == FAX_SEND_STATE_CHECK_IDLE: # -------------- Check for initial idle (110, 80, 0)
+ log.debug("%s State: Check idle" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_START_REQUEST
+
+ dl_state = self.getFaxDownloadState()
+ tx_status = self.getFaxJobTxStatus()
+ rx_status = self.getFaxJobRxStatus()
+
+ if ((dl_state == pml.UPDN_STATE_IDLE or \
+ dl_state == pml.UPDN_STATE_ERRORABORT or \
+ dl_state == pml.UPDN_STATE_XFERDONE) and \
+ (tx_status == pml.FAXJOB_TX_STATUS_IDLE or tx_status == pml.FAXJOB_TX_STATUS_DONE) and \
+ (rx_status == pml.FAXJOB_RX_STATUS_IDLE or rx_status == pml.FAXJOB_RX_STATUS_DONE)):
+
+ # xwas if state == pml.UPDN_STATE_IDLE:
+ if dl_state == pml.UPDN_STATE_IDLE:
+ log.debug("Starting in idle state")
+ else:
+ log.debug("Resetting to idle...")
+ self.dev.setPML(pml.OID_FAX_DOWNLOAD, pml.UPDN_STATE_IDLE)
+ time.sleep(0.5)
+ else:
+ fax_send_state = FAX_SEND_STATE_BUSY
+
+ elif fax_send_state == FAX_SEND_STATE_START_REQUEST: # -------------- Request fax start (110, 90, 0)
+ log.debug("%s State: Request start" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SET_PARAMS
+
+ dl_state = self.getFaxDownloadState()
+
+ if dl_state == pml.UPDN_STATE_IDLE:
+ log.debug("Try: 0")
+ log.debug("Setting to up/down state request start...")
+ self.dev.setPML(pml.OID_FAX_DOWNLOAD, pml.UPDN_STATE_REQSTART)
+ time.sleep(1)
+
+ log.debug("Waiting for active state...")
+ i = 1
+
+ while i < 10:
+ log.debug("Try: %d" % i)
+ try:
+ dl_state = self.getFaxDownloadState()
+ except Error:
+ log.error("PML/SNMP error")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ if dl_state == pml.UPDN_STATE_XFERACTIVE:
+ break
+
+ time.sleep(1)
+ log.debug("Setting to up/down state request start...")
+ self.dev.setPML(pml.OID_FAX_DOWNLOAD, pml.UPDN_STATE_REQSTART)
+
+ i += 1
+
+ else:
+ log.error("Could not get into active state!")
+ fax_send_state = FAX_SEND_STATE_BUSY
+
+ monitor_state = True
+
+ else:
+ log.error("Could not get into idle state!")
+ fax_send_state = FAX_SEND_STATE_BUSY
+
+
+ elif fax_send_state == FAX_SEND_STATE_SET_PARAMS: # -------------- Set fax send params (110, 70, 0)
+ log.debug("%s State: Set params" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_LATE_OPEN
+
+ try:
+ self.dev.setPML(pml.OID_DEV_DOWNLOAD_TIMEOUT, pml.DEFAULT_DOWNLOAD_TIMEOUT)
+ self.dev.setPML(pml.OID_FAXJOB_TX_TYPE, pml.FAXJOB_TX_TYPE_HOST_ONLY)
+ log.debug("Setting date and time on device.")
+ self.dev.setDateAndTime()
+ except Error, e:
+ log.error("PML/SNMP error (%s)" % e.msg)
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_LATE_OPEN: # -------------- Late open (older models) (110, 100, 0)
+ log.debug("%s State: Late open" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_DIAL_STRINGS
+
+ if self.dev.fax_type == FAX_TYPE_BLACK_SEND_LATE_OPEN: # older
+ log.debug("Opening fax channel.")
+ try:
+ self.dev.openFax()
+ except Error:
+ log.error("Unable to open channel.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Skipped.")
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_DIAL_STRINGS: # -------------- Dial strings (110, 110, 0)
+ log.debug("%s State: Send dial strings" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_FAX_HEADER
+
+ log.debug("Dialing: %s" % recipient['fax'])
+
+ log.debug("Sending dial strings...")
+ self.create_mfpdtf_fixed_header(DT_DIAL_STRINGS, True,
+ PAGE_FLAG_NEW_DOC | PAGE_FLAG_END_DOC | PAGE_FLAG_END_STREAM) # 0x1c on Windows, we were sending 0x0c
+ #print recipient
+ dial_strings = recipient['fax'].encode('ascii')
+ log.debug(repr(dial_strings))
+ self.create_mfpdtf_dial_strings(dial_strings)
+
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Channel write error.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_FAX_HEADER: # -------------- Fax header (110, 120, 0)
+ log.debug("%s State: Send fax header" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_PAGES
+
+ try:
+ ff = file(self.f, 'r')
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ try:
+ header = ff.read(FILE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ magic, version, total_pages, hort_dpi, vert_dpi, page_size, \
+ resolution, encoding, reserved1, reserved2 = self.decode_fax_header(header)
+
+ if magic != 'hplip_g3':
+ log.error("Invalid file header. Bad magic.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ else:
+ log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
+ (magic, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
+
+ log.debug("Sending fax header...")
+ self.create_mfpdtf_fixed_header(DT_FAX_IMAGES, True, PAGE_FLAG_NEW_DOC)
+ self.create_mfpdtf_fax_header(total_pages)
+
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Unable to write to channel.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_PAGES: # --------------------------------- Send fax pages state machine (110, 130, 0)
+ log.debug("%s State: Send pages" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_SEND_END_OF_STREAM
+ page = StringIO()
+
+ for p in range(total_pages):
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+
+ if fax_send_state == FAX_SEND_STATE_ABORT:
+ break
+
+ try:
+ header = ff.read(PAGE_HEADER_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ page_num, ppr, rpp, bytes_to_read, thumbnail_bytes, reserved2 = \
+ self.decode_page_header(header)
+
+ log.debug("Page=%d PPR=%d RPP=%d BPP=%d Thumb=%d" %
+ (page_num, ppr, rpp, bytes_to_read, thumbnail_bytes))
+
+ page.write(ff.read(bytes_to_read))
+ thumbnail = ff.read(thumbnail_bytes) # thrown away for now (should be 0 read)
+ page.seek(0)
+
+ self.create_mfpdtf_fixed_header(DT_FAX_IMAGES, page_flags=PAGE_FLAG_NEW_PAGE)
+ self.create_sop_record(page_num, hort_dpi, vert_dpi, ppr, rpp, encoding)
+
+ try:
+ data = page.read(RASTER_DATA_SIZE)
+ except IOError:
+ log.error("Unable to read fax file.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ if data == '':
+ log.error("No data!")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ continue
+
+ self.create_raster_data_record(data)
+ total_read = RASTER_DATA_SIZE
+
+ while True:
+ data = page.read(RASTER_DATA_SIZE)
+ total_read += RASTER_DATA_SIZE
+
+ dl_state = self.getFaxDownloadState()
+ if dl_state == pml.UPDN_STATE_ERRORABORT:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+ break
+
+ if data == '':
+ self.create_eop_record(rpp)
+
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Channel write error.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ else:
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Channel write error.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ status = self.getFaxJobTxStatus()
+ while status == pml.FAXJOB_TX_STATUS_DIALING:
+ self.write_queue((STATUS_DIALING, 0, recipient['fax']))
+ time.sleep(1.0)
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+ break
+
+ dl_state = self.getFaxDownloadState()
+ if dl_state == pml.UPDN_STATE_ERRORABORT:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ status = self.getFaxJobTxStatus()
+
+ if fax_send_state not in (FAX_SEND_STATE_ABORT, FAX_SEND_STATE_ERROR):
+
+ while status == pml.FAXJOB_TX_STATUS_CONNECTING:
+ self.write_queue((STATUS_CONNECTING, 0, recipient['fax']))
+ time.sleep(1.0)
+
+ if self.check_for_cancel():
+ fax_send_state = FAX_SEND_STATE_ABORT
+ break
+
+ dl_state = self.getFaxDownloadState()
+ if dl_state == pml.UPDN_STATE_ERRORABORT:
+ fax_send_state = FAX_SEND_STATE_ERROR
+ break
+
+ status = self.getFaxJobTxStatus()
+
+ if status == pml.FAXJOB_TX_STATUS_TRANSMITTING:
+ self.write_queue((STATUS_SENDING, page_num, recipient['fax']))
+
+ self.create_mfpdtf_fixed_header(DT_FAX_IMAGES, page_flags=0)
+ self.create_raster_data_record(data)
+
+ if fax_send_state in (FAX_SEND_STATE_ABORT, FAX_SEND_STATE_ERROR):
+ break
+
+ page.truncate(0)
+ page.seek(0)
+
+
+ elif fax_send_state == FAX_SEND_STATE_SEND_END_OF_STREAM: # -------------- EOS (110, 140, 0)
+ log.debug("%s State: Send EOS" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_WAIT_FOR_COMPLETE
+ log.debug("End of stream...")
+ self.create_mfpdtf_fixed_header(DT_FAX_IMAGES, False, PAGE_FLAG_END_STREAM)
+
+ try:
+ self.write_stream()
+ except Error:
+ log.error("Channel write error.")
+ fax_send_state = FAX_SEND_STATE_ERROR
+
+ monitor_state = False
+
+
+ elif fax_send_state == FAX_SEND_STATE_WAIT_FOR_COMPLETE: # -------------- Wait for complete (110, 150, 0)
+ log.debug("%s State: Wait for completion" % ("*"*20))
+
+ fax_send_state = FAX_SEND_STATE_WAIT_FOR_COMPLETE
+
+ time.sleep(1.0)
+ status = self.getFaxJobTxStatus()
+
+ if status == pml.FAXJOB_TX_STATUS_DIALING:
+ self.write_queue((STATUS_DIALING, 0, recipient['fax']))
+ log.debug("Dialing ...")
+
+ elif status == pml.FAXJOB_TX_STATUS_TRANSMITTING:
+ self.write_queue((STATUS_SENDING, page_num, recipient['fax']))
+ log.debug("Transmitting ...")
+
+ elif status in (pml.FAXJOB_TX_STATUS_DONE, pml.FAXJOB_RX_STATUS_IDLE):
+ fax_send_state = FAX_SEND_STATE_RESET_TOKEN
+ state = STATE_NEXT_RECIPIENT
+ log.debug("Transmitting done or idle ...")
+
+ else:
+ self.write_queue((STATUS_SENDING, page_num, recipient['fax']))
+ log.debug("Pending ...")
+
+
+ elif fax_send_state == FAX_SEND_STATE_RESET_TOKEN: # -------------- Release fax token (110, 160, 0)
+ log.debug("%s State: Release fax token" % ("*"*20))
+ self.write_queue((STATUS_CLEANUP, 0, ''))
+
+ try:
+ self.dev.setPML(pml.OID_FAX_TOKEN, '\x00'*16)
+ except Error:
+ log.error("Unable to release fax token.")
+
+ fax_send_state = FAX_SEND_STATE_CLOSE_SESSION
+
+
+ elif fax_send_state == FAX_SEND_STATE_CLOSE_SESSION: # -------------- Close session (110, 170, 0)
+ log.debug("%s State: Close session" % ("*"*20))
+ fax_send_state = FAX_SEND_STATE_DONE
+ log.debug("Closing session...")
+
+ try:
+ mm.close()
+ except NameError:
+ pass
+
+ try:
+ ff.close()
+ except NameError:
+ pass
+
+ if self.dev.fax_type == FAX_TYPE_BLACK_SEND_LATE_OPEN:
+ log.debug("Closing fax channel.")
+ self.dev.closeFax()
+
+ self.dev.setPML(pml.OID_FAX_DOWNLOAD, pml.UPDN_STATE_IDLE)
+
+ time.sleep(1)
+
+ if self.dev.fax_type == FAX_TYPE_BLACK_SEND_EARLY_OPEN:
+ log.debug("Closing fax channel.")
+ self.dev.closeFax()
+
+ self.dev.close()
+
+
+ elif state == STATE_CLEANUP: # --------------------------------- Cleanup (120, 0, 0)
+ log.debug("%s State: Cleanup" % ("*"*20))
+
+ if self.remove_temp_file:
+ log.debug("Removing merged file: %s" % self.f)
+ try:
+ os.remove(self.f)
+ log.debug("Removed")
+ except OSError:
+ log.debug("Not found")
+
+ state = STATE_DONE
+
+
+
+# --------------------------------- Support functions
+
+
+ def getFaxDownloadState(self):
+ result_code, state = self.dev.getPML(pml.OID_FAX_DOWNLOAD)
+ if state:
+ log.debug("D/L State=%d (%s)" % (state, pml.UPDN_STATE_STR.get(state, 'Unknown')))
+ return state
+ else:
+ return pml.UPDN_STATE_ERRORABORT
+
+ def getFaxDownloadError(self):
+ result_code, state = self.dev.getPML(pml.OID_FAX_DOWNLOAD_ERROR)
+ if state:
+ return state
+ else:
+ return pml.DN_ERROR_UNKNOWN
+
+ def getFaxJobTxStatus(self):
+ result_code, status = self.dev.getPML(pml.OID_FAXJOB_TX_STATUS)
+ if status:
+ log.debug("Tx Status=%d (%s)" % (status, pml.FAXJOB_TX_STATUS_STR.get(status, 'Unknown')))
+ return status
+ else:
+ return pml.FAXJOB_TX_STATUS_IDLE
+
+ def getFaxJobRxStatus(self):
+ result_code, status = self.dev.getPML(pml.OID_FAXJOB_RX_STATUS)
+ if status:
+ log.debug("Rx Status=%d (%s)" % (status, pml.FAXJOB_RX_STATUS_STR.get(status, 'Unknown')))
+ return status
+ else:
+ return pml.FAXJOB_RX_STATUS_IDLE
+
+ def getCfgUploadState(self):
+ result_code, state = self.dev.getPML(pml.OID_DEVICE_CFG_UPLOAD)
+ if state:
+ log.debug("Cfg Upload State = %d (%s)" % (state, pml.UPDN_STATE_STR.get(state, 'Unknown')))
+ return state
+ else:
+ return pml.UPDN_STATE_ERRORABORT
+
+ def create_mfpdtf_fixed_header(self, data_type, send_variant=False, page_flags=0):
+ header_len = FIXED_HEADER_SIZE
+
+ if send_variant:
+ if data_type == DT_DIAL_STRINGS:
+ header_len += DIAL_STRINGS_VARIANT_HEADER_SIZE
+
+ elif data_type == DT_FAX_IMAGES:
+ header_len += FAX_IMAGE_VARIANT_HEADER_SIZE
+
+ self.stream.write(struct.pack("<IHBB",
+ 0, header_len, data_type, page_flags))
+
+
+ def create_mfpdtf_dial_strings(self, number):
+ p = struct.pack("<BBHH51s",
+ MAJOR_VER, MINOR_VER,
+ 1, 51, number[:51])
+ log.debug(repr(p))
+ self.stream.write(p)
+
+
+ def adjust_fixed_header_block_size(self):
+ size = self.stream.tell()
+ self.stream.seek(0)
+ self.stream.write(struct.pack("<I", size))
+
+
+ def create_sop_record(self, page_num, hort_dpi, vert_dpi, ppr, rpp, encoding, bpp=1):
+ self.stream.write(struct.pack("<BBHHHIHHHHHHIHHHH",
+ RT_START_PAGE, encoding, page_num,
+ ppr, bpp,
+ rpp, 0x00, hort_dpi, 0x00, vert_dpi,
+ ppr, bpp,
+ rpp, 0x00, hort_dpi, 0x00, vert_dpi))
+
+
+ def create_eop_record(self, rpp):
+ self.stream.write(struct.pack("<BBBBII",
+ RT_END_PAGE, 0, 0, 0,
+ rpp, 0,))
+
+
+ def create_raster_data_record(self, data):
+ assert len(data) <= RASTER_DATA_SIZE
+ self.stream.write(struct.pack("<BBH",
+ RT_RASTER, 0, len(data),))
+ self.stream.write(data)
+
+
+ def create_mfpdtf_fax_header(self, total_pages):
+ self.stream.write(struct.pack("<BBBHBI20s20s20sI",
+ MAJOR_VER, MINOR_VER, SRC_HOST, total_pages,
+ TTI_PREPENDED_TO_IMAGE, 0, '', '', '', 0))
+
+
+ def write_stream(self):
+ self.adjust_fixed_header_block_size()
+ self.dev.writeFax(self.stream.getvalue())
+ self.stream.truncate(0)
+ self.stream.seek(0)