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

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

Minor change to please emacs...

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