root / pykota / branches / 1.26_fixes / pykota / accounters / hardware.py @ 3511

Revision 3511, 7.8 kB (checked in by jerome, 15 years ago)

Backported the code that fixed #48.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[1475]1# PyKota
2# -*- coding: ISO-8859-15 -*-
3#
4# PyKota - Print Quotas for CUPS and LPRng
5#
[3133]6# (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com>
[1475]7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
[3511]16#
[1475]17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
[2302]19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
[1475]20#
21# $Id$
22#
[2075]23#
[1475]24
25import os
[1703]26import signal
[1483]27import popen2
[1729]28
[1475]29from pykota.accounter import AccounterBase, PyKotaAccounterError
[2205]30from pykota.accounters import snmp, pjl
[1475]31
32class Accounter(AccounterBase) :
[2635]33    def __init__(self, kotabackend, arguments, ispreaccounter=0) :
[1475]34        """Initializes querying accounter."""
35        AccounterBase.__init__(self, kotabackend, arguments)
[1600]36        self.isSoftware = 0
[3511]37
38    def getPrinterInternalPageCounter(self) :
[1475]39        """Returns the printer's internal page counter."""
[2409]40        self.filter.logdebug("Reading printer %s's internal page counter..." % self.filter.PrinterName)
41        counter = self.askPrinterPageCounter(self.filter.PrinterHostName)
42        self.filter.logdebug("Printer %s's internal page counter value is : %s" % (self.filter.PrinterName, str(counter)))
[3511]43        return counter
44
45    def beginJob(self, printer) :
[1475]46        """Saves printer internal page counter at start of job."""
47        # save page counter before job
[1624]48        self.LastPageCounter = self.getPrinterInternalPageCounter()
49        self.fakeBeginJob()
[3511]50
51    def fakeBeginJob(self) :
[1624]52        """Fakes a begining of a job."""
53        self.counterbefore = self.getLastPageCounter()
[3511]54
55    def endJob(self, printer) :
[1475]56        """Saves printer internal page counter at end of job."""
57        # save page counter after job
58        self.LastPageCounter = self.counterafter = self.getPrinterInternalPageCounter()
[3511]59
60    def getJobSize(self, printer) :
[1475]61        """Returns the actual job size."""
[1713]62        if (not self.counterbefore) or (not self.counterafter) :
63            # there was a problem retrieving page counter
64            self.filter.printInfo(_("A problem occured while reading printer %s's internal page counter.") % printer.Name, "warn")
65            if printer.LastJob.Exists :
66                # if there's a previous job, use the last value from database
67                self.filter.printInfo(_("Retrieving printer %s's page counter from database instead.") % printer.Name, "warn")
[3511]68                if not self.counterbefore :
[1713]69                    self.counterbefore = printer.LastJob.PrinterPageCounter or 0
70                if not self.counterafter :
71                    self.counterafter = printer.LastJob.PrinterPageCounter or 0
[3511]72                before = min(self.counterbefore, self.counterafter)
73                after = max(self.counterbefore, self.counterafter)
[1713]74                self.counterbefore = before
75                self.counterafter = after
[1734]76                if (not self.counterbefore) or (not self.counterafter) or (self.counterbefore == self.counterafter) :
[1713]77                    self.filter.printInfo(_("Couldn't retrieve printer %s's internal page counter either before or after printing.") % printer.Name, "warn")
78                    self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
[1714]79                    self.counterbefore = 0
80                    self.counterafter = 1
[1713]81            else :
82                self.filter.printInfo(_("No previous job in database for printer %s.") % printer.Name, "warn")
83                self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
84                self.counterbefore = 0
85                self.counterafter = 1
[3511]86
87        jobsize = (self.counterafter - self.counterbefore)
[1713]88        if jobsize < 0 :
[3511]89            # Try to take care of HP printers
[1713]90            # Their internal page counter is saved to NVRAM
91            # only every 10 pages. If the printer was switched
92            # off then back on during the job, and that the
[3511]93            # counters difference is negative, we know
[1713]94            # the formula (we can't know if more than eleven
95            # pages were printed though) :
96            if jobsize > -10 :
97                jobsize += 10
[3511]98            else :
[1713]99                # here we may have got a printer being replaced
[2198]100                # DURING the job. This is HIGHLY improbable (but already happened) !
[1713]101                self.filter.printInfo(_("Inconsistent values for printer %s's internal page counter.") % printer.Name, "warn")
102                self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
103                jobsize = 1
[1475]104        return jobsize
[3511]105
[1483]106    def askPrinterPageCounter(self, printer) :
107        """Returns the page counter from the printer via an external command.
[3511]108
[1483]109           The external command must report the life time page number of the printer on stdout.
110        """
[3511]111        skipinitialwait = self.filter.config.getPrinterSkipInitialWait(self.filter.PrinterName)
[1483]112        commandline = self.arguments.strip() % locals()
[1746]113        cmdlower = commandline.lower()
[2425]114        if (cmdlower == "snmp") or cmdlower.startswith("snmp:") :
[3162]115            return snmp.Handler(self, printer, skipinitialwait).retrieveInternalPageCounter()
[2425]116        elif (cmdlower == "pjl") or cmdlower.startswith("pjl:") :
[3162]117            return pjl.Handler(self, printer, skipinitialwait).retrieveInternalPageCounter()
[3511]118
[1483]119        if printer is None :
[2409]120            raise PyKotaAccounterError, _("Unknown printer address in HARDWARE(%s) for printer %s") % (commandline, self.filter.PrinterName)
[3511]121        while 1 :
[1739]122            self.filter.printInfo(_("Launching HARDWARE(%s)...") % commandline)
123            pagecounter = None
[3511]124            child = popen2.Popen4(commandline)
[1483]125            try :
[1739]126                answer = child.fromchild.read()
[3511]127            except IOError :
[1739]128                # we were interrupted by a signal, certainely a SIGTERM
129                # caused by the user cancelling the current job
[1680]130                try :
[1739]131                    os.kill(child.pid, signal.SIGTERM)
[3511]132                except :
[1739]133                    pass # already killed ?
134                self.filter.printInfo(_("SIGTERM was sent to hardware accounter %s (pid: %s)") % (commandline, child.pid))
[3511]135            else :
[1739]136                lines = [l.strip() for l in answer.split("\n")]
[3511]137                for i in range(len(lines)) :
[1739]138                    try :
139                        pagecounter = int(lines[i])
140                    except (AttributeError, ValueError) :
141                        self.filter.printInfo(_("Line [%s] skipped in accounter's output. Trying again...") % lines[i])
[3511]142                    else :
[1739]143                        break
[3511]144            child.fromchild.close()
[1739]145            child.tochild.close()
146            try :
147                status = child.wait()
[3511]148            except OSError, msg :
[1739]149                self.filter.logdebug("Error while waiting for hardware accounter pid %s : %s" % (child.pid, msg))
[3511]150            else :
[1739]151                if os.WIFEXITED(status) :
152                    status = os.WEXITSTATUS(status)
153                self.filter.printInfo(_("Hardware accounter %s exit code is %s") % (self.arguments, str(status)))
[3511]154
[1739]155            if pagecounter is None :
156                message = _("Unable to query printer %s via HARDWARE(%s)") % (printer, commandline)
157                if self.onerror == "CONTINUE" :
158                    self.filter.printInfo(message, "error")
159                else :
[3511]160                    raise PyKotaAccounterError, message
161            else :
162                return pagecounter
Note: See TracBrowser for help on using the browser.