Show
Ignore:
Timestamp:
09/27/04 21:56:27 (20 years ago)
Author:
jalet
Message:

Added internal handling for PJL queries over port tcp/9100. Now waits
for printer being idle before asking, just like with SNMP.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/pykota/accounters/hardware.py

    r1745 r1746  
    2222# 
    2323# $Log$ 
     24# Revision 1.26  2004/09/27 19:56:27  jalet 
     25# Added internal handling for PJL queries over port tcp/9100. Now waits 
     26# for printer being idle before asking, just like with SNMP. 
     27# 
    2428# Revision 1.25  2004/09/27 09:21:37  jalet 
    2529# Now includes printer's hostname in SNMP error messages 
     
    111115 
    112116import os 
     117import socket 
    113118import time 
    114119import signal 
     
    208213                self.parent.filter.logdebug(_("Waiting for printer %s's idle status to stabilize...") % self.parent.filter.printername)     
    209214                time.sleep(SNMPDELAY) 
     215                 
     216pjlMessage = "\033%-12345X@PJL USTATUSOFF\r\n@PJL USTATUS DEVICE=ON\r\n@PJL INFO STATUS\r\n@PJL INFO PAGECOUNT\r\n\033%-12345X" 
     217pjlStatusValues = { 
     218                    "10000" : "Powersave Mode", 
     219                    "10001" : "Ready Online", 
     220                    "10002" : "Ready Offline", 
     221                    "10003" : "Warming Up", 
     222                    "10004" : "Self Test", 
     223                    "10005" : "Reset", 
     224                    "10023" : "Printing", 
     225                  } 
     226class PJLAccounter : 
     227    """A class for PJL print accounting.""" 
     228    def __init__(self, parent, printerhostname) : 
     229        self.parent = parent 
     230        self.printerHostname = printerhostname 
     231        self.printerInternalPageCounter = self.printerStatus = None 
     232        self.printerInternalPageCounter = self.printerStatus = None 
     233        self.timedout = 0 
     234         
     235    def alarmHandler(self, signum, frame) :     
     236        """Query has timedout, handle this.""" 
     237        self.timedout = 1 
     238        raise IOError, "Waiting for PJL answer timed out. Please try again later." 
     239         
     240    def retrievePJLValues(self) :     
     241        """Retrieves a printer's internal page counter and status via PJL.""" 
     242        port = 9100 
     243        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     244        try : 
     245            sock.connect((self.printerHostname, port)) 
     246        except socket.error, msg : 
     247            self.parent.filter.printInfo(_("Problem during connection to %s:%s : %s") % (self.printerHostname, port, msg), "warn") 
     248        else : 
     249            try : 
     250                sock.send(pjlMessage) 
     251            except socket.error, msg : 
     252                self.parent.filter.printInfo(_("Problem while sending PJL query to %s:%s : %s") % (self.printerHostname, port, msg), "warn") 
     253            else :     
     254                actualpagecount = self.printerStatus = None 
     255                self.timedout = 0 
     256                while (self.timedout = 0) or (actualpagecount is None) or (self.printerStatus is None) : 
     257                    signal.signal(signal.SIGALRM, self.alarmHandler) 
     258                    signal.alarm(5) 
     259                    try : 
     260                        answer = sock.recv(1024) 
     261                    except IOError, msg :     
     262                        break   # our alarm handler was launched, probably 
     263                    else :     
     264                        readnext = 0 
     265                        for line in [l.strip() for l in answer.split()] :  
     266                            if line.startswith("CODE=") : 
     267                                self.printerStatus = line.split("=")[1] 
     268                            elif line.startswith("PAGECOUNT") :     
     269                                readnext = 1 # page counter is on next line 
     270                            elif readnext :     
     271                                actualpagecount = int(line.strip()) 
     272                                readnext = 0 
     273                    signal.alarm(0) 
     274                self.printerInternalPageCounter = max(actualpagecount, self.printerInternalPageCounter) 
     275        sock.close() 
     276         
     277    def waitPrinting(self) : 
     278        """Waits for printer status being 'printing'.""" 
     279        while 1: 
     280            self.retrievePJLValues() 
     281            if self.printerStatus in ('10000', '10001', '10023') : 
     282                break 
     283            # In reality, and if I'm not mistaken, we will NEVER get there.     
     284            self.parent.filter.logdebug(_("Waiting for printer %s to be idle or printing...") % self.parent.filter.printername) 
     285            time.sleep(ITERATIONDELAY) 
     286         
     287    def waitIdle(self) : 
     288        """Waits for printer status being 'idle'.""" 
     289        idle_num = idle_flag = 0 
     290        while 1 : 
     291            self.retrievePJLValues() 
     292            idle_flag = 0 
     293            if self.printerStatus in ('10000', '10001',) : 
     294                idle_flag = 1 
     295            if idle_flag :     
     296                idle_num += 1 
     297                if idle_num > STABILIZATIONDELAY : 
     298                    # printer status is stable, we can exit 
     299                    break 
     300            else :     
     301                idle_num = 0 
     302            self.parent.filter.logdebug(_("Waiting for printer %s's idle status to stabilize...") % self.parent.filter.printername) 
     303            time.sleep(ITERATIONDELAY) 
    210304     
    211305class Accounter(AccounterBase) : 
     
    289383        """ 
    290384        commandline = self.arguments.strip() % locals() 
    291         if commandline.lower() == "snmp" : 
     385        cmdlower = commandline.lower() 
     386        if cmdlower == "snmp" : 
    292387            if hasSNMP : 
    293388                return self.askWithSNMP(printer) 
    294389            else :     
    295390                raise PyKotaAccounterError, _("Internal SNMP accounting asked, but Python-SNMP is not available. Please download it from http://pysnmp.sourceforge.net") 
     391        elif cmdlower == "pjl" : 
     392            return self.askWithPJL(printer) 
    296393             
    297394        if printer is None : 
     
    355452                self.filter.printInfo(_("SNMP querying stage interrupted. Using latest value seen for internal page counter (%s) on printer %s.") % (acc.printerInternalPageCounter, self.filter.printername), "warn") 
    356453        return acc.printerInternalPageCounter 
     454         
     455    def askWithPJL(self, printer) : 
     456        """Returns the page counter from the printer via internal PJL handling.""" 
     457        acc = PJLAccounter(self, printer) 
     458        try : 
     459            if (os.environ.get("PYKOTASTATUS") != "CANCELLED") and \ 
     460               (os.environ.get("PYKOTAACTION") != "DENY") and \ 
     461               (os.environ.get("PYKOTAPHASE") == "AFTER") : 
     462                acc.waitPrinting() 
     463            acc.waitIdle()     
     464        except :     
     465            if acc.printerInternalPageCounter is None : 
     466                raise 
     467            else :     
     468                self.filter.printInfo(_("PJL querying stage interrupted. Using latest value seen for internal page counter (%s) on printer %s.") % (acc.printerInternalPageCounter, self.filter.printername), "warn") 
     469        return acc.printerInternalPageCounter