| 215 | |
| 216 | pjlMessage = "\033%-12345X@PJL USTATUSOFF\r\n@PJL USTATUS DEVICE=ON\r\n@PJL INFO STATUS\r\n@PJL INFO PAGECOUNT\r\n\033%-12345X" |
| 217 | pjlStatusValues = { |
| 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 | } |
| 226 | class 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) |
| 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 |