Paste: getpagecount.pl (better hilight)
Author: | atax1a |
Mode: | perl |
Date: | Thu, 12 Oct 2023 00:22:48 |
Plain Text |
use strict;
use IO::Socket::INET;
use List::Util qw/first/;
my $DEBUG = undef;
# getpagecount.pl
# Purpose:
# After syncing with the printer (the name or IP of which is passed on the
# command line), wait for it to be in a READY state, acquire the page count,
# and print it on STDOUT.
#
# Languages used:
# Printer Job Language (to request status updates of our printers)
# PostScript (to read the page count register)
# Tunable constants
my $TIME = 10; # Number of seconds to wait before expecting a USTATUS message
my $READLEN = 512; # Number of bytes to sysread() from the printer to try and
# capture the ustatus.
my $MAX_PAGECOUNT_RETRIES = 3; # Number of times to try and read the pagecount
# out of the printer.
# Manifest constants
my $CRLF = qq{\cM\cJ}; # Preferred line endings
# Constants for HP PJL. The meanings, syntax, and semantics of this language
# are documented here:
# http://lprng.sourceforge.net/DISTRIB/RESOURCES/DOCS/pjltkref.pdf
# A brief summary follows each request, but please see the documentation if you
# work on this.
# The PJL Universal Escape Language.
my $PJL_ESCAPE = qq{\033%-12345X};
my $PJL_USTATUS_ON = $PJL_ESCAPE . '@PJL COMMENT Sync Printer' . $CRLF . # Enter PJL mode
# Disable any other status messages
'@PJL USTATUSOFF' . $CRLF .
# Ask for status about the device
'@PJL USTATUS DEVICE = ON' . $CRLF .
# ... Every $TIME seconds
'@PJL USTATUS TIMED = ' . $TIME . $CRLF .
# Also ask the printer for what it thinks it is.
'@PJL INFO ID' . $CRLF;
# The following PostScript job reads the page count register, and reports it in
# an easily-distinguished format on the printer's communication channel (in
# this case, our network socket)
my $PAGECOUNT_PS = q{@PJL ENTER LANGUAGE=POSTSCRIPT
%!PS
(%%[ pagecount: ) print
statusdict /pagecount get exec % Get the page count register
( ) cvs print % Stuff it into the string
( ]%%) = % Write it out
flush
};
# If our PJL INFO ID matches this, we need a different code.
my $PHASER_WORKAROUND = qr/\@PJL INFO ID\cM?\cJXerox Phaser /ms;
my $PHASER_WORKAROUND_CODE = 10023;
# PJL State constants:
# Ready, Toner Low, has-a-job (lprng leaves it in this state for some reason)
my @GOOD_CODES = (10001, 10006, 10030);
# PJL "we should sleep longer" codes:
# Power save, warming up, power save.
my @SLEEP_LONGER = (10000, 10003, 35078);
# Pull off the command-line argument. There is only one required, and it should
# be the name or IP address of a printer from which to read the page count.
my $printername = shift || die "$0 printername\n";
# Connect to the printer's JetDirect socket, or error.
my $printer = IO::Socket::INET->new(
PeerAddr => "$printername:9100",
Proto => "tcp",
Timeout => 30)
or die "Couldn't connect to $printername: $!\n";
my $buf; # A temporary buffer used for reading data out of the printer.
my $hist; # used after requesting the page count; holds the previous buffers
# Request timed unsolicted updates as well as printer identification
$printer->syswrite($PJL_USTATUS_ON) || die "$0: $printername went away? ($!)\n";
print STDERR "Connected to printer and sent PJL ustatus request\n" if $DEBUG;
sleep 1; # Wait for the printer to respond.
$printer->sysread($buf, $READLEN*2) || die "$0: $printername went away? ($!)\n";
if($buf =~ $PHASER_WORKAROUND) {
# We've got a Phaser, which needs 10023 as a "good" code.
print STDERR "Phaser workaround enabled\n" if $DEBUG;
push @GOOD_CODES, $PHASER_WORKAROUND_CODE;
}
# Main loop
RETRY: while(1) {
$buf = ""; # Clear buffers
$hist = "";
my $code = 0;
$printer->sysread($buf, $READLEN) || die "$0: $printername went away? ($!)\n";
print STDERR "Got this buffer: $buf\n" if $DEBUG;
if ($buf =~ m/CODE=(\d{5})/ims) {
$code = $1;
}
if(not $code or not first {$_ == $code} @GOOD_CODES) {
# These are things we do not want, so we wait for the system to report
# ready status with a good code.
warn "Didn't get an expected good code, instead received $code\n";
sleep $TIME;
next RETRY;
}
if(first {$_ == $code} @SLEEP_LONGER) {
# These are okay, but we need to chill for a bit. The printer needs some
# time to wake up.
sleep $TIME * 2;
next RETRY;
}
# Right now, we're guaranteed a "good" code. Send the pagecount job and its
# trailing EOF, and then the command to return to PJL mode.
$printer->syswrite($PAGECOUNT_PS . "\cD" .$PJL_ESCAPE) ||
die "$0: $printername went away? ($!)\n";
print STDERR "Sent pagecount file to the printer\n" if $DEBUG;
# Attempt to read the page count $MAX_PAGECOUNT_RETRIES times.
for(1 .. $MAX_PAGECOUNT_RETRIES) {
$printer->sysread($buf, $READLEN) ||
die "$0: $printername went away? ($!)\n";
$hist .= $buf; # Save and examine the buffer history, in case of short-reading
print STDERR $hist if $DEBUG;
if ($hist =~ m/%%\[ pagecount: (\d+) \]%%/ims) {
print "$1\n";
exit;
}
}
warn "Couldn't get pagecount after $MAX_PAGECOUNT_RETRIES tries.\n" .
"last code was $code; buffer follows: $buf\n" .
"history: $hist\n";
}
New Annotation