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

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

Moved all exceptions definitions to a dedicated module.

  • 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, 2008 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.errors import PyKotaAccounterError
28from pykota.accounter import AccounterBase
29from pykota.accounters import snmp, pjl
30
31class Accounter(AccounterBase) :
32    def __init__(self, kotabackend, arguments, ispreaccounter=0, name="hardware") :
33        """Initializes querying accounter."""
34        AccounterBase.__init__(self, kotabackend, arguments, ispreaccounter, name)
35        self.isSoftware = 0
36       
37    def getPrinterInternalPageCounter(self) :   
38        """Returns the printer's internal page counter."""
39        self.filter.logdebug("Reading printer %s's internal page counter..." % self.filter.PrinterName)
40        counter = self.askPrinterPageCounter(self.filter.PrinterHostName)
41        self.filter.logdebug("Printer %s's internal page counter value is : %s" % (self.filter.PrinterName, str(counter)))
42        return counter   
43       
44    def beginJob(self, printer) :   
45        """Saves printer internal page counter at start of job."""
46        # save page counter before job
47        self.LastPageCounter = self.getPrinterInternalPageCounter()
48        self.fakeBeginJob()
49       
50    def fakeBeginJob(self) :   
51        """Fakes a begining of a job."""
52        self.counterbefore = self.getLastPageCounter()
53       
54    def endJob(self, printer) :   
55        """Saves printer internal page counter at end of job."""
56        # save page counter after job
57        self.LastPageCounter = self.counterafter = self.getPrinterInternalPageCounter()
58       
59    def getJobSize(self, printer) :   
60        """Returns the actual job size."""
61        if (not self.counterbefore) or (not self.counterafter) :
62            # there was a problem retrieving page counter
63            self.filter.printInfo(_("A problem occured while reading printer %s's internal page counter.") % printer.Name, "warn")
64            if printer.LastJob.Exists :
65                # if there's a previous job, use the last value from database
66                self.filter.printInfo(_("Retrieving printer %s's page counter from database instead.") % printer.Name, "warn")
67                if not self.counterbefore : 
68                    self.counterbefore = printer.LastJob.PrinterPageCounter or 0
69                if not self.counterafter :
70                    self.counterafter = printer.LastJob.PrinterPageCounter or 0
71                before = min(self.counterbefore, self.counterafter)   
72                after = max(self.counterbefore, self.counterafter)   
73                self.counterbefore = before
74                self.counterafter = after
75                if (not self.counterbefore) or (not self.counterafter) or (self.counterbefore == self.counterafter) :
76                    self.filter.printInfo(_("Couldn't retrieve printer %s's internal page counter either before or after printing.") % printer.Name, "warn")
77                    self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
78                    self.counterbefore = 0
79                    self.counterafter = 1
80            else :
81                self.filter.printInfo(_("No previous job in database for printer %s.") % printer.Name, "warn")
82                self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
83                self.counterbefore = 0
84                self.counterafter = 1
85               
86        jobsize = (self.counterafter - self.counterbefore)   
87        if jobsize < 0 :
88            # Try to take care of HP printers
89            # Their internal page counter is saved to NVRAM
90            # only every 10 pages. If the printer was switched
91            # off then back on during the job, and that the
92            # counters difference is negative, we know
93            # the formula (we can't know if more than eleven
94            # pages were printed though) :
95            if jobsize > -10 :
96                jobsize += 10
97            else :   
98                # here we may have got a printer being replaced
99                # DURING the job. This is HIGHLY improbable (but already happened) !
100                self.filter.printInfo(_("Inconsistent values for printer %s's internal page counter.") % printer.Name, "warn")
101                self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
102                jobsize = 1
103        return jobsize
104       
105    def askPrinterPageCounter(self, printer) :
106        """Returns the page counter from the printer via an external command.
107       
108           The external command must report the life time page number of the printer on stdout.
109        """
110        skipinitialwait = self.filter.config.getPrinterSkipInitialWait(printer)
111        commandline = self.arguments.strip() % locals()
112        cmdlower = commandline.lower()
113        if (cmdlower == "snmp") or cmdlower.startswith("snmp:") :
114            return snmp.Handler(self, printer, skipinitialwait).retrieveInternalPageCounter()
115        elif (cmdlower == "pjl") or cmdlower.startswith("pjl:") :
116            return pjl.Handler(self, printer, skipinitialwait).retrieveInternalPageCounter()
117           
118        if printer is None :
119            raise PyKotaAccounterError, _("Unknown printer address in HARDWARE(%s) for printer %s") % (commandline, self.filter.PrinterName)
120        while 1 :   
121            self.filter.printInfo(_("Launching HARDWARE(%s)...") % commandline)
122            pagecounter = None
123            child = popen2.Popen4(commandline)   
124            try :
125                answer = child.fromchild.read()
126            except IOError :   
127                # we were interrupted by a signal, certainely a SIGTERM
128                # caused by the user cancelling the current job
129                try :
130                    os.kill(child.pid, signal.SIGTERM)
131                except :   
132                    pass # already killed ?
133                self.filter.printInfo(_("SIGTERM was sent to hardware accounter %s (pid: %s)") % (commandline, child.pid))
134            else :   
135                lines = [l.strip() for l in answer.split("\n")]
136                for i in range(len(lines)) : 
137                    try :
138                        pagecounter = int(lines[i])
139                    except (AttributeError, ValueError) :
140                        self.filter.printInfo(_("Line [%s] skipped in accounter's output. Trying again...") % lines[i])
141                    else :   
142                        break
143            child.fromchild.close()   
144            child.tochild.close()
145            try :
146                status = child.wait()
147            except OSError, msg :   
148                self.filter.logdebug("Error while waiting for hardware accounter pid %s : %s" % (child.pid, msg))
149            else :   
150                if os.WIFEXITED(status) :
151                    status = os.WEXITSTATUS(status)
152                self.filter.printInfo(_("Hardware accounter %s exit code is %s") % (self.arguments, str(status)))
153               
154            if pagecounter is None :
155                message = _("Unable to query printer %s via HARDWARE(%s)") % (printer, commandline)
156                if self.onerror == "CONTINUE" :
157                    self.filter.printInfo(message, "error")
158                else :
159                    raise PyKotaAccounterError, message 
160            else :       
161                return pagecounter       
Note: See TracBrowser for help on using the browser.