root / pykota / trunk / pykota / accounters / hardware.py @ 3260

Revision 3260, 8.0 kB (checked in by jerome, 16 years ago)

Changed license to GNU GPL v3 or later.
Changed Python source encoding from ISO-8859-15 to UTF-8 (only ASCII
was used anyway).

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