Changeset 1746

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.

Location:
pykota/trunk
Files:
4 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/conf/pykota.conf.sample

    r1729 r1746  
    213213#                 e.g. myprinter.example.com 
    214214# 
    215 #         Example :  
     215#         Recommended values : 
     216# 
     217#             accounter: hardware(snmp) 
     218# 
     219#               Extracts the printer's internal page counter via SNMP. 
     220# 
     221#         Or : 
     222# 
     223#             accounter: hardware(pjl) 
     224#  
     225#               Extracts the printer's internal page counter via PJL queries over port tcp/9100. 
     226# 
     227#         Other Examples :  
    216228#          
    217229#             accounter: hardware(/usr/bin/snmpget -v1 -c public -Ov %(printer)s mib-2.43.10.2.1.4.1.1 | cut -f 2,2 -d " ") 
     
    221233#             accounter: hardware(/usr/bin/npadmin --pagecount %(printer)s) 
    222234#          
    223 #         Another example, using the special 'snmp' value, which doesn't launch 
    224 #         any subprocess but instead all SNMP processing is done from PyKota's main 
    225 #         code (this is less configurable, but requires less CPU and can prevent zombies) :  
    226 # 
    227 #             accounter: hardware(snmp) 
    228 # 
    229235#         Another example, for AppleTalk printers which works fine : 
    230236#         (You may need the pap CUPS backend installed, and copy the  
     
    275281#         the printer) 
    276282#          
    277 #         YOU ABSOLUTELY HAVE TO BE SURE YOU HAVE A SCRIPT WHICH WAITS FOR THE 
    278 #         PRINTER BEING READY BEFORE ASKING FOR ITS INTERNAL PAGE COUNTER. 
    279 #          
    280 #         PYKOTA INCLUDES SUCH SCRIPTS FOR SNMP AND APPLETALK PRINTERS, MORE TO COME 
    281 # 
    282 #         SOME OF THE ABOVE EXAMPLES DON'T USE SUCH A SCRIPT, YOU HAVE BEEN WARNED 
     283#   YOU ABSOLUTELY HAVE TO BE SURE YOU HAVE A SCRIPT WHICH WAITS FOR THE 
     284#   PRINTER BEING READY BEFORE ASKING FOR ITS INTERNAL PAGE COUNTER. 
     285#          
     286#   PYKOTA INCLUDES SUCH SCRIPTS FOR SNMP AND APPLETALK PRINTERS, MORE TO COME 
     287# 
     288#   SOME OF THE ABOVE EXAMPLES DON'T USE SUCH A SCRIPT, YOU HAVE BEEN WARNED 
     289# 
     290# 
     291#   WITH THE SPECIAL MAGIC hardware(snmp) AND hardware(pjl) VALUES, PYKOTA 
     292#   TAKES CARE OF ALL THIS FOR YOU, SO PLEASE UNDERSTAND THAT IT IS PREFERABLE 
     293#   TO USE THESE TWO METHODS : THEY WORK FINE, REQUIRE LITTLE TO NO CPU, 
     294#   AND DO ALL THE HARD WORK AUTOMATICALLY. IF YOU REALLY NEED TO YOU CAN USE 
     295#   YOUR OWN EXTERNAL COMMANDS AS DESCRIBED ABOVE, JUST BE CAREFUL WITH THIS. 
    283296#          
    284297# 
  • pykota/trunk/NEWS

    r1742 r1746  
    2222PyKota NEWS : 
    2323 
     24    - 1.20alpha16 : 
     25     
     26        - Internal handling of socket based PJL queries over 
     27          port tcp/9100 is now available with the magic 
     28          'pjl' value in the hardware accounter. 
     29           
    2430    - 1.20alpha15 : 
    2531     
  • 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 
  • pykota/trunk/pykota/version.py

    r1742 r1746  
    2222# 
    2323 
    24 __version__ = "1.20alpha15_unofficial" 
     24__version__ = "1.20alpha16_unofficial" 
    2525 
    2626__doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""